From 28b5725d95081dc5145916d26187954bb7dc4657 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Fri, 1 Nov 2024 14:49:31 +0100 Subject: [PATCH 1/3] Move setAbstractTrackedInfo to Namer.Completer --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 11 +++++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 16 +++------------- tests/pos/abstract-tracked-2.scala | 11 +++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 tests/pos/abstract-tracked-2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 0849e57b8c7d..680af14ba9b3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -907,6 +907,16 @@ class Namer { typer: Typer => case _ => } + private def setAbstractTrackedInfo(sym: Symbol)(using Context): Unit = + if !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then + if sym.allOverriddenSymbols.exists(_.flags.is(Tracked)) then + sym.setFlag(Tracked) + if sym.flags.is(Tracked) then + original match + case tree: untpd.ValDef if tree.tpt.isEmpty => + sym.info = typedAheadExpr(tree.rhs).tpe + case _ => () + /** Invalidate `denot` by overwriting its info with `NoType` if * `denot` is a compiler generated case class method that clashes * with a user-defined method in the same scope with a matching type. @@ -989,6 +999,7 @@ class Namer { typer: Typer => addInlineInfo(sym) denot.info = typeSig(sym) invalidateIfClashingSynthetic(denot) + setAbstractTrackedInfo(sym) Checking.checkWellFormed(sym) denot.info = avoidPrivateLeaks(sym) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ca1a2a98c55d..7ef6040daf90 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2834,20 +2834,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case rhs => excludeDeferredGiven(rhs, sym): typedExpr(_, tpt1.tpe.widenExpr) - setAbstractTrackedInfo(sym, rhs1, tpt) - val tpt2 = if sym.flags.is(Tracked) && tpt.isEmpty && !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then TypeTree(rhs1.tpe) else tpt1 - val vdef2 = assignType(cpy.ValDef(vdef)(name, tpt2, rhs1), sym) - postProcessInfo(vdef2, sym) - vdef2.setDefTree + val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) + postProcessInfo(vdef1, sym) + vdef1.setDefTree } - - private def setAbstractTrackedInfo(sym: Symbol, rhs: Tree, tpt: untpd.Tree)(using Context): Unit = - if !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then - if sym.allOverriddenSymbols.exists(_.flags.is(Tracked)) then - sym.setFlag(Tracked) - if sym.flags.is(Tracked) && tpt.isEmpty then - sym.info = rhs.tpe - private def retractDefDef(sym: Symbol)(using Context): Tree = // it's a discarded method (synthetic case class method or synthetic java record constructor or overridden member), drop it val canBeInvalidated: Boolean = diff --git a/tests/pos/abstract-tracked-2.scala b/tests/pos/abstract-tracked-2.scala new file mode 100644 index 000000000000..01e4ee84c548 --- /dev/null +++ b/tests/pos/abstract-tracked-2.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.modularity +import scala.language.future + +abstract class Vec: + tracked val size: Int + +@main def main = + val v = new Vec: + val size0: size.type = 10 + val size = 10 + val size1: size.type = 10 From 4133b8388b1151431a026d0181dc800d61bd7545 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Sun, 3 Nov 2024 15:59:11 +0100 Subject: [PATCH 2/3] Move tracked inference logic to `inferredResultType ` --- .../src/dotty/tools/dotc/core/Flags.scala | 2 +- .../src/dotty/tools/dotc/typer/Namer.scala | 26 +++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index b915373da021..0775b3caaf0c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -480,7 +480,7 @@ object Flags { */ val AfterLoadFlags: FlagSet = commonFlags( FromStartFlags, AccessFlags, Final, AccessorOrSealed, - Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent, Tracked) + Abstract, LazyOrTrait, SelfName, JavaDefined, JavaAnnotation, Transparent) /** A value that's unstable unless complemented with a Stable flag */ val UnstableValueFlags: FlagSet = Mutable | Method diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 680af14ba9b3..f356057b680c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -907,16 +907,6 @@ class Namer { typer: Typer => case _ => } - private def setAbstractTrackedInfo(sym: Symbol)(using Context): Unit = - if !sym.flags.is(ParamAccessor) && !sym.flags.is(Param) then - if sym.allOverriddenSymbols.exists(_.flags.is(Tracked)) then - sym.setFlag(Tracked) - if sym.flags.is(Tracked) then - original match - case tree: untpd.ValDef if tree.tpt.isEmpty => - sym.info = typedAheadExpr(tree.rhs).tpe - case _ => () - /** Invalidate `denot` by overwriting its info with `NoType` if * `denot` is a compiler generated case class method that clashes * with a user-defined method in the same scope with a matching type. @@ -999,7 +989,6 @@ class Namer { typer: Typer => addInlineInfo(sym) denot.info = typeSig(sym) invalidateIfClashingSynthetic(denot) - setAbstractTrackedInfo(sym) Checking.checkWellFormed(sym) denot.info = avoidPrivateLeaks(sym) } @@ -2062,12 +2051,17 @@ class Namer { typer: Typer => if paramss.isEmpty then info.widenExpr else NoType - val iRawInfo = - cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName).info + val iDenot = cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName) + val iSym = iDenot.symbol + val iRawInfo = iDenot.info val iResType = instantiatedResType(iRawInfo, paramss).asSeenFrom(site, cls) - if (iResType.exists) - typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") - tp & iResType + if iSym.is(Tracked) && !mdef.rhs.isEmpty then + // When inherting a tracked member, we infer a precise type from the rhs + tp & typedAheadExpr(mdef.rhs, iResType).tpe + else + if (iResType.exists) + typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") + tp & iResType } end inherited From 9ff1af77c96d0fb9070f934d33386d9ba957a4e5 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Sun, 3 Nov 2024 16:10:30 +0100 Subject: [PATCH 3/3] Also infer a precise type when tracked is not inherited --- .../src/dotty/tools/dotc/typer/Namer.scala | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index f356057b680c..9ff01fb1a20e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -2016,6 +2016,11 @@ class Namer { typer: Typer => paramFn: Type => Type, fallbackProto: Type )(using Context): Type = + /** Is this member tracked? This is true if it is marked as `tracked` or if + * it overrides a `tracked` member. To account for the later, `isTracked` + * is overriden to `true` as a side-effect of computing `inherited`. + */ + var isTracked: Boolean = sym.is(Tracked) /** A type for this definition that might be inherited from elsewhere: * If this is a setter parameter, the corresponding getter type. @@ -2053,15 +2058,12 @@ class Namer { typer: Typer => val iDenot = cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema, sym.targetName) val iSym = iDenot.symbol + if iSym.is(Tracked) then isTracked = true val iRawInfo = iDenot.info val iResType = instantiatedResType(iRawInfo, paramss).asSeenFrom(site, cls) - if iSym.is(Tracked) && !mdef.rhs.isEmpty then - // When inherting a tracked member, we infer a precise type from the rhs - tp & typedAheadExpr(mdef.rhs, iResType).tpe - else - if (iResType.exists) - typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") - tp & iResType + if (iResType.exists) + typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType") + tp & iResType } end inherited @@ -2146,6 +2148,7 @@ class Namer { typer: Typer => if defaultTp.exists then TypeOps.SimplifyKeepUnchecked() else null) match case ctp: ConstantType if sym.isInlineVal => ctp + case tp if isTracked => tp case tp => TypeComparer.widenInferred(tp, pt, Widen.Unions) // Replace aliases to Unit by Unit itself. If we leave the alias in @@ -2156,7 +2159,7 @@ class Namer { typer: Typer => def lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.srcPos) //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType") if (inherited.exists) - if sym.isInlineVal then lhsType else inherited + if sym.isInlineVal || isTracked then lhsType else inherited else { if (sym.is(Implicit)) mdef match {