Skip to content

Commit 4a4affd

Browse files
committed
Add assertions to tryPromote
1 parent b7dba0d commit 4a4affd

File tree

1 file changed

+49
-32
lines changed

1 file changed

+49
-32
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,47 +1126,64 @@ object Semantic:
11261126
* If the object contains nested classes as members, the checker simply
11271127
* reports a warning to avoid expensive checks.
11281128
*
1129-
* TODO: we need to revisit whether this is needed once we make the
1130-
* system more flexible in other dimentions: e.g. leak to
1131-
* methods or constructors, or use ownership for creating cold data structures.
11321129
*/
11331130
def tryPromote(msg: String): Contextual[List[Error]] = log("promote " + warm.show + ", promoted = " + promoted, printer) {
11341131
val classRef = warm.klass.appliedRef
11351132
val hasInnerClass = classRef.memberClasses.filter(_.symbol.hasSource).nonEmpty
11361133
if hasInnerClass then
11371134
return PromoteError(msg + "Promotion cancelled as the value contains inner classes. ", trace.toVector) :: Nil
11381135

1139-
val errors = Reporter.stopEarly {
1140-
for klass <- warm.klass.baseClasses if klass.hasSource do
1141-
val obj = warm.objekt
1142-
val outer = obj.outer(klass)
1136+
val obj = warm.objekt
1137+
1138+
def doPromote(klass: ClassSymbol, subClass: ClassSymbol, subClassSegmentHot: Boolean)(using Reporter): Unit =
1139+
val outer = obj.outer(klass)
1140+
val isHotSegment = outer.isHot && {
11431141
val ctor = klass.primaryConstructor
1144-
val isHotSegment = outer.isHot && {
1145-
val ctorDef = ctor.defTree.asInstanceOf[DefDef]
1146-
val params = ctorDef.termParamss.flatten.map(_.symbol)
1147-
// We have cached all parameters on the object
1148-
params.forall(param => obj.field(param).isHot)
1149-
}
1142+
val ctorDef = ctor.defTree.asInstanceOf[DefDef]
1143+
val params = ctorDef.termParamss.flatten.map(_.symbol)
1144+
// We have cached all parameters on the object
1145+
params.forall(param => obj.field(param).isHot)
1146+
}
11501147

1151-
// If the outer and parameters of a class are all hot, then accessing fields and methods of the current
1152-
// segment of the object should be OK. They may only create problems via virtual method calls on `this`, but
1153-
// those methods are checked as part of the check for the class where they are defined.
1154-
if !isHotSegment then
1155-
for member <- klass.info.decls do
1156-
if !member.isType && !member.isConstructor && member.hasSource && !member.is(Flags.Deferred) then
1157-
if member.is(Flags.Method, butNot = Flags.Accessor) then
1158-
withTrace(Trace.empty) {
1159-
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty))
1160-
val res = warm.call(member, args, receiver = warm.klass.typeRef, superType = NoType)
1161-
res.promote("Cannot prove that the return value of " + member.show + " is hot. Found = " + res.show + ". ")
1162-
}
1163-
else
1164-
withTrace(Trace.empty) {
1165-
val res = warm.select(member, receiver = warm.klass.typeRef)
1166-
res.promote("Cannot prove that the field " + member.show + " is hot. Found = " + res.show + ". ")
1167-
}
1168-
end for
1169-
end for
1148+
// check invariant: subClassSegmentHot => isHotSegment
1149+
if subClassSegmentHot && !isHotSegment then
1150+
report.error("[Internal error] Expect current segment to hot in promotion, current klass = " + klass.show +
1151+
", subclass = " + subClass.show + Trace.show, Trace.position)
1152+
1153+
// If the outer and parameters of a class are all hot, then accessing fields and methods of the current
1154+
// segment of the object should be OK. They may only create problems via virtual method calls on `this`, but
1155+
// those methods are checked as part of the check for the class where they are defined.
1156+
if !isHotSegment then
1157+
for member <- klass.info.decls do
1158+
if !member.isType && !member.isConstructor && member.hasSource && !member.is(Flags.Deferred) then
1159+
given Trace = Trace.empty
1160+
if member.is(Flags.Method, butNot = Flags.Accessor) then
1161+
val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty))
1162+
val res = warm.call(member, args, receiver = warm.klass.typeRef, superType = NoType)
1163+
withTrace(trace.add(member.defTree)) {
1164+
res.promote("Cannot prove that the return value of " + member.show + " is hot. Found = " + res.show + ". ")
1165+
}
1166+
else
1167+
val res = warm.select(member, receiver = warm.klass.typeRef)
1168+
withTrace(trace.add(member.defTree)) {
1169+
res.promote("Cannot prove that the field " + member.show + " is hot. Found = " + res.show + ". ")
1170+
}
1171+
end for
1172+
1173+
// Promote parents
1174+
//
1175+
// Note that a parameterized trait may only get parameters from the class that extends the trait.
1176+
// A trait may not supply constructor arguments to another trait.
1177+
if !klass.is(Flags.Trait) then
1178+
for parent <- klass.parentSyms if parent.hasSource do doPromote(parent.asClass, klass, isHotSegment)
1179+
// We still need to handle indirectly extended traits via traits, which are not in the parent list.
1180+
val superCls = klass.superClass
1181+
val mixins = klass.baseClasses.tail.takeWhile(_ != superCls)
1182+
for mixin <- mixins if mixin.hasSource do doPromote(mixin.asClass, klass, isHotSegment)
1183+
end doPromote
1184+
1185+
val errors = Reporter.stopEarly {
1186+
doPromote(warm.klass, subClass = warm.klass, subClassSegmentHot = false)
11701187
}
11711188

11721189
if errors.isEmpty then Nil

0 commit comments

Comments
 (0)