Skip to content

Commit e04bfef

Browse files
committed
Implemented AppliedTerm, a type representing a term-level application. AppliedTerms are currently only introduced when they are stable, that is, when both the function and the arguments are. For this purpose, methods on primitive value classes are now marked stable. Also adds new syntax for singleton types -- type trees written { expr } are translated to the singleton type of expr, provided that type is stable.
1 parent f2bb231 commit e04bfef

File tree

13 files changed

+145
-22
lines changed

13 files changed

+145
-22
lines changed

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
561561
false
562562
}
563563
compareTypeBounds
564+
case tp2: AppliedTermRef =>
565+
def compareAppliedTerm = tp1 match {
566+
case _ if !ctx.phase.isTyper =>
567+
// Need to be more permissive when checking later phases, applications may have been rewritten
568+
isSubType(tp1, tp2.resType)
569+
case tp1: AppliedTermRef =>
570+
tp1.args.size == tp2.args.size && isSubType(tp1.fn, tp2.fn) &&
571+
(tp1.args zip tp2.args).forall(t => isSubType(t._1, t._2))
572+
case _ => fourthTry
573+
}
574+
compareAppliedTerm
564575
case ClassInfo(pre2, cls2, _, _, _) =>
565576
def compareClassInfo = tp1 match {
566577
case ClassInfo(pre1, cls1, _, _, _) =>

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

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ object Types {
148148
/** Does this type denote a stable reference (i.e. singleton type)? */
149149
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
150150
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable || tp.info.isStable
151+
case tp: AppliedTermRef => tp.fn.isStable && tp.args.forall(_.isStable)
151152
case _: SingletonType | NoPrefix => true
152153
case tp: RefinedOrRecType => tp.parent.isStable
153154
case tp: ExprType => tp.resultType.isStable
@@ -279,14 +280,14 @@ object Types {
279280
/** Is this the type of a method that has a repeated parameter type as
280281
* last parameter type?
281282
*/
282-
def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match {
283+
def isVarArgsMethod(implicit ctx: Context): Boolean = stripMethodPrefix match {
283284
case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam
284285
case _ => false
285286
}
286287

287288
/** Is this the type of a method with a leading empty parameter list?
288289
*/
289-
def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match {
290+
def isNullaryMethod(implicit ctx: Context): Boolean = stripMethodPrefix match {
290291
case MethodType(Nil) => true
291292
case _ => false
292293
}
@@ -903,9 +904,10 @@ object Types {
903904
*/
904905
def stripAnnots(implicit ctx: Context): Type = this
905906

906-
/** Strip PolyType prefix */
907-
def stripPoly(implicit ctx: Context): Type = this match {
908-
case tp: PolyType => tp.resType.stripPoly
907+
/** Strip PolyType and AppliedTermRef prefix */
908+
def stripMethodPrefix(implicit ctx: Context): Type = this match {
909+
case tp: PolyType => tp.resType.stripMethodPrefix
910+
case tp: AppliedTermRef => tp.resType.stripMethodPrefix
909911
case _ => this
910912
}
911913

@@ -1216,26 +1218,26 @@ object Types {
12161218
}
12171219

12181220
/** The parameter types of a PolyType or MethodType, Empty list for others */
1219-
final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match {
1221+
final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripMethodPrefix match {
12201222
case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss
12211223
case _ => Nil
12221224
}
12231225

12241226
/** The parameter names of a PolyType or MethodType, Empty list for others */
1225-
final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripPoly match {
1227+
final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripMethodPrefix match {
12261228
case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess
12271229
case _ => Nil
12281230
}
12291231

12301232

12311233
/** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */
1232-
final def firstParamTypes(implicit ctx: Context): List[Type] = stripPoly match {
1234+
final def firstParamTypes(implicit ctx: Context): List[Type] = stripMethodPrefix match {
12331235
case mt: MethodType => mt.paramInfos
12341236
case _ => Nil
12351237
}
12361238

12371239
/** Is this either not a method at all, or a parameterless method? */
1238-
final def isParameterless(implicit ctx: Context): Boolean = stripPoly match {
1240+
final def isParameterless(implicit ctx: Context): Boolean = stripMethodPrefix match {
12391241
case mt: MethodType => false
12401242
case _ => true
12411243
}
@@ -1246,7 +1248,7 @@ object Types {
12461248
/** The final result type of a PolyType, MethodType, or ExprType, after skipping
12471249
* all parameter sections, the type itself for all others.
12481250
*/
1249-
def finalResultType(implicit ctx: Context): Type = resultType.stripPoly match {
1251+
def finalResultType(implicit ctx: Context): Type = resultType.stripMethodPrefix match {
12501252
case mt: MethodType => mt.resultType.finalResultType
12511253
case _ => resultType
12521254
}
@@ -2169,6 +2171,53 @@ object Types {
21692171
apply(prefix, if (denot.symbol.exists) denot.symbol.asType else name).withDenot(denot)
21702172
}
21712173

2174+
// --- AppliedTermRef ---------------------------------------------------------------------
2175+
2176+
/** A precise representation of a term-level application `fn(... args)`. **/
2177+
abstract case class AppliedTermRef(fn: /*TermRef | AppliedTermRef*/ SingletonType, args: List[Type])
2178+
extends CachedProxyType with SingletonType
2179+
{
2180+
protected[this] var myResType: Type = _
2181+
def resType(implicit ctx: Context): Type = {
2182+
if (myResType == null)
2183+
fn.widen match {
2184+
case methTpe: MethodType => myResType = ctx.typer.applicationResultType(methTpe, args)
2185+
}
2186+
myResType
2187+
}
2188+
2189+
def underlying(implicit ctx: Context): Type = resType
2190+
2191+
def derivedAppliedTerm(fn: Type, args: List[Type])(implicit ctx: Context): Type =
2192+
if ((this.fn eq fn) && (this.args eq args)) this
2193+
else AppliedTermRef(fn, args)
2194+
2195+
override def computeHash(bs: Binders) = doHash(bs, fn, args)
2196+
2197+
override def eql(that: Type) = that match {
2198+
case that: AppliedTermRef => (this.fn eq that.fn) && this.args.eqElements(that.args)
2199+
case _ => false
2200+
}
2201+
}
2202+
2203+
final class CachedAppliedTermRef(fn: SingletonType, args: List[Type]) extends AppliedTermRef(fn, args)
2204+
2205+
object AppliedTermRef {
2206+
def apply(fn: Type, args: List[Type])(implicit ctx: Context): Type = {
2207+
assertUnerased()
2208+
fn.dealias match {
2209+
case fn: TermRef => unique(new CachedAppliedTermRef(fn, args))
2210+
case fn: AppliedTermRef => unique(new CachedAppliedTermRef(fn, args))
2211+
case _ =>
2212+
fn.widenDealias match {
2213+
case methTpe: MethodType => ctx.typer.applicationResultType(methTpe, args)
2214+
case _: WildcardType => WildcardType
2215+
case tp => throw new AssertionError(i"Don't know how to apply $tp.")
2216+
}
2217+
}
2218+
}
2219+
}
2220+
21722221
// --- Other SingletonTypes: ThisType/SuperType/ConstantType ---------------------------
21732222

21742223
/** The type cls.this
@@ -3791,7 +3840,7 @@ object Types {
37913840
object SAMType {
37923841
def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match {
37933842
case tp: ClassInfo =>
3794-
def zeroParams(tp: Type): Boolean = tp.stripPoly match {
3843+
def zeroParams(tp: Type): Boolean = tp.stripMethodPrefix match {
37953844
case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType]
37963845
case et: ExprType => true
37973846
case _ => false
@@ -3912,6 +3961,8 @@ object Types {
39123961
tp.derivedSuperType(thistp, supertp)
39133962
protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type =
39143963
tp.derivedAppliedType(tycon, args)
3964+
protected def derivedAppliedTerm(tp: AppliedTermRef, fn: Type, args: List[Type]): Type =
3965+
tp.derivedAppliedTerm(fn, args)
39153966
protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type): Type =
39163967
tp.derivedAndType(tp1, tp2)
39173968
protected def derivedOrType(tp: OrType, tp1: Type, tp2: Type): Type =
@@ -3967,6 +4018,9 @@ object Types {
39674018
}
39684019
derivedAppliedType(tp, this(tp.tycon), mapArgs(tp.args, tp.typeParams))
39694020

4021+
case tp: AppliedTermRef =>
4022+
derivedAppliedTerm(tp, this(tp.fn), tp.args.mapConserve(this))
4023+
39704024
case tp: RefinedType =>
39714025
derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo))
39724026

@@ -4263,6 +4317,25 @@ object Types {
42634317
else tp.derivedAppliedType(tycon, args)
42644318
}
42654319

4320+
override protected def derivedAppliedTerm(tp: AppliedTermRef, fn: Type, args: List[Type]): Type =
4321+
fn match {
4322+
case Range(fnLo, fnHi) =>
4323+
range(derivedAppliedTerm(tp, fnLo, args), derivedAppliedTerm(tp, fnHi, args))
4324+
case _ =>
4325+
if (fn.isBottomType) {
4326+
fn
4327+
} else if (args.exists(isRange)) {
4328+
val loBuf, hiBuf = new mutable.ListBuffer[Type]
4329+
args foreach {
4330+
case Range(lo, hi) => loBuf += lo; hiBuf += hi
4331+
case arg => loBuf += arg; hiBuf += arg
4332+
}
4333+
range(tp.derivedAppliedTerm(fn, loBuf.toList), tp.derivedAppliedTerm(fn, hiBuf.toList))
4334+
} else {
4335+
tp.derivedAppliedTerm(fn, args)
4336+
}
4337+
}
4338+
42664339
override protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type) =
42674340
if (isRange(tp1) || isRange(tp2)) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2))
42684341
else tp.derivedAndType(tp1, tp2)
@@ -4353,6 +4426,9 @@ object Types {
43534426
}
43544427
foldArgs(this(x, tycon), tp.typeParams, args)
43554428

4429+
case tp: AppliedTermRef =>
4430+
foldOver(this(x, tp.fn), tp.args)
4431+
43564432
case _: BoundType | _: ThisType => x
43574433

43584434
case tp: LambdaType =>

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ object TastyFormat {
416416
METHODtype + implicitOffset + erasedOffset
417417
}
418418

419+
final val APPLIEDTERMREF = 180
419420
final val HOLE = 255
420421

421422
final val firstNatTreeTag = SHAREDterm

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ class TreePickler(pickler: TastyPickler) {
157157
case AppliedType(tycon, args) =>
158158
writeByte(APPLIEDtype)
159159
withLength { pickleType(tycon); args.foreach(pickleType(_)) }
160+
case AppliedTermRef(fn, args) =>
161+
writeByte(APPLIEDTERMREF)
162+
withLength { pickleType(fn); args.foreach(pickleType(_)) }
160163
case ConstantType(value) =>
161164
pickleConstant(value)
162165
case tpe: NamedType =>

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ class TreeUnpickler(reader: TastyReader,
273273
// Eta expansion of the latter puts readType() out of the expression.
274274
case APPLIEDtype =>
275275
readType().appliedTo(until(end)(readType()))
276+
case APPLIEDTERMREF =>
277+
AppliedTermRef(readType(), until(end)(readType()))
276278
case TYPEBOUNDS =>
277279
TypeBounds(readType(), readType())
278280
case ANNOTATEDtype =>

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,12 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
423423
}
424424
}
425425

426+
// TODO(gsps): This is a hack to mark certain primitive methods as stable.
427+
// In the future such primitive methods should be pickled with the Stable flag set.
428+
def markPrimitiveStable(owner: Symbol, name: Name, flags: FlagSet): FlagSet =
429+
if (defn.ScalaValueClasses().contains(owner)) flags | Stable
430+
else flags
431+
426432
tag match {
427433
case NONEsym => return NoSymbol
428434
case EXTref | EXTMODCLASSref => return readExtSymbol()
@@ -447,6 +453,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
447453
name =
448454
if (name == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR
449455
else name.asTermName.unmangle(Scala2MethodNameKinds)
456+
flags = markPrimitiveStable(owner, name, flags)
450457
}
451458
if ((flags is Scala2ExpandedName)) {
452459
name = name.unmangle(ExpandedName)

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ object Parsers {
863863
makeTupleOrParens(inParens(argTypes(namedOK = false, wildOK = true)))
864864
}
865865
else if (in.token == LBRACE)
866-
atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
866+
atPos(in.offset) { inBraces(emptyRefinementOrSingletonExpr()) }
867867
else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
868868
else if (in.token == USCORE) {
869869
val start = in.skipToken()
@@ -877,6 +877,11 @@ object Parsers {
877877
}
878878
}
879879

880+
def emptyRefinementOrSingletonExpr(): Tree = {
881+
if (!isStatSeqEnd && !isDclIntro) SingletonTypeTree(postfixExpr())
882+
else RefinedTypeTree(EmptyTree, refineStatSeq())
883+
}
884+
880885
val handleSingletonType: Tree => Tree = t =>
881886
if (in.token == TYPE) {
882887
in.nextToken()

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
6060
homogenize(tp.ref)
6161
case AppliedType(tycon, args) =>
6262
tycon.dealias.appliedTo(args)
63+
case tp: AppliedTermRef =>
64+
homogenize(tp.underlying)
6365
case _ =>
6466
tp
6567
}
@@ -141,6 +143,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
141143
ParamRefNameString(tp) ~ ".type"
142144
case tp: TypeParamRef =>
143145
ParamRefNameString(tp) ~ lambdaHash(tp.binder)
146+
case tp: AppliedTermRef =>
147+
toTextRef(tp) ~ ".type"
144148
case tp: SingletonType =>
145149
toTextLocal(tp.underlying) ~ "(" ~ toTextRef(tp) ~ ")"
146150
case AppliedType(tycon, args) =>
@@ -260,6 +264,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
260264
toTextPrefix(tp.prefix) ~ selectionString(tp)
261265
case tp: ThisType =>
262266
nameString(tp.cls) + ".this"
267+
case AppliedTermRef(fn, args) =>
268+
(toTextRef(fn) ~ "(" ~ Text(args map argText, ", ") ~ ")").close
263269
case SuperType(thistpe: SingletonType, _) =>
264270
toTextRef(thistpe).map(_.replaceAll("""\bthis$""", "super"))
265271
case SuperType(thistpe, _) =>

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
167167
api.Annotation.of(api.Constant.of(Constants.emptyType, name), Array())
168168
val orMarker = marker("Or")
169169
val byNameMarker = marker("ByName")
170+
val appliedTermRefMarker = marker("AppliedTermRef")
170171

171172

172173
/** Extract the API representation of a source file */
@@ -445,6 +446,9 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
445446
val apiTycon = apiType(tycon)
446447
val apiArgs = args.map(processArg)
447448
api.Parameterized.of(apiTycon, apiArgs.toArray)
449+
case tp: AppliedTermRef =>
450+
val apiTps = (tp.fn :: tp.args).map(apiType)
451+
withMarker(combineApiTypes(apiTps: _*), appliedTermRefMarker)
448452
case tl: TypeLambda =>
449453
val apiTparams = tl.typeParams.map(apiTypeParameter)
450454
val apiRes = apiType(tl.resType)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,7 +1345,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
13451345
case x => x
13461346
}
13471347

1348-
def sizeFits(alt: TermRef, tp: Type): Boolean = tp.stripPoly match {
1348+
def sizeFits(alt: TermRef, tp: Type): Boolean = tp.stripMethodPrefix match {
13491349
case tp: MethodType =>
13501350
val ptypes = tp.paramInfos
13511351
val numParams = ptypes.length

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ object Implicits {
121121
!(isFunctionInS2 || isImplicitConverter || isConforms)
122122
}
123123

124-
def discardForValueType(tpw: Type): Boolean = tpw.stripPoly match {
124+
def discardForValueType(tpw: Type): Boolean = tpw.stripMethodPrefix match {
125125
case tpw: MethodType => !tpw.isImplicitMethod
126126
case _ => false
127127
}

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -358,14 +358,22 @@ trait TypeAssigner {
358358
tp
359359
}
360360

361+
def applicationResultType(methTp: MethodType, args: List[Type])(implicit ctx: Context): Type =
362+
if (methTp.isResultDependent) safeSubstParams(methTp.resultType, methTp.paramRefs, args)
363+
else methTp.resultType
364+
361365
def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
362-
val ownType = fn.tpe.widen match {
363-
case fntpe: MethodType =>
364-
if (sameLength(fntpe.paramInfos, args) || ctx.phase.prev.relaxedTyping)
365-
if (fntpe.isResultDependent) safeSubstParams(fntpe.resultType, fntpe.paramRefs, args.tpes)
366-
else fntpe.resultType
367-
else
368-
errorType(i"wrong number of arguments at ${ctx.phase.prev} for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.pos)
366+
val fnTpe = fn.tpe
367+
val ownType = fnTpe.widen match {
368+
case methTp: MethodType =>
369+
if (sameLength(methTp.paramInfos, args) || ctx.phase.prev.relaxedTyping) {
370+
val argTpes = args.tpes
371+
if (!ctx.erasedTypes && fnTpe.isStable && argTpes.forall(_.isStable))
372+
AppliedTermRef(fnTpe, argTpes)
373+
else
374+
applicationResultType(methTp, argTpes)
375+
} else
376+
errorType(i"wrong number of arguments at ${ctx.phase.prev} for $methTp: $fnTpe, expected: ${methTp.paramInfos.length}, found: ${args.length}", tree.pos)
369377
case t =>
370378
errorType(err.takesNoParamsStr(fn, ""), tree.pos)
371379
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1465,7 +1465,7 @@ class Typer extends Namer
14651465
* @param psym Its type symbol
14661466
* @param cinfo The info of its constructor
14671467
*/
1468-
def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo.stripPoly match {
1468+
def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo.stripMethodPrefix match {
14691469
case cinfo @ MethodType(Nil) if cinfo.resultType.isImplicitMethod =>
14701470
val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone
14711471
typedExpr(untpd.TypedSplice(icall))(superCtx)

0 commit comments

Comments
 (0)