diff options
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 7 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 2 | ||||
-rw-r--r-- | test/dotc/tests.scala | 1 | ||||
-rw-r--r-- | tests/neg/projections.scala | 7 | ||||
-rw-r--r-- | tests/pos/projections.scala | 14 |
6 files changed, 30 insertions, 7 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 89facfee5..827e851a9 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -117,14 +117,15 @@ object Types { } /** A type T is a legal prefix in a type selection T#A if - * T is stable or T contains no abstract types + * T is stable or T contains no abstract types except possibly A. * !!! Todo: What about non-final vals that contain abstract types? */ - final def isLegalPrefix(implicit ctx: Context): Boolean = + final def isLegalPrefixFor(selector: Name)(implicit ctx: Context): Boolean = isStable || { val absTypeNames = memberNames(abstractTypeNameFilter) if (absTypeNames.nonEmpty) typr.println(s"abstract type members of ${this.showWithUnderlying()}: $absTypeNames") - absTypeNames.isEmpty + absTypeNames.isEmpty || + absTypeNames.head == selector && absTypeNames.tail.isEmpty } /** Is this type guaranteed not to have `null` as a value? diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 5e52c5d7e..7da00e051 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -51,8 +51,8 @@ trait Checking { /** Check that type `tp` is a legal prefix for '#'. * @return The type itself */ - def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit = - if (!tp.isLegalPrefix) ctx.error(d"$tp is not a valid prefix for '#'", pos) + def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = + if (!tp.isLegalPrefixFor(selector)) ctx.error(d"$tp is not a valid prefix for '# $selector'", pos) /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is * false check that `tp` is a trait. @@ -139,7 +139,7 @@ trait NoChecking extends Checking { override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = () override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit = () + override def checkLegalPrefix(tp: Type, selector: Name, 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 d6b724270..c01cf714f 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -277,7 +277,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = track("typedSelectFromTypeTree") { val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) - checkLegalPrefix(qual1.tpe, qual1.pos) + checkLegalPrefix(qual1.tpe, tree.name, qual1.pos) assignType(cpy.SelectFromTypeTree(tree, qual1, tree.name), qual1) } diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index c7b0dc044..64d520500 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -67,6 +67,7 @@ class tests extends CompilerTest { @Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", "-language:noAutoTupling" :: Nil, xerrors = 4) @Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 4) @Test def neg_companions = compileFile(negDir, "companions", xerrors = 1) + @Test def neg_projections = compileFile(negDir, "projections", xerrors = 1) @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 1) @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 4) @Test def neg_t0273_doubledefs = compileFile(negDir, "t0273", xerrors = 1) diff --git a/tests/neg/projections.scala b/tests/neg/projections.scala new file mode 100644 index 000000000..5d80e1151 --- /dev/null +++ b/tests/neg/projections.scala @@ -0,0 +1,7 @@ +class projections { + + class Lambda { type Arg; type Apply } + + var x: (Lambda { type Apply = Int }) # Apply = _ // error: illegal prefix + +} diff --git a/tests/pos/projections.scala b/tests/pos/projections.scala new file mode 100644 index 000000000..894a00bcf --- /dev/null +++ b/tests/pos/projections.scala @@ -0,0 +1,14 @@ +class projections { + + class Lambda { type Arg; type Apply } + + var x: (Lambda { type Apply = Int; type Arg = String }) # Apply = _ + var y: Int = _ + x = y + y = x + + var xx: (Lambda { type Apply = Arg } { type Arg = Int }) # Apply = _ + xx = y + y = xx + +} |