Skip to content

Commit d3d961e

Browse files
committed
add hasSpecializations property to CompilationUnits
This commit adds a new field to the CompilationUnit to check whether the unit contains an inline trait. This property is then used by inline trait specific passes to determine whether or not they need to run.
1 parent 3adfcbd commit d3d961e

6 files changed

Lines changed: 129 additions & 93 deletions

File tree

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ class CompilationUnit protected (val source: SourceFile, val info: CompilationUn
5757
*/
5858
var needsInlining: Boolean = false
5959

60+
/** Set to `true` if there are inline traits (with or without Specialized context bounds)
61+
* This is used by:
62+
* DesugarSpecializedTraits, SpecializeInlineTraits, PruneInlinedMethods, PruneInlineTraits
63+
*/
64+
var hasSpecializations: Boolean = false
65+
6066
var hasMacroAnnotations: Boolean = false
6167

6268
def hasUnrollDefs: Boolean = unrolledClasses.nonEmpty

compiler/src/dotty/tools/dotc/transform/DesugarSpecializedTraits.scala

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -353,74 +353,6 @@ class DesugarSpecializedTraits extends MiniPhase, IdentityDenotTransformer:
353353
// but not sure if that's worth doing (it would be throwing away work).
354354
(generatedTraitStatsFinal ++ generatedClassStatsFinal ++ stats, specializationsFinal)
355355
}
356-
357-
private def checkSpecializedTraitRules(tree: Tree)(using Context) =
358-
def checkType(t: Type, pos: SrcPos) = t.widen.dealias match {
359-
case SpecializedEvidence(_) =>
360-
report.error(s"Only inline traits and inline functions may take Specialized type parameters", pos)
361-
case _ =>
362-
}
363-
364-
// TODO: Depending on how we ultimately organize the phasing
365-
// If we settle on using miniphases, this could be moved into transformDefDef and transformBlock.
366-
tree.foreachSubTree {
367-
case ddef: DefDef if ddef.symbol.isConstructor && !ddef.symbol.owner.is(Flags.Inline) =>
368-
ddef.paramss.flatten.foreach(p => checkType(p.tpe, ddef.srcPos))
369-
case ddef: DefDef if !ddef.symbol.isConstructor && !ddef.symbol.is(Flags.Inline) =>
370-
ddef.paramss.flatten.foreach(p => checkType(p.tpe, ddef.srcPos))
371-
case AnonymousClassInstance(anon) =>
372-
def deandify(tp: Type): Iterator[Type] = tp match {
373-
case AndType(l, r) => deandify(l) ++ deandify(r)
374-
case _ => Iterator.single(tp)
375-
}
376-
anon.typeTree.tpe match {
377-
case a: AndType => /* Multiple mixed in traits will be typed as an AndType */
378-
deandify(a) foreach {trt =>
379-
Specialization.unapply(trt, anon.typeTree.span) foreach { spec =>
380-
if spec.hasSpecializedParams then
381-
report.error(
382-
"""
383-
Anonymous classes acting as instances of Specialized traits may not mix in other traits;
384-
You can make a named object instead if you like.
385-
""",
386-
anon.srcPos
387-
)
388-
}
389-
}
390-
391-
case tpe =>
392-
Specialization.unapply(tpe, anon.typeTree.span).map(spec =>
393-
{
394-
if spec.hasSpecializedParams then
395-
// Only allowed to contain evidence parameters
396-
if anon.body.filterNot(x => x.symbol.name.is(ContextBoundParamName)).nonEmpty then
397-
report.error(
398-
"""
399-
Anonymous classes acting as instances of Specialized traits may not have additional members;
400-
you can make a named object instead if you like.
401-
""",
402-
anon.srcPos
403-
)
404-
405-
anon.parentCalls match {
406-
case (obj :: parentsOfSpecTrait) :+ (app@Apply(_, _))
407-
if (obj.symbol.owner == ctx.definitions.ObjectClass) &&
408-
(parentsOfSpecTrait.forall(x => spec.symbol.asClass.baseClasses.exists(p => p == x.symbol.owner))) =>
409-
case _ =>
410-
report.error(
411-
"""
412-
Anonymous classes acting as instances of Specialized traits may not mix in other traits;
413-
you can make a named object instead if you like.""",
414-
anon.srcPos
415-
)
416-
}
417-
else
418-
tree
419-
}).getOrElse(tree)
420-
}
421-
422-
case _ =>
423-
}
424356

