@@ -1126,47 +1126,64 @@ object Semantic:
1126
1126
* If the object contains nested classes as members, the checker simply
1127
1127
* reports a warning to avoid expensive checks.
1128
1128
*
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.
1132
1129
*/
1133
1130
def tryPromote (msg : String ): Contextual [List [Error ]] = log(" promote " + warm.show + " , promoted = " + promoted, printer) {
1134
1131
val classRef = warm.klass.appliedRef
1135
1132
val hasInnerClass = classRef.memberClasses.filter(_.symbol.hasSource).nonEmpty
1136
1133
if hasInnerClass then
1137
1134
return PromoteError (msg + " Promotion cancelled as the value contains inner classes. " , trace.toVector) :: Nil
1138
1135
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 && {
1143
1141
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
+ }
1150
1147
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 )
1170
1187
}
1171
1188
1172
1189
if errors.isEmpty then Nil
0 commit comments