diff options
-rw-r--r-- | src/dotty/tools/dotc/core/CheckRealizable.scala | 140 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeOps.scala | 93 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/PostTyper.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 32 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 9 | ||||
-rw-r--r-- | test/dotc/tests.scala | 13 | ||||
-rw-r--r-- | tests/neg/cycles.scala | 4 | ||||
-rw-r--r-- | tests/neg/i1050.scala | 119 | ||||
-rw-r--r-- | tests/neg/i1050a.scala | 9 | ||||
-rw-r--r-- | tests/neg/i1050c.scala | 31 | ||||
-rw-r--r-- | tests/neg/ski.scala | 4 |
12 files changed, 300 insertions, 168 deletions
diff --git a/src/dotty/tools/dotc/core/CheckRealizable.scala b/src/dotty/tools/dotc/core/CheckRealizable.scala new file mode 100644 index 000000000..ce922635b --- /dev/null +++ b/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -0,0 +1,140 @@ +package dotty.tools +package dotc +package core + +import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ +import SymDenotations._, Denotations.SingleDenotation +import config.Printers._ +import util.Positions._ +import Decorators._ +import StdNames._ +import Annotations._ +import collection.mutable +import ast.tpd._ + +/** Realizability status */ +object CheckRealizable { + + abstract class Realizability(val msg: String) { + def andAlso(other: => Realizability) = + if (this == Realizable) other else this + def mapError(f: Realizability => Realizability) = + if (this == Realizable) this else f(this) + } + + object Realizable extends Realizability("") + + object NotConcrete extends Realizability(" is not a concrete type") + + object NotStable extends Realizability(" is not a stable reference") + + class NotFinal(sym: Symbol)(implicit ctx: Context) + extends Realizability(i" refers to nonfinal $sym") + + class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) + extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") + + class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context) + extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}") + + class ProblemInUnderlying(tp: Type, problem: Realizability)(implicit ctx: Context) + extends Realizability(i"s underlying type ${tp}${problem.msg}") { + assert(problem != Realizable) + } + + def realizability(tp: Type)(implicit ctx: Context) = + new CheckRealizable().realizability(tp) + + def boundsRealizability(tp: Type)(implicit ctx: Context) = + new CheckRealizable().boundsRealizability(tp) +} + +/** Compute realizability status */ +class CheckRealizable(implicit ctx: Context) { + import CheckRealizable._ + + /** A set of all fields that have already been checked. Used + * to avoid infinite recursions when analyzing recursive types. + */ + private val checkedFields: mutable.Set[Symbol] = mutable.LinkedHashSet[Symbol]() + + /** Is this type a path with some part that is initialized on use? */ + private def isLateInitialized(tp: Type): Boolean = tp.dealias match { + case tp: TermRef => + tp.symbol.isLateInitialized || isLateInitialized(tp.prefix) + case _: SingletonType | NoPrefix => + false + case tp: TypeRef => + true + case tp: TypeProxy => + isLateInitialized(tp.underlying) + case tp: AndOrType => + isLateInitialized(tp.tp1) || isLateInitialized(tp.tp2) + case _ => + true + } + + /** The realizability status of given type `tp`*/ + def realizability(tp: Type): Realizability = tp.dealias match { + case tp: TermRef => + val sym = tp.symbol + if (sym.is(Stable)) realizability(tp.prefix) + else { + val r = + if (!sym.isStable) NotStable + else if (!sym.isLateInitialized) realizability(tp.prefix) + else if (!sym.isEffectivelyFinal) new NotFinal(sym) + else realizability(tp.info).mapError(r => new ProblemInUnderlying(tp.info, r)) + if (r == Realizable) sym.setFlag(Stable) + r + } + case _: SingletonType | NoPrefix => + Realizable + case tp => + def isConcrete(tp: Type): Boolean = tp.dealias match { + case tp: TypeRef => tp.symbol.isClass + case tp: TypeProxy => isConcrete(tp.underlying) + case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) + case _ => false + } + if (!isConcrete(tp)) NotConcrete + else boundsRealizability(tp).andAlso(memberRealizability(tp)) + } + + /** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance + * pointing to a bad bounds member otherwise. + */ + private def boundsRealizability(tp: Type) = { + def hasBadBounds(mbr: SingleDenotation) = { + val bounds = mbr.info.bounds + !(bounds.lo <:< bounds.hi) + } + tp.nonClassTypeMembers.find(hasBadBounds) match { + case Some(mbr) => new HasProblemBounds(mbr) + case _ => Realizable + } + } + + /** `Realizable` if `tp` all of `tp`'s non-struct fields have realizable types, + * a `HasProblemField` instance pointing to a bad field otherwise. + */ + private def memberRealizability(tp: Type) = { + def checkField(sofar: Realizability, fld: SingleDenotation): Realizability = + sofar andAlso { + if (checkedFields.contains(fld.symbol) || fld.symbol.is(Private | Mutable | Lazy)) + Realizable + else { + checkedFields += fld.symbol + realizability(fld.info).mapError(r => new HasProblemField(fld, r)) + } + } + if (ctx.settings.strict.value) + // check fields only under strict mode for now. + // Reason: We do track nulls, so an embedded field could well be nullable + // which means it is not a path and need not be checked; but we cannot recognize + // this situation until we have a typesystem that tracks nullability. + ((Realizable: Realizability) /: tp.fields)(checkField) + else + Realizable + } +} diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index fb59cae59..a4082607c 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -12,6 +12,7 @@ import scala.reflect.io.AbstractFile import Decorators.SymbolIteratorDecorator import ast._ import annotation.tailrec +import CheckRealizable._ import typer.Mode import util.SimpleMap import util.Stats @@ -522,15 +523,6 @@ object SymDenotations { final def isStable(implicit ctx: Context) = isType || !is(UnstableValue, butNot = Stable) - /** Is this a denotation of a realizable term (or an arbitrary type)? */ - final def isRealizable(implicit ctx: Context) = - is(Stable) || isType || { - val isRealizable = - !isLateInitialized || - isEffectivelyFinal && ctx.realizability(info) == TypeOps.Realizable - isRealizable && { setFlag(Stable); true } - } - /** Field is initialized on use, not on definition; * we do not count modules as fields here. */ diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 925ebcbcd..4251648a3 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -14,7 +14,6 @@ import collection.mutable import ast.tpd._ trait TypeOps { this: Context => // TODO: Make standalone object. - import TypeOps._ /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec * for what this means. Called very often, so the code is optimized heavily. @@ -342,73 +341,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - /** Is this type a path with some part that is initialized on use? */ - def isLateInitialized(tp: Type): Boolean = tp.dealias match { - case tp: TermRef => - tp.symbol.isLateInitialized || isLateInitialized(tp.prefix) - case _: SingletonType | NoPrefix => - false - case tp: TypeRef => - true - case tp: TypeProxy => - isLateInitialized(tp.underlying) - case tp: AndOrType => - isLateInitialized(tp.tp1) || isLateInitialized(tp.tp2) - case _ => - true - } - - /** The realizability status of given type `tp`*/ - def realizability(tp: Type): Realizability = tp.dealias match { - case tp: TermRef => - if (tp.symbol.isRealizable) - if (tp.symbol.isLateInitialized || // we already checked realizability of info in that case - !isLateInitialized(tp.prefix)) // symbol was definitely constructed in that case - Realizable - else - realizability(tp.info) - else if (!tp.symbol.isStable) NotStable - else if (!tp.symbol.isEffectivelyFinal) new NotFinal(tp.symbol) - else new ProblemInUnderlying(tp.info, realizability(tp.info)) - case _: SingletonType | NoPrefix => - Realizable - case tp => - def isConcrete(tp: Type): Boolean = tp.dealias match { - case tp: TypeRef => tp.symbol.isClass - case tp: TypeProxy => isConcrete(tp.underlying) - case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2) - case _ => false - } - if (!isConcrete(tp)) NotConcrete - else boundsRealizability(tp) - } - - /** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance - * pointing to a bad bounds member otherwise. - */ - def boundsRealizability(tp: Type)(implicit ctx: Context) = { - def hasBadBounds(mbr: SingleDenotation) = { - val bounds = mbr.info.bounds - !(bounds.lo <:< bounds.hi) - } - tp.nonClassTypeMembers.find(hasBadBounds) match { - case Some(mbr) => new HasProblemBounds(mbr) - case _ => Realizable - } - } - - /* Might need at some point in the future - def memberRealizability(tp: Type)(implicit ctx: Context) = { - println(i"check member rel of $tp") - def isUnrealizable(fld: SingleDenotation) = - !fld.symbol.is(Lazy) && realizability(fld.info) != Realizable - tp.fields.find(isUnrealizable) match { - case Some(fld) => new HasProblemField(fld) - case _ => Realizable - } - } - */ - private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = { val lazyInfo = new LazyType { // needed so we do not force `formal`. def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { @@ -625,30 +557,5 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } object TypeOps { - val emptyDNF = (Nil, Set[Name]()) :: Nil @sharable var track = false // !!!DEBUG - - // ----- Realizibility Status ----------------------------------------------------- - - abstract class Realizability(val msg: String) - - object Realizable extends Realizability("") - - object NotConcrete extends Realizability(" is not a concrete type") - - object NotStable extends Realizability(" is not a stable reference") - - class NotFinal(sym: Symbol)(implicit ctx: Context) - extends Realizability(i" refers to nonfinal $sym") - - class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) - extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") - - /* Might need at some point in the future - class HasProblemField(fld: SingleDenotation)(implicit ctx: Context) - extends Realizability(i" has a member $fld which is uneligible as a path since ${fld.symbol.name}${ctx.realizability(fld.info)}") - */ - - class ProblemInUnderlying(tp: Type, problem: Realizability)(implicit ctx: Context) - extends Realizability(i"s underlying type ${tp}${problem.msg}") } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index f9862bb95..14edaa7b5 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -101,7 +101,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: TypeTree => tree case _ => if (tree.isType) { - Checking.boundsChecker.traverse(tree) + Checking.typeChecker.traverse(tree) TypeTree(tree.tpe).withPos(tree.pos) } else tree.tpe.widenTermRefExpr match { @@ -180,7 +180,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val tree1 = if (sym.isClass) tree else { - Checking.boundsChecker.traverse(tree.rhs) + Checking.typeChecker.traverse(tree.rhs) cpy.TypeDef(tree)(rhs = TypeTree(tree.symbol.info)) } super.transform(tree1) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 7e90d755b..437902d05 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -16,6 +16,7 @@ import Trees._ import ProtoTypes._ import Constants._ import Scopes._ +import CheckRealizable._ import ErrorReporting.errorTree import annotation.unchecked import util.Positions._ @@ -49,7 +50,7 @@ object Checking { checkBounds(args, poly.paramBounds, _.substParams(poly, _)) /** Check all AppliedTypeTree nodes in this tree for legal bounds */ - val boundsChecker = new TreeTraverser { + val typeChecker = new TreeTraverser { def traverse(tree: Tree)(implicit ctx: Context) = { tree match { case AppliedTypeTree(tycon, args) => @@ -57,6 +58,12 @@ object Checking { val bounds = tparams.map(tparam => tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) checkBounds(args, bounds, _.substDealias(tparams, _)) + case Select(qual, name) if name.isTypeName => + checkRealizable(qual.tpe, qual.pos) + case SelectFromTypeTree(qual, name) if name.isTypeName => + checkRealizable(qual.tpe, qual.pos) + case SingletonTypeTree(ref) => + checkRealizable(ref.tpe, ref.pos) case _ => } traverseChildren(tree) @@ -83,6 +90,15 @@ object Checking { case _ => } + /** Check that type `tp` is realizable. */ + def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = { + val rstatus = realizability(tp) + if (rstatus ne Realizable) { + def msg = d"$tp is not a legal path\n since it${rstatus.msg}" + if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos) + } + } + /** A type map which checks that the only cycles in a type are F-bounds * and that protects all F-bounded references by LazyRefs. */ @@ -321,19 +337,10 @@ trait Checking { def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = if (!tp.isStable) ctx.error(d"$tp is not stable", pos) - /** Check that type `tp` is realizable. */ - def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = { - val rstatus = ctx.realizability(tp) - if (rstatus ne TypeOps.Realizable) { - def msg = d"$tp is not a legal path since it${rstatus.msg}" - if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos) - } - } - /** Check that all type members of `tp` have realizable bounds */ def checkRealizableBounds(tp: Type, pos: Position)(implicit ctx: Context): Unit = { - val rstatus = ctx.boundsRealizability(tp) - if (rstatus ne TypeOps.Realizable) + val rstatus = boundsRealizability(tp) + if (rstatus ne Realizable) ctx.error(i"$tp cannot be instantiated since it${rstatus.msg}", pos) } @@ -449,7 +456,6 @@ trait NoChecking extends Checking { override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 7894a5b5f..542f78f94 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -274,7 +274,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val tree1 = ownType match { case ownType: NamedType if !prefixIsElidable(ownType) => - checkRealizable(ownType.prefix, tree.pos) ref(ownType).withPos(tree.pos) case _ => tree.withType(ownType) @@ -309,10 +308,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { def asSelect(implicit ctx: Context): Tree = { val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) - if (tree.name.isTypeName) { - checkStable(qual1.tpe, qual1.pos) - checkRealizable(qual1.tpe, qual1.pos) - } + if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) typedSelect(tree, pt, qual1) } @@ -346,7 +342,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): Tree = track("typedSelectFromTypeTree") { val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) - checkRealizable(qual1.tpe, qual1.pos) assignType(cpy.SelectFromTypeTree(tree)(qual1, tree.name), qual1) } @@ -828,7 +823,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) checkStable(ref1.tpe, tree.pos) - checkRealizable(ref1.tpe, tree.pos) assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } @@ -1064,6 +1058,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.pos) + checkRealizable(expr1.tpe, imp.expr.pos) assignType(cpy.Import(imp)(expr1, imp.selectors), sym) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index be457d916..d96fe5488 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -125,7 +125,7 @@ class tests extends CompilerTest { @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14) @Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1) @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 2) - @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 5) + @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 3) @Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12) val negTailcallDir = negDir + "tailcall/" @@ -138,7 +138,7 @@ class tests extends CompilerTest { @Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1) @Test def neg_t2660_ambi = compileFile(negDir, "t2660", xerrors = 2) - @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 5) + @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2) @Test def neg_subtyping = compileFile(negDir, "subtyping", xerrors = 5) @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @Test def neg_variancesConstr = compileFile(negDir, "variances-constr", xerrors = 2) @@ -147,7 +147,7 @@ class tests extends CompilerTest { @Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1) @Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1) @Test def neg_clashes = compileFile(negDir, "clashes", xerrors = 2) - @Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 9) + @Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 7) @Test def neg_boundspropagation = compileFile(negDir, "boundspropagation", xerrors = 5) @Test def neg_refinedSubtyping = compileFile(negDir, "refinedSubtyping", xerrors = 2) @Test def neg_hklower = compileFile(negDir, "hklower", xerrors = 4) @@ -162,8 +162,9 @@ class tests extends CompilerTest { @Test def neg_i803 = compileFile(negDir, "i803", xerrors = 2) @Test def neg_i866 = compileFile(negDir, "i866", xerrors = 2) @Test def neg_i974 = compileFile(negDir, "i974", xerrors = 2) - @Test def neg_i1050 = compileFile(negDir, "i1050", xerrors = 14) + @Test def neg_i1050 = compileFile(negDir, "i1050", List("-strict"), xerrors = 13) @Test def neg_i1050a = compileFile(negDir, "i1050a", xerrors = 2) + @Test def neg_i1050c = compileFile(negDir, "i1050c", xerrors = 4) @Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4) @Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2) @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) @@ -172,7 +173,7 @@ class tests extends CompilerTest { @Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 2) @Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 8) @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) - @Test def neg_ski = compileFile(negDir, "ski", xerrors = 10) + @Test def neg_ski = compileFile(negDir, "ski", xerrors = 12) @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) @Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2) @Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3) @@ -202,7 +203,7 @@ class tests extends CompilerTest { |./scala-scala/src/library/scala/collection/generic/GenSeqFactory.scala""".stripMargin) @Test def compileIndexedSeq = compileLine("./scala-scala/src/library/scala/collection/immutable/IndexedSeq.scala") - @Test def dotty = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant"))(allowDeepSubtypes) // note the -deep argument + @Test def dotty = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant", "-strict"))(allowDeepSubtypes) // note the -deep argument @Test def dotc_ast = compileDir(dotcDir, "ast") @Test def dotc_config = compileDir(dotcDir, "config") diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala index 550bc98ea..ced6f56b5 100644 --- a/tests/neg/cycles.scala +++ b/tests/neg/cycles.scala @@ -13,7 +13,7 @@ class B { } class C { - final val x: D#T = ??? // error: conflicting bounds + final val x: D#T = ??? class D { type T <: x.type // error: cycle val z: x.type = ??? @@ -25,7 +25,7 @@ class E { type T <: x.type // error: not stable val z: x.type = ??? // error: not stable } - lazy val x: F#T = ??? // error: conflicting bounds + lazy val x: F#T = ??? } class T1 { diff --git a/tests/neg/i1050.scala b/tests/neg/i1050.scala index f2c237af2..fb4160606 100644 --- a/tests/neg/i1050.scala +++ b/tests/neg/i1050.scala @@ -1,3 +1,4 @@ +// i1050 checks failing at posttyper trait A { type L <: Nothing } trait B { type L >: Any} object Test { @@ -79,36 +80,6 @@ object Tiark3 { val v = new V {} v.brand("boom!"): Nothing } -object Tiark4 { - trait U { - type Y - trait X { type L = Y } - def compute: X - final lazy val p: X = compute - def brand(x: Y): p.L = x - } - trait V extends U { - type Y >: Any <: Nothing - def compute: X = ??? - } - val v = new V {} // error: cannot be instantiated - v.brand("boom!") -} -object Import { - trait A { type L <: Nothing } - trait B { type L >: Any} - trait U { - lazy val p: B - locally { val x: p.L = ??? } // error: nonfinal lazy - locally { - import p._ - val x: L = ??? // error: nonfinal lazy - } - } -} -object V { // error: cannot be instantiated - type Y >: Any <: Nothing // error: only classes can have declared but undefined members -} object Tiark5 { trait A { type L <: Nothing } trait B { type L >: Any } @@ -136,3 +107,91 @@ object Tiark6 { val v = new U {} v.brand("boom!"): Nothing } + +object Indirect { + trait B { type L >: Any } + trait A { type L <: Nothing } + trait U { + trait X { + val q: A & B = ??? + type M = q.L + } + final lazy val p: X = ??? + def brand(x: Any): p.M = x // error: conflicting bounds + } + def main(args: Array[String]): Unit = { + val v = new U {} + v.brand("boom!"): Nothing + } +} +object Indirect2 { + trait B { type L >: Any } + trait A { type L <: Nothing } + trait U { + trait Y { + val r: A & B = ??? + } + trait X { + val q: Y = ??? + type M = q.r.L + } + final lazy val p: X = ??? + def brand(x: Any): p.M = x // error: conflicting bounds + } + def main(args: Array[String]): Unit = { + val v = new U {} + v.brand("boom!"): Nothing + } +} +object Rec1 { + trait B { type L >: Any } + trait A { type L <: Nothing } + trait U { + trait Y { + type L = Int + val r: Y + } + trait X { + val q: Y = ??? + type M = q.r.L // if we are not careful we get a stackoverflow here + } + } +} +object Rec2 { + trait B { type L >: Any } + trait A { type L <: Nothing } + trait U { + trait Y { + val r: A & B & Y + } + trait X { + val q: Y = ??? + type M = q.r.L + } + final lazy val p: X = ??? + def brand(x: Any): p.M = x // error: conflicting bounds + } + def main(args: Array[String]): Unit = { + val v = new U {} + v.brand("boom!"): Nothing + } +} +object Indirect3 { + trait B { type L >: Any } + trait A { type L <: Nothing } + trait U { + trait Y { + val r: Y & A & B = ??? + } + trait X { + val q: Y = ??? + type M = q.r.L + } + final lazy val p: X = ??? + def brand(x: Any): p.M = x // error: conflicting bounds + } + def main(args: Array[String]): Unit = { + val v = new U {} + v.brand("boom!"): Nothing + } +} diff --git a/tests/neg/i1050a.scala b/tests/neg/i1050a.scala index 47e2f0c59..4fea5e3b1 100644 --- a/tests/neg/i1050a.scala +++ b/tests/neg/i1050a.scala @@ -1,12 +1,13 @@ +// i1050 checks failing at refchecks object Tiark1 { trait A { type L <: Nothing } trait B { type L >: Any} trait U { val p: B - def brand(x: Any): p.L = x // error: not final + def brand(x: Any): p.L = x } trait V extends U { - lazy val p: A & B = ??? + lazy val p: A & B = ??? // error: may not override } val v = new V {} v.brand("boom!") @@ -17,11 +18,11 @@ object Tiark2 { trait U { type X <: B val p: X - def brand(x: Any): p.L = x // error: not final + def brand(x: Any): p.L = x } trait V extends U { type X = B & A - lazy val p: X = ??? + lazy val p: X = ??? // error: may not override } val v = new V {} v.brand("boom!"): Nothing diff --git a/tests/neg/i1050c.scala b/tests/neg/i1050c.scala new file mode 100644 index 000000000..ece1f9986 --- /dev/null +++ b/tests/neg/i1050c.scala @@ -0,0 +1,31 @@ +// i1050 checks failing at typer +object Import { + trait A { type L <: Nothing } + trait B { type L >: Any} + trait U { + lazy val p: B + locally { val x: p.L = ??? } // error: nonfinal lazy + locally { + import p._ + val x: L = ??? // error: nonfinal lazy + } + } +} +object Tiark4 { + trait U { + type Y + trait X { type L = Y } + def compute: X + final lazy val p: X = compute + def brand(x: Y): p.L = x + } + trait V extends U { + type Y >: Any <: Nothing + def compute: X = ??? + } + val v = new V {} // error: cannot be instantiated + v.brand("boom!") +} +object V { // error: cannot be instantiated + type Y >: Any <: Nothing // error: only classes can have declared but undefined members +} diff --git a/tests/neg/ski.scala b/tests/neg/ski.scala index 8fb35c7f2..b192dc9e2 100644 --- a/tests/neg/ski.scala +++ b/tests/neg/ski.scala @@ -64,9 +64,9 @@ case class Equals[A >: B <:B , B]() object Test { type T1 = Equals[Int, Int] // compiles fine - type T2 = Equals[String, Int] // was error, now masked + type T2 = Equals[String, Int] // error type T3 = Equals[I#ap[c]#eval, c] - type T3a = Equals[I#ap[c]#eval, d] // was error, now masked + type T3a = Equals[I#ap[c]#eval, d] // error // Ic -> c type T4 = Equals[I#ap[c]#eval, c] |