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
17 changes: 13 additions & 4 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ trait ContextErrors {
case class NormalTypeError(underlyingTree: Tree, errMsg: String)
extends TreeTypeError

/**
* Marks a TypeError that was constructed from a CyclicReference (under silent).
* This is used for named arguments, where we need to know if an assignment expression
* failed with a cyclic reference or some other type error.
*/
class NormalTypeErrorFromCyclicReference(underlyingTree: Tree, errMsg: String)
extends NormalTypeError(underlyingTree, errMsg)

case class AccessTypeError(underlyingTree: Tree, errMsg: String)
extends TreeTypeError

Expand Down Expand Up @@ -1087,8 +1095,9 @@ trait ContextErrors {
// hence we (together with reportTypeError in TypeDiagnostics) make sure that this CyclicReference
// evades all the handlers on its way and successfully reaches `isCyclicOrErroneous` in Implicits
throw ex
case CyclicReference(sym, info: TypeCompleter) =>
issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage())
case c @ CyclicReference(sym, info: TypeCompleter) =>
val error = new NormalTypeErrorFromCyclicReference(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage)
issueTypeError(error)
case _ =>
contextNamerErrorGen.issue(TypeErrorWithUnderlyingTree(tree, ex))
}
Expand Down Expand Up @@ -1275,8 +1284,8 @@ trait ContextErrors {
}

def WarnAfterNonSilentRecursiveInference(param: Symbol, arg: Tree)(implicit context: Context) = {
val note = "type-checking the invocation of "+ param.owner +" checks if the named argument expression '"+ param.name + " = ...' is a valid assignment\n"+
"in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for "+ param.name +"."
val note = "failed to determine if '"+ param.name + " = ...' is a named argument or an assignment expression.\n"+
"an explicit type is required for the definition mentioned in the error message above."
context.warning(arg.pos, note)
}