425357
private def specializedTraitCtx(using Context): Context =
426358
ctx.fresh.setInlineTraitState(ctx.inlineTraitState.copyInPhase(InlineTraitState.InlineContext.SpecializedTraits))
@@ -439,10 +371,9 @@ class DesugarSpecializedTraits extends MiniPhase, IdentityDenotTransformer:
439371
// As long as we remember to call transformFollowing on the synthetic classes (which we do) then
440372
// we should be composable in the way that we want to be.
441373
override def transformUnit(tree: Tree)(using Context): Tree =
442-
tree match {
374+
if !ctx.compilationUnit.hasSpecializations then tree
375+
else tree match {
443376
case pkg@PackageDef(pid, stats) =>
444-
checkSpecializedTraitRules(tree)
445-
446377
val (stats1, specializedTraitCache2) = transformStatements(stats, specializedTraitCache)
447378

448379
specializedTraitCache = specializedTraitCache2

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,85 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
315315
private def checkInlTraitPrivateMemberIsLocal(tree: Tree)(using Context): Unit =
316316
if tree.symbol.owner.isInlineTrait && tree.symbol.isAllOf(Private, butNot = Local) then
317317
report.error(em"implementation restriction: inline traits cannot have non-local private members. This also means no retained inline methods.", tree.srcPos)
318+
319+
private def checkFunctionContextBounds(ddef: DefDef)(using Context): Unit = {
320+
val symbol = ddef.symbol
321+
322+
val isConstructorOfNonInlineType = ddef.symbol.isConstructor && !ddef.symbol.owner.is(Flags.Inline)
323+
val isRegularDefDef = !ddef.symbol.isConstructor && !ddef.symbol.is(Flags.Inline)
324+
if isConstructorOfNonInlineType || isRegularDefDef then
325+
ddef.paramss.flatten.foreach {
326+
param => if SpecializedEvidence.unapply(param.tpe.widen.dealias).nonEmpty then
327+
report.error(s"Only inline traits and inline functions may take Specialized type parameters", param.srcPos)
328+
329+
}
330+
}
331+
332+
private def checkForIllegalUseOfSpecialized(tdef: TypeDef)(using Context): Unit = {
333+
val containUseOfSpecialized =
334+
tdef.rhs.tpe.existsPart(t => t.typeSymbol == defn.SpecializedClass.asType)
335+
if containUseOfSpecialized && (tdef.symbol ne defn.SpecializedClass)then
336+
report.error(IllegalUseOfSpecialized(), tdef.srcPos)
337+
}
338+
339+
private def checkValidAnonymousSpecialization(tree: Block)(using Context): Unit = tree match {
340+
case AnonymousClassInstance(anon) =>
341+
def deandify(tp: Type): Iterator[Type] = tp match {
342+
case AndType(l, r) => deandify(l) ++ deandify(r)
343+
case _ => Iterator.single(tp)
344+
}
345+
anon.typeTree.tpe match {
346+
case a: AndType => /* Multiple mixed in traits will be typed as an AndType */
347+
deandify(a) foreach {
348+
Specialization.unapply(_, anon.typeTree.span) foreach {
349+
spec => if spec.hasSpecializedParams then
350+
report.error(
351+
"""
352+
Anonymous classes acting as instances of Specialized traits may not mix in other traits;
353+
You can make a named object instead if you like.
354+
""",
355+
anon.srcPos
356+
)
357+
}
358+
}
359+
360+
case tpe =>
361+
Specialization
362+
.unapply(tpe, anon.typeTree.span)
363+
.foreach(spec =>
364+
if spec.hasSpecializedParams then
365+
// Only allowed to contain evidence parameters
366+
if anon.body.filterNot(x => x.symbol.name.is(ContextBoundParamName)).nonEmpty then
367+
report.error(
368+
"""
369+
Anonymous classes acting as instances of Specialized traits may not have additional members;
370+
you can make a named object instead if you like.
371+
""",
372+
anon.srcPos
373+
)
374+
375+
def hasOnlyValidParents: Boolean = anon.parentCalls match {
376+
case (obj :: parentsOfSpecTrait) :+ (app@Apply(_, _)) =>
377+
val isFirstParentObject = obj.symbol.owner == ctx.definitions.ObjectClass
378+
val validOtherParents =
379+
parentsOfSpecTrait.forall(x => spec.symbol.asClass.baseClasses.exists(p => p == x.symbol.owner))
380+
381+
isFirstParentObject && validOtherParents
382+
case _ => false
383+
}
384+
385+
if !hasOnlyValidParents then
386+
report.error(
387+
"""
388+
Anonymous classes acting as instances of Specialized traits may not mix in other traits;
389+
you can make a named object instead if you like.""",
390+
anon.srcPos
391+
)
392+
)
393+
}
394+
case _ =>
395+
}
396+
318397

319398
private def transformSelect(tree: Select, targs: List[Tree])(using Context): Tree = {
320399
val qual = tree.qualifier
@@ -581,6 +660,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
581660
if tree.symbol == defn.SpecializedModule && (ctx.owner ne defn.SpecializedModule.moduleClass) then
582661
report.error(IllegalUseOfSpecialized(), tree.srcPos)
583662
registerNeedsInlining(tree)
663+
registerIfHasSpecializations(tree)
584664
val tree1 = checkUsableAsValue(tree)
585665
tree1.tpe match {
586666
case tpe: ThisType => This(tpe.cls).withSpan(tree.span)
@@ -678,14 +758,13 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
678758
case tree: DefDef =>
679759
registerIfHasMacroAnnotations(tree)
680760
Checking.checkPolyFunctionType(tree.tpt)
761+
checkFunctionContextBounds(tree)
681762
annotateContextResults(tree)
682763
val tree1 = cpy.DefDef(tree)(tpt = explicifyTpt(tree))
683764
processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef]))
684765
case tree: TypeDef =>
685-
if tree.symbol.isInlineTrait then
686-
ctx.compilationUnit.needsInlining = true // Check and transform inline traits
687-
if tree.rhs.tpe.existsPart(t => t.typeSymbol == defn.SpecializedClass.asType) && (tree.symbol ne defn.SpecializedClass) then
688-
report.error(IllegalUseOfSpecialized(), tree.srcPos)
766+
registerIfHasSpecializations(tree)
767+
checkForIllegalUseOfSpecialized(tree)
689768
registerIfHasMacroAnnotations(tree)
690769
val sym = tree.symbol
691770
if (sym.isClass)
@@ -696,9 +775,8 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
696775
sym.keepAnnotationsCarrying(thisPhase, Set(defn.CompanionClassMetaAnnot), orNoneOf = defn.MetaAnnots)
697776
tree.rhs match
698777
case impl: Template =>
778+
registerIfHasSpecializations(impl)
699779
for parent <- impl.parents do
700-
if Inlines.symbolFromParent(parent).isInlineTrait then
701-
ctx.compilationUnit.needsInlining = true
702780
Checking.checkTraitInheritance(parent.tpe.classSymbol, sym.asClass, parent.srcPos)
703781
// Constructor parameters are in scope when typing a parent.
704782
// While they can safely appear in a parent tree, to preserve
@@ -789,6 +867,9 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
789867
)
790868
case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) =>
791869
superAcc.withInvalidCurrentClass(super.transform(tree))
870+
case block: Block =>
871+
checkValidAnonymousSpecialization(block)
872+
super.transform(tree)
792873
case tree: RefinedTypeTree =>
793874
Checking.checkPolyFunctionType(tree)
794875
super.transform(tree)
@@ -814,6 +895,18 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
814895
if tree.symbol.is(Inline) && !Inlines.inInlineMethod && !ctx.mode.is(Mode.NoInline) then
815896
ctx.compilationUnit.needsInlining = true
816897

