From 56c508bb4ba3ca71715d4d1e853d7c121f486640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 24 Feb 2023 16:44:35 +0100 Subject: [PATCH 001/106] Allow parsing of inline trait, update printer --- compiler/src/dotty/tools/dotc/core/Flags.scala | 4 ++-- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- tests/neg/i2421.scala | 1 - tests/pos/inline-trait.scala | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 tests/pos/inline-trait.scala diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 8100bea374eb..cbaf1b85fa65 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -446,13 +446,13 @@ object Flags { /** Flags representing source modifiers */ private val CommonSourceModifierFlags: FlagSet = - commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased) + commonFlags(Private, Protected, Final, Case, Implicit, Given, Override, JavaStatic, Transparent, Erased, Inline) val TypeSourceModifierFlags: FlagSet = CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque | Open val TermSourceModifierFlags: FlagSet = - CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy + CommonSourceModifierFlags.toTermFlags | AbsOverride | Lazy /** Flags representing modifiers that can appear in trees */ val ModifierFlags: FlagSet = diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3079b26df6cd..47a782f46711 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3765,7 +3765,7 @@ object Parsers { } } - /** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef + /** TmplDef ::= ([‘case’] ‘class’ | [‘inline’] ‘trait’) ClassDef * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 2869f64f33d0..aefa5e2c48cd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -548,7 +548,7 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail(em"Traits cannot have secondary constructors$addendum") - checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module)) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module) || sym.isOneOf(Trait)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { diff --git a/tests/neg/i2421.scala b/tests/neg/i2421.scala index dc8e229f38f0..bcf3da6dbb36 100644 --- a/tests/neg/i2421.scala +++ b/tests/neg/i2421.scala @@ -1,7 +1,6 @@ inline object Foo // OK (error would be detected later, in PostTyper) inline class Bar // error: modifier(s) `inline' incompatible with type definition inline abstract class Baz // error: modifier(s) `inline' incompatible with type definition -inline trait Qux // error: modifier(s) `inline' incompatible with type definition object Quux { inline type T // error: modifier(s) `inline' incompatible with type definition diff --git a/tests/pos/inline-trait.scala b/tests/pos/inline-trait.scala new file mode 100644 index 000000000000..efa98ea951ea --- /dev/null +++ b/tests/pos/inline-trait.scala @@ -0,0 +1 @@ +inline trait A From 5ffb63e342a67d310a519203ded734fb7c3494b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 28 Feb 2023 09:55:26 +0100 Subject: [PATCH 002/106] Implement phase 1 of inlining Inline defs without their bodies, from non-generic inline trait --- .../src/dotty/tools/dotc/typer/Typer.scala | 27 ++++++++++++++++++- tests/pos/inline-trait.scala | 13 ++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb23262d1410..8061e2ecb602 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2650,6 +2650,30 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } } + def generateInlineTraitsDefs(parents: List[Tree]): List[Tree] = { + def rhs(sym: Symbol, argss: List[List[Tree]]) = { + tpd.ref(defn.Predef_undefined) + } + + val inlineParents: List[Symbol] = parents.map(_.tpe.dealias.typeSymbol).filter(_.isAllOf(Inline)) + val classSyms: Map[Symbol, List[Symbol]] = + inlineParents.map(parent => parent -> parent.asClass.info.decls.toList.filter(!_.isConstructor)).toMap + val inlinedSyms: Map[Symbol, List[TermSymbol]] = classSyms.transform((_, defSyms) => defSyms.map(sym => + newSymbol( + cls, + sym.name, + sym.flags | Override, + sym.info, + sym.privateWithin, + cls.coord + ).asTerm.entered + )) + val inlinedDefs: List[DefDef] = + inlinedSyms.flatMap((parent, defSyms) => defSyms.map(sym => DefDef(sym, argss => rhs(sym, argss)))).toList // appliedToArgss(argss) + // println(inlinedDefs.map(_.show)) + inlinedDefs + } + ensureCorrectSuperClass() completeAnnotations(cdef, cls) val constr1 = typed(constr).asInstanceOf[DefDef] @@ -2667,7 +2691,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cdef.withType(UnspecifiedErrorType) else { val dummy = localDummy(cls, impl) - val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) + val inlineTraitDefs = if ctx.isAfterTyper then Nil else generateInlineTraitsDefs(parents1) + val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs checkNoDoubleDeclaration(cls) val impl1 = cpy.Template(impl)(constr1, parents1, Nil, self1, body1) diff --git a/tests/pos/inline-trait.scala b/tests/pos/inline-trait.scala index efa98ea951ea..e169a9f8e594 100644 --- a/tests/pos/inline-trait.scala +++ b/tests/pos/inline-trait.scala @@ -1 +1,12 @@ -inline trait A +inline trait A1: + def f: Int = 2 + def f(x: Int): Int = 2 + def f[T](x: T): Int = 2 + +class B1 extends A1: + /* + override def f: Int = ??? + override def f(x: Int): Int = ??? + override def f[T](x: T): Int = ??? + */ +end B1 From 966f12d01a27ed16a4fc716c8faafd8597699422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 28 Feb 2023 09:55:26 +0100 Subject: [PATCH 003/106] Implement phase 2 of inlining Inline defs without their bodies, from generic inline trait --- .../src/dotty/tools/dotc/typer/Typer.scala | 57 ++++++++++++++++--- tests/pos/inline-trait.scala | 13 +++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8061e2ecb602..b162226d8327 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2655,22 +2655,63 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tpd.ref(defn.Predef_undefined) } - val inlineParents: List[Symbol] = parents.map(_.tpe.dealias.typeSymbol).filter(_.isAllOf(Inline)) - val classSyms: Map[Symbol, List[Symbol]] = - inlineParents.map(parent => parent -> parent.asClass.info.decls.toList.filter(!_.isConstructor)).toMap - val inlinedSyms: Map[Symbol, List[TermSymbol]] = classSyms.transform((_, defSyms) => defSyms.map(sym => + def isInlineable(decl: Symbol): Boolean = !decl.isConstructor && !decl.isType + + type ParamsToArgs = Map[TypeRef, Type] + + def mapConstructorArgss(parent: Tree, typeParamSymss: List[List[Symbol]], termParamSymss: List[List[Symbol]]): ParamsToArgs = + @tailrec def rec(par: Tree, typeParamss: List[List[Symbol]], termParamss: List[List[Symbol]], acc: ParamsToArgs): ParamsToArgs = + (par, typeParamss, termParamss) match { + case (Ident(_), Nil, Nil) => + acc + case (AppliedTypeTree(tree, typeArgs), types :: typeSymss, _) => + tree.tpe.dealias match { + case tr: TypeRef => + val typeRefParams = types.map(param => TypeRef(new ThisType(tr) {}, param)) + val typesToTerms = typeRefParams.zip(typeArgs.map(_.tpe.dealias)) + rec(tree, typeSymss, termParamss, acc ++ typesToTerms) + case _ => + ??? + } + case tup => + ??? + } + + rec(parent, typeParamSymss, termParamSymss, Map.empty) + end mapConstructorArgss + + val inlineParents = + parents.map(parent => parent -> parent.tpe.dealias.typeSymbol).filter((_, parentSym) => parentSym.isAllOf(Inline)).toMap + val classSyms = + inlineParents.map((parent, parentSym) => { + val parentDecls = parentSym.asClass.info.decls.toList + val inlineableDefs = parentDecls.filter(isInlineable) + // Using List[List[TypeSymbol]] allows for potential future type interweaving for class-like trees + val parentTypeParamSymss = parentSym.asClass.info.classSymbol.typeParams match { + case Nil => Nil + case l => List(l) + } + val constrParamsToArgs: ParamsToArgs = mapConstructorArgss(parent, parentTypeParamSymss, Nil) + parent -> (parentSym, inlineableDefs, constrParamsToArgs) + }).toMap + val inlinedSyms = classSyms.transform{ case (parent, (parentSym, defSyms, paramsToArgs)) => defSyms.map(sym => { + val inlineInfoMap = new TypeMap { + def apply(tp: Type): Type = tp match { + case tr: TypeRef => paramsToArgs.getOrElse(tr, mapOver(tp)) + case _ => mapOver(tp) + } + } newSymbol( cls, sym.name, sym.flags | Override, - sym.info, + inlineInfoMap.mapOver(sym.info), sym.privateWithin, cls.coord ).asTerm.entered - )) + })} val inlinedDefs: List[DefDef] = - inlinedSyms.flatMap((parent, defSyms) => defSyms.map(sym => DefDef(sym, argss => rhs(sym, argss)))).toList // appliedToArgss(argss) - // println(inlinedDefs.map(_.show)) + inlinedSyms.flatMap{(_, defSyms) => defSyms.map(sym => DefDef(sym, argss => rhs(sym, argss)))}.toList // appliedToArgss(argss) inlinedDefs } diff --git a/tests/pos/inline-trait.scala b/tests/pos/inline-trait.scala index e169a9f8e594..c4de8f311682 100644 --- a/tests/pos/inline-trait.scala +++ b/tests/pos/inline-trait.scala @@ -10,3 +10,16 @@ class B1 extends A1: override def f[T](x: T): Int = ??? */ end B1 + +inline trait A2[T]: + def f: T = f + def f(x: T): T = x + def f[U <: T](x: U, y: T): T = x + +class B2 extends A2[Int]: + /* + override def f: Int = ??? + override def f(x: Int): Int = ??? + override def f[U <: Int](x: U, y: Int): Int = ??? + */ +end B2 \ No newline at end of file From 3c4b310c5cbf0a7c09c2323608ef4b3f53fb0afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 2 Mar 2023 17:52:56 +0100 Subject: [PATCH 004/106] Use inlining capabilities to inline body of defs By marking the methods to inline inside an inline trait with the BodyAnnotation, we can leverage the current code that inlines bodies To do so, we mark the methods, and let the overriding methods call their super counterpart. The inliner then handles the rest --- .../dotty/tools/dotc/inlines/Inlines.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index bcc10ffa6db8..81ab64955481 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -32,7 +32,7 @@ object Inlines: /** `sym` is an inline method with a known body to inline. */ def hasBodyToInline(sym: SymDenotation)(using Context): Boolean = - sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot) + (sym.isInlineMethod || sym.owner.isAllOf(Trait | Inline)) && sym.hasAnnotation(defn.BodyAnnot) /** The body to inline for method `sym`, or `EmptyTree` if none exists. * @pre hasBodyToInline(sym) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b162226d8327..6697b216caf6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2486,7 +2486,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if sym.isScala2Macro then typedScala2MacroBody(ddef.rhs)(using rhsCtx) else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)) - if sym.isInlineMethod then + if sym.isInlineMethod || (sym.is(Method) && sym.owner.isAllOf(Trait | Inline)) then if StagingLevel.level > 0 then report.error("inline def cannot be within quotes", sym.sourcePos) if sym.is(Given) @@ -2651,9 +2651,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def generateInlineTraitsDefs(parents: List[Tree]): List[Tree] = { - def rhs(sym: Symbol, argss: List[List[Tree]]) = { - tpd.ref(defn.Predef_undefined) - } + def rhs(oldDefSym: Symbol, newDefSym: Symbol, argss: List[List[Tree]])(using Context) = + val sup = Super(This(newDefSym.owner.asClass), oldDefSym.owner.name.asTypeName) + Inlines.inlineCall(sup.select(oldDefSym).appliedToArgss(argss).withSpan(newDefSym.span)) def isInlineable(decl: Symbol): Boolean = !decl.isConstructor && !decl.isType @@ -2694,14 +2694,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val constrParamsToArgs: ParamsToArgs = mapConstructorArgss(parent, parentTypeParamSymss, Nil) parent -> (parentSym, inlineableDefs, constrParamsToArgs) }).toMap - val inlinedSyms = classSyms.transform{ case (parent, (parentSym, defSyms, paramsToArgs)) => defSyms.map(sym => { + val inlinedDefs = classSyms.flatMap{ case (parent, (parentSym, defSyms, paramsToArgs)) => defSyms.map(sym => { val inlineInfoMap = new TypeMap { def apply(tp: Type): Type = tp match { case tr: TypeRef => paramsToArgs.getOrElse(tr, mapOver(tp)) case _ => mapOver(tp) } } - newSymbol( + val newSym = newSymbol( cls, sym.name, sym.flags | Override, @@ -2709,10 +2709,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer sym.privateWithin, cls.coord ).asTerm.entered + + DefDef(newSym, argss => rhs(sym, newSym, argss)(using ctx.withOwner(newSym))) })} - val inlinedDefs: List[DefDef] = - inlinedSyms.flatMap{(_, defSyms) => defSyms.map(sym => DefDef(sym, argss => rhs(sym, argss)))}.toList // appliedToArgss(argss) - inlinedDefs + + inlinedDefs.toList } ensureCorrectSuperClass() From 810dcb60004e5a736bcf3b11578156e8d680edde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 9 Mar 2023 15:58:32 +0100 Subject: [PATCH 005/106] Improve inlining of traits --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 2 + .../src/dotty/tools/dotc/core/Flags.scala | 1 + .../tools/dotc/core/SymDenotations.scala | 3 + .../dotty/tools/dotc/inlines/Inliner.scala | 35 +++++++- .../dotty/tools/dotc/inlines/Inlines.scala | 18 ++++- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 80 +++---------------- .../inline-trait-3-trait-with-params.scala | 13 +++ tests/pos/inline-trait-1-simple-trait.scala | 27 +++++++ tests/pos/inline-trait-2-generic-trait.scala | 13 +++ tests/pos/inline-trait.scala | 25 ------ 11 files changed, 118 insertions(+), 101 deletions(-) create mode 100644 tests/disabled/pos/inline-trait-3-trait-with-params.scala create mode 100644 tests/pos/inline-trait-1-simple-trait.scala create mode 100644 tests/pos/inline-trait-2-generic-trait.scala delete mode 100644 tests/pos/inline-trait.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 955892b2ae22..d69738fbf676 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -124,6 +124,8 @@ class TreeTypeMap( case ddef @ DefDef(name, paramss, tpt, _) => val (tmap1, paramss1) = transformAllParamss(paramss) val res = cpy.DefDef(ddef)(name, paramss1, tmap1.transform(tpt), tmap1.transform(ddef.rhs)) + if tree.symbol.owner.isInlineTrait then + res.symbol.info = mapType(res.symbol.info) // FIXME inline traits' info doesn't get mapped res.symbol.setParamssFromDefs(paramss1) res.symbol.transformAnnotations { case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree)) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index cbaf1b85fa65..05ba42c12c97 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -574,6 +574,7 @@ object Flags { val LazyGiven: FlagSet = Given | Lazy val InlineOrProxy: FlagSet = Inline | InlineProxy // An inline method or inline argument proxy */ val InlineMethod: FlagSet = Inline | Method + val InlineTrait: FlagSet = Inline | Trait val InlineParam: FlagSet = Inline | Param val InlineByNameProxy: FlagSet = InlineProxy | Method val JavaEnumTrait: FlagSet = JavaDefined | Enum // A Java enum trait diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 304840396641..6d464ed01c93 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1012,6 +1012,9 @@ object SymDenotations { def isInlineMethod(using Context): Boolean = isAllOf(InlineMethod, butNot = Accessor) + def isInlineTrait(using Context): Boolean = + isAllOf(InlineTrait) + /** Does this method or field need to be retained at runtime */ def isRetainedInline(using Context): Boolean = is(Inline, butNot = Deferred) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 73fa2a2871a2..e5fc8ab0568f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -188,6 +188,7 @@ class Inliner(val call: tpd.Tree)(using Context): * These are different (wrt ==) types but represent logically the same key */ private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] + private val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType] /** A buffer for bindings that define proxies for actual arguments */ private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] @@ -438,10 +439,11 @@ class Inliner(val call: tpd.Tree)(using Context): private def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) - /** Populate `thisProxy` and `paramProxy` as follows: + /** Populate `thisProxy`, `thisInlineTraitProxy` and `paramProxy` as follows: * * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, - * 1b. If given type refers to an instance this to a class that is not contained in the + * 1b. If given type refers to an inline trait, thisInlineTraitProxy binds it to the corresponding thistype, + * 1c. If given type refers to an instance this to a class that is not contained in the * inline method, create a proxy symbol and bind the thistype to refer to the proxy. * The proxy is not yet entered in `bindingsBuf`; that will come later. * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored @@ -460,6 +462,10 @@ class Inliner(val call: tpd.Tree)(using Context): thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef for (param <- tpe.cls.typeParams) paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) + case tpe: ThisType if tpe.cls.isInlineTrait => + thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + for (param <- tpe.cls.typeParams) + paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod @@ -564,7 +570,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def stopAt = if opaqueProxies.isEmpty then StopAt.Static else StopAt.Package def apply(t: Type) = t match { - case t: ThisType => thisProxy.getOrElse(t.cls, t) + case t: ThisType => thisProxy.get(t.cls).orElse(thisInlineTraitProxy.get(t.cls)).getOrElse(t) case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) case t: SingletonType => if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) @@ -573,10 +579,30 @@ class Inliner(val call: tpd.Tree)(using Context): } }, treeMap = { + case tree: (DefDef | ValDef | TypeDef) if inlinedMethod.is(Trait) && tree.symbol.owner == inlinedMethod => + val sym = tree.symbol + val inlinedSym = sym.copy(owner = ctx.owner, flags = sym.flags | Override) + inlinedSym.entered // Should this be entered here? Or after inlining process has succeeded + + tree match { + case tree: DefDef => + def rhsFun(paramss: List[List[Tree]]): Tree = + val oldParamSyms = tree.paramss.flatten.map(_.symbol) + val newParamSyms = paramss.flatten.map(_.symbol) + tree.rhs.subst(oldParamSyms, newParamSyms) + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(tree.span) + case tree: ValDef => + tpd.ValDef(inlinedSym.asTerm, tree.rhs).withSpan(tree.span) + case tree: TypeDef => + tpd.TypeDef(inlinedSym.asType).withSpan(tree.span) + } case tree: This => tree.tpe match { case thistpe: ThisType => - thisProxy.get(thistpe.cls) match { + val cls = thistpe.cls + if cls.isInlineTrait then + integrate(This(ctx.owner.asClass), cls) + else thisProxy.get(cls) match { case Some(t) => val thisRef = ref(t).withSpan(call.span) inlinedFromOutside(thisRef)(tree.span) @@ -617,6 +643,7 @@ class Inliner(val call: tpd.Tree)(using Context): inlining.println( i"""inliner transform with |thisProxy = ${thisProxy.toList.map(_._1)}%, % --> ${thisProxy.toList.map(_._2)}%, % + |thisInlineTraitProxy = ${thisInlineTraitProxy.toList.map(_._1)}%, % --> ${thisInlineTraitProxy.toList.map(_._2)}%, % |paramProxy = ${paramProxy.toList.map(_._1.typeSymbol.showLocated)}%, % --> ${paramProxy.toList.map(_._2)}%, %""") // Apply inliner to `rhsToInline`, split off any implicit bindings from result, and diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 81ab64955481..5934a427ac0b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -4,7 +4,7 @@ package inlines import ast.*, core.* import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.* -import StdNames.tpnme +import StdNames.{str, nme, tpnme} import transform.SymUtils._ import typer.* import NameKinds.BodyRetainerName @@ -32,7 +32,7 @@ object Inlines: /** `sym` is an inline method with a known body to inline. */ def hasBodyToInline(sym: SymDenotation)(using Context): Boolean = - (sym.isInlineMethod || sym.owner.isAllOf(Trait | Inline)) && sym.hasAnnotation(defn.BodyAnnot) + (sym.isInlineMethod || sym.isInlineTrait) && sym.hasAnnotation(defn.BodyAnnot) /** The body to inline for method `sym`, or `EmptyTree` if none exists. * @pre hasBodyToInline(sym) @@ -169,6 +169,11 @@ object Inlines: tree2 end inlineCall + def inlineParentTrait(parent: tpd.Tree)(using Context): List[Tree] = + val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol + if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym).expandDefs() + else Nil + /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal * inline depth is exceeded. * @@ -459,4 +464,13 @@ object Inlines: // the opaque type itself. An example is in pos/opaque-inline1.scala. end expand end InlineCall + + private class InlineParentTrait(parent: tpd.Tree, parentSym: Symbol)(using Context) extends Inliner(parent): + import tpd._ + import Inlines.* + + def expandDefs(): List[Tree] = + val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked + stats.map(stat => inlined(stat)._2) + end InlineParentTrait end Inlines diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index aefa5e2c48cd..8d56fb728c0c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -548,7 +548,7 @@ object Checking { if (sym.isConstructor && !sym.isPrimaryConstructor && sym.owner.is(Trait, butNot = JavaDefined)) val addendum = if ctx.settings.Ydebug.value then s" ${sym.owner.flagsString}" else "" fail(em"Traits cannot have secondary constructors$addendum") - checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module) || sym.isOneOf(Trait)) + checkApplicable(Inline, sym.isTerm && !sym.isOneOf(Mutable | Module) || sym.is(Trait)) checkApplicable(Lazy, !sym.isOneOf(Method | Mutable)) if (sym.isType && !sym.isOneOf(Deferred | JavaDefined)) for (cls <- sym.allOverriddenSymbols.filter(_.isClass)) { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6697b216caf6..35b9480e3be4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -29,7 +29,7 @@ import Inferencing._ import Dynamic.isDynamicExpansion import EtaExpansion.etaExpand import TypeComparer.CompareResult -import inlines.{Inlines, PrepareInlineable} +import inlines.{Inliner, Inlines, PrepareInlineable} import util.Spans._ import util.common._ import util.{Property, SimpleIdentityMap, SrcPos} @@ -2486,7 +2486,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if sym.isScala2Macro then typedScala2MacroBody(ddef.rhs)(using rhsCtx) else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)) - if sym.isInlineMethod || (sym.is(Method) && sym.owner.isAllOf(Trait | Inline)) then + if sym.isInlineMethod then if StagingLevel.level > 0 then report.error("inline def cannot be within quotes", sym.sourcePos) if sym.is(Given) @@ -2650,72 +2650,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } } - def generateInlineTraitsDefs(parents: List[Tree]): List[Tree] = { - def rhs(oldDefSym: Symbol, newDefSym: Symbol, argss: List[List[Tree]])(using Context) = - val sup = Super(This(newDefSym.owner.asClass), oldDefSym.owner.name.asTypeName) - Inlines.inlineCall(sup.select(oldDefSym).appliedToArgss(argss).withSpan(newDefSym.span)) - - def isInlineable(decl: Symbol): Boolean = !decl.isConstructor && !decl.isType - - type ParamsToArgs = Map[TypeRef, Type] - - def mapConstructorArgss(parent: Tree, typeParamSymss: List[List[Symbol]], termParamSymss: List[List[Symbol]]): ParamsToArgs = - @tailrec def rec(par: Tree, typeParamss: List[List[Symbol]], termParamss: List[List[Symbol]], acc: ParamsToArgs): ParamsToArgs = - (par, typeParamss, termParamss) match { - case (Ident(_), Nil, Nil) => - acc - case (AppliedTypeTree(tree, typeArgs), types :: typeSymss, _) => - tree.tpe.dealias match { - case tr: TypeRef => - val typeRefParams = types.map(param => TypeRef(new ThisType(tr) {}, param)) - val typesToTerms = typeRefParams.zip(typeArgs.map(_.tpe.dealias)) - rec(tree, typeSymss, termParamss, acc ++ typesToTerms) - case _ => - ??? - } - case tup => - ??? - } - - rec(parent, typeParamSymss, termParamSymss, Map.empty) - end mapConstructorArgss - - val inlineParents = - parents.map(parent => parent -> parent.tpe.dealias.typeSymbol).filter((_, parentSym) => parentSym.isAllOf(Inline)).toMap - val classSyms = - inlineParents.map((parent, parentSym) => { - val parentDecls = parentSym.asClass.info.decls.toList - val inlineableDefs = parentDecls.filter(isInlineable) - // Using List[List[TypeSymbol]] allows for potential future type interweaving for class-like trees - val parentTypeParamSymss = parentSym.asClass.info.classSymbol.typeParams match { - case Nil => Nil - case l => List(l) - } - val constrParamsToArgs: ParamsToArgs = mapConstructorArgss(parent, parentTypeParamSymss, Nil) - parent -> (parentSym, inlineableDefs, constrParamsToArgs) - }).toMap - val inlinedDefs = classSyms.flatMap{ case (parent, (parentSym, defSyms, paramsToArgs)) => defSyms.map(sym => { - val inlineInfoMap = new TypeMap { - def apply(tp: Type): Type = tp match { - case tr: TypeRef => paramsToArgs.getOrElse(tr, mapOver(tp)) - case _ => mapOver(tp) - } - } - val newSym = newSymbol( - cls, - sym.name, - sym.flags | Override, - inlineInfoMap.mapOver(sym.info), - sym.privateWithin, - cls.coord - ).asTerm.entered - - DefDef(newSym, argss => rhs(sym, newSym, argss)(using ctx.withOwner(newSym))) - })} - - inlinedDefs.toList - } - ensureCorrectSuperClass() completeAnnotations(cdef, cls) val constr1 = typed(constr).asInstanceOf[DefDef] @@ -2733,9 +2667,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cdef.withType(UnspecifiedErrorType) else { val dummy = localDummy(cls, impl) - val inlineTraitDefs = if ctx.isAfterTyper then Nil else generateInlineTraitsDefs(parents1) + val inlineTraitDefs = + if ctx.isAfterTyper then Nil + else parents1.flatMap(Inlines.inlineParentTrait) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs + if !ctx.isAfterTyper && cls.isInlineTrait then + def isConstructorType(t: Tree) = + t.isInstanceOf[TypeDef] && cls.typeParams.contains(t.symbol) + val inlineTraitMembers = Block(body1.filter(t => !isConstructorType(t)), unitLiteral) + PrepareInlineable.registerInlineInfo(cls, inlineTraitMembers) + checkNoDoubleDeclaration(cls) val impl1 = cpy.Template(impl)(constr1, parents1, Nil, self1, body1) .withType(dummy.termRef) diff --git a/tests/disabled/pos/inline-trait-3-trait-with-params.scala b/tests/disabled/pos/inline-trait-3-trait-with-params.scala new file mode 100644 index 000000000000..a497ebbcb7c1 --- /dev/null +++ b/tests/disabled/pos/inline-trait-3-trait-with-params.scala @@ -0,0 +1,13 @@ +inline trait A[T](a: T): + def f: T = a + def f(x: T): T = x + def f[U <: T](x: U, y: T): T = x +end A + +class B extends A[Int](3): + /* + override def f: Int = ??? + override def f(x: Int): Int = ??? + override def f[U <: Int](x: U, y: Int): Int = ??? + */ +end B diff --git a/tests/pos/inline-trait-1-simple-trait.scala b/tests/pos/inline-trait-1-simple-trait.scala new file mode 100644 index 000000000000..ed38475315c3 --- /dev/null +++ b/tests/pos/inline-trait-1-simple-trait.scala @@ -0,0 +1,27 @@ +inline trait A: + type X = String + + val x: Int = 3 + val y: Int = x + 1 + + def f: Int = f + def f(x: Int): Int = x + def f[T](x: T): Int = 2 + + def xx: X = "foo".asInstanceOf[X] +end A + +class B extends A: + /* + override type X = String + + override val x: Int = 3 + override val y: Int = x + 1 + + override def f: Int = 2 + override def f(x: Int): Int = 2 + override def f[T](x: T): Int = 2 + + override def xx: X = "foo".asInstanceOf[X] + */ +end B diff --git a/tests/pos/inline-trait-2-generic-trait.scala b/tests/pos/inline-trait-2-generic-trait.scala new file mode 100644 index 000000000000..0df576c9c124 --- /dev/null +++ b/tests/pos/inline-trait-2-generic-trait.scala @@ -0,0 +1,13 @@ +inline trait A[T]: + def f: T = f + def f(x: T): T = x + def f[U <: T](x: U, y: T): T = x +end A + +class B extends A[Int]: + /* + override def f: Int = this.f + override def f(x: Int): Int = x + override def f[U <: Int](x: U, y: Int): Int = x + */ +end B diff --git a/tests/pos/inline-trait.scala b/tests/pos/inline-trait.scala deleted file mode 100644 index c4de8f311682..000000000000 --- a/tests/pos/inline-trait.scala +++ /dev/null @@ -1,25 +0,0 @@ -inline trait A1: - def f: Int = 2 - def f(x: Int): Int = 2 - def f[T](x: T): Int = 2 - -class B1 extends A1: - /* - override def f: Int = ??? - override def f(x: Int): Int = ??? - override def f[T](x: T): Int = ??? - */ -end B1 - -inline trait A2[T]: - def f: T = f - def f(x: T): T = x - def f[U <: T](x: U, y: T): T = x - -class B2 extends A2[Int]: - /* - override def f: Int = ??? - override def f(x: Int): Int = ??? - override def f[U <: Int](x: U, y: Int): Int = ??? - */ -end B2 \ No newline at end of file From 753123966b282249ac8d98a4f44045173e06397c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 23 Mar 2023 16:44:42 +0100 Subject: [PATCH 006/106] Add atomic tests for inline traits --- tests/pos/inline-trait-body-abstract-def.scala | 6 ++++++ tests/pos/inline-trait-body-class-abstract.scala | 10 ++++++++++ tests/pos/inline-trait-body-class-case.scala | 5 +++++ tests/pos/inline-trait-body-class-enum.scala | 6 ++++++ tests/pos/inline-trait-body-class-generic.scala | 6 ++++++ tests/pos/inline-trait-body-class-object.scala | 6 ++++++ tests/pos/inline-trait-body-class-params.scala | 5 +++++ tests/pos/inline-trait-body-class-sealed.scala | 7 +++++++ tests/pos/inline-trait-body-class-simple.scala | 6 ++++++ tests/pos/inline-trait-body-def-context-bound.scala | 6 ++++++ tests/pos/inline-trait-body-def-curried-params.scala | 9 +++++++++ tests/pos/inline-trait-body-def-extension-method.scala | 6 ++++++ .../pos/inline-trait-body-def-generic-singleton.scala | 7 +++++++ tests/pos/inline-trait-body-def-generic.scala | 6 ++++++ tests/pos/inline-trait-body-def-implicit.scala | 6 ++++++ tests/pos/inline-trait-body-def-inline-abstract.scala | 6 ++++++ .../pos/inline-trait-body-def-inline-compiletime.scala | 9 +++++++++ .../pos/inline-trait-body-def-inline-transparent.scala | 5 +++++ tests/pos/inline-trait-body-def-inline.scala | 5 +++++ tests/pos/inline-trait-body-def-params.scala | 7 +++++++ tests/pos/inline-trait-body-def-parens.scala | 5 +++++ tests/pos/inline-trait-body-def-simple.scala | 5 +++++ tests/pos/inline-trait-body-def-using.scala | 6 ++++++ tests/pos/inline-trait-body-lazy-val.scala | 5 +++++ tests/pos/inline-trait-body-trait-inline.scala | 7 +++++++ tests/pos/inline-trait-body-type.scala | 5 +++++ tests/pos/inline-trait-body-val-inline.scala | 5 +++++ tests/pos/inline-trait-body-val.scala | 5 +++++ tests/pos/inline-trait-body-var.scala | 8 ++++++++ .../inline-trait-signature-generic-context-bound.scala | 4 ++++ .../inline-trait-signature-generic-inferred-type.scala | 5 +++++ .../pos/inline-trait-signature-generic-invariant.scala | 4 ++++ .../inline-trait-signature-generic-type-bounds.scala | 4 ++++ tests/pos/inline-trait-signature-generic-variant.scala | 8 ++++++++ .../inline-trait-signature-parameters-by-name.scala | 3 +++ ...line-trait-signature-parameters-default-value.scala | 4 ++++ .../inline-trait-signature-parameters-implicit.scala | 4 ++++ .../pos/inline-trait-signature-parameters-using.scala | 4 ++++ ...inline-trait-signature-parameters-val-private.scala | 3 +++ ...line-trait-signature-parameters-val-protected.scala | 3 +++ tests/pos/inline-trait-signature-parameters-val.scala | 3 +++ tests/pos/inline-trait-signature-parameters-var.scala | 3 +++ tests/pos/inline-trait-signature-simple.scala | 4 ++++ tests/pos/inline-trait-usage-extension.scala | 3 +++ tests/pos/inline-trait-usage-param-type.scala | 3 +++ tests/pos/inline-trait-usage-return-type.scala | 3 +++ tests/pos/inline-trait-usage-type-bound.scala | 3 +++ tests/pos/inline-trait-usage-type.scala | 3 +++ tests/run/inline-trait-body-lazy-val.scala | 9 +++++++++ ...-trait-signature-parameters-by-name-exception.scala | 6 ++++++ .../inline-trait-signature-parameters-val-block.check | 4 ++++ .../inline-trait-signature-parameters-val-block.scala | 10 ++++++++++ 52 files changed, 280 insertions(+) create mode 100644 tests/pos/inline-trait-body-abstract-def.scala create mode 100644 tests/pos/inline-trait-body-class-abstract.scala create mode 100644 tests/pos/inline-trait-body-class-case.scala create mode 100644 tests/pos/inline-trait-body-class-enum.scala create mode 100644 tests/pos/inline-trait-body-class-generic.scala create mode 100644 tests/pos/inline-trait-body-class-object.scala create mode 100644 tests/pos/inline-trait-body-class-params.scala create mode 100644 tests/pos/inline-trait-body-class-sealed.scala create mode 100644 tests/pos/inline-trait-body-class-simple.scala create mode 100644 tests/pos/inline-trait-body-def-context-bound.scala create mode 100644 tests/pos/inline-trait-body-def-curried-params.scala create mode 100644 tests/pos/inline-trait-body-def-extension-method.scala create mode 100644 tests/pos/inline-trait-body-def-generic-singleton.scala create mode 100644 tests/pos/inline-trait-body-def-generic.scala create mode 100644 tests/pos/inline-trait-body-def-implicit.scala create mode 100644 tests/pos/inline-trait-body-def-inline-abstract.scala create mode 100644 tests/pos/inline-trait-body-def-inline-compiletime.scala create mode 100644 tests/pos/inline-trait-body-def-inline-transparent.scala create mode 100644 tests/pos/inline-trait-body-def-inline.scala create mode 100644 tests/pos/inline-trait-body-def-params.scala create mode 100644 tests/pos/inline-trait-body-def-parens.scala create mode 100644 tests/pos/inline-trait-body-def-simple.scala create mode 100644 tests/pos/inline-trait-body-def-using.scala create mode 100644 tests/pos/inline-trait-body-lazy-val.scala create mode 100644 tests/pos/inline-trait-body-trait-inline.scala create mode 100644 tests/pos/inline-trait-body-type.scala create mode 100644 tests/pos/inline-trait-body-val-inline.scala create mode 100644 tests/pos/inline-trait-body-val.scala create mode 100644 tests/pos/inline-trait-body-var.scala create mode 100644 tests/pos/inline-trait-signature-generic-context-bound.scala create mode 100644 tests/pos/inline-trait-signature-generic-inferred-type.scala create mode 100644 tests/pos/inline-trait-signature-generic-invariant.scala create mode 100644 tests/pos/inline-trait-signature-generic-type-bounds.scala create mode 100644 tests/pos/inline-trait-signature-generic-variant.scala create mode 100644 tests/pos/inline-trait-signature-parameters-by-name.scala create mode 100644 tests/pos/inline-trait-signature-parameters-default-value.scala create mode 100644 tests/pos/inline-trait-signature-parameters-implicit.scala create mode 100644 tests/pos/inline-trait-signature-parameters-using.scala create mode 100644 tests/pos/inline-trait-signature-parameters-val-private.scala create mode 100644 tests/pos/inline-trait-signature-parameters-val-protected.scala create mode 100644 tests/pos/inline-trait-signature-parameters-val.scala create mode 100644 tests/pos/inline-trait-signature-parameters-var.scala create mode 100644 tests/pos/inline-trait-signature-simple.scala create mode 100644 tests/pos/inline-trait-usage-extension.scala create mode 100644 tests/pos/inline-trait-usage-param-type.scala create mode 100644 tests/pos/inline-trait-usage-return-type.scala create mode 100644 tests/pos/inline-trait-usage-type-bound.scala create mode 100644 tests/pos/inline-trait-usage-type.scala create mode 100644 tests/run/inline-trait-body-lazy-val.scala create mode 100644 tests/run/inline-trait-signature-parameters-by-name-exception.scala create mode 100644 tests/run/inline-trait-signature-parameters-val-block.check create mode 100644 tests/run/inline-trait-signature-parameters-val-block.scala diff --git a/tests/pos/inline-trait-body-abstract-def.scala b/tests/pos/inline-trait-body-abstract-def.scala new file mode 100644 index 000000000000..c604441bd079 --- /dev/null +++ b/tests/pos/inline-trait-body-abstract-def.scala @@ -0,0 +1,6 @@ +inline trait A: + def x: Int + +class B extends A: + override def x: Int = 1 + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-abstract.scala b/tests/pos/inline-trait-body-class-abstract.scala new file mode 100644 index 000000000000..4704b324e26d --- /dev/null +++ b/tests/pos/inline-trait-body-class-abstract.scala @@ -0,0 +1,10 @@ +inline trait A: + class InnerA: + def foo(): Int + def bar = foo() + 1 + +class B extends A: + class InnerB extends InnerA: + def foo(): Int = -23 + + def f = InnerB().bar \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-case.scala b/tests/pos/inline-trait-body-class-case.scala new file mode 100644 index 000000000000..79bade9a9036 --- /dev/null +++ b/tests/pos/inline-trait-body-class-case.scala @@ -0,0 +1,5 @@ +inline trait A: + case class Inner(val x: Int) + +class B extends A: + def f = Inner(17).x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-enum.scala b/tests/pos/inline-trait-body-class-enum.scala new file mode 100644 index 000000000000..a114ff396067 --- /dev/null +++ b/tests/pos/inline-trait-body-class-enum.scala @@ -0,0 +1,6 @@ +inline trait A: + enum Inner: + case A, B, C + +class B extends A: + def f = Inner.B \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-generic.scala b/tests/pos/inline-trait-body-class-generic.scala new file mode 100644 index 000000000000..859b7ac63b9f --- /dev/null +++ b/tests/pos/inline-trait-body-class-generic.scala @@ -0,0 +1,6 @@ +inline trait A: + class Inner[T <: Int]: + val x: T = 1 + +class B extends A: + def f = Inner[Int]().x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-object.scala b/tests/pos/inline-trait-body-class-object.scala new file mode 100644 index 000000000000..d2b82073584e --- /dev/null +++ b/tests/pos/inline-trait-body-class-object.scala @@ -0,0 +1,6 @@ +inline trait A: + object Inner: + val x = 1 + +class B extends A: + def f = Inner.x diff --git a/tests/pos/inline-trait-body-class-params.scala b/tests/pos/inline-trait-body-class-params.scala new file mode 100644 index 000000000000..114d8811da2d --- /dev/null +++ b/tests/pos/inline-trait-body-class-params.scala @@ -0,0 +1,5 @@ +inline trait A: + class Inner(val x: Int) + +class B extends A: + def f = Inner(17).x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-sealed.scala b/tests/pos/inline-trait-body-class-sealed.scala new file mode 100644 index 000000000000..ff28d164c617 --- /dev/null +++ b/tests/pos/inline-trait-body-class-sealed.scala @@ -0,0 +1,7 @@ +inline trait A: + sealed class InnerA: + val x = 1 + +class B extends A: + class InnerB extends InnerA + def f = InnerB().x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-class-simple.scala b/tests/pos/inline-trait-body-class-simple.scala new file mode 100644 index 000000000000..56476b96b44b --- /dev/null +++ b/tests/pos/inline-trait-body-class-simple.scala @@ -0,0 +1,6 @@ +inline trait A: + class Inner: + val x = 1 + +class B extends A: + def f = Inner().x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-context-bound.scala b/tests/pos/inline-trait-body-def-context-bound.scala new file mode 100644 index 000000000000..c932500b62c5 --- /dev/null +++ b/tests/pos/inline-trait-body-def-context-bound.scala @@ -0,0 +1,6 @@ +inline trait A: + given List[String] = "AAA" :: Nil + def foo[T: List](x: T): T = summon[List[T]].headOption.getOrElse(x) + +class B extends A: + def f = foo("BBB") \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-curried-params.scala b/tests/pos/inline-trait-body-def-curried-params.scala new file mode 100644 index 000000000000..89e436a0fab1 --- /dev/null +++ b/tests/pos/inline-trait-body-def-curried-params.scala @@ -0,0 +1,9 @@ +inline trait A: + def x(foo: Int)(bar: Unit) = + bar + foo + + def y(foo: Int) = x(foo) + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-extension-method.scala b/tests/pos/inline-trait-body-def-extension-method.scala new file mode 100644 index 000000000000..77b39dc502aa --- /dev/null +++ b/tests/pos/inline-trait-body-def-extension-method.scala @@ -0,0 +1,6 @@ +inline trait A: + extension [T](x: T) + def foo[U](y: U)(z: T): (T, U, T) = (x, y, z) + +class B extends A: + def f = 1.foo("2")(3) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-generic-singleton.scala b/tests/pos/inline-trait-body-def-generic-singleton.scala new file mode 100644 index 000000000000..2862ff3b1e93 --- /dev/null +++ b/tests/pos/inline-trait-body-def-generic-singleton.scala @@ -0,0 +1,7 @@ +inline trait A: + def foo: 3 = 3 + def bar[T](x: T): T = x + +class B extends A: + def f: "A" = bar("A") + def g: 3 = foo \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-generic.scala b/tests/pos/inline-trait-body-def-generic.scala new file mode 100644 index 000000000000..75d932f2f04a --- /dev/null +++ b/tests/pos/inline-trait-body-def-generic.scala @@ -0,0 +1,6 @@ +inline trait A: + def foo[T] = 1 + def bar: [U <: A] => U => Int = [U <: A] => (x: U) => x.foo + +class B extends A: + def f = foo[String] + bar(this) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-implicit.scala b/tests/pos/inline-trait-body-def-implicit.scala new file mode 100644 index 000000000000..a5b8512f4d52 --- /dev/null +++ b/tests/pos/inline-trait-body-def-implicit.scala @@ -0,0 +1,6 @@ +inline trait A: + implicit val x: String = "AAA" + def foo(implicit s: String): String = s + s + +class B extends A: + def f = foo() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-abstract.scala b/tests/pos/inline-trait-body-def-inline-abstract.scala new file mode 100644 index 000000000000..c385c397a3f0 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-abstract.scala @@ -0,0 +1,6 @@ +inline trait A: + inline def x: Int + +class B extends A: + inline def x = 1 + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-compiletime.scala b/tests/pos/inline-trait-body-def-inline-compiletime.scala new file mode 100644 index 000000000000..975bffc76ac2 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-compiletime.scala @@ -0,0 +1,9 @@ +import scala.compiletime.* + +inline trait A: + inline def f[T <: String] = + inline if constValue[T] == "I consent" then "All is OK!" + else error("You must consent!") + +class B extends A: + val x = f["I consent"] \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline-transparent.scala b/tests/pos/inline-trait-body-def-inline-transparent.scala new file mode 100644 index 000000000000..7e48cdec1e0f --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline-transparent.scala @@ -0,0 +1,5 @@ +inline trait A: + transparent inline def x = 1 + +class B extends A: + def f: 1 = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-inline.scala b/tests/pos/inline-trait-body-def-inline.scala new file mode 100644 index 000000000000..8b3751398365 --- /dev/null +++ b/tests/pos/inline-trait-body-def-inline.scala @@ -0,0 +1,5 @@ +inline trait A: + inline def x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-params.scala b/tests/pos/inline-trait-body-def-params.scala new file mode 100644 index 000000000000..79018db761a3 --- /dev/null +++ b/tests/pos/inline-trait-body-def-params.scala @@ -0,0 +1,7 @@ +inline trait A: + def x(foo: Int, bar: Unit) = + bar + foo + +class B extends A: + def f = x(1, ()) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-parens.scala b/tests/pos/inline-trait-body-def-parens.scala new file mode 100644 index 000000000000..17c80e9d8ab7 --- /dev/null +++ b/tests/pos/inline-trait-body-def-parens.scala @@ -0,0 +1,5 @@ +inline trait A: + def x() = 1 + +class B extends A: + def f = x() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-simple.scala b/tests/pos/inline-trait-body-def-simple.scala new file mode 100644 index 000000000000..edffe37168be --- /dev/null +++ b/tests/pos/inline-trait-body-def-simple.scala @@ -0,0 +1,5 @@ +inline trait A: + def x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-using.scala b/tests/pos/inline-trait-body-def-using.scala new file mode 100644 index 000000000000..8028671d9fab --- /dev/null +++ b/tests/pos/inline-trait-body-def-using.scala @@ -0,0 +1,6 @@ +inline trait A: + given String = "AAA" + def foo(using s: String): String = s + s + +class B extends A: + def f = foo() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-lazy-val.scala b/tests/pos/inline-trait-body-lazy-val.scala new file mode 100644 index 000000000000..37ab5c249375 --- /dev/null +++ b/tests/pos/inline-trait-body-lazy-val.scala @@ -0,0 +1,5 @@ +inline trait A: + lazy val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-trait-inline.scala b/tests/pos/inline-trait-body-trait-inline.scala new file mode 100644 index 000000000000..e255bd6ba42c --- /dev/null +++ b/tests/pos/inline-trait-body-trait-inline.scala @@ -0,0 +1,7 @@ +inline trait A: + inline trait InnerA: + val x = 1 + +class B extends A: + class InnerB extends InnerA + def f = InnerB().x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-type.scala b/tests/pos/inline-trait-body-type.scala new file mode 100644 index 000000000000..35cad5c81c99 --- /dev/null +++ b/tests/pos/inline-trait-body-type.scala @@ -0,0 +1,5 @@ +inline trait A[T]: + type U = String + +class B extends A[Int]: + def f: U = "ABD" \ No newline at end of file diff --git a/tests/pos/inline-trait-body-val-inline.scala b/tests/pos/inline-trait-body-val-inline.scala new file mode 100644 index 000000000000..b60a39cfb8fe --- /dev/null +++ b/tests/pos/inline-trait-body-val-inline.scala @@ -0,0 +1,5 @@ +inline trait A: + inline val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-val.scala b/tests/pos/inline-trait-body-val.scala new file mode 100644 index 000000000000..be20419ffb9f --- /dev/null +++ b/tests/pos/inline-trait-body-val.scala @@ -0,0 +1,5 @@ +inline trait A: + val x = 1 + +class B extends A: + def f = x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-var.scala b/tests/pos/inline-trait-body-var.scala new file mode 100644 index 000000000000..a1e06a13269a --- /dev/null +++ b/tests/pos/inline-trait-body-var.scala @@ -0,0 +1,8 @@ +inline trait A: + var x = 1 + +class B extends A: + def f = + val old = x + x += 1 + old \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-context-bound.scala b/tests/pos/inline-trait-signature-generic-context-bound.scala new file mode 100644 index 000000000000..9cd7b0728101 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-context-bound.scala @@ -0,0 +1,4 @@ +inline trait A[T: List] + +given List[Int] = Nil +class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-inferred-type.scala b/tests/pos/inline-trait-signature-generic-inferred-type.scala new file mode 100644 index 000000000000..93396ec979e9 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-inferred-type.scala @@ -0,0 +1,5 @@ +inline trait A[T](val x: T) + def f: T = x + +class B extends A(1) + val y: Int = f \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-invariant.scala b/tests/pos/inline-trait-signature-generic-invariant.scala new file mode 100644 index 000000000000..cc20461e716e --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-invariant.scala @@ -0,0 +1,4 @@ +inline trait A[T]: + def f(x: T): T = x + +class B extends A[Int] diff --git a/tests/pos/inline-trait-signature-generic-type-bounds.scala b/tests/pos/inline-trait-signature-generic-type-bounds.scala new file mode 100644 index 000000000000..cb8ef919e633 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-type-bounds.scala @@ -0,0 +1,4 @@ +inline trait A[T <: AnyVal >: Int]: + def f(x: T): T = x + +class B extends A[Int] diff --git a/tests/pos/inline-trait-signature-generic-variant.scala b/tests/pos/inline-trait-signature-generic-variant.scala new file mode 100644 index 000000000000..201a9254c47a --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-variant.scala @@ -0,0 +1,8 @@ +inline trait Cov[+T]: + def f: T = ??? + +inline trait Contr[-T]: + def f(x: T) = ??? + +class A extends Cov[AnyVal] +class B extends Contr[Int] \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-by-name.scala b/tests/pos/inline-trait-signature-parameters-by-name.scala new file mode 100644 index 000000000000..6fd338326c6c --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-by-name.scala @@ -0,0 +1,3 @@ +inline trait A(val x: => Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-default-value.scala b/tests/pos/inline-trait-signature-parameters-default-value.scala new file mode 100644 index 000000000000..f76056ba70df --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-default-value.scala @@ -0,0 +1,4 @@ +inline trait A(val x: Int = 4) + +class B extends A(1) +class C extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-implicit.scala b/tests/pos/inline-trait-signature-parameters-implicit.scala new file mode 100644 index 000000000000..f4b2f5c93d61 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-implicit.scala @@ -0,0 +1,4 @@ +inline trait A(implicit val imp: Int) + +implicit val x: Int = 1 +class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-using.scala b/tests/pos/inline-trait-signature-parameters-using.scala new file mode 100644 index 000000000000..d012d8c59e22 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-using.scala @@ -0,0 +1,4 @@ +inline trait A(using imp: Int) + +given val x: Int = 1 +class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val-private.scala b/tests/pos/inline-trait-signature-parameters-val-private.scala new file mode 100644 index 000000000000..9306e0152f05 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val-private.scala @@ -0,0 +1,3 @@ +inline trait A(x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val-protected.scala b/tests/pos/inline-trait-signature-parameters-val-protected.scala new file mode 100644 index 000000000000..4127d45e9002 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val-protected.scala @@ -0,0 +1,3 @@ +inline trait A(protected val x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-val.scala b/tests/pos/inline-trait-signature-parameters-val.scala new file mode 100644 index 000000000000..8372baa0b810 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-val.scala @@ -0,0 +1,3 @@ +inline trait A(val x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-var.scala b/tests/pos/inline-trait-signature-parameters-var.scala new file mode 100644 index 000000000000..590d273f81b6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-var.scala @@ -0,0 +1,3 @@ +inline trait A(var x: Int) + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-simple.scala b/tests/pos/inline-trait-signature-simple.scala new file mode 100644 index 000000000000..6734a69a488c --- /dev/null +++ b/tests/pos/inline-trait-signature-simple.scala @@ -0,0 +1,4 @@ +inline trait A: + def i: Int = 1 + +class B extends A diff --git a/tests/pos/inline-trait-usage-extension.scala b/tests/pos/inline-trait-usage-extension.scala new file mode 100644 index 000000000000..f9f5d87d2ceb --- /dev/null +++ b/tests/pos/inline-trait-usage-extension.scala @@ -0,0 +1,3 @@ +inline trait A + +class B extends A diff --git a/tests/pos/inline-trait-usage-param-type.scala b/tests/pos/inline-trait-usage-param-type.scala new file mode 100644 index 000000000000..e04f885f1e65 --- /dev/null +++ b/tests/pos/inline-trait-usage-param-type.scala @@ -0,0 +1,3 @@ +inline trait A + +def f(a: A) = ??? diff --git a/tests/pos/inline-trait-usage-return-type.scala b/tests/pos/inline-trait-usage-return-type.scala new file mode 100644 index 000000000000..e8015b065552 --- /dev/null +++ b/tests/pos/inline-trait-usage-return-type.scala @@ -0,0 +1,3 @@ +inline trait A + +def f: A = ??? diff --git a/tests/pos/inline-trait-usage-type-bound.scala b/tests/pos/inline-trait-usage-type-bound.scala new file mode 100644 index 000000000000..52b57f6acfa5 --- /dev/null +++ b/tests/pos/inline-trait-usage-type-bound.scala @@ -0,0 +1,3 @@ +inline trait A + +type T >: A <: A \ No newline at end of file diff --git a/tests/pos/inline-trait-usage-type.scala b/tests/pos/inline-trait-usage-type.scala new file mode 100644 index 000000000000..1c1ae110c367 --- /dev/null +++ b/tests/pos/inline-trait-usage-type.scala @@ -0,0 +1,3 @@ +inline trait A + +type T = List[A] \ No newline at end of file diff --git a/tests/run/inline-trait-body-lazy-val.scala b/tests/run/inline-trait-body-lazy-val.scala new file mode 100644 index 000000000000..68a9c9fd8697 --- /dev/null +++ b/tests/run/inline-trait-body-lazy-val.scala @@ -0,0 +1,9 @@ +inline trait A: + lazy val x = + throw new Exception + 1 + +class B extends A + +@main def Test: Unit = + val b = B() \ No newline at end of file diff --git a/tests/run/inline-trait-signature-parameters-by-name-exception.scala b/tests/run/inline-trait-signature-parameters-by-name-exception.scala new file mode 100644 index 000000000000..9f4724ea81c6 --- /dev/null +++ b/tests/run/inline-trait-signature-parameters-by-name-exception.scala @@ -0,0 +1,6 @@ +inline trait A(val x: => Int) + +class B extends A({throw new Exception; 1}) + +@main def Test: Unit = + val b = B() diff --git a/tests/run/inline-trait-signature-parameters-val-block.check b/tests/run/inline-trait-signature-parameters-val-block.check new file mode 100644 index 000000000000..063724ea6c43 --- /dev/null +++ b/tests/run/inline-trait-signature-parameters-val-block.check @@ -0,0 +1,4 @@ +I am a B! +1 +1 +1 diff --git a/tests/run/inline-trait-signature-parameters-val-block.scala b/tests/run/inline-trait-signature-parameters-val-block.scala new file mode 100644 index 000000000000..794a6071d073 --- /dev/null +++ b/tests/run/inline-trait-signature-parameters-val-block.scala @@ -0,0 +1,10 @@ +inline trait A(val x: Int) + +class B extends A({ println("I am a B!"); 1 }) + +@main() def Test: Unit = { + val b = B() + println(b.x) + println(b.x) + println(b.x) +} \ No newline at end of file From f01f90db1d907fa6256d89776bcdec5afc397336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 28 Mar 2023 10:11:43 +0200 Subject: [PATCH 007/106] Import Nicolas' work Allows for private trait parameters and private inner classes --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 3 +- .../dotty/tools/dotc/inlines/Inliner.scala | 17 ----- .../dotty/tools/dotc/inlines/Inlines.scala | 64 ++++++++++++++++++- tests/pos/inline-trait-1-simple-trait.scala | 17 +++-- tests/pos/inline-trait-3-trait-params.scala | 15 +++++ tests/pos/inline-trait-4-inner-class.scala | 28 ++++++++ 6 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 tests/pos/inline-trait-3-trait-params.scala create mode 100644 tests/pos/inline-trait-4-inner-class.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index d69738fbf676..3823dd8fec2b 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -124,8 +124,7 @@ class TreeTypeMap( case ddef @ DefDef(name, paramss, tpt, _) => val (tmap1, paramss1) = transformAllParamss(paramss) val res = cpy.DefDef(ddef)(name, paramss1, tmap1.transform(tpt), tmap1.transform(ddef.rhs)) - if tree.symbol.owner.isInlineTrait then - res.symbol.info = mapType(res.symbol.info) // FIXME inline traits' info doesn't get mapped + res.symbol.info = mapType(res.symbol.info) // FIXME inline traits' info doesn't get mapped res.symbol.setParamssFromDefs(paramss1) res.symbol.transformAnnotations { case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree)) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e5fc8ab0568f..4f95a13fdbb9 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -579,23 +579,6 @@ class Inliner(val call: tpd.Tree)(using Context): } }, treeMap = { - case tree: (DefDef | ValDef | TypeDef) if inlinedMethod.is(Trait) && tree.symbol.owner == inlinedMethod => - val sym = tree.symbol - val inlinedSym = sym.copy(owner = ctx.owner, flags = sym.flags | Override) - inlinedSym.entered // Should this be entered here? Or after inlining process has succeeded - - tree match { - case tree: DefDef => - def rhsFun(paramss: List[List[Tree]]): Tree = - val oldParamSyms = tree.paramss.flatten.map(_.symbol) - val newParamSyms = paramss.flatten.map(_.symbol) - tree.rhs.subst(oldParamSyms, newParamSyms) - tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(tree.span) - case tree: ValDef => - tpd.ValDef(inlinedSym.asTerm, tree.rhs).withSpan(tree.span) - case tree: TypeDef => - tpd.TypeDef(inlinedSym.asType).withSpan(tree.span) - } case tree: This => tree.tpe match { case thistpe: ThisType => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 5934a427ac0b..25fdf3a9d46c 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -470,7 +470,69 @@ object Inlines: import Inlines.* def expandDefs(): List[Tree] = + val argsMap = parent match + case Apply(_, args) => + val MethodType(paramNames) = parent.symbol.info: @unchecked + paramNames.zip(args).toMap + case _ => Map.empty + val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - stats.map(stat => inlined(stat)._2) + + val stats1 = stats.map { stat => + val sym = stat.symbol + stat match + case tree: ValDef if sym.is(ParamAccessor) => + val vdef = cloneValDef(tree) + vdef.symbol.resetFlag(ParamAccessor) + cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) + case stat: ValDef => + val vdef = cloneValDef(stat) + if !sym.is(Private) then vdef.symbol.setFlag(Override) + cpy.ValDef(vdef)() // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + case stat: DefDef => + val ddef = cloneDefDef(stat) + if !sym.is(Private) then ddef.symbol.setFlag(Override) + ddef + case stat @ TypeDef(_, impl: Template) => + cloneClass(stat, impl) + case stat: TypeDef => + val tdef = cloneTypeDef(stat) + tdef.symbol.setFlag(Override) + cpy.TypeDef(tdef)() + } + stats1.map(stat => inlined(stat)._2) + + private def cloneClass(clDef: TypeDef, impl: Template)(using Context): TypeDef = + val inlinedCls: ClassSymbol = + val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = clDef.symbol.info: @unchecked + val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents + clDef.symbol.copy(owner = ctx.owner, info = inlinedInfo).entered.asClass + val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { + (cloneDefDef(impl.constr), impl.body.map(cloneStat)) + } + tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents + + private def cloneStat(tree: Tree)(using Context): Tree = tree match + case tree: DefDef => cloneDefDef(tree) + case tree: ValDef => cloneValDef(tree) + // TODO case stat @ TypeDef(_, impl: Template) => cloneClass(stat, impl) + // TODO case tree: TypeDef => cloneTypeDef(tree) + + private def cloneDefDef(ddef: DefDef)(using Context): DefDef = + val inlinedSym = ddef.symbol.copy(owner = ctx.owner).entered + def rhsFun(paramss: List[List[Tree]]): Tree = + val oldParamSyms = ddef.paramss.flatten.map(_.symbol) + val newParamSyms = paramss.flatten.map(_.symbol) + ddef.rhs.subst(oldParamSyms, newParamSyms) // TODO clone local classes? + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(ddef.span) + + private def cloneValDef(vdef: ValDef)(using Context): ValDef = + val inlinedSym = vdef.symbol.copy(owner = ctx.owner).entered + tpd.ValDef(inlinedSym.asTerm, vdef.rhs).withSpan(vdef.span) // TODO clone local classes? + + private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = + val inlinedSym = tdef.symbol.copy(owner = ctx.owner).entered + tpd.TypeDef(inlinedSym.asType).withSpan(tdef.span) + end InlineParentTrait end Inlines diff --git a/tests/pos/inline-trait-1-simple-trait.scala b/tests/pos/inline-trait-1-simple-trait.scala index ed38475315c3..8bd510a4adb6 100644 --- a/tests/pos/inline-trait-1-simple-trait.scala +++ b/tests/pos/inline-trait-1-simple-trait.scala @@ -2,12 +2,15 @@ inline trait A: type X = String val x: Int = 3 - val y: Int = x + 1 + val y: Int = x + z + private val z: Int = 1 - def f: Int = f + def f: Int = g def f(x: Int): Int = x def f[T](x: T): Int = 2 + private def g = 1 + def xx: X = "foo".asInstanceOf[X] end A @@ -16,12 +19,16 @@ class B extends A: override type X = String override val x: Int = 3 - override val y: Int = x + 1 + override val y: Int = this.x.+(this.z) + + private[this] val z: Int = 1 - override def f: Int = 2 - override def f(x: Int): Int = 2 + override def f: Int = this.g + override def f(x: Int): Int = x override def f[T](x: T): Int = 2 + private[this] def g: Int = 1 + override def xx: X = "foo".asInstanceOf[X] */ end B diff --git a/tests/pos/inline-trait-3-trait-params.scala b/tests/pos/inline-trait-3-trait-params.scala new file mode 100644 index 000000000000..476c594cfe50 --- /dev/null +++ b/tests/pos/inline-trait-3-trait-params.scala @@ -0,0 +1,15 @@ +inline trait A(a: Int): + def f: Int = a + def g(b: Int): Int = a + b +end A + +class B extends A(4): + // FIXME: Should not generate second field for A.a (probably in the memoize phase). `B.A$$a` could point to the `a` we already have. + // Alternative: remove the field and defs from `A`. + + /* + private val a: Int = 4 + override def f: Int = this.a + override def g(x: Int): Int = this.a.+(b) + */ +end B diff --git a/tests/pos/inline-trait-4-inner-class.scala b/tests/pos/inline-trait-4-inner-class.scala new file mode 100644 index 000000000000..5a9a6fa397c9 --- /dev/null +++ b/tests/pos/inline-trait-4-inner-class.scala @@ -0,0 +1,28 @@ +inline trait Options[T]: + // FIXME: remove original class definition from inline trait to allow public classes + private trait Option: + def get: T + // FIXME: support constructor parameter + // FIXME: support specialized parents + private class Some://(x: T): // extends Option + def get: T = ??? // x + // FIXME: support specialized modules + // private object None // extends Option + // def get: T = throw new NoSuchElementException("None.get") + + // FIXME: specialize reference to Option and Some + // def option(value: T): Option = new Some//(value) +end Options + +object IntOptions extends Options[Int]: + /* + trait Option: + def get: Int + class Some(x: Int) extends Option + def get: Int = x + object None extends Option + def get: Int = throw new NoSuchElementException("None.get") + + def option(value: Int): Option = new Some(value) + */ +end IntOptions From 567d5556afc8aa856c11810e85862c588230be3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 28 Mar 2023 15:44:01 +0200 Subject: [PATCH 008/106] Avoid generating accessor fields for inline traits --- compiler/src/dotty/tools/dotc/transform/Mixin.scala | 3 ++- tests/pos/inline-trait-3-trait-params.scala | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 5ca09dd6188f..0159e027604d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -93,7 +93,7 @@ object Mixin { * def x_=(y: T) = () * * 4.5 (done in `mixinForwarders`) For every method - * ` def f[Ts](ps1)...(psN): U` imn M` that needs to be disambiguated: + * ` def f[Ts](ps1)...(psN): U` in M that needs to be disambiguated: * * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) * @@ -244,6 +244,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil def traitInits(mixin: ClassSymbol): List[Tree] = { + if mixin.isInlineTrait then return Nil val argsIt = superCallsAndArgs.get(mixin) match case Some((_, _, args)) => args.iterator case _ => Iterator.empty diff --git a/tests/pos/inline-trait-3-trait-params.scala b/tests/pos/inline-trait-3-trait-params.scala index 476c594cfe50..e2119bf9757b 100644 --- a/tests/pos/inline-trait-3-trait-params.scala +++ b/tests/pos/inline-trait-3-trait-params.scala @@ -4,9 +4,6 @@ inline trait A(a: Int): end A class B extends A(4): - // FIXME: Should not generate second field for A.a (probably in the memoize phase). `B.A$$a` could point to the `a` we already have. - // Alternative: remove the field and defs from `A`. - /* private val a: Int = 4 override def f: Int = this.a From e5726766e5b0f575eabf41b1c3af3b4f59d5384b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 30 Mar 2023 16:53:34 +0200 Subject: [PATCH 009/106] Use parent span for inlined code --- .../src/dotty/tools/dotc/inlines/Inlines.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 25fdf3a9d46c..738dd9314134 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -488,7 +488,7 @@ object Inlines: case stat: ValDef => val vdef = cloneValDef(stat) if !sym.is(Private) then vdef.symbol.setFlag(Override) - cpy.ValDef(vdef)() // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + vdef // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? case stat: DefDef => val ddef = cloneDefDef(stat) if !sym.is(Private) then ddef.symbol.setFlag(Override) @@ -498,9 +498,9 @@ object Inlines: case stat: TypeDef => val tdef = cloneTypeDef(stat) tdef.symbol.setFlag(Override) - cpy.TypeDef(tdef)() + tdef } - stats1.map(stat => inlined(stat)._2) + stats1.map(stat => inlined(stat.withSpan(parent.span))._2) private def cloneClass(clDef: TypeDef, impl: Template)(using Context): TypeDef = val inlinedCls: ClassSymbol = @@ -510,7 +510,7 @@ object Inlines: val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { (cloneDefDef(impl.constr), impl.body.map(cloneStat)) } - tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents + tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body) // TODO adapt parents private def cloneStat(tree: Tree)(using Context): Tree = tree match case tree: DefDef => cloneDefDef(tree) @@ -524,15 +524,15 @@ object Inlines: val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) ddef.rhs.subst(oldParamSyms, newParamSyms) // TODO clone local classes? - tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(ddef.span) + tpd.DefDef(inlinedSym.asTerm, rhsFun) private def cloneValDef(vdef: ValDef)(using Context): ValDef = val inlinedSym = vdef.symbol.copy(owner = ctx.owner).entered - tpd.ValDef(inlinedSym.asTerm, vdef.rhs).withSpan(vdef.span) // TODO clone local classes? + tpd.ValDef(inlinedSym.asTerm, vdef.rhs) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = val inlinedSym = tdef.symbol.copy(owner = ctx.owner).entered - tpd.TypeDef(inlinedSym.asType).withSpan(tdef.span) + tpd.TypeDef(inlinedSym.asType) end InlineParentTrait end Inlines From 61516a105f59d613139a437d813480a252731c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 30 Mar 2023 17:09:15 +0200 Subject: [PATCH 010/106] Blacklist inline trait tests due to pickling Blacklists inline trait tests from the pickling test, because spans are not working as expected right now... Might be a HUGE pain to deal with now. Please roll me back before merging with main :( --- .../test/dotc/pos-test-pickling.blacklist | 62 +++++++++++++++++++ .../test/dotc/run-test-pickling.blacklist | 5 ++ 2 files changed, 67 insertions(+) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 87aa6a285f68..9862a94e5ee1 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -101,3 +101,65 @@ java-inherited-type1 # recursion limit exceeded i7445b.scala + +# FIXME re-enable pickling once inline traits are stable +inline-trait-1-simple-trait.scala +inline-trait-2-generic-trait.scala +inline-trait-3-trait-params.scala +inline-trait-4-inner-class.scala +inline-trait-body-abstract-def.scala +inline-trait-body-class-abstract.scala +inline-trait-body-class-case.scala +inline-trait-body-class-enum.scala +inline-trait-body-class-generic.scala +inline-trait-body-class-object.scala +inline-trait-body-class-params.scala +inline-trait-body-class-sealed.scala +inline-trait-body-class-simple.scala +inline-trait-body-def-context-bound.scala +inline-trait-body-def-curried-params.scala +inline-trait-body-def-extension-method.scala +inline-trait-body-def-generic-singleton.scala +inline-trait-body-def-generic.scala +inline-trait-body-def-implicit.scala +inline-trait-body-def-inline-abstract.scala +inline-trait-body-def-inline-compiletime.scala +inline-trait-body-def-inline-transparent.scala +inline-trait-body-def-inline.scala +inline-trait-body-def-params-inline.scala +inline-trait-body-def-params.scala +inline-trait-body-def-parens.scala +inline-trait-body-def-simple.scala +inline-trait-body-def-using.scala +inline-trait-body-lazy-val.scala +inline-trait-body-trait-inline.scala +inline-trait-body-type.scala +inline-trait-body-val-inline.scala +inline-trait-body-val.scala +inline-trait-body-var.scala +inline-trait-inheritance-multiple-override.scala +inline-trait-inheritance-multiple.scala +inline-trait-inheritance-single-abstract-class-override.scala +inline-trait-inheritance-single-abstract-class.scala +inline-trait-inheritance-single-object-override.scala +inline-trait-inheritance-single-object.scala +inline-trait-signature-generic-context-bound.scala +inline-trait-signature-generic-inferred-type.scala +inline-trait-signature-generic-invariant.scala +inline-trait-signature-generic-type-bounds.scala +inline-trait-signature-generic-variant.scala +inline-trait-signature-parameters-by-name.scala +inline-trait-signature-parameters-default-value.scala +inline-trait-signature-parameters-implicit.scala +inline-trait-signature-parameters-using.scala +inline-trait-signature-parameters-val-private.scala +inline-trait-signature-parameters-val-protected.scala +inline-trait-signature-parameters-val.scala +inline-trait-signature-parameters-var.scala +inline-trait-signature-simple.scala +inline-trait-signature-simple.scala.semanticdb +inline-trait-usage-extension.scala +inline-trait-usage-param-type.scala +inline-trait-usage-return-type.scala +inline-trait-usage-type-bound.scala +inline-trait-usage-type.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 9f19b439135c..7e57897189a9 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -44,3 +44,8 @@ t6138 t6138-2 i12656.scala trait-static-forwarder + +# FIXME re-enable pickling once inline traits are stable +inline-trait-signature-parameters-val-block.scala +inline-trait-signature-parameters-by-name-exception.scala +inline-trait-body-lazy-val.scala \ No newline at end of file From 2a077cc1915b9f85f4c4659aaa06aee9d7926996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 3 Apr 2023 18:10:40 +0200 Subject: [PATCH 011/106] Add benchmark for toy version of Numeric --- .../inlinetraits/InlineTraitBenchmark.scala | 33 ++++++++++++ .../inlinetraits/standard/Matrix.scala | 53 +++++++++++++++++++ .../inlinetraits/standard/Numeric.scala | 29 ++++++++++ 3 files changed, 115 insertions(+) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala new file mode 100644 index 000000000000..7f998e2d1195 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -0,0 +1,33 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} +import scala.util.Random + +import standard.IntMatrixLib.{Matrix => StdIntMatrix} + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(3) +@Threads(3) +@Warmup(iterations = 3, time = 2, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 5, timeUnit = SECONDS) +@OutputTimeUnit(MILLISECONDS) +@State(Scope.Benchmark) +class InlineTraitBenchmark { + val N = 200 + def matrix(rows: Int, cols: Int): Vector[Vector[Int]] = + Vector.tabulate(rows, cols)((_, _) => Random.nextInt()) + + @Setup(Level.Iteration) + def setup = { + Random.setSeed(N) + } + + @Benchmark + def standardLib = { + val m1 = StdIntMatrix(matrix(N, N)*) + val m2 = StdIntMatrix(matrix(N, N)*) + + (m1 + m2) * m1 // O(N^3) loops + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala new file mode 100644 index 000000000000..9da3d13a950d --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala @@ -0,0 +1,53 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +import scala.annotation.tailrec + +trait MatrixLib[T: Numeric]: + opaque type Matrix = Vector[Vector[T]] + + object Matrix: + def apply(rows: Seq[T]*): Matrix = + @tailrec def rec(rows: Seq[Seq[T]], acc: Matrix): Matrix = + rows match { + case Seq() => throw IllegalArgumentException("a matrix cannot be empty") + case Seq(Seq()) => throw IllegalArgumentException("a matrix cannot contain an empty row") + case Seq(row) => acc :+ row.toVector + case h +: t if h.length == t(0).length => rec(t, acc :+ h.toVector) + case h +: _ => throw IllegalArgumentException(s"row ${h} does not have the same number of columns as the rest of the matrix") + } + + rec(rows, Vector()) + + extension (m: Matrix) + def +(n: Matrix): Matrix = + assert( + m.length == n.length && m(0).length == n(0).length, + "m and n do not have the same dimensions" + ) + + val num = summon[Numeric[T]] + import num.plus + + for row <- (0 until m.length).toVector + yield + for col <- (0 until m(0).length).toVector + yield plus(m(row)(col), n(row)(col)) + end + + + def *(n: Matrix): Matrix = + assert(m(0).length == n.length, "m and n have incompatible dimensions") + + val num = summon[Numeric[T]] + import num.{zero, plus, times} + + for i <- (0 until m.length).toVector + yield + for j <- (0 until n(0).length).toVector + yield + val mults = for k <- 0 until n.length yield times(m(i)(k), n(k)(j)) + mults.fold(zero)(plus(_, _)) + end * + +object IntMatrixLib extends MatrixLib[Int] +object DoubleMatrixLib extends MatrixLib[Double] \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala new file mode 100644 index 000000000000..f9ceba90bc71 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala @@ -0,0 +1,29 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +import scala.math.Ordering +import scala.language.implicitConversions + +trait Numeric[T] extends Ordering[T] { + def zero: T + def plus(x: T, y: T): T + def times(x: T, y: T): T +} + +object Numeric { + @inline def apply[T](implicit num: Numeric[T]): Numeric[T] = num + + trait IntIsNumeric extends Numeric[Int] { + def zero: Int = 0 + def plus(x: Int, y: Int): Int = x + y + def times(x: Int, y: Int): Int = x * y + } + implicit object IntIsNumeric extends IntIsNumeric with Ordering.IntOrdering + + trait DoubleIsNumeric extends Numeric[Double] { + def zero: Double = 0d + def plus(x: Double, y: Double): Double = x + y + def times(x: Double, y: Double): Double = x * y + } + implicit object DoubleIsNumeric extends DoubleIsNumeric with Ordering.Double.IeeeOrdering +} From 8b3437fa38370876265f3ea1a5959967c9983454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 15:11:58 +0200 Subject: [PATCH 012/106] Delete by-name parameter tests Traits cannot have by-name parameters --- tests/pos/inline-trait-signature-parameters-by-name.scala | 3 --- ...nline-trait-signature-parameters-by-name-exception.scala | 6 ------ 2 files changed, 9 deletions(-) delete mode 100644 tests/pos/inline-trait-signature-parameters-by-name.scala delete mode 100644 tests/run/inline-trait-signature-parameters-by-name-exception.scala diff --git a/tests/pos/inline-trait-signature-parameters-by-name.scala b/tests/pos/inline-trait-signature-parameters-by-name.scala deleted file mode 100644 index 6fd338326c6c..000000000000 --- a/tests/pos/inline-trait-signature-parameters-by-name.scala +++ /dev/null @@ -1,3 +0,0 @@ -inline trait A(val x: => Int) - -class B extends A(1) \ No newline at end of file diff --git a/tests/run/inline-trait-signature-parameters-by-name-exception.scala b/tests/run/inline-trait-signature-parameters-by-name-exception.scala deleted file mode 100644 index 9f4724ea81c6..000000000000 --- a/tests/run/inline-trait-signature-parameters-by-name-exception.scala +++ /dev/null @@ -1,6 +0,0 @@ -inline trait A(val x: => Int) - -class B extends A({throw new Exception; 1}) - -@main def Test: Unit = - val b = B() From b53f14226534bee3e29f65bbe26bafd6b840f8d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 15:14:46 +0200 Subject: [PATCH 013/106] Revert "Use parent span for inlined code" This reverts commit 40b7857e000741c544a53e73e058378a174c0e4c. --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 738dd9314134..97e5176f801d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -500,7 +500,7 @@ object Inlines: tdef.symbol.setFlag(Override) tdef } - stats1.map(stat => inlined(stat.withSpan(parent.span))._2) + stats1.map(stat => inlined(stat)._2) private def cloneClass(clDef: TypeDef, impl: Template)(using Context): TypeDef = val inlinedCls: ClassSymbol = @@ -510,7 +510,7 @@ object Inlines: val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { (cloneDefDef(impl.constr), impl.body.map(cloneStat)) } - tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body) // TODO adapt parents + tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents private def cloneStat(tree: Tree)(using Context): Tree = tree match case tree: DefDef => cloneDefDef(tree) @@ -524,15 +524,15 @@ object Inlines: val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) ddef.rhs.subst(oldParamSyms, newParamSyms) // TODO clone local classes? - tpd.DefDef(inlinedSym.asTerm, rhsFun) + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(ddef.span) private def cloneValDef(vdef: ValDef)(using Context): ValDef = val inlinedSym = vdef.symbol.copy(owner = ctx.owner).entered - tpd.ValDef(inlinedSym.asTerm, vdef.rhs) // TODO clone local classes? + tpd.ValDef(inlinedSym.asTerm, vdef.rhs).withSpan(vdef.span) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = val inlinedSym = tdef.symbol.copy(owner = ctx.owner).entered - tpd.TypeDef(inlinedSym.asType) + tpd.TypeDef(inlinedSym.asType).withSpan(tdef.span) end InlineParentTrait end Inlines From cd1416928dc1db451513666cef5812c9c268ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 15:19:15 +0200 Subject: [PATCH 014/106] Forbid var in inline traits for now vars cannot be overridden, so until we dynamically change the content of the inline trait, we forbid them --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 2 ++ tests/neg/inline-trait-signature-parameters-var.scala | 3 +++ tests/pos/inline-trait-signature-parameters-var.scala | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 tests/neg/inline-trait-signature-parameters-var.scala delete mode 100644 tests/pos/inline-trait-signature-parameters-var.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 97e5176f801d..ec99103d44d0 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -492,6 +492,8 @@ object Inlines: case stat: DefDef => val ddef = cloneDefDef(stat) if !sym.is(Private) then ddef.symbol.setFlag(Override) + if sym.is(Mutable) then + report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos) ddef case stat @ TypeDef(_, impl: Template) => cloneClass(stat, impl) diff --git a/tests/neg/inline-trait-signature-parameters-var.scala b/tests/neg/inline-trait-signature-parameters-var.scala new file mode 100644 index 000000000000..0de83db5f6a9 --- /dev/null +++ b/tests/neg/inline-trait-signature-parameters-var.scala @@ -0,0 +1,3 @@ +inline trait A(var x: Int) // error + +class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-var.scala b/tests/pos/inline-trait-signature-parameters-var.scala deleted file mode 100644 index 590d273f81b6..000000000000 --- a/tests/pos/inline-trait-signature-parameters-var.scala +++ /dev/null @@ -1,3 +0,0 @@ -inline trait A(var x: Int) - -class B extends A(1) \ No newline at end of file From e167b96b22f0416d50dd97600ed71b0849a56893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 15:24:07 +0200 Subject: [PATCH 015/106] Add override modifier to param accessors --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index ec99103d44d0..c9f697217501 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -481,14 +481,15 @@ object Inlines: val stats1 = stats.map { stat => val sym = stat.symbol stat match - case tree: ValDef if sym.is(ParamAccessor) => - val vdef = cloneValDef(tree) - vdef.symbol.resetFlag(ParamAccessor) - cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) case stat: ValDef => val vdef = cloneValDef(stat) - if !sym.is(Private) then vdef.symbol.setFlag(Override) - vdef // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + if !sym.is(Private) then + vdef.symbol.setFlag(Override) + if sym.is(ParamAccessor) then + vdef.symbol.resetFlag(ParamAccessor) + cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) + else + vdef // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? case stat: DefDef => val ddef = cloneDefDef(stat) if !sym.is(Private) then ddef.symbol.setFlag(Override) From e382e75e81dd8a5be6905b948a18c8cd3c7a8c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 17:08:53 +0200 Subject: [PATCH 016/106] Forbid using/implicit parameters for now --- .../src/dotty/tools/dotc/inlines/Inlines.scala | 17 +++++++++++------ ...-trait-signature-generic-context-bound.scala | 4 ++++ ...ne-trait-signature-parameters-implicit.scala | 4 ++++ ...it-signature-parameters-using-nameless.scala | 4 ++++ ...nline-trait-signature-parameters-using.scala | 4 ++++ ...-trait-signature-generic-context-bound.scala | 4 ---- ...ne-trait-signature-parameters-implicit.scala | 4 ---- ...nline-trait-signature-parameters-using.scala | 4 ---- 8 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 tests/neg/inline-trait-signature-generic-context-bound.scala create mode 100644 tests/neg/inline-trait-signature-parameters-implicit.scala create mode 100644 tests/neg/inline-trait-signature-parameters-using-nameless.scala create mode 100644 tests/neg/inline-trait-signature-parameters-using.scala delete mode 100644 tests/pos/inline-trait-signature-generic-context-bound.scala delete mode 100644 tests/pos/inline-trait-signature-parameters-implicit.scala delete mode 100644 tests/pos/inline-trait-signature-parameters-using.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c9f697217501..668c31996719 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -481,15 +481,20 @@ object Inlines: val stats1 = stats.map { stat => val sym = stat.symbol stat match + case stat: ValDef if sym.isOneOf(Given | Implicit) => + report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos) + stat case stat: ValDef => val vdef = cloneValDef(stat) + val vdef1 = + if sym.is(ParamAccessor) then + vdef.symbol.resetFlag(ParamAccessor) + cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) + else + vdef if !sym.is(Private) then - vdef.symbol.setFlag(Override) - if sym.is(ParamAccessor) then - vdef.symbol.resetFlag(ParamAccessor) - cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) - else - vdef // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + vdef1.symbol.setFlag(Override) + vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? case stat: DefDef => val ddef = cloneDefDef(stat) if !sym.is(Private) then ddef.symbol.setFlag(Override) diff --git a/tests/neg/inline-trait-signature-generic-context-bound.scala b/tests/neg/inline-trait-signature-generic-context-bound.scala new file mode 100644 index 000000000000..957cf0f02c2f --- /dev/null +++ b/tests/neg/inline-trait-signature-generic-context-bound.scala @@ -0,0 +1,4 @@ +inline trait A[T: List] // error + +given List[Int] = Nil +class B extends A \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-implicit.scala b/tests/neg/inline-trait-signature-parameters-implicit.scala new file mode 100644 index 000000000000..b703f146f5a9 --- /dev/null +++ b/tests/neg/inline-trait-signature-parameters-implicit.scala @@ -0,0 +1,4 @@ +inline trait A(implicit val imp: Int) // error + +implicit val x: Int = 1 +class B extends A() \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-using-nameless.scala b/tests/neg/inline-trait-signature-parameters-using-nameless.scala new file mode 100644 index 000000000000..21c64e1415af --- /dev/null +++ b/tests/neg/inline-trait-signature-parameters-using-nameless.scala @@ -0,0 +1,4 @@ +inline trait A(using Int) // error + +given x: Int = 1 +class B extends A() \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-using.scala b/tests/neg/inline-trait-signature-parameters-using.scala new file mode 100644 index 000000000000..2e3c231232f6 --- /dev/null +++ b/tests/neg/inline-trait-signature-parameters-using.scala @@ -0,0 +1,4 @@ +inline trait A(using usng: Int) // error + +given x: Int = 1 +class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-context-bound.scala b/tests/pos/inline-trait-signature-generic-context-bound.scala deleted file mode 100644 index 9cd7b0728101..000000000000 --- a/tests/pos/inline-trait-signature-generic-context-bound.scala +++ /dev/null @@ -1,4 +0,0 @@ -inline trait A[T: List] - -given List[Int] = Nil -class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-implicit.scala b/tests/pos/inline-trait-signature-parameters-implicit.scala deleted file mode 100644 index f4b2f5c93d61..000000000000 --- a/tests/pos/inline-trait-signature-parameters-implicit.scala +++ /dev/null @@ -1,4 +0,0 @@ -inline trait A(implicit val imp: Int) - -implicit val x: Int = 1 -class B extends A() \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-using.scala b/tests/pos/inline-trait-signature-parameters-using.scala deleted file mode 100644 index d012d8c59e22..000000000000 --- a/tests/pos/inline-trait-signature-parameters-using.scala +++ /dev/null @@ -1,4 +0,0 @@ -inline trait A(using imp: Int) - -given val x: Int = 1 -class B extends A() \ No newline at end of file From ab2c439cad9a0b3f22a5a61f67860bc8cd29ca98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Apr 2023 17:17:47 +0200 Subject: [PATCH 017/106] Fix typo in type bound --- tests/pos/inline-trait-signature-generic-type-bounds.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/inline-trait-signature-generic-type-bounds.scala b/tests/pos/inline-trait-signature-generic-type-bounds.scala index cb8ef919e633..9cf4a00c80d7 100644 --- a/tests/pos/inline-trait-signature-generic-type-bounds.scala +++ b/tests/pos/inline-trait-signature-generic-type-bounds.scala @@ -1,4 +1,4 @@ -inline trait A[T <: AnyVal >: Int]: +inline trait A[T >: Int <: AnyVal]: def f(x: T): T = x class B extends A[Int] From fdfbe26fab40daf6860f3826b41be62b63ae811c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 6 Apr 2023 16:36:18 +0200 Subject: [PATCH 018/106] Multiple files tests, quick fix of span Add tests with multiple files, and compiling in multiple phases. Multiphase doesn't work yet Quick fix for the span, might need to be cleaned/cleaner --- .../dotty/tools/dotc/inlines/Inlines.scala | 21 +++++++++++-------- tests/pos/inline-trait-multiple-files/A.scala | 2 ++ tests/pos/inline-trait-multiple-files/B.scala | 1 + .../inline-trait-multiple-stages/A_1.scala | 2 ++ .../inline-trait-multiple-stages/B_2.scala | 1 + 5 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 tests/pos/inline-trait-multiple-files/A.scala create mode 100644 tests/pos/inline-trait-multiple-files/B.scala create mode 100644 tests/pos/inline-trait-multiple-stages/A_1.scala create mode 100644 tests/pos/inline-trait-multiple-stages/B_2.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 668c31996719..407e42c8acf1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -18,7 +18,7 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} -import util.Spans.Span +import util.Spans.{Span, spanCoord} /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -510,11 +510,14 @@ object Inlines: } stats1.map(stat => inlined(stat)._2) + private def inlinedRhs(rhs: Tree): Inlined = + Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span) + private def cloneClass(clDef: TypeDef, impl: Template)(using Context): TypeDef = val inlinedCls: ClassSymbol = val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = clDef.symbol.info: @unchecked val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents - clDef.symbol.copy(owner = ctx.owner, info = inlinedInfo).entered.asClass + clDef.symbol.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { (cloneDefDef(impl.constr), impl.body.map(cloneStat)) } @@ -527,20 +530,20 @@ object Inlines: // TODO case tree: TypeDef => cloneTypeDef(tree) private def cloneDefDef(ddef: DefDef)(using Context): DefDef = - val inlinedSym = ddef.symbol.copy(owner = ctx.owner).entered + val inlinedSym = ddef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered def rhsFun(paramss: List[List[Tree]]): Tree = val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) - ddef.rhs.subst(oldParamSyms, newParamSyms) // TODO clone local classes? - tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(ddef.span) + inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms)) // TODO clone local classes? + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) private def cloneValDef(vdef: ValDef)(using Context): ValDef = - val inlinedSym = vdef.symbol.copy(owner = ctx.owner).entered - tpd.ValDef(inlinedSym.asTerm, vdef.rhs).withSpan(vdef.span) // TODO clone local classes? + val inlinedSym = vdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered + tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs)).withSpan(parent.span) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = - val inlinedSym = tdef.symbol.copy(owner = ctx.owner).entered - tpd.TypeDef(inlinedSym.asType).withSpan(tdef.span) + val inlinedSym = tdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered + tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) end InlineParentTrait end Inlines diff --git a/tests/pos/inline-trait-multiple-files/A.scala b/tests/pos/inline-trait-multiple-files/A.scala new file mode 100644 index 000000000000..a137403cdf53 --- /dev/null +++ b/tests/pos/inline-trait-multiple-files/A.scala @@ -0,0 +1,2 @@ +inline trait A: + val i: Int = 1 \ No newline at end of file diff --git a/tests/pos/inline-trait-multiple-files/B.scala b/tests/pos/inline-trait-multiple-files/B.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/tests/pos/inline-trait-multiple-files/B.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-multiple-stages/A_1.scala b/tests/pos/inline-trait-multiple-stages/A_1.scala new file mode 100644 index 000000000000..a137403cdf53 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages/A_1.scala @@ -0,0 +1,2 @@ +inline trait A: + val i: Int = 1 \ No newline at end of file diff --git a/tests/pos/inline-trait-multiple-stages/B_2.scala b/tests/pos/inline-trait-multiple-stages/B_2.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages/B_2.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file From e4e42aed33c4ede37aeb06d6c518b879376db0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 6 Apr 2023 18:53:57 +0200 Subject: [PATCH 019/106] Improve benchmark, make adding implementations easier --- .../inlinetraits/InlineTraitBenchmark.scala | 29 +++++---- .../benchmarks/inlinetraits/MatrixOps.scala | 29 +++++++++ .../inlinetraits/standard/Matrix.scala | 59 ++++++++----------- 3 files changed, 68 insertions(+), 49 deletions(-) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala index 7f998e2d1195..97891ca54356 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -4,8 +4,6 @@ import org.openjdk.jmh.annotations._ import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} import scala.util.Random -import standard.IntMatrixLib.{Matrix => StdIntMatrix} - @BenchmarkMode(Array(Mode.AverageTime)) @Fork(3) @Threads(3) @@ -14,20 +12,25 @@ import standard.IntMatrixLib.{Matrix => StdIntMatrix} @OutputTimeUnit(MILLISECONDS) @State(Scope.Benchmark) class InlineTraitBenchmark { - val N = 200 - def matrix(rows: Int, cols: Int): Vector[Vector[Int]] = - Vector.tabulate(rows, cols)((_, _) => Random.nextInt()) + val n = 300 + + def intMatrixElems(): Seq[Seq[Int]] = + Seq.tabulate(n, n)((_, _) => Random.nextInt()) + + @Param(Array("standard")) + var matrixType: String = "" - @Setup(Level.Iteration) + var m1: Matrix[BenchmarkMatrix] = Matrix.empty + var m2: Matrix[BenchmarkMatrix] = Matrix.empty + + @Setup(Level.Trial) def setup = { - Random.setSeed(N) + Random.setSeed(n) + val matrixFactory = Matrix.ofType(matrixType) + m1 = matrixFactory(intMatrixElems()) + m2 = matrixFactory(intMatrixElems()) } @Benchmark - def standardLib = { - val m1 = StdIntMatrix(matrix(N, N)*) - val m2 = StdIntMatrix(matrix(N, N)*) - - (m1 + m2) * m1 // O(N^3) loops - } + def matrixBenchmark = (m1 + m2) * m1 // O(n^3) loops } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala new file mode 100644 index 000000000000..5c7175baae30 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala @@ -0,0 +1,29 @@ +package dotty.tools.benchmarks.inlinetraits + +import standard.IntMatrixLib.{Matrix => StdIntMatrix} +// import specialized.IntMatrixLib.{Matrix => SpeIntMatrix} +// import inline.IntMatrixLib.{Matrix => InlIntMatrix} + +type BenchmarkMatrix = StdIntMatrix // | SpeIntMatrix | InlIntMatrix + +class Matrix[M <: BenchmarkMatrix] private (private val matrix: M): + def +(n: Matrix[M]): Matrix[BenchmarkMatrix] = (matrix, n.matrix) match { + case (m: StdIntMatrix, n: StdIntMatrix) => Matrix(m + n) + //case (m: SpeIntMatrix, n: SpeIntMatrix) => Matrix(m + n) + //case (m: InlIntMatrix, n: InlIntMatrix) => Matrix(m + n) + //case _ => ??? + } + def *(n: Matrix[M]): Matrix[BenchmarkMatrix] = (matrix, n.matrix) match { + case (m: StdIntMatrix, n: StdIntMatrix) => Matrix(m * n) + //case (m: SpeIntMatrix, n: SpeIntMatrix) => Matrix(m * n) + //case (m: InlIntMatrix, n: InlIntMatrix) => Matrix(m * n) + //case _ => ??? + } + +object Matrix: + def ofType(tpe: String): Seq[Seq[Int]] => Matrix[BenchmarkMatrix] = + (elems: Seq[Seq[Int]]) => tpe.toLowerCase() match { + case "standard" => Matrix(StdIntMatrix(elems*)) + } + + val empty: Matrix[BenchmarkMatrix] = Matrix(StdIntMatrix(Nil)) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala index 9da3d13a950d..0326aea07e29 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala @@ -2,52 +2,39 @@ package dotty.tools.benchmarks.inlinetraits package standard import scala.annotation.tailrec +import scala.reflect.ClassTag -trait MatrixLib[T: Numeric]: - opaque type Matrix = Vector[Vector[T]] +trait MatrixLib[T: ClassTag]: + opaque type Matrix = Array[Array[T]] object Matrix: def apply(rows: Seq[T]*): Matrix = - @tailrec def rec(rows: Seq[Seq[T]], acc: Matrix): Matrix = - rows match { - case Seq() => throw IllegalArgumentException("a matrix cannot be empty") - case Seq(Seq()) => throw IllegalArgumentException("a matrix cannot contain an empty row") - case Seq(row) => acc :+ row.toVector - case h +: t if h.length == t(0).length => rec(t, acc :+ h.toVector) - case h +: _ => throw IllegalArgumentException(s"row ${h} does not have the same number of columns as the rest of the matrix") - } + rows.map(_.toArray).toArray - rec(rows, Vector()) + extension (m: Matrix) + def apply(x: Int)(y: Int): T = m(x)(y) + def rows: Int = m.length + def cols: Int = m(0).length +object IntMatrixLib extends MatrixLib[Int] { extension (m: Matrix) def +(n: Matrix): Matrix = - assert( - m.length == n.length && m(0).length == n(0).length, - "m and n do not have the same dimensions" - ) - - val num = summon[Numeric[T]] - import num.plus - - for row <- (0 until m.length).toVector - yield - for col <- (0 until m(0).length).toVector - yield plus(m(row)(col), n(row)(col)) + val sum = + for row <- 0 until m.rows + yield + for col <- 0 until m.cols + yield m(row)(col) + n(row)(col) + Matrix(sum*) end + def *(n: Matrix): Matrix = - assert(m(0).length == n.length, "m and n have incompatible dimensions") - - val num = summon[Numeric[T]] - import num.{zero, plus, times} - - for i <- (0 until m.length).toVector - yield - for j <- (0 until n(0).length).toVector + val prod = + for i <- 0 until m.rows yield - val mults = for k <- 0 until n.length yield times(m(i)(k), n(k)(j)) - mults.fold(zero)(plus(_, _)) + for j <- 0 until n.cols + yield + val mults = for k <- 0 until n.rows yield m(i)(k) * n(k)(j) + mults.fold(0)(_ + _) + Matrix(prod*) end * - -object IntMatrixLib extends MatrixLib[Int] -object DoubleMatrixLib extends MatrixLib[Double] \ No newline at end of file +} \ No newline at end of file From 586605734a35c558461a7a6c091b921b1afd7247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 13 Apr 2023 10:40:08 +0200 Subject: [PATCH 020/106] Add specialized and inline trait matrices for benchmark Implement matrix library with code equivalent to the `@specialized` annotation from Scala 2 Implement matrix library with code equivalent to what inline traits will need to do (to be removed when inline traits work) --- .../inlinetraits/InlineTraitBenchmark.scala | 15 +-- .../benchmarks/inlinetraits/MatrixOps.scala | 60 ++++++---- .../inlinetraits/inlinetrait/Matrix.scala | 54 +++++++++ .../inlinetraits/specialized/Matrix.scala | 113 ++++++++++++++++++ .../inlinetraits/standard/Matrix.scala | 6 +- .../inlinetraits/standard/Numeric.scala | 9 +- 6 files changed, 220 insertions(+), 37 deletions(-) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala index 97891ca54356..a86529216b22 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -7,26 +7,27 @@ import scala.util.Random @BenchmarkMode(Array(Mode.AverageTime)) @Fork(3) @Threads(3) -@Warmup(iterations = 3, time = 2, timeUnit = SECONDS) +@Warmup(iterations = 3, time = 3, timeUnit = SECONDS) @Measurement(iterations = 5, time = 5, timeUnit = SECONDS) @OutputTimeUnit(MILLISECONDS) @State(Scope.Benchmark) class InlineTraitBenchmark { - val n = 300 + // @Param(Array("100", "200", "300")) + var n: Int = 300 def intMatrixElems(): Seq[Seq[Int]] = Seq.tabulate(n, n)((_, _) => Random.nextInt()) - @Param(Array("standard")) - var matrixType: String = "" + @Param(Array("standard", "specialized", "inlinetrait")) + var matrixLibType: String = "" - var m1: Matrix[BenchmarkMatrix] = Matrix.empty - var m2: Matrix[BenchmarkMatrix] = Matrix.empty + var m1 = BenchmarkMatrix.empty + var m2 = BenchmarkMatrix.empty @Setup(Level.Trial) def setup = { Random.setSeed(n) - val matrixFactory = Matrix.ofType(matrixType) + val matrixFactory = BenchmarkMatrix.ofType(matrixLibType) m1 = matrixFactory(intMatrixElems()) m2 = matrixFactory(intMatrixElems()) } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala index 5c7175baae30..bed34099891c 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala @@ -1,29 +1,49 @@ package dotty.tools.benchmarks.inlinetraits import standard.IntMatrixLib.{Matrix => StdIntMatrix} -// import specialized.IntMatrixLib.{Matrix => SpeIntMatrix} -// import inline.IntMatrixLib.{Matrix => InlIntMatrix} +import specialized.IntMatrixLib.{Matrix => SpeIntMatrix} +import inlinetrait.IntMatrixLib.{Matrix => InlIntMatrix} -type BenchmarkMatrix = StdIntMatrix // | SpeIntMatrix | InlIntMatrix +trait BenchmarkMatrix: + def +(n: BenchmarkMatrix): BenchmarkMatrix + def *(n: BenchmarkMatrix): BenchmarkMatrix -class Matrix[M <: BenchmarkMatrix] private (private val matrix: M): - def +(n: Matrix[M]): Matrix[BenchmarkMatrix] = (matrix, n.matrix) match { - case (m: StdIntMatrix, n: StdIntMatrix) => Matrix(m + n) - //case (m: SpeIntMatrix, n: SpeIntMatrix) => Matrix(m + n) - //case (m: InlIntMatrix, n: InlIntMatrix) => Matrix(m + n) - //case _ => ??? +object BenchmarkMatrix: + def ofType(tpe: String): Seq[Seq[Int]] => BenchmarkMatrix = + (elems: Seq[Seq[Int]]) => tpe.toLowerCase() match { + case "standard" => StdBenchmarkMatrix(StdIntMatrix(elems*)) + case "specialized" => SpeBenchmarkMatrix(SpeIntMatrix(elems*)) + case "inlinetrait" => InlBenchmarkMatrix(InlIntMatrix(elems*)) + } + + val empty: BenchmarkMatrix = new BenchmarkMatrix { + override def +(n: BenchmarkMatrix): BenchmarkMatrix = ??? + override def *(n: BenchmarkMatrix): BenchmarkMatrix = ??? + } + + +private class StdBenchmarkMatrix(val m: StdIntMatrix) extends BenchmarkMatrix: + override def +(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { + case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m + stdN.m) } - def *(n: Matrix[M]): Matrix[BenchmarkMatrix] = (matrix, n.matrix) match { - case (m: StdIntMatrix, n: StdIntMatrix) => Matrix(m * n) - //case (m: SpeIntMatrix, n: SpeIntMatrix) => Matrix(m * n) - //case (m: InlIntMatrix, n: InlIntMatrix) => Matrix(m * n) - //case _ => ??? + override def *(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { + case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m * stdN.m) } -object Matrix: - def ofType(tpe: String): Seq[Seq[Int]] => Matrix[BenchmarkMatrix] = - (elems: Seq[Seq[Int]]) => tpe.toLowerCase() match { - case "standard" => Matrix(StdIntMatrix(elems*)) - } +private class SpeBenchmarkMatrix(val m: SpeIntMatrix) extends BenchmarkMatrix: + import specialized.IntMatrixLib + + override def +(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(IntMatrixLib.+(this.m)(speN.m)) + } + override def *(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(IntMatrixLib.*(this.m)(speN.m)) + } - val empty: Matrix[BenchmarkMatrix] = Matrix(StdIntMatrix(Nil)) +private class InlBenchmarkMatrix(val m: InlIntMatrix) extends BenchmarkMatrix: + override def +(n: BenchmarkMatrix): InlBenchmarkMatrix = n match { + case inlN: InlBenchmarkMatrix => InlBenchmarkMatrix(this.m + inlN.m) + } + override def *(n: BenchmarkMatrix): InlBenchmarkMatrix = n match { + case inlN: InlBenchmarkMatrix => InlBenchmarkMatrix(this.m * inlN.m) + } \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala new file mode 100644 index 000000000000..3a265b76733a --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala @@ -0,0 +1,54 @@ +package dotty.tools.benchmarks.inlinetraits +package inlinetrait + +import scala.reflect.ClassTag + +// FIXME uncomment the following code when inline traits work +// inline trait MatrixLib[T: ClassTag]: +// opaque type Matrix = Array[Array[T]] + +// object Matrix: +// def apply(rows: Seq[T]*): Matrix = +// rows.map(_.toArray).toArray + +// extension (m: Matrix) +// def apply(x: Int)(y: Int): T = m(x)(y) +// def rows: Int = m.length +// def cols: Int = m(0).length + +object IntMatrixLib /*extends MatrixLib[Int]*/: + // FIXME remove manually "generated" code below and replace `IntMatrix` with `Matrix` when inline traits work properly + // ---------------------------------- + opaque type Matrix = Array[Array[Int]] + + object Matrix: + def apply(rows: Seq[Int]*): Matrix = + rows.map(_.toArray).toArray + + extension (m: Matrix) + /*override*/ def apply(x: Int)(y: Int): Int = m(x)(y) + /*override*/ def rows: Int = m.length + /*override*/ def cols: Int = m(0).length + // ---------------------------------- + // end of code to remove + + extension (m: Matrix) + def +(n: Matrix): Matrix = + val sum = + for row <- 0 until m.rows + yield + for col <- 0 until m.cols + yield m(row)(col) + n(row)(col) + Matrix(sum*) + end + + + def *(n: Matrix): Matrix = + val prod = + for i <- 0 until m.rows + yield + for j <- 0 until n.cols + yield + val mults = for k <- 0 until n.rows yield m(i)(k) * n(k)(j) + mults.fold(0)(_ + _) + Matrix(prod*) + end * \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala new file mode 100644 index 000000000000..6043da8207be --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Matrix.scala @@ -0,0 +1,113 @@ +package dotty.tools.benchmarks.inlinetraits +package specialized + +/* + * This implementation relies on the @specialized tag to specialize the MatrixLib. + * However, @specialized does nothing in Scala 3. Therefore, an equivalent version is + * recreated by hand further down, and the actual Scala 2 code is provided below: + * + * import scala.reflect.ClassTag + * import scala.specialized + * + * class MatrixLib[@specialized(Int) T: ClassTag] { + * type Matrix = Array[Array[T]] + * + * object Matrix { + * def apply(rows: Seq[T]*): Matrix = + * rows.map(_.toArray).toArray + * } + * + * def get(m: Matrix)(x: Int)(y: Int): T = m(x)(y) + * def rows(m: Matrix): Int = m.length + * def cols(m: Matrix): Int = m(0).length + * } + * + * object IntMatrixLib extends MatrixLib[Int] { + * def +(m: Matrix)(n: Matrix): Matrix = { + * val sum = { + * for (row <- 0 until rows(m)) + * yield { + * for (col <- 0 until cols(m)) + * yield m(row)(col) + n(row)(col) + * } + * } + * Matrix(sum: _*) + * } + * + * def *(m: Matrix)(n: Matrix): Matrix = { + * val prod = { + * for (i <- 0 until rows(m)) + * yield { + * for (j <- 0 until cols(n)) + * yield { + * val mults = + * for (k <- 0 until rows(n)) yield get(m)(i)(k) * get(n)(k)(j) + * mults.fold(0)(_ + _) + * } + * } + * } + * Matrix(prod: _*) + * } + * } + */ + +import scala.reflect.ClassTag +import scala.specialized + +class MatrixLib { + private[specialized] type ObjectMatrix = Array[Array[Object]] + private[specialized] type IntMatrix = Array[Array[Int]] + + def get(m: ObjectMatrix)(x: Int)(y: Int): Object = m(x)(y) + def rows(m: ObjectMatrix): Int = m.length + def cols(m: ObjectMatrix): Int = m(0).length + private[specialized] def get$mcI$sp(m: IntMatrix)(x: Int)(y: Int): Int = Int.unbox(get(m.asInstanceOf[ObjectMatrix])(x)(y)) + private[specialized] def rows$mcI$sp(m: IntMatrix): Int = rows(m.asInstanceOf[ObjectMatrix]) + private[specialized] def cols$mcI$sp(m: IntMatrix): Int = cols(m.asInstanceOf[ObjectMatrix]) +} + +private class MatrixLib$mcI$sp extends MatrixLib { + override def get(m: ObjectMatrix)(x: Int)(y: Int): Object = Int.box(get(m.asInstanceOf[IntMatrix])(x)(y)) + override def rows(m: ObjectMatrix): Int = rows(m.asInstanceOf[IntMatrix]) + override def cols(m: ObjectMatrix): Int = cols(m.asInstanceOf[IntMatrix]) + def get(m: IntMatrix)(x: Int)(y: Int): Int = get$mcI$sp(m)(x)(y) + def rows(m: IntMatrix): Int = rows$mcI$sp(m) + def cols(m: IntMatrix): Int = cols$mcI$sp(m) + override private[specialized] def get$mcI$sp(m: IntMatrix)(x: Int)(y: Int): Int = m(x)(y) + override private[specialized] def rows$mcI$sp(m: IntMatrix): Int = m.length + override private[specialized] def cols$mcI$sp(m: IntMatrix): Int = m(0).length +} + +object IntMatrixLib extends MatrixLib$mcI$sp { + type Matrix = IntMatrix + object Matrix { + def apply(rows: Seq[Int]*): IntMatrix = + rows.map(_.toArray).toArray + } + + def +(m: Matrix)(n: Matrix): Matrix = { + val sum = { + for (row <- 0 until rows(m)) + yield { + for (col <- 0 until cols(m)) + yield m(row)(col) + n(row)(col) + } + } + Matrix(sum: _*) + } + + def *(m: Matrix)(n: Matrix): Matrix = { + val prod = { + for (i <- 0 until rows(m)) + yield { + for (j <- 0 until cols(n)) + yield { + val mults = + for (k <- 0 until rows(n)) yield get(m)(i)(k) * get(n)(k)(j) + mults.fold(0)(_ + _) + } + } + } + Matrix(prod: _*) + } +} \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala index 0326aea07e29..66c124e0086d 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Matrix.scala @@ -1,7 +1,6 @@ package dotty.tools.benchmarks.inlinetraits package standard -import scala.annotation.tailrec import scala.reflect.ClassTag trait MatrixLib[T: ClassTag]: @@ -16,7 +15,7 @@ trait MatrixLib[T: ClassTag]: def rows: Int = m.length def cols: Int = m(0).length -object IntMatrixLib extends MatrixLib[Int] { +object IntMatrixLib extends MatrixLib[Int]: extension (m: Matrix) def +(n: Matrix): Matrix = val sum = @@ -36,5 +35,4 @@ object IntMatrixLib extends MatrixLib[Int] { val mults = for k <- 0 until n.rows yield m(i)(k) * n(k)(j) mults.fold(0)(_ + _) Matrix(prod*) - end * -} \ No newline at end of file + end * \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala index f9ceba90bc71..21325c2ca85a 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Numeric.scala @@ -1,10 +1,7 @@ package dotty.tools.benchmarks.inlinetraits package standard -import scala.math.Ordering -import scala.language.implicitConversions - -trait Numeric[T] extends Ordering[T] { +trait Numeric[T] { def zero: T def plus(x: T, y: T): T def times(x: T, y: T): T @@ -18,12 +15,12 @@ object Numeric { def plus(x: Int, y: Int): Int = x + y def times(x: Int, y: Int): Int = x * y } - implicit object IntIsNumeric extends IntIsNumeric with Ordering.IntOrdering + implicit object IntIsNumeric extends IntIsNumeric trait DoubleIsNumeric extends Numeric[Double] { def zero: Double = 0d def plus(x: Double, y: Double): Double = x + y def times(x: Double, y: Double): Double = x * y } - implicit object DoubleIsNumeric extends DoubleIsNumeric with Ordering.Double.IeeeOrdering + implicit object DoubleIsNumeric extends DoubleIsNumeric } From 8c84089b3607ff441bfcb5a73406c0435b64f2cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 13 Apr 2023 14:34:12 +0200 Subject: [PATCH 021/106] Fix issue with generic type in signature --- .../dotty/tools/dotc/inlines/Inliner.scala | 16 +-- .../dotty/tools/dotc/inlines/Inlines.scala | 127 +++++++++++++----- 2 files changed, 97 insertions(+), 46 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 4f95a13fdbb9..6d77726d969b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -150,7 +150,7 @@ class Inliner(val call: tpd.Tree)(using Context): protected val callTypeArgs = typeArgss(call).flatten protected val callValueArgss = termArgss(call) protected val inlinedMethod = methPart.symbol - private val inlineCallPrefix = + protected val inlineCallPrefix = qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass)) // Make sure all type arguments to the call are fully determined, @@ -158,7 +158,7 @@ class Inliner(val call: tpd.Tree)(using Context): for arg <- callTypeArgs do isFullyDefined(arg.tpe, ForceDegree.flipBottom) - /** A map from parameter names of the inlineable method to references of the actual arguments. + /** A map from parameter names of the inlineable method or trait to references of the actual arguments. * For a type argument this is the full argument type. * For a value argument, it is a reference to either the argument value * (if the argument is a pure expression of singleton type), or to `val` or `def` acting @@ -169,7 +169,7 @@ class Inliner(val call: tpd.Tree)(using Context): /** A map from parameter names of the inlineable method to spans of the actual arguments */ private val paramSpan = new mutable.HashMap[Name, Span] - /** A map from references to (type and value) parameters of the inlineable method + /** A map from references to (type and value) parameters of the inlineable method or trait * to their corresponding argument or proxy references, as given by `paramBinding`. */ private[inlines] val paramProxy = new mutable.HashMap[Type, Type] @@ -187,8 +187,8 @@ class Inliner(val call: tpd.Tree)(using Context): * * These are different (wrt ==) types but represent logically the same key */ - private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] - private val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType] + protected val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] + protected val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType] /** A buffer for bindings that define proxies for actual arguments */ private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] @@ -452,7 +452,7 @@ class Inliner(val call: tpd.Tree)(using Context): * references of a method are (we only know the method's type, but that contains TypeParamRefs * and MethodParams, not TypeRefs or TermRefs. */ - private def registerType(tpe: Type): Unit = tpe match { + protected def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { @@ -462,10 +462,6 @@ class Inliner(val call: tpd.Tree)(using Context): thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef for (param <- tpe.cls.typeParams) paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) - case tpe: ThisType if tpe.cls.isInlineTrait => - thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) - for (param <- tpe.cls.typeParams) - paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 407e42c8acf1..1615d40b802d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -470,45 +470,100 @@ object Inlines: import Inlines.* def expandDefs(): List[Tree] = - val argsMap = parent match - case Apply(_, args) => - val MethodType(paramNames) = parent.symbol.info: @unchecked - paramNames.zip(args).toMap - case _ => Map.empty - val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked + val s = stats.map(expandStat) + s.map(inlined(_)._2) + end expandDefs + + override protected def registerType(tpe: Type): Unit = tpe match { + case tpe: ThisType if tpe.cls.isInlineTrait => + thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + for (param <- tpe.cls.typeParams) + paramProxy(param.typeRef) = param.typeRef.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) + case _ => super.registerType(tpe) + } - val stats1 = stats.map { stat => - val sym = stat.symbol - stat match - case stat: ValDef if sym.isOneOf(Given | Implicit) => - report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos) - stat - case stat: ValDef => - val vdef = cloneValDef(stat) - val vdef1 = - if sym.is(ParamAccessor) then - vdef.symbol.resetFlag(ParamAccessor) - cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) - else - vdef - if !sym.is(Private) then - vdef1.symbol.setFlag(Override) - vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? - case stat: DefDef => - val ddef = cloneDefDef(stat) - if !sym.is(Private) then ddef.symbol.setFlag(Override) - if sym.is(Mutable) then - report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos) - ddef - case stat @ TypeDef(_, impl: Template) => - cloneClass(stat, impl) - case stat: TypeDef => - val tdef = cloneTypeDef(stat) - tdef.symbol.setFlag(Override) - tdef + private val argsMap: Map[Names.Name, untpd.Tree] = + /* + * Consider the parent `new A[String](1)("A")` with signature `inline trait A[T](x: Int)(y: T)`. + * The `parent` tree is "right-to-left": Apply(Apply(TypeApply(..., String), 1), "A") + * The `info` tree is "left-to-right": PolyType(T, ..., MethodType(x, Int, MethodType(y, T, ...))) + * This recursive solution goes as deep as possible in the tree, then matches arguments with their + * names in a bottom-up fashion. + */ + def rec(tree: Tree, info: Type, acc: Map[Names.Name, untpd.Tree]): (Option[Type], Map[Names.Name, untpd.Tree]) = + tree match { + case Apply(tree1, args) => rec(tree1, info, acc) match { + case (Some(info1), acc1) => info1 match { + case MethodTpe(paramNames, _, info2) if paramNames.length == args.length => + (Some(info2), acc1 ++ paramNames.zip(args).toMap) + case _ => + report.error(s"mismatch between Apply ${tree.show} and info ${info1}", parent.srcPos) + (None, acc1) + } + case res => res + } + case TypeApply(tree1, tpes) => rec(tree1, info, acc) match { + case (Some(info1), acc1) => info1 match { + case PolyType(lambdaParams, info2) if lambdaParams.length == tpes.length => + (Some(info2), acc1) + case _ => + report.error(s"mismatch between TypeApply ${tree.show} and info ${info1}", parent.srcPos) + (None, acc1) + } + case res => res + } + case _ => (Some(info), acc) + } + + parent match { + case _: GenericApply => + val (remainingInfo, map) = rec(parent, parent.symbol.info, Map.empty) + remainingInfo match { + case Some(MethodType(paramNames)) => + report.error(s"could not match the following parameters with their values: ${paramNames.map(_.show).mkString(", ")}", parent.srcPos) + case _ => + } + map + case _ => Map.empty } - stats1.map(stat => inlined(stat)._2) + end argsMap + + private def expandStat(stat: untpd.Tree): untpd.Tree = + val sym = stat.symbol + stat match + case stat: ValDef if sym.isOneOf(Given | Implicit) => + report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos) + stat + case stat: ValDef => + val vdef = cloneValDef(stat) + val vdef1 = + if sym.is(ParamAccessor) then + vdef.symbol.resetFlag(ParamAccessor) + cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) + else + vdef + if !sym.is(Private) then + vdef1.symbol.setFlag(Override) + vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + case stat: DefDef => + val ddef = cloneDefDef(stat) + if !sym.is(Private) then + ddef.symbol.setFlag(Override) + if sym.is(Mutable) then + report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos) + ddef + case stat @ TypeDef(_, impl: Template) => + report.error("inline traits do not handle inner classes yet", stat.srcPos) + cloneClass(stat, impl) + case stat: TypeDef => + val tdef = cloneTypeDef(stat) + tdef.symbol.setFlag(Override) + tdef + case _ => + report.error(s"unknown body element of inline ${parentSym.show}: ${stat.show}", stat.srcPos) + stat + end expandStat private def inlinedRhs(rhs: Tree): Inlined = Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span) From 4d981af4719497523343aa5ef8d986d325f8b72c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 13 Apr 2023 14:32:43 +0200 Subject: [PATCH 022/106] Import modifications by Nicolas --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 17 ++++ .../dotty/tools/dotc/inlines/Inliner.scala | 2 +- .../dotty/tools/dotc/inlines/Inlines.scala | 96 ++++++++----------- .../dotty/tools/dotc/transform/Mixin.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 12 ++- ...inline-trait-body-def-curried-params.scala | 9 +- tests/pos/inline-trait-body-def-lambda.scala | 5 + tests/pos/inline-trait-body-var.scala | 2 +- .../A_1.scala | 10 ++ .../B_2.scala | 3 + .../A_1.scala | 9 ++ .../B_2.scala | 3 + .../inline-trait-multiple-stages/A_1.scala | 2 +- .../inline-trait-multiple-stages/B_2.scala | 2 +- ...rait-signature-generic-inferred-type.scala | 9 +- 16 files changed, 110 insertions(+), 75 deletions(-) create mode 100644 tests/pos/inline-trait-body-def-lambda.scala create mode 100644 tests/pos/inline-trait-multiple-stages-defs/A_1.scala create mode 100644 tests/pos/inline-trait-multiple-stages-defs/B_2.scala create mode 100644 tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala create mode 100644 tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 98bd7152ff37..44fe88da2e74 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1050,10 +1050,27 @@ class TreeUnpickler(reader: TastyReader, .map(_.changeOwner(localDummy, constr.symbol))) else parents + val statsStart = currentAddr val lazyStats = readLater(end, rdr => { val stats = rdr.readIndexedStats(localDummy, end) tparams ++ vparams ++ stats }) + if cls.isInlineTrait then + cls.addAnnotation(LazyBodyAnnotation { (ctx0: Context) ?=> + val ctx1 = localContext(cls)(using ctx0).addMode(Mode.ReadPositions) + inContext(sourceChangeContext(Addr(0))(using ctx1)) { + // avoids space leaks by not capturing the current context + + val fork = forkAt(statsStart) + val stats = fork.readIndexedStats(localDummy, end) + val inlinedMembers = (tparams ++ vparams ++ stats).filter { stat => + !(stat.isInstanceOf[TypeDef] && cls.typeParams.contains(stat.symbol)) // !isConstructor + && !stat.symbol.is(Deferred) + && !stat.symbol.isAllOf(Inline) + } + Block(inlinedMembers, unitLiteral).withSpan(cls.span) + } + }) defn.patchStdLibClass(cls) NamerOps.addConstructorProxies(cls) setSpan(start, diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 6d77726d969b..25a4c2bf5b87 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -580,7 +580,7 @@ class Inliner(val call: tpd.Tree)(using Context): case thistpe: ThisType => val cls = thistpe.cls if cls.isInlineTrait then - integrate(This(ctx.owner.asClass), cls) + integrate(This(ctx.owner.asClass).withSpan(call.span), cls) else thisProxy.get(cls) match { case Some(t) => val thisRef = ref(t).withSpan(call.span) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 1615d40b802d..534a7fbd3795 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -3,7 +3,8 @@ package dotc package inlines import ast.*, core.* -import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.* +import Flags.*, Symbols.*, Types.*, Decorators.*, Constants.*, Contexts.*, TypeOps.* +import Names.Name import StdNames.{str, nme, tpnme} import transform.SymUtils._ import typer.* @@ -471,8 +472,7 @@ object Inlines: def expandDefs(): List[Tree] = val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - val s = stats.map(expandStat) - s.map(inlined(_)._2) + stats.map(expandStat).map(inlined(_)._2) end expandDefs override protected def registerType(tpe: Type): Unit = tpe match { @@ -483,51 +483,30 @@ object Inlines: case _ => super.registerType(tpe) } - private val argsMap: Map[Names.Name, untpd.Tree] = - /* - * Consider the parent `new A[String](1)("A")` with signature `inline trait A[T](x: Int)(y: T)`. - * The `parent` tree is "right-to-left": Apply(Apply(TypeApply(..., String), 1), "A") - * The `info` tree is "left-to-right": PolyType(T, ..., MethodType(x, Int, MethodType(y, T, ...))) - * This recursive solution goes as deep as possible in the tree, then matches arguments with their - * names in a bottom-up fashion. - */ - def rec(tree: Tree, info: Type, acc: Map[Names.Name, untpd.Tree]): (Option[Type], Map[Names.Name, untpd.Tree]) = - tree match { - case Apply(tree1, args) => rec(tree1, info, acc) match { - case (Some(info1), acc1) => info1 match { - case MethodTpe(paramNames, _, info2) if paramNames.length == args.length => - (Some(info2), acc1 ++ paramNames.zip(args).toMap) - case _ => - report.error(s"mismatch between Apply ${tree.show} and info ${info1}", parent.srcPos) - (None, acc1) - } - case res => res - } - case TypeApply(tree1, tpes) => rec(tree1, info, acc) match { - case (Some(info1), acc1) => info1 match { - case PolyType(lambdaParams, info2) if lambdaParams.length == tpes.length => - (Some(info2), acc1) - case _ => - report.error(s"mismatch between TypeApply ${tree.show} and info ${info1}", parent.srcPos) - (None, acc1) - } - case res => res - } - case _ => (Some(info), acc) - } - - parent match { - case _: GenericApply => - val (remainingInfo, map) = rec(parent, parent.symbol.info, Map.empty) - remainingInfo match { - case Some(MethodType(paramNames)) => - report.error(s"could not match the following parameters with their values: ${paramNames.map(_.show).mkString(", ")}", parent.srcPos) - case _ => - } - map - case _ => Map.empty - } - end argsMap + private val argsMap: Map[Name, Tree] = + def allArgs(tree: Tree): List[List[Tree]] = tree match + case Apply(fun, args) => args :: allArgs(fun) + case TypeApply(fun, targs) => targs :: allArgs(fun) + case AppliedTypeTree(_, targs) => targs :: Nil + case _ => Nil + def allParams(info: Type): List[List[Name]] = info match + case mt: MethodType => mt.paramNames :: allParams(mt.resultType) + case pt: PolyType => pt.paramNames :: allParams(pt.resultType) + case _ => Nil + val info = + if parent.symbol.isClass then parent.symbol.primaryConstructor.info + else parent.symbol.info + allParams(info).flatten.zip(allArgs(parent).reverse.flatten).toMap + + private val substituteTypeParams = new TypeMap { + override def apply(t: Type): Type = t match + case TypeRef(ths: ThisType, sym: Symbol) if ths.cls == parentSym => + argsMap(sym.name).tpe + case t => mapOver(t) + } + private val substituteTypeParamsInTree = new TreeTypeMap( + typeMap = substituteTypeParams + ) private def expandStat(stat: untpd.Tree): untpd.Tree = val sym = stat.symbol @@ -537,22 +516,21 @@ object Inlines: stat case stat: ValDef => val vdef = cloneValDef(stat) + vdef.symbol.info = substituteTypeParams(sym.info) + if !sym.is(Private) then + vdef.symbol.setFlag(Override) val vdef1 = if sym.is(ParamAccessor) then vdef.symbol.resetFlag(ParamAccessor) cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) else vdef - if !sym.is(Private) then - vdef1.symbol.setFlag(Override) - vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat? + substituteTypeParamsInTree(vdef1) case stat: DefDef => val ddef = cloneDefDef(stat) - if !sym.is(Private) then - ddef.symbol.setFlag(Override) - if sym.is(Mutable) then - report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos) - ddef + ddef.symbol.info = substituteTypeParams(sym.info) + if !sym.is(Private) then ddef.symbol.setFlag(Override) + substituteTypeParamsInTree(ddef) case stat @ TypeDef(_, impl: Template) => report.error("inline traits do not handle inner classes yet", stat.srcPos) cloneClass(stat, impl) @@ -574,7 +552,7 @@ object Inlines: val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents clDef.symbol.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { - (cloneDefDef(impl.constr), impl.body.map(cloneStat)) + (clonePrimaryConstructorDefDef(impl.constr), impl.body.map(cloneStat)) } tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents @@ -592,6 +570,10 @@ object Inlines: inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms)) // TODO clone local classes? tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) + private def clonePrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = + val constr = cloneDefDef(ddef) + cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) + private def cloneValDef(vdef: ValDef)(using Context): ValDef = val inlinedSym = vdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs)).withSpan(parent.span) // TODO clone local classes? diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 0159e027604d..469c033b3d74 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -288,7 +288,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => def setters(mixin: ClassSymbol): List[Tree] = val mixinSetters = mixin.info.decls.filter { sym => - sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) + sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) && !sym.owner.isAllOf(InlineTrait) } for (setter <- mixinSetters) yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index fe28a8b18833..79e0b5ac3ccb 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -502,7 +502,7 @@ object RefChecks { overrideError("needs `override` modifier") else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) overrideError("needs `abstract override` modifiers") - else if member.is(Override) && other.is(Mutable) then + else if member.is(Override) && other.is(Mutable) && !other.owner.isAllOf(InlineTrait) then overrideError("cannot override a mutable variable") else if (member.isAnyOverride && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 35b9480e3be4..2441b106b9cb 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2673,10 +2673,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs if !ctx.isAfterTyper && cls.isInlineTrait then - def isConstructorType(t: Tree) = - t.isInstanceOf[TypeDef] && cls.typeParams.contains(t.symbol) - val inlineTraitMembers = Block(body1.filter(t => !isConstructorType(t)), unitLiteral) - PrepareInlineable.registerInlineInfo(cls, inlineTraitMembers) + def isConstructorType(t: Tree) = t.isInstanceOf[TypeDef] && cls.typeParams.contains(t.symbol) + val membersToInline = body1.filter { t => + !isConstructorType(t) + && !t.symbol.is(Deferred) + && !t.symbol.isAllOf(Inline) + } + val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) + PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) checkNoDoubleDeclaration(cls) val impl1 = cpy.Template(impl)(constr1, parents1, Nil, self1, body1) diff --git a/tests/pos/inline-trait-body-def-curried-params.scala b/tests/pos/inline-trait-body-def-curried-params.scala index 89e436a0fab1..d253aa035331 100644 --- a/tests/pos/inline-trait-body-def-curried-params.scala +++ b/tests/pos/inline-trait-body-def-curried-params.scala @@ -1,9 +1,8 @@ inline trait A: - def x(foo: Int)(bar: Unit) = - bar - foo + def x(foo: Int)(bar: Int) = + foo + bar - def y(foo: Int) = x(foo) + def y(foo: Int) = x(foo)(foo) class B extends A: - def f = x \ No newline at end of file + def f = x(1)(2) diff --git a/tests/pos/inline-trait-body-def-lambda.scala b/tests/pos/inline-trait-body-def-lambda.scala new file mode 100644 index 000000000000..4eec58d40749 --- /dev/null +++ b/tests/pos/inline-trait-body-def-lambda.scala @@ -0,0 +1,5 @@ +inline trait A: + def f = (i: Int) => i + +class B extends A: + def g = f \ No newline at end of file diff --git a/tests/pos/inline-trait-body-var.scala b/tests/pos/inline-trait-body-var.scala index a1e06a13269a..081a5eb829fe 100644 --- a/tests/pos/inline-trait-body-var.scala +++ b/tests/pos/inline-trait-body-var.scala @@ -5,4 +5,4 @@ class B extends A: def f = val old = x x += 1 - old \ No newline at end of file + old diff --git a/tests/pos/inline-trait-multiple-stages-defs/A_1.scala b/tests/pos/inline-trait-multiple-stages-defs/A_1.scala new file mode 100644 index 000000000000..97532bc38ae5 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-defs/A_1.scala @@ -0,0 +1,10 @@ +inline trait A(x: Int): + def f: Int = 1 + def g(a: Int): Int = 2 + def h: Int + val i: Int = 3 + val j: Int + var k: Int = 4 + + inline val a = 5 + inline def b(a: Int): Int = 6 diff --git a/tests/pos/inline-trait-multiple-stages-defs/B_2.scala b/tests/pos/inline-trait-multiple-stages-defs/B_2.scala new file mode 100644 index 000000000000..b1a1d7d6af3d --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-defs/B_2.scala @@ -0,0 +1,3 @@ +class B extends A(10): + def h: Int = 11 + val j: Int = 12 diff --git a/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala b/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala new file mode 100644 index 000000000000..83e2f62dcd45 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-generic-defs/A_1.scala @@ -0,0 +1,9 @@ +inline trait A[T](x: T): + def f: T = x + def g(a: T): T = a + def h: T + val i: T = x + val j: T + var k: T = x + + inline def b(a: T): T = x diff --git a/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala b/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala new file mode 100644 index 000000000000..b595626379b8 --- /dev/null +++ b/tests/pos/inline-trait-multiple-stages-generic-defs/B_2.scala @@ -0,0 +1,3 @@ +class B extends A[Int](10): + def h: Int = 11 + val j: Int = 12 diff --git a/tests/pos/inline-trait-multiple-stages/A_1.scala b/tests/pos/inline-trait-multiple-stages/A_1.scala index a137403cdf53..3596275f0498 100644 --- a/tests/pos/inline-trait-multiple-stages/A_1.scala +++ b/tests/pos/inline-trait-multiple-stages/A_1.scala @@ -1,2 +1,2 @@ inline trait A: - val i: Int = 1 \ No newline at end of file + val i: Int = 1 diff --git a/tests/pos/inline-trait-multiple-stages/B_2.scala b/tests/pos/inline-trait-multiple-stages/B_2.scala index 26e47fd25fdb..a18aec3dbe9b 100644 --- a/tests/pos/inline-trait-multiple-stages/B_2.scala +++ b/tests/pos/inline-trait-multiple-stages/B_2.scala @@ -1 +1 @@ -class B extends A \ No newline at end of file +class B extends A diff --git a/tests/pos/inline-trait-signature-generic-inferred-type.scala b/tests/pos/inline-trait-signature-generic-inferred-type.scala index 93396ec979e9..2ed1e74a8389 100644 --- a/tests/pos/inline-trait-signature-generic-inferred-type.scala +++ b/tests/pos/inline-trait-signature-generic-inferred-type.scala @@ -1,5 +1,8 @@ -inline trait A[T](val x: T) +inline trait A[T](val x: T): def f: T = x -class B extends A(1) - val y: Int = f \ No newline at end of file +class B extends A(1): + val y: Int = f + +class C extends A[Int](1): + val z: Int = f \ No newline at end of file From 6b02d82f7b8242861886a2a464040f531aa60300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 14 Apr 2023 10:17:13 +0200 Subject: [PATCH 023/106] Rework and fix tests Add neg test for manual override of final field (currently fails, should be fixed when selective overriding is implemented) Allow given and implicit params, solve type issue with def param accessors, and move related tests from neg to pos Return original type if not found in list of types from the inline trait's signature Fix pos tests that were badly written --- .../dotty/tools/dotc/inlines/Inlines.scala | 27 ++++++++++++------- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- ...trait-body-def-final-manual-override.scala | 5 ++++ ...-trait-signature-parameters-implicit.scala | 4 --- ...ine-trait-signature-parameters-using.scala | 4 --- ...nline-trait-signature-parameters-var.scala | 3 --- tests/pos/inline-trait-body-def-final.scala | 4 +++ .../pos/inline-trait-body-def-implicit.scala | 2 +- .../pos/inline-trait-body-def-local-val.scala | 7 +++++ tests/pos/inline-trait-body-def-using.scala | 2 +- ...rait-signature-generic-context-bound.scala | 2 +- ...rait-signature-generic-inferred-type.scala | 3 --- ...ne-trait-signature-generic-parameter.scala | 5 ++++ ...-trait-signature-parameters-implicit.scala | 4 +++ ...-signature-parameters-using-nameless.scala | 2 +- ...ine-trait-signature-parameters-using.scala | 4 +++ ...nline-trait-signature-parameters-var.scala | 3 +++ 17 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 tests/neg/inline-trait-body-def-final-manual-override.scala delete mode 100644 tests/neg/inline-trait-signature-parameters-implicit.scala delete mode 100644 tests/neg/inline-trait-signature-parameters-using.scala delete mode 100644 tests/neg/inline-trait-signature-parameters-var.scala create mode 100644 tests/pos/inline-trait-body-def-final.scala create mode 100644 tests/pos/inline-trait-body-def-local-val.scala rename tests/{neg => pos}/inline-trait-signature-generic-context-bound.scala (54%) create mode 100644 tests/pos/inline-trait-signature-generic-parameter.scala create mode 100644 tests/pos/inline-trait-signature-parameters-implicit.scala rename tests/{neg => pos}/inline-trait-signature-parameters-using-nameless.scala (73%) create mode 100644 tests/pos/inline-trait-signature-parameters-using.scala create mode 100644 tests/pos/inline-trait-signature-parameters-var.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 534a7fbd3795..7ce502239186 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -501,19 +501,23 @@ object Inlines: private val substituteTypeParams = new TypeMap { override def apply(t: Type): Type = t match case TypeRef(ths: ThisType, sym: Symbol) if ths.cls == parentSym => - argsMap(sym.name).tpe + argsMap.get(sym.name).map(_.tpe).getOrElse(t) case t => mapOver(t) } private val substituteTypeParamsInTree = new TreeTypeMap( typeMap = substituteTypeParams ) + private val specializedType = new DeepTypeMap { + def apply(t: Type) = t match { + case t: TypeRef => argsMap.get(t.name).map(_.tpe.stripTypeVar).getOrElse(t) + case t => mapOver(t) + } + } + private def expandStat(stat: untpd.Tree): untpd.Tree = val sym = stat.symbol stat match - case stat: ValDef if sym.isOneOf(Given | Implicit) => - report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos) - stat case stat: ValDef => val vdef = cloneValDef(stat) vdef.symbol.info = substituteTypeParams(sym.info) @@ -530,9 +534,14 @@ object Inlines: val ddef = cloneDefDef(stat) ddef.symbol.info = substituteTypeParams(sym.info) if !sym.is(Private) then ddef.symbol.setFlag(Override) - substituteTypeParamsInTree(ddef) + val ddef1 = + if sym.is(ParamAccessor) then + ddef.symbol.resetFlag(ParamAccessor) + cpy.DefDef(ddef)(rhs = unitLiteral) + else + ddef + substituteTypeParamsInTree(ddef1) case stat @ TypeDef(_, impl: Template) => - report.error("inline traits do not handle inner classes yet", stat.srcPos) cloneClass(stat, impl) case stat: TypeDef => val tdef = cloneTypeDef(stat) @@ -567,7 +576,7 @@ object Inlines: def rhsFun(paramss: List[List[Tree]]): Tree = val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) - inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms)) // TODO clone local classes? + inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) // TODO clone local classes? tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) private def clonePrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = @@ -575,8 +584,8 @@ object Inlines: cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) private def cloneValDef(vdef: ValDef)(using Context): ValDef = - val inlinedSym = vdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered - tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs)).withSpan(parent.span) // TODO clone local classes? + val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = specializedType(vdef.symbol.info), coord = spanCoord(parent.span)).entered + tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = val inlinedSym = tdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 79e0b5ac3ccb..d0c2ef02baed 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -465,7 +465,7 @@ object RefChecks { overrideError("cannot be used here - opaque type aliases cannot be overridden") else if (!other.is(Deferred) && member.isClass) overrideError("cannot be used here - classes can only override abstract types") - else if other.isEffectivelyFinal then // (1.2) + else if other.isEffectivelyFinal && !other.owner.isInlineTrait then // (1.2) overrideError(i"cannot override final member ${other.showLocated}") else if (member.is(ExtensionMethod) && !other.is(ExtensionMethod)) // (1.3) overrideError("is an extension method, cannot override a normal method") diff --git a/tests/neg/inline-trait-body-def-final-manual-override.scala b/tests/neg/inline-trait-body-def-final-manual-override.scala new file mode 100644 index 000000000000..c67540a09924 --- /dev/null +++ b/tests/neg/inline-trait-body-def-final-manual-override.scala @@ -0,0 +1,5 @@ +inline trait A: + final def f(x: Int) = x + +class B extends A: + override final def f(x: Int) = x + 1 // error \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-implicit.scala b/tests/neg/inline-trait-signature-parameters-implicit.scala deleted file mode 100644 index b703f146f5a9..000000000000 --- a/tests/neg/inline-trait-signature-parameters-implicit.scala +++ /dev/null @@ -1,4 +0,0 @@ -inline trait A(implicit val imp: Int) // error - -implicit val x: Int = 1 -class B extends A() \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-using.scala b/tests/neg/inline-trait-signature-parameters-using.scala deleted file mode 100644 index 2e3c231232f6..000000000000 --- a/tests/neg/inline-trait-signature-parameters-using.scala +++ /dev/null @@ -1,4 +0,0 @@ -inline trait A(using usng: Int) // error - -given x: Int = 1 -class B extends A() \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-var.scala b/tests/neg/inline-trait-signature-parameters-var.scala deleted file mode 100644 index 0de83db5f6a9..000000000000 --- a/tests/neg/inline-trait-signature-parameters-var.scala +++ /dev/null @@ -1,3 +0,0 @@ -inline trait A(var x: Int) // error - -class B extends A(1) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-final.scala b/tests/pos/inline-trait-body-def-final.scala new file mode 100644 index 000000000000..18bdeed55a1f --- /dev/null +++ b/tests/pos/inline-trait-body-def-final.scala @@ -0,0 +1,4 @@ +inline trait A: + final def f(x: Int) = x + +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-implicit.scala b/tests/pos/inline-trait-body-def-implicit.scala index a5b8512f4d52..51381b394cef 100644 --- a/tests/pos/inline-trait-body-def-implicit.scala +++ b/tests/pos/inline-trait-body-def-implicit.scala @@ -3,4 +3,4 @@ inline trait A: def foo(implicit s: String): String = s + s class B extends A: - def f = foo() \ No newline at end of file + def f = foo \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-local-val.scala b/tests/pos/inline-trait-body-def-local-val.scala new file mode 100644 index 000000000000..c503432ec175 --- /dev/null +++ b/tests/pos/inline-trait-body-def-local-val.scala @@ -0,0 +1,7 @@ +inline trait A: + def f = + val foo = 1 + foo + +class B extends A: + def g = f \ No newline at end of file diff --git a/tests/pos/inline-trait-body-def-using.scala b/tests/pos/inline-trait-body-def-using.scala index 8028671d9fab..1436962f6b48 100644 --- a/tests/pos/inline-trait-body-def-using.scala +++ b/tests/pos/inline-trait-body-def-using.scala @@ -3,4 +3,4 @@ inline trait A: def foo(using s: String): String = s + s class B extends A: - def f = foo() \ No newline at end of file + def f = foo \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-generic-context-bound.scala b/tests/pos/inline-trait-signature-generic-context-bound.scala similarity index 54% rename from tests/neg/inline-trait-signature-generic-context-bound.scala rename to tests/pos/inline-trait-signature-generic-context-bound.scala index 957cf0f02c2f..f3587911f168 100644 --- a/tests/neg/inline-trait-signature-generic-context-bound.scala +++ b/tests/pos/inline-trait-signature-generic-context-bound.scala @@ -1,4 +1,4 @@ -inline trait A[T: List] // error +inline trait A[T: List] given List[Int] = Nil class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-inferred-type.scala b/tests/pos/inline-trait-signature-generic-inferred-type.scala index 2ed1e74a8389..275ed0c7ec5b 100644 --- a/tests/pos/inline-trait-signature-generic-inferred-type.scala +++ b/tests/pos/inline-trait-signature-generic-inferred-type.scala @@ -3,6 +3,3 @@ inline trait A[T](val x: T): class B extends A(1): val y: Int = f - -class C extends A[Int](1): - val z: Int = f \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-generic-parameter.scala b/tests/pos/inline-trait-signature-generic-parameter.scala new file mode 100644 index 000000000000..174ceba06a8c --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-parameter.scala @@ -0,0 +1,5 @@ +inline trait A[T](val x: T): + def f: T = x + +class B extends A[Int](1): + val y: Int = f diff --git a/tests/pos/inline-trait-signature-parameters-implicit.scala b/tests/pos/inline-trait-signature-parameters-implicit.scala new file mode 100644 index 000000000000..df091c0cc7e6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-implicit.scala @@ -0,0 +1,4 @@ +inline trait A(implicit val imp: Int) + +implicit val x: Int = 1 +class B extends A \ No newline at end of file diff --git a/tests/neg/inline-trait-signature-parameters-using-nameless.scala b/tests/pos/inline-trait-signature-parameters-using-nameless.scala similarity index 73% rename from tests/neg/inline-trait-signature-parameters-using-nameless.scala rename to tests/pos/inline-trait-signature-parameters-using-nameless.scala index 21c64e1415af..1882b0348488 100644 --- a/tests/neg/inline-trait-signature-parameters-using-nameless.scala +++ b/tests/pos/inline-trait-signature-parameters-using-nameless.scala @@ -1,4 +1,4 @@ inline trait A(using Int) // error given x: Int = 1 -class B extends A() \ No newline at end of file +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-using.scala b/tests/pos/inline-trait-signature-parameters-using.scala new file mode 100644 index 000000000000..5438359370f8 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-using.scala @@ -0,0 +1,4 @@ +inline trait A(using usng: Int) + +given x: Int = 1 +class B extends A \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-parameters-var.scala b/tests/pos/inline-trait-signature-parameters-var.scala new file mode 100644 index 000000000000..590d273f81b6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-var.scala @@ -0,0 +1,3 @@ +inline trait A(var x: Int) + +class B extends A(1) \ No newline at end of file From 67173de52745f2097faf176a24a553366f6055ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 14 Apr 2023 10:30:37 +0200 Subject: [PATCH 024/106] Make fields private again --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 8 ++++++-- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 8 -------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 25a4c2bf5b87..765b1806d61e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -150,7 +150,7 @@ class Inliner(val call: tpd.Tree)(using Context): protected val callTypeArgs = typeArgss(call).flatten protected val callValueArgss = termArgss(call) protected val inlinedMethod = methPart.symbol - protected val inlineCallPrefix = + private val inlineCallPrefix = qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass)) // Make sure all type arguments to the call are fully determined, @@ -452,7 +452,7 @@ class Inliner(val call: tpd.Tree)(using Context): * references of a method are (we only know the method's type, but that contains TypeParamRefs * and MethodParams, not TypeRefs or TermRefs. */ - protected def registerType(tpe: Type): Unit = tpe match { + private def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { @@ -462,6 +462,10 @@ class Inliner(val call: tpd.Tree)(using Context): thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef for (param <- tpe.cls.typeParams) paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) + case tpe: ThisType if tpe.cls.isInlineTrait => + thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + for (param <- tpe.cls.typeParams) + paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 7ce502239186..4001f70aa0a4 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -475,14 +475,6 @@ object Inlines: stats.map(expandStat).map(inlined(_)._2) end expandDefs - override protected def registerType(tpe: Type): Unit = tpe match { - case tpe: ThisType if tpe.cls.isInlineTrait => - thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) - for (param <- tpe.cls.typeParams) - paramProxy(param.typeRef) = param.typeRef.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) - case _ => super.registerType(tpe) - } - private val argsMap: Map[Name, Tree] = def allArgs(tree: Tree): List[List[Tree]] = tree match case Apply(fun, args) => args :: allArgs(fun) From a51674870d54bd6303dc1d80170eedb2ac523f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 14 Apr 2023 12:07:24 +0200 Subject: [PATCH 025/106] Fix type issue between untpd and tpd Trying to run the benchmark resulted in compilation errors, but not running testCompilation This fixes the issue, while removing the error raised when trying to inline something we do not handle yet --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 4001f70aa0a4..8935a2f20580 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -507,7 +507,7 @@ object Inlines: } } - private def expandStat(stat: untpd.Tree): untpd.Tree = + private def expandStat(stat: untpd.Tree): Tree = val sym = stat.symbol stat match case stat: ValDef => @@ -539,15 +539,12 @@ object Inlines: val tdef = cloneTypeDef(stat) tdef.symbol.setFlag(Override) tdef - case _ => - report.error(s"unknown body element of inline ${parentSym.show}: ${stat.show}", stat.srcPos) - stat end expandStat private def inlinedRhs(rhs: Tree): Inlined = Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span) - private def cloneClass(clDef: TypeDef, impl: Template)(using Context): TypeDef = + private def cloneClass(clDef: untpd.TypeDef, impl: Template)(using Context): TypeDef = val inlinedCls: ClassSymbol = val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = clDef.symbol.info: @unchecked val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents From f568ef8c67c4db3c05d07e575db1bc1681cc6b15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 14 Apr 2023 15:06:25 +0200 Subject: [PATCH 026/106] Add tests with manual override These don't pass yet, we need selective overriding first (don't override things that were manually overridden by the user) --- tests/run/inline-trait-body-override-def.scala | 9 +++++++++ tests/run/inline-trait-body-override-val.check | 1 + tests/run/inline-trait-body-override-val.scala | 9 +++++++++ tests/run/inline-trait-body-override-var.check | 1 + tests/run/inline-trait-body-override-var.scala | 9 +++++++++ 5 files changed, 29 insertions(+) create mode 100644 tests/run/inline-trait-body-override-def.scala create mode 100644 tests/run/inline-trait-body-override-val.check create mode 100644 tests/run/inline-trait-body-override-val.scala create mode 100644 tests/run/inline-trait-body-override-var.check create mode 100644 tests/run/inline-trait-body-override-var.scala diff --git a/tests/run/inline-trait-body-override-def.scala b/tests/run/inline-trait-body-override-def.scala new file mode 100644 index 000000000000..2ec53e02e669 --- /dev/null +++ b/tests/run/inline-trait-body-override-def.scala @@ -0,0 +1,9 @@ +inline trait A: + def foo: Unit = throw Exception("I should not be run!") + +class B extends A: + override def foo: Unit = () + +@main def Test = + val b = B() + b.foo diff --git a/tests/run/inline-trait-body-override-val.check b/tests/run/inline-trait-body-override-val.check new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/tests/run/inline-trait-body-override-val.check @@ -0,0 +1 @@ +2 diff --git a/tests/run/inline-trait-body-override-val.scala b/tests/run/inline-trait-body-override-val.scala new file mode 100644 index 000000000000..a90448deafb6 --- /dev/null +++ b/tests/run/inline-trait-body-override-val.scala @@ -0,0 +1,9 @@ +inline trait A: + val x: Int = 1 + +class B extends A: + override val x: 2 + +@main def Test = + val b = B() + println(b.x) diff --git a/tests/run/inline-trait-body-override-var.check b/tests/run/inline-trait-body-override-var.check new file mode 100644 index 000000000000..0cfbf08886fc --- /dev/null +++ b/tests/run/inline-trait-body-override-var.check @@ -0,0 +1 @@ +2 diff --git a/tests/run/inline-trait-body-override-var.scala b/tests/run/inline-trait-body-override-var.scala new file mode 100644 index 000000000000..a4bf7238da24 --- /dev/null +++ b/tests/run/inline-trait-body-override-var.scala @@ -0,0 +1,9 @@ +inline trait A: + var x: Int = 1 + +class B extends A: + override var x: 2 + +@main def Test = + val b = B() + println(b.x) From bd94dc6eb1a7bcb79018e8f154ef82f291078874 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 14 Apr 2023 15:14:19 +0200 Subject: [PATCH 027/106] Simplify type argument substitution in inline methods --- .../dotty/tools/dotc/inlines/Inlines.scala | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 8935a2f20580..3c9743f803e8 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -496,23 +496,12 @@ object Inlines: argsMap.get(sym.name).map(_.tpe).getOrElse(t) case t => mapOver(t) } - private val substituteTypeParamsInTree = new TreeTypeMap( - typeMap = substituteTypeParams - ) - - private val specializedType = new DeepTypeMap { - def apply(t: Type) = t match { - case t: TypeRef => argsMap.get(t.name).map(_.tpe.stripTypeVar).getOrElse(t) - case t => mapOver(t) - } - } private def expandStat(stat: untpd.Tree): Tree = val sym = stat.symbol stat match case stat: ValDef => val vdef = cloneValDef(stat) - vdef.symbol.info = substituteTypeParams(sym.info) if !sym.is(Private) then vdef.symbol.setFlag(Override) val vdef1 = @@ -521,10 +510,9 @@ object Inlines: cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) else vdef - substituteTypeParamsInTree(vdef1) + vdef1 case stat: DefDef => val ddef = cloneDefDef(stat) - ddef.symbol.info = substituteTypeParams(sym.info) if !sym.is(Private) then ddef.symbol.setFlag(Override) val ddef1 = if sym.is(ParamAccessor) then @@ -532,7 +520,7 @@ object Inlines: cpy.DefDef(ddef)(rhs = unitLiteral) else ddef - substituteTypeParamsInTree(ddef1) + ddef1 case stat @ TypeDef(_, impl: Template) => cloneClass(stat, impl) case stat: TypeDef => @@ -561,7 +549,7 @@ object Inlines: // TODO case tree: TypeDef => cloneTypeDef(tree) private def cloneDefDef(ddef: DefDef)(using Context): DefDef = - val inlinedSym = ddef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered + val inlinedSym = ddef.symbol.copy(owner = ctx.owner, info = substituteTypeParams(ddef.symbol.info), coord = spanCoord(parent.span)).entered def rhsFun(paramss: List[List[Tree]]): Tree = val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) @@ -573,7 +561,7 @@ object Inlines: cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) private def cloneValDef(vdef: ValDef)(using Context): ValDef = - val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = specializedType(vdef.symbol.info), coord = spanCoord(parent.span)).entered + val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = substituteTypeParams(vdef.symbol.info), coord = spanCoord(parent.span)).entered tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = From fd1402538a9fb3dc6b64de97cbc1e92b0589e6f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 14 Apr 2023 16:08:15 +0200 Subject: [PATCH 028/106] Refactor creation of inlined symbols --- .../dotty/tools/dotc/inlines/Inlines.scala | 83 +++++++++++-------- tests/pos/inline-trait-body-setter.scala | 6 ++ 2 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 tests/pos/inline-trait-body-setter.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 3c9743f803e8..7d5f63cfbf52 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -471,8 +471,9 @@ object Inlines: import Inlines.* def expandDefs(): List[Tree] = - val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - stats.map(expandStat).map(inlined(_)._2) + val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked + val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) + stats.zip(inlinedSymbols).map(expandStat).map(inlined(_)._2) end expandDefs private val argsMap: Map[Name, Tree] = @@ -497,37 +498,53 @@ object Inlines: case t => mapOver(t) } - private def expandStat(stat: untpd.Tree): Tree = - val sym = stat.symbol - stat match - case stat: ValDef => - val vdef = cloneValDef(stat) - if !sym.is(Private) then - vdef.symbol.setFlag(Override) - val vdef1 = - if sym.is(ParamAccessor) then - vdef.symbol.resetFlag(ParamAccessor) - cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName)) - else - vdef - vdef1 - case stat: DefDef => - val ddef = cloneDefDef(stat) - if !sym.is(Private) then ddef.symbol.setFlag(Override) - val ddef1 = - if sym.is(ParamAccessor) then - ddef.symbol.resetFlag(ParamAccessor) - cpy.DefDef(ddef)(rhs = unitLiteral) - else - ddef - ddef1 - case stat @ TypeDef(_, impl: Template) => - cloneClass(stat, impl) - case stat: TypeDef => - val tdef = cloneTypeDef(stat) - tdef.symbol.setFlag(Override) - tdef - end expandStat + private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match + case stat: ValDef if stat.symbol.is(ParamAccessor) => + tpd.ValDef(inlinedSym.asTerm, argsMap(inlinedSym.name.asTermName)).withSpan(parent.span) + case stat: ValDef => + inlinedValDef(stat, inlinedSym) + case stat: DefDef if stat.symbol.isSetter => + tpd.DefDef(inlinedSym.asTerm, unitLiteral).withSpan(parent.span) + case stat: DefDef => + inlinedDefDef(stat, inlinedSym) + case stat @ TypeDef(_, impl: Template) => + inlinedClassDef(stat, impl, inlinedSym.asClass) + case stat: TypeDef => + inlinedTypeDef(stat, inlinedSym) + + private def inlinedMember(sym: Symbol)(using Context): Symbol = + if sym.isClass then + val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = sym.info: @unchecked + val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents + sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass + else + var flags = sym.flags + if sym.isType || !sym.is(Private) then flags |= Override + if !sym.isType && sym.is(ParamAccessor) then flags &~= ParamAccessor + sym.copy( + owner = ctx.owner, + flags = flags, + info = substituteTypeParams(sym.info), + coord = spanCoord(parent.span)).entered + + private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = + tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) + + private def inlinedDefDef(ddef: DefDef, inlinedSym: Symbol)(using Context): DefDef = + def rhsFun(paramss: List[List[Tree]]): Tree = + val oldParamSyms = ddef.paramss.flatten.map(_.symbol) + val newParamSyms = paramss.flatten.map(_.symbol) + inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) + tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) + + private def inlinedClassDef(clDef: TypeDef, impl: Template, inlinedCls: ClassSymbol)(using Context): TypeDef = + val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { + (clonePrimaryConstructorDefDef(impl.constr), impl.body.map(cloneStat)) + } + tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents + + private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = + tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) private def inlinedRhs(rhs: Tree): Inlined = Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span) diff --git a/tests/pos/inline-trait-body-setter.scala b/tests/pos/inline-trait-body-setter.scala new file mode 100644 index 000000000000..f1fcf528da9f --- /dev/null +++ b/tests/pos/inline-trait-body-setter.scala @@ -0,0 +1,6 @@ +inline trait A: + def x = 1 + def x_= (x: Int) = ??? + var y = 1 + +class B extends A \ No newline at end of file From c06a5015b1dfb3bd81fbe7540ffa6889b72e4cc4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 14 Apr 2023 16:13:58 +0200 Subject: [PATCH 029/106] Only call `inline` on rhs --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 7d5f63cfbf52..8e34dc2cf9f6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -473,7 +473,7 @@ object Inlines: def expandDefs(): List[Tree] = val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) - stats.zip(inlinedSymbols).map(expandStat).map(inlined(_)._2) + stats.zip(inlinedSymbols).map(expandStat)//.map(inlined(_)._2) end expandDefs private val argsMap: Map[Name, Tree] = @@ -537,17 +537,19 @@ object Inlines: inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) - private def inlinedClassDef(clDef: TypeDef, impl: Template, inlinedCls: ClassSymbol)(using Context): TypeDef = + private def inlinedClassDef(clDef: TypeDef, impl: Template, inlinedCls: ClassSymbol)(using Context): Tree = val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { (clonePrimaryConstructorDefDef(impl.constr), impl.body.map(cloneStat)) } - tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents + inlined(tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body))._2.withSpan(clDef.span) // TODO adapt parents private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) private def inlinedRhs(rhs: Tree): Inlined = - Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span) + val (bindings, inlinedRhs) = inlined(rhs) + // assert(bindings.isEmpty) + Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) private def cloneClass(clDef: untpd.TypeDef, impl: Template)(using Context): TypeDef = val inlinedCls: ClassSymbol = From fa53620e9d1eba6ef28c518dc11633a93e7a0a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 17 Apr 2023 15:00:28 +0200 Subject: [PATCH 030/106] Perform misc. changes on tests Disable inner class tests for now Use better name for neg override test Add test for sealed inline trait Fix typo in override tests --- .../pos/inline-trait-body-class-abstract.scala | 0 tests/{ => disabled}/pos/inline-trait-body-class-case.scala | 0 tests/{ => disabled}/pos/inline-trait-body-class-enum.scala | 0 .../{ => disabled}/pos/inline-trait-body-class-generic.scala | 0 .../{ => disabled}/pos/inline-trait-body-class-object.scala | 0 .../{ => disabled}/pos/inline-trait-body-class-params.scala | 0 .../{ => disabled}/pos/inline-trait-body-class-sealed.scala | 0 .../{ => disabled}/pos/inline-trait-body-class-simple.scala | 0 ...ride.scala => inline-trait-body-override-def-final.scala} | 0 tests/neg/inline-trait-body-override-val-final.scala | 5 +++++ tests/pos/inline-trait-signature-sealed.scala | 4 ++++ tests/run/inline-trait-body-override-val.scala | 2 +- tests/run/inline-trait-body-override-var.scala | 2 +- 13 files changed, 11 insertions(+), 2 deletions(-) rename tests/{ => disabled}/pos/inline-trait-body-class-abstract.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-case.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-enum.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-generic.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-object.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-params.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-sealed.scala (100%) rename tests/{ => disabled}/pos/inline-trait-body-class-simple.scala (100%) rename tests/neg/{inline-trait-body-def-final-manual-override.scala => inline-trait-body-override-def-final.scala} (100%) create mode 100644 tests/neg/inline-trait-body-override-val-final.scala create mode 100644 tests/pos/inline-trait-signature-sealed.scala diff --git a/tests/pos/inline-trait-body-class-abstract.scala b/tests/disabled/pos/inline-trait-body-class-abstract.scala similarity index 100% rename from tests/pos/inline-trait-body-class-abstract.scala rename to tests/disabled/pos/inline-trait-body-class-abstract.scala diff --git a/tests/pos/inline-trait-body-class-case.scala b/tests/disabled/pos/inline-trait-body-class-case.scala similarity index 100% rename from tests/pos/inline-trait-body-class-case.scala rename to tests/disabled/pos/inline-trait-body-class-case.scala diff --git a/tests/pos/inline-trait-body-class-enum.scala b/tests/disabled/pos/inline-trait-body-class-enum.scala similarity index 100% rename from tests/pos/inline-trait-body-class-enum.scala rename to tests/disabled/pos/inline-trait-body-class-enum.scala diff --git a/tests/pos/inline-trait-body-class-generic.scala b/tests/disabled/pos/inline-trait-body-class-generic.scala similarity index 100% rename from tests/pos/inline-trait-body-class-generic.scala rename to tests/disabled/pos/inline-trait-body-class-generic.scala diff --git a/tests/pos/inline-trait-body-class-object.scala b/tests/disabled/pos/inline-trait-body-class-object.scala similarity index 100% rename from tests/pos/inline-trait-body-class-object.scala rename to tests/disabled/pos/inline-trait-body-class-object.scala diff --git a/tests/pos/inline-trait-body-class-params.scala b/tests/disabled/pos/inline-trait-body-class-params.scala similarity index 100% rename from tests/pos/inline-trait-body-class-params.scala rename to tests/disabled/pos/inline-trait-body-class-params.scala diff --git a/tests/pos/inline-trait-body-class-sealed.scala b/tests/disabled/pos/inline-trait-body-class-sealed.scala similarity index 100% rename from tests/pos/inline-trait-body-class-sealed.scala rename to tests/disabled/pos/inline-trait-body-class-sealed.scala diff --git a/tests/pos/inline-trait-body-class-simple.scala b/tests/disabled/pos/inline-trait-body-class-simple.scala similarity index 100% rename from tests/pos/inline-trait-body-class-simple.scala rename to tests/disabled/pos/inline-trait-body-class-simple.scala diff --git a/tests/neg/inline-trait-body-def-final-manual-override.scala b/tests/neg/inline-trait-body-override-def-final.scala similarity index 100% rename from tests/neg/inline-trait-body-def-final-manual-override.scala rename to tests/neg/inline-trait-body-override-def-final.scala diff --git a/tests/neg/inline-trait-body-override-val-final.scala b/tests/neg/inline-trait-body-override-val-final.scala new file mode 100644 index 000000000000..32d20c4b6696 --- /dev/null +++ b/tests/neg/inline-trait-body-override-val-final.scala @@ -0,0 +1,5 @@ +inline trait A: + final val x = 1 + +class B extends A: + override final val x = 2 // error \ No newline at end of file diff --git a/tests/pos/inline-trait-signature-sealed.scala b/tests/pos/inline-trait-signature-sealed.scala new file mode 100644 index 000000000000..db9d6a260ad3 --- /dev/null +++ b/tests/pos/inline-trait-signature-sealed.scala @@ -0,0 +1,4 @@ +inline sealed trait A: + final def f(x: Int) = x + +class B extends A \ No newline at end of file diff --git a/tests/run/inline-trait-body-override-val.scala b/tests/run/inline-trait-body-override-val.scala index a90448deafb6..ffd71ca0df56 100644 --- a/tests/run/inline-trait-body-override-val.scala +++ b/tests/run/inline-trait-body-override-val.scala @@ -2,7 +2,7 @@ inline trait A: val x: Int = 1 class B extends A: - override val x: 2 + override val x = 2 @main def Test = val b = B() diff --git a/tests/run/inline-trait-body-override-var.scala b/tests/run/inline-trait-body-override-var.scala index a4bf7238da24..cfcc3c19b2f1 100644 --- a/tests/run/inline-trait-body-override-var.scala +++ b/tests/run/inline-trait-body-override-var.scala @@ -2,7 +2,7 @@ inline trait A: var x: Int = 1 class B extends A: - override var x: 2 + override var x = 2 @main def Test = val b = B() From 59feb3f64b18d401bf28dc0408fb428515faa6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 17 Apr 2023 15:06:40 +0200 Subject: [PATCH 031/106] Only allow override final if member is synthetic Mark generated symbols as synthetic in order to allow the change to work --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 2 +- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 8e34dc2cf9f6..0708880d0b7b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -518,7 +518,7 @@ object Inlines: val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass else - var flags = sym.flags + var flags = sym.flags | Synthetic if sym.isType || !sym.is(Private) then flags |= Override if !sym.isType && sym.is(ParamAccessor) then flags &~= ParamAccessor sym.copy( diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index d0c2ef02baed..9a2bd58237cc 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -465,7 +465,7 @@ object RefChecks { overrideError("cannot be used here - opaque type aliases cannot be overridden") else if (!other.is(Deferred) && member.isClass) overrideError("cannot be used here - classes can only override abstract types") - else if other.isEffectivelyFinal && !other.owner.isInlineTrait then // (1.2) + else if other.isEffectivelyFinal && !(other.owner.isInlineTrait && member.is(Synthetic)) then // (1.2) overrideError(i"cannot override final member ${other.showLocated}") else if (member.is(ExtensionMethod) && !other.is(ExtensionMethod)) // (1.3) overrideError("is an extension method, cannot override a normal method") From 2804dfdddce08fa92f5e737441d2b4bb587dea85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 17 Apr 2023 15:07:22 +0200 Subject: [PATCH 032/106] Prevent generation of setters in inline traits --- compiler/src/dotty/tools/dotc/transform/Constructors.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Mixin.scala | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 4dd7205e4ee0..d98f5ae88987 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -226,7 +226,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = constrStats += intoConstr(stat, sym) } else dropped += sym - case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && !stat.symbol.is(Lazy) => + case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && !stat.symbol.is(Lazy) && !stat.symbol.owner.isInlineTrait => val sym = stat.symbol assert(isRetained(sym), sym) if sym.isConstExprFinalVal then diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 469c033b3d74..6e6adbeeafc2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -174,6 +174,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => sym.isGetter && !wasOneOf(sym, DeferredOrLazy | ParamAccessor) && atPhase(thisPhase) { !sym.setter.exists } && !sym.isConstExprFinalVal + && !sym.owner.isInlineTrait private def makeTraitSetter(getter: TermSymbol)(using Context): Symbol = getter.copy( @@ -288,7 +289,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => def setters(mixin: ClassSymbol): List[Tree] = val mixinSetters = mixin.info.decls.filter { sym => - sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) && !sym.owner.isAllOf(InlineTrait) + sym.isSetter && (!wasOneOf(sym, Deferred) || sym.name.is(TraitSetterName)) && !sym.owner.isInlineTrait } for (setter <- mixinSetters) yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) From 8b4741bcae82a5806eef9daab01c27d0c1f4c2ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 17 Apr 2023 15:08:10 +0200 Subject: [PATCH 033/106] Don't generate members if they are overridden by user --- .../src/dotty/tools/dotc/inlines/Inlines.scala | 14 +++++++++----- compiler/src/dotty/tools/dotc/typer/Typer.scala | 8 ++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 0708880d0b7b..5e726f7e0a22 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -170,9 +170,9 @@ object Inlines: tree2 end inlineCall - def inlineParentTrait(parent: tpd.Tree)(using Context): List[Tree] = + def inlineParentTrait(parent: tpd.Tree, childOverrideDecls: Set[Symbol])(using Context): List[Tree] = val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol - if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym).expandDefs() + if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym, childOverrideDecls).expandDefs() else Nil /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal @@ -466,14 +466,15 @@ object Inlines: end expand end InlineCall - private class InlineParentTrait(parent: tpd.Tree, parentSym: Symbol)(using Context) extends Inliner(parent): + private class InlineParentTrait(parent: tpd.Tree, parentSym: Symbol, overriddenDecls: Set[Symbol])(using Context) extends Inliner(parent): import tpd._ import Inlines.* def expandDefs(): List[Tree] = val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) - stats.zip(inlinedSymbols).map(expandStat)//.map(inlined(_)._2) + val stats1 = stats.filterNot(isMemberOverridden) + val inlinedSymbols = stats1.map(stat => inlinedMember(stat.symbol)) + stats1.zip(inlinedSymbols).map(expandStat)//.map(inlined(_)._2) end expandDefs private val argsMap: Map[Name, Tree] = @@ -498,6 +499,9 @@ object Inlines: case t => mapOver(t) } + private def isMemberOverridden(stat: Tree): Boolean = + overriddenDecls.flatMap(_.allOverriddenSymbols).toSet.contains(stat.symbol) + private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match case stat: ValDef if stat.symbol.is(ParamAccessor) => tpd.ValDef(inlinedSym.asTerm, argsMap(inlinedSym.name.asTermName)).withSpan(parent.span) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2441b106b9cb..3d09ed7fdec3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2668,12 +2668,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else { val dummy = localDummy(cls, impl) val inlineTraitDefs = - if ctx.isAfterTyper then Nil - else parents1.flatMap(Inlines.inlineParentTrait) + if ctx.isAfterTyper then + Nil + else + val clsDecls = cls.info.decls.toList.toSet + parents1.flatMap(parent => Inlines.inlineParentTrait(parent, clsDecls)) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs if !ctx.isAfterTyper && cls.isInlineTrait then def isConstructorType(t: Tree) = t.isInstanceOf[TypeDef] && cls.typeParams.contains(t.symbol) + // If the following is changed, remember to adapt TreeUnpickler.scala as well val membersToInline = body1.filter { t => !isConstructorType(t) && !t.symbol.is(Deferred) From 59d62218ee596363e497e2e89296db4bff60a072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 18 Apr 2023 17:23:37 +0200 Subject: [PATCH 034/106] Fix type params not being replaced in RHS Move inline trait related code back from Inliner to InlineParentTrait Use overridden version of DeepTypeMap to replace types in RHS of definitions Add test with trait-related type param in RHS --- .../dotty/tools/dotc/inlines/Inliner.scala | 49 +++++++++---------- .../dotty/tools/dotc/inlines/Inlines.scala | 28 ++++++----- tests/pos/inline-trait-body-rhs-type.scala | 6 +++ 3 files changed, 44 insertions(+), 39 deletions(-) create mode 100644 tests/pos/inline-trait-body-rhs-type.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 765b1806d61e..e8c1ece4c55a 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -158,7 +158,7 @@ class Inliner(val call: tpd.Tree)(using Context): for arg <- callTypeArgs do isFullyDefined(arg.tpe, ForceDegree.flipBottom) - /** A map from parameter names of the inlineable method or trait to references of the actual arguments. + /** A map from parameter names of the inlineable method to references of the actual arguments. * For a type argument this is the full argument type. * For a value argument, it is a reference to either the argument value * (if the argument is a pure expression of singleton type), or to `val` or `def` acting @@ -169,7 +169,7 @@ class Inliner(val call: tpd.Tree)(using Context): /** A map from parameter names of the inlineable method to spans of the actual arguments */ private val paramSpan = new mutable.HashMap[Name, Span] - /** A map from references to (type and value) parameters of the inlineable method or trait + /** A map from references to (type and value) parameters of the inlineable method * to their corresponding argument or proxy references, as given by `paramBinding`. */ private[inlines] val paramProxy = new mutable.HashMap[Type, Type] @@ -187,8 +187,7 @@ class Inliner(val call: tpd.Tree)(using Context): * * These are different (wrt ==) types but represent logically the same key */ - protected val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] - protected val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType] + private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] /** A buffer for bindings that define proxies for actual arguments */ private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] @@ -439,11 +438,10 @@ class Inliner(val call: tpd.Tree)(using Context): private def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) - /** Populate `thisProxy`, `thisInlineTraitProxy` and `paramProxy` as follows: + /** Populate `thisProxy` and `paramProxy` as follows: * * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, - * 1b. If given type refers to an inline trait, thisInlineTraitProxy binds it to the corresponding thistype, - * 1c. If given type refers to an instance this to a class that is not contained in the + * 1b. If given type refers to an instance this to a class that is not contained in the * inline method, create a proxy symbol and bind the thistype to refer to the proxy. * The proxy is not yet entered in `bindingsBuf`; that will come later. * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored @@ -453,7 +451,7 @@ class Inliner(val call: tpd.Tree)(using Context): * and MethodParams, not TypeRefs or TermRefs. */ private def registerType(tpe: Type): Unit = tpe match { - case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => + case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) && !tpe.cls.isInlineTrait => val proxyName = s"${tpe.cls.name}_this".toTermName val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { case typeMatchResult if typeMatchResult.exists => typeMatchResult @@ -462,10 +460,6 @@ class Inliner(val call: tpd.Tree)(using Context): thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef for (param <- tpe.cls.typeParams) paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) - case tpe: ThisType if tpe.cls.isInlineTrait => - thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) - for (param <- tpe.cls.typeParams) - paramProxy(param.typeRef) = adaptToPrefix(param.typeRef) case tpe: NamedType if tpe.symbol.is(Param) && tpe.symbol.owner == inlinedMethod @@ -511,9 +505,23 @@ class Inliner(val call: tpd.Tree)(using Context): val reducer = new InlineReducer(this) + protected class InlinerTypeMap extends DeepTypeMap { + override def stopAt = + if opaqueProxies.isEmpty then StopAt.Static else StopAt.Package + def apply(t: Type) = t match { + case t: ThisType => thisProxy.get(t.cls).getOrElse(t) + case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) + case t: SingletonType => + if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) + else paramProxy.getOrElse(t, mapOver(t)) + case t => mapOver(t) + } + } + + protected val inlinerTypeMap: InlinerTypeMap = InlinerTypeMap() + /** The Inlined node representing the inlined call */ def inlined(rhsToInline: tpd.Tree): (List[MemberDef], Tree) = - inlining.println(i"-----------------------\nInlining $call\nWith RHS $rhsToInline") def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match @@ -565,19 +573,7 @@ class Inliner(val call: tpd.Tree)(using Context): // corresponding arguments or proxies on the type and term level. It also changes // the owner from the inlined method to the current owner. val inliner = new InlinerMap( - typeMap = - new DeepTypeMap { - override def stopAt = - if opaqueProxies.isEmpty then StopAt.Static else StopAt.Package - def apply(t: Type) = t match { - case t: ThisType => thisProxy.get(t.cls).orElse(thisInlineTraitProxy.get(t.cls)).getOrElse(t) - case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) - case t: SingletonType => - if t.termSymbol.isAllOf(InlineParam) then apply(t.widenTermRefExpr) - else paramProxy.getOrElse(t, mapOver(t)) - case t => mapOver(t) - } - }, + typeMap = inlinerTypeMap, treeMap = { case tree: This => tree.tpe match { @@ -626,7 +622,6 @@ class Inliner(val call: tpd.Tree)(using Context): inlining.println( i"""inliner transform with |thisProxy = ${thisProxy.toList.map(_._1)}%, % --> ${thisProxy.toList.map(_._2)}%, % - |thisInlineTraitProxy = ${thisInlineTraitProxy.toList.map(_._1)}%, % --> ${thisInlineTraitProxy.toList.map(_._2)}%, % |paramProxy = ${paramProxy.toList.map(_._1.typeSymbol.showLocated)}%, % --> ${paramProxy.toList.map(_._2)}%, %""") // Apply inliner to `rhsToInline`, split off any implicit bindings from result, and diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 5e726f7e0a22..7fc02d7ac120 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -172,7 +172,7 @@ object Inlines: def inlineParentTrait(parent: tpd.Tree, childOverrideDecls: Set[Symbol])(using Context): List[Tree] = val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol - if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym, childOverrideDecls).expandDefs() + if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass, childOverrideDecls).expandDefs() else Nil /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal @@ -466,10 +466,12 @@ object Inlines: end expand end InlineCall - private class InlineParentTrait(parent: tpd.Tree, parentSym: Symbol, overriddenDecls: Set[Symbol])(using Context) extends Inliner(parent): + private class InlineParentTrait(parent: tpd.Tree, parentSym: ClassSymbol, overriddenDecls: Set[Symbol])(using Context) extends Inliner(parent): import tpd._ import Inlines.* + private val thisInlineTraitProxy = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + def expandDefs(): List[Tree] = val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked val stats1 = stats.filterNot(isMemberOverridden) @@ -477,6 +479,15 @@ object Inlines: stats1.zip(inlinedSymbols).map(expandStat)//.map(inlined(_)._2) end expandDefs + protected class InlineTraitTypeMap extends InlinerTypeMap { + override def apply(t: Type) = t match { + case t: ThisType if t.cls == parentSym => thisInlineTraitProxy + case t => super.apply(t) + } + } + + override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() + private val argsMap: Map[Name, Tree] = def allArgs(tree: Tree): List[List[Tree]] = tree match case Apply(fun, args) => args :: allArgs(fun) @@ -492,13 +503,6 @@ object Inlines: else parent.symbol.info allParams(info).flatten.zip(allArgs(parent).reverse.flatten).toMap - private val substituteTypeParams = new TypeMap { - override def apply(t: Type): Type = t match - case TypeRef(ths: ThisType, sym: Symbol) if ths.cls == parentSym => - argsMap.get(sym.name).map(_.tpe).getOrElse(t) - case t => mapOver(t) - } - private def isMemberOverridden(stat: Tree): Boolean = overriddenDecls.flatMap(_.allOverriddenSymbols).toSet.contains(stat.symbol) @@ -528,7 +532,7 @@ object Inlines: sym.copy( owner = ctx.owner, flags = flags, - info = substituteTypeParams(sym.info), + info = inlinerTypeMap(sym.info), coord = spanCoord(parent.span)).entered private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = @@ -572,7 +576,7 @@ object Inlines: // TODO case tree: TypeDef => cloneTypeDef(tree) private def cloneDefDef(ddef: DefDef)(using Context): DefDef = - val inlinedSym = ddef.symbol.copy(owner = ctx.owner, info = substituteTypeParams(ddef.symbol.info), coord = spanCoord(parent.span)).entered + val inlinedSym = ddef.symbol.copy(owner = ctx.owner, info = inlinerTypeMap(ddef.symbol.info), coord = spanCoord(parent.span)).entered def rhsFun(paramss: List[List[Tree]]): Tree = val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) @@ -584,7 +588,7 @@ object Inlines: cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) private def cloneValDef(vdef: ValDef)(using Context): ValDef = - val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = substituteTypeParams(vdef.symbol.info), coord = spanCoord(parent.span)).entered + val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = inlinerTypeMap(vdef.symbol.info), coord = spanCoord(parent.span)).entered tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) // TODO clone local classes? private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = diff --git a/tests/pos/inline-trait-body-rhs-type.scala b/tests/pos/inline-trait-body-rhs-type.scala new file mode 100644 index 000000000000..5b439ddf0c19 --- /dev/null +++ b/tests/pos/inline-trait-body-rhs-type.scala @@ -0,0 +1,6 @@ +inline trait A[T](x: T): + def f: T = x: T + def g: T = identity[T](x) + def h: this.type = this: this.type + +class B extends A("Hello") From b762cc93a72c0d4798d147af447d18f819cbb80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 18 Apr 2023 17:48:30 +0200 Subject: [PATCH 035/106] Clean up InlineParentTrait --- .../dotty/tools/dotc/inlines/Inlines.scala | 94 +++++++------------ 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 7fc02d7ac120..235d0f046a78 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -474,9 +474,9 @@ object Inlines: def expandDefs(): List[Tree] = val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - val stats1 = stats.filterNot(isMemberOverridden) + val stats1 = stats.filterNot(isStatAlreadyOverridden) val inlinedSymbols = stats1.map(stat => inlinedMember(stat.symbol)) - stats1.zip(inlinedSymbols).map(expandStat)//.map(inlined(_)._2) + stats1.zip(inlinedSymbols).map(expandStat) end expandDefs protected class InlineTraitTypeMap extends InlinerTypeMap { @@ -488,27 +488,26 @@ object Inlines: override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() - private val argsMap: Map[Name, Tree] = + private val paramAccessorsValueOf: Map[Name, Tree] = def allArgs(tree: Tree): List[List[Tree]] = tree match case Apply(fun, args) => args :: allArgs(fun) - case TypeApply(fun, targs) => targs :: allArgs(fun) - case AppliedTypeTree(_, targs) => targs :: Nil + case TypeApply(fun, _) => allArgs(fun) case _ => Nil def allParams(info: Type): List[List[Name]] = info match case mt: MethodType => mt.paramNames :: allParams(mt.resultType) - case pt: PolyType => pt.paramNames :: allParams(pt.resultType) + case pt: PolyType => allParams(pt.resultType) case _ => Nil val info = if parent.symbol.isClass then parent.symbol.primaryConstructor.info else parent.symbol.info allParams(info).flatten.zip(allArgs(parent).reverse.flatten).toMap - private def isMemberOverridden(stat: Tree): Boolean = + private def isStatAlreadyOverridden(stat: Tree): Boolean = overriddenDecls.flatMap(_.allOverriddenSymbols).toSet.contains(stat.symbol) private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match case stat: ValDef if stat.symbol.is(ParamAccessor) => - tpd.ValDef(inlinedSym.asTerm, argsMap(inlinedSym.name.asTermName)).withSpan(parent.span) + tpd.ValDef(inlinedSym.asTerm, paramAccessorsValueOf(stat.symbol.name)).withSpan(parent.span) case stat: ValDef => inlinedValDef(stat, inlinedSym) case stat: DefDef if stat.symbol.isSetter => @@ -521,19 +520,26 @@ object Inlines: inlinedTypeDef(stat, inlinedSym) private def inlinedMember(sym: Symbol)(using Context): Symbol = - if sym.isClass then - val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = sym.info: @unchecked - val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents - sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass - else - var flags = sym.flags | Synthetic - if sym.isType || !sym.is(Private) then flags |= Override - if !sym.isType && sym.is(ParamAccessor) then flags &~= ParamAccessor - sym.copy( - owner = ctx.owner, - flags = flags, - info = inlinerTypeMap(sym.info), - coord = spanCoord(parent.span)).entered + sym.info match { + case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => + val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents + sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass + case _ => + var flags = sym.flags | Synthetic + if sym.isType || !sym.is(Private) then flags |= Override + if !sym.isType && sym.is(ParamAccessor) then flags &~= ParamAccessor + sym.copy( + owner = ctx.owner, + flags = flags, + info = inlinerTypeMap(sym.info), + coord = spanCoord(parent.span)).entered + } + + private def inlinedStat(stat: Tree)(using Context): Tree = stat match { + // TODO check if symbols must be copied + case tree: DefDef => inlinedDefDef(tree, tree.symbol) + case tree: ValDef => inlinedValDef(tree, tree.symbol) + } private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) @@ -545,9 +551,14 @@ object Inlines: inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) + private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = + // TODO check if symbol must be copied + val constr = inlinedDefDef(ddef, ddef.symbol) + cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) + private def inlinedClassDef(clDef: TypeDef, impl: Template, inlinedCls: ClassSymbol)(using Context): Tree = val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { - (clonePrimaryConstructorDefDef(impl.constr), impl.body.map(cloneStat)) + (inlinedPrimaryConstructorDefDef(impl.constr), impl.body.map(inlinedStat)) } inlined(tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body))._2.withSpan(clDef.span) // TODO adapt parents @@ -555,45 +566,8 @@ object Inlines: tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) private def inlinedRhs(rhs: Tree): Inlined = - val (bindings, inlinedRhs) = inlined(rhs) - // assert(bindings.isEmpty) + val inlinedRhs = inlined(rhs)._2 Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) - private def cloneClass(clDef: untpd.TypeDef, impl: Template)(using Context): TypeDef = - val inlinedCls: ClassSymbol = - val ClassInfo(prefix, cls, declaredParents, scope, selfInfo) = clDef.symbol.info: @unchecked - val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents - clDef.symbol.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass - val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { - (clonePrimaryConstructorDefDef(impl.constr), impl.body.map(cloneStat)) - } - tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body).withSpan(clDef.span) // TODO adapt parents - - private def cloneStat(tree: Tree)(using Context): Tree = tree match - case tree: DefDef => cloneDefDef(tree) - case tree: ValDef => cloneValDef(tree) - // TODO case stat @ TypeDef(_, impl: Template) => cloneClass(stat, impl) - // TODO case tree: TypeDef => cloneTypeDef(tree) - - private def cloneDefDef(ddef: DefDef)(using Context): DefDef = - val inlinedSym = ddef.symbol.copy(owner = ctx.owner, info = inlinerTypeMap(ddef.symbol.info), coord = spanCoord(parent.span)).entered - def rhsFun(paramss: List[List[Tree]]): Tree = - val oldParamSyms = ddef.paramss.flatten.map(_.symbol) - val newParamSyms = paramss.flatten.map(_.symbol) - inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) // TODO clone local classes? - tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) - - private def clonePrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = - val constr = cloneDefDef(ddef) - cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) - - private def cloneValDef(vdef: ValDef)(using Context): ValDef = - val inlinedSym = vdef.symbol.copy(owner = ctx.owner, info = inlinerTypeMap(vdef.symbol.info), coord = spanCoord(parent.span)).entered - tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) // TODO clone local classes? - - private def cloneTypeDef(tdef: TypeDef)(using Context): TypeDef = - val inlinedSym = tdef.symbol.copy(owner = ctx.owner, coord = spanCoord(parent.span)).entered - tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) - end InlineParentTrait end Inlines From 4bf1c768267c3139f60e6149549ad6640ff8f320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 20 Apr 2023 15:22:32 +0200 Subject: [PATCH 036/106] Compute overriden symbols only once --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 235d0f046a78..c645a1f93da3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -170,9 +170,9 @@ object Inlines: tree2 end inlineCall - def inlineParentTrait(parent: tpd.Tree, childOverrideDecls: Set[Symbol])(using Context): List[Tree] = + def inlineParentTrait(parent: tpd.Tree, overriddenDecls: Set[Symbol])(using Context): List[Tree] = val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol - if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass, childOverrideDecls).expandDefs() + if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass, overriddenDecls).expandDefs() else Nil /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal @@ -503,7 +503,7 @@ object Inlines: allParams(info).flatten.zip(allArgs(parent).reverse.flatten).toMap private def isStatAlreadyOverridden(stat: Tree): Boolean = - overriddenDecls.flatMap(_.allOverriddenSymbols).toSet.contains(stat.symbol) + overriddenDecls.contains(stat.symbol) private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match case stat: ValDef if stat.symbol.is(ParamAccessor) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3d09ed7fdec3..f749ea11451d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2671,8 +2671,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if ctx.isAfterTyper then Nil else - val clsDecls = cls.info.decls.toList.toSet - parents1.flatMap(parent => Inlines.inlineParentTrait(parent, clsDecls)) + val overriddenSyms = cls.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet + parents1.flatMap(parent => Inlines.inlineParentTrait(parent, overriddenSyms)) val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs if !ctx.isAfterTyper && cls.isInlineTrait then From e92c7b5bfd9772ad7f4419ed0cdbec5baced0102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 21 Apr 2023 11:43:15 +0200 Subject: [PATCH 037/106] Factorize code to know what to inline from inline trait --- .../dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 7 ++----- compiler/src/dotty/tools/dotc/typer/Typer.scala | 13 ++++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 44fe88da2e74..ec6962a7d3db 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -23,6 +23,7 @@ import NamerOps._ import ContextOps._ import Variances.Invariant import TastyUnpickler.NameTable +import typer.Typer import typer.ConstFold import typer.Checking.checkNonCyclic import typer.Nullables._ @@ -1063,11 +1064,7 @@ class TreeUnpickler(reader: TastyReader, val fork = forkAt(statsStart) val stats = fork.readIndexedStats(localDummy, end) - val inlinedMembers = (tparams ++ vparams ++ stats).filter { stat => - !(stat.isInstanceOf[TypeDef] && cls.typeParams.contains(stat.symbol)) // !isConstructor - && !stat.symbol.is(Deferred) - && !stat.symbol.isAllOf(Inline) - } + val inlinedMembers = (tparams ++ vparams ++ stats).filter(member => Typer.isInlineableFromInlineTrait(cls, member)) Block(inlinedMembers, unitLiteral).withSpan(cls.span) } }) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index f749ea11451d..0ddd038f8fc5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -108,6 +108,11 @@ object Typer { def rememberSearchFailure(tree: tpd.Tree, fail: SearchFailure) = tree.putAttachment(HiddenSearchFailure, fail :: tree.attachmentOrElse(HiddenSearchFailure, Nil)) + + def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = + !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) + && !member.symbol.is(Deferred) + && !member.symbol.isAllOf(Inline) } /** Typecheck trees, the main entry point is `typed`. * @@ -2676,13 +2681,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs if !ctx.isAfterTyper && cls.isInlineTrait then - def isConstructorType(t: Tree) = t.isInstanceOf[TypeDef] && cls.typeParams.contains(t.symbol) - // If the following is changed, remember to adapt TreeUnpickler.scala as well - val membersToInline = body1.filter { t => - !isConstructorType(t) - && !t.symbol.is(Deferred) - && !t.symbol.isAllOf(Inline) - } + val membersToInline = body1.filter(member => isInlineableFromInlineTrait(cls, member)) val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) From c676689612846f705f56b12c178675c6e1a26ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 21 Apr 2023 11:45:20 +0200 Subject: [PATCH 038/106] Allow inlining of deferred defs --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 9 ++++++--- compiler/src/dotty/tools/dotc/typer/Typer.scala | 1 - tests/pos/inline-trait-body-abstract-def.scala | 10 +++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c645a1f93da3..583cf8b88f76 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -565,9 +565,12 @@ object Inlines: private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) - private def inlinedRhs(rhs: Tree): Inlined = - val inlinedRhs = inlined(rhs)._2 - Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) + private def inlinedRhs(rhs: Tree): Tree = + if rhs.isEmpty then + rhs + else + val inlinedRhs = inlined(rhs)._2 + Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) end InlineParentTrait end Inlines diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0ddd038f8fc5..8c466c1c5470 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -111,7 +111,6 @@ object Typer { def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) - && !member.symbol.is(Deferred) && !member.symbol.isAllOf(Inline) } /** Typecheck trees, the main entry point is `typed`. diff --git a/tests/pos/inline-trait-body-abstract-def.scala b/tests/pos/inline-trait-body-abstract-def.scala index c604441bd079..5b675a845236 100644 --- a/tests/pos/inline-trait-body-abstract-def.scala +++ b/tests/pos/inline-trait-body-abstract-def.scala @@ -1,6 +1,6 @@ -inline trait A: - def x: Int +inline trait A[T]: + def x: T -class B extends A: - override def x: Int = 1 - def f = x \ No newline at end of file +class B extends A[Int]: + def x = 1 + def f: Int = x \ No newline at end of file From b9a81b117922335c636c9bf4efd039d6095e20ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 21 Apr 2023 12:10:08 +0200 Subject: [PATCH 039/106] Clean up example 4, make version without inner class --- tests/pos/inline-trait-4-inner-class.scala | 38 ++++++++----------- tests/pos/inline-trait-4-no-inner-class.scala | 24 ++++++++++++ 2 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 tests/pos/inline-trait-4-no-inner-class.scala diff --git a/tests/pos/inline-trait-4-inner-class.scala b/tests/pos/inline-trait-4-inner-class.scala index 5a9a6fa397c9..b3c25e75de4c 100644 --- a/tests/pos/inline-trait-4-inner-class.scala +++ b/tests/pos/inline-trait-4-inner-class.scala @@ -1,28 +1,20 @@ -inline trait Options[T]: - // FIXME: remove original class definition from inline trait to allow public classes - private trait Option: +inline trait Options[+T]: + sealed trait Option: def get: T - // FIXME: support constructor parameter - // FIXME: support specialized parents - private class Some://(x: T): // extends Option - def get: T = ??? // x - // FIXME: support specialized modules - // private object None // extends Option - // def get: T = throw new NoSuchElementException("None.get") + def isEmpty: Boolean - // FIXME: specialize reference to Option and Some - // def option(value: T): Option = new Some//(value) + class Some(x: T) extends Option: + def get: T = x + def isEmpty: Boolean = false + + object None extends Option: + def get: T = throw new NoSuchElementException("None.get") + def isEmpty: Boolean = true end Options -object IntOptions extends Options[Int]: - /* - trait Option: - def get: Int - class Some(x: Int) extends Option - def get: Int = x - object None extends Option - def get: Int = throw new NoSuchElementException("None.get") +object IntOptions extends Options[Int] +import IntOptions._ - def option(value: Int): Option = new Some(value) - */ -end IntOptions +val o1: Option = Some(1) // specialized +val o2: Option = None +val x1: Int = o1.get // no unboxing diff --git a/tests/pos/inline-trait-4-no-inner-class.scala b/tests/pos/inline-trait-4-no-inner-class.scala new file mode 100644 index 000000000000..063f8e654fe5 --- /dev/null +++ b/tests/pos/inline-trait-4-no-inner-class.scala @@ -0,0 +1,24 @@ +inline trait Option[+T]: + def get: T + def isEmpty: Boolean +end Option + +inline trait Some[+T](x: T) extends Option[T]: + def get: T = x + def isEmpty: Boolean = false +end Some + +inline trait None extends Option[Nothing]: + def get: Nothing = throw new NoSuchElementException("None.get") + def isEmpty: Boolean = true +end None + +sealed trait IntOption extends Option[Int] +class IntSome(i: Int) extends IntOption, Some[Int](i) +object IntNone extends IntOption, None + +val o1: IntOption = IntSome(1) // specialized +val o2: IntOption = IntNone +val o3: Some[Int] = IntSome(1) // non-specialized +val x1: Int = o1.get // no unboxing +val x3: Int = o3.get // unboxing From 026212fd1853c87e349dcc5f624dd76fdda91e04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 21 Apr 2023 12:11:52 +0200 Subject: [PATCH 040/106] Update inner class tests --- ...inline-trait-body-class-extends-inline-trait.scala | 11 +++++++++++ .../disabled/pos/inline-trait-body-class-object.scala | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala diff --git a/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala b/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala new file mode 100644 index 000000000000..1f1a3836fa98 --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-class-extends-inline-trait.scala @@ -0,0 +1,11 @@ +inline trait A: + class Inner extends Trait[Int]: + val x = 1 + +inline trait Trait[T]: + def f(x: T): T = x + +class B extends A: + val inner = Inner() + def x = inner.x + def f = inner.f(x) \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-body-class-object.scala b/tests/disabled/pos/inline-trait-body-class-object.scala index d2b82073584e..dc990c69573c 100644 --- a/tests/disabled/pos/inline-trait-body-class-object.scala +++ b/tests/disabled/pos/inline-trait-body-class-object.scala @@ -1,6 +1,6 @@ -inline trait A: +inline trait A[T]: object Inner: - val x = 1 + val x: T = ??? -class B extends A: - def f = Inner.x +class B extends A[Int]: + def i: Int = Inner.x From 76e5e9a16e4b167f5e5052f3b3884dff056513dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 24 Apr 2023 11:41:39 +0200 Subject: [PATCH 041/106] Move special expansion cases in inlining methods --- .../dotty/tools/dotc/inlines/Inlines.scala | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 583cf8b88f76..6693dfa32d2d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -506,12 +506,8 @@ object Inlines: overriddenDecls.contains(stat.symbol) private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match - case stat: ValDef if stat.symbol.is(ParamAccessor) => - tpd.ValDef(inlinedSym.asTerm, paramAccessorsValueOf(stat.symbol.name)).withSpan(parent.span) case stat: ValDef => inlinedValDef(stat, inlinedSym) - case stat: DefDef if stat.symbol.isSetter => - tpd.DefDef(inlinedSym.asTerm, unitLiteral).withSpan(parent.span) case stat: DefDef => inlinedDefDef(stat, inlinedSym) case stat @ TypeDef(_, impl: Template) => @@ -542,13 +538,22 @@ object Inlines: } private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = - tpd.ValDef(inlinedSym.asTerm, inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym))).withSpan(parent.span) + val rhs = + if vdef.symbol.is(ParamAccessor) then + paramAccessorsValueOf(vdef.symbol.name) + else + inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym)) + tpd.ValDef(inlinedSym.asTerm, rhs).withSpan(parent.span) private def inlinedDefDef(ddef: DefDef, inlinedSym: Symbol)(using Context): DefDef = - def rhsFun(paramss: List[List[Tree]]): Tree = - val oldParamSyms = ddef.paramss.flatten.map(_.symbol) - val newParamSyms = paramss.flatten.map(_.symbol) - inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) + val rhsFun: List[List[Tree]] => Tree = + if ddef.symbol.isSetter then + _ => unitLiteral + else + paramss => + val oldParamSyms = ddef.paramss.flatten.map(_.symbol) + val newParamSyms = paramss.flatten.map(_.symbol) + inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = From a4fd77690454ab5ce76fbf2731277cdcf4e0bf2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 24 Apr 2023 14:05:34 +0200 Subject: [PATCH 042/106] Create InlineTreeMap, make helper function for param accessors --- .../dotty/tools/dotc/inlines/Inliner.scala | 89 ++++++++++--------- .../dotty/tools/dotc/inlines/Inlines.scala | 7 +- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e8c1ece4c55a..e71d789d86da 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -518,7 +518,53 @@ class Inliner(val call: tpd.Tree)(using Context): } } + protected class InlinerTreeMap extends (Tree => Tree) { + def apply(tree: Tree) = tree match { + case tree: This => + tree.tpe match { + case thistpe: ThisType => + val cls = thistpe.cls + if cls.isInlineTrait then + integrate(This(ctx.owner.asClass).withSpan(call.span), cls) + else thisProxy.get(cls) match { + case Some(t) => + val thisRef = ref(t).withSpan(call.span) + inlinedFromOutside(thisRef)(tree.span) + case None => tree + } + case _ => tree + } + case tree: Ident => + /* Span of the argument. Used when the argument is inlined directly without a binding */ + def argSpan = + if (tree.name == nme.WILDCARD) tree.span // From type match + else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span? + else paramSpan(tree.name) + val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source) + paramProxy.get(tree.tpe) match { + case Some(t) if tree.isTerm && t.isSingleton => + val inlinedSingleton = singleton(t).withSpan(argSpan) + inlinedFromOutside(inlinedSingleton)(tree.span) + case Some(t) if tree.isType => + inlinedFromOutside(TypeTree(t).withSpan(argSpan))(tree.span) + case _ => tree + } + case tree @ Select(qual: This, name) if tree.symbol.is(Private) && tree.symbol.isInlineMethod => + // This inline method refers to another (private) inline method (see tests/pos/i14042.scala). + // We insert upcast to access the private inline method once inlined. This makes the selection + // keep the symbol when re-typechecking in the InlineTyper. The method is inlined and hence no + // reference to a private method is kept at runtime. + cpy.Select(tree)(qual.asInstance(qual.tpe.widen), name) + + case tree => tree + } + + private def inlinedFromOutside(tree: Tree)(span: Span): Tree = + Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span) + } + protected val inlinerTypeMap: InlinerTypeMap = InlinerTypeMap() + protected val inlinerTreeMap: InlinerTreeMap = InlinerTreeMap() /** The Inlined node representing the inlined call */ def inlined(rhsToInline: tpd.Tree): (List[MemberDef], Tree) = @@ -565,54 +611,13 @@ class Inliner(val call: tpd.Tree)(using Context): val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope - def inlinedFromOutside(tree: Tree)(span: Span): Tree = - Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span) - // A tree type map to prepare the inlined body for typechecked. // The translation maps references to `this` and parameters to // corresponding arguments or proxies on the type and term level. It also changes // the owner from the inlined method to the current owner. val inliner = new InlinerMap( typeMap = inlinerTypeMap, - treeMap = { - case tree: This => - tree.tpe match { - case thistpe: ThisType => - val cls = thistpe.cls - if cls.isInlineTrait then - integrate(This(ctx.owner.asClass).withSpan(call.span), cls) - else thisProxy.get(cls) match { - case Some(t) => - val thisRef = ref(t).withSpan(call.span) - inlinedFromOutside(thisRef)(tree.span) - case None => tree - } - case _ => tree - } - case tree: Ident => - /* Span of the argument. Used when the argument is inlined directly without a binding */ - def argSpan = - if (tree.name == nme.WILDCARD) tree.span // From type match - else if (tree.symbol.isTypeParam && tree.symbol.owner.isClass) tree.span // TODO is this the correct span? - else paramSpan(tree.name) - val inlinedCtx = ctx.withSource(inlinedMethod.topLevelClass.source) - paramProxy.get(tree.tpe) match { - case Some(t) if tree.isTerm && t.isSingleton => - val inlinedSingleton = singleton(t).withSpan(argSpan) - inlinedFromOutside(inlinedSingleton)(tree.span) - case Some(t) if tree.isType => - inlinedFromOutside(TypeTree(t).withSpan(argSpan))(tree.span) - case _ => tree - } - case tree @ Select(qual: This, name) if tree.symbol.is(Private) && tree.symbol.isInlineMethod => - // This inline method refers to another (private) inline method (see tests/pos/i14042.scala). - // We insert upcast to access the private inline method once inlined. This makes the selection - // keep the symbol when re-typechecking in the InlineTyper. The method is inlined and hence no - // reference to a private method is kept at runtime. - cpy.Select(tree)(qual.asInstance(qual.tpe.widen), name) - - case tree => tree - }, + treeMap = inlinerTreeMap, oldOwners = inlinedMethod :: Nil, newOwners = ctx.owner :: Nil, substFrom = Nil, diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 6693dfa32d2d..3130f672dbce 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -505,6 +505,9 @@ object Inlines: private def isStatAlreadyOverridden(stat: Tree): Boolean = overriddenDecls.contains(stat.symbol) + extension (sym: Symbol) + private def isTermParamAccessor: Boolean = !sym.isType && sym.is(ParamAccessor) + private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match case stat: ValDef => inlinedValDef(stat, inlinedSym) @@ -523,7 +526,7 @@ object Inlines: case _ => var flags = sym.flags | Synthetic if sym.isType || !sym.is(Private) then flags |= Override - if !sym.isType && sym.is(ParamAccessor) then flags &~= ParamAccessor + if sym.isTermParamAccessor then flags &~= ParamAccessor sym.copy( owner = ctx.owner, flags = flags, @@ -539,7 +542,7 @@ object Inlines: private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = val rhs = - if vdef.symbol.is(ParamAccessor) then + if vdef.symbol.isTermParamAccessor then paramAccessorsValueOf(vdef.symbol.name) else inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym)) From 334b5d36170bd552f7e5e168f97adb8f4c9ee864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 24 Apr 2023 14:19:07 +0200 Subject: [PATCH 043/106] Make accessor values functions tailrec Make the helper functions to compute parameters and arguments tail recursive, and insert elements in the right order to avoid doing `.reverse` afterwards Also remove "proxy" from "thisInlineTraitProxy", as it is not really a proxy --- .../dotty/tools/dotc/inlines/Inlines.scala | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 3130f672dbce..8d710c81fb36 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -470,7 +470,7 @@ object Inlines: import tpd._ import Inlines.* - private val thisInlineTraitProxy = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + private val thisInlineTrait = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) def expandDefs(): List[Tree] = val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked @@ -481,7 +481,7 @@ object Inlines: protected class InlineTraitTypeMap extends InlinerTypeMap { override def apply(t: Type) = t match { - case t: ThisType if t.cls == parentSym => thisInlineTraitProxy + case t: ThisType if t.cls == parentSym => thisInlineTrait case t => super.apply(t) } } @@ -489,18 +489,18 @@ object Inlines: override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() private val paramAccessorsValueOf: Map[Name, Tree] = - def allArgs(tree: Tree): List[List[Tree]] = tree match - case Apply(fun, args) => args :: allArgs(fun) - case TypeApply(fun, _) => allArgs(fun) - case _ => Nil - def allParams(info: Type): List[List[Name]] = info match - case mt: MethodType => mt.paramNames :: allParams(mt.resultType) - case pt: PolyType => allParams(pt.resultType) - case _ => Nil + def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match + case Apply(fun, args) => allArgs(fun, acc :+ args) + case TypeApply(fun, _) => allArgs(fun, acc) + case _ => acc.toList + def allParams(info: Type, acc: List[List[Name]]): List[List[Name]] = info match + case mt: MethodType => allParams(mt.resultType, mt.paramNames :: acc) + case pt: PolyType => allParams(pt.resultType, acc) + case _ => acc val info = if parent.symbol.isClass then parent.symbol.primaryConstructor.info else parent.symbol.info - allParams(info).flatten.zip(allArgs(parent).reverse.flatten).toMap + allParams(info, Nil).flatten.zip(allArgs(parent, Vector.empty).flatten).toMap private def isStatAlreadyOverridden(stat: Tree): Boolean = overriddenDecls.contains(stat.symbol) From f1aa33e49e956537695024fb22e06342143f1a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 24 Apr 2023 14:24:57 +0200 Subject: [PATCH 044/106] Implement grandparent inline traits, fix parameters shadowing Allow an inline trait to extend another inline trait, and prevent code from being inlined inside the child inline trait: only inline in first non-inline class-like element When parameters shadow each other (e.g. val x and private[this] val x), rename the instance-private ones to avoid clash If multiple non-instance-private fields are present, the compiler should still raise the same error as with normal traits --- .../dotty/tools/dotc/inlines/Inlines.scala | 21 +++++++++++- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- ...trait-inheritance-inline-grandparent.check | 6 ++++ ...trait-inheritance-inline-grandparent.scala | 33 +++++++++++++++++++ 4 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/run/inline-trait-inheritance-inline-grandparent.check create mode 100644 tests/run/inline-trait-inheritance-inline-grandparent.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 8d710c81fb36..74a80a7cca58 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -20,6 +20,7 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} import util.Spans.{Span, spanCoord} +import NameOps.expandedName /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -486,7 +487,17 @@ object Inlines: } } + protected class InlineTraitTreeMap extends InlinerTreeMap { + override def apply(tree: Tree) = tree match { + case tree @ Select(This(ident), name) if ident.name == parentSym.name && localParamAccessorsNames.contains(name) => + Select(This(ctx.owner.asClass), localParamAccessorsNames(name)).withSpan(parent.span) + case tree => + super.apply(tree) + } + } + override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() + override protected val inlinerTreeMap: InlinerTreeMap = InlineTraitTreeMap() private val paramAccessorsValueOf: Map[Name, Tree] = def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match @@ -502,6 +513,8 @@ object Inlines: else parent.symbol.info allParams(info, Nil).flatten.zip(allArgs(parent, Vector.empty).flatten).toMap + private val localParamAccessorsNames = new mutable.HashMap[Name, Name] + private def isStatAlreadyOverridden(stat: Tree): Boolean = overriddenDecls.contains(stat.symbol) @@ -524,11 +537,17 @@ object Inlines: val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass case _ => + var name = sym.name var flags = sym.flags | Synthetic if sym.isType || !sym.is(Private) then flags |= Override - if sym.isTermParamAccessor then flags &~= ParamAccessor + if sym.isTermParamAccessor then + flags &~= ParamAccessor + if sym.is(Local) then + name = name.expandedName(parentSym) + localParamAccessorsNames(sym.name) = name sym.copy( owner = ctx.owner, + name = name, flags = flags, info = inlinerTypeMap(sym.info), coord = spanCoord(parent.span)).entered diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8c466c1c5470..fc3041d98f0e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2672,7 +2672,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else { val dummy = localDummy(cls, impl) val inlineTraitDefs = - if ctx.isAfterTyper then + if ctx.isAfterTyper || cls.isInlineTrait then Nil else val overriddenSyms = cls.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet diff --git a/tests/run/inline-trait-inheritance-inline-grandparent.check b/tests/run/inline-trait-inheritance-inline-grandparent.check new file mode 100644 index 000000000000..02a3fcae5b4e --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-grandparent.check @@ -0,0 +1,6 @@ +0 +(Test SimpleC,Hello) + +5678 +(Test C,Hello,9) +5678 diff --git a/tests/run/inline-trait-inheritance-inline-grandparent.scala b/tests/run/inline-trait-inheritance-inline-grandparent.scala new file mode 100644 index 000000000000..adc00b7b7bfe --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-grandparent.scala @@ -0,0 +1,33 @@ +package simpleGrandParent: + inline trait SimpleGrandParent[T]: + def foo(): Int = 0 + def foooo(): T = ??? + + inline trait SimpleParent[T, U](x: T, z: U) extends SimpleGrandParent[U]: + def bar(a: T) = (a, x) + + class SimpleC extends SimpleParent("Hello", 1234) + +package grandParentWithArgs: + inline trait GrandParent[T](val x: T, val y: T): + def foo(): T = x + def foooo(): T = y + + inline trait Parent[T, U](x: T, z: U) extends GrandParent[U]: + def bar(a: T) = (a, x, y) + + class C extends Parent("Hello", 1234), GrandParent(5678, 9) + +@main def Test = + import simpleGrandParent.SimpleC + import grandParentWithArgs.C + + val simpleC = SimpleC() + println(simpleC.foo()) + println(simpleC.bar("Test SimpleC")) + println + + val c = C() + println(c.foo()) + println(c.bar("Test C")) + println(c.x) \ No newline at end of file From 2d20a7946b38dc875f51dd316c11d590adcf59af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 26 Apr 2023 11:29:57 +0200 Subject: [PATCH 045/106] Clean up code to get defs to inline --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 74a80a7cca58..c047d3d648f1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -45,6 +45,11 @@ object Inlines: else EmptyTree + def defsToInline(traitSym: SymDenotation)(using Context): List[Tree] = + bodyToInline(traitSym) match + case Block(defs, _) if traitSym.isInlineTrait => defs + case _ => Nil + /** Are we in an inline method body? */ def inInlineMethod(using Context): Boolean = ctx.owner.ownersIterator.exists(_.isInlineMethod) @@ -474,10 +479,9 @@ object Inlines: private val thisInlineTrait = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) def expandDefs(): List[Tree] = - val tpd.Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked - val stats1 = stats.filterNot(isStatAlreadyOverridden) - val inlinedSymbols = stats1.map(stat => inlinedMember(stat.symbol)) - stats1.zip(inlinedSymbols).map(expandStat) + val stats = Inlines.defsToInline(parentSym).filterNot(isStatAlreadyOverridden) + val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) + stats.zip(inlinedSymbols).map(expandStat) end expandDefs protected class InlineTraitTypeMap extends InlinerTypeMap { From fcb227940a507e503083aa865443367e4dad5f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 26 Apr 2023 14:35:09 +0200 Subject: [PATCH 046/106] Start moving code from Typer --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 ++--- .../dotty/tools/dotc/inlines/Inlines.scala | 22 +++++++++++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 8 ++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ec6962a7d3db..3db04f7eda20 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -23,7 +23,6 @@ import NamerOps._ import ContextOps._ import Variances.Invariant import TastyUnpickler.NameTable -import typer.Typer import typer.ConstFold import typer.Checking.checkNonCyclic import typer.Nullables._ @@ -34,6 +33,7 @@ import Trees._ import Decorators._ import transform.SymUtils._ import cc.{adaptFunctionTypeUnderPureFuns, adaptByNameArgUnderPureFuns} +import inlines.Inlines import dotty.tools.tasty.{TastyBuffer, TastyReader} import TastyBuffer._ @@ -1051,12 +1051,12 @@ class TreeUnpickler(reader: TastyReader, .map(_.changeOwner(localDummy, constr.symbol))) else parents - val statsStart = currentAddr val lazyStats = readLater(end, rdr => { val stats = rdr.readIndexedStats(localDummy, end) tparams ++ vparams ++ stats }) if cls.isInlineTrait then + val statsStart = currentAddr cls.addAnnotation(LazyBodyAnnotation { (ctx0: Context) ?=> val ctx1 = localContext(cls)(using ctx0).addMode(Mode.ReadPositions) inContext(sourceChangeContext(Addr(0))(using ctx1)) { @@ -1064,7 +1064,7 @@ class TreeUnpickler(reader: TastyReader, val fork = forkAt(statsStart) val stats = fork.readIndexedStats(localDummy, end) - val inlinedMembers = (tparams ++ vparams ++ stats).filter(member => Typer.isInlineableFromInlineTrait(cls, member)) + val inlinedMembers = (tparams ++ vparams ++ stats).filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) Block(inlinedMembers, unitLiteral).withSpan(cls.span) } }) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c047d3d648f1..c6b2876ada5f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -58,20 +58,28 @@ object Inlines: def isInlineable(meth: Symbol)(using Context): Boolean = meth.is(Inline) && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod + def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = + !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) + && !member.symbol.isAllOf(Inline) + /** Should call be inlined in this context? */ - def needsInlining(tree: Tree)(using Context): Boolean = tree match { - case Block(_, expr) => needsInlining(expr) - case _ => - isInlineable(tree.symbol) - && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] - && StagingLevel.level == 0 + def needsInlining(tree: Tree)(using Context): Boolean = + val isInlineableInCtx = + StagingLevel.level == 0 && ( ctx.phase == Phases.inliningPhase || (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree)) ) && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining - } + + tree match + case Block(_, expr) => + needsInlining(expr) + case tdef @ TypeDef(_, impl: Template) => + !tdef.symbol.isInlineTrait && impl.parents.exists(_.symbol.isInlineTrait) && isInlineableInCtx + case _ => + isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && isInlineableInCtx private def needsTransparentInlining(tree: Tree)(using Context): Boolean = tree.symbol.is(Transparent) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index fc3041d98f0e..38bcf0a4028a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -29,7 +29,7 @@ import Inferencing._ import Dynamic.isDynamicExpansion import EtaExpansion.etaExpand import TypeComparer.CompareResult -import inlines.{Inliner, Inlines, PrepareInlineable} +import inlines.{Inlines, PrepareInlineable} import util.Spans._ import util.common._ import util.{Property, SimpleIdentityMap, SrcPos} @@ -108,10 +108,6 @@ object Typer { def rememberSearchFailure(tree: tpd.Tree, fail: SearchFailure) = tree.putAttachment(HiddenSearchFailure, fail :: tree.attachmentOrElse(HiddenSearchFailure, Nil)) - - def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = - !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) - && !member.symbol.isAllOf(Inline) } /** Typecheck trees, the main entry point is `typed`. * @@ -2680,7 +2676,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs if !ctx.isAfterTyper && cls.isInlineTrait then - val membersToInline = body1.filter(member => isInlineableFromInlineTrait(cls, member)) + val membersToInline = body1.filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) From 84a1682d3eb3d9d3eda76f0cd4f923bfd3dc4479 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 26 Apr 2023 13:56:52 +0200 Subject: [PATCH 047/106] Fix tests/pos/inline-trait-multiple-stages-generic-defs --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c6b2876ada5f..1c69671f0357 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -502,7 +502,7 @@ object Inlines: protected class InlineTraitTreeMap extends InlinerTreeMap { override def apply(tree: Tree) = tree match { case tree @ Select(This(ident), name) if ident.name == parentSym.name && localParamAccessorsNames.contains(name) => - Select(This(ctx.owner.asClass), localParamAccessorsNames(name)).withSpan(parent.span) + Inlined(EmptyTree, Nil, Select(This(ctx.owner.asClass), localParamAccessorsNames(name)).withSpan(parent.span)).withSpan(tree.span) case tree => super.apply(tree) } From e13a71f0e71feb5d0c20bcaa586e1f79ef415c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 26 Apr 2023 15:14:04 +0200 Subject: [PATCH 048/106] Pass overridden decls when expanding --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 1c69671f0357..6fc49674bf3c 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -186,7 +186,7 @@ object Inlines: def inlineParentTrait(parent: tpd.Tree, overriddenDecls: Set[Symbol])(using Context): List[Tree] = val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol - if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass, overriddenDecls).expandDefs() + if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass).expandDefs(overriddenDecls) else Nil /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal @@ -480,14 +480,14 @@ object Inlines: end expand end InlineCall - private class InlineParentTrait(parent: tpd.Tree, parentSym: ClassSymbol, overriddenDecls: Set[Symbol])(using Context) extends Inliner(parent): + private class InlineParentTrait(parent: tpd.Tree, parentSym: ClassSymbol)(using Context) extends Inliner(parent): import tpd._ import Inlines.* private val thisInlineTrait = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) - def expandDefs(): List[Tree] = - val stats = Inlines.defsToInline(parentSym).filterNot(isStatAlreadyOverridden) + def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = + val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) stats.zip(inlinedSymbols).map(expandStat) end expandDefs @@ -527,9 +527,6 @@ object Inlines: private val localParamAccessorsNames = new mutable.HashMap[Name, Name] - private def isStatAlreadyOverridden(stat: Tree): Boolean = - overriddenDecls.contains(stat.symbol) - extension (sym: Symbol) private def isTermParamAccessor: Boolean = !sym.isType && sym.is(ParamAccessor) From 59bee43854b150c67d416590030980f737fe4450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 26 Apr 2023 16:33:45 +0200 Subject: [PATCH 049/106] Remove pickling test blacklist --- .../test/dotc/pos-test-pickling.blacklist | 62 ------------------- .../test/dotc/run-test-pickling.blacklist | 5 -- 2 files changed, 67 deletions(-) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 9862a94e5ee1..87aa6a285f68 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -101,65 +101,3 @@ java-inherited-type1 # recursion limit exceeded i7445b.scala - -# FIXME re-enable pickling once inline traits are stable -inline-trait-1-simple-trait.scala -inline-trait-2-generic-trait.scala -inline-trait-3-trait-params.scala -inline-trait-4-inner-class.scala -inline-trait-body-abstract-def.scala -inline-trait-body-class-abstract.scala -inline-trait-body-class-case.scala -inline-trait-body-class-enum.scala -inline-trait-body-class-generic.scala -inline-trait-body-class-object.scala -inline-trait-body-class-params.scala -inline-trait-body-class-sealed.scala -inline-trait-body-class-simple.scala -inline-trait-body-def-context-bound.scala -inline-trait-body-def-curried-params.scala -inline-trait-body-def-extension-method.scala -inline-trait-body-def-generic-singleton.scala -inline-trait-body-def-generic.scala -inline-trait-body-def-implicit.scala -inline-trait-body-def-inline-abstract.scala -inline-trait-body-def-inline-compiletime.scala -inline-trait-body-def-inline-transparent.scala -inline-trait-body-def-inline.scala -inline-trait-body-def-params-inline.scala -inline-trait-body-def-params.scala -inline-trait-body-def-parens.scala -inline-trait-body-def-simple.scala -inline-trait-body-def-using.scala -inline-trait-body-lazy-val.scala -inline-trait-body-trait-inline.scala -inline-trait-body-type.scala -inline-trait-body-val-inline.scala -inline-trait-body-val.scala -inline-trait-body-var.scala -inline-trait-inheritance-multiple-override.scala -inline-trait-inheritance-multiple.scala -inline-trait-inheritance-single-abstract-class-override.scala -inline-trait-inheritance-single-abstract-class.scala -inline-trait-inheritance-single-object-override.scala -inline-trait-inheritance-single-object.scala -inline-trait-signature-generic-context-bound.scala -inline-trait-signature-generic-inferred-type.scala -inline-trait-signature-generic-invariant.scala -inline-trait-signature-generic-type-bounds.scala -inline-trait-signature-generic-variant.scala -inline-trait-signature-parameters-by-name.scala -inline-trait-signature-parameters-default-value.scala -inline-trait-signature-parameters-implicit.scala -inline-trait-signature-parameters-using.scala -inline-trait-signature-parameters-val-private.scala -inline-trait-signature-parameters-val-protected.scala -inline-trait-signature-parameters-val.scala -inline-trait-signature-parameters-var.scala -inline-trait-signature-simple.scala -inline-trait-signature-simple.scala.semanticdb -inline-trait-usage-extension.scala -inline-trait-usage-param-type.scala -inline-trait-usage-return-type.scala -inline-trait-usage-type-bound.scala -inline-trait-usage-type.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 7e57897189a9..9f19b439135c 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -44,8 +44,3 @@ t6138 t6138-2 i12656.scala trait-static-forwarder - -# FIXME re-enable pickling once inline traits are stable -inline-trait-signature-parameters-val-block.scala -inline-trait-signature-parameters-by-name-exception.scala -inline-trait-body-lazy-val.scala \ No newline at end of file From 845c00bbf4b96eb719060c8658ced99d3fa6c391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 26 Apr 2023 16:35:45 +0200 Subject: [PATCH 050/106] Move trait inlining into Inlining phase --- .../dotty/tools/dotc/inlines/Inlines.scala | 32 +++++++++++++++---- .../dotty/tools/dotc/transform/Inlining.scala | 4 +++ .../tools/dotc/transform/PostTyper.scala | 2 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 8 +---- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 6fc49674bf3c..683d46a4ae79 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -56,7 +56,7 @@ object Inlines: /** Can a call to method `meth` be inlined? */ def isInlineable(meth: Symbol)(using Context): Boolean = - meth.is(Inline) && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod + meth.isInlineMethod && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) @@ -72,15 +72,19 @@ object Inlines: ) && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining + && !ctx.owner.isInlineTrait tree match case Block(_, expr) => needsInlining(expr) case tdef @ TypeDef(_, impl: Template) => - !tdef.symbol.isInlineTrait && impl.parents.exists(_.symbol.isInlineTrait) && isInlineableInCtx + !tdef.symbol.isInlineTrait && impl.parents.map(symbolFromParent).exists(_.isInlineTrait) && isInlineableInCtx case _ => isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && isInlineableInCtx + private def symbolFromParent(parent: Tree)(using Context): Symbol = + if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol + private def needsTransparentInlining(tree: Tree)(using Context): Boolean = tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) @@ -184,10 +188,22 @@ object Inlines: tree2 end inlineCall - def inlineParentTrait(parent: tpd.Tree, overriddenDecls: Set[Symbol])(using Context): List[Tree] = - val traitSym = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol - if traitSym.isInlineTrait then InlineParentTrait(parent, traitSym.asClass).expandDefs(overriddenDecls) - else Nil + def inlineParentInlineTraits(cls: Tree)(using Context): Tree = + cls match { + case cls @ TypeDef(_, impl: Template) => + val overriddenSyms = cls.symbol.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet + val inlineDefs = impl.parents.flatMap( + parent => + if symbolFromParent(parent).isInlineTrait then + InlineParentTrait(parent)(using ctx.withOwner(cls.symbol)).expandDefs(overriddenSyms) + else + Nil + ) + val impl1 = cpy.Template(impl)(body = inlineDefs ::: impl.body) + cpy.TypeDef(cls)(rhs = impl1) + case _ => + cls + } /** Try to inline a pattern with an inline unapply method. Fail with error if the maximal * inline depth is exceeded. @@ -480,10 +496,12 @@ object Inlines: end expand end InlineCall - private class InlineParentTrait(parent: tpd.Tree, parentSym: ClassSymbol)(using Context) extends Inliner(parent): + private class InlineParentTrait(parent: tpd.Tree)(using Context) extends Inliner(parent): import tpd._ import Inlines.* + private val parentSym = symbolFromParent(parent) + private val thisInlineTrait = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 10f73fa94e08..7d9ad75161d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -67,6 +67,10 @@ class Inlining extends MacroTransform { override def transform(tree: Tree)(using Context): Tree = { tree match + case tree: TypeDef if Inlines.needsInlining(tree) => + val tree1 = super.transform(tree) + if tree1.tpe.isError then tree1 + else Inlines.inlineParentInlineTraits(tree1) case tree: MemberDef => if tree.symbol.is(Inline) then tree else if tree.symbol.is(Param) then super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ea5e7db2c088..8b1e5c46ac3c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -388,6 +388,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val tree1 = cpy.DefDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol)) processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef])) case tree: TypeDef => + if tree.symbol.isInlineTrait then + ctx.compilationUnit.needsInlining = true registerIfHasMacroAnnotations(tree) val sym = tree.symbol if (sym.isClass) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 38bcf0a4028a..1de68b04ff5f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2667,13 +2667,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cdef.withType(UnspecifiedErrorType) else { val dummy = localDummy(cls, impl) - val inlineTraitDefs = - if ctx.isAfterTyper || cls.isInlineTrait then - Nil - else - val overriddenSyms = cls.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet - parents1.flatMap(parent => Inlines.inlineParentTrait(parent, overriddenSyms)) - val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) ::: inlineTraitDefs + val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) if !ctx.isAfterTyper && cls.isInlineTrait then val membersToInline = body1.filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) From 3d162cbf3e8e03e7184816cbba772959681d76d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 27 Apr 2023 12:06:42 +0200 Subject: [PATCH 051/106] Implement ancestors inline traits without term args Ancestors without term args do not need to be specified in the list of extended symbols This means that we need to recreate the trees manually, and most importantly, make sure the inlined members are overridden in the correct order The order of inheritance is defined by a left-handed DFS and is accessible through SymDenotation#baseClasses --- .../dotty/tools/dotc/inlines/Inlines.scala | 36 +++++++--- ...e-trait-inheritance-inline-ancestors.check | 7 ++ ...e-trait-inheritance-inline-ancestors.scala | 72 +++++++++++++++++++ 3 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 tests/run/inline-trait-inheritance-inline-ancestors.check create mode 100644 tests/run/inline-trait-inheritance-inline-ancestors.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 683d46a4ae79..271afe76622a 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -85,6 +85,26 @@ object Inlines: private def symbolFromParent(parent: Tree)(using Context): Symbol = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol + private def inlineTraitAncestors(cls: TypeDef)(using Context): List[Tree] = cls match { + case tpd.TypeDef(_, tmpl: Template) => + val parentTrees: Map[Symbol, Tree] = tmpl.parents.map(par => symbolFromParent(par) -> par).toMap.filter(_._1.isInlineTrait) + val ancestors: List[ClassSymbol] = cls.tpe.baseClasses.filter(sym => sym.isInlineTrait && sym != cls.symbol) + ancestors.flatMap(ancestor => + def baseTree = + cls.tpe.baseType(ancestor) match + case AppliedType(tycon, targs) => + Some(AppliedTypeTree(TypeTree(tycon), targs.map(TypeTree(_)))) + case tref: TypeRef => + Some(Ident(tref)) + case baseTpe => + report.error(s"unknown base type ${baseTpe.show} for ancestor ${ancestor.show} of ${cls.symbol.show}") + None + parentTrees.get(ancestor).orElse(baseTree.map(_.withSpan(cls.span))) + ) + case _ => + Nil + } + private def needsTransparentInlining(tree: Tree)(using Context): Boolean = tree.symbol.is(Transparent) || ctx.mode.is(Mode.ForceInline) @@ -190,16 +210,14 @@ object Inlines: def inlineParentInlineTraits(cls: Tree)(using Context): Tree = cls match { - case cls @ TypeDef(_, impl: Template) => - val overriddenSyms = cls.symbol.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet - val inlineDefs = impl.parents.flatMap( - parent => - if symbolFromParent(parent).isInlineTrait then - InlineParentTrait(parent)(using ctx.withOwner(cls.symbol)).expandDefs(overriddenSyms) - else - Nil + case cls @ tpd.TypeDef(_, impl: Template) => + val clsOverriddenSyms = cls.symbol.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet + val inlineDefs = inlineTraitAncestors(cls).foldLeft(List.empty[Tree])( + (defs, parent) => + val overriddenSymbols = clsOverriddenSyms ++ defs.flatMap(_.symbol.allOverriddenSymbols) + defs ::: InlineParentTrait(parent)(using ctx.withOwner(cls.symbol)).expandDefs(overriddenSymbols) ) - val impl1 = cpy.Template(impl)(body = inlineDefs ::: impl.body) + val impl1 = cpy.Template(impl)(body = impl.body ::: inlineDefs) cpy.TypeDef(cls)(rhs = impl1) case _ => cls diff --git a/tests/run/inline-trait-inheritance-inline-ancestors.check b/tests/run/inline-trait-inheritance-inline-ancestors.check new file mode 100644 index 000000000000..4500d5fed7de --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors.check @@ -0,0 +1,7 @@ +0 +11 +12 +13 +21 +22 +30 diff --git a/tests/run/inline-trait-inheritance-inline-ancestors.scala b/tests/run/inline-trait-inheritance-inline-ancestors.scala new file mode 100644 index 000000000000..a165defc70b0 --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors.scala @@ -0,0 +1,72 @@ +/* + * Inheritance tree: + * + * ___T0___ + * / | \ + * T11 T12 T13 + * \ /\ / + * T21 T22 + * \ / + * C + * + * Each trait overrides members so that they should have the same value as their name in C + * (zero should be 0, eleven should be 11, etc.). + */ + +@main def Test = + val c = C() + for v <- allValues(c) + do println(v) + +def allValues(c: C): List[Int] = + List( + c.zero, + c.eleven, + c.twelve, + c.thirteen, + c.twentyOne, + c.twentyTwo, + c.thirty, + ) + +inline trait T0: + def zero: Int = 0 + def eleven: Int = 0 + def twelve: Int = 0 + def twentyOne: Int = 0 + def thirteen: Int = 0 + def twentyTwo: Int = 0 + def thirty: Int = 0 + +inline trait T11 extends T0: + override def eleven: Int = 11 + override def twelve: Int = 11 + override def twentyOne: Int = 11 + override def thirteen: Int = 11 + override def twentyTwo: Int = 11 + override def thirty: Int = 11 + +inline trait T12 extends T0: + override def twelve: Int = 12 + override def twentyOne: Int = 12 + override def thirteen: Int = 12 + override def twentyTwo: Int = 12 + override def thirty: Int = 12 + +inline trait T21 extends T11, T12: + override def twentyOne: Int = 21 + override def thirteen: Int = 21 + override def twentyTwo: Int = 21 + override def thirty: Int = 21 + +inline trait T13 extends T0: + override def thirteen: Int = 13 + override def twentyTwo: Int = 13 + override def thirty: Int = 13 + +inline trait T22 extends T12, T13: + override def twentyTwo: Int = 22 + override def thirty: Int = 22 + +class C extends T21, T22: + override def thirty: Int = 30 \ No newline at end of file From 50b6743b5368d61ba803fde4ce8eee37a977e6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 27 Apr 2023 12:17:45 +0200 Subject: [PATCH 052/106] Disable test with inner class --- tests/{ => disabled}/pos/inline-trait-4-inner-class.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ => disabled}/pos/inline-trait-4-inner-class.scala (100%) diff --git a/tests/pos/inline-trait-4-inner-class.scala b/tests/disabled/pos/inline-trait-4-inner-class.scala similarity index 100% rename from tests/pos/inline-trait-4-inner-class.scala rename to tests/disabled/pos/inline-trait-4-inner-class.scala From 34f77f67f0d5402527f2b1c7cc058566e6605042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 27 Apr 2023 13:08:37 +0200 Subject: [PATCH 053/106] Ensure inline traits have same extends precedence as normal traits --- ...e-trait-inheritance-inline-ancestors.check | 7 --- .../inlinetraits.scala} | 33 ++----------- .../normaltraits.scala | 47 +++++++++++++++++++ .../test.scala | 5 ++ 4 files changed, 56 insertions(+), 36 deletions(-) delete mode 100644 tests/run/inline-trait-inheritance-inline-ancestors.check rename tests/run/{inline-trait-inheritance-inline-ancestors.scala => inline-trait-inheritance-inline-ancestors/inlinetraits.scala} (67%) create mode 100644 tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala create mode 100644 tests/run/inline-trait-inheritance-inline-ancestors/test.scala diff --git a/tests/run/inline-trait-inheritance-inline-ancestors.check b/tests/run/inline-trait-inheritance-inline-ancestors.check deleted file mode 100644 index 4500d5fed7de..000000000000 --- a/tests/run/inline-trait-inheritance-inline-ancestors.check +++ /dev/null @@ -1,7 +0,0 @@ -0 -11 -12 -13 -21 -22 -30 diff --git a/tests/run/inline-trait-inheritance-inline-ancestors.scala b/tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala similarity index 67% rename from tests/run/inline-trait-inheritance-inline-ancestors.scala rename to tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala index a165defc70b0..e695ef3efd1a 100644 --- a/tests/run/inline-trait-inheritance-inline-ancestors.scala +++ b/tests/run/inline-trait-inheritance-inline-ancestors/inlinetraits.scala @@ -1,33 +1,8 @@ -/* - * Inheritance tree: - * - * ___T0___ - * / | \ - * T11 T12 T13 - * \ /\ / - * T21 T22 - * \ / - * C - * - * Each trait overrides members so that they should have the same value as their name in C - * (zero should be 0, eleven should be 11, etc.). - */ +package inlinetraits -@main def Test = +val inlineValues: List[Int] = val c = C() - for v <- allValues(c) - do println(v) - -def allValues(c: C): List[Int] = - List( - c.zero, - c.eleven, - c.twelve, - c.thirteen, - c.twentyOne, - c.twentyTwo, - c.thirty, - ) + List(c.zero, c.eleven, c.twelve, c.thirteen, c.twentyOne, c.twentyTwo, c.thirty) inline trait T0: def zero: Int = 0 @@ -69,4 +44,4 @@ inline trait T22 extends T12, T13: override def thirty: Int = 22 class C extends T21, T22: - override def thirty: Int = 30 \ No newline at end of file + override def thirty: Int = 30 diff --git a/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala b/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala new file mode 100644 index 000000000000..ba128145c793 --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors/normaltraits.scala @@ -0,0 +1,47 @@ +package normaltraits + +val normalValues: List[Int] = + val c = C() + List(c.zero, c.eleven, c.twelve, c.thirteen, c.twentyOne, c.twentyTwo, c.thirty) + +trait T0: + def zero: Int = 0 + def eleven: Int = 0 + def twelve: Int = 0 + def twentyOne: Int = 0 + def thirteen: Int = 0 + def twentyTwo: Int = 0 + def thirty: Int = 0 + +trait T11 extends T0: + override def eleven: Int = 11 + override def twelve: Int = 11 + override def twentyOne: Int = 11 + override def thirteen: Int = 11 + override def twentyTwo: Int = 11 + override def thirty: Int = 11 + +trait T12 extends T0: + override def twelve: Int = 12 + override def twentyOne: Int = 12 + override def thirteen: Int = 12 + override def twentyTwo: Int = 12 + override def thirty: Int = 12 + +trait T21 extends T11, T12: + override def twentyOne: Int = 21 + override def thirteen: Int = 21 + override def twentyTwo: Int = 21 + override def thirty: Int = 21 + +trait T13 extends T0: + override def thirteen: Int = 13 + override def twentyTwo: Int = 13 + override def thirty: Int = 13 + +trait T22 extends T12, T13: + override def twentyTwo: Int = 22 + override def thirty: Int = 22 + +class C extends T21, T22: + override def thirty: Int = 30 \ No newline at end of file diff --git a/tests/run/inline-trait-inheritance-inline-ancestors/test.scala b/tests/run/inline-trait-inheritance-inline-ancestors/test.scala new file mode 100644 index 000000000000..5f7a9be7a41b --- /dev/null +++ b/tests/run/inline-trait-inheritance-inline-ancestors/test.scala @@ -0,0 +1,5 @@ +import normaltraits.normalValues +import inlinetraits.inlineValues + +@main def Test = + assert(normalValues == inlineValues) \ No newline at end of file From d605bc5472d8b26670c075632552f2b18d6903bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 28 Apr 2023 09:39:15 +0200 Subject: [PATCH 054/106] Partially implement inner traits There are still issues with symbols being changed, but not their types --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/inlines/Inlines.scala | 46 ++++++++++++------- .../dotty/tools/dotc/transform/Inlining.scala | 36 ++++++++++++++- .../dotty/tools/dotc/typer/RefChecks.scala | 17 ++++--- 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index cd9526b27a21..d13c821c662a 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -26,6 +26,7 @@ object StdNames { inline val LOCALDUMMY_PREFIX = " needsInlining(expr) case tdef @ TypeDef(_, impl: Template) => - !tdef.symbol.isInlineTrait && impl.parents.map(symbolFromParent).exists(_.isInlineTrait) && isInlineableInCtx + impl.parents.map(symbolFromParent).exists(_.isInlineTrait) && isInlineableInCtx case _ => isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && isInlineableInCtx @@ -571,16 +571,22 @@ object Inlines: inlinedValDef(stat, inlinedSym) case stat: DefDef => inlinedDefDef(stat, inlinedSym) - case stat @ TypeDef(_, impl: Template) => - inlinedClassDef(stat, impl, inlinedSym.asClass) + case stat @ TypeDef(_, _: Template) => + inlinedClassDef(stat, inlinedSym.asClass) case stat: TypeDef => inlinedTypeDef(stat, inlinedSym) private def inlinedMember(sym: Symbol)(using Context): Symbol = sym.info match { case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => - val inlinedInfo = ClassInfo(prefix, cls, declaredParents, Scopes.newScope, selfInfo) // TODO adapt parents - sym.copy(owner = ctx.owner, info = inlinedInfo, coord = spanCoord(parent.span)).entered.asClass + val inlinedSym = newClassSymbol( + ctx.owner, + sym.asType.name, + sym.flags | Synthetic, + clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ ThisType.raw(ctx.owner.typeRef).select(sym), Scopes.newScope, selfInfo) + ) + inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) + inlinedSym case _ => var name = sym.name var flags = sym.flags | Synthetic @@ -598,11 +604,13 @@ object Inlines: coord = spanCoord(parent.span)).entered } - private def inlinedStat(stat: Tree)(using Context): Tree = stat match { - // TODO check if symbols must be copied - case tree: DefDef => inlinedDefDef(tree, tree.symbol) - case tree: ValDef => inlinedValDef(tree, tree.symbol) - } + private def inlinedStat(stat: Tree)(using Context): Tree = + val inlinedSym = inlinedMember(stat.symbol) + stat match { + // TODO check if symbols must be copied + case tree: DefDef => inlinedDefDef(tree, inlinedSym) + case tree: ValDef => inlinedValDef(tree, inlinedSym) + } private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = val rhs = @@ -625,14 +633,18 @@ object Inlines: private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = // TODO check if symbol must be copied - val constr = inlinedDefDef(ddef, ddef.symbol) + val inlinedSym = inlinedMember(ddef.symbol) + inlinedSym.resetFlag(Override) + val constr = inlinedDefDef(ddef, inlinedSym) cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) - private def inlinedClassDef(clDef: TypeDef, impl: Template, inlinedCls: ClassSymbol)(using Context): Tree = - val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { - (inlinedPrimaryConstructorDefDef(impl.constr), impl.body.map(inlinedStat)) + private def inlinedClassDef(clsDef: TypeDef, inlinedSym: ClassSymbol)(using Context): Tree = + val TypeDef(_, tmpl: Template) = clsDef: @unchecked + val (constr, body) = inContext(ctx.withOwner(inlinedSym)) { + (inlinedPrimaryConstructorDefDef(tmpl.constr), tmpl.body.map(inlinedStat)) } - inlined(tpd.ClassDefWithParents(inlinedCls, constr, impl.parents, body))._2.withSpan(clDef.span) // TODO adapt parents + val clsDef1 = tpd.ClassDefWithParents(inlinedSym, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) + inlined(clsDef1)._2.withSpan(clsDef.span) // TODO adapt parents private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 7d9ad75161d6..cf998cde42a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -11,13 +11,15 @@ import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits -import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer +import dotty.tools.dotc.core.DenotTransformers.SymTransformer import dotty.tools.dotc.staging.StagingLevel +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.StdNames.str import scala.collection.mutable.ListBuffer /** Inlines all calls to inline methods that are not in an inline method or a quote */ -class Inlining extends MacroTransform { +class Inlining extends MacroTransform, SymTransformer { import tpd._ @@ -58,6 +60,33 @@ class Inlining extends MacroTransform { new InliningTreeMap().transform(tree) } + override def transformSym(sym: SymDenotation)(using Context): SymDenotation = + if sym.isClass && sym.owner.isInlineTrait && !sym.is(Module) then + sym.copySymDenotation(name = sym.name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX, initFlags = (sym.flags &~ Final) | Trait) + else + sym + + def transformInlineClasses(tree: Tree)(using Context): Tree = + val tpd.TypeDef(name, tmpl: Template) = tree: @unchecked + val body1 = tmpl.body.flatMap { + case tdef @ tpd.TypeDef(name, tmpl1: Template) => + val newTrait = cpy.TypeDef(tdef)(name = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX) + val newTypeSym = newSymbol( + owner = tree.symbol, + name = name.asTypeName, + flags = tdef.symbol.flags & (Private | Protected), + info = Types.TypeBounds.upper(newTrait.symbol.typeRef), + privateWithin = tdef.symbol.privateWithin, + coord = tdef.symbol.coord, + nestingLevel = tdef.symbol.nestingLevel, + ).asType + List(newTrait, TypeDef(newTypeSym)) + case member => + List(member) + } + val tmpl1 = cpy.Template(tmpl)(body = body1) + cpy.TypeDef(tree)(name, tmpl1) + private class InliningTreeMap extends TreeMapWithImplicits { /** List of top level classes added by macro annotation in a package object. @@ -67,9 +96,12 @@ class Inlining extends MacroTransform { override def transform(tree: Tree)(using Context): Tree = { tree match + case tree: TypeDef if tree.symbol.isInlineTrait => + transformInlineClasses(tree) case tree: TypeDef if Inlines.needsInlining(tree) => val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 + else if tree1.symbol.isInlineTrait then transformInlineClasses(tree1) else Inlines.inlineParentInlineTraits(tree1) case tree: MemberDef => if tree.symbol.is(Inline) then tree diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 9a2bd58237cc..d143aee9942d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -340,6 +340,8 @@ object RefChecks { * of class `clazz` are met. */ def checkOverride(checkSubType: (Type, Type) => Context ?=> Boolean, member: Symbol, other: Symbol): Unit = + def overridesInlineTraitMember = other.owner.ownersIterator.exists(_.isInlineTrait) && member.is(Synthetic) + def memberTp(self: Type) = if (member.isClass) TypeAlias(member.typeRef.EtaExpand(member.typeParams)) else self.memberInfo(member) @@ -415,12 +417,13 @@ object RefChecks { def overrideTargetNameError() = val otherTargetName = i"@targetName(${other.targetName})" - if member.hasTargetName(member.name) then - overrideError(i"misses a target name annotation $otherTargetName") - else if other.hasTargetName(other.name) then - overrideError(i"should not have a @targetName annotation since the overridden member hasn't one either") - else - overrideError(i"has a different target name annotation; it should be $otherTargetName") + if !overridesInlineTraitMember then + if member.hasTargetName(member.name) then + overrideError(i"misses a target name annotation $otherTargetName") + else if other.hasTargetName(other.name) then + overrideError(i"should not have a @targetName annotation since the overridden member hasn't one either") + else + overrideError(i"has a different target name annotation; it should be $otherTargetName") //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG @@ -465,7 +468,7 @@ object RefChecks { overrideError("cannot be used here - opaque type aliases cannot be overridden") else if (!other.is(Deferred) && member.isClass) overrideError("cannot be used here - classes can only override abstract types") - else if other.isEffectivelyFinal && !(other.owner.isInlineTrait && member.is(Synthetic)) then // (1.2) + else if (other.isEffectivelyFinal && !overridesInlineTraitMember) then // (1.2) overrideError(i"cannot override final member ${other.showLocated}") else if (member.is(ExtensionMethod) && !other.is(ExtensionMethod)) // (1.3) overrideError("is an extension method, cannot override a normal method") From 9e26b35a8bcb7fc7d164e830b2354059274d7b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 28 Apr 2023 11:27:56 +0200 Subject: [PATCH 055/106] Use typeRef on ctx.owner --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index b3fe5065ab54..82a6d4ec8391 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -520,7 +520,7 @@ object Inlines: private val parentSym = symbolFromParent(parent) - private val thisInlineTrait = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner)) + private val thisInlineTrait = ThisType.raw(ctx.owner.typeRef) def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) @@ -579,11 +579,12 @@ object Inlines: private def inlinedMember(sym: Symbol)(using Context): Symbol = sym.info match { case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => + val baseThisCls = ThisType.raw(ctx.owner.typeRef).select(sym) val inlinedSym = newClassSymbol( ctx.owner, sym.asType.name, sym.flags | Synthetic, - clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ ThisType.raw(ctx.owner.typeRef).select(sym), Scopes.newScope, selfInfo) + clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) inlinedSym From 6523fcceeb43f193654ae793ee27861578d7bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 8 May 2023 09:53:44 +0200 Subject: [PATCH 056/106] Split symbol inlining method by case --- .../dotty/tools/dotc/inlines/Inlines.scala | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 82a6d4ec8391..7d37dcaa9496 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -524,7 +524,7 @@ object Inlines: def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) - val inlinedSymbols = stats.map(stat => inlinedMember(stat.symbol)) + val inlinedSymbols = stats.map(stat => inlinedSym(stat.symbol)) stats.zip(inlinedSymbols).map(expandStat) end expandDefs @@ -576,42 +576,41 @@ object Inlines: case stat: TypeDef => inlinedTypeDef(stat, inlinedSym) - private def inlinedMember(sym: Symbol)(using Context): Symbol = + private def inlinedSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = + if sym.isClass then inlinedClassSym(sym.asClass, withoutFlags) else inlinedMemberSym(sym, withoutFlags) + + private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = sym.info match { case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => val baseThisCls = ThisType.raw(ctx.owner.typeRef).select(sym) val inlinedSym = newClassSymbol( ctx.owner, sym.asType.name, - sym.flags | Synthetic, + (sym.flags | Synthetic) &~ withoutFlags, clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) inlinedSym case _ => - var name = sym.name - var flags = sym.flags | Synthetic - if sym.isType || !sym.is(Private) then flags |= Override - if sym.isTermParamAccessor then - flags &~= ParamAccessor - if sym.is(Local) then - name = name.expandedName(parentSym) - localParamAccessorsNames(sym.name) = name - sym.copy( - owner = ctx.owner, - name = name, - flags = flags, - info = inlinerTypeMap(sym.info), - coord = spanCoord(parent.span)).entered + report.error(s"Class symbol ${sym.show} does not have class info") + sym } - private def inlinedStat(stat: Tree)(using Context): Tree = - val inlinedSym = inlinedMember(stat.symbol) - stat match { - // TODO check if symbols must be copied - case tree: DefDef => inlinedDefDef(tree, inlinedSym) - case tree: ValDef => inlinedValDef(tree, inlinedSym) - } + private def inlinedMemberSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = + var name = sym.name + var flags = sym.flags | Synthetic + if sym.isType || !sym.is(Private) then flags |= Override + if sym.isTermParamAccessor then + flags &~= ParamAccessor + if sym.is(Local) then + name = name.expandedName(parentSym) + localParamAccessorsNames(sym.name) = name + sym.copy( + owner = ctx.owner, + name = name, + flags = flags &~ withoutFlags, + info = inlinerTypeMap(sym.info), + coord = spanCoord(parent.span)).entered private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = val rhs = @@ -634,17 +633,17 @@ object Inlines: private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = // TODO check if symbol must be copied - val inlinedSym = inlinedMember(ddef.symbol) - inlinedSym.resetFlag(Override) + val inlinedSym = inlinedMemberSym(ddef.symbol, withoutFlags = Override) val constr = inlinedDefDef(ddef, inlinedSym) cpy.DefDef(constr)(tpt = TypeTree(defn.UnitType), rhs = EmptyTree) - private def inlinedClassDef(clsDef: TypeDef, inlinedSym: ClassSymbol)(using Context): Tree = + private def inlinedClassDef(clsDef: TypeDef, inlinedCls: ClassSymbol)(using Context): Tree = val TypeDef(_, tmpl: Template) = clsDef: @unchecked - val (constr, body) = inContext(ctx.withOwner(inlinedSym)) { - (inlinedPrimaryConstructorDefDef(tmpl.constr), tmpl.body.map(inlinedStat)) + val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { + val inlinedTmpl = tmpl.body.map(stat => expandStat(stat, inlinedSym(stat.symbol))) + (inlinedPrimaryConstructorDefDef(tmpl.constr), inlinedTmpl) } - val clsDef1 = tpd.ClassDefWithParents(inlinedSym, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) + val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) inlined(clsDef1)._2.withSpan(clsDef.span) // TODO adapt parents private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = From bcb6702a374e13dda5b874677fc14c75fbe848a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 13:13:49 +0200 Subject: [PATCH 057/106] Improve checks in phases Instead of checking if we are inside of an inline method, some code snippets need to check if we are inside of an inline trait as well --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 5 ++++- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 2 +- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 4 +++- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 4 ++-- tests/pos/inline-trait-1-simple-trait.scala | 2 ++ 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 7d37dcaa9496..15a9845512d3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -54,6 +54,9 @@ object Inlines: def inInlineMethod(using Context): Boolean = ctx.owner.ownersIterator.exists(_.isInlineMethod) + def inInlineContext(using Context): Boolean = + ctx.owner.ownersIterator.exists(sym => sym.isInlineMethod || sym.isInlineTrait) + /** Can a call to method `meth` be inlined? */ def isInlineable(meth: Symbol)(using Context): Boolean = meth.isInlineMethod && meth.hasAnnotation(defn.BodyAnnot) && !inInlineMethod @@ -82,7 +85,7 @@ object Inlines: case _ => isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] && isInlineableInCtx - private def symbolFromParent(parent: Tree)(using Context): Symbol = + private[dotc] def symbolFromParent(parent: Tree)(using Context): Symbol = if parent.symbol.isConstructor then parent.symbol.owner else parent.symbol private def inlineTraitAncestors(cls: TypeDef)(using Context): List[Tree] = cls match { diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index cf998cde42a4..261fad989892 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -47,7 +47,7 @@ class Inlining extends MacroTransform, SymTransformer { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => + case tree: RefTree if !Inlines.inInlineContext && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 8b58f18bca52..34a11ebb3844 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -81,9 +81,9 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match case tree: Quote => - assert(Inlines.inInlineMethod) + assert(Inlines.inInlineContext) case tree: Splice => - assert(Inlines.inInlineMethod) + assert(Inlines.inInlineContext) case _ => override def run(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 8b1e5c46ac3c..0a3a9b498568 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -389,7 +389,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef])) case tree: TypeDef => if tree.symbol.isInlineTrait then - ctx.compilationUnit.needsInlining = true + ctx.compilationUnit.needsInlining = true // Transform inner classes to traits registerIfHasMacroAnnotations(tree) val sym = tree.symbol if (sym.isClass) @@ -401,6 +401,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree.rhs match case impl: Template => for parent <- impl.parents do + if Inlines.symbolFromParent(parent).isInlineTrait then + ctx.compilationUnit.needsInlining = true Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos) // Constructor parameters are in scope when typing a parent. // While they can safely appear in a parent tree, to preserve diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ff5dc5042eaf..860d389a181e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -90,8 +90,8 @@ class Splicing extends MacroTransform: case tree: Quote => val body1 = QuoteTransformer().transform(tree.body)(using quoteContext) cpy.Quote(tree)(body1, tree.tags) - case tree: DefDef if tree.symbol.is(Inline) => - // Quotes in inlined methods are only pickled after they are inlined. + case _: DefDef | _: TypeDef if tree.symbol.is(Inline) => + // Quotes in inlined methods and traits are only pickled after they are inlined. tree case _ => super.transform(tree) diff --git a/tests/pos/inline-trait-1-simple-trait.scala b/tests/pos/inline-trait-1-simple-trait.scala index 8bd510a4adb6..5a58b7ea2d4c 100644 --- a/tests/pos/inline-trait-1-simple-trait.scala +++ b/tests/pos/inline-trait-1-simple-trait.scala @@ -10,6 +10,8 @@ inline trait A: def f[T](x: T): Int = 2 private def g = 1 + protected[this] def p = 123 + private[this] def pp = 123456 def xx: X = "foo".asInstanceOf[X] end A From e3e55822d99a556b3f52adb40dfefea6791c7fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 13:29:20 +0200 Subject: [PATCH 058/106] Remove RHS of definitions in inline traits --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../dotc/transform/PruneInlineTraits.scala | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index a6118732d4ae..3e54ebb646c9 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -91,7 +91,8 @@ class Compiler { new ExplicitSelf, // Make references to non-trivial self types explicit as casts new StringInterpolatorOpt, // Optimizes raw and s and f string interpolators by rewriting them to string concatenations or formats new DropBreaks) :: // Optimize local Break throws by rewriting them - List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions + List(new PruneInlineTraits, + new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_` new InlinePatterns, // Remove placeholders of inlined patterns new VCInlineMethods, // Inlines calls to value class methods diff --git a/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala new file mode 100644 index 000000000000..075098247598 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala @@ -0,0 +1,48 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts._ +import DenotTransformers.SymTransformer +import Flags._ +import SymDenotations._ +import Symbols._ +import MegaPhase.MiniPhase +import ast.tpd + +class PruneInlineTraits extends MiniPhase with SymTransformer { thisTransform => + import tpd._ + import PruneInlineTraits._ + + override def phaseName: String = PruneInlineTraits.name + + override def description: String = PruneInlineTraits.description + + override def transformSym(sym: SymDenotation)(using Context): SymDenotation = + if isEraseable(sym) then sym.copySymDenotation(initFlags = sym.flags | Deferred) + else sym + + override def transformValDef(tree: ValDef)(using Context): ValDef = + if isEraseable(tree.symbol) then cpy.ValDef(tree)(rhs = EmptyTree) + else tree + + override def transformDefDef(tree: DefDef)(using Context): DefDef = + if isEraseable(tree.symbol) then cpy.DefDef(tree)(rhs = EmptyTree) + else tree + + private def isEraseable(sym: SymDenotation)(using Context): Boolean = + sym.owner.isInlineTrait + && !sym.isConstructor + && !sym.is(Param) + && !sym.is(ParamAccessor) + && !sym.is(Private) + && !sym.isLocalDummy + && !sym.isType +} + +object PruneInlineTraits { + import tpd._ + + val name: String = "pruneInlineTraits" + val description: String = "drop rhs definitions in inline traits" +} From c9ec22375b5a341527541a24bdfd4b42994b68c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 13:30:27 +0200 Subject: [PATCH 059/106] Add tests with macros (unstable) The macros are compiled correctly when done in two stages, but when the two files are compiled at the same time, $anonfun is considered to be a dependency --- tests/pos/inline-trait-body-macro-suspend/Macro.scala | 10 ++++++++++ tests/pos/inline-trait-body-macro-suspend/Test.scala | 2 ++ tests/pos/inline-trait-body-macro/Macro_1.scala | 10 ++++++++++ tests/pos/inline-trait-body-macro/Test_2.scala | 1 + 4 files changed, 23 insertions(+) create mode 100644 tests/pos/inline-trait-body-macro-suspend/Macro.scala create mode 100644 tests/pos/inline-trait-body-macro-suspend/Test.scala create mode 100644 tests/pos/inline-trait-body-macro/Macro_1.scala create mode 100644 tests/pos/inline-trait-body-macro/Test_2.scala diff --git a/tests/pos/inline-trait-body-macro-suspend/Macro.scala b/tests/pos/inline-trait-body-macro-suspend/Macro.scala new file mode 100644 index 000000000000..8aa252d8b7d0 --- /dev/null +++ b/tests/pos/inline-trait-body-macro-suspend/Macro.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def foo(): Int = + ${fooImpl} + +def fooImpl(using Quotes): Expr[Int] = + '{3} + +inline trait A: + val i: Int = foo() diff --git a/tests/pos/inline-trait-body-macro-suspend/Test.scala b/tests/pos/inline-trait-body-macro-suspend/Test.scala new file mode 100644 index 000000000000..5079eecd497d --- /dev/null +++ b/tests/pos/inline-trait-body-macro-suspend/Test.scala @@ -0,0 +1,2 @@ +class B extends A: + def test = foo() \ No newline at end of file diff --git a/tests/pos/inline-trait-body-macro/Macro_1.scala b/tests/pos/inline-trait-body-macro/Macro_1.scala new file mode 100644 index 000000000000..8aa252d8b7d0 --- /dev/null +++ b/tests/pos/inline-trait-body-macro/Macro_1.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def foo(): Int = + ${fooImpl} + +def fooImpl(using Quotes): Expr[Int] = + '{3} + +inline trait A: + val i: Int = foo() diff --git a/tests/pos/inline-trait-body-macro/Test_2.scala b/tests/pos/inline-trait-body-macro/Test_2.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/tests/pos/inline-trait-body-macro/Test_2.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file From 08e2689bafb874f1e0dc18a9263045bf938184ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 16:09:10 +0200 Subject: [PATCH 060/106] Fix owner of bodies to their rightful symbols When inlining the RHS of definitions, their owner was set to be the class extending the inline trait This problem, however, only raised an error when using macros in the inline trait (for some reason...?) This fix sets the owner of the RHSs to the symbol of their definition --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 7 +++++-- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 13 +++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e71d789d86da..42063814a8c8 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -566,8 +566,11 @@ class Inliner(val call: tpd.Tree)(using Context): protected val inlinerTypeMap: InlinerTypeMap = InlinerTypeMap() protected val inlinerTreeMap: InlinerTreeMap = InlinerTreeMap() + protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = + inlineContext(call).fresh.setTyper(inlineTyper).setNewScope + /** The Inlined node representing the inlined call */ - def inlined(rhsToInline: tpd.Tree): (List[MemberDef], Tree) = + def inlined(rhsToInline: tpd.Tree)(using Context): (List[MemberDef], Tree) = inlining.println(i"-----------------------\nInlining $call\nWith RHS $rhsToInline") def paramTypess(call: Tree, acc: List[List[Type]]): List[List[Type]] = call match @@ -609,7 +612,7 @@ class Inliner(val call: tpd.Tree)(using Context): val inlineTyper = new InlineTyper(ctx.reporter.errorCount) - val inlineCtx = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope + val inlineCtx = this.inlineCtx(inlineTyper) // A tree type map to prepare the inlined body for typechecked. // The translation maps references to `this` and parameters to diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 15a9845512d3..c0572be8035b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -550,6 +550,9 @@ object Inlines: override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() override protected val inlinerTreeMap: InlinerTreeMap = InlineTraitTreeMap() + override protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = + ctx.fresh.setTyper(inlineTyper).setNewScope + private val paramAccessorsValueOf: Map[Name, Tree] = def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match case Apply(fun, args) => allArgs(fun, acc :+ args) @@ -620,7 +623,7 @@ object Inlines: if vdef.symbol.isTermParamAccessor then paramAccessorsValueOf(vdef.symbol.name) else - inlinedRhs(vdef.rhs.changeOwner(vdef.symbol, inlinedSym)) + inlinedRhs(vdef, inlinedSym) tpd.ValDef(inlinedSym.asTerm, rhs).withSpan(parent.span) private def inlinedDefDef(ddef: DefDef, inlinedSym: Symbol)(using Context): DefDef = @@ -631,7 +634,8 @@ object Inlines: paramss => val oldParamSyms = ddef.paramss.flatten.map(_.symbol) val newParamSyms = paramss.flatten.map(_.symbol) - inlinedRhs(ddef.rhs.subst(oldParamSyms, newParamSyms).changeOwner(ddef.symbol, inlinedSym)) + val ddef1 = cpy.DefDef(ddef)(rhs = ddef.rhs.subst(oldParamSyms, newParamSyms)) + inlinedRhs(ddef1, inlinedSym) tpd.DefDef(inlinedSym.asTerm, rhsFun).withSpan(parent.span) private def inlinedPrimaryConstructorDefDef(ddef: DefDef)(using Context): DefDef = @@ -652,11 +656,12 @@ object Inlines: private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) - private def inlinedRhs(rhs: Tree): Tree = + private def inlinedRhs(vddef: ValOrDefDef, inlinedSym: Symbol)(using Context): Tree = + val rhs = vddef.rhs.changeOwner(vddef.symbol, inlinedSym) if rhs.isEmpty then rhs else - val inlinedRhs = inlined(rhs)._2 + val inlinedRhs = inContext(ctx.withOwner(inlinedSym)) { inlined(rhs)._2 } Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) end InlineParentTrait From 53488681db97fac7062d9eb62ffe5adfa22cd0df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 16:51:35 +0200 Subject: [PATCH 061/106] Add more tests Add more tests cases for generic signatures Add disabled tests with inner classes, traits, enums, etc. --- .../dotty/tools/dotc/transform/Inlining.scala | 3 ++- .../pos/inline-trait-body-class-generic.scala | 10 ++++----- ...-trait-inheritance-multiple-override.scala | 20 +++++++++++++++++ .../inline-trait-inheritance-multiple.scala | 17 ++++++++++++++ ...tance-single-abstract-class-override.scala | 7 ++++++ ...it-inheritance-single-abstract-class.scala | 6 +++++ ...t-inheritance-single-object-override.scala | 6 +++++ ...line-trait-inheritance-single-object.scala | 5 +++++ ...it-signature-generic-refinement-type.scala | 9 ++++++++ ...ne-trait-signature-generic-singleton.scala | 4 ++++ ...-trait-signature-parameters-currying.scala | 4 ++++ .../inline-trait-signature-parentheses.scala | 4 ++++ tests/pos/inline-trait-usage-inner.scala | 5 +++++ ...ait-inheritance-diamond-simple-trait.check | 4 ++++ ...ait-inheritance-diamond-simple-trait.scala | 22 +++++++++++++++++++ 15 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 tests/disabled/pos/inline-trait-inheritance-multiple-override.scala create mode 100644 tests/disabled/pos/inline-trait-inheritance-multiple.scala create mode 100644 tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala create mode 100644 tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala create mode 100644 tests/disabled/pos/inline-trait-inheritance-single-object-override.scala create mode 100644 tests/disabled/pos/inline-trait-inheritance-single-object.scala create mode 100644 tests/pos/inline-trait-signature-generic-refinement-type.scala create mode 100644 tests/pos/inline-trait-signature-generic-singleton.scala create mode 100644 tests/pos/inline-trait-signature-parameters-currying.scala create mode 100644 tests/pos/inline-trait-signature-parentheses.scala create mode 100644 tests/pos/inline-trait-usage-inner.scala create mode 100644 tests/run/inline-trait-inheritance-diamond-simple-trait.check create mode 100644 tests/run/inline-trait-inheritance-diamond-simple-trait.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 261fad989892..36fd0fc5c58f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -15,6 +15,7 @@ import dotty.tools.dotc.core.DenotTransformers.SymTransformer import dotty.tools.dotc.staging.StagingLevel import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.StdNames.str +import dotty.tools.dotc.core.Types.TypeBounds import scala.collection.mutable.ListBuffer @@ -75,7 +76,7 @@ class Inlining extends MacroTransform, SymTransformer { owner = tree.symbol, name = name.asTypeName, flags = tdef.symbol.flags & (Private | Protected), - info = Types.TypeBounds.upper(newTrait.symbol.typeRef), + info = TypeBounds.upper(newTrait.symbol.typeRef), privateWithin = tdef.symbol.privateWithin, coord = tdef.symbol.coord, nestingLevel = tdef.symbol.nestingLevel, diff --git a/tests/disabled/pos/inline-trait-body-class-generic.scala b/tests/disabled/pos/inline-trait-body-class-generic.scala index 859b7ac63b9f..91e90a53e5ef 100644 --- a/tests/disabled/pos/inline-trait-body-class-generic.scala +++ b/tests/disabled/pos/inline-trait-body-class-generic.scala @@ -1,6 +1,6 @@ -inline trait A: - class Inner[T <: Int]: - val x: T = 1 +inline trait A[T]: + class Inner[U](u: U): + val x: (T, U) = (???, u) + def f: (T, String) = Inner("U").x -class B extends A: - def f = Inner[Int]().x \ No newline at end of file +class B extends A[Int] \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-inheritance-multiple-override.scala b/tests/disabled/pos/inline-trait-inheritance-multiple-override.scala new file mode 100644 index 000000000000..8a429deb5101 --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-multiple-override.scala @@ -0,0 +1,20 @@ +inline trait IT1: + def i: Int = 1 + def f[T](x: T): T = x + +inline trait IT2: + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T: + def k: List[Nothing] + def h(x: Int, y: Double): Double = x + y + +class C1 extends IT1, T: + override def i: Int = 123456 + def k = Nil + override def h(x: Int, y: Double): Double = 1.0 + +class C2 extends IT1, IT2: + override def i: Int = 567890 + override def f[T](x: T): T = ??? \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-inheritance-multiple.scala b/tests/disabled/pos/inline-trait-inheritance-multiple.scala new file mode 100644 index 000000000000..15013572f9b7 --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-multiple.scala @@ -0,0 +1,17 @@ +object InlineTraits: + inline trait IT1: + def i: Int = 1 + def f[T](x: T): T = x + + inline trait IT2: + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T: + def k: List[Nothing] + def h(x: Int, y: Double): Double = x + y + +class C1 extends InlineTraits.IT1, T: + def k = Nil + +class C2 extends InlineTraits.IT1, InlineTraits.IT2 \ No newline at end of file diff --git a/tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala b/tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala new file mode 100644 index 000000000000..7baaf3885d6b --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala @@ -0,0 +1,7 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +abstract class AC extends IT: + override def i: Int = 123456 + def j: Int diff --git a/tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala b/tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala new file mode 100644 index 000000000000..49243dfd56ec --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala @@ -0,0 +1,6 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +abstract class AC extends IT: + def j: Int diff --git a/tests/disabled/pos/inline-trait-inheritance-single-object-override.scala b/tests/disabled/pos/inline-trait-inheritance-single-object-override.scala new file mode 100644 index 000000000000..7e7726e0e8bb --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-single-object-override.scala @@ -0,0 +1,6 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +object O extends IT: + override def i: Int = 123456 diff --git a/tests/disabled/pos/inline-trait-inheritance-single-object.scala b/tests/disabled/pos/inline-trait-inheritance-single-object.scala new file mode 100644 index 000000000000..3916f2e3dc36 --- /dev/null +++ b/tests/disabled/pos/inline-trait-inheritance-single-object.scala @@ -0,0 +1,5 @@ +inline trait IT: + def i: Int = 1 + def f[T](x: T): T = x + +object O extends IT diff --git a/tests/pos/inline-trait-signature-generic-refinement-type.scala b/tests/pos/inline-trait-signature-generic-refinement-type.scala new file mode 100644 index 000000000000..3683c55a4d02 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-refinement-type.scala @@ -0,0 +1,9 @@ +import reflect.Selectable.reflectiveSelectable + +class C[T](x: T): + def foo(): T = x + +inline trait A[T, U[T]](u: U[T]{ def foo(): T }): + def f: T = u.foo() + +class B extends A(C(1)) diff --git a/tests/pos/inline-trait-signature-generic-singleton.scala b/tests/pos/inline-trait-signature-generic-singleton.scala new file mode 100644 index 000000000000..c5d0cf2fcfb1 --- /dev/null +++ b/tests/pos/inline-trait-signature-generic-singleton.scala @@ -0,0 +1,4 @@ +inline trait A[T](x: T): + def f: T = x + +class B extends A[1](1) diff --git a/tests/pos/inline-trait-signature-parameters-currying.scala b/tests/pos/inline-trait-signature-parameters-currying.scala new file mode 100644 index 000000000000..9d98e0596408 --- /dev/null +++ b/tests/pos/inline-trait-signature-parameters-currying.scala @@ -0,0 +1,4 @@ +inline trait A(i: Int)(j: Double): + def f: Double = i + j + +class B extends A(1)(1.0) diff --git a/tests/pos/inline-trait-signature-parentheses.scala b/tests/pos/inline-trait-signature-parentheses.scala new file mode 100644 index 000000000000..8e9c674edad6 --- /dev/null +++ b/tests/pos/inline-trait-signature-parentheses.scala @@ -0,0 +1,4 @@ +inline trait A(): + def i: Int = 1 + +class B extends A diff --git a/tests/pos/inline-trait-usage-inner.scala b/tests/pos/inline-trait-usage-inner.scala new file mode 100644 index 000000000000..3ed7869f0016 --- /dev/null +++ b/tests/pos/inline-trait-usage-inner.scala @@ -0,0 +1,5 @@ +object O: + inline trait A[T]: + def t: T = ??? + +class B extends O.A[Int] diff --git a/tests/run/inline-trait-inheritance-diamond-simple-trait.check b/tests/run/inline-trait-inheritance-diamond-simple-trait.check new file mode 100644 index 000000000000..adbdca39e3a4 --- /dev/null +++ b/tests/run/inline-trait-inheritance-diamond-simple-trait.check @@ -0,0 +1,4 @@ +1 +2 +999 +4 diff --git a/tests/run/inline-trait-inheritance-diamond-simple-trait.scala b/tests/run/inline-trait-inheritance-diamond-simple-trait.scala new file mode 100644 index 000000000000..0bb66f48b739 --- /dev/null +++ b/tests/run/inline-trait-inheritance-diamond-simple-trait.scala @@ -0,0 +1,22 @@ +trait TGP[T]: + def i: T + def f(x: T): T = x + +inline trait IT1[T](x: T) extends TGP[T]: + override def i: T = x + +inline trait IT2 extends TGP[Int]: + override def i: Int = 999 + def j: String = "inline" + def g(x: Int): String = x.toString() + +trait T extends TGP[Int] + +class C1 extends T, IT1[Int](1) +class C2 extends IT1[Int](2), T +class C3 extends IT1[Int](3), IT2 +class C4 extends IT2, IT1[Int](4) + +@main def Test: Unit = + for c <- List(C1(), C2(), C3(), C4()) + do println(c.i) \ No newline at end of file From 060f5abf42d7dd35d8dda6366cb638d6bd4fdae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 10 May 2023 17:03:48 +0200 Subject: [PATCH 062/106] Inline code of thisType at sole call site --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c0572be8035b..66360fcdf1f3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -523,8 +523,6 @@ object Inlines: private val parentSym = symbolFromParent(parent) - private val thisInlineTrait = ThisType.raw(ctx.owner.typeRef) - def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) val inlinedSymbols = stats.map(stat => inlinedSym(stat.symbol)) @@ -533,7 +531,7 @@ object Inlines: protected class InlineTraitTypeMap extends InlinerTypeMap { override def apply(t: Type) = t match { - case t: ThisType if t.cls == parentSym => thisInlineTrait + case t: ThisType if t.cls == parentSym => ctx.owner.thisType case t => super.apply(t) } } From ffe94e8511b778edae43c86156a6323df9938860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 11 May 2023 11:29:20 +0200 Subject: [PATCH 063/106] Forbid defining inline traits inside inline traits When inlining the body of an inline trait, trying to inline another inline trait creates typing issues. For example, with the following code: ``` inline trait A[T]: inline trait InnerA[U]: val x: (T, U) = ??? class B extends A[Int]: class InnerB extends InnerA[String] ``` when the Inlining phase tries to inline InnerA inside of InnerB, it should try to change A#T to B#T but does not know that A exists in a higher scope. An idea to solve this would be to inline everything from A inside of B before inlining InnerA inside of InnerB, but this adds quite a bit of complexity. Another one would be to keep track, when inlining a trait, of all the type parameters like we do for term parameters. This could be done more easily. This will be left as future work for the time being. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++++++ tests/neg/inline-trait-body-trait-inline.scala | 7 +++++++ tests/pos/inline-trait-body-trait-inline.scala | 7 ------- 3 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 tests/neg/inline-trait-body-trait-inline.scala delete mode 100644 tests/pos/inline-trait-body-trait-inline.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1de68b04ff5f..08fc81b89b6e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2670,6 +2670,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val body1 = addAccessorDefs(cls, typedStats(impl.body, dummy)(using ctx.inClassContext(self1.symbol))._1) if !ctx.isAfterTyper && cls.isInlineTrait then + body1.map(_.symbol).filter(_.isInlineTrait).foreach(innerInlTrait => + report.error( + em"Implementation restriction: an inline trait cannot be defined inside of another inline trait", + innerInlTrait.srcPos + ) + ) val membersToInline = body1.filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) diff --git a/tests/neg/inline-trait-body-trait-inline.scala b/tests/neg/inline-trait-body-trait-inline.scala new file mode 100644 index 000000000000..82144e4c9c4d --- /dev/null +++ b/tests/neg/inline-trait-body-trait-inline.scala @@ -0,0 +1,7 @@ +inline trait A[T]: + inline trait InnerA[U]: // error + val x: (T, U) = ??? + +class B extends A[Int]: + class InnerB extends InnerA[String] + def f: (Int, String) = InnerB().x \ No newline at end of file diff --git a/tests/pos/inline-trait-body-trait-inline.scala b/tests/pos/inline-trait-body-trait-inline.scala deleted file mode 100644 index e255bd6ba42c..000000000000 --- a/tests/pos/inline-trait-body-trait-inline.scala +++ /dev/null @@ -1,7 +0,0 @@ -inline trait A: - inline trait InnerA: - val x = 1 - -class B extends A: - class InnerB extends InnerA - def f = InnerB().x \ No newline at end of file From 67d8b1eb9f3f1bc5e2086d1b012b01f946cbcd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 11 May 2023 11:41:36 +0200 Subject: [PATCH 064/106] Enable inheritance tests --- .../pos/inline-trait-inheritance-multiple-override.scala | 0 tests/{disabled => }/pos/inline-trait-inheritance-multiple.scala | 0 .../inline-trait-inheritance-single-abstract-class-override.scala | 0 .../pos/inline-trait-inheritance-single-abstract-class.scala | 0 .../pos/inline-trait-inheritance-single-object-override.scala | 0 .../pos/inline-trait-inheritance-single-object.scala | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename tests/{disabled => }/pos/inline-trait-inheritance-multiple-override.scala (100%) rename tests/{disabled => }/pos/inline-trait-inheritance-multiple.scala (100%) rename tests/{disabled => }/pos/inline-trait-inheritance-single-abstract-class-override.scala (100%) rename tests/{disabled => }/pos/inline-trait-inheritance-single-abstract-class.scala (100%) rename tests/{disabled => }/pos/inline-trait-inheritance-single-object-override.scala (100%) rename tests/{disabled => }/pos/inline-trait-inheritance-single-object.scala (100%) diff --git a/tests/disabled/pos/inline-trait-inheritance-multiple-override.scala b/tests/pos/inline-trait-inheritance-multiple-override.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-multiple-override.scala rename to tests/pos/inline-trait-inheritance-multiple-override.scala diff --git a/tests/disabled/pos/inline-trait-inheritance-multiple.scala b/tests/pos/inline-trait-inheritance-multiple.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-multiple.scala rename to tests/pos/inline-trait-inheritance-multiple.scala diff --git a/tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala b/tests/pos/inline-trait-inheritance-single-abstract-class-override.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-single-abstract-class-override.scala rename to tests/pos/inline-trait-inheritance-single-abstract-class-override.scala diff --git a/tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala b/tests/pos/inline-trait-inheritance-single-abstract-class.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-single-abstract-class.scala rename to tests/pos/inline-trait-inheritance-single-abstract-class.scala diff --git a/tests/disabled/pos/inline-trait-inheritance-single-object-override.scala b/tests/pos/inline-trait-inheritance-single-object-override.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-single-object-override.scala rename to tests/pos/inline-trait-inheritance-single-object-override.scala diff --git a/tests/disabled/pos/inline-trait-inheritance-single-object.scala b/tests/pos/inline-trait-inheritance-single-object.scala similarity index 100% rename from tests/disabled/pos/inline-trait-inheritance-single-object.scala rename to tests/pos/inline-trait-inheritance-single-object.scala From fde0c55ef1a130fc869db819b3ceacc02f6c6364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 12 May 2023 16:07:28 +0200 Subject: [PATCH 065/106] Check if owner is inline trait last Calling .owner on some traits (for example HashUtils) results in an exception with the message "Cyclic reference involving trait HashTable". Using the short-circuiting of &&, checking for the owner symbol later solves this issue when bootstrapping. --- .../src/dotty/tools/dotc/transform/PruneInlineTraits.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala index 075098247598..5d9db954201a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala @@ -31,13 +31,13 @@ class PruneInlineTraits extends MiniPhase with SymTransformer { thisTransform => else tree private def isEraseable(sym: SymDenotation)(using Context): Boolean = - sym.owner.isInlineTrait + !sym.isType && !sym.isConstructor && !sym.is(Param) && !sym.is(ParamAccessor) && !sym.is(Private) && !sym.isLocalDummy - && !sym.isType + && sym.owner.isInlineTrait } object PruneInlineTraits { From 086f0b96de48a2fcfd88e090e84289ac674f02c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 12 May 2023 16:36:12 +0200 Subject: [PATCH 066/106] Use proper thisType, add coords to inlinedSym --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 66360fcdf1f3..e2b05932c763 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -586,12 +586,15 @@ object Inlines: private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = sym.info match { case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => - val baseThisCls = ThisType.raw(ctx.owner.typeRef).select(sym) + val baseThisCls = ctx.owner.thisType.select(sym) val inlinedSym = newClassSymbol( ctx.owner, sym.asType.name, (sym.flags | Synthetic) &~ withoutFlags, - clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo) + clsSym => + ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo), + privateWithin = sym.privateWithin, + coord = spanCoord(parent.span) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) inlinedSym From 2fc66ac6e78f70bbcb83b97362005b8ad26ac1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 16 May 2023 17:10:20 +0200 Subject: [PATCH 067/106] Extract param accessors code into helper class Parameter accessors are important when inlining an inline trait, and we do manipulations both on their values and their new names when they are changed --- .../dotty/tools/dotc/inlines/Inlines.scala | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index e2b05932c763..6c5d69624404 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -522,8 +522,10 @@ object Inlines: import Inlines.* private val parentSym = symbolFromParent(parent) + private val paramAccessorsMapper = ParamAccessorsMapper() def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = + paramAccessorsMapper.registerParamValuesOf(parent) val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) val inlinedSymbols = stats.map(stat => inlinedSym(stat.symbol)) stats.zip(inlinedSymbols).map(expandStat) @@ -538,8 +540,13 @@ object Inlines: protected class InlineTraitTreeMap extends InlinerTreeMap { override def apply(tree: Tree) = tree match { - case tree @ Select(This(ident), name) if ident.name == parentSym.name && localParamAccessorsNames.contains(name) => - Inlined(EmptyTree, Nil, Select(This(ctx.owner.asClass), localParamAccessorsNames(name)).withSpan(parent.span)).withSpan(tree.span) + case This(ident) if ident.name == parentSym.name => + Inlined(EmptyTree, Nil, This(ctx.owner.asClass).withSpan(parent.span)).withSpan(tree.span) + case Select(qual, name) => + paramAccessorsMapper.getParamAccessorName(qual.symbol, name) match { + case Some(newName) => Select(apply(qual), newName).withSpan(tree.span) + case None => super.apply(tree) + } case tree => super.apply(tree) } @@ -551,22 +558,6 @@ object Inlines: override protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = ctx.fresh.setTyper(inlineTyper).setNewScope - private val paramAccessorsValueOf: Map[Name, Tree] = - def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match - case Apply(fun, args) => allArgs(fun, acc :+ args) - case TypeApply(fun, _) => allArgs(fun, acc) - case _ => acc.toList - def allParams(info: Type, acc: List[List[Name]]): List[List[Name]] = info match - case mt: MethodType => allParams(mt.resultType, mt.paramNames :: acc) - case pt: PolyType => allParams(pt.resultType, acc) - case _ => acc - val info = - if parent.symbol.isClass then parent.symbol.primaryConstructor.info - else parent.symbol.info - allParams(info, Nil).flatten.zip(allArgs(parent, Vector.empty).flatten).toMap - - private val localParamAccessorsNames = new mutable.HashMap[Name, Name] - extension (sym: Symbol) private def isTermParamAccessor: Boolean = !sym.isType && sym.is(ParamAccessor) @@ -610,8 +601,7 @@ object Inlines: if sym.isTermParamAccessor then flags &~= ParamAccessor if sym.is(Local) then - name = name.expandedName(parentSym) - localParamAccessorsNames(sym.name) = name + name = paramAccessorsMapper.registerNewName(sym) sym.copy( owner = ctx.owner, name = name, @@ -621,10 +611,9 @@ object Inlines: private def inlinedValDef(vdef: ValDef, inlinedSym: Symbol)(using Context): ValDef = val rhs = - if vdef.symbol.isTermParamAccessor then - paramAccessorsValueOf(vdef.symbol.name) - else - inlinedRhs(vdef, inlinedSym) + paramAccessorsMapper + .getParamAccessorRhs(vdef.symbol.owner, vdef.symbol.name) + .getOrElse(inlinedRhs(vdef, inlinedSym)) tpd.ValDef(inlinedSym.asTerm, rhs).withSpan(parent.span) private def inlinedDefDef(ddef: DefDef, inlinedSym: Symbol)(using Context): DefDef = @@ -665,5 +654,36 @@ object Inlines: val inlinedRhs = inContext(ctx.withOwner(inlinedSym)) { inlined(rhs)._2 } Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) + private class ParamAccessorsMapper: + private val paramAccessorsTrees: mutable.Map[Symbol, Map[Name, Tree]] = mutable.Map.empty + private val paramAccessorsNewNames: mutable.Map[(Symbol, Name), Name] = mutable.Map.empty + + def registerParamValuesOf(parent: Tree): Unit = + def allArgs(tree: Tree, acc: Vector[List[Tree]]): List[List[Tree]] = tree match + case Apply(fun, args) => allArgs(fun, acc :+ args) + case TypeApply(fun, _) => allArgs(fun, acc) + case _ => acc.toList + def allParams(info: Type, acc: List[List[Name]]): List[List[Name]] = info match + case mt: MethodType => allParams(mt.resultType, mt.paramNames :: acc) + case pt: PolyType => allParams(pt.resultType, acc) + case _ => acc + val info = + if parent.symbol.isClass then parent.symbol.primaryConstructor.info + else parent.symbol.info + val paramAccessors = allParams(info, Nil).flatten.zip(allArgs(parent, Vector.empty).flatten).toMap + paramAccessorsTrees.put(symbolFromParent(parent), paramAccessors) + + def registerNewName(paramAccessorSym: Symbol): paramAccessorSym.ThisName = + val oldName = paramAccessorSym.name + val newName = oldName.expandedName(parentSym) + paramAccessorsNewNames.put((paramAccessorSym.owner, oldName), newName) + newName + + def getParamAccessorRhs(parent: Symbol, paramAccessorName: Name): Option[Tree] = + paramAccessorsTrees.get(parent).flatMap(_.get(paramAccessorName)) + + def getParamAccessorName(parent: Symbol, paramAccessorName: Name): Option[Name] = + paramAccessorsNewNames.get(parent, paramAccessorName) + end ParamAccessorsMapper end InlineParentTrait end Inlines From 686aac0dbb7cdf7b61e291991f1e3f116b0833a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 16 May 2023 17:24:03 +0200 Subject: [PATCH 068/106] Move trait-related code to InlineParentTrait --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 7 ++----- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 8 +++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 42063814a8c8..c987289405ac 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -493,7 +493,7 @@ class Inliner(val call: tpd.Tree)(using Context): * from its `originalOwner`, and, if it comes from outside the inlined method * itself, it has to be marked as an inlined argument. */ - private def integrate(tree: Tree, originalOwner: Symbol)(using Context): Tree = + protected def integrate(tree: Tree, originalOwner: Symbol)(using Context): Tree = // assertAllPositioned(tree) // debug tree.changeOwner(originalOwner, ctx.owner) @@ -523,10 +523,7 @@ class Inliner(val call: tpd.Tree)(using Context): case tree: This => tree.tpe match { case thistpe: ThisType => - val cls = thistpe.cls - if cls.isInlineTrait then - integrate(This(ctx.owner.asClass).withSpan(call.span), cls) - else thisProxy.get(cls) match { + thisProxy.get(thistpe.cls) match { case Some(t) => val thisRef = ref(t).withSpan(call.span) inlinedFromOutside(thisRef)(tree.span) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 6c5d69624404..803c6f548a03 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -540,8 +540,14 @@ object Inlines: protected class InlineTraitTreeMap extends InlinerTreeMap { override def apply(tree: Tree) = tree match { - case This(ident) if ident.name == parentSym.name => + case tree: This if tree.qual.name == parentSym.name => Inlined(EmptyTree, Nil, This(ctx.owner.asClass).withSpan(parent.span)).withSpan(tree.span) + case tree: This => + tree.tpe match { + case thisTpe: ThisType if thisTpe.cls.isInlineTrait => + integrate(This(ctx.owner.asClass).withSpan(call.span), thisTpe.cls) + case _ => super.apply(tree) + } case Select(qual, name) => paramAccessorsMapper.getParamAccessorName(qual.symbol, name) match { case Some(newName) => Select(apply(qual), newName).withSpan(tree.span) From 78077a4b959e477ffa5054913f6b2c70a24b7e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 11:07:40 +0200 Subject: [PATCH 069/106] Add missing context to expandStat, refactor code --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 803c6f548a03..a2e1d40b774a 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -567,7 +567,7 @@ object Inlines: extension (sym: Symbol) private def isTermParamAccessor: Boolean = !sym.isType && sym.is(ParamAccessor) - private def expandStat(stat: tpd.Tree, inlinedSym: Symbol): tpd.Tree = stat match + private def expandStat(stat: tpd.Tree, inlinedSym: Symbol)(using Context): tpd.Tree = stat match case stat: ValDef => inlinedValDef(stat, inlinedSym) case stat: DefDef => @@ -590,8 +590,8 @@ object Inlines: (sym.flags | Synthetic) &~ withoutFlags, clsSym => ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo), - privateWithin = sym.privateWithin, - coord = spanCoord(parent.span) + sym.privateWithin, + spanCoord(parent.span) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) inlinedSym @@ -643,8 +643,9 @@ object Inlines: private def inlinedClassDef(clsDef: TypeDef, inlinedCls: ClassSymbol)(using Context): Tree = val TypeDef(_, tmpl: Template) = clsDef: @unchecked val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { + val inlinedConstr = inlinedPrimaryConstructorDefDef(tmpl.constr) val inlinedTmpl = tmpl.body.map(stat => expandStat(stat, inlinedSym(stat.symbol))) - (inlinedPrimaryConstructorDefDef(tmpl.constr), inlinedTmpl) + (inlinedConstr, inlinedTmpl) } val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) inlined(clsDef1)._2.withSpan(clsDef.span) // TODO adapt parents From 2172d037618282130692ffb1ad1553c6ff45c0f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 11:33:04 +0200 Subject: [PATCH 070/106] Refactor Inlining for clarity --- .../dotty/tools/dotc/transform/Inlining.scala | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 36fd0fc5c58f..3456eb7da605 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -67,26 +67,26 @@ class Inlining extends MacroTransform, SymTransformer { else sym - def transformInlineClasses(tree: Tree)(using Context): Tree = - val tpd.TypeDef(name, tmpl: Template) = tree: @unchecked + def transformInnerClasses(inlineTrait: TypeDef)(using Context): TypeDef = + val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked val body1 = tmpl.body.flatMap { - case tdef @ tpd.TypeDef(name, tmpl1: Template) => - val newTrait = cpy.TypeDef(tdef)(name = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX) + case innerClass @ tpd.TypeDef(name, tmpl1: Template) => + val newTrait = cpy.TypeDef(innerClass)(name = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX) val newTypeSym = newSymbol( - owner = tree.symbol, + owner = inlineTrait.symbol, name = name.asTypeName, - flags = tdef.symbol.flags & (Private | Protected), + flags = innerClass.symbol.flags & (Private | Protected), info = TypeBounds.upper(newTrait.symbol.typeRef), - privateWithin = tdef.symbol.privateWithin, - coord = tdef.symbol.coord, - nestingLevel = tdef.symbol.nestingLevel, + privateWithin = innerClass.symbol.privateWithin, + coord = innerClass.symbol.coord, + nestingLevel = innerClass.symbol.nestingLevel, ).asType List(newTrait, TypeDef(newTypeSym)) case member => List(member) } val tmpl1 = cpy.Template(tmpl)(body = body1) - cpy.TypeDef(tree)(name, tmpl1) + cpy.TypeDef(inlineTrait)(rhs = tmpl1) private class InliningTreeMap extends TreeMapWithImplicits { @@ -98,11 +98,11 @@ class Inlining extends MacroTransform, SymTransformer { override def transform(tree: Tree)(using Context): Tree = { tree match case tree: TypeDef if tree.symbol.isInlineTrait => - transformInlineClasses(tree) + transformInnerClasses(tree) case tree: TypeDef if Inlines.needsInlining(tree) => - val tree1 = super.transform(tree) + val tree1 = super.transform(tree).asInstanceOf[TypeDef] if tree1.tpe.isError then tree1 - else if tree1.symbol.isInlineTrait then transformInlineClasses(tree1) + else if tree1.symbol.isInlineTrait then transformInnerClasses(tree1) else Inlines.inlineParentInlineTraits(tree1) case tree: MemberDef => if tree.symbol.is(Inline) then tree From 3f749f11967328f9e015a9698f885610c65f7f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 11:44:25 +0200 Subject: [PATCH 071/106] Factorize code for new inner class names --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 3456eb7da605..42febb959757 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -16,6 +16,7 @@ import dotty.tools.dotc.staging.StagingLevel import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.StdNames.str import dotty.tools.dotc.core.Types.TypeBounds +import dotty.tools.dotc.core.Names.Name import scala.collection.mutable.ListBuffer @@ -63,7 +64,7 @@ class Inlining extends MacroTransform, SymTransformer { override def transformSym(sym: SymDenotation)(using Context): SymDenotation = if sym.isClass && sym.owner.isInlineTrait && !sym.is(Module) then - sym.copySymDenotation(name = sym.name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX, initFlags = (sym.flags &~ Final) | Trait) + sym.copySymDenotation(name = newInnerClassName(sym.name), initFlags = (sym.flags &~ Final) | Trait) else sym @@ -71,7 +72,7 @@ class Inlining extends MacroTransform, SymTransformer { val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked val body1 = tmpl.body.flatMap { case innerClass @ tpd.TypeDef(name, tmpl1: Template) => - val newTrait = cpy.TypeDef(innerClass)(name = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX) + val newTrait = cpy.TypeDef(innerClass)(name = newInnerClassName(name)) val newTypeSym = newSymbol( owner = inlineTrait.symbol, name = name.asTypeName, @@ -145,6 +146,8 @@ class Inlining extends MacroTransform, SymTransformer { else super.transform(tree) } } + + private def newInnerClassName(name: Name): name.ThisName = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX } object Inlining: From 5570b25399810c607515344b72e116ee949af1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 16:47:06 +0200 Subject: [PATCH 072/106] Remove last refs to inline trait from Inliner --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 13 ++++++++----- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 7 +++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index c987289405ac..e3a03917d111 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -283,7 +283,7 @@ class Inliner(val call: tpd.Tree)(using Context): private def classNestingLevel(cls: Symbol) = cls.ownersIterator.count(_.isClass) // Compute val-definitions for all this-proxies and append them to `bindingsBuf` - private def computeThisBindings() = { + protected def computeThisBindings() = { // All needed this-proxies, paired-with and sorted-by nesting depth of // the classes they represent (innermost first) val sortedProxies = thisProxy.toList @@ -430,7 +430,7 @@ class Inliner(val call: tpd.Tree)(using Context): else arg else arg - private def canElideThis(tpe: ThisType): Boolean = + protected def canElideThis(tpe: ThisType): Boolean = inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) || tpe.cls.isContainedIn(inlinedMethod) || tpe.cls.is(Package) @@ -451,7 +451,7 @@ class Inliner(val call: tpd.Tree)(using Context): * and MethodParams, not TypeRefs or TermRefs. */ private def registerType(tpe: Type): Unit = tpe match { - case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) && !tpe.cls.isInlineTrait => + case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) => val proxyName = s"${tpe.cls.name}_this".toTermName val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match { case typeMatchResult if typeMatchResult.exists => typeMatchResult @@ -563,6 +563,9 @@ class Inliner(val call: tpd.Tree)(using Context): protected val inlinerTypeMap: InlinerTypeMap = InlinerTypeMap() protected val inlinerTreeMap: InlinerTreeMap = InlinerTreeMap() + protected def substFrom: List[Symbol] = Nil + protected def substTo: List[Symbol] = Nil + protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope @@ -620,8 +623,8 @@ class Inliner(val call: tpd.Tree)(using Context): treeMap = inlinerTreeMap, oldOwners = inlinedMethod :: Nil, newOwners = ctx.owner :: Nil, - substFrom = Nil, - substTo = Nil + substFrom = substFrom, + substTo = substTo )(using inlineCtx) inlining.println( diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index a2e1d40b774a..3b59e8daf494 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -523,6 +523,7 @@ object Inlines: private val parentSym = symbolFromParent(parent) private val paramAccessorsMapper = ParamAccessorsMapper() + private val innerClassNewSyms: mutable.LinkedHashMap[Symbol, Symbol] = mutable.LinkedHashMap.empty def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = paramAccessorsMapper.registerParamValuesOf(parent) @@ -561,6 +562,12 @@ object Inlines: override protected val inlinerTypeMap: InlinerTypeMap = InlineTraitTypeMap() override protected val inlinerTreeMap: InlinerTreeMap = InlineTraitTreeMap() + override protected def substFrom: List[Symbol] = innerClassNewSyms.keys.toList + override protected def substTo: List[Symbol] = innerClassNewSyms.values.toList + + override protected def computeThisBindings(): Unit = () + override protected def canElideThis(tpe: ThisType): Boolean = true + override protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = ctx.fresh.setTyper(inlineTyper).setNewScope From 829f9eb60784919bb303b63c4c1bc5f80b5461e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:34:52 +0200 Subject: [PATCH 073/106] Call super before matching in Tree-/TypeMap Override some properties of Inliner that hinder the work of the inline trait inliner. Some of the work done in the Inliner works only for method calls, not for bodies --- .../dotty/tools/dotc/inlines/Inliner.scala | 11 ++++--- .../dotty/tools/dotc/inlines/Inlines.scala | 30 ++++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e3a03917d111..447eecbca36f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -115,9 +115,10 @@ object Inliner: oldOwners: List[Symbol], newOwners: List[Symbol], substFrom: List[Symbol], - substTo: List[Symbol])(using Context) + substTo: List[Symbol], + val inlineCopier: TreeCopier)(using Context) extends TreeTypeMap( - typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()): + typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, inlineCopier): override def copy( typeMap: Type => Type, @@ -126,7 +127,7 @@ object Inliner: newOwners: List[Symbol], substFrom: List[Symbol], substTo: List[Symbol])(using Context) = - new InlinerMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo) + new InlinerMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, inlineCopier) override def transformInlined(tree: Inlined)(using Context) = if tree.call.isEmpty then @@ -565,6 +566,7 @@ class Inliner(val call: tpd.Tree)(using Context): protected def substFrom: List[Symbol] = Nil protected def substTo: List[Symbol] = Nil + protected def inlineCopier: TreeCopier = InlineCopier() protected def inlineCtx(inlineTyper: InlineTyper)(using Context): Context = inlineContext(call).fresh.setTyper(inlineTyper).setNewScope @@ -624,7 +626,8 @@ class Inliner(val call: tpd.Tree)(using Context): oldOwners = inlinedMethod :: Nil, newOwners = ctx.owner :: Nil, substFrom = substFrom, - substTo = substTo + substTo = substTo, + inlineCopier = inlineCopier )(using inlineCtx) inlining.println( diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 3b59e8daf494..01e1ea119bfd 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -533,29 +533,32 @@ object Inlines: end expandDefs protected class InlineTraitTypeMap extends InlinerTypeMap { - override def apply(t: Type) = t match { - case t: ThisType if t.cls == parentSym => ctx.owner.thisType - case t => super.apply(t) + override def apply(t: Type) = super.apply(t) match { + case t: ThisType if t.cls == parentSym => + ctx.owner.thisType + case t => + mapOver(t) } } protected class InlineTraitTreeMap extends InlinerTreeMap { - override def apply(tree: Tree) = tree match { - case tree: This if tree.qual.name == parentSym.name => - Inlined(EmptyTree, Nil, This(ctx.owner.asClass).withSpan(parent.span)).withSpan(tree.span) + override def apply(tree: Tree) = super.apply(tree) match { + case tree: This if tree.symbol == parentSym => + Inlined(EmptyTree, Nil, This(ctx.owner.asClass).withSpan(parent.span)).withSpan(parent.span) case tree: This => tree.tpe match { case thisTpe: ThisType if thisTpe.cls.isInlineTrait => - integrate(This(ctx.owner.asClass).withSpan(call.span), thisTpe.cls) - case _ => super.apply(tree) + integrate(This(ctx.owner.asClass).withSpan(parent.span), thisTpe.cls) + case _ => + tree } case Select(qual, name) => paramAccessorsMapper.getParamAccessorName(qual.symbol, name) match { - case Some(newName) => Select(apply(qual), newName).withSpan(tree.span) - case None => super.apply(tree) + case Some(newName) => Select(this(qual), newName).withSpan(parent.span) + case None => Select(this(qual), name) } case tree => - super.apply(tree) + tree } } @@ -564,6 +567,11 @@ object Inlines: override protected def substFrom: List[Symbol] = innerClassNewSyms.keys.toList override protected def substTo: List[Symbol] = innerClassNewSyms.values.toList + override protected def inlineCopier: tpd.TreeCopier = new TypedTreeCopier() { + // FIXME it feels weird... Is this correct? + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(using Context): Apply = + untpd.cpy.Apply(tree)(fun, args).withTypeUnchecked(tree.tpe) + } override protected def computeThisBindings(): Unit = () override protected def canElideThis(tpe: ThisType): Boolean = true From a1bc61f7ed94ca7ea9fabb8d31c7dd62118c958c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:39:07 +0200 Subject: [PATCH 074/106] Improve class symbol inlining Pass clsInfo to inlinerTypeMap to adapt the types automatically --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 01e1ea119bfd..2b872cf800cb 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -597,19 +597,20 @@ object Inlines: private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = sym.info match { - case ClassInfo(prefix, cls, declaredParents, scope, selfInfo) => - val baseThisCls = ctx.owner.thisType.select(sym) + case clsInfo: ClassInfo => val inlinedSym = newClassSymbol( ctx.owner, - sym.asType.name, + sym.name, (sym.flags | Synthetic) &~ withoutFlags, - clsSym => - ClassInfo(inlinerTypeMap(prefix), clsSym, declaredParents :+ baseThisCls, Scopes.newScope, selfInfo), + newCls => { + val ClassInfo(prefix, _, parents, _, selfInfo) = inlinerTypeMap.mapClassInfo(clsInfo) + ClassInfo(prefix, newCls, parents :+ ctx.owner.thisType.select(sym), Scopes.newScope, selfInfo) // TODO check if need to add type params to new parent + }, sym.privateWithin, spanCoord(parent.span) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) - inlinedSym + inlinedSym.entered case _ => report.error(s"Class symbol ${sym.show} does not have class info") sym From 52264506f9dfea187b53ef022417eb4dc66c717d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:46:02 +0200 Subject: [PATCH 075/106] Prevent creating new accessor names for inner class' params If a param is inside of an inline trait, we create a new name when we inline it in the child class-like If it is inside of an inner class-like, we don't, because there is no conflict there --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 2b872cf800cb..c0ffbdf5caa1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -622,7 +622,7 @@ object Inlines: if sym.isType || !sym.is(Private) then flags |= Override if sym.isTermParamAccessor then flags &~= ParamAccessor - if sym.is(Local) then + if sym.is(Local) && sym.owner.isInlineTrait then name = paramAccessorsMapper.registerNewName(sym) sym.copy( owner = ctx.owner, From 728d837e8b5c83300e0ceebfef6a68cc642d4c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:54:42 +0200 Subject: [PATCH 076/106] Do not add override flag to inner class-like type params --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index c0ffbdf5caa1..b2a806feb275 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -660,7 +660,12 @@ object Inlines: val TypeDef(_, tmpl: Template) = clsDef: @unchecked val (constr, body) = inContext(ctx.withOwner(inlinedCls)) { val inlinedConstr = inlinedPrimaryConstructorDefDef(tmpl.constr) - val inlinedTmpl = tmpl.body.map(stat => expandStat(stat, inlinedSym(stat.symbol))) + val inlinedTmpl = tmpl.body.map { + case stat: TypeDef if stat.symbol.isAllOf(PrivateLocal | Param) => + expandStat(stat, inlinedSym(stat.symbol, withoutFlags = Override)) + case stat => + expandStat(stat, inlinedSym(stat.symbol)) + } (inlinedConstr, inlinedTmpl) } val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) From 2392575598773a83d86f146231b07931aec60059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:52:57 +0200 Subject: [PATCH 077/106] Remove incorrect parent, TODO add correct one --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index b2a806feb275..6acebd8add20 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -668,8 +668,8 @@ object Inlines: } (inlinedConstr, inlinedTmpl) } - val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents :+ This(ctx.owner.asClass).select(clsDef.symbol), body) - inlined(clsDef1)._2.withSpan(clsDef.span) // TODO adapt parents + val clsDef1 = tpd.ClassDefWithParents(inlinedCls, constr, tmpl.parents, body) // TODO add correct parent tree + inlined(clsDef1)._2.withSpan(clsDef.span) private def inlinedTypeDef(tdef: TypeDef, inlinedSym: Symbol)(using Context): TypeDef = tpd.TypeDef(inlinedSym.asType).withSpan(parent.span) From c4f13af639ff8789300c38465986974b2e86aa6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:54:00 +0200 Subject: [PATCH 078/106] Add proper type bound to generated type The type generated to act as a placeholder for the generated inner class-like needs to extend the original class-like, type params included --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 42febb959757..4a07c11a52d4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -15,7 +15,7 @@ import dotty.tools.dotc.core.DenotTransformers.SymTransformer import dotty.tools.dotc.staging.StagingLevel import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.StdNames.str -import dotty.tools.dotc.core.Types.TypeBounds +import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.Names.Name import scala.collection.mutable.ListBuffer @@ -73,11 +73,17 @@ class Inlining extends MacroTransform, SymTransformer { val body1 = tmpl.body.flatMap { case innerClass @ tpd.TypeDef(name, tmpl1: Template) => val newTrait = cpy.TypeDef(innerClass)(name = newInnerClassName(name)) + val upperBound = innerClass.symbol.primaryConstructor.info match { + case _: MethodType => + newTrait.symbol.typeRef + case poly: PolyType => + HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTrait.symbol.typeRef.appliedTo(tl.paramRefs.head)) + } val newTypeSym = newSymbol( owner = inlineTrait.symbol, name = name.asTypeName, flags = innerClass.symbol.flags & (Private | Protected), - info = TypeBounds.upper(newTrait.symbol.typeRef), + info = TypeBounds.upper(upperBound), privateWithin = innerClass.symbol.privateWithin, coord = innerClass.symbol.coord, nestingLevel = innerClass.symbol.nestingLevel, From 0b6ec5e604c6725192df1672afcbc78c1c02c920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 23 May 2023 22:55:42 +0200 Subject: [PATCH 079/106] Register class-like symbol to be replaced by inliner Instruct the inliner that all references to the old symbol of the inner class must be replaced with references to the symbol of the class-like we just generated --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 6acebd8add20..8b080ea31702 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -604,12 +604,13 @@ object Inlines: (sym.flags | Synthetic) &~ withoutFlags, newCls => { val ClassInfo(prefix, _, parents, _, selfInfo) = inlinerTypeMap.mapClassInfo(clsInfo) - ClassInfo(prefix, newCls, parents :+ ctx.owner.thisType.select(sym), Scopes.newScope, selfInfo) // TODO check if need to add type params to new parent + ClassInfo(prefix, newCls, parents :+ ctx.owner.thisType.select(sym), Scopes.newScope, selfInfo) // TODO fix new parent type (wrong symbol?) }, sym.privateWithin, spanCoord(parent.span) ) inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) + innerClassNewSyms.put(sym, inlinedSym) inlinedSym.entered case _ => report.error(s"Class symbol ${sym.show} does not have class info") From 6075cd345c40cd3c4fe9e5f0879c0b8b135b2fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 24 May 2023 14:05:32 +0200 Subject: [PATCH 080/106] Forbid term params for inline traits This decision, approved by M. Odersky and N. Stucki, comes from the fact that traits cannot call the constructors of other traits. This creates a problem when inlining inner traits, because they need to extend their original counterpart. However, since it cannot pass arguments, the extension would need to be generated when inlining, which adds the need for param bindings. Example: ``` inline trait A: trait Inner(x: Int) /* * replaced by: * * trait Inner$trait(x: Int) * type Inner <: Inner$trait */ end A class B extends A: /* * generated: * * trait Inner(x: Int) extends Inner$trait // missing term x */ end B val b = B() class C extends B.Inner(1) // invalid: must declare extension of Inner$trait explicitly ``` --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 12 ++++++++++++ tests/neg/inline-trait-body-trait-parameter.scala | 7 +++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/neg/inline-trait-body-trait-parameter.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 08fc81b89b6e..2537d72d5824 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2677,6 +2677,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ) ) val membersToInline = body1.filter(member => Inlines.isInlineableFromInlineTrait(cls, member)) + membersToInline.foreach { + case tdef: TypeDef if tdef.symbol.isClass => + def rec(paramss: List[List[Symbol]]): Unit = paramss match { + case (param :: _) :: _ if param.isTerm => + report.error(em"Implementation restriction: inline traits cannot have term parameters", param.srcPos) + case _ :: paramss => + rec(paramss) + case _ => + } + rec(tdef.symbol.primaryConstructor.paramSymss) + case _ => + } val wrappedMembersToInline = Block(membersToInline, unitLiteral).withSpan(cdef.span) PrepareInlineable.registerInlineInfo(cls, wrappedMembersToInline) diff --git a/tests/neg/inline-trait-body-trait-parameter.scala b/tests/neg/inline-trait-body-trait-parameter.scala new file mode 100644 index 000000000000..ec11c5b9052e --- /dev/null +++ b/tests/neg/inline-trait-body-trait-parameter.scala @@ -0,0 +1,7 @@ +inline trait A[T]: + trait InnerAType[T >: Int <: AnyVal] + trait InnerATypes[T <: AnyVal, U <: T] + trait InnerATerm(i: Int) // error + trait InnerATerms(i: Int, j: Double) // error + trait InnerATermsCurried(i: Int, j: Double)(k: String) // error + trait InnerAAllCurried[T, U](i: T, j: U)(k: (T, U)) // error \ No newline at end of file From 74aaba98e1ebc435096c240b431ca92b48ce7d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 25 May 2023 10:08:08 +0200 Subject: [PATCH 081/106] Create vals for child type and child tree --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 8b080ea31702..4de4ace542b6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -525,6 +525,9 @@ object Inlines: private val paramAccessorsMapper = ParamAccessorsMapper() private val innerClassNewSyms: mutable.LinkedHashMap[Symbol, Symbol] = mutable.LinkedHashMap.empty + private val childThisType = ctx.owner.thisType + private val childThisTree = This(ctx.owner.asClass).withSpan(parent.span) + def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = paramAccessorsMapper.registerParamValuesOf(parent) val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) @@ -534,17 +537,15 @@ object Inlines: protected class InlineTraitTypeMap extends InlinerTypeMap { override def apply(t: Type) = super.apply(t) match { - case t: ThisType if t.cls == parentSym => - ctx.owner.thisType - case t => - mapOver(t) + case t: ThisType if t.cls == parentSym => childThisType + case t => mapOver(t) } } protected class InlineTraitTreeMap extends InlinerTreeMap { override def apply(tree: Tree) = super.apply(tree) match { case tree: This if tree.symbol == parentSym => - Inlined(EmptyTree, Nil, This(ctx.owner.asClass).withSpan(parent.span)).withSpan(parent.span) + Inlined(EmptyTree, Nil, childThisTree).withSpan(parent.span) case tree: This => tree.tpe match { case thisTpe: ThisType if thisTpe.cls.isInlineTrait => @@ -593,7 +594,7 @@ object Inlines: inlinedTypeDef(stat, inlinedSym) private def inlinedSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = - if sym.isClass then inlinedClassSym(sym.asClass, withoutFlags) else inlinedMemberSym(sym, withoutFlags) + if sym.isClass then inlinedClassSym(sym.asClass, withoutFlags) else inlinedMemberSym(sym, withoutFlags) private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = sym.info match { From 9514ccddb160a6ee9d4eda826d35b8bb20056b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 25 May 2023 10:20:22 +0200 Subject: [PATCH 082/106] Add proper parent to inner class info This creates a satisfactory type for the new extending class, but it causes an assertion error when checking after inlining: ``` 5 |class B extends A[Int] |^^^^^^^^^^^^^^^^^^^^^^ *** error while checking tests\pos\inline-trait-body-trait.scala after phase inlining *** Exception in thread "main" java.lang.AssertionError: assertion failed: orphan param: U, hash of binder = 2032399795, tree = , type = AppliedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class )),class B)),trait InnerA$trait),List(TypeParamRef(U))) ``` --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 4de4ace542b6..dbe72c45a967 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -599,13 +599,19 @@ object Inlines: private def inlinedClassSym(sym: ClassSymbol, withoutFlags: FlagSet = EmptyFlags)(using Context): ClassSymbol = sym.info match { case clsInfo: ClassInfo => + val typeParams: List[Type] = sym.primaryConstructor.info match { + case poly: PolyType => poly.paramRefs + case _ => Nil + } + // Extend inner class from inline trait to preserve typing + val newParent = ctx.owner.thisType.select(sym).appliedTo(typeParams) val inlinedSym = newClassSymbol( ctx.owner, sym.name, (sym.flags | Synthetic) &~ withoutFlags, newCls => { val ClassInfo(prefix, _, parents, _, selfInfo) = inlinerTypeMap.mapClassInfo(clsInfo) - ClassInfo(prefix, newCls, parents :+ ctx.owner.thisType.select(sym), Scopes.newScope, selfInfo) // TODO fix new parent type (wrong symbol?) + ClassInfo(prefix, newCls, parents :+ newParent, Scopes.newScope, selfInfo) // TODO fix selfInfo (what to use?) }, sym.privateWithin, spanCoord(parent.span) From a5b53f83ad304166b36eaa1c9305949ed1596f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 25 May 2023 17:43:56 +0200 Subject: [PATCH 083/106] Make function to inline just rhs --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index dbe72c45a967..f86dd8822e0d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -684,11 +684,14 @@ object Inlines: private def inlinedRhs(vddef: ValOrDefDef, inlinedSym: Symbol)(using Context): Tree = val rhs = vddef.rhs.changeOwner(vddef.symbol, inlinedSym) + inlinedRhs(rhs)(using ctx.withOwner(inlinedSym)) + + private def inlinedRhs(rhs: Tree)(using Context): Tree = if rhs.isEmpty then rhs else - val inlinedRhs = inContext(ctx.withOwner(inlinedSym)) { inlined(rhs)._2 } - Inlined(tpd.ref(parentSym), Nil, inlinedRhs).withSpan(parent.span) + // TODO make version of inlined that does not return bindings? + Inlined(tpd.ref(parentSym), Nil, inlined(rhs)._2).withSpan(parent.span) private class ParamAccessorsMapper: private val paramAccessorsTrees: mutable.Map[Symbol, Map[Name, Tree]] = mutable.Map.empty From 6fc5bd962117d6d05d5d0f0a6e7c9f85c9943329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 25 May 2023 17:45:55 +0200 Subject: [PATCH 084/106] Handle inlining of statements in inline trait body Statements in the body of an inline trait need to be inlined properly: - before any statement of the implementing child - making sure that the body is not executed twice (statements removed in Inlining) --- .../dotty/tools/dotc/inlines/Inlines.scala | 11 +++++++--- .../dotty/tools/dotc/transform/Inlining.scala | 10 +++++---- tests/run/inline-trait-body-statements.check | 8 +++++++ tests/run/inline-trait-body-statements.scala | 22 +++++++++++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 tests/run/inline-trait-body-statements.check create mode 100644 tests/run/inline-trait-body-statements.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index f86dd8822e0d..5da63fdd9772 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -220,7 +220,7 @@ object Inlines: val overriddenSymbols = clsOverriddenSyms ++ defs.flatMap(_.symbol.allOverriddenSymbols) defs ::: InlineParentTrait(parent)(using ctx.withOwner(cls.symbol)).expandDefs(overriddenSymbols) ) - val impl1 = cpy.Template(impl)(body = impl.body ::: inlineDefs) + val impl1 = cpy.Template(impl)(body = inlineDefs ::: impl.body) cpy.TypeDef(cls)(rhs = impl1) case _ => cls @@ -531,8 +531,13 @@ object Inlines: def expandDefs(overriddenDecls: Set[Symbol]): List[Tree] = paramAccessorsMapper.registerParamValuesOf(parent) val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) - val inlinedSymbols = stats.map(stat => inlinedSym(stat.symbol)) - stats.zip(inlinedSymbols).map(expandStat) + stats.map{ + case member: MemberDef => Left((member, inlinedSym(member.symbol))) + case stat => Right(stat) + }.map{ + case Left((tree, inlinedSym)) => expandStat(tree, inlinedSym) + case Right(tree) => inlinedRhs(tree) + } end expandDefs protected class InlineTraitTypeMap extends InlinerTypeMap { diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 4a07c11a52d4..6c0269282592 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -68,7 +68,7 @@ class Inlining extends MacroTransform, SymTransformer { else sym - def transformInnerClasses(inlineTrait: TypeDef)(using Context): TypeDef = + def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked val body1 = tmpl.body.flatMap { case innerClass @ tpd.TypeDef(name, tmpl1: Template) => @@ -89,8 +89,10 @@ class Inlining extends MacroTransform, SymTransformer { nestingLevel = innerClass.symbol.nestingLevel, ).asType List(newTrait, TypeDef(newTypeSym)) - case member => + case member: MemberDef => List(member) + case _ => + Nil } val tmpl1 = cpy.Template(tmpl)(body = body1) cpy.TypeDef(inlineTrait)(rhs = tmpl1) @@ -105,11 +107,11 @@ class Inlining extends MacroTransform, SymTransformer { override def transform(tree: Tree)(using Context): Tree = { tree match case tree: TypeDef if tree.symbol.isInlineTrait => - transformInnerClasses(tree) + transformInlineTrait(tree) case tree: TypeDef if Inlines.needsInlining(tree) => val tree1 = super.transform(tree).asInstanceOf[TypeDef] if tree1.tpe.isError then tree1 - else if tree1.symbol.isInlineTrait then transformInnerClasses(tree1) + else if tree1.symbol.isInlineTrait then transformInlineTrait(tree1) else Inlines.inlineParentInlineTraits(tree1) case tree: MemberDef => if tree.symbol.is(Inline) then tree diff --git a/tests/run/inline-trait-body-statements.check b/tests/run/inline-trait-body-statements.check new file mode 100644 index 000000000000..73b256c35fd0 --- /dev/null +++ b/tests/run/inline-trait-body-statements.check @@ -0,0 +1,8 @@ +1 +foo +foo +1 +foo +bar +bar +bar diff --git a/tests/run/inline-trait-body-statements.scala b/tests/run/inline-trait-body-statements.scala new file mode 100644 index 000000000000..5da24022e99d --- /dev/null +++ b/tests/run/inline-trait-body-statements.scala @@ -0,0 +1,22 @@ +inline trait A[T]: + var x = 1 + def foo = + if x == 1 then println("1") + x = 1 - x + println("foo") + + foo + foo + foo + +class B extends A[Int]: + def bar = + if x == 1 then println("1") + println("bar") + + bar + bar + bar + +@main def Test = + B() \ No newline at end of file From 8a756d2c9704f0c85adbd1434da5ff30bdfb52d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 30 May 2023 10:12:43 +0200 Subject: [PATCH 085/106] Add tests for inner traits The test for generic inner trait is currently disabled: generic inner traits do not work yet --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 2 +- tests/disabled/pos/inline-trait-body-trait-generic.scala | 6 ++++++ tests/neg/inline-trait-body-trait-term-parameters.scala | 7 +++++++ tests/pos/inline-trait-body-trait-simple.scala | 6 ++++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/disabled/pos/inline-trait-body-trait-generic.scala create mode 100644 tests/neg/inline-trait-body-trait-term-parameters.scala create mode 100644 tests/pos/inline-trait-body-trait-simple.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 5da63fdd9772..0c1dba7e053b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -532,7 +532,7 @@ object Inlines: paramAccessorsMapper.registerParamValuesOf(parent) val stats = Inlines.defsToInline(parentSym).filterNot(stat => overriddenDecls.contains(stat.symbol)) stats.map{ - case member: MemberDef => Left((member, inlinedSym(member.symbol))) + case member: MemberDef => Left((member, inlinedSym(member.symbol))) // Private symbols must be entered before the RHSs are inlined case stat => Right(stat) }.map{ case Left((tree, inlinedSym)) => expandStat(tree, inlinedSym) diff --git a/tests/disabled/pos/inline-trait-body-trait-generic.scala b/tests/disabled/pos/inline-trait-body-trait-generic.scala new file mode 100644 index 000000000000..999dd0c8c1ea --- /dev/null +++ b/tests/disabled/pos/inline-trait-body-trait-generic.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + trait InnerA[U]: + def x: (T, U) = ??? + +class B extends A[Int]: + class InnerB extends InnerA[String] \ No newline at end of file diff --git a/tests/neg/inline-trait-body-trait-term-parameters.scala b/tests/neg/inline-trait-body-trait-term-parameters.scala new file mode 100644 index 000000000000..ef7c96baf6c4 --- /dev/null +++ b/tests/neg/inline-trait-body-trait-term-parameters.scala @@ -0,0 +1,7 @@ + +inline trait A[T]: + trait InnerA(t: T): // error + def x: T = t + +class B extends A[Int]: + class InnerB extends InnerA(???) \ No newline at end of file diff --git a/tests/pos/inline-trait-body-trait-simple.scala b/tests/pos/inline-trait-body-trait-simple.scala new file mode 100644 index 000000000000..14d4bcf64563 --- /dev/null +++ b/tests/pos/inline-trait-body-trait-simple.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + trait InnerA: + def x: T = ??? + +class B extends A[Int]: + class InnerB extends InnerA \ No newline at end of file From 39e57f26ff78547ef70f684660f12ec59042c4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 31 May 2023 17:32:06 +0200 Subject: [PATCH 086/106] Remove FIXME along with info mapping For some reason, this is not needed anymore (don't know why at all...) --- compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 3823dd8fec2b..955892b2ae22 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -124,7 +124,6 @@ class TreeTypeMap( case ddef @ DefDef(name, paramss, tpt, _) => val (tmap1, paramss1) = transformAllParamss(paramss) val res = cpy.DefDef(ddef)(name, paramss1, tmap1.transform(tpt), tmap1.transform(ddef.rhs)) - res.symbol.info = mapType(res.symbol.info) // FIXME inline traits' info doesn't get mapped res.symbol.setParamssFromDefs(paramss1) res.symbol.transformAnnotations { case ann: BodyAnnotation => ann.derivedAnnotation(transform(ann.tree)) From d7ada60d7781c5c0cefdc9825f3bf550daeaed75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 09:56:04 +0200 Subject: [PATCH 087/106] Add test for anonymous class extending inline trait --- tests/pos/inline-trait-usage-anonymous-class.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/pos/inline-trait-usage-anonymous-class.scala diff --git a/tests/pos/inline-trait-usage-anonymous-class.scala b/tests/pos/inline-trait-usage-anonymous-class.scala new file mode 100644 index 000000000000..c51dd05a6907 --- /dev/null +++ b/tests/pos/inline-trait-usage-anonymous-class.scala @@ -0,0 +1,4 @@ +inline trait A[T]: + val x: T = ??? + +val a = new A[Int] {} \ No newline at end of file From 4b99869a0636238fa85e6de03b4a4037328369e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 10:02:20 +0200 Subject: [PATCH 088/106] Rename param for clarity --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 6c0269282592..b1b7e830390d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -62,11 +62,11 @@ class Inlining extends MacroTransform, SymTransformer { new InliningTreeMap().transform(tree) } - override def transformSym(sym: SymDenotation)(using Context): SymDenotation = - if sym.isClass && sym.owner.isInlineTrait && !sym.is(Module) then - sym.copySymDenotation(name = newInnerClassName(sym.name), initFlags = (sym.flags &~ Final) | Trait) + override def transformSym(symd: SymDenotation)(using Context): SymDenotation = + if symd.isClass && symd.owner.isInlineTrait && !symd.is(Module) then + symd.copySymDenotation(name = newInnerClassName(symd.name), initFlags = (symd.flags &~ Final) | Trait) else - sym + symd def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked From 746e677a4b229120c89eff36a673f22532b7233f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 10:11:23 +0200 Subject: [PATCH 089/106] Create helper methods for inline trait rewriting --- .../dotty/tools/dotc/transform/Inlining.scala | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index b1b7e830390d..42cb2c93a2db 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -71,24 +71,10 @@ class Inlining extends MacroTransform, SymTransformer { def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked val body1 = tmpl.body.flatMap { - case innerClass @ tpd.TypeDef(name, tmpl1: Template) => - val newTrait = cpy.TypeDef(innerClass)(name = newInnerClassName(name)) - val upperBound = innerClass.symbol.primaryConstructor.info match { - case _: MethodType => - newTrait.symbol.typeRef - case poly: PolyType => - HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTrait.symbol.typeRef.appliedTo(tl.paramRefs.head)) - } - val newTypeSym = newSymbol( - owner = inlineTrait.symbol, - name = name.asTypeName, - flags = innerClass.symbol.flags & (Private | Protected), - info = TypeBounds.upper(upperBound), - privateWithin = innerClass.symbol.privateWithin, - coord = innerClass.symbol.coord, - nestingLevel = innerClass.symbol.nestingLevel, - ).asType - List(newTrait, TypeDef(newTypeSym)) + case innerClass: TypeDef if innerClass.symbol.isClass => + val newTrait = makeTraitFromInnerClass(innerClass) + val newType = makeTypeFromInnerClass(inlineTrait.symbol, innerClass, newTrait.symbol) + List(newTrait, newType) case member: MemberDef => List(member) case _ => @@ -97,6 +83,28 @@ class Inlining extends MacroTransform, SymTransformer { val tmpl1 = cpy.Template(tmpl)(body = body1) cpy.TypeDef(inlineTrait)(rhs = tmpl1) + private def makeTraitFromInnerClass(innerClass: TypeDef)(using Context): TypeDef = + cpy.TypeDef(innerClass)(name = newInnerClassName(innerClass.name)) + + private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = + val upperBound = innerClass.symbol.primaryConstructor.info match { + case _: MethodType => + newTraitSym.typeRef + case poly: PolyType => + HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTraitSym.typeRef.appliedTo(tl.paramRefs.head)) + } + val newTypeSym = newSymbol( + owner = parentSym, + name = newTraitSym.name.asTypeName, + flags = innerClass.symbol.flags & (Private | Protected), + info = TypeBounds.upper(upperBound), + privateWithin = innerClass.symbol.privateWithin, + coord = innerClass.symbol.coord, + nestingLevel = innerClass.symbol.nestingLevel, + ).asType + TypeDef(newTypeSym) + end makeTypeFromInnerClass + private class InliningTreeMap extends TreeMapWithImplicits { /** List of top level classes added by macro annotation in a package object. From d8966886c19c9b4aa308e6e4627182471fef6f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 10:17:04 +0200 Subject: [PATCH 090/106] Strip parents of inner class from concrete elements When "transforming" an inner class into an inner trait, we need to adapt its parents as well. When declaring its parents, a trait cannot call their constructor, therefore the different Apply and other trees should be pruned from them. For example: ``` inline trait A: class InnerA extends Object ``` will result in the following code after Inlining: ``` inline trait A: trait InnerA$trait extends Object type InnerA <: InnerA$trait ``` The tree for `Object` in the first case is ``` Apply(Select(New(Ident(Object)),),List()) ``` but if we make InnerA a trait instead, it is simply ``` Ident(Object) ``` The goal is therefore to strip the different "concrete" elements of the parents when transforming a class into a trait. --- .../dotty/tools/dotc/transform/Inlining.scala | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 42cb2c93a2db..5262769e5fce 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -78,13 +78,18 @@ class Inlining extends MacroTransform, SymTransformer { case member: MemberDef => List(member) case _ => + // Remove non-memberdefs, as they are normally placed into $init() Nil } val tmpl1 = cpy.Template(tmpl)(body = body1) cpy.TypeDef(inlineTrait)(rhs = tmpl1) private def makeTraitFromInnerClass(innerClass: TypeDef)(using Context): TypeDef = - cpy.TypeDef(innerClass)(name = newInnerClassName(innerClass.name)) + val TypeDef(name, tmpl: Template) = innerClass: @unchecked + val newInnerParents = tmpl.parents.mapConserve(ConcreteParentStripper.apply) + val tmpl1 = cpy.Template(tmpl)(parents = newInnerParents) // TODO .withType(???) + cpy.TypeDef(innerClass)(name = newInnerClassName(name), rhs = tmpl1) + end makeTraitFromInnerClass private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = val upperBound = innerClass.symbol.primaryConstructor.info match { @@ -164,6 +169,16 @@ class Inlining extends MacroTransform, SymTransformer { } private def newInnerClassName(name: Name): name.ThisName = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX + + private object ConcreteParentStripper extends TreeAccumulator[Tree] { + def apply(tree: Tree)(using Context): Tree = apply(tree, tree) + + override def apply(x: Tree, tree: Tree)(using Context): Tree = tree match { + case ident: Ident => ident + case tpt: TypeTree => tpt + case _ => foldOver(x, tree) + } + } } object Inlining: From b1eec85277bb75da6858d21cc4a36c8227a38a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 10:34:00 +0200 Subject: [PATCH 091/106] Add Synthetic flag to new trait and type --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 5262769e5fce..1484a65a4d9e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -88,7 +88,9 @@ class Inlining extends MacroTransform, SymTransformer { val TypeDef(name, tmpl: Template) = innerClass: @unchecked val newInnerParents = tmpl.parents.mapConserve(ConcreteParentStripper.apply) val tmpl1 = cpy.Template(tmpl)(parents = newInnerParents) // TODO .withType(???) - cpy.TypeDef(innerClass)(name = newInnerClassName(name), rhs = tmpl1) + val newTrait = cpy.TypeDef(innerClass)(name = newInnerClassName(name), rhs = tmpl1) + newTrait.symbol.setFlag(Synthetic) + newTrait end makeTraitFromInnerClass private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = @@ -101,7 +103,7 @@ class Inlining extends MacroTransform, SymTransformer { val newTypeSym = newSymbol( owner = parentSym, name = newTraitSym.name.asTypeName, - flags = innerClass.symbol.flags & (Private | Protected), + flags = innerClass.symbol.flags & (Private | Protected) | Synthetic, info = TypeBounds.upper(upperBound), privateWithin = innerClass.symbol.privateWithin, coord = innerClass.symbol.coord, From e13d3c47ed2348312ae15b64489bba0a44cb6b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Thu, 1 Jun 2023 11:21:27 +0200 Subject: [PATCH 092/106] Adapt body of child to point to inlined members --- .../dotty/tools/dotc/inlines/Inlines.scala | 45 ++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 0c1dba7e053b..3209ce7ed240 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -215,12 +215,17 @@ object Inlines: cls match { case cls @ tpd.TypeDef(_, impl: Template) => val clsOverriddenSyms = cls.symbol.info.decls.toList.flatMap(_.allOverriddenSymbols).toSet - val inlineDefs = inlineTraitAncestors(cls).foldLeft(List.empty[Tree])( - (defs, parent) => - val overriddenSymbols = clsOverriddenSyms ++ defs.flatMap(_.symbol.allOverriddenSymbols) - defs ::: InlineParentTrait(parent)(using ctx.withOwner(cls.symbol)).expandDefs(overriddenSymbols) - ) - val impl1 = cpy.Template(impl)(body = inlineDefs ::: impl.body) + val newDefs = inContext(ctx.withOwner(cls.symbol)) { + inlineTraitAncestors(cls).foldLeft((List.empty[Tree], impl.body)){ + case ((inlineDefs, childDefs), parent) => + val parentTraitInliner = InlineParentTrait(parent) + val overriddenSymbols = clsOverriddenSyms ++ inlineDefs.flatMap(_.symbol.allOverriddenSymbols) + val inlinedDefs1 = inlineDefs ::: parentTraitInliner.expandDefs(overriddenSymbols) + val childDefs1 = parentTraitInliner.adaptDefs(childDefs) // TODO do this outside of inlining: we need to adapt ALL references to inlined stuff + (inlinedDefs1, childDefs1) + } + } + val impl1 = cpy.Template(impl)(body = newDefs._1 ::: newDefs._2) cpy.TypeDef(cls)(rhs = impl1) case _ => cls @@ -540,6 +545,8 @@ object Inlines: } end expandDefs + def adaptDefs(definitions: List[Tree]): List[Tree] = definitions.mapconserve(defsAdapter(_)) + protected class InlineTraitTypeMap extends InlinerTypeMap { override def apply(t: Type) = super.apply(t) match { case t: ThisType if t.cls == parentSym => childThisType @@ -698,6 +705,32 @@ object Inlines: // TODO make version of inlined that does not return bindings? Inlined(tpd.ref(parentSym), Nil, inlined(rhs)._2).withSpan(parent.span) + private val defsAdapter = + val typeMap = new DeepTypeMap { + override def apply(tp: Type): Type = tp match { + case TypeRef(_, sym: Symbol) if innerClassNewSyms.contains(sym) => + TypeRef(childThisType, innerClassNewSyms(sym)) + case _ => + mapOver(tp) + } + } + def treeMap(tree: Tree) = tree match { + case ident: Ident if innerClassNewSyms.contains(ident.symbol) => + Ident(innerClassNewSyms(ident.symbol).namedType) + case tdef: TypeDef if tdef.symbol.isClass => + tdef.symbol.info = typeMap(tdef.symbol.info) + tdef + case tree => + tree + } + new TreeTypeMap( + typeMap = typeMap, + treeMap = treeMap, + substFrom = substFrom, + substTo = substTo, + ) + end defsAdapter + private class ParamAccessorsMapper: private val paramAccessorsTrees: mutable.Map[Symbol, Map[Name, Tree]] = mutable.Map.empty private val paramAccessorsNewNames: mutable.Map[(Symbol, Name), Name] = mutable.Map.empty From 34a556787a97a980eef2532e0a5919a800872233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 5 Jun 2023 13:57:08 +0200 Subject: [PATCH 093/106] Use wildcard for default var value in benchmark --- .../inlinetraits/InlineTraitBenchmark.scala | 21 ++++++++++--------- .../benchmarks/inlinetraits/MatrixOps.scala | 6 ------ 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala index a86529216b22..16e97e0e1a70 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -13,23 +13,24 @@ import scala.util.Random @State(Scope.Benchmark) class InlineTraitBenchmark { // @Param(Array("100", "200", "300")) - var n: Int = 300 + var matrixSize: Int = 300 - def intMatrixElems(): Seq[Seq[Int]] = - Seq.tabulate(n, n)((_, _) => Random.nextInt()) + def intMatrixElems: List[List[Int]] = + List.tabulate(matrixSize, matrixSize)((_, _) => Random.nextInt()) @Param(Array("standard", "specialized", "inlinetrait")) - var matrixLibType: String = "" + var libType: String = _ - var m1 = BenchmarkMatrix.empty - var m2 = BenchmarkMatrix.empty + var m1: BenchmarkMatrix = _ + var m2: BenchmarkMatrix = _ @Setup(Level.Trial) def setup = { - Random.setSeed(n) - val matrixFactory = BenchmarkMatrix.ofType(matrixLibType) - m1 = matrixFactory(intMatrixElems()) - m2 = matrixFactory(intMatrixElems()) + Random.setSeed(matrixSize) + + val matrixFactory = BenchmarkMatrix.ofType(libType) + m1 = matrixFactory(intMatrixElems) + m2 = matrixFactory(intMatrixElems) } @Benchmark diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala index bed34099891c..434332a5d8b0 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala @@ -16,12 +16,6 @@ object BenchmarkMatrix: case "inlinetrait" => InlBenchmarkMatrix(InlIntMatrix(elems*)) } - val empty: BenchmarkMatrix = new BenchmarkMatrix { - override def +(n: BenchmarkMatrix): BenchmarkMatrix = ??? - override def *(n: BenchmarkMatrix): BenchmarkMatrix = ??? - } - - private class StdBenchmarkMatrix(val m: StdIntMatrix) extends BenchmarkMatrix: override def +(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m + stdN.m) From 5a0339b2de337f390d90a2a2989980c0e2ab2fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 5 Jun 2023 13:59:46 +0200 Subject: [PATCH 094/106] Add Pair to benchmark This way, we have a benchmark test that does not rely on inner classes --- .../inlinetraits/InlineTraitBenchmark.scala | 22 +++++++++++-- .../benchmarks/inlinetraits/PairOps.scala | 33 +++++++++++++++++++ .../inlinetraits/inlinetrait/Pair.scala | 10 ++++++ .../inlinetraits/specialized/Pair.scala | 8 +++++ .../inlinetraits/standard/Pair.scala | 5 +++ 5 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala index 16e97e0e1a70..818ce5122b91 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -5,25 +5,33 @@ import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} import scala.util.Random @BenchmarkMode(Array(Mode.AverageTime)) -@Fork(3) +@Fork(2) @Threads(3) @Warmup(iterations = 3, time = 3, timeUnit = SECONDS) @Measurement(iterations = 5, time = 5, timeUnit = SECONDS) @OutputTimeUnit(MILLISECONDS) @State(Scope.Benchmark) class InlineTraitBenchmark { - // @Param(Array("100", "200", "300")) var matrixSize: Int = 300 + var numPairs: Int = 3_000_000 + def intMatrixElems: List[List[Int]] = List.tabulate(matrixSize, matrixSize)((_, _) => Random.nextInt()) + def pairElems: List[(First, Second)] = List.tabulate(numPairs)(_ % 2 match { + case 0 => (Random.nextInt(), Random.nextDouble()) + case 1 => (Random.nextInt(Char.MaxValue).asInstanceOf[Char], Random.nextInt(Short.MaxValue).asInstanceOf[Short]) + }) + @Param(Array("standard", "specialized", "inlinetrait")) var libType: String = _ var m1: BenchmarkMatrix = _ var m2: BenchmarkMatrix = _ + var pairs: List[BenchmarkPair] = _ + @Setup(Level.Trial) def setup = { Random.setSeed(matrixSize) @@ -31,8 +39,18 @@ class InlineTraitBenchmark { val matrixFactory = BenchmarkMatrix.ofType(libType) m1 = matrixFactory(intMatrixElems) m2 = matrixFactory(intMatrixElems) + + val pairFactory = BenchmarkPair.ofType(libType) + pairs = pairElems.map((_1, _2) => pairFactory(_1, _2)) } @Benchmark def matrixBenchmark = (m1 + m2) * m1 // O(n^3) loops + + @Benchmark + def pairsBenchmark = pairs.foldLeft(0){ case (sum, pair) => pair match { + case BenchmarkPair(i: Int, d: Double) => 7 * i + 3 * d.toInt + sum + case BenchmarkPair(c: Char, s: Short) => 5 * c + 2 * s + sum + } + } } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala new file mode 100644 index 000000000000..d621e0060705 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala @@ -0,0 +1,33 @@ +package dotty.tools.benchmarks.inlinetraits + +import standard.{Pair => StdPair} +import specialized.{Pair => SpePair} +import inlinetrait.{ + Pair => InlPair, + IntDoublePair => IDPair, + CharShortPair => CSPair, +} + +type First = Int | Char +type Second = Double | Short + +class BenchmarkPair private (val _1: First, val _2: Second): + def this(p: StdPair[First, Second]) = this(p._1, p._2) + def this(p: SpePair[First, Second]) = this(p._1, p._2) + def this(p: InlPair[First, Second]) = this(p._1, p._2) + +object BenchmarkPair: + def ofType(tpe: String): (First, Second) => BenchmarkPair = + (_1: First, _2: Second) => tpe.toLowerCase() match { + case "standard" => BenchmarkPair(StdPair(_1, _2)) + case "specialized" => BenchmarkPair(SpePair(_1, _2)) + case "inlinetrait" => + val concretePair: InlPair[First, Second] = (_1, _2) match { + case (_1: Int, _2: Double) => IDPair(_1, _2) + case (_1: Char, _2: Short) => CSPair(_1, _2) + case _ => ??? + } + BenchmarkPair(concretePair) + } + + def unapply(p: BenchmarkPair): Option[(First, Second)] = Some(p._1, p._2) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala new file mode 100644 index 000000000000..9789df669ca1 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala @@ -0,0 +1,10 @@ +package dotty.tools.benchmarks.inlinetraits +package inlinetrait + +inline trait Pair[+T1, +T2]: + val _1: T1 + val _2: T2 + final def toTuple: (T1, T2) = (_1, _2) + +case class IntDoublePair(_1: Int, _2: Double) extends Pair[Int, Double] +case class CharShortPair(_1: Char, _2: Short) extends Pair[Char, Short] \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala new file mode 100644 index 000000000000..3c1c4bca5e82 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala @@ -0,0 +1,8 @@ +package dotty.tools.benchmarks.inlinetraits +package specialized + +import scala.specialized + +case class Pair[@specialized(Int, Char) +T1, @specialized(Double, Short) +T2](_1: T1, _2: T2) { + final def toTuple: (T1, T2) = (_1, _2) +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala new file mode 100644 index 000000000000..32d1fc0d8db4 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala @@ -0,0 +1,5 @@ +package dotty.tools.benchmarks.inlinetraits +package standard + +case class Pair[+T1, +T2](_1: T1, _2: T2): + final def toTuple: (T1, T2) = (_1, _2) From b1e16ab4289653f5e149fef3cc778f534a57b158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 6 Jun 2023 11:07:25 +0200 Subject: [PATCH 095/106] Manual specialization, remove unused toTuple --- .../inlinetraits/InlineTraitBenchmark.scala | 4 ++-- .../benchmarks/inlinetraits/PairOps.scala | 8 ++++++- .../inlinetraits/inlinetrait/Pair.scala | 5 ++-- .../inlinetraits/specialized/Pair.scala | 24 ++++++++++++++++--- .../inlinetraits/standard/Pair.scala | 3 +-- 5 files changed, 33 insertions(+), 11 deletions(-) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala index 818ce5122b91..811bb2d781d4 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala @@ -40,8 +40,8 @@ class InlineTraitBenchmark { m1 = matrixFactory(intMatrixElems) m2 = matrixFactory(intMatrixElems) - val pairFactory = BenchmarkPair.ofType(libType) - pairs = pairElems.map((_1, _2) => pairFactory(_1, _2)) + val pairFactory = (l: List[(First, Second)]) => l.map((_1, _2) => BenchmarkPair.ofType(libType)(_1, _2)) + pairs = pairFactory(pairElems) } @Benchmark diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala index d621e0060705..90c74d7da17c 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala @@ -20,7 +20,13 @@ object BenchmarkPair: def ofType(tpe: String): (First, Second) => BenchmarkPair = (_1: First, _2: Second) => tpe.toLowerCase() match { case "standard" => BenchmarkPair(StdPair(_1, _2)) - case "specialized" => BenchmarkPair(SpePair(_1, _2)) + case "specialized" => + val concretePair: SpePair[First, Second] = (_1, _2) match { + case (_1: Int, _2: Double) => SpePair(_1, _2) + case (_1: Char, _2: Short) => SpePair(_1, _2) + case _ => ??? + } + BenchmarkPair(concretePair) case "inlinetrait" => val concretePair: InlPair[First, Second] = (_1, _2) match { case (_1: Int, _2: Double) => IDPair(_1, _2) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala index 9789df669ca1..1fc84826d21a 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala @@ -4,7 +4,6 @@ package inlinetrait inline trait Pair[+T1, +T2]: val _1: T1 val _2: T2 - final def toTuple: (T1, T2) = (_1, _2) -case class IntDoublePair(_1: Int, _2: Double) extends Pair[Int, Double] -case class CharShortPair(_1: Char, _2: Short) extends Pair[Char, Short] \ No newline at end of file +class IntDoublePair(val _1: Int, val _2: Double) extends Pair[Int, Double] +class CharShortPair(val _1: Char, val _2: Short) extends Pair[Char, Short] \ No newline at end of file diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala index 3c1c4bca5e82..2eb3fc149180 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/specialized/Pair.scala @@ -1,8 +1,26 @@ package dotty.tools.benchmarks.inlinetraits package specialized -import scala.specialized +/* + * This implementation relies on the @specialized tag to specialize Pair. + * However, @specialized does nothing in Scala 3. Therefore, an equivalent version is + * recreated by hand further down, and the actual Scala 2 code is provided below: + * + * import scala.specialized + * + * class Pair[@specialized(Int, Char) +T1, @specialized(Double, Short) +T2](_1: T1, _2: T2) {} + */ -case class Pair[@specialized(Int, Char) +T1, @specialized(Double, Short) +T2](_1: T1, _2: T2) { - final def toTuple: (T1, T2) = (_1, _2) +class Pair[+T1, +T2] protected (val _1: T1, val _2: T2) + +object Pair { + def apply(_1: Int, _2: Double): Pair[Int, Double] = new Pair$mcID$sp(_1, _2) + def apply(_1: Int, _2: Short): Pair[Int, Short] = new Pair$mcIS$sp(_1, _2) + def apply(_1: Char, _2: Double): Pair[Char, Double] = new Pair$mcCD$sp(_1, _2) + def apply(_1: Char, _2: Short): Pair[Char, Short] = new Pair$mcCS$sp(_1, _2) } + +private class Pair$mcID$sp(protected[this] val _1$mcI$sp: Int, protected[this] val _2$mcD$sp: Double) extends Pair[Int, Double](_1$mcI$sp, _2$mcD$sp) +private class Pair$mcIS$sp(protected[this] val _1$mcI$sp: Int, protected[this] val _2$mcS$sp: Short) extends Pair[Int, Short](_1$mcI$sp, _2$mcS$sp) +private class Pair$mcCD$sp(protected[this] val _1$mcC$sp: Char, protected[this] val _2$mcD$sp: Double) extends Pair[Char, Double](_1$mcC$sp, _2$mcD$sp) +private class Pair$mcCS$sp(protected[this] val _1$mcC$sp: Char, protected[this] val _2$mcS$sp: Short) extends Pair[Char, Short](_1$mcC$sp, _2$mcS$sp) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala index 32d1fc0d8db4..b5cd0a684cb4 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/standard/Pair.scala @@ -1,5 +1,4 @@ package dotty.tools.benchmarks.inlinetraits package standard -case class Pair[+T1, +T2](_1: T1, _2: T2): - final def toTuple: (T1, T2) = (_1, _2) +class Pair[+T1, +T2](val _1: T1, val _2: T2) From cb95383d7ec701326cdd74111e8d96d09d2bae0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Wed, 14 Jun 2023 20:02:00 +0200 Subject: [PATCH 096/106] Add boxing benchmark for report --- .../benchmarks/inlinetraits/Boxing.scala | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala new file mode 100644 index 000000000000..3bc226d605e4 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala @@ -0,0 +1,27 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} +import scala.util.Random + +// @BenchmarkMode(Array(Mode.SampleTime)) +@Fork(3) +@Threads(3) +@Warmup(iterations = 5, time = 10, timeUnit = SECONDS) +@Measurement(iterations = 10, time = 10, timeUnit = SECONDS) +// @OutputTimeUnit(MILLISECONDS) +@State(Scope.Benchmark) +class Boxing { + class Wrapper[T](val x: T) + class IntWrapper(val x: Int) + + val numbers = 1 to 1000000 + val ws = numbers.map(Wrapper(_)) + val iws = numbers.map(IntWrapper(_)) + + @Benchmark + def noUnboxing: Int = iws.map(w => IntWrapper(w.x*w.x + 2*w.x)).foldLeft(0)(_ + _.x) + + @Benchmark + def withUnboxing: Int = ws.map(w => Wrapper(w.x*w.x + 2*w.x)).foldLeft(0)(_ + _.x) +} From 2784d54abebe8a0f2ed0927c0d3f660674803669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 5 Jun 2023 17:36:04 +0200 Subject: [PATCH 097/106] Move inline trait inlining to its own phase --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../src/dotty/tools/dotc/core/Phases.scala | 6 +- .../dotty/tools/dotc/inlines/Inlines.scala | 1 + .../dotty/tools/dotc/transform/Inlining.scala | 76 +---------- .../transform/SpecializeInlineTraits.scala | 128 ++++++++++++++++++ 5 files changed, 136 insertions(+), 76 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 3e54ebb646c9..daa1b739b146 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -48,6 +48,7 @@ class Compiler { /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info + List(new SpecializeInlineTraits) :: // Inline the code of inline traits into their children List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code List(new CheckUnused.PostInlining) :: // Check for unused elements diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 3c4c45ab254a..ec01f6413a07 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -210,6 +210,7 @@ object Phases { private var myPostTyperPhase: Phase = _ private var mySbtExtractDependenciesPhase: Phase = _ private var myPicklerPhase: Phase = _ + private var mySpecializeInlineTraitsPhase: Phase = _ private var myInliningPhase: Phase = _ private var myStagingPhase: Phase = _ private var mySplicingPhase: Phase = _ @@ -235,6 +236,7 @@ object Phases { final def postTyperPhase: Phase = myPostTyperPhase final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase final def picklerPhase: Phase = myPicklerPhase + final def specializeInlineTraitsPhase: Phase = mySpecializeInlineTraitsPhase final def inliningPhase: Phase = myInliningPhase final def stagingPhase: Phase = myStagingPhase final def splicingPhase: Phase = mySplicingPhase @@ -263,6 +265,7 @@ object Phases { myPostTyperPhase = phaseOfClass(classOf[PostTyper]) mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies]) myPicklerPhase = phaseOfClass(classOf[Pickler]) + mySpecializeInlineTraitsPhase = phaseOfClass(classOf[SpecializeInlineTraits]) myInliningPhase = phaseOfClass(classOf[Inlining]) myStagingPhase = phaseOfClass(classOf[Staging]) mySplicingPhase = phaseOfClass(classOf[Splicing]) @@ -451,8 +454,9 @@ object Phases { def postTyperPhase(using Context): Phase = ctx.base.postTyperPhase def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase def picklerPhase(using Context): Phase = ctx.base.picklerPhase + def specializeInlineTraitsPhase(using Context): Phase = ctx.base.specializeInlineTraitsPhase def inliningPhase(using Context): Phase = ctx.base.inliningPhase - def stagingPhase(using Context): Phase = ctx.base.stagingPhase + def stagingPhase(using Context): Phase = ctx.base.stagingPhase def splicingPhase(using Context): Phase = ctx.base.splicingPhase def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 3209ce7ed240..2a3ce4a63b6d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -72,6 +72,7 @@ object Inlines: && ( ctx.phase == Phases.inliningPhase || (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree)) + || (ctx.phase == Phases.specializeInlineTraitsPhase && !tree.symbol.is(Macro)) ) && !ctx.typer.hasInliningErrors && !ctx.base.stopInlining diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 1484a65a4d9e..8a4155175f71 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -11,17 +11,12 @@ import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits -import dotty.tools.dotc.core.DenotTransformers.SymTransformer import dotty.tools.dotc.staging.StagingLevel -import dotty.tools.dotc.core.SymDenotations.SymDenotation -import dotty.tools.dotc.core.StdNames.str -import dotty.tools.dotc.core.Types.* -import dotty.tools.dotc.core.Names.Name import scala.collection.mutable.ListBuffer /** Inlines all calls to inline methods that are not in an inline method or a quote */ -class Inlining extends MacroTransform, SymTransformer { +class Inlining extends MacroTransform { import tpd._ @@ -62,56 +57,6 @@ class Inlining extends MacroTransform, SymTransformer { new InliningTreeMap().transform(tree) } - override def transformSym(symd: SymDenotation)(using Context): SymDenotation = - if symd.isClass && symd.owner.isInlineTrait && !symd.is(Module) then - symd.copySymDenotation(name = newInnerClassName(symd.name), initFlags = (symd.flags &~ Final) | Trait) - else - symd - - def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = - val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked - val body1 = tmpl.body.flatMap { - case innerClass: TypeDef if innerClass.symbol.isClass => - val newTrait = makeTraitFromInnerClass(innerClass) - val newType = makeTypeFromInnerClass(inlineTrait.symbol, innerClass, newTrait.symbol) - List(newTrait, newType) - case member: MemberDef => - List(member) - case _ => - // Remove non-memberdefs, as they are normally placed into $init() - Nil - } - val tmpl1 = cpy.Template(tmpl)(body = body1) - cpy.TypeDef(inlineTrait)(rhs = tmpl1) - - private def makeTraitFromInnerClass(innerClass: TypeDef)(using Context): TypeDef = - val TypeDef(name, tmpl: Template) = innerClass: @unchecked - val newInnerParents = tmpl.parents.mapConserve(ConcreteParentStripper.apply) - val tmpl1 = cpy.Template(tmpl)(parents = newInnerParents) // TODO .withType(???) - val newTrait = cpy.TypeDef(innerClass)(name = newInnerClassName(name), rhs = tmpl1) - newTrait.symbol.setFlag(Synthetic) - newTrait - end makeTraitFromInnerClass - - private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = - val upperBound = innerClass.symbol.primaryConstructor.info match { - case _: MethodType => - newTraitSym.typeRef - case poly: PolyType => - HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTraitSym.typeRef.appliedTo(tl.paramRefs.head)) - } - val newTypeSym = newSymbol( - owner = parentSym, - name = newTraitSym.name.asTypeName, - flags = innerClass.symbol.flags & (Private | Protected) | Synthetic, - info = TypeBounds.upper(upperBound), - privateWithin = innerClass.symbol.privateWithin, - coord = innerClass.symbol.coord, - nestingLevel = innerClass.symbol.nestingLevel, - ).asType - TypeDef(newTypeSym) - end makeTypeFromInnerClass - private class InliningTreeMap extends TreeMapWithImplicits { /** List of top level classes added by macro annotation in a package object. @@ -121,13 +66,6 @@ class Inlining extends MacroTransform, SymTransformer { override def transform(tree: Tree)(using Context): Tree = { tree match - case tree: TypeDef if tree.symbol.isInlineTrait => - transformInlineTrait(tree) - case tree: TypeDef if Inlines.needsInlining(tree) => - val tree1 = super.transform(tree).asInstanceOf[TypeDef] - if tree1.tpe.isError then tree1 - else if tree1.symbol.isInlineTrait then transformInlineTrait(tree1) - else Inlines.inlineParentInlineTraits(tree1) case tree: MemberDef => if tree.symbol.is(Inline) then tree else if tree.symbol.is(Param) then super.transform(tree) @@ -169,18 +107,6 @@ class Inlining extends MacroTransform, SymTransformer { else super.transform(tree) } } - - private def newInnerClassName(name: Name): name.ThisName = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX - - private object ConcreteParentStripper extends TreeAccumulator[Tree] { - def apply(tree: Tree)(using Context): Tree = apply(tree, tree) - - override def apply(x: Tree, tree: Tree)(using Context): Tree = tree match { - case ident: Ident => ident - case tpt: TypeTree => tpt - case _ => foldOver(x, tree) - } - } } object Inlining: diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala new file mode 100644 index 000000000000..a70c5865adb9 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala @@ -0,0 +1,128 @@ +package dotty.tools.dotc +package transform + +import core._ +import Flags._ +import Contexts._ +import Symbols._ +import SymUtils._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.quoted._ +import dotty.tools.dotc.inlines.Inlines +import dotty.tools.dotc.ast.TreeMapWithImplicits +import dotty.tools.dotc.core.DenotTransformers.SymTransformer +import dotty.tools.dotc.staging.StagingLevel +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.StdNames.{str, nme} +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.core.Names.{Name, TermName} + +import scala.collection.mutable.ListBuffer + +class SpecializeInlineTraits extends MacroTransform, SymTransformer { + + import tpd._ + + override def phaseName: String = SpecializeInlineTraits.name + + override def description: String = SpecializeInlineTraits.description + + override def changesMembers: Boolean = true + + override def changesParents: Boolean = true + + override def run(using Context): Unit = + try super.run + catch case _: CompilationUnit.SuspendException => () + + override def newTransformer(using Context): Transformer = new Transformer { + override def transform(tree: Tree)(using Context): Tree = tree match { + case tree: TypeDef if tree.symbol.isInlineTrait => + transformInlineTrait(tree) + case tree: TypeDef if Inlines.needsInlining(tree) => + val tree1 = super.transform(tree).asInstanceOf[TypeDef] + if tree1.tpe.isError then tree1 + else if tree1.symbol.isInlineTrait then transformInlineTrait(tree1) + else Inlines.inlineParentInlineTraits(tree1) + case _ => super.transform(tree) + } + } + + override def transformSym(symd: SymDenotation)(using Context): SymDenotation = + if symd.isClass && symd.owner.isInlineTrait && !symd.is(Module) then + symd.copySymDenotation(name = SpecializeInlineTraits.newInnerClassName(symd.name), initFlags = (symd.flags &~ Final) | Trait) + else + symd + + override def checkPostCondition(tree: Tree)(using Context): Unit = + tree match { + // TODO + case _ => + } + + extension (tree: Tree) + private def hasSpecializedVersion: Boolean = + false + + private def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = + val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked + val body1 = tmpl.body.flatMap { + case innerClass: TypeDef if innerClass.symbol.isClass => + val newTrait = makeTraitFromInnerClass(innerClass) + val newType = makeTypeFromInnerClass(inlineTrait.symbol, innerClass, newTrait.symbol) + List(newTrait, newType) + case member: MemberDef => + List(member) + case _ => + // Remove non-memberdefs, as they are normally placed into $init() + Nil + } + val tmpl1 = cpy.Template(tmpl)(body = body1) + cpy.TypeDef(inlineTrait)(rhs = tmpl1) + end transformInlineTrait + + private def makeTraitFromInnerClass(innerClass: TypeDef)(using Context): TypeDef = + val TypeDef(name, tmpl: Template) = innerClass: @unchecked + val newInnerParents = tmpl.parents.mapConserve(ConcreteParentStripper.apply) + val tmpl1 = cpy.Template(tmpl)(parents = newInnerParents) // TODO .withType(???) + val newTrait = cpy.TypeDef(innerClass)(name = SpecializeInlineTraits.newInnerClassName(name), rhs = tmpl1) + newTrait.symbol.setFlag(Synthetic) + newTrait + end makeTraitFromInnerClass + + private def makeTypeFromInnerClass(parentSym: Symbol, innerClass: TypeDef, newTraitSym: Symbol)(using Context): TypeDef = + val upperBound = innerClass.symbol.primaryConstructor.info match { + case _: MethodType => + newTraitSym.typeRef + case poly: PolyType => + HKTypeLambda(poly.paramNames)(tl => poly.paramInfos, tl => newTraitSym.typeRef.appliedTo(tl.paramRefs.head)) + } + val newTypeSym = newSymbol( + owner = parentSym, + name = newTraitSym.name.asTypeName, + flags = innerClass.symbol.flags & (Private | Protected) | Synthetic, + info = TypeBounds.upper(upperBound), + privateWithin = innerClass.symbol.privateWithin, + coord = innerClass.symbol.coord, + nestingLevel = innerClass.symbol.nestingLevel, + ).asType + TypeDef(newTypeSym) + end makeTypeFromInnerClass + + private object ConcreteParentStripper extends TreeAccumulator[Tree] { + def apply(tree: Tree)(using Context): Tree = apply(tree, tree) + + override def apply(x: Tree, tree: Tree)(using Context): Tree = tree match { + case ident: Ident => ident + case tpt: TypeTree => tpt + case _ => foldOver(x, tree) + } + } +} + +object SpecializeInlineTraits: + val name: String = "specializeInlineTraits" + val description: String = "inline the code of inline traits and specialize calls to their members" + + private[transform] def newInnerClassName(name: Name): name.ThisName = name ++ str.INLINE_TRAIT_INNER_CLASS_SUFFIX From 5c82ee0cef8d85001092d87e6cb063d5f9dd1adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 16 Jun 2023 15:05:39 +0200 Subject: [PATCH 098/106] Add simple test for inner class --- tests/pos/inline-trait-body-class-simple.scala | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/pos/inline-trait-body-class-simple.scala diff --git a/tests/pos/inline-trait-body-class-simple.scala b/tests/pos/inline-trait-body-class-simple.scala new file mode 100644 index 000000000000..0f126d75409c --- /dev/null +++ b/tests/pos/inline-trait-body-class-simple.scala @@ -0,0 +1,6 @@ +inline trait A[T]: + class InnerA: + def x: T = ??? + +class B extends A[Int]: + class InnerB extends InnerA \ No newline at end of file From 6e8a4891eec1ca6fd44f15d3f305ef7c4bca3fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 08:59:05 +0200 Subject: [PATCH 099/106] Rework benchmark files for report --- .../{Boxing.scala => BoxingBenchmark.scala} | 0 .../inlinetraits/MatrixBenchmark.scala | 35 +++++++++++++++ .../benchmarks/inlinetraits/MatrixOps.scala | 9 ++-- ...itBenchmark.scala => PairsBenchmark.scala} | 31 +++---------- .../{PairOps.scala => PairsOps.scala} | 0 .../inlinetraits/inlinetrait/Matrix.scala | 43 ++++++++----------- .../inlinetraits/inlinetrait/Pair.scala | 8 ++-- 7 files changed, 68 insertions(+), 58 deletions(-) rename bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/{Boxing.scala => BoxingBenchmark.scala} (100%) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala rename bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/{InlineTraitBenchmark.scala => PairsBenchmark.scala} (55%) rename bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/{PairOps.scala => PairsOps.scala} (100%) diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/BoxingBenchmark.scala similarity index 100% rename from bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/Boxing.scala rename to bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/BoxingBenchmark.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala new file mode 100644 index 000000000000..4d34d69fd175 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixBenchmark.scala @@ -0,0 +1,35 @@ +package dotty.tools.benchmarks.inlinetraits + +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit.SECONDS +import scala.util.Random + +@Fork(10) +@Threads(3) +@Warmup(iterations = 3, time = 5, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 10, timeUnit = SECONDS) +@State(Scope.Benchmark) +class MatrixBenchmark { + val n: Int = 100 + + def intMatrixElems: List[List[Int]] = + List.tabulate(n, n)((_, _) => Random.nextInt()) + + @Param(Array("standard", "specialized", "inlinetrait")) + var libType: String = _ + + var m1: BenchmarkMatrix = _ + var m2: BenchmarkMatrix = _ + + @Setup(Level.Trial) + def setup = { + Random.setSeed(n) + + val matrixFactory = BenchmarkMatrix.ofType(libType) + m1 = matrixFactory(intMatrixElems) + m2 = matrixFactory(intMatrixElems) + } + + @Benchmark + def matrixBenchmark = (m1 + m2) * m1 // O(n^3) loops +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala index 434332a5d8b0..e316bf05b161 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/MatrixOps.scala @@ -17,6 +17,7 @@ object BenchmarkMatrix: } private class StdBenchmarkMatrix(val m: StdIntMatrix) extends BenchmarkMatrix: + import standard.IntMatrixLib.{+, `*`} override def +(n: BenchmarkMatrix): StdBenchmarkMatrix = n match { case stdN: StdBenchmarkMatrix => StdBenchmarkMatrix(this.m + stdN.m) } @@ -25,16 +26,16 @@ private class StdBenchmarkMatrix(val m: StdIntMatrix) extends BenchmarkMatrix: } private class SpeBenchmarkMatrix(val m: SpeIntMatrix) extends BenchmarkMatrix: - import specialized.IntMatrixLib - + import specialized.IntMatrixLib.{+ => plus, `*` => times} override def +(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { - case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(IntMatrixLib.+(this.m)(speN.m)) + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(plus(this.m)(speN.m)) } override def *(n: BenchmarkMatrix): SpeBenchmarkMatrix = n match { - case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(IntMatrixLib.*(this.m)(speN.m)) + case speN: SpeBenchmarkMatrix => SpeBenchmarkMatrix(times(this.m)(speN.m)) } private class InlBenchmarkMatrix(val m: InlIntMatrix) extends BenchmarkMatrix: + import inlinetrait.IntMatrixLib.{+, `*`} override def +(n: BenchmarkMatrix): InlBenchmarkMatrix = n match { case inlN: InlBenchmarkMatrix => InlBenchmarkMatrix(this.m + inlN.m) } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala similarity index 55% rename from bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala rename to bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala index 811bb2d781d4..197b7cf714ab 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/InlineTraitBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsBenchmark.scala @@ -1,23 +1,16 @@ package dotty.tools.benchmarks.inlinetraits import org.openjdk.jmh.annotations._ -import java.util.concurrent.TimeUnit.{SECONDS, MILLISECONDS} +import java.util.concurrent.TimeUnit.SECONDS import scala.util.Random -@BenchmarkMode(Array(Mode.AverageTime)) -@Fork(2) +@Fork(10) @Threads(3) -@Warmup(iterations = 3, time = 3, timeUnit = SECONDS) -@Measurement(iterations = 5, time = 5, timeUnit = SECONDS) -@OutputTimeUnit(MILLISECONDS) +@Warmup(iterations = 3, time = 5, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 10, timeUnit = SECONDS) @State(Scope.Benchmark) -class InlineTraitBenchmark { - var matrixSize: Int = 300 - - var numPairs: Int = 3_000_000 - - def intMatrixElems: List[List[Int]] = - List.tabulate(matrixSize, matrixSize)((_, _) => Random.nextInt()) +class PairsBenchmark { + val numPairs: Int = 3_000_000 def pairElems: List[(First, Second)] = List.tabulate(numPairs)(_ % 2 match { case 0 => (Random.nextInt(), Random.nextDouble()) @@ -27,26 +20,16 @@ class InlineTraitBenchmark { @Param(Array("standard", "specialized", "inlinetrait")) var libType: String = _ - var m1: BenchmarkMatrix = _ - var m2: BenchmarkMatrix = _ - var pairs: List[BenchmarkPair] = _ @Setup(Level.Trial) def setup = { - Random.setSeed(matrixSize) - - val matrixFactory = BenchmarkMatrix.ofType(libType) - m1 = matrixFactory(intMatrixElems) - m2 = matrixFactory(intMatrixElems) + Random.setSeed(numPairs) val pairFactory = (l: List[(First, Second)]) => l.map((_1, _2) => BenchmarkPair.ofType(libType)(_1, _2)) pairs = pairFactory(pairElems) } - @Benchmark - def matrixBenchmark = (m1 + m2) * m1 // O(n^3) loops - @Benchmark def pairsBenchmark = pairs.foldLeft(0){ case (sum, pair) => pair match { case BenchmarkPair(i: Int, d: Double) => 7 * i + 3 * d.toInt + sum diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsOps.scala similarity index 100% rename from bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairOps.scala rename to bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/PairsOps.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala index 3a265b76733a..9653c65d2bac 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Matrix.scala @@ -3,35 +3,28 @@ package inlinetrait import scala.reflect.ClassTag -// FIXME uncomment the following code when inline traits work -// inline trait MatrixLib[T: ClassTag]: -// opaque type Matrix = Array[Array[T]] - -// object Matrix: -// def apply(rows: Seq[T]*): Matrix = -// rows.map(_.toArray).toArray - -// extension (m: Matrix) -// def apply(x: Int)(y: Int): T = m(x)(y) -// def rows: Int = m.length -// def cols: Int = m(0).length - -object IntMatrixLib /*extends MatrixLib[Int]*/: - // FIXME remove manually "generated" code below and replace `IntMatrix` with `Matrix` when inline traits work properly +inline trait MatrixLib[T: ClassTag]: + // FIXME make Matrix opaque once inlinedTypeRef works properly + /*opaque*/ type Matrix = Array[Array[T]] + + // FIXME uncomment object and remove following code when inner objects work + /* + * object Matrix: + * def apply(rows: Seq[T]*): Matrix = + * rows.map(_.toArray).toArray + */ // ---------------------------------- - opaque type Matrix = Array[Array[Int]] - - object Matrix: - def apply(rows: Seq[Int]*): Matrix = - rows.map(_.toArray).toArray - - extension (m: Matrix) - /*override*/ def apply(x: Int)(y: Int): Int = m(x)(y) - /*override*/ def rows: Int = m.length - /*override*/ def cols: Int = m(0).length + def Matrix(rows: Seq[T]*): Matrix = + rows.map(_.toArray).toArray // ---------------------------------- // end of code to remove + extension (m: Matrix) + def apply(x: Int)(y: Int): T = m(x)(y) + def rows: Int = m.length + def cols: Int = m(0).length + +object IntMatrixLib extends MatrixLib[Int]: extension (m: Matrix) def +(n: Matrix): Matrix = val sum = diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala index 1fc84826d21a..996e38fb293f 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/inlinetraits/inlinetrait/Pair.scala @@ -1,9 +1,7 @@ package dotty.tools.benchmarks.inlinetraits package inlinetrait -inline trait Pair[+T1, +T2]: - val _1: T1 - val _2: T2 +inline trait Pair[+T1, +T2](val _1: T1, val _2: T2) -class IntDoublePair(val _1: Int, val _2: Double) extends Pair[Int, Double] -class CharShortPair(val _1: Char, val _2: Short) extends Pair[Char, Short] \ No newline at end of file +class IntDoublePair(override val _1: Int, override val _2: Double) extends Pair[Int, Double](_1, _2) +class CharShortPair(override val _1: Char, override val _2: Short) extends Pair[Char, Short](_1, _2) \ No newline at end of file From 7c82e3d59abd756cc8f8d0a5fc92549e2ae48093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 09:00:23 +0200 Subject: [PATCH 100/106] Remove target name for inlined inner class --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 2a3ce4a63b6d..4313d643438d 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -629,7 +629,6 @@ object Inlines: sym.privateWithin, spanCoord(parent.span) ) - inlinedSym.setTargetName(sym.name ++ str.NAME_JOIN ++ ctx.owner.name) innerClassNewSyms.put(sym, inlinedSym) inlinedSym.entered case _ => From fd737ea738e63589abb6e9950f2d29c0d4dfec28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 09:33:04 +0200 Subject: [PATCH 101/106] Restrict non-local private fields Non-local private fields can be accessed by other instances of the class. However, this creates the following issue: ``` inline trait A: private val x = 1 def eq(o: A) = o.x == x class B extends A ``` In the code above, when we inline the code into B, `o.x` accesses a private field of A from B, which is not allowed. To allow this, we'd need to either make x protected, or change the signature of `eq` to take a B instead. --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 10 +++++----- .../src/dotty/tools/dotc/transform/PostTyper.scala | 4 ++++ .../neg/inline-trait-body-private-name-collision.scala | 3 +++ 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 tests/neg/inline-trait-body-private-name-collision.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 4313d643438d..907eb452dcea 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -639,11 +639,11 @@ object Inlines: private def inlinedMemberSym(sym: Symbol, withoutFlags: FlagSet = EmptyFlags)(using Context): Symbol = var name = sym.name var flags = sym.flags | Synthetic - if sym.isType || !sym.is(Private) then flags |= Override - if sym.isTermParamAccessor then - flags &~= ParamAccessor - if sym.is(Local) && sym.owner.isInlineTrait then - name = paramAccessorsMapper.registerNewName(sym) + if sym.isTermParamAccessor then flags &~= ParamAccessor + if sym.is(Local) then + name = paramAccessorsMapper.registerNewName(sym) + else + flags |= Override sym.copy( owner = ctx.owner, name = name, diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 0a3a9b498568..0b8bc405f127 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -155,6 +155,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree match case tree: ValOrDefDef if !sym.is(Synthetic) => checkInferredWellFormed(tree.tpt) + if tree.symbol.owner.isInlineTrait then checkInlTraitPrivateMemberIsLocal(tree) if sym.is(Method) then if sym.isSetter then sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) @@ -192,6 +193,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase => Checking.checkAppliedTypesIn(tree) case _ => + private def checkInlTraitPrivateMemberIsLocal(tree: Tree)(using Context): Unit = + if tree.symbol.owner.isInlineTrait && tree.symbol.isAllOf(Private, butNot = Local) then + report.error(em"implementation restriction: inline traits cannot have non-local private members", tree.srcPos) private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = { val qual = tree.qualifier diff --git a/tests/neg/inline-trait-body-private-name-collision.scala b/tests/neg/inline-trait-body-private-name-collision.scala new file mode 100644 index 000000000000..1e3d14480d82 --- /dev/null +++ b/tests/neg/inline-trait-body-private-name-collision.scala @@ -0,0 +1,3 @@ +inline trait A: + private val x: Int = 1 // error + def eq(o: A) = o.x == x From 7e2952c5da92038867bda9f417fbd9389edb89c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 09:57:03 +0200 Subject: [PATCH 102/106] Forbid user from overriding vars The current check allowed the user to override vars, we don't want that Only the compiler should be allowed to do this, and for inline traits only --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 3 ++- tests/neg/inline-trait-body-override-var.scala | 5 +++++ tests/run/inline-trait-body-override-var.check | 1 - tests/run/inline-trait-body-override-var.scala | 9 --------- tests/run/inline-trait-body-var.check | 4 ++++ tests/{pos => run}/inline-trait-body-var.scala | 7 +++++++ 6 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 tests/neg/inline-trait-body-override-var.scala delete mode 100644 tests/run/inline-trait-body-override-var.check delete mode 100644 tests/run/inline-trait-body-override-var.scala create mode 100644 tests/run/inline-trait-body-var.check rename tests/{pos => run}/inline-trait-body-var.scala (50%) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index d143aee9942d..1809b6180d07 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -341,6 +341,7 @@ object RefChecks { */ def checkOverride(checkSubType: (Type, Type) => Context ?=> Boolean, member: Symbol, other: Symbol): Unit = def overridesInlineTraitMember = other.owner.ownersIterator.exists(_.isInlineTrait) && member.is(Synthetic) + def isInlinedFromInlineTrait = other.owner.isAllOf(InlineTrait) && member.is(Synthetic) def memberTp(self: Type) = if (member.isClass) TypeAlias(member.typeRef.EtaExpand(member.typeParams)) @@ -505,7 +506,7 @@ object RefChecks { overrideError("needs `override` modifier") else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) overrideError("needs `abstract override` modifiers") - else if member.is(Override) && other.is(Mutable) && !other.owner.isAllOf(InlineTrait) then + else if member.is(Override) && other.is(Mutable) && !isInlinedFromInlineTrait then overrideError("cannot override a mutable variable") else if (member.isAnyOverride && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && diff --git a/tests/neg/inline-trait-body-override-var.scala b/tests/neg/inline-trait-body-override-var.scala new file mode 100644 index 000000000000..4ad8b79db477 --- /dev/null +++ b/tests/neg/inline-trait-body-override-var.scala @@ -0,0 +1,5 @@ +inline trait A: + var x: Int = 1 + +class B extends A: + override var x = 2 // error diff --git a/tests/run/inline-trait-body-override-var.check b/tests/run/inline-trait-body-override-var.check deleted file mode 100644 index 0cfbf08886fc..000000000000 --- a/tests/run/inline-trait-body-override-var.check +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/tests/run/inline-trait-body-override-var.scala b/tests/run/inline-trait-body-override-var.scala deleted file mode 100644 index cfcc3c19b2f1..000000000000 --- a/tests/run/inline-trait-body-override-var.scala +++ /dev/null @@ -1,9 +0,0 @@ -inline trait A: - var x: Int = 1 - -class B extends A: - override var x = 2 - -@main def Test = - val b = B() - println(b.x) diff --git a/tests/run/inline-trait-body-var.check b/tests/run/inline-trait-body-var.check new file mode 100644 index 000000000000..94ebaf900161 --- /dev/null +++ b/tests/run/inline-trait-body-var.check @@ -0,0 +1,4 @@ +1 +2 +3 +4 diff --git a/tests/pos/inline-trait-body-var.scala b/tests/run/inline-trait-body-var.scala similarity index 50% rename from tests/pos/inline-trait-body-var.scala rename to tests/run/inline-trait-body-var.scala index 081a5eb829fe..fda8fc3109b7 100644 --- a/tests/pos/inline-trait-body-var.scala +++ b/tests/run/inline-trait-body-var.scala @@ -6,3 +6,10 @@ class B extends A: val old = x x += 1 old + +@main def Test = + val b = B() + println(b.f) + println(b.f) + println(b.f) + println(b.f) From f6e171b3218d9e3c959d824de41c9c3977d67cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 10:14:57 +0200 Subject: [PATCH 103/106] Add tests for constructor arguments with side effects --- ...nline-trait-signature-side-effects-1.check | 6 +++++ ...nline-trait-signature-side-effects-1.scala | 27 +++++++++++++++++++ ...nline-trait-signature-side-effects-2.check | 6 +++++ ...nline-trait-signature-side-effects-2.scala | 16 +++++++++++ 4 files changed, 55 insertions(+) create mode 100644 tests/run/inline-trait-signature-side-effects-1.check create mode 100644 tests/run/inline-trait-signature-side-effects-1.scala create mode 100644 tests/run/inline-trait-signature-side-effects-2.check create mode 100644 tests/run/inline-trait-signature-side-effects-2.scala diff --git a/tests/run/inline-trait-signature-side-effects-1.check b/tests/run/inline-trait-signature-side-effects-1.check new file mode 100644 index 000000000000..4df3c9a85d7f --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-1.check @@ -0,0 +1,6 @@ +0 +0 +1 +0 +1 +2 diff --git a/tests/run/inline-trait-signature-side-effects-1.scala b/tests/run/inline-trait-signature-side-effects-1.scala new file mode 100644 index 000000000000..61c348eb48d1 --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-1.scala @@ -0,0 +1,27 @@ +inline trait A(i: Int): + val x = i + val y = i + +class B(i: Int) extends A(i) + +@main def Test = + var c = 0 + + val b1 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) + val b2 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) + val b3 = new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) \ No newline at end of file diff --git a/tests/run/inline-trait-signature-side-effects-2.check b/tests/run/inline-trait-signature-side-effects-2.check new file mode 100644 index 000000000000..4df3c9a85d7f --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-2.check @@ -0,0 +1,6 @@ +0 +0 +1 +0 +1 +2 diff --git a/tests/run/inline-trait-signature-side-effects-2.scala b/tests/run/inline-trait-signature-side-effects-2.scala new file mode 100644 index 000000000000..19dcad0681a3 --- /dev/null +++ b/tests/run/inline-trait-signature-side-effects-2.scala @@ -0,0 +1,16 @@ +inline trait A(i: Int): + val x = i + val y = i + +class B(i: Int) extends A(i) + +@main def Test = + var c = 0 + + for _ <- 0 until 3 + do new B({ + for i <- 0 to c + do println(i) + c += 1 + c + }) \ No newline at end of file From 102269e8690f706587a6c619360f706c13bea13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 10:30:51 +0200 Subject: [PATCH 104/106] Improve error message, remove unused method --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../dotty/tools/dotc/transform/SpecializeInlineTraits.scala | 6 +----- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index daa1b739b146..e25061883d72 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -92,7 +92,7 @@ class Compiler { new ExplicitSelf, // Make references to non-trivial self types explicit as casts new StringInterpolatorOpt, // Optimizes raw and s and f string interpolators by rewriting them to string concatenations or formats new DropBreaks) :: // Optimize local Break throws by rewriting them - List(new PruneInlineTraits, + List(new PruneInlineTraits, // Remove right-hand side of definitions in inline traits new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_` new InlinePatterns, // Remove placeholders of inlined patterns diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala index a70c5865adb9..1a2819eb3e38 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeInlineTraits.scala @@ -57,14 +57,10 @@ class SpecializeInlineTraits extends MacroTransform, SymTransformer { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match { - // TODO + // TODO check that things are inlined properly case _ => } - extension (tree: Tree) - private def hasSpecializedVersion: Boolean = - false - private def transformInlineTrait(inlineTrait: TypeDef)(using Context): TypeDef = val tpd.TypeDef(_, tmpl: Template) = inlineTrait: @unchecked val body1 = tmpl.body.flatMap { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2537d72d5824..098f7ffe3c2c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2681,7 +2681,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tdef: TypeDef if tdef.symbol.isClass => def rec(paramss: List[List[Symbol]]): Unit = paramss match { case (param :: _) :: _ if param.isTerm => - report.error(em"Implementation restriction: inline traits cannot have term parameters", param.srcPos) + report.error(em"Implementation restriction: inner classes inside inline traits cannot have term parameters", param.srcPos) case _ :: paramss => rec(paramss) case _ => From a5579401fdc455492092951ca996580b2c9563e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 10:32:02 +0200 Subject: [PATCH 105/106] Allow overriding opaque aliases for inline traits --- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 1809b6180d07..bc4096586c8c 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -340,7 +340,6 @@ object RefChecks { * of class `clazz` are met. */ def checkOverride(checkSubType: (Type, Type) => Context ?=> Boolean, member: Symbol, other: Symbol): Unit = - def overridesInlineTraitMember = other.owner.ownersIterator.exists(_.isInlineTrait) && member.is(Synthetic) def isInlinedFromInlineTrait = other.owner.isAllOf(InlineTrait) && member.is(Synthetic) def memberTp(self: Type) = @@ -418,7 +417,7 @@ object RefChecks { def overrideTargetNameError() = val otherTargetName = i"@targetName(${other.targetName})" - if !overridesInlineTraitMember then + if !isInlinedFromInlineTrait then if member.hasTargetName(member.name) then overrideError(i"misses a target name annotation $otherTargetName") else if other.hasTargetName(other.name) then @@ -463,13 +462,13 @@ object RefChecks { // direct overrides were already checked on completion (see Checking.chckWellFormed) // the test here catches indirect overriddes between two inherited base types. overrideError("cannot be used here - class definitions cannot be overridden") - else if (other.isOpaqueAlias) + else if (other.isOpaqueAlias && !isInlinedFromInlineTrait) // direct overrides were already checked on completion (see Checking.chckWellFormed) // the test here catches indirect overriddes between two inherited base types. overrideError("cannot be used here - opaque type aliases cannot be overridden") else if (!other.is(Deferred) && member.isClass) overrideError("cannot be used here - classes can only override abstract types") - else if (other.isEffectivelyFinal && !overridesInlineTraitMember) then // (1.2) + else if (other.isEffectivelyFinal && !isInlinedFromInlineTrait) then // (1.2) overrideError(i"cannot override final member ${other.showLocated}") else if (member.is(ExtensionMethod) && !other.is(ExtensionMethod)) // (1.3) overrideError("is an extension method, cannot override a normal method") From 17e17bc13f7b75a37f66be235523fcc4cc2165d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 4 Jul 2023 10:38:30 +0200 Subject: [PATCH 106/106] Let compiler complain if deferred member isn't implemented --- compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 907eb452dcea..70b352eb31e8 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -64,6 +64,7 @@ object Inlines: def isInlineableFromInlineTrait(inlinedTraitSym: ClassSymbol, member: tpd.Tree)(using Context): Boolean = !(member.isInstanceOf[tpd.TypeDef] && inlinedTraitSym.typeParams.contains(member.symbol)) && !member.symbol.isAllOf(Inline) + && !member.symbol.is(Deferred) /** Should call be inlined in this context? */ def needsInlining(tree: Tree)(using Context): Boolean =