From f0cdff58f331ac8a4faa2f21d59b7312872193b6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 8 Jul 2019 21:27:46 +0200 Subject: [PATCH 1/7] Handle multiple `val _ = ...` definitions These need to be expanded to pattern definitions, not ValDefs, otherwise duplicate definition errors result. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- tests/pos/wildcardDefs.scala | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 tests/pos/wildcardDefs.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 22c4daab4e87..35e62d523894 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1616,7 +1616,7 @@ object desugar { */ private object IdPattern { def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match { - case id: Ident => Some(id, TypeTree()) + case id: Ident if id.name != nme.WILDCARD => Some(id, TypeTree()) case Typed(id: Ident, tpt) => Some((id, tpt)) case _ => None } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f3a067ec13f1..1d6f048df18f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2574,7 +2574,7 @@ object Parsers { } } else EmptyTree lhs match { - case (id @ Ident(name: TermName)) :: Nil => + case (id @ Ident(name: TermName)) :: Nil if name != nme.WILDCARD => val vdef = ValDef(name, tpt, rhs) if (isBackquoted(id)) vdef.pushAttachment(Backquoted, ()) finalizeDef(vdef, mods, start) diff --git a/tests/pos/wildcardDefs.scala b/tests/pos/wildcardDefs.scala new file mode 100644 index 000000000000..3a6d2c1aa696 --- /dev/null +++ b/tests/pos/wildcardDefs.scala @@ -0,0 +1,4 @@ +object Test { + val _ = 2 + val _ = 3 +} \ No newline at end of file From 08eea86d03d36ddb06ce0eef6eb9fa6b22251fe7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 Jul 2019 14:06:08 +0200 Subject: [PATCH 2/7] Specialize types of inline values Specialize types of ibline values even if a wider type was gven in an inherited definition. This generalizes a previous scheme for final vals. --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 11 ++--------- tests/pos/givenFallback.scala | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 tests/pos/givenFallback.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9f2a64d555f8..7831f2358e4a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1323,15 +1323,8 @@ class Namer { typer: Typer => def cookedRhsType = deskolemize(dealiasIfUnit(widenRhs(rhsType))) def lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.span) //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType") - if (inherited.exists) { - if (sym.is(Final, butNot = Method)) { - val tp = lhsType - if (tp.isInstanceOf[ConstantType]) - tp // keep constant types that fill in for a non-constant (to be revised when inline has landed). - else inherited - } - else inherited - } + if (inherited.exists) + if (isInlineVal) lhsType else inherited else { if (sym.is(Implicit)) mdef match { diff --git a/tests/pos/givenFallback.scala b/tests/pos/givenFallback.scala new file mode 100644 index 000000000000..d9cd5c664533 --- /dev/null +++ b/tests/pos/givenFallback.scala @@ -0,0 +1,15 @@ +trait TC[T] { def x: Int; def y: Int = 0 } + +given [T] as TC[T] { + inline val x = 1 +} + +given as TC[Int] { + inline val x = 2 + inline override val y = 3 +} + +object Test extends App { + val z: 2 = the[TC[Int]].x + val _: 3 = the[TC[Int]].y +} \ No newline at end of file From be7d231a372979266a4de5402df9232f61823d93 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 10 Jul 2019 11:31:43 +0200 Subject: [PATCH 3/7] Fix Scala2 unpickler's handling of package references --- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 9b4b626655fe..d3b2dca997df 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -690,6 +690,15 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } else tp1 } + /** Read type ref, mapping a TypeRef to a package to the package's ThisType + * Packae references should be TermRefs or ThisTypes but it was observed that + * nsc sometimes pickles them as TypeRefs instead. + */ + private def readPrefix()(implicit ctx: Context): Type = readTypeRef() match { + case pre: TypeRef if pre.symbol.is(Package) => pre.symbol.thisType + case pre => pre + } + /** Read a type * * @param forceProperType is used to ease the transition to NullaryMethodTypes (commentmarker: NMT_TRANSITION) @@ -707,7 +716,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case THIStpe => readSymbolRef().thisType case SINGLEtpe => - val pre = readTypeRef() + val pre = readPrefix() val sym = readDisambiguatedSymbolRef(_.info.isParameterless) pre.select(sym) case SUPERtpe => @@ -717,7 +726,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case CONSTANTtpe => ConstantType(readConstantRef()) case TYPEREFtpe => - var pre = readTypeRef() + var pre = readPrefix() val sym = readSymbolRef() pre match { case thispre: ThisType => From aa4aee6598d524b78b5b1373639fb6df9d9dc01b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 10 Jul 2019 21:20:16 +0200 Subject: [PATCH 4/7] Fix implicit scope The previous implicit scope was hard to understand, and did not conform to spec. The new implementation is simpler to understand. It also does not conform to spec as written but there's a chance that we might change the spec to conform to it. --- .../dotty/tools/dotc/typer/Implicits.scala | 87 +++++++++++-------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 066833697f7f..f8ad376427d8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -483,28 +483,31 @@ trait ImplicitRunInfo { self: Run => val seen: mutable.Set[Type] = mutable.Set() val incomplete: mutable.Set[Type] = mutable.Set() - /** Replace every typeref that does not refer to a class by a conjunction of class types + /** Is `sym` an anchor type for which givens may exist? Anchor types are classes, + * opaque type aliases, and abstract types, but not type parameters + */ + def isAnchor(sym: Symbol) = sym.isClass && !sym.is(Package) || sym.isOpaqueAlias + + def anchors(tp: Type): List[Type] = tp match { + case tp: NamedType if isAnchor(tp.symbol) => tp :: Nil + case tp: TypeProxy => anchors(tp.superType) + case tp: AndOrType => anchors(tp.tp1) ++ anchors(tp.tp2) + case _ => Nil + } + + /** Replace every typeref that does not refer to a class by a conjunction of anchor types * that has the same implicit scope as the original typeref. The motivation for applying * this map is that it reduces the total number of types for which we need to * compute and cache the implicit scope; all variations wrt type parameters or * abstract types are eliminated. */ - object liftToClasses extends TypeMap { + object liftToAnchors extends TypeMap { override implicit protected val ctx: Context = liftingCtx override def stopAtStatic = true - private def isLiftTarget(sym: Symbol) = sym.isClass || sym.isOpaqueAlias - def apply(tp: Type) = tp match { case tp: TypeRef => - if (isLiftTarget(tp.symbol)) tp - else { - val pre = tp.prefix - def joinClass(tp: Type, cls: Symbol) = - AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner)) - val lead = if (pre eq NoPrefix) defn.AnyType else apply(pre) - (lead /: tp.parentSymbols(isLiftTarget))(joinClass) - } + ((defn.AnyType: Type) /: anchors(tp))(AndType.make(_, _)) case tp: TypeVar => apply(tp.underlying) case tp: AppliedType if !tp.tycon.typeSymbol.isClass => @@ -521,7 +524,6 @@ trait ImplicitRunInfo { self: Run => } } - // todo: compute implicits directly, without going via companionRefs? def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") { trace(i"collectCompanions($tp)", implicitsDetailed) { @@ -541,32 +543,41 @@ trait ImplicitRunInfo { self: Run => } val comps = new TermRefSet + def addCompanion(pre: Type, companion: Symbol) = + if (companion.exists && !companion.isAbsent) comps += TermRef(pre, companion) + def addCompanionNamed(pre: Type, name: TermName) = + addCompanion(pre, pre.member(name).suchThat(_.is(Module)).symbol) + + def addPath(pre: Type): Unit = pre.dealias match { + case pre: ThisType if pre.cls.is(Module) && pre.cls.isStaticOwner => + addPath(pre.cls.sourceModule.termRef) + case pre: TermRef => + if (pre.symbol.is(Package)) { + if (ctx.scala2Mode) { + addCompanionNamed(pre, nme.PACKAGE) + addPath(pre.prefix) + } + } + else { + comps += pre + addPath(pre.prefix) + } + case _ => + } tp match { - case tp: NamedType => - if (!tp.symbol.is(Package) || ctx.scala2Mode) { - // Don't consider implicits in package prefixes unless under -language:Scala2 + case tp: TermRef => + addPath(tp.prefix) + case tp: TypeRef => + val sym = tp.symbol + if (isAnchor(sym)) { val pre = tp.prefix - comps ++= iscopeRefs(pre) - def addRef(companion: TermRef): Unit = { - val compSym = companion.symbol - if (compSym is Package) { - assert(ctx.scala2Mode) - addRef(companion.select(nme.PACKAGE)) - } - else if (compSym.exists) - comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] - } - def addCompanionOf(sym: Symbol) = { - val companion = sym.companionModule - if (companion.exists) addRef(companion.termRef) - } - def addClassScope(cls: ClassSymbol): Unit = { - addCompanionOf(cls) - for (parent <- cls.classParents; ref <- iscopeRefs(tp.baseType(parent.classSymbol))) - addRef(ref) - } - tp.classSymbols(liftingCtx).foreach(addClassScope) + addPath(pre) + if (sym.is(Module)) addCompanion(pre, sym.sourceModule) + else if (sym.isClass) addCompanion(pre, sym.companionModule) + else addCompanionNamed(pre, sym.name.stripModuleClassSuffix.toTermName) } + val superAnchors = if (sym.isClass) tp.parents else anchors(tp.superType) + for (anchor <- superAnchors) comps ++= iscopeRefs(anchor) case _ => for (part <- tp.namedPartsWith(_.isType)) comps ++= iscopeRefs(part) } @@ -575,12 +586,12 @@ trait ImplicitRunInfo { self: Run => } /** The implicit scope of type `tp` - * @param isLifted Type `tp` is the result of a `liftToClasses` application + * @param isLifted Type `tp` is the result of a `liftToAnchors` application */ def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = { val canCache = Config.cacheImplicitScopes && tp.hash != NotCached && !tp.isProvisional def computeIScope() = { - val liftedTp = if (isLifted) tp else liftToClasses(tp) + val liftedTp = if (isLifted) tp else liftToAnchors(tp) val refs = if (liftedTp ne tp) { implicitsDetailed.println(i"lifted of $tp = $liftedTp") From 352ff58f5ca5cca61049b6efd92ea349c5aafc02 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jul 2019 10:57:47 +0200 Subject: [PATCH 5/7] New spec with aligned code --- .../dotty/tools/dotc/typer/Implicits.scala | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f8ad376427d8..c8cf056c89ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -471,7 +471,22 @@ trait ImplicitRunInfo { self: Run => private val EmptyTermRefSet = new TermRefSet()(NoContext) - /** The implicit scope of a type `tp` + /** The implicit scope of a type `tp`, defined as follows: + * + * The implicit scope of a type `tp` is the smallest set S of object references (i.e. TermRefs + * with Module symbol) such that + * + * - If `tp` is a class reference, S contains a reference to the companion object of the class, + * if it exists, as well as the implicit scopes of all of `tp`'s parent class references. + * - If `tp` is an opaque type alias `p.A` of type `tp'`, S contains a reference to an object `A` defined in the + * same scope as the opaque type, if it exists, as well as the implicit scope of `tp'`. + * - If `tp` is a reference `p.T` to a class or opaque type alias, S also contains all object references + * on the prefix path `p`. Under Scala-2 mode, package objects of package references on `p` also + * count towards the implicit scope. + * - If `tp` is an alias of `tp'`, S contains the implicit scope of `tp'`. + * - If `tp` is some other type, its implicit scope is the union of the implicit scopes of + * its parts (parts defined as in the spec). + * * @param liftingCtx A context to be used when computing the class symbols of * a type. Types may contain type variables with their instances * recorded in the current context. To find out the instance of @@ -545,8 +560,6 @@ trait ImplicitRunInfo { self: Run => val comps = new TermRefSet def addCompanion(pre: Type, companion: Symbol) = if (companion.exists && !companion.isAbsent) comps += TermRef(pre, companion) - def addCompanionNamed(pre: Type, name: TermName) = - addCompanion(pre, pre.member(name).suchThat(_.is(Module)).symbol) def addPath(pre: Type): Unit = pre.dealias match { case pre: ThisType if pre.cls.is(Module) && pre.cls.isStaticOwner => @@ -554,7 +567,7 @@ trait ImplicitRunInfo { self: Run => case pre: TermRef => if (pre.symbol.is(Package)) { if (ctx.scala2Mode) { - addCompanionNamed(pre, nme.PACKAGE) + addCompanion(pre, pre.member(nme.PACKAGE).symbol) addPath(pre.prefix) } } @@ -564,21 +577,21 @@ trait ImplicitRunInfo { self: Run => } case _ => } - tp match { - case tp: TermRef => - addPath(tp.prefix) + tp.dealias match { case tp: TypeRef => val sym = tp.symbol if (isAnchor(sym)) { val pre = tp.prefix addPath(pre) - if (sym.is(Module)) addCompanion(pre, sym.sourceModule) - else if (sym.isClass) addCompanion(pre, sym.companionModule) - else addCompanionNamed(pre, sym.name.stripModuleClassSuffix.toTermName) + if (sym.isClass) addCompanion(pre, sym.companionModule) + else addCompanion(pre, + pre.member(sym.name.toTermName) + .suchThat(companion => companion.is(Module) && companion.owner == sym.owner) + .symbol) } val superAnchors = if (sym.isClass) tp.parents else anchors(tp.superType) for (anchor <- superAnchors) comps ++= iscopeRefs(anchor) - case _ => + case tp => for (part <- tp.namedPartsWith(_.isType)) comps ++= iscopeRefs(part) } comps From 93b4832ffa0858337fed10440e5895068f43df0a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 11 Jul 2019 11:16:14 +0200 Subject: [PATCH 6/7] Widen singleton types when computing implicit scope --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c8cf056c89ed..3f874f1c3eca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -483,7 +483,8 @@ trait ImplicitRunInfo { self: Run => * - If `tp` is a reference `p.T` to a class or opaque type alias, S also contains all object references * on the prefix path `p`. Under Scala-2 mode, package objects of package references on `p` also * count towards the implicit scope. - * - If `tp` is an alias of `tp'`, S contains the implicit scope of `tp'`. + * - If `tp` is a (non-opaque) alias of `tp'`, S contains the implicit scope of `tp'`. + * - If `tp` is a singleton type, S contains the implicit scope of its underlying type. * - If `tp` is some other type, its implicit scope is the union of the implicit scopes of * its parts (parts defined as in the spec). * @@ -520,7 +521,7 @@ trait ImplicitRunInfo { self: Run => override implicit protected val ctx: Context = liftingCtx override def stopAtStatic = true - def apply(tp: Type) = tp match { + def apply(tp: Type) = tp.widenDealias match { case tp: TypeRef => ((defn.AnyType: Type) /: anchors(tp))(AndType.make(_, _)) case tp: TypeVar => @@ -577,7 +578,7 @@ trait ImplicitRunInfo { self: Run => } case _ => } - tp.dealias match { + tp.widenDealias match { case tp: TypeRef => val sym = tp.symbol if (isAnchor(sym)) { From 76ecf13f8a5484bf5c016a782b13632b3e7ad357 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 11 Jul 2019 12:44:19 +0200 Subject: [PATCH 7/7] Disable Scalatest tests --- .../dotty/communitybuild/CommunityBuildTest.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 96be762abae9..c28b40b54835 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -103,11 +103,11 @@ class CommunityBuildTest { updateCommand = "jvm/test:update" ) - @Test def scalatest = test( - project = "scalatest", - testCommand = ";scalacticDotty/clean;scalacticTestDotty/test;scalatestTestDotty/test", - updateCommand = "scalatest/update" - ) + // @Test def scalatest = test( + // project = "scalatest", + // testCommand = ";scalacticDotty/clean;scalacticTestDotty/test;scalatestTestDotty/test", + // updateCommand = "scalatest/update" + // ) @Test def scalaXml = test( project = "scala-xml",