Skip to content

Commit 30d9bf2

Browse files
committed
Revert "Void is not required to be null anymore."
This reverts commit 6caf9ef. Commit breaks vm-kernel-linux-debug-x64-be. Triggers asserts like ../../runtime/vm/flow_graph_compiler_x64.cc: 658: error: expected: dst_type.IsMalformedOrMalbounded() || (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && !dst_type.IsVoidType()) Quick-check with python tools/test.py -cdartk -t120 --builder-tag no_ipv6 --vm-options --no-enable-malloc-hooks isolate BUG= Review-Url: https://codereview.chromium.org/2865603003 .
1 parent a59bff3 commit 30d9bf2

20 files changed

+108
-100
lines changed

CHANGELOG.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
## 1.24.0
22

33
### Language
4-
* During a dynamic type check, `void` is not required to be `null` anymore.
5-
In practice, this makes overriding `void` functions with non-`void` functions
6-
safer.
74

85
#### Strong Mode
96

pkg/compiler/lib/src/inferrer/type_graph_nodes.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1753,13 +1753,14 @@ TypeMask _narrowType(
17531753
{bool isNullable: true}) {
17541754
if (annotation.treatAsDynamic) return type;
17551755
if (annotation.isObject) return type;
1756-
if (annotation.isVoid) return type;
17571756
TypeMask otherType;
17581757
if (annotation.isTypedef || annotation.isFunctionType) {
17591758
otherType = closedWorld.commonMasks.functionType;
17601759
} else if (annotation.isTypeVariable) {
17611760
// TODO(ngeoffray): Narrow to bound.
17621761
return type;
1762+
} else if (annotation.isVoid) {
1763+
otherType = closedWorld.commonMasks.nullType;
17631764
} else {
17641765
ResolutionInterfaceType interfaceType = annotation;
17651766
otherType = new TypeMask.nonNullSubtype(interfaceType.element, closedWorld);

pkg/compiler/lib/src/inferrer/type_system.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ class TypeSystem {
282282
TypeInformation type, ResolutionDartType annotation,
283283
{bool isNullable: true}) {
284284
if (annotation.treatAsDynamic) return type;
285-
if (annotation.isVoid) return type;
285+
if (annotation.isVoid) return nullType;
286286
if (annotation.element == closedWorld.commonElements.objectClass &&
287287
isNullable) {
288288
return type;

pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class CheckedModeHelpers {
117117
/// All the checked mode helpers.
118118
static const List<CheckedModeHelper> helpers = const <CheckedModeHelper>[
119119
const MalformedCheckedModeHelper('checkMalformedType'),
120+
const CheckedModeHelper('voidTypeCheck'),
120121
const CheckedModeHelper('stringTypeCast'),
121122
const CheckedModeHelper('stringTypeCheck'),
122123
const CheckedModeHelper('doubleTypeCast'),
@@ -202,6 +203,12 @@ class CheckedModeHelpers {
202203
return 'checkMalformedType';
203204
}
204205

206+
if (type.isVoid) {
207+
assert(!typeCast); // Cannot cast to void.
208+
if (nativeCheckOnly) return null;
209+
return 'voidTypeCheck';
210+
}
211+
205212
if (type.isTypeVariable) {
206213
return typeCast
207214
? 'subtypeOfRuntimeTypeCast'

pkg/compiler/lib/src/js_backend/impact_transformer.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,6 @@ class CodegenImpactTransformer {
363363

364364
void onIsCheckForCodegen(DartType type, TransformedWorldImpact transformed) {
365365
if (type.isDynamic) return;
366-
if (type.isVoid) return;
367366
type = type.unaliased;
368367
_impacts.typeCheck.registerImpact(transformed, _elementEnvironment);
369368

pkg/compiler/lib/src/ssa/codegen.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2928,7 +2928,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
29282928
DartType type = node.typeExpression;
29292929
assert(!type.isTypedef);
29302930
assert(!type.isDynamic);
2931-
assert(!type.isVoid);
29322931
if (type.isFunctionType) {
29332932
// TODO(5022): We currently generate $isFunction checks for
29342933
// function types.

pkg/compiler/lib/src/ssa/nodes.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,9 +1342,8 @@ abstract class HInstruction implements Spannable {
13421342
assert(!type.isTypeVariable);
13431343
assert(type.treatAsRaw || type.isFunctionType);
13441344
if (type.isDynamic) return this;
1345-
if (type.isVoid) return this;
13461345
if (type == closedWorld.commonElements.objectType) return this;
1347-
if (type.isFunctionType || type.isMalformed) {
1346+
if (type.isVoid || type.isFunctionType || type.isMalformed) {
13481347
return new HTypeConversion(
13491348
type, kind, closedWorld.commonMasks.dynamicType, this);
13501349
}

runtime/vm/flow_graph_builder.cc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,9 +1259,8 @@ bool EffectGraphVisitor::CanSkipTypeCheck(TokenPosition token_pos,
12591259
return false;
12601260
}
12611261

1262-
// Any type is more specific than the dynamic type, the Object type, or void.
1263-
if (dst_type.IsDynamicType() || dst_type.IsObjectType() ||
1264-
dst_type.IsVoidType()) {
1262+
// Any type is more specific than the dynamic type and than the Object type.
1263+
if (dst_type.IsDynamicType() || dst_type.IsObjectType()) {
12651264
return true;
12661265
}
12671266

runtime/vm/flow_graph_compiler_arm.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
532532
Label* is_instance_lbl,
533533
Label* is_not_instance_lbl) {
534534
__ Comment("InlineInstanceof");
535+
if (type.IsVoidType()) {
536+
// A non-null value is returned from a void function, which will result in a
537+
// type error. A null value is handled prior to executing this inline code.
538+
return SubtypeTestCache::null();
539+
}
535540
if (type.IsInstantiated()) {
536541
const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
537542
// A class equality check is only applicable with a dst type (not a
@@ -574,7 +579,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
574579
const AbstractType& type,
575580
LocationSummary* locs) {
576581
ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded());
577-
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
582+
ASSERT(!type.IsObjectType() && !type.IsDynamicType());
578583
const Register kInstantiatorTypeArgumentsReg = R2;
579584
const Register kFunctionTypeArgumentsReg = R1;
580585
__ PushList((1 << kInstantiatorTypeArgumentsReg) |
@@ -653,8 +658,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
653658
ASSERT(dst_type.IsFinalized());
654659
// Assignable check is skipped in FlowGraphBuilder, not here.
655660
ASSERT(dst_type.IsMalformedOrMalbounded() ||
656-
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
657-
!dst_type.IsVoidType()));
661+
(!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
658662
const Register kInstantiatorTypeArgumentsReg = R2;
659663
const Register kFunctionTypeArgumentsReg = R1;
660664
__ PushList((1 << kInstantiatorTypeArgumentsReg) |

runtime/vm/flow_graph_compiler_arm64.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
524524
Label* is_instance_lbl,
525525
Label* is_not_instance_lbl) {
526526
__ Comment("InlineInstanceof");
527+
if (type.IsVoidType()) {
528+
// A non-null value is returned from a void function, which will result in a
529+
// type error. A null value is handled prior to executing this inline code.
530+
return SubtypeTestCache::null();
531+
}
527532
if (type.IsInstantiated()) {
528533
const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
529534
// A class equality check is only applicable with a dst type (not a
@@ -566,7 +571,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
566571
const AbstractType& type,
567572
LocationSummary* locs) {
568573
ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded());
569-
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
574+
ASSERT(!type.IsObjectType() && !type.IsDynamicType());
570575
const Register kInstantiatorTypeArgumentsReg = R1;
571576
const Register kFunctionTypeArgumentsReg = R2;
572577
__ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg);
@@ -645,8 +650,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
645650
ASSERT(dst_type.IsFinalized());
646651
// Assignable check is skipped in FlowGraphBuilder, not here.
647652
ASSERT(dst_type.IsMalformedOrMalbounded() ||
648-
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
649-
!dst_type.IsVoidType()));
653+
(!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
650654
const Register kInstantiatorTypeArgumentsReg = R1;
651655
const Register kFunctionTypeArgumentsReg = R2;
652656
__ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg);

runtime/vm/flow_graph_compiler_ia32.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
538538
Label* is_instance_lbl,
539539
Label* is_not_instance_lbl) {
540540
__ Comment("InlineInstanceof");
541+
if (type.IsVoidType()) {
542+
// A non-null value is returned from a void function, which will result in a
543+
// type error. A null value is handled prior to executing this inline code.
544+
return SubtypeTestCache::null();
545+
}
541546
if (type.IsInstantiated()) {
542547
const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
543548
// A class equality check is only applicable with a dst type (not a
@@ -580,7 +585,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
580585
const AbstractType& type,
581586
LocationSummary* locs) {
582587
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
583-
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
588+
ASSERT(!type.IsObjectType() && !type.IsDynamicType());
584589

585590
__ pushl(EDX); // Store instantiator type arguments.
586591
__ pushl(ECX); // Store function type arguments.
@@ -661,8 +666,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
661666
ASSERT(dst_type.IsFinalized());
662667
// Assignable check is skipped in FlowGraphBuilder, not here.
663668
ASSERT(dst_type.IsMalformedOrMalbounded() ||
664-
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
665-
!dst_type.IsVoidType()));
669+
(!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
666670
__ pushl(EDX); // Store instantiator type arguments.
667671
__ pushl(ECX); // Store function type arguments.
668672
// A null object is always assignable and is returned as result.

runtime/vm/flow_graph_compiler_mips.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
520520
Label* is_instance_lbl,
521521
Label* is_not_instance_lbl) {
522522
__ Comment("InlineInstanceof");
523+
if (type.IsVoidType()) {
524+
// A non-null value is returned from a void function, which will result in a
525+
// type error. A null value is handled prior to executing this inline code.
526+
return SubtypeTestCache::null();
527+
}
523528
if (type.IsInstantiated()) {
524529
const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
525530
// A class equality check is only applicable with a dst type (not a
@@ -562,7 +567,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
562567
const AbstractType& type,
563568
LocationSummary* locs) {
564569
ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded());
565-
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
570+
ASSERT(!type.IsObjectType() && !type.IsDynamicType());
566571

567572
// Preserve instantiator type arguments (A1) and function type arguments (A2).
568573
__ addiu(SP, SP, Immediate(-2 * kWordSize));
@@ -648,8 +653,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
648653
ASSERT(dst_type.IsFinalized());
649654
// Assignable check is skipped in FlowGraphBuilder, not here.
650655
ASSERT(dst_type.IsMalformedOrMalbounded() ||
651-
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
652-
!dst_type.IsVoidType()));
656+
(!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
653657

654658
// Preserve instantiator type arguments (A1) and function type arguments (A2).
655659
__ addiu(SP, SP, Immediate(-2 * kWordSize));

runtime/vm/flow_graph_compiler_x64.cc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof(
533533
Label* is_instance_lbl,
534534
Label* is_not_instance_lbl) {
535535
__ Comment("InlineInstanceof");
536+
if (type.IsVoidType()) {
537+
// A non-null value is returned from a void function, which will result in a
538+
// type error. A null value is handled prior to executing this inline code.
539+
return SubtypeTestCache::null();
540+
}
536541
if (type.IsInstantiated()) {
537542
const Class& type_class = Class::ZoneHandle(zone(), type.type_class());
538543
// A class equality check is only applicable with a dst type (not a
@@ -575,7 +580,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
575580
const AbstractType& type,
576581
LocationSummary* locs) {
577582
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
578-
ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType());
583+
ASSERT(!type.IsObjectType() && !type.IsDynamicType());
579584

580585
__ pushq(RDX); // Store instantiator type arguments.
581586
__ pushq(RCX); // Store function type arguments.
@@ -584,8 +589,8 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos,
584589
// If type is instantiated and non-parameterized, we can inline code
585590
// checking whether the tested instance is a Smi.
586591
if (type.IsInstantiated()) {
587-
// A null object is only an instance of Null, Object, void and dynamic.
588-
// Object void and dynamic have already been checked above (if the type is
592+
// A null object is only an instance of Null, Object, and dynamic.
593+
// Object and dynamic have already been checked above (if the type is
589594
// instantiated). So we can return false here if the instance is null,
590595
// unless the type is Null (and if the type is instantiated).
591596
// We can only inline this null check if the type is instantiated at compile
@@ -654,8 +659,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
654659
ASSERT(dst_type.IsFinalized());
655660
// Assignable check is skipped in FlowGraphBuilder, not here.
656661
ASSERT(dst_type.IsMalformedOrMalbounded() ||
657-
(!dst_type.IsDynamicType() && !dst_type.IsObjectType() &&
658-
!dst_type.IsVoidType()));
662+
(!dst_type.IsDynamicType() && !dst_type.IsObjectType()));
659663
__ pushq(RDX); // Store instantiator type arguments.
660664
__ pushq(RCX); // Store function type arguments.
661665
// A null object is always assignable and is returned as result.

runtime/vm/flow_graph_type_propagator.cc

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ intptr_t CompileType::ToNullableCid() {
597597
} else if (type_->IsMalformed()) {
598598
cid_ = kDynamicCid;
599599
} else if (type_->IsVoidType()) {
600-
cid_ = kDynamicCid;
600+
cid_ = kNullCid;
601601
} else if (type_->IsFunctionType() || type_->IsDartFunctionType()) {
602602
cid_ = kClosureCid;
603603
} else if (type_->HasResolvedTypeClass()) {
@@ -684,7 +684,7 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type,
684684
return false;
685685
}
686686

687-
if (type.IsDynamicType() || type.IsObjectType() || type.IsVoidType()) {
687+
if (type.IsDynamicType() || type.IsObjectType()) {
688688
*is_instance = true;
689689
return true;
690690
}
@@ -696,6 +696,11 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type,
696696
// Consider the compile type of the value.
697697
const AbstractType& compile_type = *ToAbstractType();
698698

699+
// The compile-type of a value should never be void. The result of a void
700+
// function must always be null, which was checked to be null at the return
701+
// statement inside the function.
702+
ASSERT(!compile_type.IsVoidType());
703+
699704
if (compile_type.IsMalformedOrMalbounded()) {
700705
return false;
701706
}
@@ -710,6 +715,12 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type,
710715
return true;
711716
}
712717

718+
// A non-null value is not an instance of void.
719+
if (type.IsVoidType()) {
720+
*is_instance = IsNull();
721+
return HasDecidableNullability();
722+
}
723+
713724
// If the value can be null then we can't eliminate the
714725
// check unless null is allowed.
715726
if (is_nullable_ && !is_nullable) {
@@ -726,6 +737,11 @@ bool CompileType::IsMoreSpecificThan(const AbstractType& other) {
726737
return false;
727738
}
728739

740+
if (other.IsVoidType()) {
741+
// The only value assignable to void is null.
742+
return IsNull();
743+
}
744+
729745
return ToAbstractType()->IsMoreSpecificThan(other, NULL, NULL, Heap::kOld);
730746
}
731747

@@ -927,6 +943,11 @@ CompileType AssertAssignableInstr::ComputeType() const {
927943
return *value_type;
928944
}
929945

946+
if (dst_type().IsVoidType()) {
947+
// The only value assignable to void is null.
948+
return CompileType::Null();
949+
}
950+
930951
return CompileType::Create(value_type->ToCid(), dst_type());
931952
}
932953

@@ -1017,9 +1038,13 @@ CompileType StaticCallInstr::ComputeType() const {
10171038
}
10181039

10191040
if (Isolate::Current()->type_checks()) {
1041+
// Void functions are known to return null, which is checked at the return
1042+
// from the function.
10201043
const AbstractType& result_type =
10211044
AbstractType::ZoneHandle(function().result_type());
1022-
return CompileType::FromAbstractType(result_type);
1045+
return CompileType::FromAbstractType(
1046+
result_type.IsVoidType() ? AbstractType::ZoneHandle(Type::NullType())
1047+
: result_type);
10231048
}
10241049

10251050
return CompileType::Dynamic();

runtime/vm/object.cc

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3820,6 +3820,7 @@ bool Class::TypeTestNonRecursive(const Class& cls,
38203820
Zone* zone = Thread::Current()->zone();
38213821
Class& thsi = Class::Handle(zone, cls.raw());
38223822
while (true) {
3823+
ASSERT(!thsi.IsVoidClass());
38233824
// Check for DynamicType.
38243825
// Each occurrence of DynamicType in type T is interpreted as the dynamic
38253826
// type, a supertype of all types.
@@ -3838,15 +3839,10 @@ bool Class::TypeTestNonRecursive(const Class& cls,
38383839
return test_kind == Class::kIsSubtypeOf;
38393840
}
38403841
// Check for ObjectType. Any type that is not NullType or DynamicType
3841-
// (already checked above), is more specific than ObjectType/VoidType.
3842-
if (other.IsObjectClass() || other.IsVoidClass()) {
3842+
// (already checked above), is more specific than ObjectType.
3843+
if (other.IsObjectClass()) {
38433844
return true;
38443845
}
3845-
// If other is neither Object, dynamic or void, then ObjectType/VoidType
3846-
// can't be a subtype of other.
3847-
if (thsi.IsObjectClass() || thsi.IsVoidClass()) {
3848-
return false;
3849-
}
38503846
// Check for reflexivity.
38513847
if (thsi.raw() == other.raw()) {
38523848
const intptr_t num_type_params = thsi.NumTypeParameters();
@@ -15834,7 +15830,7 @@ bool Instance::IsInstanceOf(
1583415830
ASSERT(!other.IsMalformed());
1583515831
ASSERT(!other.IsMalbounded());
1583615832
if (other.IsVoidType()) {
15837-
return true;
15833+
return false;
1583815834
}
1583915835
Zone* zone = Thread::Current()->zone();
1584015836
const Class& cls = Class::Handle(zone, clazz());
@@ -16652,12 +16648,9 @@ bool AbstractType::TypeTest(TypeTestKind test_kind,
1665216648
return false;
1665316649
}
1665416650
// Any type is a subtype of (and is more specific than) Object and dynamic.
16655-
// As of Dart 1.24, void is dynamically treated like Object (except when
16656-
// comparing function-types).
1665716651
// As of Dart 1.5, the Null type is a subtype of (and is more specific than)
1665816652
// any type.
16659-
if (other.IsObjectType() || other.IsDynamicType() || other.IsVoidType() ||
16660-
IsNullType()) {
16653+
if (other.IsObjectType() || other.IsDynamicType() || IsNullType()) {
1666116654
return true;
1666216655
}
1666316656
Zone* zone = Thread::Current()->zone();

0 commit comments

Comments
 (0)