Skip to content

Commit 147671e

Browse files
authored
Merge pull request #5458 from dotty-staging/add-witness
A Proposed Alternative to Implicits
2 parents ad94ab5 + 7d2a124 commit 147671e

File tree

124 files changed

+2654
-502
lines changed

Some content is hidden

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

124 files changed

+2654
-502
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2270,9 +2270,9 @@ class JSCodeGen()(implicit ctx: Context) {
22702270
if (sym == defn.BoxedUnit_UNIT) {
22712271
js.Undefined()
22722272
} else {
2273-
val instance = genLoadModule(sym.owner)
2273+
val inst = genLoadModule(sym.owner)
22742274
val method = encodeStaticMemberSym(sym)
2275-
js.Apply(instance, method, Nil)(toIRType(sym.info))
2275+
js.Apply(inst, method, Nil)(toIRType(sym.info))
22762276
}
22772277
}
22782278

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

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,11 @@ object desugar {
163163
else vdef
164164
}
165165

166-
def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean = false)(implicit ctx: Context): List[ValDef] =
166+
def makeImplicitParameters(tpts: List[Tree], contextualFlag: FlagSet = EmptyFlags, forPrimaryConstructor: Boolean = false)(implicit ctx: Context): List[ValDef] =
167167
for (tpt <- tpts) yield {
168168
val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param
169169
val epname = EvidenceParamName.fresh()
170-
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
170+
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit | contextualFlag)
171171
}
172172

