@@ -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