Skip to content

Commit 94c4d27

Browse files
Yusufihsangorgeldart-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[analyzer/ffi] Reject Struct-constrained mixin applications
Closes #63849 GitOrigin-RevId: 55f80e7 Change-Id: I99887afa22e833297ef639e8402be4645302cf99 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/525581 Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
1 parent 1289dcf commit 94c4d27

4 files changed

Lines changed: 93 additions & 1 deletion

File tree

BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ if (is_fuchsia) {
312312
"tests/ffi/static_checks/regress_60250_test.dart",
313313
"tests/ffi/static_checks/regress_62693_test.dart",
314314
"tests/ffi/static_checks/regress_62716_test.dart",
315+
"tests/ffi/static_checks/regress_63388_test.dart",
315316
"tests/ffi/static_checks/regress_flutter_188175_test.dart",
316317
"tests/ffi/static_checks/vmspecific_function_callbacks_negative_test.dart",
317318
"tests/ffi/static_checks/vmspecific_regress_38993_test.dart",

pkg/analyzer/lib/src/generated/ffi_verifier.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2799,7 +2799,7 @@ extension on NamedType {
27992799
/// Return `true` if this represents a subtype of `Struct` or `Union`.
28002800
bool get isCompoundSubtype {
28012801
var element = this.element;
2802-
if (element is ClassElement) {
2802+
if (element is InterfaceElement) {
28032803
return element.allSupertypes.any((e) => e.isCompound);
28042804
}
28052805
return false;

pkg/analyzer/test/src/diagnostics/subtype_of_struct_class_test.dart

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,25 @@ final class AbiSpecificInteger4 implements AbiSpecificInteger1 {
6363
''');
6464
}
6565

66+
test_implements_mixin_constrained_to_struct() async {
67+
await resolveTestCodeWithDiagnostics(r'''
68+
import 'dart:ffi';
69+
70+
base mixin HeaderFields on Struct {
71+
@Int32()
72+
external int fieldA;
73+
}
74+
75+
final class ExampleStruct implements HeaderFields {
76+
// ^^^^^^^^^^^^
77+
// [diag.baseClassImplementedOutsideOfLibrary] The class 'Struct' can't be implemented outside of its library because it's a base class.
78+
// [diag.subtypeOfStructClassInImplements] The class 'ExampleStruct' can't implement 'HeaderFields' because 'HeaderFields' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'.
79+
@override
80+
int fieldA = 0;
81+
}
82+
''');
83+
}
84+
6685
test_implements_struct() async {
6786
await resolveTestCodeWithDiagnostics(r'''
6887
import 'dart:ffi';
@@ -107,6 +126,26 @@ final class C implements S {}
107126

108127
@reflectiveTest
109128
class SubtypeOfStructClassInWithTest extends PubPackageResolutionTest {
129+
test_with_mixin_constrained_to_struct() async {
130+
await resolveTestCodeWithDiagnostics(r'''
131+
import 'dart:ffi';
132+
133+
base mixin HeaderFields on Struct {
134+
@Int32()
135+
external int fieldA;
136+
137+
external Pointer<Void> fieldB;
138+
}
139+
140+
final class ExampleStruct extends Struct with HeaderFields {
141+
// ^^^^^^^^^^^^
142+
// [diag.subtypeOfStructClassInWith] The class 'ExampleStruct' can't mix in 'HeaderFields' because 'HeaderFields' is a subtype of 'Struct', 'Union', or 'AbiSpecificInteger'.
143+
@Uint32()
144+
external int fieldC;
145+
}
146+
''');
147+
}
148+
110149
test_with_struct() async {
111150
await resolveTestCodeWithDiagnostics(r'''
112151
import 'dart:ffi';
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Regression test for https://github.com/dart-lang/sdk/issues/63388
6+
7+
import 'dart:ffi';
8+
9+
base mixin HeaderFields on Struct {
10+
@Int32()
11+
external int fieldA;
12+
13+
external Pointer<Void> fieldB;
14+
}
15+
16+
final class ExampleStruct extends Struct with HeaderFields {
17+
// ^^^^^^^^^^^^^
18+
// [cfe] Class 'Struct with HeaderFields' cannot be extended or implemented.
19+
// ^^^^^^^^^^^^
20+
// [analyzer] COMPILE_TIME_ERROR.SUBTYPE_OF_STRUCT_CLASS
21+
@Uint32()
22+
external int fieldC;
23+
}
24+
25+
// An unconstrained mixin cannot be applied to a struct either: the mixin
26+
// application class is itself treated as a struct, so the CFE rejects it.
27+
mixin ExtraGetters {
28+
int get extra => 4;
29+
}
30+
31+
final class GetterStruct extends Struct with ExtraGetters {
32+
// ^^^^^^^^^^^^
33+
// [cfe] Class 'Struct with ExtraGetters' cannot be extended or implemented.
34+
// [cfe] Struct 'Struct with ExtraGetters' is empty. Empty structs and unions are undefined behavior.
35+
@Int32()
36+
external int fieldD;
37+
}
38+
39+
// Sharing members through a plain interface is allowed.
40+
abstract interface class HasChecksum {
41+
int get checksum;
42+
}
43+
44+
final class ChecksumStruct extends Struct implements HasChecksum {
45+
@Int32()
46+
external int payload;
47+
48+
@override
49+
int get checksum => payload ^ 0xFF;
50+
}
51+
52+
void main() {}

0 commit comments

Comments
 (0)