Skip to content

Commit 20d9ccb

Browse files
committed
Fix #9562: Restrict implied prefix to same extension block
1 parent e7f9eaf commit 20d9ccb

File tree

6 files changed

+44
-28
lines changed

6 files changed

+44
-28
lines changed

compiler/src/dotty/tools/dotc/core/ContextOps.scala

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,48 +19,48 @@ object ContextOps:
1919
def enter(sym: Symbol): Symbol = inContext(ctx) {
2020
ctx.owner match
2121
case cls: ClassSymbol => cls.classDenot.enter(sym)
22-
case _ => scope.openForMutations.enter(sym)
22+
case _ => ctx.scope.openForMutations.enter(sym)
2323
sym
2424
}
2525

2626
/** The denotation with the given `name` and all `required` flags in current context
2727
*/
2828
def denotNamed(name: Name, required: FlagSet = EmptyFlags): Denotation = inContext(ctx) {
29-
if (owner.isClass)
30-
if (outer.owner == owner) { // inner class scope; check whether we are referring to self
31-
if (scope.size == 1) {
32-
val elem = scope.lastEntry
29+
if (ctx.owner.isClass)
30+
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
31+
if (ctx.scope.size == 1) {
32+
val elem = ctx.scope.lastEntry
3333
if (elem.name == name) return elem.sym.denot // return self
3434
}
35-
val pre = owner.thisType
35+
val pre = ctx.owner.thisType
3636
pre.findMember(name, pre, required, EmptyFlags)
3737
}
3838
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
39-
owner.findMember(name, owner.thisType, required, EmptyFlags)
39+
ctx.owner.findMember(name, ctx.owner.thisType, required, EmptyFlags)
4040
else
41-
scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix)
41+
ctx.scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix)
4242
}
4343

4444
/** A fresh local context with given tree and owner.
4545
* Owner might not exist (can happen for self valdefs), in which case
4646
* no owner is set in result context
4747
*/
4848
def localContext(tree: untpd.Tree, owner: Symbol): FreshContext = inContext(ctx) {
49-
val freshCtx = fresh.setTree(tree)
50-
if (owner.exists) freshCtx.setOwner(owner) else freshCtx
49+
val freshCtx = ctx.fresh.setTree(tree)
50+
if owner.exists then freshCtx.setOwner(owner) else freshCtx
5151
}
5252

5353
/** Context where `sym` is defined, assuming we are in a nested context. */
5454
def defContext(sym: Symbol): Context = inContext(ctx) {
55-
outersIterator
55+
ctx.outersIterator
5656
.dropWhile(_.owner != sym)
5757
.dropWhile(_.owner == sym)
5858
.next()
5959
}
6060

