diff --git a/compiler/src/dotty/tools/dotc/cc/SafeRefs.scala b/compiler/src/dotty/tools/dotc/cc/SafeRefs.scala index 52a799161361..7a323187ac5b 100644 --- a/compiler/src/dotty/tools/dotc/cc/SafeRefs.scala +++ b/compiler/src/dotty/tools/dotc/cc/SafeRefs.scala @@ -120,28 +120,46 @@ object SafeRefs { sym.hasAnnotation(defn.AssumeSafeAnnot) || isSafe(if sym.is(ModuleVal) then sym.moduleClass else sym.owner) - val (sym, checkLater) = tree match - case tree: New => - (tree.tpt.symbol, false) - case tree: RefTree => - val checkLater = - !tree.symbol.is(Method) - && pt.match - case pt: PathSelectionProto => pt.selector.isStatic - case _: SelectionProto => true - case _ => false - (tree.symbol, checkLater) + val sym = tree match + case tree: New => tree.tpt.symbol + case tree: RefTree => tree.symbol + + def checkLater = + sym.isTerm && !sym.is(Method) && pt.match + case pt: PathSelectionProto => pt.selector.isStatic + case _: SelectionProto => true + case _ => false + + def isStatic = tree match + case tree: Ident => + // Idents might refer to inherited symbols of static objects. + // in this case we need to check whether the prefix is static + // For Selects this is not an issue since we have already checked + // the qualifier for safety. safemode-pkg-inherit.scala is a test case. + tree.tpe match + case NamedType(prefix, _) => + prefix.dealias match + case prefix: ThisType => prefix.cls.isStatic + case prefix: TermRef => prefix.symbol.isStatic + case _ => sym.isStatic + case _ => sym.isStatic + case _ => sym.isStatic if Feature.safeEnabled && sym.exists + && !sym.is(Package) && checkNotRejected(sym, tree.srcPos) && !checkLater - && sym.isStatic // if it's not static it is local, a parameter, or comes from another symbol, - // which has been checked - && !sym.is(Package) + && isStatic // if it's not static it is local, a parameter, or comes from another symbol, + // which has been checked && !isSafe(sym) then fail(sym, "it is neither compiled in safe mode nor tagged with @assumedSafe", tree.srcPos) + else if false then + tree.tpe match + case TermRef(prefix: TermRef, _) => + println(i"safe: $tree, ${tree.tpe}, ${prefix.symbol.isStaticOwner}") + case _ => } private def checkSafeAnnot(ann: Annotation, pos: SrcPos)(using Context): Unit = diff --git a/tests/neg-custom-args/captures/safemode-pkg-inherit.check b/tests/neg-custom-args/captures/safemode-pkg-inherit.check new file mode 100644 index 000000000000..f3c49c0e476b --- /dev/null +++ b/tests/neg-custom-args/captures/safemode-pkg-inherit.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-custom-args/captures/safemode-pkg-inherit.scala:4:13 ----------------------------------------------- +4 |val result = Seq("sh", "-c", "echo 'oh no'").!! // error + | ^ + |Cannot refer to method stringSeqToProcess in trait ProcessImplicits from safe code since it is neither compiled in safe mode nor tagged with @assumedSafe diff --git a/tests/neg-custom-args/captures/safemode-pkg-inherit.scala b/tests/neg-custom-args/captures/safemode-pkg-inherit.scala new file mode 100644 index 000000000000..683b28fc7cd6 --- /dev/null +++ b/tests/neg-custom-args/captures/safemode-pkg-inherit.scala @@ -0,0 +1,5 @@ +import scala.language.experimental.safe +import scala.sys.process.* + +val result = Seq("sh", "-c", "echo 'oh no'").!! // error +