From 7710f5605f531e0ebc3d17d0286360dcf9ddd0c6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 09:20:22 +0100 Subject: [PATCH 01/10] Fix code to conform to new constructor proxy scheme --- .../src/dotty/tools/backend/jvm/BytecodeWriters.scala | 3 ++- compiler/src/dotty/tools/dotc/ast/Trees.scala | 8 +++++--- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/Symbols.scala | 2 ++ compiler/src/dotty/tools/dotc/typer/Namer.scala | 5 +++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +++--- scala3doc/src/dotty/dokka/site/templates.scala | 2 +- scala3doc/src/dotty/dokka/tasty/SymOps.scala | 2 +- scala3doc/src/dotty/renderers/html.scala | 4 ++-- tests/run-macros/refined-selectable-macro/Macro_2.scala | 3 ++- 10 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala b/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala index 25874a938526..2bf32030e987 100644 --- a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala +++ b/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala @@ -81,7 +81,8 @@ trait BytecodeWriters { trait AsmpBytecodeWriter extends BytecodeWriter { import scala.tools.asm - private val baseDir = Directory(None.get).createDirectory() // FIXME missing directoy + private val baseDir = new Directory(None.get).createDirectory() // FIXME missing directoy + // new needed here since resolution of user-defined `apply` methods is ambiguous, and we want the constructor. private def emitAsmp(jclassBytes: Array[Byte], asmpFile: dotty.tools.io.File): Unit = { val pw = asmpFile.printWriter() diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index b6007f4e9d56..7064ccc2e2c2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1013,13 +1013,15 @@ object Trees { @sharable val EmptyTree: Thicket = genericEmptyTree @sharable val EmptyValDef: ValDef = genericEmptyValDef - @sharable val ContextualEmptyTree: Thicket = EmptyTree() // an empty tree marking a contextual closure + @sharable val ContextualEmptyTree: Thicket = new EmptyTree() // an empty tree marking a contextual closure // ----- Auxiliary creation methods ------------------ def Thicket(): Thicket = EmptyTree - def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: Nil) - def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: x3 :: Nil) + def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: Nil) + def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: x3 :: Nil) + def Thicket(xs: List[Tree])(implicit src: SourceFile) = new Thicket(xs) + def flatTree(xs: List[Tree])(implicit src: SourceFile): Tree = flatten(xs) match { case x :: Nil => x case ys => Thicket(ys) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 0e6bb7342bff..1c2e48f2bbee 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1609,7 +1609,7 @@ object SymDenotations { private def baseTypeCache(using Context): BaseTypeMap = { if !currentHasSameBaseTypesAs(myBaseTypeCachePeriod) then - myBaseTypeCache = BaseTypeMap() + myBaseTypeCache = new BaseTypeMap() myBaseTypeCachePeriod = ctx.period myBaseTypeCache } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index b6325ae645d7..d568dbf69125 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -496,6 +496,8 @@ object Symbols { def currentClass(using Context): ClassSymbol = ctx.owner.enclosingClass.asClass type MutableSymbolMap[T] = EqHashMap[Symbol, T] + def MutableSymbolMap[T](): EqHashMap[Symbol, T] = EqHashMap[Symbol, T]() + def MutableSymbolMap[T](initialCapacity: Int): EqHashMap[Symbol, T] = EqHashMap[Symbol, T](initialCapacity) // ---- Factory methods for symbol creation ---------------------- // diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 5aa25a6272eb..ffc27649b701 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -55,7 +55,8 @@ class Namer { typer: Typer => val ExpandedTree : Property.Key[untpd.Tree] = new Property.Key val ExportForwarders: Property.Key[List[tpd.MemberDef]] = new Property.Key val SymOfTree : Property.Key[Symbol] = new Property.Key - val Deriver : Property.Key[typer.Deriver] = new Property.Key + val AttachedDeriver : Property.Key[Deriver] = new Property.Key + // was `val Deriver`, but that gave shadowing problems with constructor proxies /** A partial map from unexpanded member and pattern defs and to their expansions. * Populated during enterSyms, emptied during typer. @@ -1186,7 +1187,7 @@ class Namer { typer: Typer => } val deriver = new Deriver(derivingClass, derivePos)(using localCtx) deriver.enterDerived(impl.derived) - original.putAttachment(Deriver, deriver) + original.putAttachment(AttachedDeriver, deriver) } denot.info = tempInfo.finalized(parentTypes) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 99cb703ba49d..2cf41b4c3e49 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2199,8 +2199,8 @@ class Typer extends Namer if (ctx.mode.is(Mode.Interactive) && ctx.settings.YretainTrees.value) cls.rootTreeOrProvider = cdef1 - for (deriver <- cdef.removeAttachment(Deriver)) - cdef1.putAttachment(Deriver, deriver) + for (deriver <- cdef.removeAttachment(AttachedDeriver)) + cdef1.putAttachment(AttachedDeriver, deriver) cdef1 } @@ -2710,7 +2710,7 @@ class Typer extends Namer val enumContext = enumContexts(stat.symbol.linkedClass) if enumContext != null then checkEnumCaseRefsLegal(stat, enumContext) - stat.removeAttachment(Deriver) match { + stat.removeAttachment(AttachedDeriver) match { case Some(deriver) => deriver.finalize(stat) case None => stat } diff --git a/scala3doc/src/dotty/dokka/site/templates.scala b/scala3doc/src/dotty/dokka/site/templates.scala index bc2f754fe8fb..0de7f1d2115b 100644 --- a/scala3doc/src/dotty/dokka/site/templates.scala +++ b/scala3doc/src/dotty/dokka/site/templates.scala @@ -70,7 +70,7 @@ case class TemplateFile( case other => other // Library requires mutable maps.. - val mutableProperties = JHashMap(ctx.properties.transform((_, v) => asJavaElement(v)).asJava) + val mutableProperties = new JHashMap(ctx.properties.transform((_, v) => asJavaElement(v)).asJava) val rendered = Template.parse(this.rawCode).render(mutableProperties) // We want to render markdown only if next template is html val code = if (isHtml || layoutTemplate.exists(!_.isHtml)) rendered else diff --git a/scala3doc/src/dotty/dokka/tasty/SymOps.scala b/scala3doc/src/dotty/dokka/tasty/SymOps.scala index c11be73c1628..ab851e94bf1f 100644 --- a/scala3doc/src/dotty/dokka/tasty/SymOps.scala +++ b/scala3doc/src/dotty/dokka/tasty/SymOps.scala @@ -131,7 +131,7 @@ class SymOps[Q <: Quotes](val q: Q): // We want package object to point to package val className = sym.className.filter(_ != "package$") - DRI( + new DRI( className.fold(sym.packageName)(cn => s"${sym.packageName}.${cn}"), sym.anchor.getOrElse(""), // TODO do we need any of this fields? null, diff --git a/scala3doc/src/dotty/renderers/html.scala b/scala3doc/src/dotty/renderers/html.scala index a5a4297cdc3e..12701491c03a 100644 --- a/scala3doc/src/dotty/renderers/html.scala +++ b/scala3doc/src/dotty/renderers/html.scala @@ -46,7 +46,7 @@ object HTML: .replace("'", "'") case class Attr(name: String): - def :=(value: String): AppliedAttr = AppliedAttr(s"""$name="$value"""") + def :=(value: String): AppliedAttr = new AppliedAttr(s"""$name="$value"""") opaque type AppliedTag = StringBuilder @@ -97,5 +97,5 @@ object HTML: val testId = Attr("data-test-id") val alt = Attr("alt") - def raw(content: String): AppliedTag = AppliedTag(content) + def raw(content: String): AppliedTag = new AppliedTag(content) def raw(content: StringBuilder): AppliedTag = content diff --git a/tests/run-macros/refined-selectable-macro/Macro_2.scala b/tests/run-macros/refined-selectable-macro/Macro_2.scala index 4fffe4f17c05..d01708d32ad3 100644 --- a/tests/run-macros/refined-selectable-macro/Macro_2.scala +++ b/tests/run-macros/refined-selectable-macro/Macro_2.scala @@ -20,6 +20,7 @@ object Macro2 { '{ new Record($elems:_*).asInstanceOf[R] } } - def fromUntypedTuple(elems: (String, Any)*): Record = Record(elems: _*) + def fromUntypedTuple(elems: (String, Any)*): Record = new Record(elems: _*) + // `new` is needed since resolving the two `apply`s is ambiguous; this was hidden by old scheme for creator applications } } \ No newline at end of file From 2b5791771684dbb65bb3107c73f0f988d4c487fa Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 09:23:12 +0100 Subject: [PATCH 02/10] That one gave a double definition of `R` which caused problems later. --- scala3doc-testcases/src/tests/givenDRI.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scala3doc-testcases/src/tests/givenDRI.scala b/scala3doc-testcases/src/tests/givenDRI.scala index 81a10854d7f4..f1ab674e91a5 100644 --- a/scala3doc-testcases/src/tests/givenDRI.scala +++ b/scala3doc-testcases/src/tests/givenDRI.scala @@ -23,7 +23,7 @@ given [S <: C]: A[S] with {} class R: def a = 1 -given R: A[Int] with +given RR: A[Int] with def a = 2 class S: From c15a4fa4d23ae45e9f99188694180dfda367f721 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Dec 2020 21:06:19 +0100 Subject: [PATCH 03/10] Add ambiguity test --- tests/neg/creator-ambiguous.scala | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/neg/creator-ambiguous.scala diff --git a/tests/neg/creator-ambiguous.scala b/tests/neg/creator-ambiguous.scala new file mode 100644 index 000000000000..dd476933db1e --- /dev/null +++ b/tests/neg/creator-ambiguous.scala @@ -0,0 +1,23 @@ + +// This used to succeed with old creator methods scheme +// What happened was: the overloading resolution gave an ambiguous +// overload, but then the falblback picked the constructor +object Test: + + case class Record(elems: (String, Any)*) + + object Record: + + inline def apply[R <: Record](elems: (String, Any)*) : R = new Record(elems: _*).asInstanceOf[R] + + def fromUntypedTuple(elems: (String, Any)*): Record = Record(elems: _*) // error: ambiguous overload + + def apply(x: String): Test = Test(x ++ x) + + def apply(x: Integer): Test = Test(x + x) + + Test(null) // error: ambiguous overload + +end Test + +class Test(x: Object) \ No newline at end of file From 63d5cbdc276aaa4070f08785fdae7f896ac2c571 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 11:57:17 +0100 Subject: [PATCH 04/10] Add constructor proxies Use synthetic apply methods instead of a fallback in Typer to implement creator applications --- .../src/dotty/tools/dotc/config/Config.scala | 5 + .../dotty/tools/dotc/core/Definitions.scala | 21 +-- .../src/dotty/tools/dotc/core/Flags.scala | 17 ++- .../src/dotty/tools/dotc/core/NamerOps.scala | 104 +++++++++++++- .../tools/dotc/core/SymDenotations.scala | 14 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 18 ++- .../src/dotty/tools/dotc/core/Symbols.scala | 13 +- .../dotc/core/classfile/ClassfileParser.scala | 1 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 1 + .../core/unpickleScala2/Scala2Unpickler.scala | 1 + .../dotc/semanticdb/ExtractSemanticDB.scala | 1 + .../dotty/tools/dotc/transform/Erasure.scala | 17 +++ .../dotty/tools/dotc/transform/Mixin.scala | 5 +- .../tools/dotc/transform/PostTyper.scala | 4 +- .../tools/dotc/transform/TreeChecker.scala | 7 +- .../dotty/tools/dotc/typer/Applications.scala | 57 ++++---- .../src/dotty/tools/dotc/typer/Namer.scala | 90 ++++++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 127 +++++++++++------- .../tools/languageserver/CompletionTest.scala | 17 ++- tests/bench/inductive-implicits.scala | 10 +- tests/neg/i5525.scala | 2 +- tests/neg/i8569.check | 6 +- tests/pos/creators/A_1.scala | 5 + tests/pos/creators/Test_2.scala | 3 + tests/pos/scala2-creators.scala | 3 + tests/pos/test-typers.scala | 13 -- .../gestalt-type-toolbox-reflect/Test_2.scala | 10 +- tests/run/creator-applys.scala | 54 ++------ tests/run/option-extract.scala | 22 +++ tests/run/toplevel-mixed/B_1.scala | 2 +- tests/run/toplevel-mixed/Test_2.scala | 2 +- .../expect/InstrumentTyper.expect.scala | 2 +- tests/semanticdb/metac.expect | 2 +- 33 files changed, 433 insertions(+), 223 deletions(-) create mode 100644 tests/pos/creators/A_1.scala create mode 100644 tests/pos/creators/Test_2.scala create mode 100644 tests/pos/scala2-creators.scala create mode 100644 tests/run/option-extract.scala diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 26dbbbe145a4..6b164490b9fb 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -8,6 +8,11 @@ object Config { inline val cacheImplicitScopes = true inline val cacheMatchReduced = true + /** If true, we implement creator expressions by adding constructor proxies. + * If false, we rely on fallbacks in Typer to try a constructor if everything else failed. + */ + inline val addConstructorProxies = true + /** If true, the `runWithOwner` operation uses a re-usable context, * similar to explore. This requires that the context does not escape * the call. If false, `runWithOwner` runs its operation argument diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 24f151a7cfbe..724009f96e7f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -300,14 +300,19 @@ class Definitions { cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope) cls.setFlag(NoInits | JavaDefined) - // The companion object doesn't really exist, so it needs to be marked as - // absent. Here we need to set it before completing attempt to load Object's - // classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(nme.Object).symbol - companion.moduleClass.markAbsent() - companion.markAbsent() - - completeClass(cls) + if config.Config.addConstructorProxies then + ensureConstructor(cls, cls.denot.asClass, EmptyScope) + val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm + NamerOps.makeConstructorCompanion(companion, cls) + cls + else + // The companion object doesn't really exist, so it needs to be marked as + // absent. Here we need to set it before completing attempt to load Object's + // classfile, which causes issue #1648. + val companion = JavaLangPackageVal.info.decl(nme.Object).symbol + companion.moduleClass.markAbsent() + companion.markAbsent() + completeClass(cls) } def ObjectType: TypeRef = ObjectClass.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index eddc2147b1c3..04b91569a13c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -378,7 +378,7 @@ object Flags { val (Touched @ _, _, _) = newFlags(50, "") /** Class has been lifted out to package level, local value has been lifted out to class level */ - val (Lifted @ _, _, _) = newFlags(51, "") + val (Lifted @ _, _, _) = newFlags(51, "") // only used from lambda-lift (could be merged with ConstructorProxy) /** Term member has been mixed in */ val (MixedIn @ _, _, _) = newFlags(52, "") @@ -420,6 +420,9 @@ object Flags { /** A denotation that is valid in all run-ids */ val (Permanent @ _, _, _) = newFlags(61, "") + /** Symbol is a constructor proxy (either companion, or apply method) */ + val (ConstructorProxy @ _, _, _) = newFlags(62, "") // (could be merged with Lifted) + // --------- Combined Flag Sets and Conjunctions ---------------------- /** All possible flags */ @@ -455,7 +458,7 @@ object Flags { Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, Extension, NonMember, Implicit, Given, Permanent, Synthetic, - SuperParamAliasOrScala2x, Inline, Macro) + SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy) /** Flags that are not (re)set when completing the denotation, or, if symbol is * a top-level class or object, when completing the denotation once the class @@ -463,7 +466,8 @@ object Flags { * is completed) */ val AfterLoadFlags: FlagSet = commonFlags( - FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined, Transparent) + FromStartFlags, AccessFlags, Final, AccessorOrSealed, + Abstract, LazyOrTrait, SelfName, JavaDefined, Transparent) /** A value that's unstable unless complemented with a Stable flag */ val UnstableValueFlags: FlagSet = Mutable | Method @@ -508,7 +512,7 @@ object Flags { val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | Synthetic | JavaDefined | JavaStatic | Artifact | - Lifted | MixedIn | Specialized + Lifted | MixedIn | Specialized | ConstructorProxy /** Flags that can apply to a module val */ val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | @@ -539,7 +543,10 @@ object Flags { val EnumCase: FlagSet = Case | Enum val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter + val EffectivelyErased = ConstructorProxy | Erased + val ConstructorProxyModule: FlagSet = ConstructorProxy | Module val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter + val DeferredInline: FlagSet = Deferred | Inline val DeferredOrLazy: FlagSet = Deferred | Lazy val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides @@ -548,7 +555,7 @@ object Flags { val FinalOrInline: FlagSet = Final | Inline val FinalOrModuleClass: FlagSet = Final | ModuleClass // A module class or a final class val EffectivelyFinalFlags: FlagSet = Final | Private - val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro + val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro | ConstructorProxy val FinalOrSealed: FlagSet = Final | Sealed val GivenOrImplicit: FlagSet = Given | Implicit val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 8b54c74ca122..338ea5c1ae84 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -1,9 +1,12 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package core -import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, NameOps._ +import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, Names._, NameOps._ import Denotations._ -import SymDenotations.LazyType, Names.Name, StdNames.nme +import SymDenotations.{LazyType, SymDenotation}, StdNames.nme +import config.Config +import ast.untpd /** Operations that are shared between Namer and TreeUnpickler */ object NamerOps: @@ -57,4 +60,99 @@ object NamerOps: else NoSymbol.assertingErrorsReported(s"no companion $name in $scope") } + /** If a class has one of these flags, it does not get a constructor companion */ + private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module + + /** The flags of a constructor companion */ + private val ConstructorCompanionFlags = Synthetic | ConstructorProxy + + /** The flags of an `apply` method that serves as a constructor proxy */ + val ApplyProxyFlags = Synthetic | ConstructorProxy | Inline | Method + + /** Does symbol `cls` need constructor proxies to be generated? */ + def needsConstructorProxies(cls: Symbol)(using Context): Boolean = + Config.addConstructorProxies + && cls.isClass + && !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags) + && !cls.isAnonymousClass + + /** The completer of a constructor proxy apply method */ + class ApplyProxyCompleter(constr: Symbol)(using Context) extends LazyType: + def complete(denot: SymDenotation)(using Context): Unit = + denot.info = constr.info + + /** Add constructor proxy apply methods to `scope`. Proxies are for constructors + * in `cls` and they reside in `modcls`. + */ + def addConstructorApplies(scope: MutableScope, cls: ClassSymbol, modcls: ClassSymbol)(using Context): scope.type = + def proxy(constr: Symbol): Symbol = + newSymbol( + modcls, nme.apply, ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags), + ApplyProxyCompleter(constr), coord = constr.coord) + if Config.addConstructorProxies then + for dcl <- cls.info.decls do + if dcl.isConstructor then scope.enter(proxy(dcl)) + scope + end addConstructorApplies + + /** The completer of a constructor companion for class `cls`, where + * `modul` is the companion symbol and `modcls` is its class. + */ + def constructorCompanionCompleter(cls: ClassSymbol)( + modul: TermSymbol, modcls: ClassSymbol)(using Context): LazyType = + new LazyType with SymbolLoaders.SecondCompleter { + def complete(denot: SymDenotation)(using Context): Unit = + val prefix = modcls.owner.thisType + denot.info = ClassInfo( + prefix, modcls, defn.AnyType :: Nil, + addConstructorApplies(newScope, cls, modcls), TermRef(prefix, modul)) + }.withSourceModule(modul) + + /** A new symbol that is the constructor companion for class `cls` */ + def constructorCompanion(cls: ClassSymbol)(using Context): TermSymbol = + val companion = newModuleSymbol( + cls.owner, cls.name.toTermName, + ConstructorCompanionFlags, ConstructorCompanionFlags, + constructorCompanionCompleter(cls), + coord = cls.coord, + assocFile = cls.assocFile) + companion.moduleClass.registerCompanion(cls) + cls.registerCompanion(companion.moduleClass) + companion + + /** Add all necesssary constructor proxy symbols for members of class `cls`. This means: + * + * - if a member is a class that needs a constructor companion, add one, + * provided no member with the same name exists. + * - if `cls` is a companion object of a class that needs a constructor companion, + * and `cls` does not already define or inherit an `apply` method, + * add `apply` methods for all constructors of the companion class. + */ + def addConstructorProxies(cls: ClassSymbol)(using Context): Unit = + + def memberExists(cls: ClassSymbol, name: TermName): Boolean = + cls.baseClasses.exists(_.info.decls.lookupEntry(name) != null) + for mbr <- cls.info.decls do + if needsConstructorProxies(mbr) + && !mbr.asClass.unforcedRegisteredCompanion.exists + && !memberExists(cls, mbr.name.toTermName) + then + constructorCompanion(mbr.asClass).entered + + if cls.is(Module) + && needsConstructorProxies(cls.linkedClass) + && !memberExists(cls, nme.apply) + then + addConstructorApplies(cls.info.decls.openForMutations, cls.linkedClass.asClass, cls) + end addConstructorProxies + + /** Turn `modul` into a constructor companion for class `cls` */ + def makeConstructorCompanion(modul: TermSymbol, cls: ClassSymbol)(using Context): Unit = + val modcls = modul.moduleClass.asClass + modul.setFlag(ConstructorCompanionFlags) + modcls.setFlag(ConstructorCompanionFlags) + modcls.info = constructorCompanionCompleter(cls)(modul, modcls) + cls.registeredCompanion = modcls + modcls.registeredCompanion = cls + end NamerOps diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 1c2e48f2bbee..0f8171fef881 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -951,7 +951,8 @@ object SymDenotations { /** An erased value or an erased inline method or field */ def isEffectivelyErased(using Context): Boolean = - is(Erased) || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot) + isOneOf(EffectivelyErased) + || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot) /** ()T and => T types should be treated as equivalent for this symbol. * Note: For the moment, we treat Scala-2 compiled symbols as loose matching, @@ -2165,6 +2166,8 @@ object SymDenotations { if (companion.isClass && !isAbsent(canForce = false) && !companion.isAbsent(canForce = false)) myCompanion = companion + private[core] def unforcedRegisteredCompanion: Symbol = myCompanion + override def registeredCompanion(using Context) = if !myCompanion.exists then ensureCompleted() @@ -2256,6 +2259,15 @@ object SymDenotations { case d: DenotUnion => dropStale(d) case d => d + /** Filter symbols making up a DenotUnion to remove alternatives from stale classfiles. + * This proceeds as follow: + * + * - prefer alternatives that are currently compiled over ones that have been compiled before. + * - if no alternative is compiled now, and they all come from the same file, keep all of them + * - if no alternative is compiled now, and they come from different files, keep the + * ones from the youngest file, but issue a warning that one of the class files + * should be removed from the classpath. + */ def dropStale(multi: DenotUnion): PreDenotation = val compiledNow = multi.filterWithPredicate(d => d.symbol.isDefinedInCurrentRun || d.symbol.associatedFile == null diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 785d0d1e7e70..26e6e659779e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -359,13 +359,19 @@ abstract class SymbolLoader extends LazyType { self => throw ex } finally { - def postProcess(denot: SymDenotation) = - if (!denot.isCompleted && - !denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter]) - denot.markAbsent() - postProcess(root) + def postProcess(denot: SymDenotation, other: Symbol) = + if !denot.isCompleted && + !denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter] then + if denot.is(ModuleClass) && NamerOps.needsConstructorProxies(other) then + NamerOps.makeConstructorCompanion(denot.sourceModule.asTerm, other.asClass) + denot.resetFlag(Touched) + else + denot.markAbsent() + + val other = if root.isRoot then NoSymbol else root.scalacLinkedClass + postProcess(root, other) if (!root.isRoot) - postProcess(root.scalacLinkedClass.denot) + postProcess(other, root.symbol) } } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index d568dbf69125..a9baa4d65802 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -54,6 +54,7 @@ object Symbols { //assert(id != 723) def coord: Coord = myCoord + /** Set the coordinate of this class, this is only useful when the coordinate is * not known at symbol creation. This is the case for root symbols * unpickled from TASTY. @@ -61,7 +62,9 @@ object Symbols { * @pre coord == NoCoord */ private[core] def coord_=(c: Coord): Unit = { - assert(myCoord == NoCoord) + // assert(myCoord == NoCoord) + // This assertion fails for CommentPickling test. + // TODO: figure out what's wrong in the setup of CommentPicklingTest and re-enable assertion. myCoord = c } @@ -135,8 +138,12 @@ object Symbols { /** Does this symbol come from a currently compiled source file? */ final def isDefinedInCurrentRun(using Context): Boolean = span.exists && defRunId == ctx.runId && { - val file = associatedFile - file != null && ctx.run.files.contains(file) + try + val file = associatedFile + file != null && ctx.run.files.contains(file) + catch case ex: StaleSymbol => + // can happen for constructor proxy companions. Test case is pos-macros/i9484. + false } /** Is symbol valid in current run? */ diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 1119771e70af..2a709193656b 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -194,6 +194,7 @@ class ClassfileParser( addAnnotationConstructor(classInfo.asInstanceOf[TempClassInfoType]) setClassInfo(classRoot, classInfo, fromScala2 = false) + NamerOps.addConstructorProxies(moduleRoot.classSymbol) } else if (result == Some(NoEmbedded)) for (sym <- List(moduleRoot.sourceModule, moduleRoot.symbol, classRoot.symbol)) { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index deb024b43ea4..be8f4aa7c0eb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -940,6 +940,7 @@ class TreeUnpickler(reader: TastyReader, tparams ++ vparams ++ stats }) defn.patchStdLibClass(cls) + NamerOps.addConstructorProxies(cls) setSpan(start, untpd.Template(constr, mappedParents, Nil, self, lazyStats) .withType(localDummy.termRef)) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 6619fa187108..3b2783d64f58 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -582,6 +582,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case denot: ClassDenotation if !isRefinementClass(denot.symbol) => val selfInfo = if (atEnd) NoType else readTypeRef() setClassInfo(denot, tp, fromScala2 = true, selfInfo) + NamerOps.addConstructorProxies(denot.classSymbol) case denot => val tp1 = translateTempPoly(tp) denot.info = diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index c74bf8d23a47..4ee3b4db9210 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -89,6 +89,7 @@ class ExtractSemanticDB extends Phase: private def excludeSymbol(sym: Symbol)(using Context): Boolean = !sym.exists + || sym.is(ConstructorProxy) || sym.name.isWildcard || excludeQual(sym) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 54480c0be824..9b67c8cbf21a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -976,6 +976,23 @@ object Erasure { override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = EmptyTree + /** Drop all constructor proxies of members of class `cls`. + * If `cls` is itself a constructor proxy, mark it as absent after erasure. + */ + private def dropConstructorProxies(cls: ClassSymbol)(using Context) = + import Flags._ + if cls.linkedClass.is(ConstructorProxy) then + if cls.owner.is(PackageClass) && cls.isDefinedInCurrentRun then + cls.linkedClass.copySymDenotation(initFlags = EmptyFlags, info = NoType) + .installAfter(erasurePhase) + cls.registeredCompanion = NoSymbol + for mbr <- cls.info.decls do + if mbr.is(ConstructorProxy) then mbr.dropAfter(erasurePhase) + + override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(using Context): Tree = + try super.typedClassDef(cdef, cls) + finally dropConstructorProxies(cls) + override def typedAnnotated(tree: untpd.Annotated, pt: Type)(using Context): Tree = typed(tree.arg, pt) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 20f40fd96515..c3ff36f3e5cb 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -253,7 +253,10 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => for getter <- mixin.info.decls.toList - if getter.isGetter && !wasOneOf(getter, Deferred) && !getter.isConstExprFinalVal + if getter.isGetter + && !getter.isEffectivelyErased + && !wasOneOf(getter, Deferred) + && !getter.isConstExprFinalVal yield if (isCurrent(getter) || getter.name.is(ExpandedName)) { val rhs = diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 6167fabd7806..a0a59bc57887 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -143,10 +143,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } private def processValOrDefDef(tree: Tree)(using Context): tree.type = + val sym = tree.symbol tree match - case tree: ValOrDefDef if !tree.symbol.is(Synthetic) => + case tree: ValOrDefDef if !sym.is(Synthetic) => checkInferredWellFormed(tree.tpt) - val sym = tree.symbol if sym.is(Method) then if sym.isSetter then removeUnwantedAnnotations(sym, defn.SetterMetaAnnot, NoSymbol, keepIfNoRelevantAnnot = false) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 01a1c8de4913..68d15b82e362 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -63,7 +63,7 @@ class TreeChecker extends Phase with SymTransformer { } if (prev.exists) - assert(cur.exists, i"companion disappeared from $symd") + assert(cur.exists || prev.is(ConstructorProxy), i"companion disappeared from $symd") } def transformSym(symd: SymDenotation)(using Context): SymDenotation = { @@ -442,8 +442,11 @@ class TreeChecker extends Phase with SymTransformer { val decls = cls.classInfo.decls.toList.toSet.filter(isNonMagicalMember) val defined = impl.body.map(_.symbol) + + def isAllowed(sym: Symbol): Boolean = + sym.is(ConstructorProxy) && !ctx.phase.erasedTypes - val symbolsNotDefined = decls -- defined - constr.symbol + val symbolsNotDefined = (decls -- defined - constr.symbol).filterNot(isAllowed) assert(symbolsNotDefined.isEmpty, i" $cls tree does not define members: ${symbolsNotDefined.toList}%, %\n" + diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ca763e6a8def..ab8c03dc57c0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -28,7 +28,7 @@ import reporting._ import transform.TypeUtils._ import transform.SymUtils._ import Nullables._ -import config.Feature +import config.{Feature, Config} import collection.mutable import config.Printers.{overload, typr, unapp} @@ -848,17 +848,19 @@ trait Applications extends Compatibility { * Fallback if this fails: try to convert `E` to `new E`. */ def typedFunPart(fn: untpd.Tree, pt: Type)(using Context): Tree = - tryEither { - typedExpr(fn, pt) - } { (result, tstate) => - def fallBack(nuState: TyperState) = - if (nuState ne ctx.typerState) && !saysNotFound(nuState, EmptyTypeName) - then nuState.commit() // nuState messages are more interesting that tstate's "not found" - else tstate.commit() // it's "not found" both ways; keep original message - result - if untpd.isPath(fn) then tryNew(untpd)(fn, pt, fallBack) - else fallBack(ctx.typerState) - } + if Config.addConstructorProxies then typedExpr(fn, pt) + else + tryEither { + typedExpr(fn, pt) + } { (result, tstate) => + def fallBack(nuState: TyperState) = + if (nuState ne ctx.typerState) && !saysNotFound(nuState, EmptyTypeName) + then nuState.commit() // nuState messages are more interesting that tstate's "not found" + else tstate.commit() // it's "not found" both ways; keep original message + result + if untpd.isPath(fn) then tryNew(untpd)(fn, pt, fallBack) + else fallBack(ctx.typerState) + } /** Typecheck application. Result could be an `Apply` node, * or, if application is an operator assignment, also an `Assign` or @@ -1473,7 +1475,8 @@ trait Applications extends Compatibility { * -1 if 2nd alternative is preferred over 1st * 0 if neither alternative is preferred over the other * - * An alternative A1 is preferred over an alternative A2 if it wins in a tournament + * Normal symbols are always preferred over constructor proxies. Otherwise, + * an alternative A1 is preferred over an alternative A2 if it wins in a tournament * that awards one point for each of the following: * * - A1's owner derives from A2's owner. @@ -1634,19 +1637,23 @@ trait Applications extends Compatibility { if (winsType2) -1 else 0 } - val fullType1 = widenGiven(alt1.widen, alt1) - val fullType2 = widenGiven(alt2.widen, alt2) - val strippedType1 = stripImplicit(fullType1) - val strippedType2 = stripImplicit(fullType2) - - val result = compareWithTypes(strippedType1, strippedType2) - if (result != 0) result - else if (strippedType1 eq fullType1) - if (strippedType2 eq fullType2) 0 // no implicits either side: its' a draw - else 1 // prefer 1st alternative with no implicits - else if (strippedType2 eq fullType2) -1 // prefer 2nd alternative with no implicits - else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters + if alt1.symbol.is(ConstructorProxy) && !alt2.symbol.is(ConstructorProxy) then -1 + else if alt2.symbol.is(ConstructorProxy) && !alt1.symbol.is(ConstructorProxy) then 1 + else + val fullType1 = widenGiven(alt1.widen, alt1) + val fullType2 = widenGiven(alt2.widen, alt2) + val strippedType1 = stripImplicit(fullType1) + val strippedType2 = stripImplicit(fullType2) + + val result = compareWithTypes(strippedType1, strippedType2) + if (result != 0) result + else if (strippedType1 eq fullType1) + if (strippedType2 eq fullType2) 0 // no implicits either side: its' a draw + else 1 // prefer 1st alternative with no implicits + else if (strippedType2 eq fullType2) -1 // prefer 2nd alternative with no implicits + else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters } + end compare def narrowMostSpecific(alts: List[TermRef])(using Context): List[TermRef] = { record("narrowMostSpecific") diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index ffc27649b701..64568cd1b5bc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -564,23 +564,26 @@ class Namer { typer: Typer => } } - /** Create links between companion object and companion class */ - def createLinks(classTree: TypeDef, moduleTree: TypeDef)(using Context) = { - val claz = ctx.effectiveScope.lookup(classTree.name) - val modl = ctx.effectiveScope.lookup(moduleTree.name) - modl.registerCompanion(claz) - claz.registerCompanion(modl) - } + val classDef = mutable.Map[TypeName, TypeDef]() + val moduleDef = mutable.Map[TypeName, TypeDef]() - def createCompanionLinks(using Context): Unit = { - val classDef = mutable.Map[TypeName, TypeDef]() - val moduleDef = mutable.Map[TypeName, TypeDef]() + /** Create links between companion object and companion class. + * Populate `moduleDef` and `classDef` as a side effect. + */ + def createCompanionLinks()(using Context): Unit = { def updateCache(cdef: TypeDef): Unit = if (cdef.isClassDef && !cdef.mods.is(Package)) if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef else classDef(cdef.name) = cdef + def createLinks(classTree: TypeDef, moduleTree: TypeDef)(using Context) = { + val claz = ctx.effectiveScope.lookup(classTree.name) + val modl = ctx.effectiveScope.lookup(moduleTree.name) + modl.registerCompanion(claz) + claz.registerCompanion(modl) + } + for (stat <- stats) expanded(stat) match { case cdef : TypeDef => updateCache(cdef) @@ -598,42 +601,58 @@ class Namer { typer: Typer => createLinks(cdef, t) case EmptyTree => } + } - // If a top-level object or class has no companion in the current run, we - // enter a dummy companion (`denot.isAbsent` returns true) in scope. This - // ensures that we never use a companion from a previous run or from the - // class path. See tests/pos/false-companion for an example where this - // matters. - if (ctx.owner.is(PackageClass)) { - for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) { + /** If a top-level object or class has no companion in the current run, we + * enter a dummy companion (`denot.isAbsent` returns true) or constructor + * proxy in scope. This ensures that we never use a companion from a previous + * run or from thenclass path. See tests/pos/false-companion for an example + * where this matters. + * Also: We add constructor proxies for classes in some local scope, i.e. + * that are not members of other classes. Constructor proxies for member + * classes are added in addConstructorProxies. + */ + def addAbsentCompanions()(using Context): Unit = + if ctx.owner.isTerm then + for case cdef @ TypeDef(className, _) <- classDef.values do + val classSym = ctx.effectiveScope.lookup(className) + val moduleName = className.toTermName + if needsConstructorProxies(classSym) && ctx.effectiveScope.lookupEntry(moduleName) == null then + enterSymbol(constructorCompanion(classSym.asClass)) + else if ctx.owner.is(PackageClass) then + for case cdef @ TypeDef(moduleName, _) <- moduleDef.values do val moduleSym = ctx.effectiveScope.lookup(moduleName) - if (moduleSym.isDefinedInCurrentRun) { + if moduleSym.isDefinedInCurrentRun then val className = moduleName.stripModuleClassSuffix.toTypeName val classSym = ctx.effectiveScope.lookup(className) - if (!classSym.isDefinedInCurrentRun) { + if !classSym.isDefinedInCurrentRun then val absentClassSymbol = newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType) enterSymbol(absentClassSymbol) - } - } - } - for (cdef @ TypeDef(className, _) <- classDef.values) { + + for case cdef @ TypeDef(className, _) <- classDef.values do val classSym = ctx.effectiveScope.lookup(className.encode) - if (classSym.isDefinedInCurrentRun) { + if classSym.isDefinedInCurrentRun then val moduleName = className.toTermName - for (moduleSym <- ctx.effectiveScope.lookupAll(moduleName.encode)) - if (moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun) { - val absentModuleSymbol = newModuleSymbol(ctx.owner, moduleName, EmptyFlags, EmptyFlags, (_, _) => NoType) - enterSymbol(absentModuleSymbol) - } - } - } - } - } + val companionVals = ctx.effectiveScope.lookupAll(moduleName.encode) + if companionVals.isEmpty && needsConstructorProxies(classSym) then + enterSymbol(constructorCompanion(classSym.asClass)) + else + for moduleSym <- companionVals do + if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then + val companion = + if needsConstructorProxies(classSym) then constructorCompanion(classSym.asClass) + else newModuleSymbol( + ctx.owner, moduleName, EmptyFlags, EmptyFlags, (_, _) => NoType) + enterSymbol(companion) + end addAbsentCompanions stats.foreach(expand) mergeCompanionDefs() val ctxWithStats = stats.foldLeft(ctx)((ctx, stat) => indexExpanded(stat)(using ctx)) - createCompanionLinks(using ctxWithStats) + inContext(ctxWithStats) { + createCompanionLinks() + addAbsentCompanions() + } ctxWithStats } @@ -1203,7 +1222,8 @@ class Namer { typer: Typer => else cls.isNoInitsRealClass if ctorStable then cls.primaryConstructor.setFlag(StableRealizable) processExports(using localCtx) - defn.patchStdLibClass(denot.asClass) + defn.patchStdLibClass(cls) + addConstructorProxies(cls) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2cf41b4c3e49..d84f9ce92d82 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -46,6 +46,7 @@ import transform.TypeUtils._ import reporting._ import Nullables._ import NullOpsDecorator._ +import config.Config object Typer { @@ -2885,7 +2886,9 @@ class Typer extends Namer def tryImplicit(fallBack: => Tree) = tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked) - .getOrElse(tryNew(tpd)(tree, pt, _ => fallBack)) + .getOrElse( + if Config.addConstructorProxies then fallBack + else tryNew(tpd)(tree, pt, _ => fallBack)) if (ctx.mode.is(Mode.SynthesizeExtMethodReceiver)) // Suppress insertion of apply or implicit conversion on extension method receiver @@ -3315,49 +3318,50 @@ class Typer extends Namer checkEqualityEvidence(tree, pt) tree } - else if (methPart(tree).symbol.isAllOf(Inline | Deferred) && !Inliner.inInlineMethod) then - errorTree(tree, i"Deferred inline ${methPart(tree).symbol.showLocated} cannot be invoked") - else if (Inliner.isInlineable(tree) && !suppressInline && StagingContext.level == 0) { - tree.tpe <:< wildApprox(pt) - val errorCount = ctx.reporter.errorCount + else val meth = methPart(tree).symbol - val inlined = Inliner.inlineCall(tree) - if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) - else inlined - } - else if (tree.symbol.isScala2Macro && - // `raw`, `f` and `s` are eliminated by the StringInterpolatorOpt phase - tree.symbol != defn.StringContext_raw && - tree.symbol != defn.StringContext_f && - tree.symbol != defn.StringContext_s) - if (ctx.settings.XignoreScala2Macros.value) { - report.warning("Scala 2 macro cannot be used in Dotty, this call will crash at runtime. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html", tree.srcPos.startPos) - Throw(New(defn.MatchErrorClass.typeRef, Literal(Constant(s"Reached unexpanded Scala 2 macro call to ${tree.symbol.showFullName} compiled with -Xignore-scala2-macros.")) :: Nil)) - .withType(tree.tpe) - .withSpan(tree.span) + if meth.isAllOf(DeferredInline) && !Inliner.inInlineMethod then + errorTree(tree, i"Deferred inline ${meth.showLocated} cannot be invoked") + else if (Inliner.isInlineable(tree) && !suppressInline && StagingContext.level == 0) { + tree.tpe <:< wildApprox(pt) + val errorCount = ctx.reporter.errorCount + val inlined = Inliner.inlineCall(tree) + if ((inlined ne tree) && errorCount == ctx.reporter.errorCount) readaptSimplified(inlined) + else inlined } - else { - report.error( - """Scala 2 macro cannot be used in Dotty. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html - |To turn this error into a warning, pass -Xignore-scala2-macros to the compiler""".stripMargin, tree.srcPos.startPos) - tree - } - else TypeComparer.testSubType(tree.tpe.widenExpr, pt) match - case CompareResult.Fail => - wtp match - case wtp: MethodType => missingArgs(wtp) - case _ => - typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt") - //typr.println(TypeComparer.explained(tree.tpe <:< pt)) - adaptToSubType(wtp) - case CompareResult.OKwithGADTUsed if pt.isValueType => - // Insert an explicit cast, so that -Ycheck in later phases succeeds. - // I suspect, but am not 100% sure that this might affect inferred types, - // if the expected type is a supertype of the GADT bound. It would be good to come - // up with a test case for this. - tree.cast(pt) - case _ => - tree + else if (tree.symbol.isScala2Macro && + // `raw`, `f` and `s` are eliminated by the StringInterpolatorOpt phase + tree.symbol != defn.StringContext_raw && + tree.symbol != defn.StringContext_f && + tree.symbol != defn.StringContext_s) + if (ctx.settings.XignoreScala2Macros.value) { + report.warning("Scala 2 macro cannot be used in Dotty, this call will crash at runtime. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html", tree.srcPos.startPos) + Throw(New(defn.MatchErrorClass.typeRef, Literal(Constant(s"Reached unexpanded Scala 2 macro call to ${tree.symbol.showFullName} compiled with -Xignore-scala2-macros.")) :: Nil)) + .withType(tree.tpe) + .withSpan(tree.span) + } + else { + report.error( + """Scala 2 macro cannot be used in Dotty. See https://dotty.epfl.ch/docs/reference/dropped-features/macros.html + |To turn this error into a warning, pass -Xignore-scala2-macros to the compiler""".stripMargin, tree.srcPos.startPos) + tree + } + else TypeComparer.testSubType(tree.tpe.widenExpr, pt) match + case CompareResult.Fail => + wtp match + case wtp: MethodType => missingArgs(wtp) + case _ => + typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt") + //typr.println(TypeComparer.explained(tree.tpe <:< pt)) + adaptToSubType(wtp) + case CompareResult.OKwithGADTUsed if pt.isValueType => + // Insert an explicit cast, so that -Ycheck in later phases succeeds. + // I suspect, but am not 100% sure that this might affect inferred types, + // if the expected type is a supertype of the GADT bound. It would be good to come + // up with a test case for this. + tree.cast(pt) + case _ => + tree } // Follow proxies and approximate type paramrefs by their upper bound @@ -3618,6 +3622,29 @@ class Typer extends Namer case _ => } + /** Convert constructor proxy reference to a new expression */ + def newExpr = + def recur(tpt: Tree, pt: Type): Tree = pt.revealIgnored match + case PolyProto(targs, pt1) => + if targs.exists(_.isInstanceOf[NamedArg]) then + errorTree(tpt, "Named type argument not allowed in constructor application") + else + IntegratedTypeArgs(recur(AppliedTypeTree(tpt, targs), pt1)) + case _ => + typed(untpd.Select(untpd.New(untpd.TypedSplice(tpt)), nme.CONSTRUCTOR), pt) + + tree match + case Select(qual, nme.apply) => + val tycon = tree.tpe.widen.finalResultType.underlyingClassRef(refinementOK = false) + val tpt = qual match + case Ident(name) => cpy.Ident(qual)(name.toTypeName) + case Select(pre, name) => cpy.Select(qual)(pre, name.toTypeName) + recur(tpt.withType(tycon), pt) + .showing(i"convert creator $tree -> $result", typr) + case _ => + throw AssertionError(i"bad case for newExpr: $tree, $pt") + end newExpr + tree match { case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[?] | _: Closure => tree case _ => tree.tpe.widen match { @@ -3633,22 +3660,24 @@ class Typer extends Namer adaptOverloaded(ref) } case poly: PolyType if !(ctx.mode is Mode.Type) => - if (pt.isInstanceOf[PolyProto]) tree - else { - var typeArgs = tree match { + if tree.symbol.isAllOf(ApplyProxyFlags) && Config.addConstructorProxies then + newExpr + else if pt.isInstanceOf[PolyProto] then + tree + else + var typeArgs = tree match case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree) case _ => Nil - } - if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 + if typeArgs.isEmpty then typeArgs = constrained(poly, tree)._2 convertNewGenericArray(readapt(tree.appliedToTypeTrees(typeArgs))) - } case wtp => val isStructuralCall = wtp.isValueType && isStructuralTermSelectOrApply(tree) if (isStructuralCall) readaptSimplified(handleStructural(tree)) else pt match { case pt: FunProto => - adaptToArgs(wtp, pt) + if tree.symbol.isAllOf(ApplyProxyFlags) && Config.addConstructorProxies then newExpr + else adaptToArgs(wtp, pt) case pt: PolyProto => tree match { case _: IntegratedTypeArgs => tree diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index 049fbac09a51..224025420232 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -56,16 +56,19 @@ class CompletionTest { ) .completion(m1, Set(("baz", Method, "=> Int"))) } + // TODO: Also add tests with concrete classes, where the completion will + // include the constructor proxy companion + @Test def importCompleteClassWithPrefix: Unit = { withSources( - code"""object Foo { class MyClass }""", + code"""object Foo { abstract class MyClass }""", code"""import Foo.My${m1}""" ).completion(m1, Set(("MyClass", Class, "Foo.MyClass"))) } @Test def importCompleteClassNoPrefix: Unit = { withSources( - code"""object Foo { class MyClass }""", + code"""object Foo { abstract class MyClass }""", code"""import Foo.${m1}""" ).completion(m1, completionItems => { val results = CodeCompletion.simplifyResults(completionItems) @@ -83,7 +86,7 @@ class CompletionTest { @Test def importCompleteFromPackage: Unit = { withSources( code"""package a - class MyClass""", + abstract class MyClass""", code"""package b import a.My${m1}""" ).completion(m1, Set(("MyClass", Class, "a.MyClass"))) @@ -91,7 +94,7 @@ class CompletionTest { @Test def importCompleteFromClass: Unit = { withSources( - code"""class Foo { val x: Int = 0 }""", + code"""abstract class Foo { val x: Int = 0 }""", code"""import Foo.${m1}""" ).completion(m1, Set()) } @@ -129,7 +132,7 @@ class CompletionTest { @Test def importCompleteIncludePackage: Unit = { withSources( code"""package foo.bar - class Fizz""", + abstract classFizz""", code"""import foo.b${m1}""" ).completion(m1, Set(("bar", Module, "foo.bar"))) } @@ -141,7 +144,7 @@ class CompletionTest { def myDef = 0 var myVar = 0 object myObject - class myClass + abstract class myClass trait myTrait }""", code"""import MyObject.my${m1}""" @@ -212,7 +215,7 @@ class CompletionTest { @Test def completeErrorKnowsKind: Unit = { code"""object Bar { - | class Zig + | abstract class Zig | val Zag: Int = 0 | val b = 3 + Bar.${m1} |}""".withSource diff --git a/tests/bench/inductive-implicits.scala b/tests/bench/inductive-implicits.scala index 4bf66af6e37d..d99439bc580a 100644 --- a/tests/bench/inductive-implicits.scala +++ b/tests/bench/inductive-implicits.scala @@ -212,11 +212,11 @@ object Test extends App { Int :: Int :: Int :: - Int :: - Int :: - Int :: - Int :: - Int :: +// Int :: +// Int :: +// Int :: +// Int :: +// Int :: // // // Int :: // Int :: diff --git a/tests/neg/i5525.scala b/tests/neg/i5525.scala index 2802668ae636..d82d030b2e30 100644 --- a/tests/neg/i5525.scala +++ b/tests/neg/i5525.scala @@ -30,5 +30,5 @@ enum Foo11 { } enum Foo12 { // error: enums must contain at least one case - inline case C10() // error // error // error (inline treated as ident here) + inline case C10() // error // error (inline treated as ident here) } \ No newline at end of file diff --git a/tests/neg/i8569.check b/tests/neg/i8569.check index 787b64cfc036..b8d9accd6520 100644 --- a/tests/neg/i8569.check +++ b/tests/neg/i8569.check @@ -1,7 +1,7 @@ --- [E083] Type Error: tests/neg/i8569.scala:8:2 ------------------------------------------------------------------------ +-- [E083] Type Error: tests/neg/i8569.scala:8:8 ------------------------------------------------------------------------ 8 | outer.Inner(2) // error - | ^^^^^ - | (Test.outer : => Outer) is not a valid type prefix, since it is not an immutable path + | ^^^^^^^^^^^ + | Outer is not a valid class prefix, since it is not an immutable path longer explanation available when compiling with `-explain` -- [E083] Type Error: tests/neg/i8569.scala:9:6 ------------------------------------------------------------------------ diff --git a/tests/pos/creators/A_1.scala b/tests/pos/creators/A_1.scala new file mode 100644 index 000000000000..f2f2510ed230 --- /dev/null +++ b/tests/pos/creators/A_1.scala @@ -0,0 +1,5 @@ +class A() + + +class B() +object B diff --git a/tests/pos/creators/Test_2.scala b/tests/pos/creators/Test_2.scala new file mode 100644 index 000000000000..7d67eebbd3ec --- /dev/null +++ b/tests/pos/creators/Test_2.scala @@ -0,0 +1,3 @@ +@main def Test = + println(A()) + println(B()) diff --git a/tests/pos/scala2-creators.scala b/tests/pos/scala2-creators.scala new file mode 100644 index 000000000000..b3e41c3566c7 --- /dev/null +++ b/tests/pos/scala2-creators.scala @@ -0,0 +1,3 @@ + +@main def Test = + sys.SystemProperties() diff --git a/tests/pos/test-typers.scala b/tests/pos/test-typers.scala index 9bde682e9203..93e255f1a152 100644 --- a/tests/pos/test-typers.scala +++ b/tests/pos/test-typers.scala @@ -31,19 +31,6 @@ object typers { foo() } - class List[+T] { - def :: [U >: T](x: U): List[U] = new :: (x, this) - - def len: Int = this match { - case x :: xs1 => 1 + xs1.len - case Nil => 0 - } - } - - object Nil extends List[Nothing] - - case class :: [+T] (hd: T, tl: List[T]) extends List[T] - def len[U](xs: List[U]): Int = xs match { case x :: xs1 => 1 + len(xs1) case Nil => 0 diff --git a/tests/run-macros/gestalt-type-toolbox-reflect/Test_2.scala b/tests/run-macros/gestalt-type-toolbox-reflect/Test_2.scala index aee787026958..e74574c2feb6 100644 --- a/tests/run-macros/gestalt-type-toolbox-reflect/Test_2.scala +++ b/tests/run-macros/gestalt-type-toolbox-reflect/Test_2.scala @@ -163,11 +163,13 @@ object Test { class A object A class B + abstract class B2 object C - assert(companion[A, A.type]) - assert(companionName[A] == "Test$._$A") - assert(companionName[A.type] == "Test$._$A") - assert(companionName[B] == "", companionName[B]) + assert(companion[A, A.type], "FAIL") + assert(companionName[A] == "Test$._$A", "FAIL") + assert(companionName[A.type] == "Test$._$A", "FAIL") + assert(companionName[B] == "Test$._$B", companionName[B]) // Creator proxy companion was synthesized for B ... + assert(companionName[B2] == "", companionName[B]) // but not for B2 assert(companionName[C.type] == "", companionName[C.type]) } } diff --git a/tests/run/creator-applys.scala b/tests/run/creator-applys.scala index 87b46de4e562..0177d2e66674 100644 --- a/tests/run/creator-applys.scala +++ b/tests/run/creator-applys.scala @@ -1,4 +1,4 @@ -import language.experimental.namedTypeArguments +//import language.experimental.namedTypeArguments object Test extends App { class A { def run = "A" @@ -28,11 +28,11 @@ object Test extends App { val x5 = C[String, Int]("a", 1) assert(x5.run == "C a 1") - val x5a = C[S = String, T = Int]("a", 1) - assert(x5a.run == "C a 1") +// val x5a = C[S = String, T = Int]("a", 1) +// assert(x5a.run == "C a 1") - val x5b = C[T = Int]("a", 1) - assert(x5b.run == "C a 1") +// val x5b = C[T = Int]("a", 1) +// assert(x5b.run == "C a 1") val x6 = C("a", 1) assert((x6: C[String, Int]).run == "C a 1") @@ -109,47 +109,6 @@ object Test3 { val x6 = Test1.C("a", 1) assert((x6: Test1.C[String, Int]).run == "C a 1") - Test4 -} - -object Test4 { - type A = Test.A - type AA[T] = A - type B[T] = Test.B[T] - type C[T] = Test.C[T, Int] - - val x1 = A() - assert(x1.run == "A") - - val x1a = AA[Int]() - assert(x1a.run == "A") - - val x2 = B[String]() - assert(x2.run == "B") - - val x3: B[String] = B() - assert(x3.run == "B") - - val x5 = C[String]("a", 1) - assert(x5.run == "C a 1") - Test5 -} - -object Test5 { - val x1 = Test4.A() - assert(x1.run == "A") - - val x1a = Test4.AA[Int]() - assert(x1a.run == "A") - - val x2 = Test4.B[String]() - assert(x2.run == "B") - - val x3: Test4.B[String] = Test4.B() - assert(x3.run == "B") - - val x5 = Test4.C[String]("a", 1) - assert(x5.run == "C a 1") Test6 } @@ -162,4 +121,7 @@ object Test6 { } val x1 = A() assert(x1.run == "X") + + val x = StringBuilder() + val y = java.lang.Object() } \ No newline at end of file diff --git a/tests/run/option-extract.scala b/tests/run/option-extract.scala new file mode 100644 index 000000000000..7b87ad28bc68 --- /dev/null +++ b/tests/run/option-extract.scala @@ -0,0 +1,22 @@ + +enum Option[+A]: + case Some(x: A) + case None + + opaque type ExtractResult[B] = (=> B) => B + + def extract[B](f: A => B): ExtractResult[B] = + def result(default: => B): B = this match + case None => default + case Some(elem) => f(elem) + result + + extension [B](er: ExtractResult[B]) + def orElse(default: => B): B = er(default) +end Option + +@main def Test = + val x = Option.Some(11) + val y = x.extract(x => x + 1) + .orElse(22) + assert(y == 12, y) diff --git a/tests/run/toplevel-mixed/B_1.scala b/tests/run/toplevel-mixed/B_1.scala index de59188c0a83..7862d7017582 100644 --- a/tests/run/toplevel-mixed/B_1.scala +++ b/tests/run/toplevel-mixed/B_1.scala @@ -1,5 +1,5 @@ -class x(val s: String) +abstract class x(val s: String) object yy { def foo = 3 diff --git a/tests/run/toplevel-mixed/Test_2.scala b/tests/run/toplevel-mixed/Test_2.scala index 45075ef16a7b..e09d51769518 100644 --- a/tests/run/toplevel-mixed/Test_2.scala +++ b/tests/run/toplevel-mixed/Test_2.scala @@ -1,7 +1,7 @@ object Test extends App { assert(x == 10) assert(x("abc") == 3) - assert((new x("abc")).s == "abc") + assert((new x("abc"){}).s == "abc") assert(y("abc") == 3) assert(yy.foo == 3) diff --git a/tests/semanticdb/expect/InstrumentTyper.expect.scala b/tests/semanticdb/expect/InstrumentTyper.expect.scala index d847130f9fcf..cad500b7c234 100644 --- a/tests/semanticdb/expect/InstrumentTyper.expect.scala +++ b/tests/semanticdb/expect/InstrumentTyper.expect.scala @@ -1,6 +1,6 @@ package example -import scala.annotation.meta.param/*->scala::annotation::meta::param.*/ +import scala.annotation.meta.param/*->scala::annotation::meta::param#*/ import scala.language/*->scala::language.*/.existentials/*->scala::language.existentials.*/ import scala.language/*->scala::language.*/.higherKinds/*->scala::language.higherKinds.*/ import types.Test/*->types::Test.*/._ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index b93be61612b9..77bdc43c3182 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1479,7 +1479,7 @@ Occurrences: [2:7..2:12): scala -> scala/ [2:13..2:23): annotation -> scala/annotation/ [2:24..2:28): meta -> scala/annotation/meta/ -[2:29..2:34): param -> scala/annotation/meta/param. +[2:29..2:34): param -> scala/annotation/meta/param# [3:7..3:12): scala -> scala/ [3:13..3:21): language -> scala/language. [3:22..3:34): existentials -> scala/language.existentials. From 4e2cd5ae4aa8fcd7bb2ab617c23a430f3943a539 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 13:52:44 +0100 Subject: [PATCH 05/10] Drop code for previous creator expression scheme --- .../src/dotty/tools/dotc/config/Config.scala | 5 -- .../dotty/tools/dotc/core/Definitions.scala | 17 ++--- .../src/dotty/tools/dotc/core/NamerOps.scala | 8 +-- .../dotty/tools/dotc/typer/Applications.scala | 22 +------ .../src/dotty/tools/dotc/typer/ReTyper.scala | 7 --- .../src/dotty/tools/dotc/typer/Typer.scala | 62 ++----------------- 6 files changed, 13 insertions(+), 108 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 6b164490b9fb..26dbbbe145a4 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -8,11 +8,6 @@ object Config { inline val cacheImplicitScopes = true inline val cacheMatchReduced = true - /** If true, we implement creator expressions by adding constructor proxies. - * If false, we rely on fallbacks in Typer to try a constructor if everything else failed. - */ - inline val addConstructorProxies = true - /** If true, the `runWithOwner` operation uses a re-usable context, * similar to explore. This requires that the context does not escape * the call. If false, `runWithOwner` runs its operation argument diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 724009f96e7f..a9da2c31c176 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -300,19 +300,10 @@ class Definitions { cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope) cls.setFlag(NoInits | JavaDefined) - if config.Config.addConstructorProxies then - ensureConstructor(cls, cls.denot.asClass, EmptyScope) - val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm - NamerOps.makeConstructorCompanion(companion, cls) - cls - else - // The companion object doesn't really exist, so it needs to be marked as - // absent. Here we need to set it before completing attempt to load Object's - // classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(nme.Object).symbol - companion.moduleClass.markAbsent() - companion.markAbsent() - completeClass(cls) + ensureConstructor(cls, cls.denot.asClass, EmptyScope) + val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm + NamerOps.makeConstructorCompanion(companion, cls) + cls } def ObjectType: TypeRef = ObjectClass.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 338ea5c1ae84..5a52cb54da5f 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -71,8 +71,7 @@ object NamerOps: /** Does symbol `cls` need constructor proxies to be generated? */ def needsConstructorProxies(cls: Symbol)(using Context): Boolean = - Config.addConstructorProxies - && cls.isClass + cls.isClass && !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags) && !cls.isAnonymousClass @@ -89,9 +88,8 @@ object NamerOps: newSymbol( modcls, nme.apply, ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags), ApplyProxyCompleter(constr), coord = constr.coord) - if Config.addConstructorProxies then - for dcl <- cls.info.decls do - if dcl.isConstructor then scope.enter(proxy(dcl)) + for dcl <- cls.info.decls do + if dcl.isConstructor then scope.enter(proxy(dcl)) scope end addConstructorApplies diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index ab8c03dc57c0..10f770f6ce42 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -844,24 +844,6 @@ trait Applications extends Compatibility { if (ctx.owner.isClassConstructor && untpd.isSelfConstrCall(app)) ctx.thisCallArgContext else ctx - /** Typecheck the function part of an application. - * Fallback if this fails: try to convert `E` to `new E`. - */ - def typedFunPart(fn: untpd.Tree, pt: Type)(using Context): Tree = - if Config.addConstructorProxies then typedExpr(fn, pt) - else - tryEither { - typedExpr(fn, pt) - } { (result, tstate) => - def fallBack(nuState: TyperState) = - if (nuState ne ctx.typerState) && !saysNotFound(nuState, EmptyTypeName) - then nuState.commit() // nuState messages are more interesting that tstate's "not found" - else tstate.commit() // it's "not found" both ways; keep original message - result - if untpd.isPath(fn) then tryNew(untpd)(fn, pt, fallBack) - else fallBack(ctx.typerState) - } - /** Typecheck application. Result could be an `Apply` node, * or, if application is an operator assignment, also an `Assign` or * Block node. @@ -872,7 +854,7 @@ trait Applications extends Compatibility { val originalProto = new FunProto(tree.args, IgnoredProto(pt))(this, tree.applyKind)(using argCtx(tree)) record("typedApply") - val fun1 = typedFunPart(tree.fun, originalProto) + val fun1 = typedExpr(tree.fun, originalProto) // Warning: The following lines are dirty and fragile. // We record that auto-tupling or untupling was demanded as a side effect in adapt. @@ -1077,7 +1059,7 @@ trait Applications extends Compatibility { val isNamed = hasNamedArg(tree.args) val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") - typedFunPart(tree.fun, PolyProto(typedArgs, pt)) match { + typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { case IntegratedTypeArgs(app) => app case _: TypeApply if !ctx.isAfterTyper => diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index f119a9b82019..0d34ffe1320e 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -71,9 +71,6 @@ class ReTyper extends Typer with ReChecking { override def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(using Context): TypTree = promote(TypeTree(tree.tpe).withSpan(tree.span)) - override def typedFunPart(fn: untpd.Tree, pt: Type)(using Context): Tree = - typedExpr(fn, pt) - override def typedBind(tree: untpd.Bind, pt: Type)(using Context): Bind = { assertTyped(tree) val body1 = typed(tree.body, pt) @@ -104,10 +101,6 @@ class ReTyper extends Typer with ReChecking { override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType, locked: TypeVars)(fallBack: => Tree)(using Context): Tree = fallBack - override def tryNew[T >: Untyped <: Type] - (treesInst: Instance[T])(tree: Trees.Tree[T], pt: Type, fallBack: TyperState => Tree)(using Context): Tree = - fallBack(ctx.typerState) - override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(using Context): Unit = () override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(using Context): List[Tree] = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d84f9ce92d82..6a40e5dd7bc1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2800,55 +2800,6 @@ class Typer extends Namer case _ => false } - /** Try to rename `tpt` to a type `T` and typecheck `new T` with given expected type `pt`. - * The operation is called from either `adapt` or `typedApply`. `adapt` gets to call `tryNew` - * for calls `p.C(..)` if there is a value `p.C`. `typedApply` calls `tryNew` as a fallback - * in case typing `p.C` fails since there is no value with path `p.C`. The call from `adapt` - * is more efficient since it re-uses the prefix `p` in typed form. - */ - def tryNew[T >: Untyped <: Type] - (treesInst: Instance[T])(tree: Trees.Tree[T], pt: Type, fallBack: TyperState => Tree)(using Context): Tree = { - - def tryWithType(tpt: untpd.Tree): Tree = - tryEither { - val tycon = typed(tpt) - if (summon[Context].reporter.hasErrors) - EmptyTree // signal that we should return the error in fallBack - else { - def recur(tpt: Tree, pt: Type): Tree = pt.revealIgnored match { - case PolyProto(targs, pt1) if !targs.exists(_.isInstanceOf[NamedArg]) => - // Applications with named arguments cannot be converted, since new expressions - // don't accept named arguments - IntegratedTypeArgs(recur(AppliedTypeTree(tpt, targs), pt1)) - case _ => - typed(untpd.Select(untpd.New(untpd.TypedSplice(tpt)), nme.CONSTRUCTOR), pt) - } - recur(tycon, pt) - .showing(i"try new $tree -> $result", typr) - } - } { (nu, nuState) => - if (nu.isEmpty) fallBack(nuState) - else { - // we found a type constructor, signal the error in its application instead of the original one - nuState.commit() - nu - } - } - - tree match { - case Ident(name) => - tryWithType(cpy.Ident(tree)(name.toTypeName)) - case Select(qual, name) => - val qual1 = treesInst match { - case `tpd` => untpd.TypedSplice(qual) - case `untpd` => qual - } - tryWithType(cpy.Select(tree)(qual1, name.toTypeName)) - case _ => - fallBack(ctx.typerState) - } - } - /** Potentially add apply node or implicit conversions. Before trying either, * if the function is applied to an empty parameter list (), we try * @@ -2885,10 +2836,7 @@ class Typer extends Namer } def tryImplicit(fallBack: => Tree) = - tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked) - .getOrElse( - if Config.addConstructorProxies then fallBack - else tryNew(tpd)(tree, pt, _ => fallBack)) + tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked).getOrElse(fallBack) if (ctx.mode.is(Mode.SynthesizeExtMethodReceiver)) // Suppress insertion of apply or implicit conversion on extension method receiver @@ -3660,10 +3608,8 @@ class Typer extends Namer adaptOverloaded(ref) } case poly: PolyType if !(ctx.mode is Mode.Type) => - if tree.symbol.isAllOf(ApplyProxyFlags) && Config.addConstructorProxies then - newExpr - else if pt.isInstanceOf[PolyProto] then - tree + if tree.symbol.isAllOf(ApplyProxyFlags) then newExpr + else if pt.isInstanceOf[PolyProto] then tree else var typeArgs = tree match case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo.map(TypeTree) @@ -3676,7 +3622,7 @@ class Typer extends Namer readaptSimplified(handleStructural(tree)) else pt match { case pt: FunProto => - if tree.symbol.isAllOf(ApplyProxyFlags) && Config.addConstructorProxies then newExpr + if tree.symbol.isAllOf(ApplyProxyFlags) then newExpr else adaptToArgs(wtp, pt) case pt: PolyProto => tree match { From aa1795e71253f95e89f2151acaada8933903d7cc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 17:00:24 +0100 Subject: [PATCH 06/10] Update doc page --- .../creator-applications.md | 47 ++++++++++--------- docs/sidebar.yml | 2 +- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/docs/docs/reference/other-new-features/creator-applications.md b/docs/docs/reference/other-new-features/creator-applications.md index 482e858ff6ed..4035f12d0116 100644 --- a/docs/docs/reference/other-new-features/creator-applications.md +++ b/docs/docs/reference/other-new-features/creator-applications.md @@ -1,10 +1,11 @@ --- layout: doc-page -title: "Creator Applications" +title: "Universal Apply Methods" --- -Creator applications allow using simple function call syntax to create instances -of a class, even if there is no apply method implemented. Example: +Scala case classes generate apply methods, so that values of case classes can be created using simple function application, without needing to write `new`. + +Scala 3 generalizes this scheme to all concrete classes. Example: ```scala class StringBuilder(s: String) { def this() = this("") @@ -13,31 +14,33 @@ class StringBuilder(s: String) { StringBuilder("abc") // same as new StringBuilder("abc") StringBuilder() // same as new StringBuilder() ``` -Creator applications generalize a functionality provided so far only for case classes, but the mechanism how this is achieved is different. Instead of generating an apply method, the compiler adds a new possible interpretation to a function call `f(args)`. The previous rules are: - -Given a function call `f(args)`, +This works since a companion object with two apply methods +is generated together with the class. The object looks like this: +```scala +object StringBuilder { + inline def apply(s: String): StringBuilder = new StringBuilder(s) + inline def apply(): StringBuilder = new StringBuilder() +} +``` +The synthetic object `StringBuilder` and its `apply` methods are called _constructor proxies_. +Constructor proxies are generated even for Java classes and classes coming from Scala 2. +The precise rules are as follows: - - if `f` is a method applicable to `args`, typecheck `f(args)` unchanged, - - otherwise, if `f` has an `apply` method applicable to `args` as a member, continue with `f.apply(args)`, - - otherwise, if `f` is of the form `p.m` and there is an implicit conversion `c` applicable to `p` so that `c(p).m` is applicable to `args`, continue with `c(p).m(args)` + 1. A constructor proxy companion object `object C` is created for a concrete class `C`, provided the class does not have already a companion, and there is also no other value or method named `C` defined or inherited in the scope where `C` is defined. -There's now a fourth rule following these rules: + 2. Constructor proxy `apply` methods are generated for a concrete class provided - - otherwise, if `f` is syntactically a stable identifier, and `new f` where `f` is interpreted as a type identifier is applicable to `args`, continue with `new f(args)`. + - the class has a companion object (which might have been generated in step 1), and + - that companion object does not already define a member named `apply`. - Analogously, the possible interpretations of a function call with type arguments `f[targs]` are augmented with the following interpretation as a final fallback: + Each generated `apply` method forwards to one constructor of the class. It has the + same type and value parameters as the constructor. - - if `f` is syntactically a stable identifier, and `new f[targs]` where `f` is interpreted as a type identifier is well-typed, continue with `new f[targs]`. +Constructor proxy companions cannot be used as values by themselves. A proxy companion object must be selected with `apply` (or be applied to arguments, in which case the `apply` is implicitly inserted). +Constructor proxies are also not allowed to shadow normal definitions. That is, +if an identifier resolves to a constructor proxy, and the same identifier is also +defined or imported in some other scope, an ambiguity is reported. ### Motivation Leaving out `new` hides an implementation detail and makes code more pleasant to read. Even though it requires a new rule, it will likely increase the perceived regularity of the language, since case classes already provide function call creation syntax (and are often defined for this reason alone). - -### Discussion - -An alternative design would auto-generate `apply` methods for normal classes, in the same way it is done now for case classes. This design was tried but abandoned since it -caused numerous problems, including - - - overloading ambiguities - - overriding errors - - shadowing of user-defined `apply` methods by more specific auto-generated ones. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 5fccddd313d6..4cde10a1b4ed 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -93,7 +93,7 @@ sidebar: url: docs/reference/other-new-features/trait-parameters.html - title: Transparent Traits url: docs/reference/other-new-features/transparent-traits.html - - title: Creator Applications + - title: Universal Applies url: docs/reference/other-new-features/creator-applications.html - title: Export Clauses url: docs/reference/other-new-features/export.html From 9a42d09006c6f157ce896aeb0292f9c9ebff4fa8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 17:53:24 +0100 Subject: [PATCH 07/10] Enforce restriction that constructor proxies cannot be values --- .../dotty/tools/dotc/transform/PostTyper.scala | 6 ++++++ tests/neg/constructor-proxy-values.scala | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/neg/constructor-proxy-values.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index a0a59bc57887..63e6dcde4dfe 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -250,9 +250,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase } } + def checkNoConstructorProxy(tree: Tree)(using Context): Unit = + if tree.symbol.is(ConstructorProxy) then + report.error(em"constructor proxy ${tree.symbol} cannot be used as a value", tree.srcPos) + override def transform(tree: Tree)(using Context): Tree = try tree match { case tree: Ident if !tree.isType => + checkNoConstructorProxy(tree) tree.tpe match { case tpe: ThisType => This(tpe.cls).withSpan(tree.span) case _ => tree @@ -263,6 +268,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase withMode(Mode.Type)(super.transform(tree)) } else + checkNoConstructorProxy(tree) transformSelect(tree, Nil) case tree: Apply => val methType = tree.fun.tpe.widen diff --git a/tests/neg/constructor-proxy-values.scala b/tests/neg/constructor-proxy-values.scala new file mode 100644 index 000000000000..7bb4d5e8a454 --- /dev/null +++ b/tests/neg/constructor-proxy-values.scala @@ -0,0 +1,16 @@ + +object Test extends App { + class A { + class A22(s: String) { + def run = s + } + } + val a = A() + val x = a.A22("x") // OK + val x2 = a.A22.apply("X") // OK + val x3 = a.A22.apply(_) // OK + val y = a.A22 // error: Cannot be used as value + val z = a.A22.toString // error: Cannot be used as value + val u = A // error: Cannot be used as value + println(y) +} \ No newline at end of file From 6aafaf73bf8d02cce229d9d42a31a2a8dad978c4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 19:10:38 +0100 Subject: [PATCH 08/10] Enforce no shadowing restriction --- .../dotty/tools/dotc/ast/MainProxies.scala | 2 +- .../dotty/tools/dotc/core/ContextOps.scala | 29 +++++++++--------- .../src/dotty/tools/dotc/typer/Typer.scala | 30 ++++++++++++++----- tests/neg/constructor-proxy-shadowing.scala | 10 +++++++ tests/pos/t522.scala | 4 +-- 5 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 tests/neg/constructor-proxy-shadowing.scala diff --git a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala index bbac310470c1..9facdc08eff7 100644 --- a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala +++ b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala @@ -40,7 +40,7 @@ object MainProxies { } private def checkNoShadowing(mainFun: Symbol)(using Context) = - val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, mainFun).typeSymbol + val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, mainFun).typeSymbol if cls.exists && cls.owner != ctx.owner then report.warning( i"""The class `${ctx.printer.fullNameString(mainFun)}` generated from `@main` will shadow the existing ${cls.showLocated}. diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 3d3abf2ada55..34956d9294c9 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -25,21 +25,22 @@ object ContextOps: /** The denotation with the given `name` and all `required` flags in current context */ - def denotNamed(name: Name, required: FlagSet = EmptyFlags): Denotation = inContext(ctx) { - if (ctx.owner.isClass) - if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self - if (ctx.scope.size == 1) { - val elem = ctx.scope.lastEntry - if (elem.name == name) return elem.sym.denot // return self + def denotNamed(name: Name, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation = + inContext(ctx) { + if (ctx.owner.isClass) + if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self + if (ctx.scope.size == 1) { + val elem = ctx.scope.lastEntry + if (elem.name == name) return elem.sym.denot // return self + } + val pre = ctx.owner.thisType + pre.findMember(name, pre, required, excluded) } - val pre = ctx.owner.thisType - pre.findMember(name, pre, required, EmptyFlags) - } - else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext. - ctx.owner.findMember(name, ctx.owner.thisType, required, EmptyFlags) - else - ctx.scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix) - } + else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext. + ctx.owner.findMember(name, ctx.owner.thisType, required, excluded) + else + ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix) + } /** A fresh local context with given tree and owner. * Owner might not exist (can happen for self valdefs), in which case diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6a40e5dd7bc1..adb76201cbf2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -135,12 +135,16 @@ class Typer extends Namer * @param name the name of the identifier * @param pt the expected type * @param required flags the result's symbol must have + * @param excluded flags the result's symbol must not have * @param pos indicates position to use for error reporting */ - def findRef(name: Name, pt: Type, required: FlagSet, pos: SrcPos)(using Context): Type = { + def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos)(using Context): Type = { val refctx = ctx val noImports = ctx.mode.is(Mode.InPackageClauseName) - def fail(msg: Message) = report.error(msg, pos) + def suppressErrors = excluded.is(ConstructorProxy) + // when searching for references shadowed by a constructor proxy, do not report errors + def fail(msg: Message) = + if !suppressErrors then report.error(msg, pos) /** A symbol qualifies if it really exists and is not a package class. * In addition, if we are in a constructor of a pattern, we ignore all definitions @@ -204,7 +208,7 @@ class Typer extends Namer imp.sym.info match case ImportType(expr) => val pre = expr.tpe - var denot = pre.memberBasedOnFlags(name, required, EmptyFlags) + var denot = pre.memberBasedOnFlags(name, required, excluded) .accessibleFrom(pre)(using refctx) // Pass refctx so that any errors are reported in the context of the // reference instead of the context of the import scope @@ -332,7 +336,7 @@ class Typer extends Namer val scope = if owner.isClass then owner.info.decls else outer.scope if scope.lookup(name).exists then val symsMatch = scope.lookupAll(name).exists(denot.containsSym) - if !symsMatch then + if !symsMatch && !suppressErrors then report.errorOrMigrationWarning( AmbiguousReference(name, Definition, Inheritance, prevCtx)(using outer), pos) @@ -344,7 +348,7 @@ class Typer extends Namer checkNoOuterDefs(denot, outer, prevCtx) if isNewDefScope then - val defDenot = ctx.denotNamed(name, required) + val defDenot = ctx.denotNamed(name, required, excluded) if (qualifies(defDenot)) { val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType @@ -462,7 +466,7 @@ class Typer extends Namer unimported = Set.empty foundUnderScala2 = NoType try - val found = findRef(name, pt, EmptyFlags, tree.srcPos) + val found = findRef(name, pt, EmptyFlags, EmptyFlags, tree.srcPos) if foundUnderScala2.exists && !(foundUnderScala2 =:= found) then report.migrationWarning( ex"""Name resolution will change. @@ -474,7 +478,17 @@ class Typer extends Namer unimported = saved1 foundUnderScala2 = saved2 + def checkNotShadowed(ownType: Type) = ownType match + case ownType: TermRef if ownType.symbol.is(ConstructorProxy) => + val shadowed = findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos) + if shadowed.exists then + report.error( + em"""Reference to creator proxy for ${ownType.symbol.companionClass.showLocated} + |shadows outer reference to ${shadowed.termSymbol.showLocated}""", tree.srcPos) + case _ => + def setType(ownType: Type): Tree = + checkNotShadowed(ownType) val tree1 = ownType match case ownType: NamedType if !prefixIsElidable(ownType) => ref(ownType).withSpan(tree.span) @@ -3484,10 +3498,10 @@ class Typer extends Namer case selProto @ SelectionProto(selName: TermName, mbrType, _, _) => def tryExtension(using Context): Tree = try - findRef(selName, WildcardType, ExtensionMethod, tree.srcPos) match + findRef(selName, WildcardType, ExtensionMethod, EmptyFlags, tree.srcPos) match case ref: TermRef => extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType) - case _ => findRef(selProto.extensionName, WildcardType, ExtensionMethod, tree.srcPos) match + case _ => findRef(selProto.extensionName, WildcardType, ExtensionMethod, EmptyFlags, tree.srcPos) match case ref: TermRef => extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType) case _ => EmptyTree diff --git a/tests/neg/constructor-proxy-shadowing.scala b/tests/neg/constructor-proxy-shadowing.scala new file mode 100644 index 000000000000..857ef986cb79 --- /dev/null +++ b/tests/neg/constructor-proxy-shadowing.scala @@ -0,0 +1,10 @@ + +object Test extends App { + def A22(s: String): String = s + class A { + class A22(s: String) { + def run = s + } + val x = A22("") // error: shadowing + } +} \ No newline at end of file diff --git a/tests/pos/t522.scala b/tests/pos/t522.scala index e6eb25b6c330..5798b7ee398b 100644 --- a/tests/pos/t522.scala +++ b/tests/pos/t522.scala @@ -1,9 +1,9 @@ package imptwice -class foo(s: String); +abstract class foo(s: String); object Util { - def foo(s: String) = new foo(s) + def foo(s: String) = new foo(s) {} } import imptwice.Util._ From c5c0d315bb972884183c47f86847396ed17308e3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 20:28:55 +0100 Subject: [PATCH 09/10] Adapt own code to shadowing restriction --- compiler/test/dotty/tools/compilerSupport.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/compilerSupport.scala b/compiler/test/dotty/tools/compilerSupport.scala index 97c3c8c6fe46..487a10bd216c 100644 --- a/compiler/test/dotty/tools/compilerSupport.scala +++ b/compiler/test/dotty/tools/compilerSupport.scala @@ -18,7 +18,7 @@ import dotc.core.Comments.{ContextDoc, ContextDocstrings} * issues involving retrieving symbols defined in a previous run. */ def inCompilerContext[T](classpath: String, separateRun: Boolean = true, scalaSources: String*)(op: Context ?=> T): T = - val compiler = Compiler() + val compiler = new Compiler() val rootCtx = initCtx(classpath) val firstRun = compiler.newRun(using rootCtx) firstRun.compileFromStrings(scalaSources.toList) From 6a93525058c0cbea018fc356ed38551147ede5a2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Dec 2020 21:44:01 +0100 Subject: [PATCH 10/10] Avoid shadowing error in shapeless This was interesting. It looks like we wanted the creator proxy since there was an explicit import. But we resolved to the outer apply before. Now we see that there's a conflict since we get the shadowing error. --- community-build/community-projects/shapeless | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index 9a9b53f8d388..de443fc103e3 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit 9a9b53f8d388028b2d2922d1fca951cad68e5516 +Subproject commit de443fc103e3390f7dec3f34eb995d82e46c8cb8