Skip to content

Inline traits for specialization in Scala 3 #17329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 106 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
56c508b
Allow parsing of inline trait, update printer
timotheeandres Feb 24, 2023
5ffb63e
Implement phase 1 of inlining
timotheeandres Feb 28, 2023
966f12d
Implement phase 2 of inlining
timotheeandres Feb 28, 2023
3c4b310
Use inlining capabilities to inline body of defs
timotheeandres Mar 2, 2023
810dcb6
Improve inlining of traits
timotheeandres Mar 9, 2023
7531239
Add atomic tests for inline traits
timotheeandres Mar 23, 2023
f01f90d
Import Nicolas' work
timotheeandres Mar 28, 2023
567d555
Avoid generating accessor fields for inline traits
timotheeandres Mar 28, 2023
e572676
Use parent span for inlined code
timotheeandres Mar 30, 2023
61516a1
Blacklist inline trait tests due to pickling
timotheeandres Mar 30, 2023
2a077cc
Add benchmark for toy version of Numeric
timotheeandres Apr 3, 2023
8b3437f
Delete by-name parameter tests
timotheeandres Apr 4, 2023
b53f142
Revert "Use parent span for inlined code"
timotheeandres Apr 4, 2023
cd14169
Forbid var in inline traits for now
timotheeandres Apr 4, 2023
e167b96
Add override modifier to param accessors
timotheeandres Apr 4, 2023
e382e75
Forbid using/implicit parameters for now
timotheeandres Apr 4, 2023
ab2c439
Fix typo in type bound
timotheeandres Apr 4, 2023
fdfbe26
Multiple files tests, quick fix of span
timotheeandres Apr 6, 2023
e4e42ae
Improve benchmark, make adding implementations easier
timotheeandres Apr 6, 2023
5866057
Add specialized and inline trait matrices for benchmark
timotheeandres Apr 13, 2023
8c84089
Fix issue with generic type in signature
timotheeandres Apr 13, 2023
4d981af
Import modifications by Nicolas
nicolasstucki Apr 13, 2023
6b02d82
Rework and fix tests
timotheeandres Apr 14, 2023
67173de
Make fields private again
timotheeandres Apr 14, 2023
a516748
Fix type issue between untpd and tpd
timotheeandres Apr 14, 2023
f568ef8
Add tests with manual override
timotheeandres Apr 14, 2023
bd94dc6
Simplify type argument substitution in inline methods
nicolasstucki Apr 14, 2023
fd14025
Refactor creation of inlined symbols
nicolasstucki Apr 14, 2023
c06a501
Only call `inline` on rhs
nicolasstucki Apr 14, 2023
fa53620
Perform misc. changes on tests
timotheeandres Apr 17, 2023
59feb3f
Only allow override final if member is synthetic
timotheeandres Apr 17, 2023
2804dfd
Prevent generation of setters in inline traits
timotheeandres Apr 17, 2023
8b4741b
Don't generate members if they are overridden by user
timotheeandres Apr 17, 2023
59d6221
Fix type params not being replaced in RHS
timotheeandres Apr 18, 2023
b762cc9
Clean up InlineParentTrait
timotheeandres Apr 18, 2023
4bf1c76
Compute overriden symbols only once
timotheeandres Apr 20, 2023
e92c7b5
Factorize code to know what to inline from inline trait
timotheeandres Apr 21, 2023
c676689
Allow inlining of deferred defs
timotheeandres Apr 21, 2023
b9a81b1
Clean up example 4, make version without inner class
timotheeandres Apr 21, 2023
026212f
Update inner class tests
timotheeandres Apr 21, 2023
76e5e9a
Move special expansion cases in inlining methods
timotheeandres Apr 24, 2023
a4fd776
Create InlineTreeMap, make helper function for param accessors
timotheeandres Apr 24, 2023
334b5d3
Make accessor values functions tailrec
timotheeandres Apr 24, 2023
f1aa33e
Implement grandparent inline traits, fix parameters shadowing
timotheeandres Apr 24, 2023
2d20a79
Clean up code to get defs to inline
timotheeandres Apr 26, 2023
fcb2279
Start moving code from Typer
timotheeandres Apr 26, 2023
84a1682
Fix tests/pos/inline-trait-multiple-stages-generic-defs
nicolasstucki Apr 26, 2023
e13a71f
Pass overridden decls when expanding
timotheeandres Apr 26, 2023
59bee43
Remove pickling test blacklist
timotheeandres Apr 26, 2023
845c00b
Move trait inlining into Inlining phase
timotheeandres Apr 26, 2023
3d162cb
Implement ancestors inline traits without term args
timotheeandres Apr 27, 2023
50b6743
Disable test with inner class
timotheeandres Apr 27, 2023
34f77f6
Ensure inline traits have same extends precedence as normal traits
timotheeandres Apr 27, 2023
d605bc5
Partially implement inner traits
timotheeandres Apr 28, 2023
9e26b35
Use typeRef on ctx.owner
timotheeandres Apr 28, 2023
6523fcc
Split symbol inlining method by case
timotheeandres May 8, 2023
bcb6702
Improve checks in phases
timotheeandres May 10, 2023
e3e5582
Remove RHS of definitions in inline traits
timotheeandres May 10, 2023
c9ec223
Add tests with macros (unstable)
timotheeandres May 10, 2023
08e2689
Fix owner of bodies to their rightful symbols
timotheeandres May 10, 2023
5348868
Add more tests
timotheeandres May 10, 2023
060f5ab
Inline code of thisType at sole call site
timotheeandres May 10, 2023
ffe94e8
Forbid defining inline traits inside inline traits
timotheeandres May 11, 2023
67d8b1e
Enable inheritance tests
timotheeandres May 11, 2023
fde0c55
Check if owner is inline trait last
timotheeandres May 12, 2023
086f0b9
Use proper thisType, add coords to inlinedSym
timotheeandres May 12, 2023
2fc66ac
Extract param accessors code into helper class
timotheeandres May 16, 2023
686aac0
Move trait-related code to InlineParentTrait
timotheeandres May 16, 2023
78077a4
Add missing context to expandStat, refactor code
timotheeandres May 23, 2023
2172d03
Refactor Inlining for clarity
timotheeandres May 23, 2023
3f749f1
Factorize code for new inner class names
timotheeandres May 23, 2023
5570b25
Remove last refs to inline trait from Inliner
timotheeandres May 23, 2023
829f9eb
Call super before matching in Tree-/TypeMap
timotheeandres May 23, 2023
a1bc61f
Improve class symbol inlining
timotheeandres May 23, 2023
5226450
Prevent creating new accessor names for inner class' params
timotheeandres May 23, 2023
728d837
Do not add override flag to inner class-like type params
timotheeandres May 23, 2023
2392575
Remove incorrect parent, TODO add correct one
timotheeandres May 23, 2023
c4f13af
Add proper type bound to generated type
timotheeandres May 23, 2023
0b6ec5e
Register class-like symbol to be replaced by inliner
timotheeandres May 23, 2023
6075cd3
Forbid term params for inline traits
timotheeandres May 24, 2023
74aaba9
Create vals for child type and child tree
timotheeandres May 25, 2023
9514ccd
Add proper parent to inner class info
timotheeandres May 25, 2023
a5b53f8
Make function to inline just rhs
timotheeandres May 25, 2023
6fc5bd9
Handle inlining of statements in inline trait body
timotheeandres May 25, 2023
8a756d2
Add tests for inner traits
timotheeandres May 30, 2023
39e57f2
Remove FIXME along with info mapping
timotheeandres May 31, 2023
d7ada60
Add test for anonymous class extending inline trait
timotheeandres Jun 1, 2023
4b99869
Rename param for clarity
timotheeandres Jun 1, 2023
746e677
Create helper methods for inline trait rewriting
timotheeandres Jun 1, 2023
d896688
Strip parents of inner class from concrete elements
timotheeandres Jun 1, 2023
b1eec85
Add Synthetic flag to new trait and type
timotheeandres Jun 1, 2023
e13d3c4
Adapt body of child to point to inlined members
timotheeandres Jun 1, 2023
34a5567
Use wildcard for default var value in benchmark
timotheeandres Jun 5, 2023
5a0339b
Add Pair to benchmark
timotheeandres Jun 5, 2023
b1e16ab
Manual specialization, remove unused toTuple
timotheeandres Jun 6, 2023
cb95383
Add boxing benchmark for report
timotheeandres Jun 14, 2023
2784d54
Move inline trait inlining to its own phase
timotheeandres Jun 5, 2023
5c82ee0
Add simple test for inner class
timotheeandres Jun 16, 2023
6e8a489
Rework benchmark files for report
timotheeandres Jul 4, 2023
7c82e3d
Remove target name for inlined inner class
timotheeandres Jul 4, 2023
fd737ea
Restrict non-local private fields
timotheeandres Jul 4, 2023
7e2952c
Forbid user from overriding vars
timotheeandres Jul 4, 2023
f6e171b
Add tests for constructor arguments with side effects
timotheeandres Jul 4, 2023
102269e
Improve error message, remove unused method
timotheeandres Jul 4, 2023
a557940
Allow overriding opaque aliases for inline traits
timotheeandres Jul 4, 2023
17e17bc
Let compiler complain if deferred member isn't implemented
timotheeandres Jul 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix issue with generic type in signature
  • Loading branch information