173173
/** 1. Expand context bounds to evidence params. E.g.,
@@ -197,12 +197,13 @@ object desugar {
197197
* inline def f(x: Boolean): Any = (if (x) 1 else ""): Any
198198
*/
199199
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
200-
val DefDef(name, tparams, vparamss, tpt, rhs) = meth
200+
val DefDef(_, tparams, vparamss, tpt, rhs) = meth
201+
val methName = normalizeName(meth, tpt).asTermName
201202
val mods = meth.mods
202203
val epbuf = new ListBuffer[ValDef]
203204
def desugarContextBounds(rhs: Tree): Tree = rhs match {
204205
case ContextBounds(tbounds, cxbounds) =>
205-
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
206+
epbuf ++= makeImplicitParameters(cxbounds, forPrimaryConstructor = isPrimaryConstructor)
206207
tbounds
207208
case LambdaTypeTree(tparams, body) =>
208209
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
@@ -213,7 +214,8 @@ object desugar {
213214
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
214215
}
215216

216-
var meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
217+
var meth1 = addEvidenceParams(
218+
cpy.DefDef(meth)(name = methName, tparams = tparams1), epbuf.toList)
217219

218220
if (meth1.mods.is(Inline))
219221
meth1.tpt match {
@@ -245,7 +247,7 @@ object desugar {
245247
case (vparam :: vparams) :: vparamss1 =>
246248
def defaultGetter: DefDef =
247249
DefDef(
248-
name = DefaultGetterName(meth.name, n),
250+
name = DefaultGetterName(methName, n),
249251
tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))),
250252
vparamss = takeUpTo(normalizedVparamss.nestedMap(toDefParam), n),
251253
tpt = TypeTree(),
@@ -302,8 +304,8 @@ object desugar {
302304

303305
/** The expansion of a class definition. See inline comments for what is involved */
304306
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
305-
val className = checkNotReservedName(cdef).asTypeName
306-
val impl @ Template(_, _, self, _) = cdef.rhs
307+
val impl @ Template(constr0, _, self, _) = cdef.rhs
308+
val className = normalizeName(cdef, impl).asTypeName
307309
val parents = impl.parents
308310
val mods = cdef.mods
309311
val companionMods = mods
@@ -341,6 +343,7 @@ object desugar {
341343
val isCaseClass = mods.is(Case) && !mods.is(Module)
342344
val isCaseObject = mods.is(Case) && mods.is(Module)
343345
val isImplicit = mods.is(Implicit)
346+
val isInstance = isImplicit && mods.mods.exists(_.isInstanceOf[Mod.Instance])
344347
val isEnum = mods.isEnumClass && !mods.is(Module)
345348
def isEnumCase = mods.isEnumCase
346349
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
@@ -445,7 +448,15 @@ object desugar {
445448
}
446449

447450
// new C[Ts](paramss)
448-
lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
451+
lazy val creatorExpr = {
452+
val vparamss = constrVparamss match {
453+
case (vparam :: _) :: _ if vparam.mods.is(Implicit) => // add a leading () to match class parameters
454+
Nil :: constrVparamss
455+
case _ =>
456+
constrVparamss
457+
}
458+
New(classTypeRef, vparamss.nestedMap(refOfDef))
459+
}
449460

450461
val copiedAccessFlags = if (ctx.scala2Setting) EmptyFlags else AccessFlags
451462

@@ -660,16 +671,23 @@ object desugar {
660671
ctx.error(ImplicitCaseClass(cdef), cdef.sourcePos)
661672
Nil
662673
}
663-
else if (arity != 1) {
674+
else if (arity != 1 && !isInstance) {
664675
ctx.error(ImplicitClassPrimaryConstructorArity(), cdef.sourcePos)
665676
Nil
666677
}
667-
else
678+
else {
679+
val defParamss = constrVparamss match {
680+
case Nil :: paramss =>
681+
paramss // drop leading () that got inserted by class
682+
// TODO: drop this once we do not silently insert empty class parameters anymore
683+
case paramss => paramss
684+
}
668685
// implicit wrapper is typechecked in same scope as constructor, so
669686
// we can reuse the constructor parameters; no derived params are needed.
670-
DefDef(className.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
671-
.withMods(companionMods | Synthetic | Implicit)
687+
DefDef(className.toTermName, constrTparams, defParamss, classTypeRef, creatorExpr)
688+
.withMods(companionMods | Synthetic | Implicit | Final)
672689
.withSpan(cdef.span) :: Nil
690+
}
673691

674692
val self1 = {
675693
val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
@@ -713,9 +731,9 @@ object desugar {
713731
* <module> final class name$ extends parents { self: name.type => body }
714732
*/
715733
def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
716-
val moduleName = checkNotReservedName(mdef).asTermName
717734
val impl = mdef.impl
718735
val mods = mdef.mods
736+
val moduleName = normalizeName(mdef, impl).asTermName
719737
def isEnumCase = mods.isEnumCase
720738

721739
def flagSourcePos(flag: FlagSet) = mods.mods.find(_.flags == flag).fold(mdef.sourcePos)(_.sourcePos)
@@ -793,19 +811,69 @@ object desugar {
793811
Thicket(aliasType :: companions.toList)
794812
}
795813

796-
/** The name of `mdef`, after checking that it does not redefine a Scala core class.
797-
* If it does redefine, issue an error and return a mangled name instead of the original one.
814+
/** The normalized name of `mdef`. This means
815+
* 1. Check that the name does not redefine a Scala core class.
816+
* If it does redefine, issue an error and return a mangled name instead of the original one.
817+
* 2. If the name is missing (this can be the case for instance definitions), invent one instead.
798818
*/
799-
def checkNotReservedName(mdef: MemberDef)(implicit ctx: Context): Name = {
800-
val name = mdef.name
819+
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
820+
var name = mdef.name
821+
if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_instance".toTermName)
801822
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
802823
def kind = if (name.isTypeName) "class" else "object"
803824
ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos)
804-
name.errorName
825+
name = name.errorName
805826
}
806-
else name
827+
name
807828
}
808829

830+
/** Invent a name for an anonymous instance with template `impl`.
831+
*/
832+
private def inventName(impl: Tree)(implicit ctx: Context): String = impl match {
833+
case impl: Template =>
834+
if (impl.parents.isEmpty)
835+
impl.body.find {
836+
case dd: DefDef if dd.mods.is(Extension) => true
837+
case _ => false
838+
} match {
839+
case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) =>
840+
s"${name}_of_${inventTypeName(vparam.tpt)}"
841+
case _ =>
842+
ctx.error(i"anonymous instance must have `for` part or must define at least one extension method", impl.sourcePos)
843+
nme.ERROR.toString
844+
}
845+
else
846+
impl.parents.map(inventTypeName(_)).mkString("_")
847+
case impl: Tree =>
848+
inventTypeName(impl)
849+
}
850+
851+
private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] {
852+
private def extractArgs(args: List[Tree])(implicit ctx: Context): String =
853+
args.map(argNameExtractor.apply("", _)).mkString("_")
854+
override def apply(x: String, tree: Tree)(implicit ctx: Context): String =
855+
if (x.isEmpty)
856+
tree match {
857+
case Select(pre, nme.CONSTRUCTOR) => foldOver(x, pre)
858+
case tree: RefTree if tree.name.isTypeName => tree.name.toString
859+
case tree: TypeDef => tree.name.toString
860+
case tree: AppliedTypeTree if followArgs && tree.args.nonEmpty =>
861+
s"${apply(x, tree.tpt)}_${extractArgs(tree.args)}"
862+
case tree: LambdaTypeTree =>
863+
apply(x, tree.body)
864+
case tree: Tuple =>
865+
if (followArgs) extractArgs(tree.trees) else "Tuple"
866+
case tree: Function if tree.args.nonEmpty =>
867+
if (followArgs) s"${extractArgs(tree.args)}_to_${apply("", tree.body)}" else "Function"
868+
case _ => foldOver(x, tree)
869+
}
870+
else x
871+
}
872+
private val typeNameExtractor = new NameExtractor(followArgs = true)
873+
private val argNameExtractor = new NameExtractor(followArgs = false)
874+
875+
private def inventTypeName(tree: Tree)(implicit ctx: Context): String = typeNameExtractor("", tree)
876+
809877
/** val p1, ..., pN: T = E
810878
* ==>
811879
* makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]]
@@ -960,14 +1028,11 @@ object desugar {
9601028
* def $anonfun(params) = body
9611029
* Closure($anonfun)
9621030
*/
963-
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = null, isImplicit: Boolean)(implicit ctx: Context): Block = {
964-
val span = params.headOption.fold(body.span)(_.span.union(body.span))
1031+
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = null, isContextual: Boolean)(implicit ctx: Context): Block =
9651032
Block(
9661033
DefDef(nme.ANON_FUN, Nil, params :: Nil, if (tpt == null) TypeTree() else tpt, body)
967-
.withSpan(span)
9681034
.withMods(synthetic | Artifact),
969-
Closure(Nil, Ident(nme.ANON_FUN), if (isImplicit) ImplicitEmptyTree else EmptyTree)).withSpan(span)
970-
}
1035+
Closure(Nil, Ident(nme.ANON_FUN), if (isContextual) ContextualEmptyTree else EmptyTree))
9711036

