Skip to content

Commit

Permalink
Rename InlineCopier to ConservativeTreeCopier, use it in `TypeMap…
Browse files Browse the repository at this point in the history
…`s (#21941)

Another attempt to fix #17242.

Previous attempts: #17256 and #21884.
  • Loading branch information
mbovel authored Nov 14, 2024
2 parents fdb9b71 + bb63e31 commit a304c85
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 11 deletions.
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Closure(tree: Tree)(env, meth, tpt)
}

// This is a more fault-tolerant copier that does not cause errors when
// function types in applications are undefined.
// This was called `Inliner.InlineCopier` before 3.6.3.
class ConservativeTreeCopier() 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)

override def skipTransform(tree: Tree)(using Context): Boolean = tree.tpe.isError

implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal {
Expand Down
9 changes: 8 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6291,7 +6291,14 @@ object Types extends TypeUtils {
}
}

private def treeTypeMap = new TreeTypeMap(typeMap = this)
private def treeTypeMap = new TreeTypeMap(
typeMap = this,
// Using `ConservativeTreeCopier` is needed when copying dependent annoted
// types, where we can refer to a previous parameter represented as
// `TermParamRef` that has no underlying type yet.
// See tests/pos/annot-17242.scala.
cpy = ConservativeTreeCopier()
)

def mapOver(syms: List[Symbol]): List[Symbol] = mapSymbols(syms, treeTypeMap)

Expand Down
17 changes: 7 additions & 10 deletions compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,6 @@ object Inliner:
}
end isElideableExpr

// 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.
private 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 @@ -116,7 +107,13 @@ object Inliner:
substFrom: List[Symbol],
substTo: List[Symbol])(using Context)
extends TreeTypeMap(
typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()):
typeMap, treeMap, oldOwners, newOwners, substFrom, substTo,
// It is necessary to use the `ConservativeTreeCopier` 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.
ConservativeTreeCopier()
):

override def copy(
typeMap: Type => Type,
Expand Down
5 changes: 5 additions & 0 deletions tests/pos/annot-17242.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// See also tests/pos/annot-21595.scala

class local(predicate: Int) extends annotation.StaticAnnotation

def failing1(x: Int, z: Int @local(x + x)) = ()
30 changes: 30 additions & 0 deletions tests/pos/dependent-annot2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class dummy(b: Any) extends annotation.StaticAnnotation

class X:
def foo() = 1
def bar() = 2
def eq(x: X) = true
def id(): this.type = this

class Y extends X:
override def bar() = 2
override def eq(x: X) = true

def f(x: Int) = x
def g(x: String) = x
def g(x: Int) = x

object AnnotationTests:
def foo1(elem: Int, bla: Int @dummy(Array(elem))) = bla
def foo2(elem: X, bla: Int @dummy(elem.foo())) = bla
def foo3(elem: Y, bla: Int @dummy(elem.foo())) = bla
def foo4(elem: X, bla: Int @dummy(elem.bar())) = bla
def foo5(elem: Y, bla: Int @dummy(elem.bar())) = bla
def foo6(elem: X, bla: Int @dummy(elem.eq(X()))) = bla
def foo7(elem: Y, bla: Int @dummy(elem.eq(Y()))) = bla
def foo8(elem: X, bla: Int @dummy(elem.id().foo())) = bla
def foo9(elem: Y, bla: Int @dummy(elem.id().foo())) = bla
def foo10(elem: Int, bla: Int @dummy(f(elem))) = bla
def foo11(elem: Int, bla: Int @dummy(g(elem))) = bla
def foo12(elem: Int, bla: Int @dummy(0 == elem)) = bla
def foo13(elem: Int, bla: Int @dummy(elem == 0)) = bla

0 comments on commit a304c85

Please sign in to comment.