timotheeandres committed Jun 16, 2023
commit 8c84089b3607ff441bfcb5a73406c0435b64f2cf
16 changes: 6 additions & 10 deletions compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ class Inliner(val call: tpd.Tree)(using Context):
protected val callTypeArgs = typeArgss(call).flatten
protected val callValueArgss = termArgss(call)
protected val inlinedMethod = methPart.symbol
private val inlineCallPrefix =
protected val inlineCallPrefix =
qualifier(methPart).orElse(This(inlinedMethod.enclosingClass.asClass))

// Make sure all type arguments to the call are fully determined,
// but continue if that's not achievable (or else i7459.scala would crash).
for arg <- callTypeArgs do
isFullyDefined(arg.tpe, ForceDegree.flipBottom)

/** A map from parameter names of the inlineable method to references of the actual arguments.
/** A map from parameter names of the inlineable method or trait to references of the actual arguments.
* For a type argument this is the full argument type.
* For a value argument, it is a reference to either the argument value
* (if the argument is a pure expression of singleton type), or to `val` or `def` acting
Expand All @@ -169,7 +169,7 @@ class Inliner(val call: tpd.Tree)(using Context):
/** A map from parameter names of the inlineable method to spans of the actual arguments */
private val paramSpan = new mutable.HashMap[Name, Span]

