diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 3777969b1076..55021bf50ace 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -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 { diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ca9d73df03aa..31e11487ae38 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 103f3aac7630..db041b7e8591 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -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 @@ -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, diff --git a/tests/pos/annot-17242.scala b/tests/pos/annot-17242.scala new file mode 100644 index 000000000000..a8fcc9dbe15f --- /dev/null +++ b/tests/pos/annot-17242.scala @@ -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)) = () diff --git a/tests/pos/dependent-annot2.scala b/tests/pos/dependent-annot2.scala new file mode 100644 index 000000000000..9bfa8b594c2b --- /dev/null +++ b/tests/pos/dependent-annot2.scala @@ -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