1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 00:08:46 +00:00

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: 521dc6620f
Reverted: a3c0cb65af
Committed: 4e52c457a9
Reverted: 4b35d3995b
Committed: 6caf9ef443
Reverted: 30d9bf2ed3
Review-Url: https://codereview.chromium.org/2718513002 .
This commit is contained in:
Florian Loitsch 2017-05-08 11:31:33 +02:00
parent c162e0f011
commit 8e01f69305
22 changed files with 101 additions and 113 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -117,7 +117,6 @@ class CheckedModeHelpers {
/// All the checked mode helpers.
static const List<CheckedModeHelper> helpers = const <CheckedModeHelper>[
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'

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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) |

View File

@ -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);

View File

@ -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.

View File

@ -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));

View File

@ -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.

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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();

View File

@ -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));
}

View File

@ -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);
}