diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 74da63f7d7da..c393b257af11 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -37,7 +37,8 @@ class TreeTypeMap( val oldOwners: List[Symbol] = Nil, val newOwners: List[Symbol] = Nil, val substFrom: List[Symbol] = Nil, - val substTo: List[Symbol] = Nil)(using Context) extends tpd.TreeMap { + val substTo: List[Symbol] = Nil, + cpy: tpd.TreeCopier = tpd.cpy)(using Context) extends tpd.TreeMap(cpy) { import tpd._ def copy( diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 0a762356869f..6c18f688b857 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -903,6 +903,15 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { def inlinedFromOutside(tree: Tree)(span: Span): Tree = Inlined(EmptyTree, Nil, tree)(using ctx.withSource(inlinedMethod.topLevelClass.source)).withSpan(span) + // InlineCopier is a more fault-tolerant copier that does not cause errors when + // function types in applications are undefined. This is necessary since we copy at + // the same time as establishing the proper context in which the copied tree should + // be evaluated. This matters for opaque types, see neg/i14653.scala. + class InlineCopier() extends TypedTreeCopier: + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(using Context): Apply = + if fun.tpe.widen.exists then super.Apply(tree)(fun, args) + else untpd.cpy.Apply(tree)(fun, args).withTypeUnchecked(tree.tpe) + // InlinerMap is a TreeTypeMap with special treatment for inlined arguments: // They are generally left alone (not mapped further, and if they wrap a type // the type Inlined wrapper gets dropped @@ -913,7 +922,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) { newOwners: List[Symbol], substFrom: List[Symbol], substTo: List[Symbol])(using Context) - extends TreeTypeMap(typeMap, treeMap, oldOwners, newOwners, substFrom, substTo): + extends TreeTypeMap( + typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()): override def copy( typeMap: Type => Type, diff --git a/tests/neg/i14653.scala b/tests/neg/i14653.scala new file mode 100644 index 000000000000..29a013534159 --- /dev/null +++ b/tests/neg/i14653.scala @@ -0,0 +1,18 @@ +type Amount = Amount.Type +object Amount: + opaque type Type = BigDecimal + inline def apply(inline dec: BigDecimal): Type = dec + + extension (self: Type) + inline def value: BigDecimal = self + inline def +(y: Type): Type = self + y + +@main def r(): Unit = + val aa: Amount = Amount(1) + val ab: Amount = Amount(2) + val ac: Amount = Amount(2) + val as1: Amount = aa + ab // error + val as2: Amount = aa + ab + ac // error + + println(s"aa + ab = ${as1}") + println(s"aa + ab = ${as2}")