@@ -14,16 +14,14 @@ import ast.tpd.*
1414import SymDenotations .*
1515import Flags .*
1616import Types .*
17- import config .Feature
1817import config .Printers .capt
19- import typer .ProtoTypes .SelectionProto
2018
2119/** Check whether references from safe mode should be allowed */
2220object SafeRefs {
2321
2422 val assumedSafePackages = List (
2523 " scala" , " scala.runtime" , " scala.collection.immutable" , " scala.compiletime.ops" ,
26- " scala.math" , " scala.util" , " java.math" , " java.time" ,
24+ " scala.math" , " scala.util" , " scala.caps " , " java.math" , " java.time" ,
2725 " java.util.function" , " java.util.regex" , " java.util.stream"
2826 )
2927
@@ -56,7 +54,7 @@ object SafeRefs {
5654 * Once we have an updated ccException in the bootstrap compiler, we could add annotations
5755 * to library classes manually, as long as these library classes are capture checked.
5856 */
59- def init ()(using Context ): Unit =
57+ def init ()(using Context ): Unit = {
6058 assumeSafe(" scala.Predef" , except = List (" print" , " println" , " printf" ))
6159 assumeSafe(" scala.runtime.coverage.Invoker" )
6260 assumeSafe(" scala.reflect.ClassTag" )
@@ -172,21 +170,73 @@ object SafeRefs {
172170 rejectSafe(" scala.runtime.LazyFloat" )
173171 rejectSafe(" scala.runtime.LazyDouble" )
174172 rejectSafe(" scala.runtime.LazyUnit" )
173+ }
175174
176175 private def fail (sym : Symbol , reason : String , pos : SrcPos )(using Context ) =
177176 report.error(em " Cannot refer to ${sym.sanitizedDescription}${sym.showExtendedLocation} from safe code since $reason" , pos)
178177 false
179178
180179 private def checkNotRejected (sym : Symbol , pos : SrcPos )(using Context ): Boolean =
181- if ! sym.exists then true
182- else sym.getAnnotation(defn.RejectSafeAnnot ) match
180+ ! sym.exists || sym.is(Package ) || sym.getAnnotation(defn.RejectSafeAnnot ).match
183181 case Some (annot) =>
184182 val message = annot.argumentConstantString(0 ).getOrElse(" " )
185- fail(sym, if message.nonEmpty then message else i " it is tagged @rejectSafe " , pos)
183+ fail(sym, if message.nonEmpty then message else i " it is tagged @rejectSafe " , pos)
186184 case _ =>
187- sym.owner.is(Package ) || checkNotRejected(sym.owner, pos)
185+ checkNotRejected(sym.owner, pos)
186+
187+ /** Check that all nodes of given tree for the following conditions.
188+ * - No reference to a symbol under a @rejectSafe annotation
189+ * - All references to static symbols are assumed safe: This means
190+ * they have been compiled in safe mode, or have an @assumeSafe
191+ * annotation or are owned by a symbol with an @assumeSafe annotation.
192+ * - No reference to a user-defined annotation which is marked @rejectSafe
193+ */
194+ object checker extends TreeTraverser :
195+ private var checkTypes = false
196+ def traverse (tree : Tree )(using Context ) =
197+ val sym = tree.symbol
198+ tree match
199+ case tree : Ident =>
200+ checkNotRejected(sym, tree.srcPos)
201+ val isStatic = tree.tpe match
202+ case NamedType (prefix, _) =>
203+ prefix.dealias match
204+ case prefix : ThisType => prefix.cls.isStatic
205+ case prefix : TermRef => prefix.symbol.isStatic
206+ case _ => sym.isStatic
207+ case _ => sym.isStatic
208+ // if sym is not static it is local, a parameter, or comes from another symbol,
209+ // which has been checked
210+ if isStatic && (checkTypes || sym.isTerm) then
211+ checkSafe(sym, tree)
212+ case tree : Select =>
213+ checkNotRejected(sym, tree.srcPos)
214+ if sym.isStatic && (checkTypes || sym.isTerm)
215+ then checkSafe(sym, tree)
216+ else traverseChildren(tree)
217+ case New (tpt) =>
218+ val saved = checkTypes
219+ checkTypes = true
220+ try traverse(tpt)
221+ finally checkTypes = saved
222+ case Inlined (call, _, _) =>
223+ traverse(call)
224+ case tree : MemberDef if ! sym.is(Synthetic ) =>
225+ for ann <- sym.annotations do
226+ checkSafeAnnot(ann, sym.srcPos)
227+ traverseChildren(tree)
228+ case tree : TypeApply if sym == defn.Any_asInstanceOf =>
229+ report.error(em " Cannot use asInstanceOf in safe mode " , tree.srcPos)
230+ case Annotated (arg, annot) =>
231+ checkNotRejected(annot.symbol, annot.srcPos.orElse(tree.srcPos))
232+ traverseChildren(arg)
233+ case tree : Import =>
234+ // skip imports, we want to be able to wildcard import from an unsafe
235+ // object as long as all used members are @assumeSafe
236+ case _ =>
237+ traverseChildren(tree)
188238
189- def checkSafe (tree : Tree , pt : Type )(using Context ): Unit = {
239+ def checkSafe (sym : Symbol , tree : Tree )(using Context ): Unit = {
190240
191241 def isSafe (sym : Symbol ): Boolean =
192242 if ! sym.exists then false
@@ -196,62 +246,15 @@ object SafeRefs {
196246 sym.hasAnnotation(defn.AssumeSafeAnnot )
197247 || isSafe(if sym.is(ModuleVal ) then sym.moduleClass else sym.owner)
198248
199- val sym = tree match
200- case tree : New => tree.tpt.tpe.classSymbol
201- case tree : RefTree => tree.symbol
202-
203- def checkLater =
204- sym.isTerm && ! sym.is(Method ) && pt.match
205- case pt : PathSelectionProto => pt.selector.isStatic
206- case _ : SelectionProto => true
207- case _ => false
208-
209- def isStatic = tree match
210- case tree : Ident =>
211- // Idents might refer to inherited symbols of static objects.
212- // in this case we need to check whether the prefix is static
213- // For Selects this is not an issue since we have already checked
214- // the qualifier for safety. safemode-pkg-inherit.scala is a test case.
215- tree.tpe match
216- case NamedType (prefix, _) =>
217- prefix.dealias match
218- case prefix : ThisType => prefix.cls.isStatic
219- case prefix : TermRef => prefix.symbol.isStatic
220- case _ => sym.isStatic
221- case _ => sym.isStatic
222- case _ => sym.isStatic
223-
224- if Feature .safeEnabled
225- && sym.exists
226- && ! sym.is(Package )
227- && checkNotRejected(sym, tree.srcPos)
228- && ! checkLater
229- && isStatic // if it's not static it is local, a parameter, or comes from another symbol,
230- // which has been checked
231- && ! isSafe(sym)
232- then
249+ if sym.exists && ! sym.is(Package ) && ! isSafe(sym) then
233250 fail(sym, " it is neither compiled in safe mode nor tagged with @assumedSafe" , tree.srcPos)
234251 else
235- capt.println(i " checked safe $tree, $sym, $checkLater " )
252+ capt.println(i " checked safe $tree, $sym" )
236253 }
237254
238255 private def checkSafeAnnot (ann : Annotation , pos : SrcPos )(using Context ): Unit =
239256 val span = ann.tree.span
240257 // Skip compiler inserted annotations that have no or zero extent span.
241258 if ! span.exists || span.isZeroExtent then return
242- var errpos = ann.tree.srcPos
243- if ! pos.sourcePos.exists then errpos = pos
244- checkNotRejected(ann.symbol, errpos)
245-
246- def checkSafeAnnots (sym : Symbol )(using Context ): Unit =
247- if Feature .safeEnabled && ! sym.is(Synthetic ) then
248- for ann <- sym.annotations do
249- checkSafeAnnot(ann, sym.srcPos)
250-
251- def checkSafeAnnotsInType (tree : Tree )(using Context ): Unit =
252- def checkAnnotatedType (tp : Type ) = tp match
253- case AnnotatedType (tp, ann) => checkSafeAnnot(ann, tree.srcPos)
254- case _ =>
255- if Feature .safeEnabled then
256- tree.tpe.foreachPart(checkAnnotatedType(_))
259+ checkNotRejected(ann.symbol, ann.tree.srcPos)
257260}
0 commit comments