6161
/** A new context for the interior of a class */
6262
def inClassContext(selfInfo: TypeOrSymbol): Context = inContext(ctx) {
63-
val localCtx: Context = fresh.setNewScope
63+
val localCtx: Context = ctx.fresh.setNewScope
6464
selfInfo match {
6565
case sym: Symbol if sym.exists && sym.name != nme.WILDCARD => localCtx.scope.openForMutations.enter(sym)
6666
case _ =>
@@ -69,7 +69,7 @@ object ContextOps:
6969
}
7070

7171
def packageContext(tree: untpd.PackageDef, pkg: Symbol): Context = inContext(ctx) {
72-
if (pkg.is(Package)) fresh.setOwner(pkg.moduleClass).setTree(tree)
72+
if (pkg.is(Package)) ctx.fresh.setOwner(pkg.moduleClass).setTree(tree)
7373
else ctx
7474
}
7575
end ContextOps

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
409409

410410
/** Is this an anonymous class deriving from an enum definition? */
411411
extension (cls: ClassSymbol) private def isEnumValueImplementation(using Context): Boolean =
412-
isAnonymousClass && classParents.head.typeSymbol.is(Enum) // asserted in Typer
412+
cls.isAnonymousClass && cls.classParents.head.typeSymbol.is(Enum) // asserted in Typer
413413

414414
/** If this is the class backing a serializable singleton enum value with base class `MyEnum`,
415415
* and not deriving from `java.lang.Enum` add the method:

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,10 @@ object Applications {
199199
* I.e., if the expected type is a PolyProto, then `app` will be a `TypeApply(_, args)` where
200200
* `args` are the type arguments of the expected type.
201201
*/
202-
class IntegratedTypeArgs(val app: Tree)(implicit @constructorOnly src: SourceFile) extends tpd.Tree {
202+
class IntegratedTypeArgs(val app: Tree)(implicit @constructorOnly src: SourceFile) extends ProxyTree {
203203
override def span = app.span
204204

205+
def forwardTo = app
205206
def canEqual(that: Any): Boolean = app.canEqual(that)
206207
def productArity: Int = app.productArity
207208
def productElement(n: Int): Any = app.productElement(n)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ trait Checking {
11101110
def checkEnumParent(cls: Symbol, firstParent: Symbol)(using Context): Unit =
11111111

11121112
extension (sym: Symbol) def typeRefApplied(using Context): Type =
1113-
typeRef.appliedTo(typeParams.map(_.info.loBound))
1113+
sym.typeRef.appliedTo(sym.typeParams.map(_.info.loBound))
11141114

11151115
def ensureParentDerivesFrom(enumCase: Symbol)(using Context) =
11161116
val enumCls = enumCase.owner.linkedClass

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

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -446,17 +446,7 @@ class Typer extends Namer
446446
// cyclic references.
447447
if (name == nme.ROOTPKG)
448448
return tree.withType(defn.RootPackage.termRef)
449-
450-
/** Convert a reference `f` to an extension method select `p.f`, where
451-
* `p` is the closest enclosing extension parameter, or else `this`.
452-
*/
453-
def extensionMethodSelect: untpd.Tree =
454-
val xmethod = ctx.owner.enclosingExtensionMethod
455-
val qualifier =
456-
if xmethod.exists then untpd.ref(xmethod.extensionParam.termRef)
457-
else untpd.This(untpd.EmptyTypeIdent)
458-
untpd.cpy.Select(tree)(qualifier, name)
459-
449+
460450
val rawType = {
461451
val saved1 = unimported
462452
val saved2 = foundUnderScala2
@@ -507,7 +497,20 @@ class Typer extends Namer
507497
else if name.toTermName == nme.ERROR then
508498
setType(UnspecifiedErrorType)
509499
else if name.isTermName then
510-
tryEither(typed(extensionMethodSelect, pt))((_, _) => fail)
500+
// Convert a reference `f` to an extension method select `p.f`, where
501+
// `p` is the closest enclosing extension parameter, or else convert to `this.f`.
502+
val xmethod = ctx.owner.enclosingExtensionMethod
503+
val qualifier =
504+
if xmethod.exists then untpd.ref(xmethod.extensionParam.termRef)
505+
else untpd.This(untpd.EmptyTypeIdent)
506+
val selection = untpd.cpy.Select(tree)(qualifier, name)
507+
val result = tryEither(typed(selection, pt))((_, _) => fail)
508+
def canAccessUnqualified(sym: Symbol) =
509+
sym.isExtensionMethod && (sym.extensionParam.span == xmethod.extensionParam.span)
510+
if !xmethod.exists || result.tpe.isError || canAccessUnqualified(result.symbol) then
511+
result
512+
else
513+
fail
511514
else
512515
fail
513516
end typedIdent

tests/neg/i9562.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class Foo:
2+
def foo = 23
3+
4+
object Unrelated:
5+
extension (f: Foo)
6+
def g = f.foo // OK
7+
8+
extension (f: Foo)
9+
def h1: Int = foo // error
10+
def h2: Int = h1 + 1 // OK
11+
def h3: Int = g // error
12+
def ++: (x: Int): Int = h1 + x // OK

0 commit comments

Comments
 (0)