9721037
/** If `nparams` == 1, expand partial function
9731038
*
@@ -1020,9 +1085,9 @@ object desugar {
10201085
Function(param :: Nil, Block(vdefs, body))
10211086
}
10221087

1023-
def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
1024-
val params = makeImplicitParameters(formals.map(TypeTree))
1025-
new FunctionWithMods(params, body, Modifiers(Implicit))
1088+
def makeContextualFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
1089+
val params = makeImplicitParameters(formals.map(TypeTree), Contextual)
1090+
new FunctionWithMods(params, body, Modifiers(Implicit | Contextual))
10261091
}
10271092

10281093
/** Add annotation to tree:

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,15 +330,15 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
330330
functionWithUnknownParamType(tree).isDefined
331331

332332
/** Is `tree` an implicit function or closure, possibly nested in a block? */
333-
def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
334-
case tree: FunctionWithMods => tree.mods.is(Implicit)
335-
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit)
333+
def isContextualClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
334+
case tree: FunctionWithMods => tree.mods.is(Contextual)
335+
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Contextual)
336336
case Closure(_, meth, _) => true
337-
case Block(Nil, expr) => isImplicitClosure(expr)
337+
case Block(Nil, expr) => isContextualClosure(expr)
338338
case Block(DefDef(nme.ANON_FUN, _, params :: _, _, _) :: Nil, cl: Closure) =>
339339
params match {
340-
case param :: _ => param.mods.is(Implicit)
341-
case Nil => cl.tpt.eq(untpd.ImplicitEmptyTree) || defn.isImplicitFunctionType(cl.tpt.typeOpt)
340+
case param :: _ => param.mods.is(Contextual)
341+
case Nil => cl.tpt.eq(untpd.ContextualEmptyTree) || defn.isImplicitFunctionType(cl.tpt.typeOpt)
342342
}
343343
case _ => false
344344
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,8 @@ object Trees {
430430
case class Apply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
431431
extends GenericApply[T] {
432432
type ThisTree[-T >: Untyped] = Apply[T]
433+
434+
def isContextual = getAttachment(untpd.WithApply).nonEmpty
433435
}
434436

435437
/** fun[args] */
@@ -951,7 +953,7 @@ object Trees {
951953

952954
@sharable val EmptyTree: Thicket = genericEmptyTree
953955
@sharable val EmptyValDef: ValDef = genericEmptyValDef
954-
@sharable val ImplicitEmptyTree: Thicket = new EmptyTree // an empty tree marking an implicit closure
956+
@sharable val ContextualEmptyTree: Thicket = new EmptyTree // an empty tree marking a contextual closure
955957

956958
// ----- Auxiliary creation methods ------------------
957959

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
233233
val previousParamRefs = if (isParamDependent) new mutable.ListBuffer[TermRef]() else null
234234

235235
def valueParam(name: TermName, origInfo: Type): TermSymbol = {
236-
val maybeImplicit = if (tp.isImplicitMethod) Implicit else EmptyFlags
236+
val maybeImplicit =
237+
if (tp.isContextual) Implicit | Contextual
238+
else if (tp.isImplicitMethod) Implicit
239+
else EmptyFlags
237240
val maybeErased = if (tp.isErasedMethod) Erased else EmptyFlags
238241

239242
def makeSym(info: Type) = ctx.newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord)
@@ -1058,9 +1061,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
10581061
}
10591062
}
10601063

