Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -527,17 +527,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case _ =>
}
either(recur(tp1, tp21), recur(tp1, tp22)) || fourthTry
case tp2: MethodOrPoly =>
case tp2: MethodType =>
def compareMethod = tp1 match {
case tp1: MethodOrPoly =>
(tp1.signature consistentParams tp2.signature) &&
matchingParams(tp1, tp2) &&
(!tp2.isImplicitMethod || tp1.isImplicitMethod) &&
isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1))
case _ =>
false
case tp1: MethodType => compareMethodOrPoly(tp1, tp2)
case _ => false
}
compareMethod
case tp2: PolyType =>
def comparePoly = tp1 match {
case tp1: PolyType => compareMethodOrPoly(tp1, tp2)
case _ => false
}
comparePoly
case tp2 @ ExprType(restpe2) =>
def compareExpr = tp1 match {
// We allow ()T to be a subtype of => T.
Expand Down Expand Up @@ -573,6 +574,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
fourthTry
}

def compareMethodOrPoly(tp1: MethodOrPoly, tp2: MethodOrPoly) =
(tp1.signature consistentParams tp2.signature) &&
matchingParams(tp1, tp2) &&
(!tp2.isImplicitMethod || tp1.isImplicitMethod) &&
isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1))

def fourthTry: Boolean = tp1 match {
case tp1: TypeRef =>
tp1.info match {
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1804,10 +1804,11 @@ object Types {
param.derivedSingleDenotation(param, argInfo)
}
else {
assert(ctx.reporter.errorsReported,
i"""bad parameter reference $this at ${ctx.phase}
|the parameter is ${param.showLocated} but the prefix $prefix
|does not define any corresponding arguments.""")
if (!ctx.reporter.errorsReported)
throw new TypeError(
i"""bad parameter reference $this at ${ctx.phase}
|the parameter is ${param.showLocated} but the prefix $prefix
|does not define any corresponding arguments.""")
NoDenotation
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Uniques.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object Uniques {
if (h == NotCached) newType
else {
val r = findPrevious(h, prefix, designator)
if (r ne null) r else addEntryAfterScan(newType)
if ((r ne null) && (r.isTerm == isTerm)) r else addEntryAfterScan(newType)
}
}
}
Expand Down
34 changes: 25 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1929,7 +1929,10 @@ object Parsers {
commaSeparated(importExpr) match {
case t :: rest =>
// The first import should start at the position of the keyword.
t.withPos(t.pos.withStart(offset)) :: rest
val firstPos =
if (t.pos.exists) t.pos.withStart(offset)
else Position(offset, in.lastOffset)
t.withPos(firstPos) :: rest
case nil => nil
}
}
Expand Down Expand Up @@ -2422,22 +2425,35 @@ object Parsers {
(self, if (stats.isEmpty) List(EmptyTree) else stats.toList)
}

/** RefineStatSeq ::= RefineStat {semi RefineStat}
* RefineStat ::= Dcl
* |
* (in reality we admit Defs and filter them out afterwards)
/** RefineStatSeq ::= RefineStat {semi RefineStat}
* RefineStat ::= ‘val’ VarDcl
* | ‘def’ DefDcl
* | ‘type’ {nl} TypeDcl
* (in reality we admit Defs and vars and filter them out afterwards in `checkLegal`)
*/
def refineStatSeq(): List[Tree] = {
val stats = new ListBuffer[Tree]
def checkLegal(tree: Tree): List[Tree] = {
val isLegal = tree match {
case tree: ValDef => tree.rhs.isEmpty && !tree.mods.flags.is(Mutable)
case tree: DefDef => tree.rhs.isEmpty
case tree: TypeDef => true
case _ => false
}
if (isLegal) tree :: Nil
else {
syntaxError("illegal refinement", tree.pos)
Nil
}
}
while (!isStatSeqEnd) {
if (isDclIntro) {
stats += defOrDcl(in.offset, Modifiers())
} else if (!isStatSep) {
if (isDclIntro)
stats ++= checkLegal(defOrDcl(in.offset, Modifiers()))
else if (!isStatSep)
syntaxErrorOrIncomplete(
"illegal start of declaration" +
(if (inFunReturnType) " (possible cause: missing `=' in front of current method body)"
else ""))
}
acceptStatSepUnlessAtEnd()
}
stats.toList
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ object Formatting {
entry match {
case param: TypeParamRef =>
s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}"
case param: TermParamRef =>
s"is a reference to a value parameter"
case sym: Symbol =>
val info =
if (ctx.gadt.bounds.contains(sym))
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
val denot = cx.denotNamed(getterName)
if (denot.exists) ref(TermRef(cx.owner.thisType, getterName, denot))
else {
assert(ctx.mode.is(Mode.Interactive),
assert(ctx.mode.is(Mode.Interactive) || ctx.reporter.errorsReported,
s"non-existent getter denotation ($denot) for getter($getterName)")
findGetter(cx.outer)
}
Expand Down Expand Up @@ -720,7 +720,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}

fun1.tpe match {
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err)
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, proto.typedArgs).withType(err)
case TryDynamicCallType => typedDynamicApply(tree, pt)
case _ =>
if (originalProto.isDropped) fun1
Expand Down Expand Up @@ -967,6 +967,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
}

var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos)
Expand Down
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import config.Printers.typr
import NameKinds.DefaultGetterName

import collection.mutable
import SymDenotations.NoCompleter
import SymDenotations.{NoCompleter, NoDenotation}
import dotty.tools.dotc.reporting.diagnostic.{ErrorMessageID, Message}
import dotty.tools.dotc.reporting.diagnostic.messages._
import dotty.tools.dotc.transform.ValueClasses._
Expand Down Expand Up @@ -731,20 +731,25 @@ trait Checking {
case Nil =>
}

/** Check that all named type that form part of this type have a denotation.
/** Check that all named types that form part of this type have a denotation.
* Called on inferred (result) types of ValDefs and DefDefs.
* This could fail for types where the member was originally available as part
* of the self type, yet is no longer visible once the `this` has been replaced
* by some other prefix. See neg/i3083.scala
*/
def checkMembersOK(tp: Type, pos: Position)(implicit ctx: Context): Type = {
var ok = true
val check: Type => Unit = {
case ref: NamedType if !ref.denot.exists =>
ctx.error(em"$ref is not defined in inferred type $tp", pos)
case ref: NamedType =>
val d = try ref.denot catch { case ex: TypeError => NoDenotation }
if (!d.exists) {
ctx.error(em"$ref is not defined in inferred type $tp", pos)
ok = false
}
case _ =>
}
tp.foreachPart(check, stopAtStatic = true)
tp
if (ok) tp else UnspecifiedErrorType
}

/** Check that all non-synthetic references of the form `<ident>` or
Expand Down
21 changes: 13 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -907,14 +907,19 @@ class Namer { typer: Typer =>
case TypeApply(core, targs) => (core, targs)
case core => (core, Nil)
}
val Select(New(tpt), nme.CONSTRUCTOR) = core
val targs1 = targs map (typedAheadType(_))
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
if (ptype.typeParams.isEmpty) ptype
else {
if (denot.is(ModuleClass) && denot.sourceModule.is(Implicit))
missingType(denot.symbol, "parent ")(creationContext)
fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.pos)
core match {
case Select(New(tpt), nme.CONSTRUCTOR) =>
val targs1 = targs map (typedAheadType(_))
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
if (ptype.typeParams.isEmpty) ptype
else {
if (denot.is(ModuleClass) && denot.sourceModule.is(Implicit))
missingType(denot.symbol, "parent ")(creationContext)
fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.pos)
}
case _ =>
assert(ctx.reporter.errorsReported)
UnspecifiedErrorType
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ trait TypeAssigner {
def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = {
var qualType = qual1.tpe.widenIfUnstable
if (qualType.isLambdaSub) qualType = errorType(em"$qualType takes type parameters", qual1.pos)
else if (!qualType.isInstanceOf[TermType]) qualType = errorType(em"$qualType is illegal as a selection prefix", qual1.pos)
val ownType = selectionType(qualType, tree.name, tree.pos)
ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.pos)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,7 @@ class Typer extends Namer
val refineCls = createSymbol(refineClsDef).asClass
val TypeDef(_, impl: Template) = typed(refineClsDef)
val refinements1 = impl.body
assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1")
assert(tree.refinements.hasSameLengthAs(refinements1), i"${tree.refinements}%, % > $refinements1%, %")
val seen = mutable.Set[Symbol]()
for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions
typr.println(s"adding refinement $refinement")
Expand Down Expand Up @@ -2447,7 +2447,7 @@ class Typer extends Namer
if (isFullyDefined(wtp, force = ForceDegree.all) &&
ctx.typerState.constraint.ne(prevConstraint)) readapt(tree)
else err.typeMismatch(tree, pt, failure)
if (ctx.mode.is(Mode.ImplicitsEnabled))
if (ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType)
inferView(tree, pt) match {
case SearchSuccess(inferred, _, _) =>
readapt(inferred)(ctx.retractMode(Mode.ImplicitsEnabled))
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ ParamValueType ::= Type [‘*’]
TypeArgs ::= ‘[’ ArgTypes ‘]’ ts
NamedTypeArg ::= id ‘=’ Type NamedArg(id, t)
NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’ nts
Refinement ::= ‘{’ [Dcl] {semi [Dcl]} ‘}’ ds
Refinement ::= ‘{’ [RefineDcl] {semi [RefineDcl]} ‘}’ ds
TypeBounds ::= [‘>:’ Type] [‘<:’ Type] | INT TypeBoundsTree(lo, hi)
TypeParamBounds ::= TypeBounds {‘<%’ Type} {‘:’ Type} ContextBounds(typeBounds, tps)
```
Expand Down Expand Up @@ -299,12 +299,12 @@ ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’]

### Declarations and Definitions
```ebnf
Dcl ::= ‘val’ ValDcl
| ‘var’ VarDcl
RefineDcl ::= ‘val’ VarDcl
| ‘def’ DefDcl
| ‘type’ {nl} TypeDcl
| INT

Dcl ::= RefineDcl
| ‘var’ VarDcl
ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
DefDcl ::= DefSig [‘:’ Type] DefDef(_, name, tparams, vparamss, tpe, EmptyTree)
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i1640.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object Test extends App {
List(1, 2, 3) map (_ match { case x => x + 1 })
List((1, 2)) x (_ match { case (x, z) => x + z }) // error
List((1, 2)) x (_ match { case (x, z) => x + z }) // error // error // error
}
2 changes: 1 addition & 1 deletion tests/neg/i1641.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package object println { def bippy(x: Int, y: Int, z: Int) = "(Int, Int, Int)" }
object Test {
def main(args: Array[String]): Unit = {
println(bar.bippy(5.5)) // error
println(bar.bippy(1, 2, 3)) // error
println(bar.bippy(1, 2, 3)) // error // error
}
}
2 changes: 1 addition & 1 deletion tests/neg/i1707.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object DepBug {
}
{ // error: Null does not take parameters (follow on)
import dep._
a m (b)
a m (b) // error: not found: a
}
dep.a m (dep b) // error (follow on)
}
7 changes: 7 additions & 0 deletions tests/neg/illegal-refinements.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait x0 {

type T = String { val x: Int = 1 } // error: illegal refinement
type U = String { def x(): Int = 1 } // error: illegal refinement
type V = String { var x: Int } // error: illegal refinement

}
3 changes: 3 additions & 0 deletions tests/neg/parser-stability-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
object x0 {
x1 match // error
def this // error // error
13 changes: 13 additions & 0 deletions tests/neg/parser-stability-10.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
object i0 {
def unapply(i1: Int)(i6: List[Int]): Int = {
def i7(i8: i0) = i1 match { // error
case i0(_) => // error
(i1, i8) match {
case i0(i1, i1) => case _ => i2 // error // error
}
}
} // error
object i5 {
import collection.mutable._
try { ??? mutable { case i1(i5, i3, i4) => i5 }} // error // error // error
} // error
2 changes: 2 additions & 0 deletions tests/neg/parser-stability-2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
trait x0 {
new _ // error // error
4 changes: 4 additions & 0 deletions tests/neg/parser-stability-3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
object x0 {
def x0(x1: x2 = 0, x1 x3) x4 = // error // error // error
x5 x6 x7[x8[x9 x2]] = x0
val x10 = x0( ) // error // error
2 changes: 2 additions & 0 deletions tests/neg/parser-stability-4.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class x0{
def x0: x0 = (x0 => x0) => x0) // error // error
3 changes: 3 additions & 0 deletions tests/neg/parser-stability-5.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trait x0 {
x1 : { // error
var x2 // error // error
3 changes: 3 additions & 0 deletions tests/neg/parser-stability-6.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class x0 {
1 + x1 * / |= 2 // error: not found: x1
}
8 changes: 8 additions & 0 deletions tests/neg/parser-stability-7.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class x0[x1] {
def x2: x1
}
trait x3 extends x0 {
class x2
var x2 = 0 // error
var x4 = x5 x2 // error
}
1 change: 1 addition & 0 deletions tests/neg/parser-stability-8.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
case class I0 extends this // error // error
1 change: 1 addition & 0 deletions tests/neg/parser-stability-9.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import // error
2 changes: 2 additions & 0 deletions tests/neg/parser-stability.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class I2[I2] {
def I2: I2 = (I2 => I2) => I2 // error // error
2 changes: 1 addition & 1 deletion tests/neg/structural.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ object Test3 {
def g(x: { type T ; def t: T ; def f(a: T): Boolean }) = x.f(x.t) // error: no ClassTag for x.T
g(new { type T = Int; def t = 4; def f(a:T) = true })
g(new { type T = Any; def t = 4; def f(a:T) = true })
val y: { type T = Int; def t = 4; def f(a:T) = true }
val y: { type T = Int; def t = 4; def f(a:T) = true } // error: illegal refinement // error: illegal refinement
= new { type T = Int; def t = 4; def f(a:T) = true }

def h(x: { def f[T](a: T): Int }) = x.f[Int](4) // error: polymorphic refinement method ... no longer allowed
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/t7239.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Test {
(implicit F0: NoImplicit): HasWithFilter = ???
}

BrokenMethod().withFilter(_ => true) // error
BrokenMethod().withFilter(_ => true) // error // error
BrokenMethod().filter(_ => true) // ok

locally {
Expand All @@ -35,6 +35,6 @@ object Test {
// `(B => Boolean)`. Only later during pickling does the
// defensive check for erroneous types in the tree pick up
// the problem.
BrokenMethod().withFilter(x => true) // error
BrokenMethod().withFilter(x => true) // error // error
}
}