Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f162db3

Browse files
dcharkescommit-bot@chromium.org
authored and
commit-bot@chromium.org
committedApr 12, 2021
[analyzer/ffi] Support Unions
This CL does not add unions to `dart:ffi` yet, only to the mock SDK in the analyzer for testing the analyzer. This means we can review and land it separately. Also, this CL fixes some tests to not use nullable fields in Structs. Bug: #38491 Change-Id: Ia972f31475859aa57e012adf39b636919995b183 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194788 Commit-Queue: Daco Harkes <dacoharkes@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
1 parent ac2412c commit f162db3

11 files changed

+259
-84
lines changed
 

‎pkg/analyzer/lib/src/dart/error/ffi_code.dart

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class FfiCode extends AnalyzerErrorCode {
5252
*/
5353
static const FfiCode FIELD_IN_STRUCT_WITH_INITIALIZER = FfiCode(
5454
name: 'FIELD_IN_STRUCT_WITH_INITIALIZER',
55-
message: "Fields in subclasses of 'Struct' can't have initializers.",
55+
message:
56+
"Fields in subclasses of 'Struct' and 'Union' can't have initializers.",
5657
correction:
5758
"Try removing the initializer and marking the field as external.");
5859

@@ -61,7 +62,8 @@ class FfiCode extends AnalyzerErrorCode {
6162
*/
6263
static const FfiCode FIELD_INITIALIZER_IN_STRUCT = FfiCode(
6364
name: 'FIELD_INITIALIZER_IN_STRUCT',
64-
message: "Constructors in subclasses of 'Struct' can't have field "
65+
message:
66+
"Constructors in subclasses of 'Struct' and 'Union' can't have field "
6567
"initializers.",
6668
correction: "Try removing the field initializer and marking the field as"
6769
" external.");
@@ -72,7 +74,8 @@ class FfiCode extends AnalyzerErrorCode {
7274
*/
7375
static const FfiCode GENERIC_STRUCT_SUBCLASS = FfiCode(
7476
name: 'GENERIC_STRUCT_SUBCLASS',
75-
message: "The class '{0}' can't extend 'Struct' because it is generic.",
77+
message:
78+
"The class '{0}' can't extend 'Struct' or 'Union' because it is generic.",
7679
correction: "Try removing the type parameters from '{0}'.");
7780

7881
/**
@@ -95,10 +98,10 @@ class FfiCode extends AnalyzerErrorCode {
9598
message:
9699
"Fields in struct classes can't have the type '{0}'. They can only "
97100
"be declared as 'int', 'double', 'Array', 'Pointer', or subtype of "
98-
"'Struct'.",
101+
"'Struct' or 'Union'.",
99102
correction:
100103
"Try using 'int', 'double', 'Array', 'Pointer', or subtype of "
101-
"'Struct'.");
104+
"'Struct' or 'Union'.");
102105

103106
/**
104107
* No parameters.
@@ -202,9 +205,9 @@ class FfiCode extends AnalyzerErrorCode {
202205
message:
203206
"Type arguments to '{0}' can't have the type '{1}'. They can only "
204207
"be declared as native integer, 'Float', 'Double', 'Pointer', or "
205-
"subtype of Struct'.",
208+
"subtype of 'Struct' or 'Union'.",
206209
correction: "Try using a native integer, 'Float', 'Double', 'Pointer', "
207-
"or subtype of 'Struct'.");
210+
"or subtype of 'Struct' or 'Union'.");
208211

209212
/**
210213
* No parameters.
@@ -252,7 +255,7 @@ class FfiCode extends AnalyzerErrorCode {
252255
static const FfiCode SUBTYPE_OF_FFI_CLASS_IN_EXTENDS = FfiCode(
253256
name: 'SUBTYPE_OF_FFI_CLASS',
254257
message: "The class '{0}' can't extend '{1}'.",
255-
correction: "Try extending 'Struct'.",
258+
correction: "Try extending 'Struct' or 'Union'.",
256259
uniqueName: 'SUBTYPE_OF_FFI_CLASS_IN_EXTENDS',
257260
);
258261

@@ -264,7 +267,7 @@ class FfiCode extends AnalyzerErrorCode {
264267
static const FfiCode SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS = FfiCode(
265268
name: 'SUBTYPE_OF_FFI_CLASS',
266269
message: "The class '{0}' can't implement '{1}'.",
267-
correction: "Try extending 'Struct'.",
270+
correction: "Try extending 'Struct' or 'Union'.",
268271
uniqueName: 'SUBTYPE_OF_FFI_CLASS_IN_IMPLEMENTS',
269272
);
270273

@@ -276,7 +279,7 @@ class FfiCode extends AnalyzerErrorCode {
276279
static const FfiCode SUBTYPE_OF_FFI_CLASS_IN_WITH = FfiCode(
277280
name: 'SUBTYPE_OF_FFI_CLASS',
278281
message: "The class '{0}' can't mix in '{1}'.",
279-
correction: "Try extending 'Struct'.",
282+
correction: "Try extending 'Struct' or 'Union'.",
280283
uniqueName: 'SUBTYPE_OF_FFI_CLASS_IN_WITH',
281284
);
282285

@@ -288,8 +291,8 @@ class FfiCode extends AnalyzerErrorCode {
288291
static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS = FfiCode(
289292
name: 'SUBTYPE_OF_STRUCT_CLASS',
290293
message: "The class '{0}' can't extend '{1}' because '{1}' is a subtype of "
291-
"'Struct'.",
292-
correction: "Try extending 'Struct' directly.",
294+
"'Struct' or 'Union'.",
295+
correction: "Try extending 'Struct' or 'Union' directly.",
293296
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS',
294297
);
295298

@@ -302,8 +305,8 @@ class FfiCode extends AnalyzerErrorCode {
302305
name: 'SUBTYPE_OF_STRUCT_CLASS',
303306
message:
304307
"The class '{0}' can't implement '{1}' because '{1}' is a subtype of "
305-
"'Struct'.",
306-
correction: "Try extending 'Struct' directly.",
308+
"'Struct' or 'Union'.",
309+
correction: "Try extending 'Struct' or 'Union' directly.",
307310
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_IMPLEMENTS',
308311
);
309312

@@ -315,8 +318,8 @@ class FfiCode extends AnalyzerErrorCode {
315318
static const FfiCode SUBTYPE_OF_STRUCT_CLASS_IN_WITH = FfiCode(
316319
name: 'SUBTYPE_OF_STRUCT_CLASS',
317320
message: "The class '{0}' can't mix in '{1}' because '{1}' is a subtype of "
318-
"'Struct'.",
319-
correction: "Try extending 'Struct' directly.",
321+
"'Struct' or 'Union'.",
322+
correction: "Try extending 'Struct' or 'Union' directly.",
320323
uniqueName: 'SUBTYPE_OF_STRUCT_CLASS_IN_WITH',
321324
);
322325

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

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
4141

4242
static const _structClassName = 'Struct';
4343

44+
static const _unionClassName = 'Union';
45+
4446
/// The type system used to check types.
4547
final TypeSystemImpl typeSystem;
4648

@@ -49,41 +51,43 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
4951

5052
/// A flag indicating whether we are currently visiting inside a subclass of
5153
/// `Struct`.
52-
bool inStruct = false;
54+
bool inCompound = false;
5355

54-
/// Subclass of `Struct` we are currently visiting, or `null`.
55-
ClassDeclaration? struct;
56+
/// Subclass of `Struct` or `Union` we are currently visiting, or `null`.
57+
ClassDeclaration? compound;
5658

5759
/// Initialize a newly created verifier.
5860
FfiVerifier(this.typeSystem, this._errorReporter);
5961

6062
@override
6163
void visitClassDeclaration(ClassDeclaration node) {
62-
inStruct = false;
63-
struct = null;
64+
inCompound = false;
65+
compound = null;
6466
// Only the Allocator, Opaque and Struct class may be extended.
6567
var extendsClause = node.extendsClause;
6668
if (extendsClause != null) {
6769
final TypeName superclass = extendsClause.superclass;
6870
final ffiClass = superclass.ffiClass;
6971
if (ffiClass != null) {
7072
final className = ffiClass.name;
71-
if (className == _structClassName) {
72-
inStruct = true;
73-
struct = node;
73+
if (className == _structClassName || className == _unionClassName) {
74+
inCompound = true;
75+
compound = node;
7476
if (node.declaredElement!.isEmptyStruct) {
7577
_errorReporter
7678
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [node.name]);
7779
}
78-
_validatePackedAnnotation(node.metadata);
80+
if (className == _structClassName) {
81+
_validatePackedAnnotation(node.metadata);
82+
}
7983
} else if (className != _allocatorClassName &&
8084
className != _opaqueClassName) {
8185
_errorReporter.reportErrorForNode(
8286
FfiCode.SUBTYPE_OF_FFI_CLASS_IN_EXTENDS,
8387
superclass.name,
8488
[node.name.name, superclass.name.name]);
8589
}
86-
} else if (superclass.isStructSubtype) {
90+
} else if (superclass.isCompoundSubtype) {
8791
_errorReporter.reportErrorForNode(
8892
FfiCode.SUBTYPE_OF_STRUCT_CLASS_IN_EXTENDS,
8993
superclass,
@@ -101,7 +105,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
101105
if (typename.ffiClass != null) {
102106
_errorReporter.reportErrorForNode(
103107
subtypeOfFfiCode, typename, [node.name, typename.name]);
104-
} else if (typename.isStructSubtype) {
108+
} else if (typename.isCompoundSubtype) {
105109
_errorReporter.reportErrorForNode(
106110
subtypeOfStructCode, typename, [node.name, typename.name]);
107111
}
@@ -122,7 +126,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
122126
}
123127
}
124128

125-
if (inStruct && node.declaredElement!.typeParameters.isNotEmpty) {
129+
if (inCompound && node.declaredElement!.typeParameters.isNotEmpty) {
126130
_errorReporter.reportErrorForNode(
127131
FfiCode.GENERIC_STRUCT_SUBCLASS, node.name, [node.name]);
128132
}
@@ -131,7 +135,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
131135

132136
@override
133137
void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
134-
if (inStruct) {
138+
if (inCompound) {
135139
_errorReporter.reportErrorForNode(
136140
FfiCode.FIELD_INITIALIZER_IN_STRUCT, node);
137141
}
@@ -140,8 +144,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
140144

141145
@override
142146
void visitFieldDeclaration(FieldDeclaration node) {
143-
if (inStruct) {
144-
_validateFieldsInStruct(node);
147+
if (inCompound) {
148+
_validateFieldsInCompound(node);
145149
}
146150
super.visitFieldDeclaration(node);
147151
}
@@ -248,7 +252,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
248252
case _PrimitiveDartType.none:
249253
break;
250254
}
251-
if (nativeType.isStructSubtype) {
255+
if (nativeType.isCompoundSubtype) {
252256
return true;
253257
}
254258
if (nativeType.isPointer) {
@@ -311,10 +315,10 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
311315
final nativeArgumentType = nativeType.typeArguments.single;
312316
return _isValidFfiNativeType(nativeArgumentType,
313317
allowVoid: true, allowEmptyStruct: true, allowHandle: true) ||
314-
nativeArgumentType.isStructSubtype ||
318+
nativeArgumentType.isCompoundSubtype ||
315319
nativeArgumentType.isNativeType;
316320
}
317-
if (nativeType.isStructSubtype) {
321+
if (nativeType.isCompoundSubtype) {
318322
if (!allowEmptyStruct) {
319323
if (nativeType.element.isEmptyStruct) {
320324
// TODO(dartbug.com/36780): This results in an error message not
@@ -548,8 +552,8 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
548552
}
549553

550554
/// Validate that the fields declared by the given [node] meet the
551-
/// requirements for fields within a struct class.
552-
void _validateFieldsInStruct(FieldDeclaration node) {
555+
/// requirements for fields within a struct or union class.
556+
void _validateFieldsInCompound(FieldDeclaration node) {
553557
if (node.isStatic) {
554558
return;
555559
}
@@ -576,18 +580,18 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
576580
final arrayDimensions = declaredType.arrayDimensions;
577581
_validateSizeOfAnnotation(fieldType, annotations, arrayDimensions);
578582
final arrayElement = declaredType.arrayElementType;
579-
if (arrayElement.isStructSubtype) {
583+
if (arrayElement.isCompoundSubtype) {
580584
final elementClass = (arrayElement as InterfaceType).element;
581-
_validatePackingNesting(struct!.declaredElement!, elementClass,
585+
_validatePackingNesting(compound!.declaredElement!, elementClass,
582586
errorNode: fieldType);
583587
}
584-
} else if (declaredType.isStructSubtype) {
588+
} else if (declaredType.isCompoundSubtype) {
585589
final clazz = (declaredType as InterfaceType).element;
586590
if (clazz.isEmptyStruct) {
587591
_errorReporter
588592
.reportErrorForNode(FfiCode.EMPTY_STRUCT, node, [clazz.name]);
589593
}
590-
_validatePackingNesting(struct!.declaredElement!, clazz,
594+
_validatePackingNesting(compound!.declaredElement!, clazz,
591595
errorNode: fieldType);
592596
} else {
593597
_errorReporter.reportErrorForNode(FfiCode.INVALID_FIELD_TYPE_IN_STRUCT,
@@ -632,7 +636,7 @@ class FfiVerifier extends RecursiveAstVisitor<void> {
632636
if ((FT as FunctionType).returnType.isVoid ||
633637
R.isPointer ||
634638
R.isHandle ||
635-
R.isStructSubtype) {
639+
R.isCompoundSubtype) {
636640
if (argCount != 1) {
637641
_errorReporter.reportErrorForNode(
638642
FfiCode.INVALID_EXCEPTION_VALUE, node.argumentList.arguments[1]);
@@ -975,7 +979,7 @@ extension on ClassElement {
975979
return false;
976980
} else if (declaredType.isPointer) {
977981
return false;
978-
} else if (declaredType.isStructSubtype) {
982+
} else if (declaredType.isCompoundSubtype) {
979983
return false;
980984
} else if (declaredType.isArray) {
981985
return false;
@@ -1087,22 +1091,25 @@ extension on DartType {
10871091
return false;
10881092
}
10891093

1090-
bool get isStruct {
1094+
bool get isCompound {
10911095
final self = this;
10921096
if (self is InterfaceType) {
10931097
final element = self.element;
1094-
return element.name == FfiVerifier._structClassName && element.isFfiClass;
1098+
final name = element.name;
1099+
return (name == FfiVerifier._structClassName ||
1100+
name == FfiVerifier._unionClassName) &&
1101+
element.isFfiClass;
10951102
}
10961103
return false;
10971104
}
10981105

10991106
/// Returns `true` if this is a struct type, i.e. a subtype of `Struct`.
1100-
bool get isStructSubtype {
1107+
bool get isCompoundSubtype {
11011108
final self = this;
11021109
if (self is InterfaceType) {
11031110
final superType = self.element.supertype;
11041111
if (superType != null) {
1105-
return superType.isStruct;
1112+
return superType.isCompound;
11061113
}
11071114
}
11081115
return false;
@@ -1115,17 +1122,17 @@ extension on TypeName {
11151122
return name.staticElement.ffiClass;
11161123
}
11171124

1118-
/// Return `true` if this represents a subtype of `Struct`.
1119-
bool get isStructSubtype {
1125+
/// Return `true` if this represents a subtype of `Struct` or `Union`.
1126+
bool get isCompoundSubtype {
11201127
var element = name.staticElement;
11211128
if (element is ClassElement) {
1122-
bool isStruct(InterfaceType? type) {
1123-
return type != null && type.isStruct;
1129+
bool isCompound(InterfaceType? type) {
1130+
return type != null && type.isCompound;
11241131
}
11251132

1126-
return isStruct(element.supertype) ||
1127-
element.interfaces.any(isStruct) ||
1128-
element.mixins.any(isStruct);
1133+
return isCompound(element.supertype) ||
1134+
element.interfaces.any(isCompound) ||
1135+
element.mixins.any(isCompound);
11291136
}
11301137
return false;
11311138
}

‎pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,12 +670,18 @@ class Pointer<T extends NativeType> extends NativeType {
670670
[Object exceptionalReturn]) {}
671671
}
672672
673+
final Pointer<Never> nullptr = Pointer.fromAddress(0);
674+
673675
extension NativeFunctionPointer<NF extends Function>
674676
on Pointer<NativeFunction<NF>> {
675677
external DF asFunction<DF extends Function>();
676678
}
677679
678-
class Struct extends NativeType {}
680+
class _Compound extends NativeType {}
681+
682+
class Struct extends _Compound {}
683+
684+
class Union extends _Compound {}
679685
680686
class Packed {
681687
final int memberAlignment;

‎pkg/analyzer/test/src/diagnostics/field_in_struct_with_initializer_test.dart

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,21 @@ class FieldInStructWithInitializerTest extends PubPackageResolutionTest {
1919
await assertErrorsInCode(r'''
2020
import 'dart:ffi';
2121
class C extends Struct {
22-
Pointer? p = null;
22+
Pointer p = nullptr;
2323
}
2424
''', [
25-
error(FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER, 55, 1),
25+
error(FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER, 54, 1),
26+
]);
27+
}
28+
29+
test_instance_withInitializer2() async {
30+
await assertErrorsInCode(r'''
31+
import 'dart:ffi';
32+
class C extends Union {
33+
Pointer p = nullptr;
34+
}
35+
''', [
36+
error(FfiCode.FIELD_IN_STRUCT_WITH_INITIALIZER, 53, 1),
2637
]);
2738
}
2839

0 commit comments

Comments
 (0)
Please sign in to comment.