898+
private def registerIfHasSpecializations(tree: Tree)(using Context): Unit = {
899+
val hasSpecializations = tree match
900+
case tree: Ident => tree.symbol == defn.SpecializedModule_apply
901+
case tree: TypeDef => tree.symbol.isInlineTrait
902+
case tree: Template => tree.parents.exists(parent => Inlines.symbolFromParent(parent).isInlineTrait)
903+
case _ => false
904+
905+
if hasSpecializations then
906+
ctx.compilationUnit.needsInlining = true
907+
ctx.compilationUnit.hasSpecializations = true
908+
}
909+
817910
/** Check if the definition has macro annotation and sets `compilationUnit.hasMacroAnnotations` if needed. */
818911
private def registerIfHasMacroAnnotations(tree: DefTree)(using Context) =
819912
if !Inlines.inInlineMethod && tree.symbol.hasMacroAnnotation then

compiler/src/dotty/tools/dotc/transform/PruneInlineTraits.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,23 @@ class PruneInlineTraits extends MiniPhase with SymTransformer { thisTransform =>
3232
else sym
3333

3434
override def transformTemplate(tree: Template)(using Context): Tree =
35-
cpy.Template(tree)(body = tree.body.flatMap({
35+
if !ctx.compilationUnit.hasSpecializations then tree
36+
else cpy.Template(tree)(body = tree.body.flatMap({
3637
case stmt: ValDef if isEraseable(stmt.symbol) => Some(cpy.ValDef(stmt)(rhs = EmptyTree))
3738
case stmt: DefDef if isEraseable(stmt.symbol) => Some(cpy.DefDef(stmt)(rhs = EmptyTree))
3839
case stmt: (ValDef | DefDef) if isDeletable(stmt.symbol) => None
3940
case stmt => Some(stmt)
4041
}))
4142

4243
private def isEraseable(sym: SymDenotation)(using Context): Boolean =
43-
!sym.isType
44+
sym.exists
45+
&& sym.owner.isInlineTrait
46+
&& !sym.isType
4447
&& !sym.isConstructor
4548
&& !sym.is(Param)
4649
&& !sym.is(ParamAccessor)
4750
&& !sym.is(Local)
4851
&& !sym.isLocalDummy
49-
&& sym.exists
50-
&& sym.owner.isInlineTrait
5152

5253
private def isDeletable(sym: SymDenotation)(using Context): Boolean =
5354
!sym.isType

compiler/src/dotty/tools/dotc/transform/PruneInlinedMethods.scala

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,24 @@ class PruneInlinedMethods extends MiniPhase with InfoTransformer { thisTransform
2020

2121
override def description: String = PruneInlinedMethods.description
2222

23-
override def transformInfo(tp: Type, sym: Symbol)(using Context) = tp match {
24-
case clsInfo: ClassInfo if sym.isClass && !sym.is(Package) && !sym.is(JavaDefined) =>
25-
clsInfo.derivedClassInfo(decls =
26-
clsInfo.decls.filteredScope(!isDeletable(_))
27-
)
28-
case _ => tp
23+
override def transformInfo(tp: Type, sym: Symbol)(using Context) =
24+
if !ctx.compilationUnit.hasSpecializations then tp
25+
else tp match {
26+
case clsInfo: ClassInfo if sym.isClass && !sym.is(Package) && !sym.is(JavaDefined) =>
27+
clsInfo.derivedClassInfo(decls =
28+
clsInfo.decls.filteredScope(!isDeletable(_))
29+
)
30+
case _ => tp
2931
}
3032

3133
override def transformTemplate(tree: Template)(using Context): Tree =
32-
cpy.Template(tree)(body = tree.body.flatMap({
33-
case stmt: DefDef if isDeletable(stmt.symbol) => None
34-
case stmt => Some(stmt)
35-
}))
34+
if !ctx.compilationUnit.hasSpecializations then tree
35+
else cpy.Template(tree)(
36+
body = tree.body.flatMap {
37+
case stmt: DefDef if isDeletable(stmt.symbol) => None
38+
case stmt => Some(stmt)
39+
}
40+
)
3641

3742
private def isDeletable(sym: Symbol)(using Context): Boolean =
3843
Specialization.isSpecializedMethod(sym)

tests/run/i3000b.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
Foo$$anon$2
2-
bar.Bar$$anon$1
1+
Foo$$anon$1
2+
bar.Bar$$anon$2

0 commit comments

Comments
 (0)