Skip to content

Commit cfba20a

Browse files
authored
Merge pull request #5114 from dotty-staging/add-extensions
New proposal for extension methods
2 parents 11ed751 + 49bae02 commit cfba20a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1535
-322
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotty.tools.backend.jvm
33
import dotty.tools.dotc.ast.tpd
44
import dotty.tools.dotc.ast.Trees
55
import dotty.tools.dotc
6-
import dotty.tools.dotc.core.Flags.FlagSet
6+
import dotty.tools.dotc.core.Flags.{termFlagSet, termFlagConjunction}
77
import dotty.tools.dotc.transform.{Erasure, GenericSignatures}
88
import dotty.tools.dotc.transform.SymUtils._
99
import java.io.{File => _}
@@ -801,7 +801,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
801801

802802

803803
def freshLocal(cunit: CompilationUnit, name: String, tpe: Type, pos: Position, flags: Flags): Symbol = {
804-
ctx.newSymbol(sym, name.toTermName, FlagSet(flags), tpe, NoSymbol, pos)
804+
ctx.newSymbol(sym, name.toTermName, termFlagSet(flags), tpe, NoSymbol, pos)
805805
}
806806

807807
def getter(clz: Symbol): Symbol = decorateSymbol(sym).getter
@@ -881,7 +881,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
881881
def =:=(other: Type): Boolean = tp =:= other
882882

883883
def membersBasedOnFlags(excludedFlags: Flags, requiredFlags: Flags): List[Symbol] =
884-
tp.membersBasedOnFlags(FlagSet(requiredFlags), FlagSet(excludedFlags)).map(_.symbol).toList
884+
tp.membersBasedOnFlags(termFlagConjunction(requiredFlags), termFlagSet(excludedFlags)).map(_.symbol).toList
885885

886886
def resultType: Type = tp.resultType
887887

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package ast
44
import util.Positions._
55
import core.Contexts.Context
66
import core.Decorators._
7-
import core.Flags.JavaDefined
7+
import core.Flags.{JavaDefined, Extension}
88
import core.StdNames.nme
99

