From 0a416d88b5810b1fc86877c38b17e6c833a4c5e9 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 3 Oct 2024 18:31:35 +0200 Subject: [PATCH] Add an adaptation step in Inliner We sometimes face a problem that we inline a reference `x: T` which upon further inlining is adapted to an expected type `x`. It only seems to occur in complicated scenarios. I could not completely narrow it down. But in any case it's safe to drop the widening cast in order to avoid a type error here. We do that in a last-effort adaptation step that's only enabled in the Inliner: Faced with an expression `x: T` and a singleton expected type `y.type` where `x.type <: y.type`, rewrite to `x`. --- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 6 ++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 10 +++++++++- tests/pos/i21413.scala | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i21413.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 7c79e972c126..103f3aac7630 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -957,6 +957,12 @@ class Inliner(val call: tpd.Tree)(using Context): case None => tree case _ => tree + + /** For inlining only: Given `(x: T)` with expected type `x.type`, replace the tree with `x`. + */ + override def healAdapt(tree: Tree, pt: Type)(using Context): Tree = (tree, pt) match + case (Typed(tree1, _), pt: SingletonType) if tree1.tpe <:< pt => tree1 + case _ => tree end InlineTyper /** Drop any side-effect-free bindings that are unused in expansion or other reachable bindings. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 93ea3f3c3ae0..57bbc3ee98e8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4602,7 +4602,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def recover(failure: SearchFailureType) = if canDefineFurther(wtp) || canDefineFurther(pt) then readapt(tree) - else err.typeMismatch(tree, pt, failure) + else + val tree1 = healAdapt(tree, pt) + if tree1 ne tree then readapt(tree1) + else err.typeMismatch(tree, pt, failure) pt match case _: SelectionProto => @@ -4751,6 +4754,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } } + /** Hook for inheriting Typers to do a last-effort adaptation. If a different + * tree is returned, we will readpat that one, ptherwise we issue a type error afterwards. + */ + protected def healAdapt(tree: Tree, pt: Type)(using Context): Tree = tree + /** True if this inline typer has already issued errors */ def hasInliningErrors(using Context): Boolean = false diff --git a/tests/pos/i21413.scala b/tests/pos/i21413.scala new file mode 100644 index 000000000000..d2dc52e34630 --- /dev/null +++ b/tests/pos/i21413.scala @@ -0,0 +1,2 @@ +val x = (aaa = 1).aaa +//val y = x.aaa \ No newline at end of file