Skip to content

Commit 02e60f8

Browse files
authored
Merge pull request #9576 from dotty-staging/fix-#9562
Fix #9562: Fix handling of identifiers in extension methods
2 parents c908b13 + ec964a9 commit 02e60f8

File tree

16 files changed

+89
-60
lines changed

16 files changed

+89
-60
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ object desugar {
899899
name = mdef.name.toExtensionName,
900900
tparams = ext.tparams ++ mdef.tparams,
901901
vparamss = mdef.vparamss match
902-
case vparams1 :: vparamss1 if !isLeftAssoc(mdef.name) =>
902+
case vparams1 :: vparamss1 if mdef.name.isRightAssocOperatorName =>
903903
vparams1 :: ext.vparamss ::: vparamss1
904904
case _ =>
905905
ext.vparamss ++ mdef.vparamss
@@ -1204,10 +1204,10 @@ object desugar {
12041204
case _ =>
12051205
Apply(sel, arg :: Nil)
12061206

1207-
if (isLeftAssoc(op.name))
1208-
makeOp(left, right, Span(left.span.start, op.span.end, op.span.start))
1209-
else
1207+
if op.name.isRightAssocOperatorName then
12101208
makeOp(right, left, Span(op.span.start, right.span.end))
1209+
else
1210+
makeOp(left, right, Span(left.span.start, op.span.end, op.span.start))
12111211
}
12121212

12131213
/** Translate tuple expressions of arity <= 22

compiler/src/dotty/tools/dotc/ast/Positioned.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import util.Spans._
66
import util.{SourceFile, NoSource, SourcePosition, SrcPos}
77
import core.Contexts._
88
import core.Decorators._
9+
import core.NameOps._
910
import core.Flags.{JavaDefined, ExtensionMethod}
1011
import core.StdNames.nme
1112
import ast.Trees.mods
@@ -208,7 +209,7 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Src
208209
check(tree.vparamss)
209210
case tree: DefDef if tree.mods.is(ExtensionMethod) =>
210211
tree.vparamss match {
211-
case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) =>
212+
case vparams1 :: vparams2 :: rest if tree.name.isRightAssocOperatorName =>
212213
check(tree.tparams)
213214
check(vparams2)
214215
check(vparams1)

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,6 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
185185
case _ => false
186186
}
187187

188-
/** Is name a left-associative operator? */
189-
def isLeftAssoc(operator: Name): Boolean = !operator.isEmpty && (operator.toSimpleName.last != ':')
190-
191188
/** Is this argument node of the form <expr> : _*, or is it a reference to
192189
* such an argument ? The latter case can happen when an argument is lifted.
193190
*/

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/core/NameOps.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ object NameOps {
7272
def isAnonymousClassName: Boolean = name.startsWith(str.ANON_CLASS)
7373
def isAnonymousFunctionName: Boolean = name.startsWith(str.ANON_FUN)
7474
def isUnapplyName: Boolean = name == nme.unapply || name == nme.unapplySeq
75+
def isRightAssocOperatorName: Boolean = name.lastPart.last == ':'
7576

7677
def isOperatorName: Boolean = name match
7778
case name: SimpleName => name.exists(isOperatorPart)

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,18 @@ object SymDenotations {
325325
else recurWithParamss(info, rawParamss)
326326
end paramSymss
327327

328+
/** The extension parameter of this extension method
329+
* @pre this symbol is an extension method
330+
*/
331+
final def extensionParam(using Context): Symbol =
332+
def leadParam(paramss: List[List[Symbol]]): Symbol = paramss match
333+
case (param :: _) :: paramss1 if param.isType => leadParam(paramss1)
334+
case _ :: (snd :: Nil) :: _ if name.isRightAssocOperatorName => snd
335+
case (fst :: Nil) :: _ => fst
336+
case _ => NoSymbol
337+
assert(isAllOf(ExtensionMethod))
338+
leadParam(rawParamss)
339+
328340
/** The denotation is completed: info is not a lazy type and attributes have defined values */
329341
final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType]
330342

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ object Parsers {
927927
var opStack: List[OpInfo] = Nil
928928

929929
def checkAssoc(offset: Token, op1: Name, op2: Name, op2LeftAssoc: Boolean): Unit =
930-
if (isLeftAssoc(op1) != op2LeftAssoc)
930+
if (op1.isRightAssocOperatorName == op2LeftAssoc)
931931
syntaxError(MixedLeftAndRightAssociativeOps(op1, op2, op2LeftAssoc), offset)
932932

933933
def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean, op2: Name, isType: Boolean): Tree = {
@@ -967,7 +967,7 @@ object Parsers {
967967
def recur(top: Tree): Tree =
968968
if (isIdent && isOperator) {
969969
val op = if (isType) typeIdent() else termIdent()
970-
val top1 = reduceStack(base, top, precedence(op.name), isLeftAssoc(op.name), op.name, isType)
970+
val top1 = reduceStack(base, top, precedence(op.name), !op.name.isRightAssocOperatorName, op.name, isType)
971971
opStack = OpInfo(top1, op, in.offset) :: opStack
972972
colonAtEOLOpt()
973973
newLineOptWhenFollowing(canStartOperand)
@@ -3316,7 +3316,7 @@ object Parsers {
33163316
typeParamClause(ParamOwner.Def)
33173317
else leadingTparams
33183318
val vparamss = paramClauses() match
3319-
case rparams :: rparamss if leadingVparamss.nonEmpty && !isLeftAssoc(ident.name) =>
3319+
case rparams :: rparamss if leadingVparamss.nonEmpty && ident.name.isRightAssocOperatorName =>
33203320
rparams :: leadingVparamss ::: rparamss
33213321
case rparamss =>
33223322
leadingVparamss ::: rparamss

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
795795
val (prefix, vparamss) =
796796
if isExtension then
797797
val (leadingParams, otherParamss) = (tree.vparamss: @unchecked) match
798-
case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) =>
798+
case vparams1 :: vparams2 :: rest if tree.name.isRightAssocOperatorName =>
799799
(vparams2, vparams1 :: rest)
800800
case vparams1 :: rest =>
801801
(vparams1, rest)

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)

0 commit comments

Comments
 (0)