1010
/** A base class for things that have positions (currently: modifiers and trees)
@@ -208,6 +208,22 @@ abstract class Positioned extends Product {
208208
// Leave out tparams, they are copied with wrong positions from parent class
209209
check(tree.mods)
210210
check(tree.vparamss)
211+
case tree: DefDef if tree.mods.is(Extension) =>
212+
tree.vparamss match {
213+
case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) =>
214+
check(vparams2)
215+
check(tree.tparams)
216+
check(vparams1)
217+
check(rest)
218+
case vparams1 :: rest =>
219+
check(vparams1)
220+
check(tree.tparams)
221+
check(rest)
222+
case _ =>
223+
check(tree.tparams)
224+
}
225+
check(tree.tpt)
226+
check(tree.rhs)
211227
case _ =>
212228
val end = productArity
213229
var n = 0

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,9 @@ object Trees {
345345

346346
def withFlags(flags: FlagSet): ThisTree[Untyped] = withMods(untpd.Modifiers(flags))
347347

348-
def setComment(comment: Option[Comment]): ThisTree[Untyped] = {
348+
def setComment(comment: Option[Comment]): this.type = {
349349
comment.map(putAttachment(DocComment, _))
350-
asInstanceOf[ThisTree[Untyped]]
350+
this
351351
}
352352

353353
/** Destructively update modifiers. To be used with care. */

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ object Printers {
1717
val checks: Printer = noPrinter
1818
val config: Printer = noPrinter
1919
val cyclicErrors: Printer = noPrinter
20+
val debug = noPrinter
2021
val dottydoc: Printer = noPrinter
2122
val exhaustivity: Printer = noPrinter
2223
val gadts: Printer = noPrinter

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

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ object Denotations {
110110
*/
111111
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation
112112

113-
/** Keep only those denotations in this group whose flags do not intersect
114-
* with `excluded`.
113+
/** Keep only those denotations in this group that have all of the flags in `required`,
114+
* but none of the flags in `excluded`.
115115
*/
116-
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation
116+
def filterWithFlags(required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): PreDenotation
117117

118118
private[this] var cachedPrefix: Type = _
119119
private[this] var cachedAsSeenFrom: AsSeenFromResult = _
@@ -254,13 +254,12 @@ object Denotations {
254254
*/
255255
def accessibleFrom(pre: Type, superAccess: Boolean = false)(implicit ctx: Context): Denotation
256256

257-
/** Find member of this denotation with given name and
258-
* produce a denotation that contains the type of the member
259-
* as seen from given prefix `pre`. Exclude all members that have
260-
* flags in `excluded` from consideration.
257+
/** Find member of this denotation with given `name`, all `required`
258+
* flags and no `excluded` flag, and produce a denotation that contains the type of the member
259+
* as seen from given prefix `pre`.
261260
*/
262-
def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation =
263-
info.findMember(name, pre, excluded)
261+
def findMember(name: Name, pre: Type, required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): Denotation =
262+
info.findMember(name, pre, required, excluded)
264263

265264
/** If this denotation is overloaded, filter with given predicate.
266265
* If result is still overloaded throw a TypeError.
@@ -1076,16 +1075,17 @@ object Denotations {
10761075
d >= Signature.ParamMatch && info.matches(other.info)
10771076
}
10781077

1079-
final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
1080-
if (p(this)) this else NoDenotation
1081-
final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =
1082-
if (denots.exists && denots.matches(this)) NoDenotation else this
10831078
def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =
10841079
if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation
10851080
else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre)
10861081
else asSeenFrom(pre).filterDisjoint(ownDenots)
1087-
final def filterExcluded(excluded: FlagSet)(implicit ctx: Context): SingleDenotation =
1088-
if (excluded.isEmpty || !(this overlaps excluded)) this else NoDenotation
1082+
1083+
final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
1084+
if (p(this)) this else NoDenotation
1085+
final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =
1086+
if (denots.exists && denots.matches(this)) NoDenotation else this
1087+
def filterWithFlags(required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): SingleDenotation =
1088+
if (required.isEmpty && excluded.isEmpty || compatibleWith(required, excluded)) this else NoDenotation
10891089

10901090
type AsSeenFromResult = SingleDenotation
10911091
protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = {
@@ -1098,9 +1098,14 @@ object Denotations {
10981098
else derivedSingleDenotation(symbol, symbol.info.asSeenFrom(pre, owner))
10991099
}
11001100

1101-
private def overlaps(fs: FlagSet)(implicit ctx: Context): Boolean = this match {
1102-
case sd: SymDenotation => sd is fs
1103-
case _ => symbol is fs
1101+
/** Does this denotation have all the `required` flags but none of the `excluded` flags?
1102+
*/
1103+
private def compatibleWith(required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): Boolean = {
1104+
val symd: SymDenotation = this match {
1105+
case symd: SymDenotation => symd
1106+
case _ => symbol.denot
1107+
}
1108+
symd.is(required) && !symd.is(excluded)
11041109
}
11051110
}
11061111

@@ -1179,15 +1184,15 @@ object Denotations {
11791184
def last: Denotation = denot2.last
11801185
def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
11811186
denot1.matches(other) || denot2.matches(other)
1187+
def mapInherited(owndenot: PreDenotation, prevdenot: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation =
1188+
derivedUnion(denot1.mapInherited(owndenot, prevdenot, pre), denot2.mapInherited(owndenot, prevdenot, pre))
11821189
def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation =
11831190
derivedUnion(denot1 filterWithPredicate p, denot2 filterWithPredicate p)
11841191
def filterDisjoint(denot: PreDenotation)(implicit ctx: Context): PreDenotation =
11851192
derivedUnion(denot1 filterDisjoint denot, denot2 filterDisjoint denot)
1186-
def mapInherited(owndenot: PreDenotation, prevdenot: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation =
1187-
derivedUnion(denot1.mapInherited(owndenot, prevdenot, pre), denot2.mapInherited(owndenot, prevdenot, pre))
1188-
def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation =
1189-
derivedUnion(denot1.filterExcluded(excluded), denot2.filterExcluded(excluded))
1190-
protected def derivedUnion(denot1: PreDenotation, denot2: PreDenotation): PreDenotation =
1193+
def filterWithFlags(required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): PreDenotation =
1194+
derivedUnion(denot1.filterWithFlags(required, excluded), denot2.filterWithFlags(required, excluded))
1195+
protected def derivedUnion(denot1: PreDenotation, denot2: PreDenotation) =
11911196
if ((denot1 eq this.denot1) && (denot2 eq this.denot2)) this
11921197
else denot1 union denot2
11931198
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ object Flags {
3737
else FlagSet(tbits | ((this.bits & ~that.bits) & ~KINDFLAGS))
3838
}
3939

40+
def ^ (that: FlagSet) =
41+
FlagSet((bits | that.bits) & KINDFLAGS | (bits ^ that.bits) & ~KINDFLAGS)
42+
4043
/** Does this flag set have a non-empty intersection with the given flag set?
4144
* This means that both the kind flags and the carrier bits have non-empty intersection.
4245
*/
@@ -55,7 +58,7 @@ object Flags {
5558
*/
5659
def is(flags: FlagConjunction): Boolean = {
5760
val fs = bits & flags.bits
58-
(fs & KINDFLAGS) != 0 &&
61+
((fs & KINDFLAGS) != 0 || flags.bits == 0) &&
5962
(fs >>> TYPESHIFT) == (flags.bits >>> TYPESHIFT)
6063
}
6164

@@ -119,6 +122,8 @@ object Flags {
119122
override def toString: String = flagStrings.mkString(" ")
120123
}
121124

125+
def termFlagSet(x: Long) = FlagSet(TERMS | x)
126+
122127
/** A class representing flag sets that should be tested
123128
* conjunctively. I.e. for a flag conjunction `fc`,
124129
* `x is fc` tests whether `x` contains all flags in `fc`.
@@ -127,6 +132,8 @@ object Flags {
127132
override def toString: String = FlagSet(bits).toString
128133
}
129134

135+
def termFlagConjunction(x: Long) = FlagConjunction(TERMS | x)
136+
130137
private final val TYPESHIFT = 2
131138
private final val TERMindex = 0
132139
private final val TYPEindex = 1
@@ -179,6 +186,8 @@ object Flags {
179186
flag
180187
}
181188

189+
def allOf(flags: FlagSet) = FlagConjunction(flags.bits)
190+
182191
/** The conjunction of all flags in given flag set */
183192
def allOf(flags1: FlagSet, flags2: FlagSet): FlagConjunction = {
184193
assert(flags1.numFlags == 1 && flags2.numFlags == 1, "Flags.allOf doesn't support flag " + (if (flags1.numFlags != 1) flags1 else flags2))
@@ -197,6 +206,8 @@ object Flags {
197206
/** The empty flag set */
198207
final val EmptyFlags: FlagSet = FlagSet(0)
199208

209+
final val EmptyFlagConjunction = FlagConjunction(0)
210+
200211
/** The undefined flag set */
201212
final val UndefinedFlags: FlagSet = FlagSet(~KINDFLAGS)
202213

@@ -334,6 +345,9 @@ object Flags {
334345
/** A method that has default params */
335346
final val DefaultParameterized: FlagSet = termFlag(27, "<defaultparam>")
336347

348+
/** An extension method */
349+
final val Extension = termFlag(28, "<extension>")
350+
337351
/** Symbol is defined by a Java class */
338352
final val JavaDefined: FlagSet = commonFlag(30, "<java>")
339353

@@ -466,7 +480,7 @@ object Flags {
466480
HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags |
467481
Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic |
468482
CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags |
469-
NonMember | ImplicitCommon | Permanent | Synthetic |
483+
Extension.toCommonFlags | NonMember | ImplicitCommon | Permanent | Synthetic |
470484
SuperAccessorOrScala2x | Inline
471485

472486
/** Flags that are not (re)set when completing the denotation, or, if symbol is
@@ -588,6 +602,9 @@ object Flags {
588602
/** An inline parameter */
589603
final val InlineParam: FlagConjunction = allOf(Inline, Param)
590604

605+
/** An extension method */
606+
final val ExtensionMethod = allOf(Method, Extension)
607+
591608
/** An enum case */
592609
final val EnumCase: FlagConjunction = allOf(Enum, Case)
593610

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,7 @@ object Mode {
100100

101101
/** Read comments from definitions when unpickling from TASTY */
102102
val ReadComments: Mode = newMode(22, "ReadComments")
103+
104+
/** Suppress insertion of apply or implicit conversion on qualifier */
105+
val FixedQualifier: Mode = newMode(23, "FixedQualifier")
103106
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,9 @@ object Phases {
364364
assert(start <= Periods.MaxPossiblePhaseId, s"Too many phases, Period bits overflow")
365365
myBase = base
366366
myPeriod = Period(NoRunId, start, end)
367-
myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes
368-
myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses
369-
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
367+
myErasedTypes = prev.getClass == classOf[Erasure] || prev.erasedTypes
368+
myFlatClasses = prev.getClass == classOf[Flatten] || prev.flatClasses
369+
myRefChecked = prev.getClass == classOf[RefChecks] || prev.refChecked
370370
mySameMembersStartId = if (changesMembers) id else prev.sameMembersStartId
371371
mySameParentsStartId = if (changesParents) id else prev.sameParentsStartId
372372
mySameBaseTypesStartId = if (changesBaseTypes) id else prev.sameBaseTypesStartId

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ object SymDenotations {
177177
if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags
178178
else AfterLoadFlags)
179179

180+
final def relevantFlagsFor(fs: FlagSet)(implicit ctx: Context) =
181+
if (isCurrent(fs)) myFlags else flags
182+
180183
/** Has this denotation one of the flags in `fs` set? */
181184
final def is(fs: FlagSet)(implicit ctx: Context): Boolean =
182185
(if (isCurrent(fs)) myFlags else flags) is fs
@@ -862,7 +865,7 @@ object SymDenotations {
862865
/** The module implemented by this module class, NoSymbol if not applicable. */
863866
final def sourceModule(implicit ctx: Context): Symbol = myInfo match {
864867
case ClassInfo(_, _, _, _, selfType) if this is ModuleClass =>
865-
def sourceOfSelf(tp: Any): Symbol = tp match {
868+
def sourceOfSelf(tp: TypeOrSymbol): Symbol = tp match {
866869
case tp: TermRef => tp.symbol
867870
case tp: Symbol => sourceOfSelf(tp.info)
868871
case tp: RefinedType => sourceOfSelf(tp.parent)
@@ -1670,9 +1673,9 @@ object SymDenotations {
16701673
else collect(ownDenots, classParents)
16711674
}
16721675

1673-
override final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
1676+
override final def findMember(name: Name, pre: Type, required: FlagConjunction, excluded: FlagSet)(implicit ctx: Context): Denotation = {
16741677
val raw = if (excluded is Private) nonPrivateMembersNamed(name) else membersNamed(name)
1675-
raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre)
1678+
raw.filterWithFlags(required, excluded).asSeenFrom(pre).toDenot(pre)
16761679
}
16771680

16781681
/** Compute tp.baseType(this) */
@@ -1919,7 +1922,7 @@ object SymDenotations {
19191922
if (packageObjRunId != ctx.runId) {
19201923
packageObjRunId = ctx.runId
19211924
packageObjCache = NoDenotation // break cycle in case we are looking for package object itself
1922-
packageObjCache = findMember(nme.PACKAGE, thisType, EmptyFlags).asSymDenotation
1925+
packageObjCache = findMember(nme.PACKAGE, thisType, EmptyFlagConjunction, EmptyFlags).asSymDenotation
19231926
}
19241927
packageObjCache
19251928
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ object TypeErasure {
281281

282282
// From the spec, "Linearization also satisfies the property that a
283283
// linearization of a class always contains the linearization of its
284-
// direct superclass as a suffix"; it's enought to consider every
284+
// direct superclass as a suffix"; it's enough to consider every
285285
// candidate up to the first class.
286286
val candidates = takeUntil(tp2superclasses)(!_.is(Trait))
287287

0 commit comments

Comments
 (0)