[vm,aot] Fix unboxing info (including calling conventions) on unreachable fields

For some reason unboxing information was not generated for unreachable
fields. However, unreachable field can be used as an interface target,
and unboxing info contains a flag indicating if register calling
convention should be used which affects how the call via this field
as an interface target is going to be performed.
So it is incorrect to omit such unboxing info and this change fixes
this bug.

TEST=vm/dart/regress_b_450678575_test

Bug: b/450678575
Change-Id: Ic1f299d9c0b09005c8bbfd95d1a409c113630263
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/454681
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Alexander Markov
2025-10-14 13:35:02 -07:00
committed by Commit Queue
parent 631fb95ef1
commit 72bd9baa54
3 changed files with 80 additions and 16 deletions
@@ -731,24 +731,22 @@ class AnnotateKernel extends RecursiveVisitor {
_setUnreachable(member);
}
if (member is! Field) {
final unboxingInfoMetadata = _unboxingInfo.getUnboxingInfoOfMember(
member,
);
if (unboxingInfoMetadata != null) {
// Check for partitions that only have abstract methods should be marked as boxed.
if (unboxingInfoMetadata.returnInfo == UnboxingType.kUnknown) {
unboxingInfoMetadata.returnInfo = UnboxingType.kBoxed;
}
for (int i = 0; i < unboxingInfoMetadata.argsInfo.length; i++) {
if (unboxingInfoMetadata.argsInfo[i] == UnboxingType.kUnknown) {
unboxingInfoMetadata.argsInfo[i] = UnboxingType.kBoxed;
}
}
if (!unboxingInfoMetadata.isTrivial) {
_unboxingInfoMetadata.mapping[member] = unboxingInfoMetadata;
final unboxingInfoMetadata = _unboxingInfo.getUnboxingInfoOfMember(
member,
);
if (unboxingInfoMetadata != null) {
// Check for partitions that only have abstract methods should be marked as boxed.
if (unboxingInfoMetadata.returnInfo == UnboxingType.kUnknown) {
unboxingInfoMetadata.returnInfo = UnboxingType.kBoxed;
}
for (int i = 0; i < unboxingInfoMetadata.argsInfo.length; i++) {
if (unboxingInfoMetadata.argsInfo[i] == UnboxingType.kUnknown) {
unboxingInfoMetadata.argsInfo[i] = UnboxingType.kBoxed;
}
}
if (!unboxingInfoMetadata.isTrivial) {
_unboxingInfoMetadata.mapping[member] = unboxingInfoMetadata;
}
}
}
@@ -6,6 +6,7 @@ abstract class Foo extends core::Object {
[@vm.unreachable.metadata=]
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]
[@vm.unboxing-info.metadata=[!regcc]]
@#C3
late final field core::int hashCode = throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
}
@@ -0,0 +1,65 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Regression test for b/450678575.
//
// Verifies that compiler uses correct calling conventions (!regcc)
// when using unreachable field as an interface target.
import 'package:expect/expect.dart';
bool opaqueTrue = int.parse('1') == 1;
int listener = -1;
abstract class ChangeNotifier {
void addListener();
}
class Notifier1 implements ChangeNotifier {
@pragma('vm:never-inline')
void addListener() {
listener = 1;
}
}
class Notifier2 implements ChangeNotifier {
@pragma('vm:never-inline')
void addListener() {
listener = 2;
}
}
abstract class M {
// This field is unreachable and only serves as
// an interface target.
@pragma("vm:entry-point")
final ChangeNotifier accessibilityFocus = Notifier2();
}
class B implements M {
@pragma("vm:entry-point")
final ChangeNotifier accessibilityFocus = Notifier1();
}
class C implements M {
@pragma("vm:entry-point")
final ChangeNotifier accessibilityFocus = Notifier2();
}
// Clobber values on top of the stack.
@pragma('vm:never-inline')
@pragma("vm:entry-point")
void boxed(int a, int b) {
print(a);
print(b);
}
M instance = opaqueTrue ? B() : C();
void main(List<String> args) {
boxed(1, 2);
instance.accessibilityFocus.addListener();
Expect.equals(1, listener);
}