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 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/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/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/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/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 24f151a7cfbe..a9da2c31c176 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -300,14 +300,10 @@ 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) + 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/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..5a52cb54da5f 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,97 @@ 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 = + 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) + 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 0e6bb7342bff..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, @@ -1609,7 +1610,7 @@ object SymDenotations { private def baseTypeCache(using Context): BaseTypeMap = { if !currentHasSameBaseTypesAs(myBaseTypeCachePeriod) then - myBaseTypeCache = BaseTypeMap() + myBaseTypeCache = new BaseTypeMap() myBaseTypeCachePeriod = ctx.period myBaseTypeCache } @@ -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 b6325ae645d7..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? */ @@ -496,6 +503,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/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..63e6dcde4dfe 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) @@ -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/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..10f770f6ce42 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} @@ -844,22 +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 = - 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. @@ -870,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. @@ -1075,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 => @@ -1473,7 +1457,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 +1619,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 5aa25a6272eb..64568cd1b5bc 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. @@ -563,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) @@ -597,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 } @@ -1186,7 +1206,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) @@ -1202,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/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 99cb703ba49d..adb76201cbf2 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 { @@ -134,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 @@ -203,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 @@ -331,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) @@ -343,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 @@ -461,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. @@ -473,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) @@ -2199,8 +2214,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 +2725,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 } @@ -2799,55 +2814,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 * @@ -2884,8 +2850,7 @@ class Typer extends Namer } def tryImplicit(fallBack: => Tree) = - tryInsertImplicitOnQualifier(tree, pt.withContext(ctx), locked) - .getOrElse(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 @@ -3315,49 +3280,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 @@ -3532,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 @@ -3618,6 +3584,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 +3622,22 @@ 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) 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) then newExpr + else adaptToArgs(wtp, pt) case pt: PolyProto => tree match { case _: IntegratedTypeArgs => tree 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) 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 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/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: 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/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/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/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 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 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/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._ 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-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 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.