/** A map from references to (type and value) parameters of the inlineable method
/** A map from references to (type and value) parameters of the inlineable method or trait
* to their corresponding argument or proxy references, as given by `paramBinding`.
*/
private[inlines] val paramProxy = new mutable.HashMap[Type, Type]
Expand All @@ -187,8 +187,8 @@ class Inliner(val call: tpd.Tree)(using Context):
*
* These are different (wrt ==) types but represent logically the same key
*/
private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef]
private val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType]
protected val thisProxy = new mutable.HashMap[ClassSymbol, TermRef]
protected val thisInlineTraitProxy = new mutable.HashMap[ClassSymbol, ThisType]

/** A buffer for bindings that define proxies for actual arguments */
private val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]
Expand Down Expand Up @@ -452,7 +452,7 @@ class Inliner(val call: tpd.Tree)(using Context):
* references of a method are (we only know the method's type, but that contains TypeParamRefs
* and MethodParams, not TypeRefs or TermRefs.
*/
private def registerType(tpe: Type): Unit = tpe match {
protected def registerType(tpe: Type): Unit = tpe match {
case tpe: ThisType if !canElideThis(tpe) && !thisProxy.contains(tpe.cls) =>
val proxyName = s"${tpe.cls.name}_this".toTermName
val proxyType = inlineCallPrefix.tpe.dealias.tryNormalize match {
Expand All @@ -462,10 +462,6 @@ class Inliner(val call: tpd.Tree)(using Context):
thisProxy(tpe.cls) = newSym(proxyName, InlineProxy, proxyType).termRef
for (param <- tpe.cls.typeParams)
paramProxy(param.typeRef) = adaptToPrefix(param.typeRef)
case tpe: ThisType if tpe.cls.isInlineTrait =>
thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner))
for (param <- tpe.cls.typeParams)
paramProxy(param.typeRef) = adaptToPrefix(param.typeRef)
case tpe: NamedType
if tpe.symbol.is(Param)
&& tpe.symbol.owner == inlinedMethod
Expand Down
127 changes: 91 additions & 36 deletions compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -470,45 +470,100 @@ object Inlines:
import Inlines.*

def expandDefs(): List[Tree] =
val argsMap = parent match
case Apply(_, args) =>
val MethodType(paramNames) = parent.symbol.info: @unchecked
paramNames.zip(args).toMap
case _ => Map.empty

val Block(stats, _) = Inlines.bodyToInline(parentSym): @unchecked
val s = stats.map(expandStat)
s.map(inlined(_)._2)
end expandDefs

override protected def registerType(tpe: Type): Unit = tpe match {
case tpe: ThisType if tpe.cls.isInlineTrait =>
thisInlineTraitProxy(tpe.cls) = ThisType.raw(TypeRef(ctx.owner.prefix, ctx.owner))
for (param <- tpe.cls.typeParams)
paramProxy(param.typeRef) = param.typeRef.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner)
case _ => super.registerType(tpe)
}

val stats1 = stats.map { stat =>
val sym = stat.symbol
stat match
case stat: ValDef if sym.isOneOf(Given | Implicit) =>
report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos)
stat
case stat: ValDef =>
val vdef = cloneValDef(stat)
val vdef1 =
if sym.is(ParamAccessor) then
vdef.symbol.resetFlag(ParamAccessor)
cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName))
else
vdef
if !sym.is(Private) then
vdef1.symbol.setFlag(Override)
vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat?
case stat: DefDef =>
val ddef = cloneDefDef(stat)
if !sym.is(Private) then ddef.symbol.setFlag(Override)
if sym.is(Mutable) then
report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos)
ddef
case stat @ TypeDef(_, impl: Template) =>
cloneClass(stat, impl)
case stat: TypeDef =>
val tdef = cloneTypeDef(stat)
tdef.symbol.setFlag(Override)
tdef
private val argsMap: Map[Names.Name, untpd.Tree] =
/*
* Consider the parent `new A[String](1)("A")` with signature `inline trait A[T](x: Int)(y: T)`.
* The `parent` tree is "right-to-left": Apply(Apply(TypeApply(..., String), 1), "A")
* The `info` tree is "left-to-right": PolyType(T, ..., MethodType(x, Int, MethodType(y, T, ...)))
* This recursive solution goes as deep as possible in the tree, then matches arguments with their
* names in a bottom-up fashion.
*/
def rec(tree: Tree, info: Type, acc: Map[Names.Name, untpd.Tree]): (Option[Type], Map[Names.Name, untpd.Tree]) =
tree match {
case Apply(tree1, args) => rec(tree1, info, acc) match {
case (Some(info1), acc1) => info1 match {
case MethodTpe(paramNames, _, info2) if paramNames.length == args.length =>
(Some(info2), acc1 ++ paramNames.zip(args).toMap)
case _ =>
report.error(s"mismatch between Apply ${tree.show} and info ${info1}", parent.srcPos)
(None, acc1)
}
case res => res
}
case TypeApply(tree1, tpes) => rec(tree1, info, acc) match {
case (Some(info1), acc1) => info1 match {
case PolyType(lambdaParams, info2) if lambdaParams.length == tpes.length =>
(Some(info2), acc1)
case _ =>
report.error(s"mismatch between TypeApply ${tree.show} and info ${info1}", parent.srcPos)
(None, acc1)
}
case res => res
}
case _ => (Some(info), acc)
}

