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

Rename InlineCopier to ConservativeTreeCopier, use it in TypeMaps #21941

Merged
merged 1 commit into from
Nov 14, 2024
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
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
Loading