Skip to content

Commit

Permalink
Tolarate some faults when copying trees in InlineTreeMap
Browse files Browse the repository at this point in the history
As a preparatory step the inliner maps the inlined body with a tree type map.
This map adjusts the tree and at the same time sets up the environment for
typing the tree. But this has the potential that re-typing during copying
will fail since the environment is not yet set up correctly. We avoid the
problem by ignoring a specific failure (function type in application does
not exist) and proceeding with the previous type.
  • Loading branch information
odersky committed Mar 12, 2022
1 parent 9abe753 commit 8366bad
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 2 deletions.
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
12 changes: 11 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions tests/neg/i14653.scala
Original file line number Diff line number Diff line change
@@ -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}")

0 comments on commit 8366bad

Please sign in to comment.