Skip to content
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

Tolerate some faults when copying trees in InlineTreeMap #14674

Merged
merged 1 commit into from
Mar 15, 2022
Merged
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
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}")