From 09eb5f7eb98921ec592f32d4e67dedfebe6c81d1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 10:44:29 +0200 Subject: [PATCH 01/13] Give up typechecking class definitions if basic type info is missing --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 +++++- tests/neg/parser-stability-11.scala | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/neg/parser-stability-11.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 6950934fb8f6..c5a7f96b2411 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1447,7 +1447,11 @@ class Typer extends Namer assignType(cpy.TypeDef(tdef)(name, rhs1), sym) } - def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { + def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context): Tree = track("typedClassDef") { + if (!cls.info.isInstanceOf[ClassInfo]) { + assert(ctx.reporter.errorsReported) + return cdef.withType(UnspecifiedErrorType) + } val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef val superCtx = ctx.superCallContext diff --git a/tests/neg/parser-stability-11.scala b/tests/neg/parser-stability-11.scala new file mode 100644 index 000000000000..632a0a6230ea --- /dev/null +++ b/tests/neg/parser-stability-11.scala @@ -0,0 +1,5 @@ +package x0 { +case class x0 +} +package x0 +class x0 From 77f75b7e7c4768e612f2173b5ae1e45f7431fe99 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 11:08:07 +0200 Subject: [PATCH 02/13] Align computeBaseClasses with checkedParentType `checkedParentType` tests that the underlying class symbol of a type is a class. So `computeBaseClasses` should assume exactly the same property. --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 6 ++++-- tests/neg/parser-stability-12.scala | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/neg/parser-stability-12.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 995fd474d0bb..0213a1e03fc1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1467,8 +1467,10 @@ object SymDenotations { onBehalf.signalProvisional() val builder = new BaseDataBuilder for (p <- classParents) { - if (p.typeSymbol.isClass) builder.addAll(p.typeSymbol.asClass.baseClasses) - else assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") + p.underlyingClassRef(refinementOK = false).typeSymbol match { + case pcls: ClassSymbol => builder.addAll(pcls.baseClasses) + case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") + } } (classSymbol :: builder.baseClasses, builder.baseClassSet) } diff --git a/tests/neg/parser-stability-12.scala b/tests/neg/parser-stability-12.scala new file mode 100644 index 000000000000..52b88d30f9b9 --- /dev/null +++ b/tests/neg/parser-stability-12.scala @@ -0,0 +1,3 @@ +trait x0[] + trait x1[x1 <:x0] + extends x1[ From 297c216fe48643453984d54a00788d2c328695ba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 12:11:58 +0200 Subject: [PATCH 03/13] Survive bad symbols of module vals Also: fix a cyclic reference caused by 5252b152417a --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 6 +++++- compiler/src/dotty/tools/dotc/typer/Namer.scala | 14 ++++++++++---- tests/neg/parser-stability-14.scala | 4 ++++ tests/neg/parser-stability-16.scala | 5 +++++ 4 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 tests/neg/parser-stability-14.scala create mode 100644 tests/neg/parser-stability-16.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 0213a1e03fc1..cd727cbc4135 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1467,7 +1467,11 @@ object SymDenotations { onBehalf.signalProvisional() val builder = new BaseDataBuilder for (p <- classParents) { - p.underlyingClassRef(refinementOK = false).typeSymbol match { + var pcls = p.typeSymbol + if (!pcls.isClass) pcls = p.underlyingClassRef(refinementOK = false).typeSymbol + // This roundabout way is necessary for avoiding cyclic references. + // A test case is CompilationTests.compileMixed + pcls match { case pcls: ClassSymbol => builder.addAll(pcls.baseClasses) case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index a231ff18f6e5..281471c599fe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -164,8 +164,11 @@ object NamerContextOps { /** Find moduleClass/sourceModule in effective scope */ private def findModuleBuddy(name: Name, scope: Scope)(implicit ctx: Context) = { val it = scope.lookupAll(name).filter(_ is Module) - assert(it.hasNext, s"no companion $name in $scope") - it.next() + if (it.hasNext) it.next() + else { + assert(ctx.reporter.errorsReported, s"no companion $name in $scope") + NoSymbol + } } } @@ -1033,8 +1036,11 @@ class Namer { typer: Typer => */ def moduleValSig(sym: Symbol)(implicit ctx: Context): Type = { val clsName = sym.name.moduleClassName - val cls = ctx.denotNamed(clsName) suchThat (_ is ModuleClass) - ctx.owner.thisType select (clsName, cls) + val cls = ctx.denotNamed(clsName).suchThat(_ is ModuleClass).orElse { + assert(ctx.reporter.errorsReported) + ctx.newStubSymbol(ctx.owner, clsName) + } + ctx.owner.thisType.select(clsName, cls) } /** The type signature of a ValDef or DefDef diff --git a/tests/neg/parser-stability-14.scala b/tests/neg/parser-stability-14.scala new file mode 100644 index 000000000000..a34e8f9096d1 --- /dev/null +++ b/tests/neg/parser-stability-14.scala @@ -0,0 +1,4 @@ +object x0 { +{ +val x1 x0 // error +object x1 // error // error diff --git a/tests/neg/parser-stability-16.scala b/tests/neg/parser-stability-16.scala new file mode 100644 index 000000000000..f6f6f19aa890 --- /dev/null +++ b/tests/neg/parser-stability-16.scala @@ -0,0 +1,5 @@ +class x0[x0] { + val x1 : x0 +} +trait x3 extends x0 { +x1 = 0 object // error // error From 5597dd060116aefa76f05ed97c6d61a74e7f9033 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 16:05:30 +0200 Subject: [PATCH 04/13] Rework Trees.flatten - Handle the case where Thickets can contain themselves Thickets - Make it tail recursive instead of using an outer while loop --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 34 +++++++++---------- tests/neg/parser-stability-11.scala | 4 +-- tests/neg/parser-stability-12.scala | 4 +-- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index f67bfd228d0d..32235e35e904 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -769,28 +769,26 @@ object Trees { def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]] def flatten[T >: Untyped](trees: List[Tree[T]]): List[Tree[T]] = { - var buf: ListBuffer[Tree[T]] = null - var xs = trees - while (!xs.isEmpty) { - xs.head match { - case Thicket(elems) => - if (buf == null) { - buf = new ListBuffer - var ys = trees - while (ys ne xs) { - buf += ys.head - ys = ys.tail + def recur(buf: ListBuffer[Tree[T]], remaining: List[Tree[T]]): ListBuffer[Tree[T]] = + remaining match { + case Thicket(elems) :: remaining1 => + var buf1 = buf + if (buf1 == null) { + buf1 = new ListBuffer[Tree[T]] + var scanned = trees + while (scanned `ne` remaining) { + buf1 += scanned.head + scanned = scanned.tail } } - for (elem <- elems) { - assert(!elem.isInstanceOf[Thicket[_]]) - buf += elem - } - case tree => + recur(recur(buf1, elems), remaining1) + case tree :: remaining1 => if (buf != null) buf += tree + recur(buf, remaining1) + case nil => + buf } - xs = xs.tail - } + val buf = recur(null, trees) if (buf != null) buf.toList else trees } diff --git a/tests/neg/parser-stability-11.scala b/tests/neg/parser-stability-11.scala index 632a0a6230ea..ae9d6cde7b6a 100644 --- a/tests/neg/parser-stability-11.scala +++ b/tests/neg/parser-stability-11.scala @@ -1,5 +1,5 @@ package x0 { -case class x0 +case class x0 // error // error } package x0 -class x0 +class x0 // error // error diff --git a/tests/neg/parser-stability-12.scala b/tests/neg/parser-stability-12.scala index 52b88d30f9b9..956e80768cf5 100644 --- a/tests/neg/parser-stability-12.scala +++ b/tests/neg/parser-stability-12.scala @@ -1,3 +1,3 @@ -trait x0[] +trait x0[] // error trait x1[x1 <:x0] - extends x1[ + extends x1[ // error From de586537a4f74b4bc1d17fc133d3538c04f43a0d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 12:45:28 +0200 Subject: [PATCH 05/13] Survive ill-formed parent types In the face of previous errors: - Constant#classBound cannot assume all class parents have good types - dominator cannot assume that all classes in an OrType inherit from the same parent --- compiler/src/dotty/tools/dotc/core/Constants.scala | 3 ++- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 2 +- tests/neg/parser-stability-15.scala | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 tests/neg/parser-stability-15.scala diff --git a/compiler/src/dotty/tools/dotc/core/Constants.scala b/compiler/src/dotty/tools/dotc/core/Constants.scala index 4f013a088132..38cdac21d7fe 100644 --- a/compiler/src/dotty/tools/dotc/core/Constants.scala +++ b/compiler/src/dotty/tools/dotc/core/Constants.scala @@ -153,7 +153,8 @@ object Constants { */ def convertTo(pt: Type)(implicit ctx: Context): Constant = { def classBound(pt: Type): Type = pt.dealias.stripTypeVar match { - case tref: TypeRef if !tref.symbol.isClass => classBound(tref.info.bounds.lo) + case tref: TypeRef if !tref.symbol.isClass && tref.info.exists => + classBound(tref.info.bounds.lo) case param: TypeParamRef => ctx.typerState.constraint.entry(param) match { case TypeBounds(lo, hi) => diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index d59c540432c3..1ff312dae475 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -140,7 +140,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu if (cs == c.baseClasses) accu1 else dominators(rest, accu1) case Nil => // this case can happen because after erasure we do not have a top class anymore - assert(ctx.erasedTypes) + assert(ctx.erasedTypes || ctx.reporter.errorsReported) defn.ObjectClass :: Nil } diff --git a/tests/neg/parser-stability-15.scala b/tests/neg/parser-stability-15.scala new file mode 100644 index 000000000000..89748da11dd6 --- /dev/null +++ b/tests/neg/parser-stability-15.scala @@ -0,0 +1,6 @@ +package x0 +class x0 { +def x1 = +x2 match // error +] // error +case x3[] => x0 // error // error // error // error From a6ba33b6158384f6b7311c729af7754484cedeb9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 14:17:43 +0200 Subject: [PATCH 06/13] Handle refinements where the refined type is a class --- .../src/dotty/tools/dotc/core/Types.scala | 9 ++--- tests/neg/parser-stability-18.scala | 4 +++ tests/pos/class-refinement.scala | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 tests/neg/parser-stability-18.scala create mode 100644 tests/pos/class-refinement.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 19c645afb389..8e0284e3137e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -589,13 +589,14 @@ object Types { def goRefined(tp: RefinedType) = { val pdenot = go(tp.parent) + val pinfo = pdenot.info val rinfo = tp.refinedInfo - if (name.isTypeName) { // simplified case that runs more efficiently + if (name.isTypeName && !pinfo.isInstanceOf[ClassInfo]) { // simplified case that runs more efficiently val jointInfo = if (rinfo.isAlias) rinfo - else if (pdenot.info.isAlias) pdenot.info - else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo - else pdenot.info recoverable_& rinfo + else if (pinfo.isAlias) pinfo + else if (ctx.pendingMemberSearches.contains(name)) pinfo safe_& rinfo + else pinfo recoverable_& rinfo pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } else { pdenot & ( diff --git a/tests/neg/parser-stability-18.scala b/tests/neg/parser-stability-18.scala new file mode 100644 index 000000000000..3eefbbcbd333 --- /dev/null +++ b/tests/neg/parser-stability-18.scala @@ -0,0 +1,4 @@ +trait x0 { + class x1 (x1:x0 + { +type x1 <: List[x1 <: // error // error diff --git a/tests/pos/class-refinement.scala b/tests/pos/class-refinement.scala new file mode 100644 index 000000000000..a8a83fa20f00 --- /dev/null +++ b/tests/pos/class-refinement.scala @@ -0,0 +1,36 @@ +object Test { + + class C { + + class I + + } + + trait T + + val x: C { type I <: T } = ??? // direct refinement of class member + + val y: x.I = ??? + +} + +class B { + class C { + type I + } + trait T + + type CC <: C + + val x: CC { type I <: T } = ??? +} + +object Test2 extends B { + + class CC extends C { class I } + + val y: x.I = ??? // indirect refinement of class member + + +} + From 64e36f149c1f61f2a4f4a2d60030aa41e32dfb75 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 13:04:13 +0200 Subject: [PATCH 07/13] Surivive bad selftypes These are sometimes detected after they are computed. Need to avoid a crash in AndType due to an operand that is not a value type. --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- tests/neg/bad-selftype.scala | 6 ++++++ tests/neg/parser-stability-17.scala | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/neg/bad-selftype.scala create mode 100644 tests/neg/parser-stability-17.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8e0284e3137e..aa8e863824ca 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3478,7 +3478,7 @@ object Types { if (selfTypeCache == null) selfTypeCache = { val given = cls.givenSelfType - if (!given.exists) appliedRef + if (!given.isValueType) appliedRef else if (cls is Module) given else if (ctx.erasedTypes) appliedRef else AndType(given, appliedRef) diff --git a/tests/neg/bad-selftype.scala b/tests/neg/bad-selftype.scala new file mode 100644 index 000000000000..46a538d1f879 --- /dev/null +++ b/tests/neg/bad-selftype.scala @@ -0,0 +1,6 @@ +trait x0[T] { self: x0 => } // error + +trait x1[T] { self: (=> String) => } // error + +trait x2[T] { self: ([X] => X) => } // error + diff --git a/tests/neg/parser-stability-17.scala b/tests/neg/parser-stability-17.scala new file mode 100644 index 000000000000..8d7102672b5b --- /dev/null +++ b/tests/neg/parser-stability-17.scala @@ -0,0 +1,2 @@ +trait x0[] { x0: x0 => } + class x0[x1] extends x0[x0 x0] x2 x0 From f7c46afeaf784b131e9e9321059b9317230dd329 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 15:36:46 +0200 Subject: [PATCH 08/13] Harden resultTypeApprox --- .../src/dotty/tools/dotc/core/Types.scala | 20 +++++++++---------- .../dotty/tools/dotc/typer/ProtoTypes.scala | 8 +++++--- tests/neg/parser-stability-17.scala | 4 ++-- tests/neg/parser-stability-19.scala | 4 ++++ 4 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 tests/neg/parser-stability-19.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index aa8e863824ca..3de52cb16f87 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -142,9 +142,12 @@ object Types { /** Is this type a value type? */ final def isValueType: Boolean = this.isInstanceOf[ValueType] - /** Is the is value type or type lambda? */ + /** Is this a value type or a type lambda? */ final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[TypeLambda] + /** Is this a value type or a wildcard? */ + final def isValueTypeOrWildcard: Boolean = isValueType || this.isInstanceOf[WildcardType] + /** Does this type denote a stable reference (i.e. singleton type)? */ final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable || tp.info.isStable @@ -1524,11 +1527,8 @@ object Types { /** A marker trait for types that can be types of values or prototypes of value types */ trait ValueTypeOrProto extends TermType - /** A marker trait for types that can be types of values or wildcards */ - trait ValueTypeOrWildcard extends TermType - /** A marker trait for types that can be types of values or that are higher-kinded */ - trait ValueType extends ValueTypeOrProto with ValueTypeOrWildcard + trait ValueType extends ValueTypeOrProto /** A marker trait for types that are guaranteed to contain only a * single non-null value (they might contain null in addition). @@ -2480,8 +2480,8 @@ object Types { object AndType { def apply(tp1: Type, tp2: Type)(implicit ctx: Context): AndType = { - assert(tp1.isInstanceOf[ValueTypeOrWildcard] && - tp2.isInstanceOf[ValueTypeOrWildcard], i"$tp1 & $tp2 / " + s"$tp1 & $tp2") + assert(tp1.isValueTypeOrWildcard && + tp2.isValueTypeOrWildcard, i"$tp1 & $tp2 / " + s"$tp1 & $tp2") unchecked(tp1, tp2) } @@ -2525,8 +2525,8 @@ object Types { myBaseClasses } - assert(tp1.isInstanceOf[ValueTypeOrWildcard] && - tp2.isInstanceOf[ValueTypeOrWildcard], s"$tp1 $tp2") + assert(tp1.isValueTypeOrWildcard && + tp2.isValueTypeOrWildcard, s"$tp1 $tp2") private[this] var myJoin: Type = _ private[this] var myJoinPeriod: Period = Nowhere @@ -3757,7 +3757,7 @@ object Types { object TryDynamicCallType extends FlexType /** Wildcard type, possibly with bounds */ - abstract case class WildcardType(optBounds: Type) extends CachedGroundType with ValueTypeOrWildcard { + abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType { def derivedWildcardType(optBounds: Type)(implicit ctx: Context) = if (optBounds eq this.optBounds) this else if (!optBounds.exists) WildcardType diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index cc0c07c79266..5a32fcf19364 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -472,16 +472,18 @@ object ProtoTypes { } /** Create a new TypeVar that represents a dependent method parameter singleton */ - def newDepTypeVar(tp: Type)(implicit ctx: Context): TypeVar = + def newDepTypeVar(tp: Type)(implicit ctx: Context): TypeVar = { newTypeVar(TypeBounds.upper(AndType(tp.widenExpr, defn.SingletonClass.typeRef))) - + } /** The result type of `mt`, where all references to parameters of `mt` are * replaced by either wildcards (if typevarsMissContext) or TypeParamRefs. */ def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type = if (mt.isResultDependent) { def replacement(tp: Type) = - if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepTypeVar(tp) + if (ctx.mode.is(Mode.TypevarsMissContext) || + !tp.widenExpr.isValueTypeOrWildcard) WildcardType + else newDepTypeVar(tp) mt.resultType.substParams(mt, mt.paramInfos.map(replacement)) } else mt.resultType diff --git a/tests/neg/parser-stability-17.scala b/tests/neg/parser-stability-17.scala index 8d7102672b5b..ff603a677378 100644 --- a/tests/neg/parser-stability-17.scala +++ b/tests/neg/parser-stability-17.scala @@ -1,2 +1,2 @@ -trait x0[] { x0: x0 => } - class x0[x1] extends x0[x0 x0] x2 x0 +trait x0[] { x0: x0 => } // error // error + class x0[x1] extends x0[x0 x0] x2 x0 // error // error diff --git a/tests/neg/parser-stability-19.scala b/tests/neg/parser-stability-19.scala new file mode 100644 index 000000000000..306b9479a2e4 --- /dev/null +++ b/tests/neg/parser-stability-19.scala @@ -0,0 +1,4 @@ +object x0 { + case class x0[] // error // error + def x0( ) ] // error // error + def x0 ( x0:x0 ):x0.type = x1 x0 // error // error // error From f8f5176bba97da849b32101160247a2b2a237239 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 16:38:59 +0200 Subject: [PATCH 09/13] Harden typedUnaply In the tested case, an unApply ended up with an inserted toplevel apply method, which confused the follow-on logic. This case is now rejected. --- .../dotty/tools/dotc/typer/Applications.scala | 20 ++++++++++++------- tests/neg/parser-stability-20.scala | 5 +++++ 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 tests/neg/parser-stability-20.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a45942481a4f..bc49e12cd0c4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -879,19 +879,25 @@ trait Applications extends Compatibility { self: Typer with Dynamic => */ def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = { // try first for non-overloaded, then for overloaded ocurrences - def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree = - tryEither { implicit ctx => - val specificProto = new UnapplyFunProto(selType, this) - typedExpr(untpd.Select(qual, name), specificProto) + def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree = { + def tryWithProto(pt: Type)(implicit ctx: Context) = { + val result = typedExpr(untpd.Select(qual, name), new UnapplyFunProto(pt, this)) + if (!result.symbol.exists || result.symbol.name == name) result + else notAnExtractor(result) + // It might be that the result of typedExpr is an `apply` selection or implicit conversion. + // Reject in this case. + } + tryEither { + implicit ctx => tryWithProto(selType) } { (sel, _) => - tryEither { implicit ctx => - val genericProto = new UnapplyFunProto(WildcardType, this) - typedExpr(untpd.Select(qual, name), genericProto) + tryEither { + implicit ctx => tryWithProto(WildcardType) } { (_, _) => fallBack(sel) } } + } // try first for unapply, then for unapplySeq tryWithName(nme.unapply) { sel => tryWithName(nme.unapplySeq)(_ => fallBack(sel)) // for backwards compatibility; will be dropped diff --git a/tests/neg/parser-stability-20.scala b/tests/neg/parser-stability-20.scala new file mode 100644 index 000000000000..bb1165042410 --- /dev/null +++ b/tests/neg/parser-stability-20.scala @@ -0,0 +1,5 @@ +object x0 { +def unapply= Array +x0 match +x0 // error +case x0( // error // error From 5ad42d12e8768ac48b9a295b7dad0996a6a10a29 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 18:02:28 +0200 Subject: [PATCH 10/13] Be careful to always use .classSymbol to retrieve a parent symbol Parent types can be aliases, so .typeSymbol can be wrong. --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 13 ++++--------- .../dotty/tools/dotc/transform/CheckReentrant.scala | 2 +- .../tools/dotc/transform/GenericSignatures.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 +- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- tests/neg/parser-stability-21.scala | 2 ++ 6 files changed, 12 insertions(+), 15 deletions(-) create mode 100644 tests/neg/parser-stability-21.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index cd727cbc4135..6d508a26b4b6 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1466,16 +1466,11 @@ object SymDenotations { if (classParents.isEmpty && !emptyParentsExpected) onBehalf.signalProvisional() val builder = new BaseDataBuilder - for (p <- classParents) { - var pcls = p.typeSymbol - if (!pcls.isClass) pcls = p.underlyingClassRef(refinementOK = false).typeSymbol - // This roundabout way is necessary for avoiding cyclic references. - // A test case is CompilationTests.compileMixed - pcls match { + for (p <- classParents) + p.classSymbol match { case pcls: ClassSymbol => builder.addAll(pcls.baseClasses) case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") } - } (classSymbol :: builder.baseClasses, builder.baseClassSet) } @@ -1604,7 +1599,7 @@ object SymDenotations { def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match { case p :: ps => val denots1 = collect(denots, ps) - p.typeSymbol.denot match { + p.classSymbol.denot match { case parentd: ClassDenotation => denots1 union parentd.nonPrivateMembersNamed(name) @@ -1753,7 +1748,7 @@ object SymDenotations { var names = Set[Name]() def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name for (p <- classParents) - for (name <- p.typeSymbol.asClass.memberNames(keepOnly)) + for (name <- p.classSymbol.asClass.memberNames(keepOnly)) maybeAdd(name) val ownSyms = if (keepOnly eq implicitFilter) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index cdb72fd429d8..c60d8edf04fc 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -82,7 +82,7 @@ class CheckReentrant extends MiniPhase { } } for (parent <- cls.classInfo.classParents) - addVars(parent.typeSymbol.asClass) + addVars(parent.classSymbol.asClass) } } } diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index 944dc240491c..276e45a9eac8 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -53,7 +53,7 @@ object GenericSignatures { val validParents = if (isTraitSignature) // java is unthrilled about seeing interfaces inherit from classes - minParents filter (p => isInterfaceOrTrait(p.typeSymbol)) + minParents filter (p => isInterfaceOrTrait(p.classSymbol)) else minParents val ps = ensureClassAsFirstParent(validParents) @@ -329,11 +329,11 @@ object GenericSignatures { def isUnshadowed(psym: Symbol) = !(psyms exists (qsym => (psym ne qsym) && (qsym isSubClass psym))) val cs = parents.iterator.filter { p => // isUnshadowed is a bit expensive, so try classes first - val psym = p.typeSymbol + val psym = p.classSymbol psym.ensureCompleted() psym.isClass && !psym.is(Trait) && isUnshadowed(psym) } - (if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.typeSymbol))).next() + (if (cs.hasNext) cs else parents.iterator.filter(p => isUnshadowed(p.classSymbol))).next() } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 99c4116d8637..5990b102dac0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -449,7 +449,7 @@ trait ImplicitRunInfo { self: Run => comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] } def addParentScope(parent: Type): Unit = - iscopeRefs(tp.baseType(parent.typeSymbol)) foreach addRef + iscopeRefs(tp.baseType(parent.classSymbol)) foreach addRef val companion = cls.companionModule if (companion.exists) addRef(companion.termRef) cls.classParents foreach addParentScope diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 0864fde2ff81..bec2403aff7d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -103,7 +103,7 @@ object RefChecks { cls.pos) } for (parent <- cinfo.classParents) - checkSelfConforms(parent.typeSymbol.asClass, "illegal inheritance", "parent") + checkSelfConforms(parent.classSymbol.asClass, "illegal inheritance", "parent") for (reqd <- cinfo.cls.givenSelfType.classSymbols) checkSelfConforms(reqd, "missing requirement", "required") case _ => diff --git a/tests/neg/parser-stability-21.scala b/tests/neg/parser-stability-21.scala new file mode 100644 index 000000000000..97bbe74c6068 --- /dev/null +++ b/tests/neg/parser-stability-21.scala @@ -0,0 +1,2 @@ +class x0[x1[]] // error + extends x1[ // error // error From c3bd95a8bc0a41fa4e19a9f5f5c0102428165375 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 18:02:47 +0200 Subject: [PATCH 11/13] Harden typedPackageDef --- .../src/dotty/tools/dotc/typer/Typer.scala | 18 +++++++++--------- tests/neg/parser-stability-22.scala | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 tests/neg/parser-stability-22.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c5a7f96b2411..bfefd02116b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1626,16 +1626,16 @@ class Typer extends Namer def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { val pid1 = typedExpr(tree.pid, AnySelectionProto)(ctx.addMode(Mode.InPackageClauseName)) val pkg = pid1.symbol - - // Package will not exist if a duplicate type has already been entered, see - // `tests/neg/1708.scala`, else branch's error message should be supressed - if (pkg.exists) { - if (!pkg.is(Package)) ctx.error(PackageNameAlreadyDefined(pkg), tree.pos) - val packageCtx = ctx.packageContext(tree, pkg) - val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageCtx) - cpy.PackageDef(tree)(pid1.asInstanceOf[RefTree], stats1) withType pkg.termRef + pid1 match { + case pid1: RefTree if pkg.exists => + if (!pkg.is(Package)) ctx.error(PackageNameAlreadyDefined(pkg), tree.pos) + val packageCtx = ctx.packageContext(tree, pkg) + val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageCtx) + cpy.PackageDef(tree)(pid1, stats1).withType(pkg.termRef) + case _ => + // Package will not exist if a duplicate type has already been entered, see `tests/neg/1708.scala` + errorTree(tree, i"package ${tree.pid.name} does not exist") } - else errorTree(tree, i"package ${tree.pid.name} does not exist") } def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { diff --git a/tests/neg/parser-stability-22.scala b/tests/neg/parser-stability-22.scala new file mode 100644 index 000000000000..83e21b31816c --- /dev/null +++ b/tests/neg/parser-stability-22.scala @@ -0,0 +1,3 @@ +package x0.x0 {} +object x0 { + def x0 (implicit // error // error From 5f9f3a74443aa21305fa70f2a792cca092f0bb3e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 18:13:24 +0200 Subject: [PATCH 12/13] Fix indentation --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6d508a26b4b6..af680510de14 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1467,7 +1467,7 @@ object SymDenotations { onBehalf.signalProvisional() val builder = new BaseDataBuilder for (p <- classParents) - p.classSymbol match { + p.classSymbol match { case pcls: ClassSymbol => builder.addAll(pcls.baseClasses) case _ => assert(isRefinementClass || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p") } From 690321d2aa75001873bf4a2e5183cacf6bffe61f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Apr 2018 18:38:16 +0200 Subject: [PATCH 13/13] Streamline "assert errors reported" logic --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 5 ++--- .../src/dotty/tools/dotc/core/Decorators.scala | 10 +++++++++- .../src/dotty/tools/dotc/typer/Applications.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Namer.scala | 14 ++++---------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 6 ++---- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index a7586f876435..77885417a443 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -88,9 +88,8 @@ object desugar { else { def msg = s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}" - if (ctx.reporter.errorsReported) ErrorType(msg) - else throw new java.lang.Error(msg) - } + ErrorType(msg).assertingErrorsReported(msg) + } case _ => mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 9639e520d010..1417d15087a1 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -175,8 +175,16 @@ object Decorators { recur(enclosingInlineds, pos) } - implicit class reportingDeco[T](val x: T) extends AnyVal { + implicit class genericDeco[T](val x: T) extends AnyVal { def reporting(op: T => String): T = { println(op(x)); x } + def assertingErrorsReported(implicit ctx: Context): T = { + assert(ctx.reporter.errorsReported) + x + } + def assertingErrorsReported(msg: => String)(implicit ctx: Context): T = { + assert(ctx.reporter.errorsReported, msg) + x + } } implicit class StringInterpolators(val sc: StringContext) extends AnyVal { diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index bc49e12cd0c4..01e66223d4e6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -973,7 +973,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); args2 case Apply(unapply, `dummyArg` :: Nil) => Nil case Inlined(u, _, _) => unapplyImplicits(u) - case _ => assert(ctx.reporter.errorsReported); Nil + case _ => Nil.assertingErrorsReported } var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 281471c599fe..2d6ecae78fe4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -165,10 +165,7 @@ object NamerContextOps { private def findModuleBuddy(name: Name, scope: Scope)(implicit ctx: Context) = { val it = scope.lookupAll(name).filter(_ is Module) if (it.hasNext) it.next() - else { - assert(ctx.reporter.errorsReported, s"no companion $name in $scope") - NoSymbol - } + else NoSymbol.assertingErrorsReported(s"no companion $name in $scope") } } @@ -921,8 +918,7 @@ class Namer { typer: Typer => fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.pos) } case _ => - assert(ctx.reporter.errorsReported) - UnspecifiedErrorType + UnspecifiedErrorType.assertingErrorsReported } } @@ -1036,10 +1032,8 @@ class Namer { typer: Typer => */ def moduleValSig(sym: Symbol)(implicit ctx: Context): Type = { val clsName = sym.name.moduleClassName - val cls = ctx.denotNamed(clsName).suchThat(_ is ModuleClass).orElse { - assert(ctx.reporter.errorsReported) - ctx.newStubSymbol(ctx.owner, clsName) - } + val cls = ctx.denotNamed(clsName).suchThat(_ is ModuleClass) + .orElse(ctx.newStubSymbol(ctx.owner, clsName).assertingErrorsReported) ctx.owner.thisType.select(clsName, cls) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bfefd02116b7..24ed19112fb6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1448,10 +1448,8 @@ class Typer extends Namer } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context): Tree = track("typedClassDef") { - if (!cls.info.isInstanceOf[ClassInfo]) { - assert(ctx.reporter.errorsReported) - return cdef.withType(UnspecifiedErrorType) - } + if (!cls.info.isInstanceOf[ClassInfo]) return EmptyTree.assertingErrorsReported + val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef val superCtx = ctx.superCallContext