diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 17 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala | 18 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 19 | ||||
-rw-r--r-- | test/files/neg/names-defaults-neg.check | 12 | ||||
-rw-r--r-- | test/files/neg/t5044.check | 4 | ||||
-rw-r--r-- | test/files/neg/t5091.check | 4 | ||||
-rw-r--r-- | test/files/neg/t8463.check | 19 | ||||
-rw-r--r-- | test/files/neg/t8841.check | 9 | ||||
-rw-r--r-- | test/files/neg/t8841.scala | 15 |
9 files changed, 95 insertions, 22 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 5c36bd9d28..c80aaea160 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -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 @@ -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)) } @@ -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) } diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 50f658f68d..39cd610b1c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -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 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index e4255e5333..3a85d16f55 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -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 { @@ -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) @@ -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 { @@ -4435,7 +4439,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. @@ -4483,10 +4487,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]) = { @@ -4537,6 +4545,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) }) diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index 20ddd55f1f..2db24b6f32 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -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. @@ -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. diff --git a/test/files/neg/t5044.check b/test/files/neg/t5044.check index 197da2a4e8..dc3708123f 100644 --- a/test/files/neg/t5044.check +++ b/test/files/neg/t5044.check @@ -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 diff --git a/test/files/neg/t5091.check b/test/files/neg/t5091.check index abd24e3145..156f695f41 100644 --- a/test/files/neg/t5091.check +++ b/test/files/neg/t5091.check @@ -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 diff --git a/test/files/neg/t8463.check b/test/files/neg/t8463.check index 1a3eea2870..9aaacf8391 100644 --- a/test/files/neg/t8463.check +++ b/test/files/neg/t8463.check @@ -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 diff --git a/test/files/neg/t8841.check b/test/files/neg/t8841.check new file mode 100644 index 0000000000..ad525dc3f8 --- /dev/null +++ b/test/files/neg/t8841.check @@ -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 diff --git a/test/files/neg/t8841.scala b/test/files/neg/t8841.scala new file mode 100644 index 0000000000..80430d997e --- /dev/null +++ b/test/files/neg/t8841.scala @@ -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 + } +} |