Skip to content

Attempt to fix #9886 #10326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class Compiler {
new ElimPolyFunction, // Rewrite PolyFunction subclasses to FunctionN subclasses
new TailRec, // Rewrite tail recursion to loops
new CompleteJavaEnums, // Fill in constructors for Java enums
new Mixin, // Expand trait fields and trait initializers
new Mixin) :: List( // Expand trait fields and trait initializers
new LazyVals, // Expand lazy vals
new Memoize, // Add private fields to getters and setters
new NonLocalReturns, // Expand non-local returns
Expand Down
17 changes: 14 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/ElimByName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Contexts._
import Types._
import core.StdNames.nme
import ast.Trees._
import Decorators._
import Flags._

/** This phase eliminates ExprTypes `=> T` as types of method parameter references, and replaces them b
* nullary function types. More precisely:
Expand Down Expand Up @@ -62,13 +64,22 @@ class ElimByName extends TransformByNameApply with InfoTransformer {

override def transformValDef(tree: ValDef)(using Context): Tree =
atPhase(next) {
if (exprBecomesFunction(tree.symbol))
cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info))
val sym = tree.symbol
if (exprBecomesFunction(sym))
if sym.is(ParamAccessor) && sym.owner.is(Trait) then
println("TVD")
cpy.DefDef(tree)(tree.name, Nil, Nil, tree.tpt.withType(sym.info.widenExpr), tree.rhs)
else
cpy.ValDef(tree)(tpt = tree.tpt.withType(sym.info))
else tree
}

def transformInfo(tp: Type, sym: Symbol)(using Context): Type = tp match {
case ExprType(rt) => defn.FunctionOf(Nil, rt)
case ExprType(rt) =>
if sym.is(ParamAccessor) && sym.owner.is(Trait) then
ExprType(defn.FunctionOf(Nil, rt))
else
defn.FunctionOf(Nil, rt)
case _ => tp
}

Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/Getters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Flags._
import ValueClasses._
import SymUtils._
import NameOps._
import Decorators._
import collection.mutable


Expand Down Expand Up @@ -81,6 +82,15 @@ class Getters extends MiniPhase with SymTransformer { thisPhase =>
// seem to be a problem since references to a getter don't care whether
// it's a `T` or a `=> T`
}
else if d.info.isInstanceOf[ExprType]
&& d.is(ParamAccessor) && d.owner.is(Trait)
&& !d.is(Method)
then
// Value parameters of traits get an accessor by the first clause of the
// definition of `d1` above, but by-name parameters don't since they are not
// value types. I.e. we do not want to replace `=> T` with `=> (=> T)`.
// But we still need to mark them as accessors.
d.copySymDenotation(initFlags = d.flags | AccessorCreationFlags)
else d

// Drop the Local flag from all private[this] and protected[this] members.
Expand Down
11 changes: 8 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SymUtils._
import Constants._
import ast.Trees._
import MegaPhase._
import NameKinds.TraitSetterName
import NameKinds.{TraitSetterName, ExpandedName}
import NameOps._
import Flags._
import Decorators._
Expand Down Expand Up @@ -116,8 +116,12 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
}

if sym.is(Accessor, butNot = NoFieldNeeded) then
def adaptToField(field: Symbol, tree: Tree): Tree =
if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen)
def adaptToField(field: Symbol, rhs: Tree): Tree =
if (rhs.isEmpty) rhs
else
if (sym.name.is(ExpandedName))
println(i"adapt to field $tree, ${sym.nextOverriddenSymbol.showLocated}")
rhs.ensureConforms(field.info.widen)

def isErasableBottomField(field: Symbol, cls: Symbol): Boolean =
!field.isVolatile && ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass))
Expand All @@ -137,6 +141,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass)
else transformFollowingDeep(ref(field))(using ctx.withOwner(sym))
val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
// println(i"add getter $getterDef, $sym, ${sym.flagsString}, ${rhs.info}")
addAnnotations(fieldDef.denot)
removeUnwantedAnnotations(sym, defn.GetterMetaAnnot)
Thicket(fieldDef, getterDef)
Expand Down
26 changes: 16 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/Mixin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,17 +253,23 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>

for (getter <- mixin.info.decls.toList if getter.isGetter && !wasOneOf(getter, Deferred)) yield {
if (isCurrent(getter) || getter.name.is(ExpandedName)) {
val rhs =
if (wasOneOf(getter, ParamAccessor))
nextArgument()
else if (getter.is(Lazy, butNot = Module))
transformFollowing(superRef(getter).appliedToNone)
else if (getter.is(Module))
New(getter.info.resultType, List(This(cls)))
val (rhs, toDrop) =
if wasOneOf(getter, ParamAccessor) then
val arg = nextArgument()
if arg.tpe.classSymbol == defn.FunctionClass(0)
&& getter.initial.info.isInstanceOf[ExprType]
then
(arg.select(defn.Function0_apply).appliedToNone, Accessor)
else
(arg, EmptyFlags)
else if getter.is(Lazy, butNot = Module) then
(transformFollowing(superRef(getter).appliedToNone), EmptyFlags)
else if getter.is(Module) then
(New(getter.info.resultType, List(This(cls))), EmptyFlags)
else
Underscore(getter.info.resultType)
(Underscore(getter.info.resultType), EmptyFlags)
// transformFollowing call is needed to make memoize & lazy vals run
transformFollowing(DefDef(mkForwarderSym(getter.asTerm), rhs))
transformFollowing(DefDef(mkForwarderSym(getter.asTerm, dropFlags = toDrop), rhs))
}
else EmptyTree
}
Expand All @@ -280,7 +286,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth))
yield {
util.Stats.record("mixin forwarders")
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth)))
transformFollowing(polyDefDef(mkForwarderSym(meth.asTerm, extraFlags = Bridge), forwarderRhsFn(meth)))
}

cpy.Template(impl)(
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/MixinOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) {
map(n => getClassIfDefined("org.junit." + n)).
filter(_.exists)

def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags): TermSymbol = {
def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags, dropFlags: FlagSet = EmptyFlags): TermSymbol = {
val res = member.copy(
owner = cls,
name = member.name.stripScala2LocalSuffix,
flags = member.flags &~ Deferred &~ Module | Synthetic | extraFlags,
flags = member.flags &~ Deferred &~ Module &~ dropFlags | Synthetic | extraFlags,
info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm
res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot))
res
Expand Down
11 changes: 11 additions & 0 deletions tests/pos/i9886.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class A(l: Any):
def f = l

class B(l: => Any):
def f = l

trait C(l: Any):
def f = l

trait D(l: => Any):
def f = l
31 changes: 31 additions & 0 deletions tests/run/i9886.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class A(l: Any):
def f = l

class B(l: => Any):
def f = l

trait C(l: Any):
def f = l

trait D(l: => Int):
def f = l

object Test extends App:
var x = 0
//object c extends C(x)
object d extends D({ x += 1; println("called"); x })
assert(d.f + d.f == 3, x)


/*

trait D(l: => Int):
def f = l

@main def Test =
var x = 0
val d = new D( { x += 1; x }){}
assert(d.f + d.f == 3)*/