Skip to content

Commit 4c9e365

Browse files
committed
Fix #7986: Better warning on cyclic references when extension methods are tried
1 parent 93f24a0 commit 4c9e365

File tree

4 files changed

+34
-11
lines changed

4 files changed

+34
-11
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3483,28 +3483,41 @@ class Typer extends Namer
34833483
}
34843484

34853485
// try an extension method in scope
3486-
pt match {
3486+
pt match
34873487
case selProto @ SelectionProto(selName: TermName, mbrType, _, _) =>
3488+
val origCtx = ctx
34883489
def tryExtension(using Context): Tree =
3490+
var tried: untpd.Tree = untpd.EmptyTree
34893491
try
34903492
findRef(selName, WildcardType, ExtensionMethod, tree.srcPos) match
34913493
case ref: TermRef =>
3492-
extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType)
3494+
tried = untpd.ref(ref).withSpan(tree.span)
3495+
extMethodApply(tried, tree, mbrType)
34933496
case _ => findRef(selProto.extensionName, WildcardType, ExtensionMethod, tree.srcPos) match
34943497
case ref: TermRef =>
3495-
extMethodApply(untpd.ref(ref).withSpan(tree.span), tree, mbrType)
3498+
tried = untpd.ref(ref).withSpan(tree.span)
3499+
extMethodApply(tried, tree, mbrType)
34963500
case _ => EmptyTree
3497-
catch case ex: TypeError => errorTree(tree, ex, tree.srcPos)
3501+
catch case ex: TypeError =>
3502+
ex match
3503+
case ex: CyclicReference if ex.denot.is(Extension) && ex.denot.name == selName =>
3504+
report.warning(
3505+
em"""An extension method for `.$selName` was tried but could not be fully constructed
3506+
|since there was a cyclic reference involving ${ex.denot.symbol.showLocated}""",
3507+
tree.srcPos)(using origCtx)
3508+
case _ =>
3509+
if tried.isEmpty then EmptyTree
3510+
else errorTree(tried, ex, tree.srcPos)
3511+
34983512
val nestedCtx = ctx.fresh.setNewTyperState()
3499-
val app = tryExtension(using nestedCtx)
3500-
if (!app.isEmpty && !nestedCtx.reporter.hasErrors) {
3513+
val tried = tryExtension(using nestedCtx)
3514+
if !tried.isEmpty && !nestedCtx.reporter.hasErrors then
35013515
nestedCtx.typerState.commit()
3502-
return ExtMethodApply(app)
3503-
}
3504-
else if !app.isEmpty then
3505-
rememberSearchFailure(tree, SearchFailure(app.withType(FailedExtension(app, pt))))
3516+
return ExtMethodApply(tried)
3517+
else if !tried.isEmpty then
3518+
rememberSearchFailure(tree,
3519+
SearchFailure(tried.withType(FailedExtension(tried, pt))))
35063520
case _ =>
3507-
}
35083521

35093522
// try an implicit conversion
35103523
val prevConstraint = ctx.typerState.constraint

tests/neg/i7986.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- [E008] Not Found Error: tests/neg/i7986.scala:3:61 ------------------------------------------------------------------
2+
3 |extension (project: Project) def dependencies = project.name.dependencies // error, following a cyclic reference warning
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| value dependencies is not a member of String

tests/neg/i7986.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
case class Project(name: String)
2+
extension (name: String) def dependencies = ???
3+
extension (project: Project) def dependencies = project.name.dependencies // error, following a cyclic reference warning

tests/pos/i7986.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
case class Project(name: String)
2+
extension (name: String) def dependencies: String = ???
3+
extension (project: Project) def dependencies: String = project.name.dependencies

0 commit comments

Comments
 (0)