Skip to content

Commit cb907d3

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 0353e64 commit cb907d3

13 files changed

+146
-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
@@ -547,6 +547,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
547547
false
548548
}
549549
compareTypeBounds
550+
case tp2: AppliedTermRef =>
551+
def compareAppliedTerm = tp1 match {
552+
case _ if !ctx.phase.isTyper =>
553+
// Need to be more permissive when checking later phases, applications may have been rewritten
554+
isSubType(tp1, tp2.resType)
555+
case tp1: AppliedTermRef =>
556+
tp1.args.size == tp2.args.size && isSubType(tp1.fn, tp2.fn) &&
557+
(tp1.args zip tp2.args).forall(t => isSubType(t._1, t._2))
558+
case _ => fourthTry
559+
}
560+
compareAppliedTerm
550561
case ClassInfo(pre2, cls2, _, _, _) =>
551562
def compareClassInfo = tp1 match {
552563
case ClassInfo(pre1, cls1, _, _, _) =>

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

Lines changed: 89 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
@@ -282,14 +283,14 @@ object Types {
282283
/** Is this the type of a method that has a repeated parameter type as
283284
* last parameter type?
284285
*/
285-
def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match {
286+
def isVarArgsMethod(implicit ctx: Context): Boolean = stripMethodPrefix match {
286287
case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam
287288
case _ => false
288289
}
289290

290291
/** Is this the type of a method with a leading empty parameter list?
291292
*/
292-
def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match {
293+
def isNullaryMethod(implicit ctx: Context): Boolean = stripMethodPrefix match {
293294
case MethodType(Nil) => true
294295
case _ => false
295296
}
@@ -899,9 +900,10 @@ object Types {
899900
*/
900901
def stripAnnots(implicit ctx: Context): Type = this
901902

902-
/** Strip PolyType prefix */
903-
def stripPoly(implicit ctx: Context): Type = this match {
904-
case tp: PolyType => tp.resType.stripPoly
903+
/** Strip PolyType and AppliedTermRef prefix */
904+
def stripMethodPrefix(implicit ctx: Context): Type = this match {
905+
case tp: PolyType => tp.resType.stripMethodPrefix
906+
case tp: AppliedTermRef => tp.resType.stripMethodPrefix
905907
case _ => this
906908
}
907909

@@ -1212,26 +1214,26 @@ object Types {
12121214
}
12131215

12141216
/** The parameter types of a PolyType or MethodType, Empty list for others */
1215-
final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match {
1217+
final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripMethodPrefix match {
12161218
case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss
12171219
case _ => Nil
12181220
}
12191221

12201222
/** The parameter names of a PolyType or MethodType, Empty list for others */
1221-
final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripPoly match {
1223+
final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripMethodPrefix match {
12221224
case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess
12231225
case _ => Nil
12241226
}
12251227

12261228

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

12331235
/** Is this either not a method at all, or a parameterless method? */
1234-
final def isParameterless(implicit ctx: Context): Boolean = stripPoly match {
1236+
final def isParameterless(implicit ctx: Context): Boolean = stripMethodPrefix match {
12351237
case mt: MethodType => false
12361238
case _ => true
12371239
}
@@ -1242,7 +1244,7 @@ object Types {
12421244
/** The final result type of a PolyType, MethodType, or ExprType, after skipping
12431245
* all parameter sections, the type itself for all others.
12441246
*/
1245-
def finalResultType(implicit ctx: Context): Type = resultType.stripPoly match {
1247+
def finalResultType(implicit ctx: Context): Type = resultType.stripMethodPrefix match {
12461248
case mt: MethodType => mt.resultType.finalResultType
12471249
case _ => resultType
12481250
}
@@ -2171,6 +2173,55 @@ object Types {
21712173
apply(prefix, if (denot.symbol.exists) denot.symbol.asType else name).withDenot(denot)
21722174
}
21732175

2176+
// --- AppliedTermRef ---------------------------------------------------------------------
2177+
2178+
/** An opaque representation of a term-level application. **/
2179+
abstract case class AppliedTermRef(fn: /*TermRef | AppliedTermRef*/ SingletonType, args: List[Type])
2180+
extends CachedProxyType with SingletonType
2181+
{
2182+
protected[this] var myResType: Type = _
2183+
def resType(implicit ctx: Context): Type = {
2184+
if (myResType == null)
2185+
// FIXME(gsps): Sometimes TermRef(TermParamRef(<fun>, 1), +) might not have a denot, e.g., when calling
2186+
// def add[X<:Int, Y<:Int, Z<:Int](x: X, y: Y, z: {x + y}): z.type = z
2187+
fn.widen match {
2188+
case methTpe: MethodType => myResType = ctx.typer.applicationResultType(methTpe, args)
2189+
}
2190+
myResType
2191+
}
2192+
2193+
def underlying(implicit ctx: Context): Type = resType
2194+
2195+
def derivedAppliedTerm(fn: Type, args: List[Type])(implicit ctx: Context): Type =
2196+
if ((this.fn eq fn) && (this.args eq args)) this
2197+
else AppliedTermRef(fn, args)
2198+
2199+
override def computeHash(bs: Binders) = doHash(bs, fn, args)
2200+
2201+
override def eql(that: Type) = that match {
2202+
case that: AppliedTermRef => (this.fn eq that.fn) && this.args.eqElements(that.args)
2203+
case _ => false
2204+
}
2205+
}
2206+
2207+
final class CachedAppliedTermRef(fn: SingletonType, args: List[Type]) extends AppliedTermRef(fn, args)
2208+
2209+
object AppliedTermRef {
2210+
def apply(fn: Type, args: List[Type])(implicit ctx: Context): Type = {
2211+
assertUnerased()
2212+
fn.dealias match {
2213+
case fn: TermRef => unique(new CachedAppliedTermRef(fn, args))
2214+
case fn: AppliedTermRef => unique(new CachedAppliedTermRef(fn, args))
2215+
case _ =>
2216+
fn.widenDealias match {
2217+
case methTpe: MethodType => ctx.typer.applicationResultType(methTpe, args) // TODO(gsps): Check length?
2218+
case _: WildcardType => WildcardType
2219+
case tp => throw new AssertionError(i"Don't know how to apply $tp.")
2220+
}
2221+
}
2222+
}
2223+
}
2224+
21742225
// --- Other SingletonTypes: ThisType/SuperType/ConstantType ---------------------------
21752226

21762227
/** The type cls.this
@@ -3791,7 +3842,7 @@ object Types {
37913842
object SAMType {
37923843
def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match {
37933844
case tp: ClassInfo =>
3794-
def zeroParams(tp: Type): Boolean = tp.stripPoly match {
3845+
def zeroParams(tp: Type): Boolean = tp.stripMethodPrefix match {
37953846
case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType]
37963847
case et: ExprType => true
37973848
case _ => false
@@ -3877,6 +3928,8 @@ object Types {
38773928
tp.derivedSuperType(thistp, supertp)
38783929
protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type =
38793930
tp.derivedAppliedType(tycon, args)
3931+
protected def derivedAppliedTerm(tp: AppliedTermRef, fn: Type, args: List[Type]): Type =
3932+
tp.derivedAppliedTerm(fn, args)
38803933
protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type): Type =
38813934
tp.derivedAndType(tp1, tp2)
38823935
protected def derivedOrType(tp: OrType, tp1: Type, tp2: Type): Type =
@@ -3932,6 +3985,9 @@ object Types {
39323985
}
39333986
derivedAppliedType(tp, this(tp.tycon), mapArgs(tp.args, tp.typeParams))
39343987

3988+
case tp: AppliedTermRef =>
3989+
derivedAppliedTerm(tp, this(tp.fn), tp.args.mapConserve(this))
3990+
39353991
case tp: RefinedType =>
39363992
derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo))
39373993

@@ -4228,6 +4284,25 @@ object Types {
42284284
else tp.derivedAppliedType(tycon, args)
42294285
}
42304286

4287+
override protected def derivedAppliedTerm(tp: AppliedTermRef, fn: Type, args: List[Type]): Type =
4288+
fn match {
4289+
case Range(fnLo, fnHi) =>
4290+
range(derivedAppliedTerm(tp, fnLo, args), derivedAppliedTerm(tp, fnHi, args))
4291+
case _ =>
4292+
if (fn.isBottomType) {
4293+
fn
4294+
} else if (args.exists(isRange)) {
4295+
val loBuf, hiBuf = new mutable.ListBuffer[Type]
4296+
args foreach {
4297+
case Range(lo, hi) => loBuf += lo; hiBuf += hi
4298+
case arg => loBuf += arg; hiBuf += arg
4299+
}
4300+
range(tp.derivedAppliedTerm(fn, loBuf.toList), tp.derivedAppliedTerm(fn, hiBuf.toList))
4301+
} else {
4302+
tp.derivedAppliedTerm(fn, args)
4303+
}
4304+
}
4305+
42314306
override protected def derivedAndType(tp: AndType, tp1: Type, tp2: Type) =
42324307
if (isRange(tp1) || isRange(tp2)) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2))
42334308
else tp.derivedAndType(tp1, tp2)
@@ -4318,6 +4393,9 @@ object Types {
43184393
}
43194394
foldArgs(this(x, tycon), tp.typeParams, args)
43204395

4396+
case tp: AppliedTermRef =>
4397+
foldOver(this(x, tp.fn), tp.args)
4398+
43214399
case _: BoundType | _: ThisType => x
43224400

43234401
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
@@ -400,6 +400,7 @@ object TastyFormat {
400400
final val ANNOTATION = 173
401401
final val TERMREFin = 174
402402
final val TYPEREFin = 175
403+
final val APPLIEDTERM = 176
403404
final val HOLE = 255
404405

405406
final val firstSimpleTreeTag = UNITconst

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(APPLIEDTERM)
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
@@ -272,6 +272,8 @@ class TreeUnpickler(reader: TastyReader,
272272
// Eta expansion of the latter puts readType() out of the expression.
273273
case APPLIEDtype =>
274274
readType().appliedTo(until(end)(readType()))
275+
case APPLIEDTERM =>
276+
AppliedTermRef(readType(), until(end)(readType()))
275277
case TYPEBOUNDS =>
276278
TypeBounds(readType(), readType())
277279
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,9 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder
394394
val apiTycon = simpleType(tycon)
395395
val apiArgs = args.map(processArg)
396396
new api.Parameterized(apiTycon, apiArgs.toArray)
397+
// TODO(gsps): Implement sbt-api translation for AppliedTermRef
398+
// case tp: AppliedTermRef =>
399+
// ???
397400
case tl: TypeLambda =>
398401
val apiTparams = tl.typeParams.map(apiTypeParameter)
399402
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
@@ -120,7 +120,7 @@ object Implicits {
120120
!(isFunctionInS2 || isImplicitConverter || isConforms)
121121
}
122122

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

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
@@ -1444,7 +1444,7 @@ class Typer extends Namer
14441444
* @param psym Its type symbol
14451445
* @param cinfo The info of its constructor
14461446
*/
1447-
def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo.stripPoly match {
1447+
def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo.stripMethodPrefix match {
14481448
case cinfo @ MethodType(Nil) if cinfo.resultType.isImplicitMethod =>
14491449
val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone
14501450
typedExpr(untpd.TypedSplice(icall))(superCtx)

0 commit comments

Comments
 (0)