1061-
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type)(implicit ctx: Context): Tree = {
1064+
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type],
1065+
expectedType: Type, isContextual: Boolean = false)(implicit ctx: Context): Tree = {
10621066
val typer = ctx.typer
1063-
val proto = new FunProtoTyped(args, expectedType)(typer)
1067+
val proto = new FunProtoTyped(args, expectedType)(typer, isContextual)
10641068
val denot = receiver.tpe.member(method)
10651069
assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}")
10661070
val selected =

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
6868
override def isType: Boolean = body.isType
6969
}
7070

71-
/** A function type with `implicit` or `erased` modifiers */
71+
/** A function type with `implicit`, `erased`, or `contextual` modifiers */
7272
class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers)(implicit @constructorOnly src: SourceFile)
7373
extends Function(args, body)
7474

@@ -149,6 +149,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
149149
case class Inline()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Inline)
150150

151151
case class Enum()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Enum)
152+
153+
case class Instance()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Implicit)
152154
}
153155

154156
/** Modifiers and annotations for definitions
@@ -213,6 +215,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
213215
def hasFlags: Boolean = flags != EmptyFlags
214216
def hasAnnotations: Boolean = annotations.nonEmpty
215217
def hasPrivateWithin: Boolean = privateWithin != tpnme.EMPTY
218+
def hasMod(cls: Class[_]) = mods.exists(_.getClass == cls)
216219

217220
private def isEnum = is(Enum, butNot = JavaDefined)
218221

@@ -269,6 +272,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
269272
*/
270273
val OriginalSymbol: Property.Key[Symbol] = new Property.Key
271274

275+
/** Property key for contextual Apply trees of the form `fn with arg` */
276+
val WithApply: Property.StickyKey[Unit] = new Property.StickyKey
277+
272278
// ------ Creation methods for untyped only -----------------
273279

274280
def Ident(name: Name)(implicit src: SourceFile): Ident = new Ident(name)
@@ -398,9 +404,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
398404
def makeParameter(pname: TermName, tpe: Tree, mods: Modifiers = EmptyModifiers)(implicit ctx: Context): ValDef =
399405
ValDef(pname, tpe, EmptyTree).withMods(mods | Param)
400406

401-
def makeSyntheticParameter(n: Int = 1, tpt: Tree = null)(implicit ctx: Context): ValDef =
407+
def makeSyntheticParameter(n: Int = 1, tpt: Tree = null, flags: FlagSet = EmptyFlags)(implicit ctx: Context): ValDef =
402408
ValDef(nme.syntheticParamName(n), if (tpt == null) TypeTree() else tpt, EmptyTree)
403-
.withFlags(SyntheticTermParam)
409+
.withFlags(flags | SyntheticTermParam)
404410

405411
def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context): Tree =
406412
if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object Printers {
1717
val checks: Printer = noPrinter
1818
val config: Printer = noPrinter
1919
val cyclicErrors: Printer = noPrinter
20-
val debug = noPrinter
20+
val debug = noPrinter // no type annotion here to force inlining
2121
val derive: Printer = noPrinter
2222
val dottydoc: Printer = noPrinter
2323
val exhaustivity: Printer = noPrinter

0 commit comments

Comments
 (0)