From 0843dd3f72d45ffef3a13251085afbe704870ac1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 26 May 2017 17:10:03 +0200 Subject: [PATCH 1/2] Fix #2492: Avoid typing the body when self has error type --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 1 + .../src/dotty/tools/dotc/typer/Typer.scala | 71 ++++++++++--------- tests/neg/i2492.scala | 4 ++ tests/neg/i2492b.scala | 4 ++ tests/repl/i2492.check | 8 +++ 5 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 tests/neg/i2492.scala create mode 100644 tests/neg/i2492b.scala create mode 100644 tests/repl/i2492.check diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 10f2cf7e726d..d1fccac6a2aa 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -466,6 +466,7 @@ trait TypeAssigner { val rsym = refinement.symbol val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info if (rinfo.isError) rinfo + else if (rinfo eq NoType) parent // can happen after failure in self type definition else RefinedType(parent, rsym.name, rinfo) } val refined = (parent.tpe /: refinements)(addRefinement) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index da9eb62f88c8..9f2ce9f8175d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1321,44 +1321,49 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.namePos) val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible - val dummy = localDummy(cls, impl) - val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol)) - cls.setNoInitsFlags((NoInitsInterface /: body1)((fs, stat) => fs & defKind(stat))) - - // Expand comments and type usecases - cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope) - - checkNoDoubleDefs(cls) - val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) - .withType(dummy.nonMemberTermRef) - checkVariance(impl1) - if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) - val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) - if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { - val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) - ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, + if (self1.tpt.tpe.isError) { + // fail fast to avoid typing the body with an error type + cdef.withType(UnspecifiedErrorType) + } else { + val dummy = localDummy(cls, impl) + val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol)) + cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat))) + + // Expand comments and type usecases + cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope) + + checkNoDoubleDefs(cls) + val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) + .withType(dummy.nonMemberTermRef) + checkVariance(impl1) + if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) + val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) + if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { + val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) + ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, cls, isRequired, cdef.pos) - } + } - // Check that phantom lattices are defined in a static object - if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner) - ctx.error("only static objects can extend scala.Phantom", cdef.pos) + // Check that phantom lattices are defined in a static object + if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner) + ctx.error("only static objects can extend scala.Phantom", cdef.pos) - // check value class constraints - checkDerivedValueClass(cls, body1) + // check value class constraints + checkDerivedValueClass(cls, body1) - if (ctx.settings.YretainTrees.value) { - cls.myTree = cdef1 + if (ctx.settings.YretainTrees.value) { + cls.myTree = cdef1 + } + cdef1 + + // todo later: check that + // 1. If class is non-abstract, it is instantiatable: + // - self type is s supertype of own type + // - all type members have consistent bounds + // 2. all private type members have consistent bounds + // 3. Types do not override classes. + // 4. Polymorphic type defs override nothing. } - cdef1 - - // todo later: check that - // 1. If class is non-abstract, it is instantiatable: - // - self type is s supertype of own type - // - all type members have consistent bounds - // 2. all private type members have consistent bounds - // 3. Types do not override classes. - // 4. Polymorphic type defs override nothing. } /** Ensure that the first type in a list of parent types Ps points to a non-trait class. diff --git a/tests/neg/i2492.scala b/tests/neg/i2492.scala new file mode 100644 index 000000000000..d69a837f3f9d --- /dev/null +++ b/tests/neg/i2492.scala @@ -0,0 +1,4 @@ +class Map[K, V] +object Foo { + val s: Map {type Map$K = String; type Map$V = Int} = null // error +} diff --git a/tests/neg/i2492b.scala b/tests/neg/i2492b.scala new file mode 100644 index 000000000000..fbd238c0bdb8 --- /dev/null +++ b/tests/neg/i2492b.scala @@ -0,0 +1,4 @@ +class Map[K] +object Foo { + type X = Map { type Map$$K = String } // error +} diff --git a/tests/repl/i2492.check b/tests/repl/i2492.check new file mode 100644 index 000000000000..f3bd52d48000 --- /dev/null +++ b/tests/repl/i2492.check @@ -0,0 +1,8 @@ +scala> class Map[K, V] +defined class Map +scala> val s: Map {type Map$K =String;type Map$V = Int} = null +-- [E055] Syntax Error: :5:7 ------------------------------------------ +5 |val s: Map {type Map$K =String;type Map$V = Int} = null + | ^^^ + |missing type parameter for [line1$object$$iw$$iw$Map$$K, line1$object$$iw$$iw$Map$$V] => Map[K, V] +scala> :quit From 4dfda044b3cf369cd19b6fb0cb23980a71bb8320 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 28 May 2017 09:47:39 +0200 Subject: [PATCH 2/2] Improve check --- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index d1fccac6a2aa..fc97a9d88a71 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -466,7 +466,7 @@ trait TypeAssigner { val rsym = refinement.symbol val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info if (rinfo.isError) rinfo - else if (rinfo eq NoType) parent // can happen after failure in self type definition + else if (!rinfo.exists) parent // can happen after failure in self type definition else RefinedType(parent, rsym.name, rinfo) } val refined = (parent.tpe /: refinements)(addRefinement)