Skip to content

Addendum when apply error involves default arg #21133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
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
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ object Diagnostic:
pos: SourcePosition
) extends Warning(msg, pos)

def unapply(d: Diagnostic): (Message, SourcePosition, Int) = (d.msg, d.pos, d.level)

class Diagnostic(
val msg: Message,
val pos: SourcePosition,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ package reporting

import scala.language.unsafeNulls

import collection.mutable
import collection.mutable.ListBuffer
import core.Contexts.Context
import Diagnostic.*

/** A re-usable Reporter used in Contexts#test */
class ExploringReporter extends StoreReporter(null, fromTyperState = false):
infos = new mutable.ListBuffer[Diagnostic]
infos = ListBuffer.empty[Diagnostic]

override def hasUnreportedErrors: Boolean =
infos.exists(_.isInstanceOf[Error])
Expand Down
10 changes: 6 additions & 4 deletions compiler/src/dotty/tools/dotc/reporting/Message.scala
Original file line number Diff line number Diff line change
Expand Up @@ -394,14 +394,16 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self =>

def mapMsg(f: String => String): Message = new Message(errorId):
val kind = self.kind
def msg(using Context) = f(self.msg)
def explain(using Context) = self.explain
protected def msg(using Context) = f(self.msg)
override protected def msgPostscript(using Context) = self.msgPostscript
protected def explain(using Context) = self.explain
override def canExplain = self.canExplain

def appendExplanation(suffix: => String): Message = new Message(errorId):
val kind = self.kind
def msg(using Context) = self.msg
def explain(using Context) = self.explain ++ suffix
protected def msg(using Context) = self.msg
override protected def msgPostscript(using Context) = self.msgPostscript
protected def explain(using Context) = self.explain ++ suffix
override def canExplain = true

/** Override with `true` for messages that should always be shown even if their
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotc
package reporting

import core.Contexts.*
import collection.mutable
import collection.mutable.ListBuffer
import config.Printers.typr
import Diagnostic.*

Expand All @@ -19,11 +19,11 @@ import Diagnostic.*
*/
class StoreReporter(outer: Reporter | Null = Reporter.NoReporter, fromTyperState: Boolean = false) extends Reporter {

protected var infos: mutable.ListBuffer[Diagnostic] | Null = null
protected var infos: ListBuffer[Diagnostic] | Null = null

override def doReport(dia: Diagnostic)(using Context): Unit = {
typr.println(s">>>> StoredError: ${dia.message}") // !!! DEBUG
if (infos == null) infos = new mutable.ListBuffer
if (infos == null) infos = ListBuffer.empty
infos.uncheckedNN += dia
}

Expand Down
65 changes: 36 additions & 29 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@ import reporting.*
import Nullables.*, NullOpsDecorator.*
import config.{Feature, SourceVersion}

import collection.mutable
import collection.mutable.ListBuffer
import config.Printers.{overload, typr, unapp}
import inlines.Inlines
import interfaces.Diagnostic.ERROR
import TypeApplications.*
import Annotations.Annotation

import Constants.{Constant, IntTag}
import Denotations.SingleDenotation
import annotation.threadUnsafe

import scala.util.control.NonFatal
import dotty.tools.dotc.inlines.Inlines
import scala.util.chaining.given

object Applications {
import tpd.*
Expand Down Expand Up @@ -260,7 +261,7 @@ object Applications {

end UnapplyArgs

def wrapDefs(defs: mutable.ListBuffer[Tree] | Null, tree: Tree)(using Context): Tree =
def wrapDefs(defs: ListBuffer[Tree] | Null, tree: Tree)(using Context): Tree =
if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree

/** Optionally, if `sym` is a symbol created by `resolveMapped`, i.e. representing
Expand Down Expand Up @@ -872,8 +873,8 @@ trait Applications extends Compatibility {
extends Application(methRef, fun.tpe, args, resultType) {
type TypedArg = Tree
def isVarArg(arg: Trees.Tree[T]): Boolean = untpd.isWildcardStarArg(arg)
private var typedArgBuf = new mutable.ListBuffer[Tree]
private var liftedDefs: mutable.ListBuffer[Tree] | Null = null
private var typedArgBuf = ListBuffer.empty[Tree]
private var liftedDefs: ListBuffer[Tree] | Null = null
private var myNormalizedFun: Tree = fun
init()

Expand Down Expand Up @@ -911,11 +912,11 @@ trait Applications extends Compatibility {

override def liftFun(): Unit =
if (liftedDefs == null) {
liftedDefs = new mutable.ListBuffer[Tree]
liftedDefs = ListBuffer.empty[Tree]
myNormalizedFun = lifter.liftApp(liftedDefs.uncheckedNN, myNormalizedFun)
}

/** The index of the first difference between lists of trees `xs` and `ys`
/** The index of the first difference between lists of trees `xs` and `ys`.
* -1 if there are no differences.
*/
private def firstDiff[T <: Trees.Tree[?]](xs: List[T], ys: List[T], n: Int = 0): Int = xs match {
Expand All @@ -939,11 +940,25 @@ trait Applications extends Compatibility {
isPureExpr(arg)
|| arg.isInstanceOf[RefTree | Apply | TypeApply] && arg.symbol.name.is(DefaultGetterName)

val result: Tree = {
def defaultsAddendum(args: List[Tree]): Unit =
def check(arg: Tree): Boolean = arg match
case TypeApply(Select(_, name), _) => name.is(DefaultGetterName)
case Apply(Select(_, name), _) => name.is(DefaultGetterName)
case _ => false
val faulties = args.filter(check)
ctx.reporter.mapBufferedMessages:
case Diagnostic(msg: TypeMismatch, pos, ERROR)
if msg.inTree.exists(t => faulties.exists(_.span == t.span)) =>
val noteText = i"Error occurred in an application involving default arguments."
val explained = i"Expanded application: ${cpy.Apply(app)(normalizedFun, args)}"
Diagnostic.Error(msg.append(s"\n$noteText").appendExplanation(s"\n\n$explained"), pos)
case dia => dia

val result: Tree = {
var typedArgs = typedArgBuf.toList
def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later
val app1 =
if (!success || typedArgs.exists(_.tpe.isError)) app0.withType(UnspecifiedErrorType)
if !success || typedArgs.exists(_.tpe.isError).tap(if (_) then defaultsAddendum(typedArgs)) then app0.withType(UnspecifiedErrorType)
else {
if isJavaAnnotConstr(methRef.symbol) then
// #19951 Make sure all arguments are NamedArgs for Java annotations
Expand All @@ -960,7 +975,7 @@ trait Applications extends Compatibility {
liftFun()

// lift arguments in the definition order
val argDefBuf = mutable.ListBuffer.empty[Tree]
val argDefBuf = ListBuffer.empty[Tree]
typedArgs = lifter.liftArgs(argDefBuf, methType, typedArgs)
// Lifted arguments ordered based on the original order of typedArgBuf and
// with all non-explicit default parameters at the end in declaration order.
Expand Down Expand Up @@ -1105,23 +1120,15 @@ trait Applications extends Compatibility {
if fun1.symbol.name == nme.apply && fun1.span.isSynthetic then
fun1 match
case Select(qualifier, _) =>
def mapMessage(dia: Diagnostic): Diagnostic =
dia match
case dia: Diagnostic.Error =>
dia.msg match
case msg: TypeMismatch =>
msg.inTree match
case Some(arg) if tree.args.exists(_.span == arg.span) =>
val noteText =
i"""The required type comes from a parameter of the automatically
|inserted `apply` method of `${qualifier.tpe}`.""".stripMargin
Diagnostic.Error(msg.appendExplanation("\n\n" + noteText), dia.pos)
case _ => dia
case msg => dia
case dia => dia
failedState.reporter.mapBufferedMessages(mapMessage)
case _ => ()
else ()
failedState.reporter.mapBufferedMessages:
case Diagnostic(msg: TypeMismatch, pos, ERROR)
if msg.inTree.exists(t => tree.args.exists(_.span == t.span)) =>
val noteText =
i"""The required type comes from a parameter of the automatically
|inserted `apply` method of `${qualifier.tpe}`.""".stripMargin
Diagnostic.Error(msg.appendExplanation("\n\n" + noteText), pos)
case dia => dia
case _ =>

fun1.tpe match {
case err: ErrorType => cpy.Apply(tree)(fun1, proto.typedArgs()).withType(err)
Expand Down Expand Up @@ -1211,7 +1218,7 @@ trait Applications extends Compatibility {
val (lhs1, name, rhss) = (tree: @unchecked) match
case Apply(Select(lhs, name), rhss) => (typedExpr(lhs), name, rhss)
case Apply(untpd.TypedSplice(Select(lhs1, name)), rhss) => (lhs1, name, rhss)
val liftedDefs = new mutable.ListBuffer[Tree]
val liftedDefs = ListBuffer.empty[Tree]
val lhs2 = untpd.TypedSplice(LiftComplex.liftAssigned(liftedDefs, lhs1))
val assign = untpd.Assign(lhs2,
untpd.Apply(untpd.Select(lhs2, name.asSimpleName.dropRight(1)), rhss))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ trait Checking {
enum Reason:
case NonConforming, RefutableExtractor

def fail(pat: Tree, pt: Type, reason: Reason): Boolean = {
def fail(pat: Tree, pt: Type, reason: Reason): Boolean = !pat.tpe.isErroneous && !pt.isErroneous && {
import Reason.*
val message = reason match
case NonConforming =>
Expand Down
25 changes: 25 additions & 0 deletions tests/neg/t4727.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- [E007] Type Mismatch Error: tests/neg/t4727.scala:7:8 ---------------------------------------------------------------
7 | new C[Int] // error
| ^^^^^^
| Found: Null
| Required: Int
| Error occurred in an application involving default arguments.
| Note that implicit conversions were not tried because the result of an implicit conversion
| must be more specific than Int
|---------------------------------------------------------------------------------------------------------------------
| Explanation (enabled by `-explain`)
|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
| Tree: C.$lessinit$greater$default$1[Int]
| I tried to show that
| Null
| conforms to
| Int
| but none of the attempts shown below succeeded:
|
| ==> Null <: Int = false
|
| The tests were made under the empty constraint
|
| Expanded application: new C[Int](C.$lessinit$greater$default$1[Int])
---------------------------------------------------------------------------------------------------------------------
4 changes: 3 additions & 1 deletion tests/untried/neg/t4727.scala → tests/neg/t4727.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//> using options -explain

class C[T](x : T = null)

object Test {
def main(args: Array[String]): Unit = {
new C[Int]
new C[Int] // error
}
}
5 changes: 0 additions & 5 deletions tests/untried/neg/t4727.check

This file was deleted.

Loading