diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 1aa1cce1073d..d141b7488f2a 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -53,8 +53,9 @@ class Compiler { new Literalize, new GettersSetters), List(new Erasure), - List(new CapturedVars, new Constructors)/*, - List(new LambdaLift)*/ + List(new CapturedVars, + new Constructors), + List(new LambdaLift) ) var runId = 1 diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 50aaafc82279..851be76583bf 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -258,8 +258,10 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild else this(parent) case tp: TermRef => this(tp.widen) - case ThisType(_) | SuperType(_, _) => + case ThisType(_) => tp + case SuperType(thistpe, supertpe) => + SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => MethodType(Nil, Nil, this(rt)) case tp: TypeProxy => diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 33be848c1ed1..32c89c94fb51 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -79,14 +79,13 @@ final class TreeTypeMap( override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { case impl @ Template(constr, parents, self, body) => - val tmap = withMappedSyms(impl.symbol :: impl.constr.symbol :: Nil) - val parents1 = parents mapconserve transform - val (_, constr1 :: self1 :: Nil) = transformDefs(constr :: self :: Nil) - val body1 = tmap.transformStats(body) - updateDecls(constr :: body, constr1 :: body1) + val tmap = withMappedSyms(localSyms(impl :: self :: Nil)) cpy.Template(impl)( - constr1.asInstanceOf[DefDef], parents1, self1.asInstanceOf[ValDef], body1) - .withType(tmap.mapType(impl.tpe)) + constr = tmap.transformSub(constr), + parents = parents mapconserve transform, + self = tmap.transformSub(self), + body = body mapconserve tmap.transform + ).withType(tmap.mapType(impl.tpe)) case tree1 => tree1.withType(mapType(tree1.tpe)) match { case id: Ident if tpd.needsSelect(id.tpe) => @@ -160,8 +159,24 @@ final class TreeTypeMap( * and return a treemap that contains the substitution * between original and mapped symbols. */ - def withMappedSyms(syms: List[Symbol]): TreeTypeMap = { - val mapped = ctx.mapSymbols(syms, this) - withSubstitution(syms, mapped) + def withMappedSyms(syms: List[Symbol], mapAlways: Boolean = false): TreeTypeMap = + withMappedSyms(syms, ctx.mapSymbols(syms, this, mapAlways)) + + /** The tree map with the substitution between originals `syms` + * and mapped symbols `mapped`. Also goes into mapped classes + * and substitutes their declarations. + */ + def withMappedSyms(syms: List[Symbol], mapped: List[Symbol]): TreeTypeMap = { + val symsChanged = syms ne mapped + val substMap = withSubstitution(syms, mapped) + val fullMap = (substMap /: mapped.filter(_.isClass)) { (tmap, cls) => + val origDcls = cls.decls.toList + val mappedDcls = ctx.mapSymbols(origDcls, tmap) + val tmap1 = tmap.withMappedSyms(origDcls, mappedDcls) + if (symsChanged) (origDcls, mappedDcls).zipped.foreach(cls.asClass.replace) + tmap1 + } + if (symsChanged || (fullMap eq substMap)) fullMap + else withMappedSyms(syms, mapAlways = true) } } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 9b7c9cbae810..4c21fcf49229 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -9,6 +9,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols. import Denotations._, Decorators._ import config.Printers._ import typer.Mode +import collection.mutable import typer.ErrorReporting._ import scala.annotation.tailrec @@ -620,6 +621,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } acc(false, tree) } + + def filterSubTrees(f: Tree => Boolean): List[Tree] = { + val buf = new mutable.ListBuffer[Tree] + foreachSubTree { tree => if (f(tree)) buf += tree } + buf.toList + } } implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 3926be59b121..f8d7f8de557a 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -4,10 +4,12 @@ object Printers { class Printer { def println(msg: => String): Unit = System.out.println(msg) + def echo[T](msg: => String, value: T): T = { println(msg + value); value } } object noPrinter extends Printer { override def println(msg: => String): Unit = () + override def echo[T](msg: => String, value: T): T = value } val default: Printer = new Printer diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index 99af4d0cb42e..e0d7fae33735 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -100,6 +100,11 @@ object Decorators { else x1 :: xs1 } + def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match { + case Nil => z + case x :: xs1 => op(x, xs1.foldRightBN(z)(op)) + } + final def hasSameLengthAs[U](ys: List[U]): Boolean = { @tailrec def loop(xs: List[T], ys: List[U]): Boolean = if (xs.isEmpty) ys.isEmpty diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index a27dd6614fac..e5bf27eaeb00 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -331,7 +331,9 @@ object Flags { final val JavaDefined = commonFlag(30, "") /** Symbol is implemented as a Java static */ - final val Static = commonFlag(31, "") + final val JavaStatic = commonFlag(31, "") + final val JavaStaticTerm = JavaStatic.toTermFlags + final val JavaStaticType = JavaStatic.toTypeFlags /** Variable is accessed from nested function. */ final val Captured = termFlag(32, "") @@ -421,7 +423,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = commonFlags(Private, Protected, Abstract, Final, - Sealed, Case, Implicit, Override, AbsOverride, Lazy, Static) + Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic) /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = @@ -436,7 +438,7 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = AccessFlags | Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | - InSuperCall | Touched | Static | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | + InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | SelfNameOrImplClass @@ -473,7 +475,7 @@ object Flags { */ final val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | - Synthetic | ExpandedName | JavaDefined | Static | Artifact | + Synthetic | ExpandedName | JavaDefined | JavaStatic | Artifact | Erroneous | Lifted | MixedIn | Specialized /** Flags that can apply to a module val */ @@ -487,7 +489,7 @@ object Flags { /** Packages and package classes always have these flags set */ final val PackageCreationFlags = - Module | Package | Final | JavaDefined | Static + Module | Package | Final | JavaDefined /** These flags are pickled */ final val PickledFlags = flagRange(FirstFlag, FirstNotPickledFlag) @@ -562,7 +564,7 @@ object Flags { final val ProtectedLocal = allOf(Protected, Local) /** Java symbol which is `protected` and `static` */ - final val StaticProtected = allOf(JavaDefined, Protected, Static) + final val StaticProtected = allOf(JavaDefined, Protected, JavaStatic) final val AbstractFinal = allOf(Abstract, Final) final val AbstractSealed = allOf(Abstract, Sealed) diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 3f7a4cb2ca20..494a26f7e58d 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -278,6 +278,7 @@ object Scopes { if (e.sym == prev) e.sym = replacement e = lookupNextEntry(e) } + elemsCache = null } /** Lookup a symbol entry matching given name. diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ae37ab87cdeb..ae8fceeb743b 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -390,7 +390,7 @@ object SymDenotations { /** Is this denotation static (i.e. with no outer instance)? */ final def isStatic(implicit ctx: Context) = - (this is Static) || this.exists && owner.isStaticOwner + (this is JavaStatic) || this.exists && owner.isStaticOwner /** Is this a package class or module class that defines static symbols? */ final def isStaticOwner(implicit ctx: Context): Boolean = @@ -666,10 +666,16 @@ object SymDenotations { * for these definitions. */ final def enclosingClass(implicit ctx: Context): Symbol = { - def enclClass(d: SymDenotation): Symbol = - if (d.isClass || !d.exists) d.symbol else enclClass(d.owner) - val cls = enclClass(this) - if (this is InSuperCall) cls.owner.enclosingClass else cls + def enclClass(sym: Symbol, skip: Boolean): Symbol = { + def newSkip = sym.is(InSuperCall) || sym.is(JavaStaticTerm) + if (!sym.exists) + NoSymbol + else if (sym.isClass) + if (skip) enclClass(sym.owner, newSkip) else sym + else + enclClass(sym.owner, skip || newSkip) + } + enclClass(symbol, false) } final def isEffectivelyFinal(implicit ctx: Context): Boolean = { @@ -976,7 +982,7 @@ object SymDenotations { /** The type parameters of this class */ override final def typeParams(implicit ctx: Context): List[TypeSymbol] = { def computeTypeParams = { - if (ctx.erasedTypes && (symbol ne defn.ArrayClass)) Nil + if (ctx.erasedTypes || is(Module)) Nil // fast return for modules to avoid scanning package decls else if (this ne initial) initial.asSymDenotation.typeParams else decls.filter(sym => (sym is TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]] diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 0174f75cf6f6..83fb2c134e91 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -267,10 +267,10 @@ trait Symbols { this: Context => * Cross symbol references are brought over from originals to copies. * Do not copy any symbols if all attributes of all symbols stay the same. */ - def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap) = - if (originals forall (sym => + def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap, mapAlways: Boolean = false): List[Symbol] = + if (originals.forall(sym => (ttmap.mapType(sym.info) eq sym.info) && - !(ttmap.oldOwners contains sym.owner))) + !(ttmap.oldOwners contains sym.owner)) && !mapAlways) originals else { val copies: List[Symbol] = for (original <- originals) yield diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index f36572755eb8..dcd8af3b65e4 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -515,6 +515,14 @@ class TypeComparer(initctx: Context) extends DotClass { case _ => secondTry(tp1, tp2) } + case tp2: SuperType => + tp1 match { + case tp1: SuperType => + isSubType(tp1.thistpe, tp2.thistpe) && + isSameType(tp1.supertpe, tp2.supertpe) + case _ => + secondTry(tp1, tp2) + } case AndType(tp21, tp22) => isSubType(tp1, tp21) && isSubType(tp1, tp22) case ErrorType => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index d93e4eb09338..d0ddfdd28b50 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -116,6 +116,8 @@ object Types { tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) case tp: OrType => tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) + case tp: JavaArrayType => + cls == defn.ObjectClass case _ => false } @@ -408,6 +410,8 @@ object Types { goAnd(l, r) case OrType(l, r) => goOr(l, r) + case tp: JavaArrayType => + defn.ObjectType.findMember(name, pre, excluded) case ErrorType => ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name) case _ => diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala index c3850d0fd76d..c35b9ca474fd 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileConstants.scala @@ -343,7 +343,7 @@ object ClassfileConstants { case JAVA_ACC_PROTECTED => Protected case JAVA_ACC_FINAL => Final case JAVA_ACC_SYNTHETIC => Synthetic - case JAVA_ACC_STATIC => Static + case JAVA_ACC_STATIC => JavaStatic case JAVA_ACC_ABSTRACT => if (isAnnotation) EmptyFlags else if (isClass) Abstract else Deferred case JAVA_ACC_INTERFACE => if (isAnnotation) EmptyFlags else JavaInterface case _ => EmptyFlags diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index ef2b4acb2700..d2a05bf3a4dc 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -236,7 +236,7 @@ object PickleBuffer { JAVA -> JavaDefined, SYNTHETIC -> Synthetic, STABLE -> Stable, - STATIC -> Static, + STATIC -> JavaStatic, CASEACCESSOR -> CaseAccessor, DEFAULTPARAM -> (DefaultParameterized, Trait), BRIDGE -> Bridge, diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index ed853f8f1c2c..31397e08a723 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -101,7 +101,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => } def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(implicit ctx: Context): Unit = - assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") + assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") } object Erasure extends TypeTestsCasts{ @@ -183,6 +183,7 @@ object Erasure extends TypeTestsCasts{ } /** Generate a synthetic cast operation from tree.tpe to pt. + * Does not do any boxing/unboxing (this is handled upstream). */ def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { // TODO: The commented out assertion fails for tailcall/t6574.scala @@ -315,7 +316,7 @@ object Erasure extends TypeTestsCasts{ override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree) else { - ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}") + ctx.log(i"computing outer path from ${ctx.owner.ownersIterator.toList}%, % to ${tree.symbol}, encl class = ${ctx.owner.enclosingClass}") outer.path(tree.symbol) } @@ -377,10 +378,8 @@ object Erasure extends TypeTestsCasts{ EmptyTree override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = { - val statsFlatten = Trees.flatten(stats) - val stats1 = super.typedStats(statsFlatten, exprOwner) - - if (ctx.owner.isClass) stats1:::addBridges(statsFlatten, stats1)(ctx) else stats1 + val stats1 = Trees.flatten(super.typedStats(stats, exprOwner)) + if (ctx.owner.isClass) stats1 ::: addBridges(stats, stats1)(ctx) else stats1 } // this implementation doesn't check for bridge clashes with value types! diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 179f8d7124e6..d056d7e351a2 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -179,7 +179,8 @@ object ExplicitOuter { * definitions in the class to find the one with the OuterAccessor flag. */ def outerAccessor(cls: ClassSymbol)(implicit ctx: Context): Symbol = - cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor).symbol orElse + if (cls.isStatic) NoSymbol // fast return to avoid scanning package decls + else cls.info.member(outerAccName(cls)).suchThat(_ is OuterAccessor).symbol orElse cls.info.decls.find(_ is OuterAccessor).getOrElse(NoSymbol) /** Class has an outer accessor. Can be called only after phase ExplicitOuter. */ diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index a8cbb05959b6..fed47b160d9d 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -10,6 +10,8 @@ import Types._ import Constants.Constant import Contexts.Context import Symbols._ +import SymDenotations._ +import Decorators._ import scala.collection.mutable import DenotTransformers._ import typer.Checking @@ -21,6 +23,7 @@ import NameOps._ * - ensures there are companion objects for all classes except module classes * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree * - converts Select/Ident/SelectFromTypeTree nodes that refer to types to TypeTrees. + * - inserts `.package` for selections of package object members * - checks the bounds of AppliedTypeTrees * - stubs out native methods */ @@ -29,6 +32,15 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { override def phaseName = "companions" + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = tree match { + case Select(qual, _) if tree.symbol.exists => + assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe}") + case _: TypeTree => + case _: Import | _: NamedArg | _: TypTree => + assert(false, i"illegal tree: $tree") + case _ => + } + /** Reorder statements so that module classes always come after their companion classes, add missing companion classes */ private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = { val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]() @@ -96,14 +108,22 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { } override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = - normalizeType(tree) + normalizeType { + val qual = tree.qualifier + qual.symbol.moduleClass.denot match { + case pkg: PackageClassDenotation if tree.symbol.maybeOwner.isPackageObject => + cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name) + case _ => + tree + } + } override def transformSelectFromTypeTree(tree: SelectFromTypeTree)(implicit ctx: Context, info: TransformerInfo) = normalizeType(tree) override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree - case tree: NamedArg => tree.arg + case tree: NamedArg => transform(tree.arg) case AppliedTypeTree(tycon, args) => val tparams = tycon.tpe.typeSymbol.typeParams Checking.checkBounds( diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index a08df1c33ef0..196753e82d8e 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -31,7 +31,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this import ast.tpd._ /** the following two members override abstract members in Transform */ - val phaseName: String = "lambdalift" + val phaseName: String = "lambdaLift" override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors]) // Constructors has to happen before LambdaLift because the lambda lift logic @@ -88,7 +88,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = { - println(i"narrow lifted $sym") + ctx.log(i"narrow lifted $sym to $owner") if (sym.owner.skipConstructor.isTerm && owner.isProperlyContainedIn(liftedOwner(sym))) { changedLiftedOwner = true @@ -128,7 +128,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this */ private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Boolean = try { if (!enclosure.exists) throw new NoPath - println(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") + ctx.log(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure") (enclosure == sym.enclosure) || { ctx.debuglog(i"$enclosure != ${sym.enclosure}") narrowLiftedOwner(enclosure, sym.enclosingClass) @@ -164,9 +164,13 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this val sym = tree.symbol tree match { case tree: Ident => - if (sym.maybeOwner.isTerm) - if (sym is (Method, butNot = Label)) markCalled(sym, enclosure) + if (sym.maybeOwner.isTerm) { + if (sym is Label) + assert(enclosure == sym.enclosure, + i"attempt to refer to label $sym from nested $enclosure") + else if (sym is Method) markCalled(sym, enclosure) else if (sym.isTerm) markFree(sym, enclosure) + } case tree: Select => if (sym.isConstructor && sym.owner.owner.isTerm) markCalled(sym, enclosure) @@ -183,7 +187,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this case tree: TypeDef => if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner case tree: Template => - liftedDefs(enclosure) = new mutable.ListBuffer + liftedDefs(tree.symbol.owner) = new mutable.ListBuffer case _ => } foldOver(enclosure, tree) @@ -198,7 +202,6 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def computeFreeVars()(implicit ctx: Context): Unit = do { changedFreeVars = false - // println(s"called = ${called.toList map { case (from, to) => from.showLocated + " -> " + to.toList.map(_.showLocated) }}") for { caller <- called.keys callee <- called(caller) @@ -257,7 +260,7 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def liftLocals()(implicit ctx: Context): Unit = { for ((local, lOwner) <- liftedOwner) { val (newOwner, maybeStatic) = - if (lOwner is Package) (local.topLevelClass, Static) + if (lOwner is Package) (local.topLevelClass, JavaStatic) else (lOwner, EmptyFlags) val maybeNotJavaPrivate = if (calledFromInner(local)) NotJavaPrivate else EmptyFlags local.copySymDenotation( @@ -276,6 +279,12 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } override def init(implicit ctx: Context, info: TransformerInfo) = { + free.clear() + proxyMap.clear() + called.clear() + calledFromInner.clear() + liftedOwner.clear() + liftedDefs.clear() assert(ctx.phase == thisTransform) (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree) computeFreeVars() @@ -292,8 +301,11 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this private def proxy(sym: Symbol)(implicit ctx: Context): Symbol = { def searchIn(enclosure: Symbol): Symbol = { - if (!enclosure.exists) - throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, currentOwner= $currentEnclosure") + if (!enclosure.exists) { + def enclosures(encl: Symbol): List[Symbol] = + if (encl.exists) encl :: enclosures(encl.enclosure) else Nil + throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, encl = $currentEnclosure, owners = ${currentEnclosure.ownersIterator.toList}%, %; enclosures = ${enclosures(currentEnclosure)}%, %") + } ctx.debuglog(i"searching for $sym(${sym.owner}) in $enclosure") proxyMap get enclosure match { case Some(pmap) => @@ -309,13 +321,15 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this } private def memberRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val clazz = sym.owner - val qual = if (clazz.isStaticOwner) singleton(clazz.thisType) else outer.path(clazz) + val clazz = sym.enclosingClass + val qual = + if (clazz.isStaticOwner) singleton(clazz.thisType) + else outer(ctx.withPhase(thisTransform)).path(clazz) transformFollowingDeep(qual.select(sym)) } private def proxyRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = { - val psym = proxy(sym) + val psym = proxy(sym)(ctx.withPhase(thisTransform)) transformFollowingDeep(if (psym.owner.isTerm) ref(psym) else memberRef(psym)) } @@ -351,8 +365,11 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { val sym = tree.symbol tree.tpe match { - case TermRef(NoPrefix, _) if sym.enclosure != currentEnclosure && !sym.isStatic => - (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) + case tpe @ TermRef(prefix, _) => + if ((prefix eq NoPrefix) && sym.enclosure != currentEnclosure && !sym.isStatic) + (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos) + else if (!prefixIsElidable(tpe)) ref(tpe) + else tree case _ => tree } @@ -368,7 +385,9 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this val sym = tree.symbol val proxyHolder = sym.skipConstructor if (needsLifting(proxyHolder)) { - val paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef] + var paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef] + if (sym.is(JavaStatic) && !paramsAdded.mods.is(JavaStatic)) + paramsAdded = cpy.DefDef(paramsAdded)(mods = paramsAdded.mods | JavaStatic) if (sym.isConstructor) paramsAdded else liftDef(paramsAdded) } else tree @@ -389,16 +408,4 @@ class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { this override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = if (needsLifting(tree.symbol)) liftDef(tree) else tree - } - - -/* done in lazyvals? - case Block(stats, expr0) => - val (lzyVals, rest) = stats partition { - case stat: ValDef => stat.symbol.isLazy || stat.symbol.isModuleVar - case _ => false - } - if (lzyVals.isEmpty) tree - else treeCopy.Block(tree, lzyVals ::: rest, expr0) - -*/ +} diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index ac92bb80c009..8af1b4a214d0 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -393,12 +393,14 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val res: Tree val nextBinder: Symbol - lazy val introducedRebindings = + lazy val introducedRebindings = /* if(nextBinder ne prevBinder) Rebindings(prevBinder, nextBinder) - else NoRebindings + else */ NoRebindings def chainBefore(next: Tree)(casegen: Casegen): Tree = - /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//) + if(prevBinder ne nextBinder) // happens when typeTest is known to succeed + /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//) + else casegen.flatMapGuard(cond, next) } // unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns @@ -426,6 +428,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans private lazy val (stored, substed) = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) } + // dd: this didn't yet trigger error. But I believe it would. if this causes double denition of symbol error this can be replaced with NoRebindings protected lazy val introducedRebindings: Rebindings = if (!emitVars) Rebindings(subPatBinders, subPatRefs) else { val (subPatBindersSubstituted, subPatRefsSubstituted) = substed.unzip @@ -946,7 +949,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! case Ident(nme.WILDCARD) => true case Alternative(ps) => ps forall unapply - case Typed(PatternBoundToUnderscore(), _) => true + case Typed(PatternBoundToUnderscore(), _) => false // true // Dmitry: change in dotty. Type test will be performed and the field must be stored case _ => false } } diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 1b5cc7c07c1a..4a7d280e5344 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -20,6 +20,7 @@ import reporting.ThrowingReporter import ast.Trees._ import ast.{tpd, untpd} import util.SourcePosition +import collection.mutable import ProtoTypes._ import java.lang.AssertionError @@ -53,21 +54,53 @@ class TreeChecker { val checkingCtx = ctx.fresh .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter))) val checker = new Checker(previousPhases(phasesToRun.toList)(ctx)) - checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) + try checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) + catch { + case ex: Throwable => + implicit val ctx: Context = checkingCtx + println(i"*** error while checking after phase ${checkingCtx.phase.prev} ***") + throw ex + } } class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { - override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = { + + val definedSyms = new mutable.HashSet[Symbol] + + def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = { + if (tree.isDef) { + assert(!definedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree") + definedSyms += tree.symbol + //println(i"defined: ${tree.symbol}") + val res = op + definedSyms -= tree.symbol + //println(i"undefined: ${tree.symbol}") + res + } + else op + } + + def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = + trees.foldRightBN(op)(withDefinedSym(_)(_)) + + def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T = + vparamss.foldRightBN(op)(withDefinedSyms(_)(_)) + + def assertDefined(tree: untpd.Tree)(implicit ctx: Context) = + if (tree.symbol.maybeOwner.isTerm) + assert(definedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}") + + override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context): tpd.Tree = { val res = tree match { case _: untpd.UnApply => // can't recheck patterns tree.asInstanceOf[tpd.Tree] case _: untpd.TypedSplice | _: untpd.Thicket | _: EmptyValDef[_] => - super.typed(tree) + super.typedUnadapted(tree) case _ if tree.isType => promote(tree) case _ => - val tree1 = super.typed(tree, pt) + val tree1 = super.typedUnadapted(tree, pt) def isSubType(tp1: Type, tp2: Type) = (tp1 eq tp2) || // accept NoType / NoType (tp1 <:< tp2) @@ -79,16 +112,18 @@ class TreeChecker { |After checking: ${tree1.show} |Why different : """.stripMargin + core.TypeComparer.explained((tp1 <:< tp2)(_)) - assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) + if (tree.hasType) // it might not be typed because Typer sometimes constructs new untyped trees and resubmits them to typedUnadapted + assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) tree1 - } + } phasesToCheck.foreach(_.checkPostCondition(res)) res } override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = { assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase) - assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree") + assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}") + assertDefined(tree) super.typedIdent(tree, pt) } @@ -101,20 +136,38 @@ class TreeChecker { def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = symOwner == ctxOwner || ctxOwner.isWeakOwner && ownerMatches(symOwner, ctxOwner.owner) - if(!ownerMatches(tree.symbol.owner, ctx.owner)) { - assert(ownerMatches(tree.symbol.owner, ctx.owner), - i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + - i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") - } + assert(ownerMatches(tree.symbol.owner, ctx.owner), + i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + + i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") } override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = { val TypeDef(_, _, impl @ Template(constr, _, _, _)) = cdef + assert(cdef.symbol == cls) + assert(impl.symbol.owner == cls) + assert(constr.symbol.owner == cls) + assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}") checkOwner(impl) checkOwner(impl.constr) super.typedClassDef(cdef, cls) } + override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = + withDefinedSyms(ddef.tparams) { + withDefinedSymss(ddef.vparamss) { + super.typedDefDef(ddef, sym) + } + } + + override def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = { + withDefinedSyms(tree.pat.asInstanceOf[tpd.Tree].filterSubTrees(_.isInstanceOf[ast.Trees.Bind[_]])) { + super.typedCase(tree, pt, selType, gadtSyms) + } + } + + override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = + withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) } + /** Check that all defined symbols have legal owners. * An owner is legal if it is either the same as the context's owner * or there's an owner chain of valdefs starting at the context's owner and @@ -137,10 +190,13 @@ class TreeChecker { override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = { def isPrimaryConstructorReturn = ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) - if (ctx.mode.isExpr && !isPrimaryConstructorReturn && !pt.isInstanceOf[FunProto]) + if (ctx.mode.isExpr && + !tree.isEmpty && + !isPrimaryConstructorReturn && + !pt.isInstanceOf[FunProto]) assert(tree.tpe <:< pt, s"error at ${sourcePos(tree.pos)}\n" + - err.typeMismatchStr(tree.tpe, pt)) + err.typeMismatchStr(tree.tpe, pt) + "tree = " + tree) tree } } diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 6895f997874a..713549840a0c 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -47,6 +47,9 @@ class ReTyper extends Typer { override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = promote(tree) + override def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = + promote(tree) + override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = promote(tree) @@ -84,8 +87,8 @@ class ReTyper extends Typer { super.handleUnexpectedFunType(tree, fun) } - override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = - try super.typed(tree, pt) + override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = + try super.typedUnadapted(tree, pt) catch { case ex: Throwable => println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index ccf67b55b9ae..cb6fefab1472 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -49,7 +49,21 @@ trait TypeAssigner { case TypeAlias(ref) => apply(ref) case info: ClassInfo => - mapOver(info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))) + val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) + def addRefinement(parent: Type, decl: Symbol) = { + val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) + val inheritedInfo = inherited.atSignature(decl.info .signature).info + if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) + typr.echo( + i"add ref $parent $decl --> ", + RefinedType(parent, decl.name, decl.info)) + else + parent + } + val refinableDecls = info.decls.filterNot( + sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor) + val fullType = (parentType /: refinableDecls)(addRefinement) + mapOver(fullType) case _ => mapOver(tp) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 80eb5965c63f..1afa5f9f33f9 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -415,17 +415,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { var hoisted: Set[Symbol] = Set() lazy val locals = localSyms(block.stats).toSet - def isLocal(sym: Symbol): Boolean = - (locals contains sym) && !isHoistableClass(sym) - def isHoistableClass(sym: Symbol) = - sym.isClass && { - (hoisted contains sym) || { - hoisted += sym - !classLeaks(sym.asClass) - } - } def leakingTypes(tp: Type): collection.Set[NamedType] = - tp namedPartsWith (tp => isLocal(tp.symbol)) + tp namedPartsWith (tp => locals.contains(tp.symbol)) def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty def classLeaks(sym: ClassSymbol): Boolean = (ctx.owner is Method) || // can't hoist classes out of method bodies @@ -619,29 +610,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit accu(Set.empty, selType) } - def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") { - def caseRest(pat: Tree)(implicit ctx: Context) = { - gadtSyms foreach (_.resetGADTFlexType) - pat foreachSubTree { - case b: Bind => - if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) - else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) - case _ => - } - val guard1 = typedExpr(tree.guard, defn.BooleanType) - val body1 = typedExpr(tree.body, pt) - assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) - } - val doCase: () => CaseDef = - () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) - (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))() - } - - val cases1 = tree.cases mapconserve typedCase + val cases1 = tree.cases mapconserve (typedCase(_, pt, selType, gadtSyms)) assignType(cpy.Match(tree)(sel1, cases1), cases1) } } + def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") { + def caseRest(pat: Tree)(implicit ctx: Context) = { + gadtSyms foreach (_.resetGADTFlexType) + pat foreachSubTree { + case b: Bind => + if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol) + else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos) + case _ => + } + val guard1 = typedExpr(tree.guard, defn.BooleanType) + val body1 = typedExpr(tree.body, pt) + assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) + } + val doCase: () => CaseDef = + () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) + (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))() + } + def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") { def returnProto(owner: Symbol) = if (owner.isConstructor) defn.UnitType else owner.info.finalResultType diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 614dc95271db..4c6d004bf216 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,7 +15,7 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-Yno-deep-subtypes", - "-Ycheck:patternMatcher,gettersSetters,constructors" + "-Ycheck:patternMatcher,gettersSetters,lambdaLift" ) val twice = List("#runs", "2", "-YnoDoubleBindings") @@ -99,12 +99,16 @@ class tests extends CompilerTest { @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2) @Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1) + @Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1) @Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes) @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes) @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes) - @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) + + @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice)(defaultOptions ++ List("-Ycheck:pat,era,lam")) + //disabled, awaiting fix for call-by-name function types. + @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) diff --git a/tests/neg/t1569-failedAvoid.scala b/tests/neg/t1569-failedAvoid.scala new file mode 100644 index 000000000000..9d0fbb37a3b8 --- /dev/null +++ b/tests/neg/t1569-failedAvoid.scala @@ -0,0 +1,9 @@ +// This was t1569.scala. +// It fails in dotty because the expected type of the anonymous function in the last line +// is fully determined (C). So that type is taken as the type of the anonymous function. +// See pos/t1569a.scala for related examples that work. +object Bug { + class C { type T } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new C { type T = String })("hello") +} diff --git a/tests/pos/tailcall/t1672.scala b/tests/pending/pos/tailcall/t1672.scala similarity index 100% rename from tests/pos/tailcall/t1672.scala rename to tests/pending/pos/tailcall/t1672.scala diff --git a/tests/pos/avoid.scala b/tests/pos/avoid.scala new file mode 100644 index 000000000000..51471feaae65 --- /dev/null +++ b/tests/pos/avoid.scala @@ -0,0 +1,10 @@ +abstract class C { + def y: Any +} + +object test { + val x = new C{ + def y: String = "abc" + } + val z: String = x.y +} diff --git a/tests/pos/t1569.scala b/tests/pos/t1569.scala deleted file mode 100644 index a7200a6d1ebc..000000000000 --- a/tests/pos/t1569.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Bug { - class C { type T } - def foo(x: Int)(y: C)(z: y.T): Unit = {} - foo(3)(new C { type T = String })("hello") -} diff --git a/tests/pos/t1569a.scala b/tests/pos/t1569a.scala new file mode 100644 index 000000000000..6cc3619a4697 --- /dev/null +++ b/tests/pos/t1569a.scala @@ -0,0 +1,12 @@ +object Bug { + class C[T] { type TT = T } + def foo[U](x: Int)(y: C[U])(z: y.TT): Unit = {} + foo(3)(new C[String])("hello") +} + +object Bug2 { + class C { type T } + class D extends C { type T = String } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new D)("hello") +}