diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index fb7c6d41e885..83f7a7076a98 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -241,7 +241,7 @@ import transform.SymUtils._ } } - class TypeMismatch(found: Type, expected: Type, addenda: => String*)(using Context) + class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): // replace constrained TypeParamRefs and their typevars by their bounds where possible @@ -280,7 +280,13 @@ import transform.SymUtils._ val (foundStr, expectedStr) = Formatting.typeDiff(found2, expected2)(using printCtx) s"""|Found: $foundStr |Required: $expectedStr""".stripMargin - + whereSuffix + postScript + + whereSuffix + postScript + + override def explain = + val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") + treeStr + "\n" + super.explain + + end TypeMismatch class NotAMember(site: Type, val name: Name, selected: String, addendum: => String = "")(using Context) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 8972a1e12ddd..29fd1adb6688 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -592,7 +592,7 @@ class TreeChecker extends Phase with SymTransformer { !isPrimaryConstructorReturn && !pt.isInstanceOf[FunOrPolyProto]) assert(tree.tpe <:< pt, { - val mismatch = TypeMismatch(tree.tpe, pt) + val mismatch = TypeMismatch(tree.tpe, pt, Some(tree)) i"""|${mismatch.msg} |found: ${infoStr(tree.tpe)} |expected: ${infoStr(pt)} diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 9600963952b4..ec55a33dc72a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -441,7 +441,7 @@ trait Applications extends Compatibility { // it might be healed by an implicit conversion () else - fail(TypeMismatch(methType.resultType, resultType)) + fail(TypeMismatch(methType.resultType, resultType, None)) // match all arguments with corresponding formal parameters matchArgs(orderedArgs, methType.paramInfos, 0) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 3da9d97b2528..0194eb7db1ae 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -131,7 +131,7 @@ object ErrorReporting { case If(_, _, elsep @ Literal(Constant(()))) if elsep.span.isSynthetic => "\nMaybe you are missing an else part for the conditional?" case _ => "" - errorTree(tree, TypeMismatch(treeTp, pt, implicitFailure.whyNoConversion, missingElse)) + errorTree(tree, TypeMismatch(treeTp, pt, Some(tree), implicitFailure.whyNoConversion, missingElse)) } /** A subtype log explaining why `found` does not conform to `expected` */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7e0191048a9c..9a70f797a81d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3881,7 +3881,7 @@ class Typer extends Namer // - it complicates the protocol // - such code patterns usually implies hidden errors in the code // - it's safe/sound to reject the code - report.error(TypeMismatch(tree.tpe, pt, "\npattern type is incompatible with expected type"), tree.srcPos) + report.error(TypeMismatch(tree.tpe, pt, Some(tree), "\npattern type is incompatible with expected type"), tree.srcPos) else val cmp = untpd.Apply( diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index e9284286b070..a88f94565e32 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -177,6 +177,7 @@ class CompilationTests { compileFile("tests/neg-custom-args/matchable.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")), compileFile("tests/neg-custom-args/i7314.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")), compileFile("tests/neg-custom-args/feature-shadowing.scala", defaultOptions.and("-Xfatal-warnings", "-feature")), + compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")), ).checkExpectedErrors() } diff --git a/tests/neg-custom-args/hidden-type-errors.check b/tests/neg-custom-args/hidden-type-errors.check new file mode 100644 index 000000000000..a373e409af2f --- /dev/null +++ b/tests/neg-custom-args/hidden-type-errors.check @@ -0,0 +1,28 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/hidden-type-errors/Test.scala:6:24 -------------------------------- +6 | val x = X.doSomething("XXX") // error + | ^^^^^^^^^^^^^^^^^^^^ + | Found: String + | Required: Int + | This location contains code that was inlined from Test.scala:6 + +Explanation +=========== + +Tree: t12717.A.bar("XXX") + +I tried to show that + String +conforms to + Int +but the comparison trace ended with `false`: + + ==> String <: Int + ==> String <: Int (recurring) + ==> String <: Int (recurring) + <== String <: Int (recurring) = false + <== String <: Int (recurring) = false + <== String <: Int = false + +The tests were made under the empty constraint + +1 error found diff --git a/tests/neg-custom-args/hidden-type-errors/Macro.scala b/tests/neg-custom-args/hidden-type-errors/Macro.scala new file mode 100644 index 000000000000..17b4f3d52ffe --- /dev/null +++ b/tests/neg-custom-args/hidden-type-errors/Macro.scala @@ -0,0 +1,22 @@ +package t12717 + +import scala.quoted._ + +object A: + + def foo(x:Int): Int = ??? + + def bar(x:String): String = ??? + + +object X: + + inline def doSomething[T](inline x:T):Any = ${ + doSomethingImpl('x) + } + + def doSomethingImpl[T:Type](x:Expr[T])(using Quotes):Expr[Any] = + import quotes.reflect._ + val aTerm = '{A}.asTerm + val xBar = Apply(Select.unique(aTerm,"bar"),List(x.asTerm)) + Apply(Select.unique(aTerm,"foo"), List(xBar)).asExpr diff --git a/tests/neg-custom-args/hidden-type-errors/Test.scala b/tests/neg-custom-args/hidden-type-errors/Test.scala new file mode 100644 index 000000000000..180aa07cfb50 --- /dev/null +++ b/tests/neg-custom-args/hidden-type-errors/Test.scala @@ -0,0 +1,6 @@ +package t12717 + + +object Test: + + val x = X.doSomething("XXX") // error