From cc767f0f357f7750cea8759d1c57dde6a8e224e9 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 18 Sep 2024 12:14:32 +0200 Subject: [PATCH] A PoC with one working case --- .../src/dotty/tools/dotc/typer/Namer.scala | 70 ++++++++++++++++--- tests/pos/infer-tracked.scala | 22 ++++++ 2 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 tests/pos/infer-tracked.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6167db62fbe0..e3387208e7c7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -877,16 +877,16 @@ class Namer { typer: Typer => protected def addAnnotations(sym: Symbol): Unit = original match { case original: untpd.MemberDef => lazy val annotCtx = annotContext(original, sym) - original.setMods: + original.setMods: original.mods.withAnnotations : - original.mods.annotations.mapConserve: annotTree => + original.mods.annotations.mapConserve: annotTree => val cls = typedAheadAnnotationClass(annotTree)(using annotCtx) if (cls eq sym) report.error(em"An annotation class cannot be annotated with iself", annotTree.srcPos) annotTree else - val ann = - if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass) + val ann = + if cls.is(JavaDefined) then Checking.checkNamedArgumentForJavaAnnotation(annotTree, cls.asClass) else annotTree val ann1 = Annotation.deferred(cls)(typedAheadExpr(ann)(using annotCtx)) sym.addAnnotation(ann1) @@ -1545,6 +1545,8 @@ class Namer { typer: Typer => case completer: Completer => completer.indexConstructor(constr, constrSym) case _ => + // constrSym.info = typeSig(constrSym) + tempInfo = denot.asClass.classInfo.integrateOpaqueMembers.asInstanceOf[TempClassInfo] denot.info = savedInfo } @@ -1646,6 +1648,7 @@ class Namer { typer: Typer => * as an attachment on the ClassDef tree. */ def enterParentRefinementSyms(refinements: List[(Name, Type)]) = + println(s"For class $cls, entering parent refinements: $refinements") val refinedSyms = mutable.ListBuffer[Symbol]() for (name, tp) <- refinements do if decls.lookupEntry(name) == null then @@ -1653,7 +1656,9 @@ class Namer { typer: Typer => case tp: MethodOrPoly => Method | Synthetic | Deferred | Tracked case _ if name.isTermName => Synthetic | Deferred | Tracked case _ => Synthetic | Deferred - refinedSyms += newSymbol(cls, name, flags, tp, coord = original.rhs.span.startPos).entered + val s = newSymbol(cls, name, flags, tp, coord = original.rhs.span.startPos).entered + refinedSyms += s + println(s" entered $s") if refinedSyms.nonEmpty then typr.println(i"parent refinement symbols: ${refinedSyms.toList}") original.pushAttachment(ParentRefinements, refinedSyms.toList) @@ -1695,6 +1700,7 @@ class Namer { typer: Typer => end addUsingTraits completeConstructor(denot) + val constrSym = symbolOfTree(constr) denot.info = tempInfo.nn val parentTypes = defn.adjustForTuple(cls, cls.typeParams, @@ -1928,7 +1934,7 @@ class Namer { typer: Typer => val mt = wrapMethType(effectiveResultType(sym, paramSymss)) if sym.isPrimaryConstructor then checkCaseClassParamDependencies(mt, sym.owner) mt - else if sym.isAllOf(Given | Method) && Feature.enabled(modularity) then + else if Feature.enabled(modularity) then // set every context bound evidence parameter of a given companion method // to be tracked, provided it has a type that has an abstract type member. // Add refinements for all tracked parameters to the result type. @@ -1986,14 +1992,60 @@ class Namer { typer: Typer => cls.srcPos) case _ => - /** Under x.modularity, we add `tracked` to context bound witnesses - * that have abstract type members + /** Try to infer if the parameter needs a `tracked` modifier */ def needsTracked(sym: Symbol, param: ValDef)(using Context) = !sym.is(Tracked) - && param.hasAttachment(ContextBoundParam) + && ( + isContextBoundWitnessWithAbstractMembers(sym, param) + || isReferencedInPublicSignatures(sym) + // || isPassedToTrackedParentParameter(sym, param) + ) + + /** Under x.modularity, we add `tracked` to context bound witnesses + * that have abstract type members + */ + def isContextBoundWitnessWithAbstractMembers(sym: Symbol, param: ValDef)(using Context): Boolean = + param.hasAttachment(ContextBoundParam) && sym.info.memberNames(abstractTypeNameFilter).nonEmpty + /** Under x.modularity, we add `tracked` to term parameters whose types are referenced + * in public signatures of the defining class + */ + def isReferencedInPublicSignatures(sym: Symbol)(using Context): Boolean = + val owner = sym.maybeOwner.maybeOwner + val accessorSyms = maybeParamAccessors(owner, sym) + def checkOwnerMemberSignatures(owner: Symbol): Boolean = + owner.infoOrCompleter match + case info: ClassInfo => + info.decls.filter(d => !d.isConstructor).exists(d => tpeContainsSymbolRef(d.info, accessorSyms)) + case _ => false + checkOwnerMemberSignatures(owner) + + def isPassedToTrackedParentParameter(sym: Symbol, param: ValDef)(using Context): Boolean = + val owner = sym.maybeOwner.maybeOwner + val accessorSyms = maybeParamAccessors(owner, sym) + owner.infoOrCompleter match + // case info: ClassInfo => + // info.parents.foreach(println) + // info.parents.exists(tpeContainsSymbolRef(_, accessorSyms)) + case _ => false + + private def namedTypeWithPrefixContainsSymbolRef(tpe: Type, syms: List[Symbol])(using Context): Boolean = tpe match + case tpe: NamedType => tpe.prefix.exists && tpeContainsSymbolRef(tpe.prefix, syms) + case _ => false + + private def tpeContainsSymbolRef(tpe: Type, syms: List[Symbol])(using Context): Boolean = + tpe.termSymbol.exists && syms.contains(tpe.termSymbol) + || tpe.argInfos.exists(tpeContainsSymbolRef(_, syms)) + || namedTypeWithPrefixContainsSymbolRef(tpe, syms) + + private def maybeParamAccessors(owner: Symbol, sym: Symbol)(using Context): List[Symbol] = + owner.infoOrCompleter match + case info: ClassInfo => + info.decls.lookupAll(sym.name).filter(d => d.is(ParamAccessor)).toList + case _ => List.empty + /** Under x.modularity, set every context bound evidence parameter of a class to be tracked, * provided it has a type that has an abstract type member. Reset private and local flags * so that the parameter becomes a `val`. diff --git a/tests/pos/infer-tracked.scala b/tests/pos/infer-tracked.scala new file mode 100644 index 000000000000..161c3b981a78 --- /dev/null +++ b/tests/pos/infer-tracked.scala @@ -0,0 +1,22 @@ +import scala.language.experimental.modularity +import scala.language.future + +abstract class C: + type T + def foo: T + +class F(val x: C): + val result: x.T = x.foo + +class G(override val x: C) extends F(x) + +def Test = + val c = new C: + type T = Int + def foo = 42 + + val f = new F(c) + val i: Int = f.result + + // val g = new G(c) + // val j: Int = g.result