parent match {
case _: GenericApply =>
val (remainingInfo, map) = rec(parent, parent.symbol.info, Map.empty)
remainingInfo match {
case Some(MethodType(paramNames)) =>
report.error(s"could not match the following parameters with their values: ${paramNames.map(_.show).mkString(", ")}", parent.srcPos)
case _ =>
}
map
case _ => Map.empty
}
stats1.map(stat => inlined(stat)._2)
end argsMap

private def expandStat(stat: untpd.Tree): untpd.Tree =
val sym = stat.symbol
stat match
case stat: ValDef if sym.isOneOf(Given | Implicit) =>
report.error("implementation restriction: inline traits cannot have implicit or given variables", stat.srcPos)
stat
case stat: ValDef =>
val vdef = cloneValDef(stat)
val vdef1 =
if sym.is(ParamAccessor) then
vdef.symbol.resetFlag(ParamAccessor)
cpy.ValDef(vdef)(rhs = argsMap(sym.name.asTermName))
else
vdef
if !sym.is(Private) then
vdef1.symbol.setFlag(Override)
vdef1 // TODO keep rhs? Can we do a single ValOrDefDef case using cloneStat?
case stat: DefDef =>
val ddef = cloneDefDef(stat)
if !sym.is(Private) then
ddef.symbol.setFlag(Override)
if sym.is(Mutable) then
report.error("implementation restriction: inline traits cannot have mutable variables", stat.srcPos)
ddef
case stat @ TypeDef(_, impl: Template) =>
report.error("inline traits do not handle inner classes yet", stat.srcPos)
cloneClass(stat, impl)
case stat: TypeDef =>
val tdef = cloneTypeDef(stat)
tdef.symbol.setFlag(Override)
tdef
case _ =>
report.error(s"unknown body element of inline ${parentSym.show}: ${stat.show}", stat.srcPos)
stat
end expandStat

private def inlinedRhs(rhs: Tree): Inlined =
Inlined(tpd.ref(parentSym), Nil, rhs).withSpan(parent.span)
Expand Down