From 30d9bf2ed3225f04c52d999a1a5c61e63a90578d Mon Sep 17 00:00:00 2001 From: Jens Johansen Date: Fri, 5 May 2017 08:56:22 +0200 Subject: [PATCH] Revert "Void is not required to be `null` anymore." This reverts commit 6caf9ef4439e20ea726d8a5383ed34d42b02c84b. 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 . --- CHANGELOG.md | 3 -- .../lib/src/inferrer/type_graph_nodes.dart | 3 +- .../lib/src/inferrer/type_system.dart | 2 +- .../src/js_backend/checked_mode_helpers.dart | 7 ++++ .../src/js_backend/impact_transformer.dart | 1 - pkg/compiler/lib/src/ssa/codegen.dart | 1 - pkg/compiler/lib/src/ssa/nodes.dart | 3 +- runtime/vm/flow_graph_builder.cc | 5 +-- runtime/vm/flow_graph_compiler_arm.cc | 10 +++-- runtime/vm/flow_graph_compiler_arm64.cc | 10 +++-- runtime/vm/flow_graph_compiler_ia32.cc | 10 +++-- runtime/vm/flow_graph_compiler_mips.cc | 10 +++-- runtime/vm/flow_graph_compiler_x64.cc | 14 ++++--- runtime/vm/flow_graph_type_propagator.cc | 31 ++++++++++++++-- runtime/vm/object.cc | 17 +++------ .../_internal/js_runtime/lib/js_helper.dart | 5 +++ tests/co19/co19-co19.status | 9 ----- tests/language/return_type_test.dart | 2 +- tests/language/void_check_test.dart | 37 ------------------- tests/language/void_type_test.dart | 28 +++++++++----- 20 files changed, 108 insertions(+), 100 deletions(-) delete mode 100644 tests/language/void_check_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 552d111aa21..fbf02d27bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,6 @@ ## 1.24.0 ### Language -* During a dynamic type check, `void` is not required to be `null` anymore. - In practice, this makes overriding `void` functions with non-`void` functions - safer. #### Strong Mode diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index 8856df0dd57..3a38bda26a2 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -1753,13 +1753,14 @@ TypeMask _narrowType( {bool isNullable: true}) { if (annotation.treatAsDynamic) return type; if (annotation.isObject) return type; - if (annotation.isVoid) return type; TypeMask otherType; if (annotation.isTypedef || annotation.isFunctionType) { otherType = closedWorld.commonMasks.functionType; } else if (annotation.isTypeVariable) { // TODO(ngeoffray): Narrow to bound. return type; + } else if (annotation.isVoid) { + otherType = closedWorld.commonMasks.nullType; } else { ResolutionInterfaceType interfaceType = annotation; otherType = new TypeMask.nonNullSubtype(interfaceType.element, closedWorld); diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart index 43f4074b1ad..d329b77f372 100644 --- a/pkg/compiler/lib/src/inferrer/type_system.dart +++ b/pkg/compiler/lib/src/inferrer/type_system.dart @@ -282,7 +282,7 @@ class TypeSystem { TypeInformation type, ResolutionDartType annotation, {bool isNullable: true}) { if (annotation.treatAsDynamic) return type; - if (annotation.isVoid) return type; + if (annotation.isVoid) return nullType; if (annotation.element == closedWorld.commonElements.objectClass && isNullable) { return type; diff --git a/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart b/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart index d239c1aa9b7..571e1061676 100644 --- a/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart +++ b/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart @@ -117,6 +117,7 @@ class CheckedModeHelpers { /// All the checked mode helpers. static const List helpers = const [ const MalformedCheckedModeHelper('checkMalformedType'), + const CheckedModeHelper('voidTypeCheck'), const CheckedModeHelper('stringTypeCast'), const CheckedModeHelper('stringTypeCheck'), const CheckedModeHelper('doubleTypeCast'), @@ -202,6 +203,12 @@ class CheckedModeHelpers { return 'checkMalformedType'; } + if (type.isVoid) { + assert(!typeCast); // Cannot cast to void. + if (nativeCheckOnly) return null; + return 'voidTypeCheck'; + } + if (type.isTypeVariable) { return typeCast ? 'subtypeOfRuntimeTypeCast' diff --git a/pkg/compiler/lib/src/js_backend/impact_transformer.dart b/pkg/compiler/lib/src/js_backend/impact_transformer.dart index 1cb567c6ec3..02393a1fee5 100644 --- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart +++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart @@ -363,7 +363,6 @@ class CodegenImpactTransformer { void onIsCheckForCodegen(DartType type, TransformedWorldImpact transformed) { if (type.isDynamic) return; - if (type.isVoid) return; type = type.unaliased; _impacts.typeCheck.registerImpact(transformed, _elementEnvironment); diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 5396b585fbe..20163ce776d 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -2928,7 +2928,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { DartType type = node.typeExpression; assert(!type.isTypedef); assert(!type.isDynamic); - assert(!type.isVoid); if (type.isFunctionType) { // TODO(5022): We currently generate $isFunction checks for // function types. diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index 7746f75e7f6..cfee3948cae 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1342,9 +1342,8 @@ abstract class HInstruction implements Spannable { assert(!type.isTypeVariable); assert(type.treatAsRaw || type.isFunctionType); if (type.isDynamic) return this; - if (type.isVoid) return this; if (type == closedWorld.commonElements.objectType) return this; - if (type.isFunctionType || type.isMalformed) { + if (type.isVoid || type.isFunctionType || type.isMalformed) { return new HTypeConversion( type, kind, closedWorld.commonMasks.dynamicType, this); } diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc index 946978ea8f4..53ae53b1100 100644 --- a/runtime/vm/flow_graph_builder.cc +++ b/runtime/vm/flow_graph_builder.cc @@ -1259,9 +1259,8 @@ bool EffectGraphVisitor::CanSkipTypeCheck(TokenPosition token_pos, return false; } - // Any type is more specific than the dynamic type, the Object type, or void. - if (dst_type.IsDynamicType() || dst_type.IsObjectType() || - dst_type.IsVoidType()) { + // Any type is more specific than the dynamic type and than the Object type. + if (dst_type.IsDynamicType() || dst_type.IsObjectType()) { return true; } diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc index 7e5daa0601f..b0b936d6fc1 100644 --- a/runtime/vm/flow_graph_compiler_arm.cc +++ b/runtime/vm/flow_graph_compiler_arm.cc @@ -532,6 +532,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( Label* is_instance_lbl, Label* is_not_instance_lbl) { __ Comment("InlineInstanceof"); + if (type.IsVoidType()) { + // A non-null value is returned from a void function, which will result in a + // type error. A null value is handled prior to executing this inline code. + return SubtypeTestCache::null(); + } if (type.IsInstantiated()) { const Class& type_class = Class::ZoneHandle(zone(), type.type_class()); // A class equality check is only applicable with a dst type (not a @@ -574,7 +579,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType()); const Register kInstantiatorTypeArgumentsReg = R2; const Register kFunctionTypeArgumentsReg = R1; __ PushList((1 << kInstantiatorTypeArgumentsReg) | @@ -653,8 +658,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, ASSERT(dst_type.IsFinalized()); // Assignable check is skipped in FlowGraphBuilder, not here. ASSERT(dst_type.IsMalformedOrMalbounded() || - (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && - !dst_type.IsVoidType())); + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); const Register kInstantiatorTypeArgumentsReg = R2; const Register kFunctionTypeArgumentsReg = R1; __ PushList((1 << kInstantiatorTypeArgumentsReg) | diff --git a/runtime/vm/flow_graph_compiler_arm64.cc b/runtime/vm/flow_graph_compiler_arm64.cc index 970e2b9772d..e51c0ab389f 100644 --- a/runtime/vm/flow_graph_compiler_arm64.cc +++ b/runtime/vm/flow_graph_compiler_arm64.cc @@ -524,6 +524,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( Label* is_instance_lbl, Label* is_not_instance_lbl) { __ Comment("InlineInstanceof"); + if (type.IsVoidType()) { + // A non-null value is returned from a void function, which will result in a + // type error. A null value is handled prior to executing this inline code. + return SubtypeTestCache::null(); + } if (type.IsInstantiated()) { const Class& type_class = Class::ZoneHandle(zone(), type.type_class()); // A class equality check is only applicable with a dst type (not a @@ -566,7 +571,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType()); const Register kInstantiatorTypeArgumentsReg = R1; const Register kFunctionTypeArgumentsReg = R2; __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); @@ -645,8 +650,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, ASSERT(dst_type.IsFinalized()); // Assignable check is skipped in FlowGraphBuilder, not here. ASSERT(dst_type.IsMalformedOrMalbounded() || - (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && - !dst_type.IsVoidType())); + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); const Register kInstantiatorTypeArgumentsReg = R1; const Register kFunctionTypeArgumentsReg = R2; __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); diff --git a/runtime/vm/flow_graph_compiler_ia32.cc b/runtime/vm/flow_graph_compiler_ia32.cc index 3751e539890..34d3452ec62 100644 --- a/runtime/vm/flow_graph_compiler_ia32.cc +++ b/runtime/vm/flow_graph_compiler_ia32.cc @@ -538,6 +538,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( Label* is_instance_lbl, Label* is_not_instance_lbl) { __ Comment("InlineInstanceof"); + if (type.IsVoidType()) { + // A non-null value is returned from a void function, which will result in a + // type error. A null value is handled prior to executing this inline code. + return SubtypeTestCache::null(); + } if (type.IsInstantiated()) { const Class& type_class = Class::ZoneHandle(zone(), type.type_class()); // A class equality check is only applicable with a dst type (not a @@ -580,7 +585,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType()); __ pushl(EDX); // Store instantiator type arguments. __ pushl(ECX); // Store function type arguments. @@ -661,8 +666,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, ASSERT(dst_type.IsFinalized()); // Assignable check is skipped in FlowGraphBuilder, not here. ASSERT(dst_type.IsMalformedOrMalbounded() || - (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && - !dst_type.IsVoidType())); + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); __ pushl(EDX); // Store instantiator type arguments. __ pushl(ECX); // Store function type arguments. // A null object is always assignable and is returned as result. diff --git a/runtime/vm/flow_graph_compiler_mips.cc b/runtime/vm/flow_graph_compiler_mips.cc index f9425c62306..5f7a62f6b3a 100644 --- a/runtime/vm/flow_graph_compiler_mips.cc +++ b/runtime/vm/flow_graph_compiler_mips.cc @@ -520,6 +520,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( Label* is_instance_lbl, Label* is_not_instance_lbl) { __ Comment("InlineInstanceof"); + if (type.IsVoidType()) { + // A non-null value is returned from a void function, which will result in a + // type error. A null value is handled prior to executing this inline code. + return SubtypeTestCache::null(); + } if (type.IsInstantiated()) { const Class& type_class = Class::ZoneHandle(zone(), type.type_class()); // A class equality check is only applicable with a dst type (not a @@ -562,7 +567,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType()); // Preserve instantiator type arguments (A1) and function type arguments (A2). __ addiu(SP, SP, Immediate(-2 * kWordSize)); @@ -648,8 +653,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, ASSERT(dst_type.IsFinalized()); // Assignable check is skipped in FlowGraphBuilder, not here. ASSERT(dst_type.IsMalformedOrMalbounded() || - (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && - !dst_type.IsVoidType())); + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); // Preserve instantiator type arguments (A1) and function type arguments (A2). __ addiu(SP, SP, Immediate(-2 * kWordSize)); diff --git a/runtime/vm/flow_graph_compiler_x64.cc b/runtime/vm/flow_graph_compiler_x64.cc index 3bb98222d23..59fed95ea73 100644 --- a/runtime/vm/flow_graph_compiler_x64.cc +++ b/runtime/vm/flow_graph_compiler_x64.cc @@ -533,6 +533,11 @@ RawSubtypeTestCache* FlowGraphCompiler::GenerateInlineInstanceof( Label* is_instance_lbl, Label* is_not_instance_lbl) { __ Comment("InlineInstanceof"); + if (type.IsVoidType()) { + // A non-null value is returned from a void function, which will result in a + // type error. A null value is handled prior to executing this inline code. + return SubtypeTestCache::null(); + } if (type.IsInstantiated()) { const Class& type_class = Class::ZoneHandle(zone(), type.type_class()); // A class equality check is only applicable with a dst type (not a @@ -575,7 +580,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType()); __ pushq(RDX); // Store instantiator type arguments. __ pushq(RCX); // Store function type arguments. @@ -584,8 +589,8 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, // If type is instantiated and non-parameterized, we can inline code // checking whether the tested instance is a Smi. if (type.IsInstantiated()) { - // A null object is only an instance of Null, Object, void and dynamic. - // Object void and dynamic have already been checked above (if the type is + // A null object is only an instance of Null, Object, and dynamic. + // Object and dynamic have already been checked above (if the type is // instantiated). So we can return false here if the instance is null, // unless the type is Null (and if the type is instantiated). // We can only inline this null check if the type is instantiated at compile @@ -654,8 +659,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos, ASSERT(dst_type.IsFinalized()); // Assignable check is skipped in FlowGraphBuilder, not here. ASSERT(dst_type.IsMalformedOrMalbounded() || - (!dst_type.IsDynamicType() && !dst_type.IsObjectType() && - !dst_type.IsVoidType())); + (!dst_type.IsDynamicType() && !dst_type.IsObjectType())); __ pushq(RDX); // Store instantiator type arguments. __ pushq(RCX); // Store function type arguments. // A null object is always assignable and is returned as result. diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc index be1366cdbf2..9617c03b348 100644 --- a/runtime/vm/flow_graph_type_propagator.cc +++ b/runtime/vm/flow_graph_type_propagator.cc @@ -597,7 +597,7 @@ intptr_t CompileType::ToNullableCid() { } else if (type_->IsMalformed()) { cid_ = kDynamicCid; } else if (type_->IsVoidType()) { - cid_ = kDynamicCid; + cid_ = kNullCid; } else if (type_->IsFunctionType() || type_->IsDartFunctionType()) { cid_ = kClosureCid; } else if (type_->HasResolvedTypeClass()) { @@ -684,7 +684,7 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type, return false; } - if (type.IsDynamicType() || type.IsObjectType() || type.IsVoidType()) { + if (type.IsDynamicType() || type.IsObjectType()) { *is_instance = true; return true; } @@ -696,6 +696,11 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type, // Consider the compile type of the value. const AbstractType& compile_type = *ToAbstractType(); + // The compile-type of a value should never be void. The result of a void + // function must always be null, which was checked to be null at the return + // statement inside the function. + ASSERT(!compile_type.IsVoidType()); + if (compile_type.IsMalformedOrMalbounded()) { return false; } @@ -710,6 +715,12 @@ bool CompileType::CanComputeIsInstanceOf(const AbstractType& type, return true; } + // A non-null value is not an instance of void. + if (type.IsVoidType()) { + *is_instance = IsNull(); + return HasDecidableNullability(); + } + // If the value can be null then we can't eliminate the // check unless null is allowed. if (is_nullable_ && !is_nullable) { @@ -726,6 +737,11 @@ bool CompileType::IsMoreSpecificThan(const AbstractType& other) { return false; } + if (other.IsVoidType()) { + // The only value assignable to void is null. + return IsNull(); + } + return ToAbstractType()->IsMoreSpecificThan(other, NULL, NULL, Heap::kOld); } @@ -927,6 +943,11 @@ CompileType AssertAssignableInstr::ComputeType() const { return *value_type; } + if (dst_type().IsVoidType()) { + // The only value assignable to void is null. + return CompileType::Null(); + } + return CompileType::Create(value_type->ToCid(), dst_type()); } @@ -1017,9 +1038,13 @@ CompileType StaticCallInstr::ComputeType() const { } if (Isolate::Current()->type_checks()) { + // Void functions are known to return null, which is checked at the return + // from the function. const AbstractType& result_type = AbstractType::ZoneHandle(function().result_type()); - return CompileType::FromAbstractType(result_type); + return CompileType::FromAbstractType( + result_type.IsVoidType() ? AbstractType::ZoneHandle(Type::NullType()) + : result_type); } return CompileType::Dynamic(); diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index a6ba79ffd30..e00640e3ab0 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -3820,6 +3820,7 @@ bool Class::TypeTestNonRecursive(const Class& cls, Zone* zone = Thread::Current()->zone(); Class& thsi = Class::Handle(zone, cls.raw()); while (true) { + ASSERT(!thsi.IsVoidClass()); // Check for DynamicType. // Each occurrence of DynamicType in type T is interpreted as the dynamic // type, a supertype of all types. @@ -3838,15 +3839,10 @@ bool Class::TypeTestNonRecursive(const Class& cls, return test_kind == Class::kIsSubtypeOf; } // Check for ObjectType. Any type that is not NullType or DynamicType - // (already checked above), is more specific than ObjectType/VoidType. - if (other.IsObjectClass() || other.IsVoidClass()) { + // (already checked above), is more specific than ObjectType. + if (other.IsObjectClass()) { return true; } - // If other is neither Object, dynamic or void, then ObjectType/VoidType - // can't be a subtype of other. - if (thsi.IsObjectClass() || thsi.IsVoidClass()) { - return false; - } // Check for reflexivity. if (thsi.raw() == other.raw()) { const intptr_t num_type_params = thsi.NumTypeParameters(); @@ -15834,7 +15830,7 @@ bool Instance::IsInstanceOf( ASSERT(!other.IsMalformed()); ASSERT(!other.IsMalbounded()); if (other.IsVoidType()) { - return true; + return false; } Zone* zone = Thread::Current()->zone(); const Class& cls = Class::Handle(zone, clazz()); @@ -16652,12 +16648,9 @@ bool AbstractType::TypeTest(TypeTestKind test_kind, return false; } // Any type is a subtype of (and is more specific than) Object and dynamic. - // As of Dart 1.24, void is dynamically treated like Object (except when - // comparing function-types). // As of Dart 1.5, the Null type is a subtype of (and is more specific than) // any type. - if (other.IsObjectType() || other.IsDynamicType() || other.IsVoidType() || - IsNullType()) { + if (other.IsObjectType() || other.IsDynamicType() || IsNullType()) { return true; } Zone* zone = Thread::Current()->zone(); diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart index 42529f413b4..b84ff432b14 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -3415,6 +3415,11 @@ listSuperNativeTypeCast(value, property) { propertyTypeCastError(value, property); } +voidTypeCheck(value) { + if (value == null) return value; + throw new TypeErrorImplementation(value, 'void'); +} + extractFunctionTypeObjectFrom(o) { var interceptor = getInterceptor(o); var signatureName = JS_GET_NAME(JsGetName.SIGNATURE_NAME); diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status index 850f5cf1e8c..0f9f513e4e9 100644 --- a/tests/co19/co19-co19.status +++ b/tests/co19/co19-co19.status @@ -66,12 +66,3 @@ LibTest/math/cos_A01_t01: PASS, FAIL, OK # Issue 26261 LibTest/math/tan_A01_t01: PASS, FAIL, OK # Issue 26261 LibTest/math/log_A01_t01: PASS, FAIL, OK # Issue 26261 -[ $compiler != dart2analyzer && $checked ] -# Tests that fail on every runtime in checked mode, but not on the analyzer. -Language/Functions/generator_return_type_t01: Fail # Co19 issue 110 -Language/Functions/generator_return_type_t02: Fail # Co19 issue 110 -Language/Functions/async_return_type_t01: Fail # Co19 issue 110 -Language/Types/Type_Void/returning_t02: Fail # Co19 issue 110 -Language/Types/Type_Void/returning_t03: Fail # Co19 issue 110 -Language/Types/Type_Void/returning_t04: Fail # Co19 issue 110 -Language/Types/Type_Void/returning_t05: Fail # Co19 issue 110 diff --git a/tests/language/return_type_test.dart b/tests/language/return_type_test.dart index 1346e415cea..a2137bb0ca4 100644 --- a/tests/language/return_type_test.dart +++ b/tests/language/return_type_test.dart @@ -21,7 +21,7 @@ void returnString2() => 's'; main() { if (isCheckedMode()) { Expect.throws(returnString1, (e) => e is TypeError); - returnString2(); + Expect.throws(returnString2, (e) => e is TypeError); returnNull(); } else { returnString1(); diff --git a/tests/language/void_check_test.dart b/tests/language/void_check_test.dart deleted file mode 100644 index 2e42a255359..00000000000 --- a/tests/language/void_check_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2017, 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. - -// Tests that `void` accepts any value and won't throw on non-`null` values. -// The test is set up in a way that `--trust-type-annotations` and type -// propagation must not assume that `void` is `null` either. - -import 'package:expect/expect.dart'; - -class A { - void foo() { - return bar(); - } - - void bar() {} -} - -class B extends A { - int bar() => 42; -} - -// Makes the typing cleaner: the return type here is `dynamic` and we are -// guaranteed that there won't be any warnings. -// Dart2js can still infer the type by itself. -@NoInline() -callFoo(A a) => a.foo(); - -main() { - var a = new A(); - var b = new B(); - // The following line is not throwing, even though `a.foo()` (inside - // `callFoo`) is supposedly `void`. - callFoo(b).abs(); - Expect.isNull(callFoo(a)); - Expect.equals(42, callFoo(b)); -} diff --git a/tests/language/void_type_test.dart b/tests/language/void_type_test.dart index 2ce3233c25b..faf8b3f2ff3 100644 --- a/tests/language/void_type_test.dart +++ b/tests/language/void_type_test.dart @@ -41,7 +41,7 @@ void f_f() { return f(); } -void test(int n, void func()) { +void test(int n, void func(), bool must_get_error) { // Test as closure call. { bool got_type_error = false; @@ -50,7 +50,12 @@ void test(int n, void func()) { } on TypeError catch (error) { got_type_error = true; } - Expect.isFalse(got_type_error); + // Never a type error in production mode. + if (isCheckedMode()) { + Expect.isTrue(got_type_error == must_get_error); + } else { + Expect.isFalse(got_type_error); + } } // Test as direct call. { @@ -80,15 +85,20 @@ void test(int n, void func()) { } on TypeError catch (error) { got_type_error = true; } - Expect.isFalse(got_type_error); + // Never a type error in production mode. + if (isCheckedMode()) { + Expect.isTrue(got_type_error == must_get_error); + } else { + Expect.isFalse(got_type_error); + } } } main() { - test(0, f); - test(1, f_null); - test(2, f_1); - test(3, f_dyn_null); - test(4, f_dyn_1); - test(5, f_f); + test(0, f, false); + test(1, f_null, false); + test(2, f_1, true); + test(3, f_dyn_null, false); + test(4, f_dyn_1, true); + test(5, f_f, false); }