diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index ae3ed9fcad3b..3432e349fdf6 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -842,14 +842,6 @@ 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 df7700c73a17..89917207f673 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3823,9 +3823,47 @@ object Types extends TypeUtils { def integrate(tparams: List[ParamInfo], tp: Type)(using Context): Type = (tparams: @unchecked) match { case LambdaParam(lam, _) :: _ => tp.subst(lam, this) // This is where the precondition is necessary. - case params: List[Symbol @unchecked] => tp.subst(params, paramRefs) + case params: List[Symbol @unchecked] => IntegrateMap(params, paramRefs)(tp) } + /** A map that replaces references to symbols in `params` by the types in + * `paramRefs`. + * + * It is similar to [[Substituters#subst]] but avoids reloading denotations + * of named types by overriding `derivedSelect`. + * + * This is needed because during integration, [[TermParamRef]]s refer to a + * [[LambdaType]] that is not yet fully constructed, in particular for wich + * `paramInfos` is `null`. In that case all [[TermParamRef]]s have + * [[NoType]] as underlying type. Reloading denotions of selections + * involving such [[TermParamRef]]s in [[NamedType#withPrefix]] could then + * result in a [[NoDenotation]], which would make later disambiguation of + * overloads impossible. See `tests/pos/annot-17242.scala` for example. + */ + private class IntegrateMap(from: List[Symbol], to: List[Type])(using Context) extends TypeMap: + override def apply(tp: Type) = + // Same implementation as in `SubstMap`, except the `derivedSelect` in + // the `NamedType` case, and the default case that just calls `mapOver`. + tp match + case tp: NamedType => + val sym = tp.symbol + var fs = from + var ts = to + while (fs.nonEmpty && ts.nonEmpty) { + if (fs.head eq sym) return ts.head + fs = fs.tail + ts = ts.tail + } + if (tp.prefix `eq` NoPrefix) tp + else derivedSelect(tp, apply(tp.prefix)) + case _: BoundType | _: ThisType => tp + case _ => mapOver(tp) + + override final def derivedSelect(tp: NamedType, pre: Type): Type = + if tp.prefix eq pre then tp + else if tp.symbol.exists then NamedType(pre, tp.name, tp.denot.asSeenFrom(pre)) + else NamedType(pre, tp.name) + final def derivedLambdaType(paramNames: List[ThisName] = this.paramNames, paramInfos: List[PInfo] = this.paramInfos, resType: Type = this.resType)(using Context): This = @@ -6310,14 +6348,7 @@ object Types extends TypeUtils { } } - 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() - ) + private def treeTypeMap = new TreeTypeMap(typeMap = this) 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 92cae663352a..41d3f5bbd676 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -96,6 +96,15 @@ 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. @@ -108,13 +117,7 @@ object Inliner: substFrom: List[Symbol], substTo: List[Symbol])(using Context) extends TreeTypeMap( - 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() - ): + typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()): override def transform(tree: Tree)(using Context): Tree = tree match diff --git a/tests/pos/annot-17242.scala b/tests/pos/annot-17242.scala index a8fcc9dbe15f..a1a62638ced3 100644 --- a/tests/pos/annot-17242.scala +++ b/tests/pos/annot-17242.scala @@ -1,5 +1,3 @@ -// See also tests/pos/annot-21595.scala - class local(predicate: Int) extends annotation.StaticAnnotation def failing1(x: Int, z: Int @local(x + x)) = ()