Expand Down
18 changes: 16 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,22 @@ trait NamesDefaults { self: Analyzer =>
WarnAfterNonSilentRecursiveInference(param, arg)(context)
res
} match {
case SilentResultValue(t) => !t.isErroneous // #4041
case _ => false
case SilentResultValue(t) =>
!t.isErroneous // #4041
case SilentTypeError(e: NormalTypeErrorFromCyclicReference) =>
// If we end up here, the CyclicReference was reported in a silent context. This can
// happen for local definitions, when the completer for a definition is created during
// type checking in silent mode. ContextErrors.TypeSigError catches that cyclic reference
// and transforms it into a NormalTypeErrorFromCyclicReference.
// The cycle needs to be reported, because the program cannot be typed: we don't know
// if we have an assignment or a named arg.
context.issue(e)
// 'err = true' is required because we're in a silent context
WarnAfterNonSilentRecursiveInference(param, arg)(context)
false
case _ =>
// We got a type error, so it cannot be an assignment (it doesn't type check as one).
false
}
catch {
// `silent` only catches and returns TypeErrors which are not
Expand Down
19 changes: 14 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case s : SilentTypeError => f(s.reportableErrors)
}
}
class SilentTypeError private(val errors: List[AbsTypeError]) extends SilentResult[Nothing] {
class SilentTypeError private(val errors: List[AbsTypeError], val warnings: List[(Position, String)]) extends SilentResult[Nothing] {
override def isEmpty = true
def err: AbsTypeError = errors.head
def reportableErrors = errors match {
Expand All @@ -87,10 +87,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
object SilentTypeError {
def apply(errors: AbsTypeError*): SilentTypeError = new SilentTypeError(errors.toList)
def apply(errors: AbsTypeError*): SilentTypeError = apply(errors.toList, Nil)
def apply(errors: List[AbsTypeError], warnings: List[(Position, String)]): SilentTypeError = new SilentTypeError(errors, warnings)
// todo: this extracts only one error, should be a separate extractor.
def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption
}

// todo: should include reporter warnings in SilentResultValue.
// e.g. tryTypedApply could print warnings on arguments when the typing succeeds.
case class SilentResultValue[+T](value: T) extends SilentResult[T] { override def isEmpty = false }

def newTyper(context: Context): Typer = new NormalTyper(context)
Expand Down Expand Up @@ -661,7 +665,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
@inline def wrapResult(reporter: ContextReporter, result: T) =
if (reporter.hasErrors) {
stopStats()
SilentTypeError(reporter.errors: _*)
SilentTypeError(reporter.errors.toList, reporter.warnings.toList)
} else SilentResultValue(result)

try {
Expand Down Expand Up @@ -4401,7 +4405,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
val start = if (Statistics.canEnable) Statistics.startTimer(failedApplyNanos) else null

def onError(typeErrors: Seq[AbsTypeError]): Tree = {
def onError(typeErrors: Seq[AbsTypeError], warnings: Seq[(Position, String)]): Tree = {
if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, start)

// If the problem is with raw types, copnvert to existentials and try again.
Expand Down Expand Up @@ -4449,10 +4453,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
typeErrors foreach context.issue
warnings foreach { case (p, m) => context.warning(p, m) }
setError(treeCopy.Apply(tree, fun, args))
}

silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError
silent(_.doTypedApply(tree, fun, args, mode, pt)) match {
case SilentResultValue(value) => value
case e: SilentTypeError => onError(e.errors, e.warnings)
}
}

def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = {
Expand Down Expand Up @@ -4503,6 +4511,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case err: SilentTypeError =>
onError({
err.reportableErrors foreach context.issue
err.warnings foreach { case (p, m) => context.warning(p, m) }
args foreach (arg => typed(arg, mode, ErrorType))
setError(tree)
})
Expand Down
12 changes: 6 additions & 6 deletions test/files/neg/names-defaults-neg.check
Original file line number Diff line number Diff line change
Expand Up @@ -151,15 +151,15 @@ names-defaults-neg.scala:144: error: variable definition needs type because 'x'
names-defaults-neg.scala:147: error: variable definition needs type because 'x' is used as a named argument in its body.
object t6 { var x = t.f(x = 1) }
^
names-defaults-neg.scala:147: warning: type-checking the invocation of method f checks if the named argument expression 'x = ...' is a valid assignment
in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for x.
names-defaults-neg.scala:147: warning: failed to determine if 'x = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
object t6 { var x = t.f(x = 1) }
^
names-defaults-neg.scala:150: error: variable definition needs type because 'x' is used as a named argument in its body.
class t9 { var x = t.f(x = 1) }
^
names-defaults-neg.scala:150: warning: type-checking the invocation of method f checks if the named argument expression 'x = ...' is a valid assignment
in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for x.
names-defaults-neg.scala:150: warning: failed to determine if 'x = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
class t9 { var x = t.f(x = 1) }
^
names-defaults-neg.scala:164: error: variable definition needs type because 'x' is used as a named argument in its body.
Expand All @@ -174,8 +174,8 @@ names-defaults-neg.scala:170: error: reference to x is ambiguous; it is both a m
names-defaults-neg.scala:177: error: variable definition needs type because 'x' is used as a named argument in its body.
class u15 { var x = u.f(x = 1) }
^
names-defaults-neg.scala:177: warning: type-checking the invocation of method f checks if the named argument expression 'x = ...' is a valid assignment
in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for x.
names-defaults-neg.scala:177: warning: failed to determine if 'x = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
class u15 { var x = u.f(x = 1) }
^
names-defaults-neg.scala:180: error: reference to x is ambiguous; it is both a method parameter and a variable in scope.
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/t5044.check
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
t5044.scala:7: error: recursive value a needs type
val id = m(a)
^
t5044.scala:6: warning: type-checking the invocation of method foo checks if the named argument expression 'id = ...' is a valid assignment
in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for id.
t5044.scala:6: warning: failed to determine if 'id = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
val a = foo(id = 1)
^
one warning found
Expand Down
4 changes: 2 additions & 2 deletions test/files/neg/t5091.check
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
t5091.scala:8: error: recursive value xxx needs type
val param = bar(xxx)
^
t5091.scala:7: warning: type-checking the invocation of method foo checks if the named argument expression 'param = ...' is a valid assignment
in the current scope. The resulting type inference error (see above) can be fixed by providing an explicit type in the local definition for param.
t5091.scala:7: warning: failed to determine if 'param = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
val xxx = foo(param = null)
^
one warning found
Expand Down
19 changes: 18 additions & 1 deletion test/files/neg/t8463.check
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,21 @@ Note that implicit conversions are not applicable because they are ambiguous:
are possible conversion functions from Long to ?T[Long]
insertCell(Foo(5))
^
one error found
t8463.scala:5: error: no type parameters for method apply: (activity: T[Long])Test.Foo[T] in object Foo exist so that it can be applied to arguments (Long)
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Long
required: ?T[Long]
insertCell(Foo(5))
^
t8463.scala:5: error: type mismatch;
found : Long(5L)
required: T[Long]
insertCell(Foo(5))
^
t8463.scala:5: error: type mismatch;
found : Test.Foo[T]
required: Test.Foo[Test.Cell]
insertCell(Foo(5))
^
four errors found
9 changes: 9 additions & 0 deletions test/files/neg/t8841.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
t8841.scala:13: error: recursive value c needs type
val ambiguousName = c.ambiguousName
^
t8841.scala:12: warning: failed to determine if 'ambiguousName = ...' is a named argument or an assignment expression.
an explicit type is required for the definition mentioned in the error message above.
val c = new Cell(ambiguousName = Some("bla"))
^
one warning found
one error found
15 changes: 15 additions & 0 deletions test/files/neg/t8841.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class Cell(val ambiguousName: Option[String])

class Test {
def wrap(f: Any): Nothing = ???

wrap {
// the namer for these two ValDefs is created when typing the argument expression
// of wrap. This happens to be in a silent context (tryTypedApply). Therefore, the
// cyclic reference will not be thrown, but transformed into a NormalTypeError by
// `silent`. This requires different handling in NamesDefaults.

val c = new Cell(ambiguousName = Some("bla"))
val ambiguousName = c.ambiguousName
}
}