From e467a03c993b40ba4dc99c7ed0154f9e81b26de0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 9 Sep 2021 19:32:01 +0200 Subject: [PATCH 1/4] Add map operation to SimpleIdentitySet --- .../src/dotty/tools/dotc/util/SimpleIdentitySet.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala index ffca320d53d3..45ee3652fe16 100644 --- a/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala +++ b/compiler/src/dotty/tools/dotc/util/SimpleIdentitySet.scala @@ -12,6 +12,7 @@ abstract class SimpleIdentitySet[+Elem <: AnyRef] { def contains[E >: Elem <: AnyRef](x: E): Boolean def foreach(f: Elem => Unit): Unit def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A def toList: List[Elem] @@ -55,6 +56,7 @@ object SimpleIdentitySet { def contains[E <: AnyRef](x: E): Boolean = false def foreach(f: Nothing => Unit): Unit = () def exists[E <: AnyRef](p: E => Boolean): Boolean = false + def map[B <: AnyRef](f: Nothing => B): SimpleIdentitySet[B] = empty def /: [A, E <: AnyRef](z: A)(f: (A, E) => A): A = z def toList = Nil } @@ -69,6 +71,8 @@ object SimpleIdentitySet { def foreach(f: Elem => Unit): Unit = f(x0.asInstanceOf[Elem]) def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = p(x0.asInstanceOf[E]) + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = + Set1(f(x0.asInstanceOf[Elem])) def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = f(z, x0.asInstanceOf[E]) def toList = x0.asInstanceOf[Elem] :: Nil @@ -86,6 +90,8 @@ object SimpleIdentitySet { def foreach(f: Elem => Unit): Unit = { f(x0.asInstanceOf[Elem]); f(x1.asInstanceOf[Elem]) } def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E]) + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = + Set2(f(x0.asInstanceOf[Elem]), f(x1.asInstanceOf[Elem])) def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]) def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: Nil @@ -114,6 +120,8 @@ object SimpleIdentitySet { } def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = p(x0.asInstanceOf[E]) || p(x1.asInstanceOf[E]) || p(x2.asInstanceOf[E]) + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = + Set3(f(x0.asInstanceOf[Elem]), f(x1.asInstanceOf[Elem]), f(x2.asInstanceOf[Elem])) def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = f(f(f(z, x0.asInstanceOf[E]), x1.asInstanceOf[E]), x2.asInstanceOf[E]) def toList = x0.asInstanceOf[Elem] :: x1.asInstanceOf[Elem] :: x2.asInstanceOf[Elem] :: Nil @@ -156,6 +164,8 @@ object SimpleIdentitySet { } def exists[E >: Elem <: AnyRef](p: E => Boolean): Boolean = xs.asInstanceOf[Array[E]].exists(p) + def map[B <: AnyRef](f: Elem => B): SimpleIdentitySet[B] = + SetN(xs.map(x => f(x.asInstanceOf[Elem]).asInstanceOf[AnyRef])) def /: [A, E >: Elem <: AnyRef](z: A)(f: (A, E) => A): A = xs.asInstanceOf[Array[E]].foldLeft(z)(f) def toList: List[Elem] = { From f2cef0b472474bea43888d8a43095fab105df0ed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 15 Sep 2021 10:27:24 +0200 Subject: [PATCH 2/4] Keep erased implicit args --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index c7e02a5c6837..048395e8dffa 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -289,7 +289,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase tree.fun, tree.args.mapConserve(arg => if (methType.isImplicitMethod && arg.span.isSynthetic) - PruneErasedDefs.trivialErasedTree(arg) + arg match + case _: RefTree | _: Apply | _: TypeApply if arg.symbol.is(Erased) => + dropInlines.transform(arg) + case _ => + PruneErasedDefs.trivialErasedTree(arg) else dropInlines.transform(arg))) else tree From 631449fc6d332751b5da6f40820d9f646cef3e31 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 Sep 2021 14:11:47 +0200 Subject: [PATCH 3/4] Mark result types of anonymous functions as InferredTypeTrees --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 6 +++++- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 40467dc5be3f..7e00972f354d 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -458,7 +458,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def AppliedTypeTree(tpt: Tree, arg: Tree)(implicit src: SourceFile): AppliedTypeTree = AppliedTypeTree(tpt, arg :: Nil) - def TypeTree(tpe: Type)(using Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe)) + def TypeTree(tpe: Type)(using Context): TypedSplice = + TypedSplice(TypeTree().withTypeUnchecked(tpe)) + + def InferredTypeTree(tpe: Type)(using Context): TypedSplice = + TypedSplice(new InferredTypeTree().withTypeUnchecked(tpe)) def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8883b492e2d9..94a14cd801f4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1134,8 +1134,8 @@ class Typer extends Namer */ private def decomposeProtoFunction(pt: Type, defaultArity: Int, pos: SrcPos)(using Context): (List[Type], untpd.Tree) = { def typeTree(tp: Type) = tp match { - case _: WildcardType => untpd.TypeTree() - case _ => untpd.TypeTree(tp) + case _: WildcardType => new untpd.InferredTypeTree() + case _ => untpd.InferredTypeTree(tp) } def interpolateWildcards = new TypeMap { def apply(t: Type): Type = t match From bbaff2b15dc4abe75635f1898754b36670070f85 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 27 Sep 2021 11:57:04 +0200 Subject: [PATCH 4/4] RefChecks refactoring Break out the logic that visits all overriding pairs with a given base and tests them with a given check function. We need to re-use that logic in the rechecker. --- .../dotty/tools/dotc/typer/RefChecks.scala | 93 ++++++++++--------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 4a77573a8386..615e2915f6f8 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -192,6 +192,54 @@ object RefChecks { // Override checking ------------------------------------------------------------ + /** A class for checking all overriding pairs of `class` with a given check function */ + class OverridingPairsChecker(clazz: ClassSymbol, self: Type)(using Context) extends OverridingPairs.Cursor(clazz): + + override def matches(sym1: Symbol, sym2: Symbol): Boolean = + isOverridingPair(sym1, sym2, self) + + private def inLinearizationOrder(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean = + val owner1 = sym1.owner + val owner2 = sym2.owner + def precedesIn(bcs: List[ClassSymbol]): Boolean = (bcs: @unchecked) match + case bc :: bcs1 => + if owner1 eq bc then true + else if owner2 eq bc then false + else precedesIn(bcs1) + case _ => + false + precedesIn(parent.asClass.baseClasses) + + // We can exclude pairs safely from checking only under two additional conditions + // - their signatures also match in the parent class. + // See neg/i12828.scala for an example where this matters. + // - They overriding/overridden appear in linearization order. + // See neg/i5094.scala for an example where this matters. + override def canBeHandledByParent(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean = + isOverridingPair(sym1, sym2, parent.thisType) + .showing(i"already handled ${sym1.showLocated}: ${sym1.asSeenFrom(parent.thisType).signature}, ${sym2.showLocated}: ${sym2.asSeenFrom(parent.thisType).signature} = $result", refcheck) + && inLinearizationOrder(sym1, sym2, parent) + + def checkAll(checkOverride: (Symbol, Symbol) => Unit) = + while hasNext do + checkOverride(overriding, overridden) + next() + + // The OverridingPairs cursor does assume that concrete overrides abstract + // We have to check separately for an abstract definition in a subclass that + // overrides a concrete definition in a superclass. E.g. the following (inspired + // from neg/i11130.scala) needs to be rejected as well: + // + // class A { type T = B } + // class B extends A { override type T } + for dcl <- clazz.info.decls.iterator do + if dcl.is(Deferred) then + for other <- dcl.allOverriddenSymbols do + if !other.is(Deferred) then + checkOverride(dcl, other) + end checkAll + end OverridingPairsChecker + /** 1. Check all members of class `clazz` for overriding conditions. * That is for overriding member M and overridden member O: * @@ -469,50 +517,7 @@ object RefChecks { }*/ } - val opc = new OverridingPairs.Cursor(clazz): - override def matches(sym1: Symbol, sym2: Symbol): Boolean = - isOverridingPair(sym1, sym2, self) - - private def inLinearizationOrder(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean = - val owner1 = sym1.owner - val owner2 = sym2.owner - def precedesIn(bcs: List[ClassSymbol]): Boolean = (bcs: @unchecked) match - case bc :: bcs1 => - if owner1 eq bc then true - else if owner2 eq bc then false - else precedesIn(bcs1) - case _ => - false - precedesIn(parent.asClass.baseClasses) - - // We can exclude pairs safely from checking only under two additional conditions - // - their signatures also match in the parent class. - // See neg/i12828.scala for an example where this matters. - // - They overriding/overridden appear in linearization order. - // See neg/i5094.scala for an example where this matters. - override def canBeHandledByParent(sym1: Symbol, sym2: Symbol, parent: Symbol): Boolean = - isOverridingPair(sym1, sym2, parent.thisType) - .showing(i"already handled ${sym1.showLocated}: ${sym1.asSeenFrom(parent.thisType).signature}, ${sym2.showLocated}: ${sym2.asSeenFrom(parent.thisType).signature} = $result", refcheck) - && inLinearizationOrder(sym1, sym2, parent) - end opc - - while opc.hasNext do - checkOverride(opc.overriding, opc.overridden) - opc.next() - - // The OverridingPairs cursor does assume that concrete overrides abstract - // We have to check separately for an abstract definition in a subclass that - // overrides a concrete definition in a superclass. E.g. the following (inspired - // from neg/i11130.scala) needs to be rejected as well: - // - // class A { type T = B } - // class B extends A { override type T } - for dcl <- clazz.info.decls.iterator do - if dcl.is(Deferred) then - for other <- dcl.allOverriddenSymbols do - if !other.is(Deferred) then - checkOverride(dcl, other) - + OverridingPairsChecker(clazz, self).checkAll(checkOverride) printMixinOverrideErrors() // Verifying a concrete class has nothing unimplemented.