From 8e01f69305c48ceabc53f4634f0f8a203dec15ca Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Mon, 8 May 2017 11:31:33 +0200 Subject: [PATCH] Void is not required to be `null` anymore. In checked mode, `void` now accepts any value. Fixes #28937. Fixes #28938. BUG= http://dartbug.com/28937 BUG= http://dartbug.com/28938 R=johnniwinther@google.com, kmillikin@google.com, lrn@google.com, regis@google.com Committed: https://github.com/dart-lang/sdk/commit/521dc6620fd2c4591aa975745c14b304428f17eb Reverted: https://github.com/dart-lang/sdk/commit/a3c0cb65af4bbbdde756208dcccdcdf896902b5b Committed: https://github.com/dart-lang/sdk/commit/4e52c457a9ac412b6b0a3a350020998c7e2f7d9a Reverted: https://github.com/dart-lang/sdk/commit/4b35d3995b2b273728751c19976ece0edca0eddc Committed: https://github.com/dart-lang/sdk/commit/6caf9ef4439e20ea726d8a5383ed34d42b02c84b Reverted: https://github.com/dart-lang/sdk/commit/30d9bf2ed3225f04c52d999a1a5c61e63a90578d Review-Url: https://codereview.chromium.org/2718513002 . --- 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/kernel_to_il.cc | 2 +- runtime/vm/object.cc | 17 ++++++--- .../_internal/js_runtime/lib/js_helper.dart | 5 --- tests/co19/co19-co19.status | 9 +++++ tests/co19/co19-dartium.status | 4 -- tests/language/return_type_test.dart | 2 +- tests/language/void_check_test.dart | 37 +++++++++++++++++++ tests/language/void_type_test.dart | 28 +++++--------- 22 files changed, 101 insertions(+), 113 deletions(-) create mode 100644 tests/language/void_check_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 518f18d0f50..cbbf5bc88e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## 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 1f41e682937..0fa960c921a 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -1753,14 +1753,13 @@ 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 884debc17e5..65ecb5563d2 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 nullType; + if (annotation.isVoid) return type; 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 571e1061676..d239c1aa9b7 100644 --- a/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart +++ b/pkg/compiler/lib/src/js_backend/checked_mode_helpers.dart @@ -117,7 +117,6 @@ 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'), @@ -203,12 +202,6 @@ 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 02393a1fee5..1cb567c6ec3 100644 --- a/pkg/compiler/lib/src/js_backend/impact_transformer.dart +++ b/pkg/compiler/lib/src/js_backend/impact_transformer.dart @@ -363,6 +363,7 @@ 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 e66c7226f6d..2ae4d86578c 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -2926,6 +2926,7 @@ 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 cfee3948cae..7746f75e7f6 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -1342,8 +1342,9 @@ 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.isVoid || type.isFunctionType || type.isMalformed) { + if (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 53ae53b1100..946978ea8f4 100644 --- a/runtime/vm/flow_graph_builder.cc +++ b/runtime/vm/flow_graph_builder.cc @@ -1259,8 +1259,9 @@ bool EffectGraphVisitor::CanSkipTypeCheck(TokenPosition token_pos, return false; } - // Any type is more specific than the dynamic type and than the Object type. - if (dst_type.IsDynamicType() || dst_type.IsObjectType()) { + // Any type is more specific than the dynamic type, the Object type, or void. + if (dst_type.IsDynamicType() || dst_type.IsObjectType() || + dst_type.IsVoidType()) { return true; } diff --git a/runtime/vm/flow_graph_compiler_arm.cc b/runtime/vm/flow_graph_compiler_arm.cc index b0b936d6fc1..7e5daa0601f 100644 --- a/runtime/vm/flow_graph_compiler_arm.cc +++ b/runtime/vm/flow_graph_compiler_arm.cc @@ -532,11 +532,6 @@ 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 @@ -579,7 +574,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); const Register kInstantiatorTypeArgumentsReg = R2; const Register kFunctionTypeArgumentsReg = R1; __ PushList((1 << kInstantiatorTypeArgumentsReg) | @@ -658,7 +653,8 @@ 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.IsDynamicType() && !dst_type.IsObjectType() && + !dst_type.IsVoidType())); 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 e51c0ab389f..970e2b9772d 100644 --- a/runtime/vm/flow_graph_compiler_arm64.cc +++ b/runtime/vm/flow_graph_compiler_arm64.cc @@ -524,11 +524,6 @@ 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 @@ -571,7 +566,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); const Register kInstantiatorTypeArgumentsReg = R1; const Register kFunctionTypeArgumentsReg = R2; __ PushPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg); @@ -650,7 +645,8 @@ 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.IsDynamicType() && !dst_type.IsObjectType() && + !dst_type.IsVoidType())); 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 34d3452ec62..3751e539890 100644 --- a/runtime/vm/flow_graph_compiler_ia32.cc +++ b/runtime/vm/flow_graph_compiler_ia32.cc @@ -538,11 +538,6 @@ 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 @@ -585,7 +580,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); __ pushl(EDX); // Store instantiator type arguments. __ pushl(ECX); // Store function type arguments. @@ -666,7 +661,8 @@ 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.IsDynamicType() && !dst_type.IsObjectType() && + !dst_type.IsVoidType())); __ 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 5f7a62f6b3a..f9425c62306 100644 --- a/runtime/vm/flow_graph_compiler_mips.cc +++ b/runtime/vm/flow_graph_compiler_mips.cc @@ -520,11 +520,6 @@ 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 @@ -567,7 +562,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); // Preserve instantiator type arguments (A1) and function type arguments (A2). __ addiu(SP, SP, Immediate(-2 * kWordSize)); @@ -653,7 +648,8 @@ 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.IsDynamicType() && !dst_type.IsObjectType() && + !dst_type.IsVoidType())); // 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 59fed95ea73..3bb98222d23 100644 --- a/runtime/vm/flow_graph_compiler_x64.cc +++ b/runtime/vm/flow_graph_compiler_x64.cc @@ -533,11 +533,6 @@ 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 +575,7 @@ void FlowGraphCompiler::GenerateInstanceOf(TokenPosition token_pos, const AbstractType& type, LocationSummary* locs) { ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded()); - ASSERT(!type.IsObjectType() && !type.IsDynamicType()); + ASSERT(!type.IsObjectType() && !type.IsDynamicType() && !type.IsVoidType()); __ pushq(RDX); // Store instantiator type arguments. __ pushq(RCX); // Store function type arguments. @@ -589,8 +584,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, and dynamic. - // Object and dynamic have already been checked above (if the type is + // 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 // 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 @@ -659,7 +654,8 @@ 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.IsDynamicType() && !dst_type.IsObjectType() && + !dst_type.IsVoidType())); __ 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 9617c03b348..be1366cdbf2 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_ = kNullCid; + cid_ = kDynamicCid; } 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()) { + if (type.IsDynamicType() || type.IsObjectType() || type.IsVoidType()) { *is_instance = true; return true; } @@ -696,11 +696,6 @@ 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; } @@ -715,12 +710,6 @@ 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) { @@ -737,11 +726,6 @@ 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); } @@ -943,11 +927,6 @@ 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()); } @@ -1038,13 +1017,9 @@ 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.IsVoidType() ? AbstractType::ZoneHandle(Type::NullType()) - : result_type); + return CompileType::FromAbstractType(result_type); } return CompileType::Dynamic(); diff --git a/runtime/vm/kernel_to_il.cc b/runtime/vm/kernel_to_il.cc index 139c2f17448..f5c2b833bb0 100644 --- a/runtime/vm/kernel_to_il.cc +++ b/runtime/vm/kernel_to_il.cc @@ -4053,7 +4053,7 @@ Fragment FlowGraphBuilder::CheckAssignableInCheckedMode( const dart::String& dst_name) { Fragment instructions; if (I->type_checks() && !dst_type.IsDynamicType() && - !dst_type.IsObjectType()) { + !dst_type.IsObjectType() && !dst_type.IsVoidType()) { LocalVariable* top_of_stack = MakeTemporary(); instructions += LoadLocal(top_of_stack); instructions += AssertAssignable(dst_type, dst_name); diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index e00640e3ab0..a6ba79ffd30 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -3820,7 +3820,6 @@ 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. @@ -3839,10 +3838,15 @@ 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. - if (other.IsObjectClass()) { + // (already checked above), is more specific than ObjectType/VoidType. + if (other.IsObjectClass() || other.IsVoidClass()) { 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(); @@ -15830,7 +15834,7 @@ bool Instance::IsInstanceOf( ASSERT(!other.IsMalformed()); ASSERT(!other.IsMalbounded()); if (other.IsVoidType()) { - return false; + return true; } Zone* zone = Thread::Current()->zone(); const Class& cls = Class::Handle(zone, clazz()); @@ -16648,9 +16652,12 @@ 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() || IsNullType()) { + if (other.IsObjectType() || other.IsDynamicType() || other.IsVoidType() || + 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 d1866168b48..fc1987ad7e4 100644 --- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart +++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart @@ -3416,11 +3416,6 @@ 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 0f9f513e4e9..850f5cf1e8c 100644 --- a/tests/co19/co19-co19.status +++ b/tests/co19/co19-co19.status @@ -66,3 +66,12 @@ 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/co19/co19-dartium.status b/tests/co19/co19-dartium.status index 9f7c708ae57..b774ad1b5f4 100644 --- a/tests/co19/co19-dartium.status +++ b/tests/co19/co19-dartium.status @@ -1308,14 +1308,11 @@ LayoutTests/fast/lists/list-style-position-inside_t01: RuntimeError # co19-roll LayoutTests/fast/lists/marker-preferred-margins_t01: RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/multicol/fixed-column-percent-logical-height-orthogonal-writing-mode_t01: RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/multicol/vertical-lr/image-inside-nested-blocks-with-border_t01: RuntimeError # co19-roll r801: Please triage this failure. -LayoutTests/fast/parser/pre-first-line-break_t01: RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/replaced/available-height-for-content_t01: RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/replaced/computed-image-width-with-percent-height-and-fixed-ancestor-vertical-lr_t01: Pass, RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/replaced/computed-image-width-with-percent-height-and-fixed-ancestor_t01: Pass, RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/replaced/computed-image-width-with-percent-height-inside-table-cell-and-fixed-ancestor-vertical-lr_t01: Pass, RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/replaced/computed-image-width-with-percent-height-inside-table-cell-and-fixed-ancestor_t01: Pass, RuntimeError # co19-roll r801: Please triage this failure. -LayoutTests/fast/shapes/parsing/parsing-shape-image-threshold_t01: RuntimeError # co19-roll r801: Please triage this failure. -LayoutTests/fast/shapes/parsing/parsing-shape-margin_t01: RuntimeError # co19-roll r801: Please triage this failure. LayoutTests/fast/table/absolute-table-percent-lengths_t01: RuntimeError # co19-roll r786: Please triage this failure. LayoutTests/fast/table/css-table-width-with-border-padding_t01: RuntimeError # Please triage this failure. LayoutTests/fast/table/css-table-width_t01: RuntimeError # co19-roll r786: Please triage this failure. @@ -1342,6 +1339,5 @@ WebPlatformTest/custom-elements/instantiating/createElement_A05_t01: RuntimeErro WebPlatformTest/html-templates/parsing-html-templates/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-body-context_t01: RuntimeError # co19-roll r722: Please triage this failure. WebPlatformTest/html-templates/parsing-html-templates/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-context_t01: RuntimeError # co19-roll r722: Please triage this failure. WebPlatformTest/html-templates/parsing-html-templates/clearing-the-stack-back-to-a-given-context/clearing-stack-back-to-a-table-row-context_t01: RuntimeError # co19-roll r722: Please triage this failure. -WebPlatformTest/html/dom/elements/global-attributes/dataset-enumeration_t01: RuntimeError # co19-roll r738: Please triage this failure. WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: RuntimeError # co19-roll r738: Please triage this failure. WebPlatformTest/shadow-dom/shadow-trees/upper-boundary-encapsulation/ownerdocument-001_t01: RuntimeError # co19-roll r722: Please triage this failure. diff --git a/tests/language/return_type_test.dart b/tests/language/return_type_test.dart index a2137bb0ca4..1346e415cea 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); - Expect.throws(returnString2, (e) => e is TypeError); + returnString2(); returnNull(); } else { returnString1(); diff --git a/tests/language/void_check_test.dart b/tests/language/void_check_test.dart new file mode 100644 index 00000000000..2e42a255359 --- /dev/null +++ b/tests/language/void_check_test.dart @@ -0,0 +1,37 @@ +// 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 faf8b3f2ff3..2ce3233c25b 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(), bool must_get_error) { +void test(int n, void func()) { // Test as closure call. { bool got_type_error = false; @@ -50,12 +50,7 @@ void test(int n, void func(), bool must_get_error) { } on TypeError catch (error) { got_type_error = true; } - // Never a type error in production mode. - if (isCheckedMode()) { - Expect.isTrue(got_type_error == must_get_error); - } else { - Expect.isFalse(got_type_error); - } + Expect.isFalse(got_type_error); } // Test as direct call. { @@ -85,20 +80,15 @@ void test(int n, void func(), bool must_get_error) { } on TypeError catch (error) { got_type_error = true; } - // Never a type error in production mode. - if (isCheckedMode()) { - Expect.isTrue(got_type_error == must_get_error); - } else { - Expect.isFalse(got_type_error); - } + Expect.isFalse(got_type_error); } } main() { - 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); + 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); }