[cfe] Implement pointwise implicit downcasts for record literals

This CL implements the adjustment of the static semantics for records
as described in
d19f6d5644. The
adjustment is based on the discussion at
https://github.com/dart-lang/language/issues/2488.

Part of https://github.com/dart-lang/sdk/issues/49713

Change-Id: I7a9d456f702ad0fb14aa3bd121ba9d2bbd104414
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/262202
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Chloe Stefantsova 2022-10-04 11:41:45 +00:00 committed by Commit Queue
parent d968be9021
commit 7e57f6e371
19 changed files with 267 additions and 76 deletions

View file

@ -303,7 +303,21 @@ class ExpressionInferenceResult {
/// The inferred expression.
final Expression expression;
ExpressionInferenceResult(this.inferredType, this.expression)
/// More precise type of the expression after coercion.
///
/// Consider the following code:
///
/// dynamic foo = 3;
/// int bar = foo;
///
/// In the example above `foo` is coerced to `foo as int`, but
/// [inferredType]` of `foo` stays `dynamic`. In some situations, like
/// coercing elements of record literals, we want to know the more precise
/// type of the expression after coercion, `int` in the example above.
final DartType? postCoercionType;
ExpressionInferenceResult(this.inferredType, this.expression,
{this.postCoercionType = null})
// ignore: unnecessary_null_comparison
: assert(expression != null);
@ -416,6 +430,9 @@ class NullAwareExpressionInferenceResult implements ExpressionInferenceResult {
'NullAwareExpressionInferenceResult');
}
@override
DartType? get postCoercionType => null;
@override
ExpressionInferenceResult stopShorting() {
Expression expression = nullAwareAction;

View file

@ -7362,7 +7362,8 @@ class InferenceVisitorImpl extends InferenceVisitorBase
ExpressionInferenceResult visitInternalRecordLiteral(
InternalRecordLiteral node, DartType typeContext) {
List<Expression> positional = node.positional;
List<NamedExpression> named = node.named;
List<NamedExpression> namedUnsorted = node.named;
List<NamedExpression> named = namedUnsorted;
Map<String, NamedExpression>? namedElements = node.namedElements;
List<Object> originalElementOrder = node.originalElementOrder;
List<VariableDeclaration>? hoistedExpressions;
@ -7371,19 +7372,23 @@ class InferenceVisitorImpl extends InferenceVisitorBase
Map<String, DartType>? namedTypeContexts;
if (typeContext is RecordType &&
typeContext.positional.length == positional.length &&
typeContext.named.length == named.length) {
typeContext.named.length == namedUnsorted.length) {
namedTypeContexts = <String, DartType>{};
for (NamedType namedType in typeContext.named) {
namedTypeContexts[namedType.name] = namedType.type;
}
bool sameNames = true;
for (int i = 0; sameNames && i < named.length; i++) {
if (typeContext.named[i].name != named[i].name) {
for (int i = 0; sameNames && i < namedUnsorted.length; i++) {
if (!namedTypeContexts.containsKey(namedUnsorted[i].name)) {
sameNames = false;
}
}
if (sameNames) {
positionalTypeContexts = typeContext.positional;
namedTypeContexts = <String, DartType>{};
for (NamedType namedType in typeContext.named) {
namedTypeContexts[namedType.name] = namedType.type;
}
} else {
namedTypeContexts = null;
}
}
@ -7396,9 +7401,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
namedTypes = [];
for (int index = 0; index < positional.length; index++) {
Expression expression = positional[index];
ExpressionInferenceResult expressionResult = inferExpression(expression,
positionalTypeContexts?[index] ?? const UnknownType(), true);
positionalTypes.add(expressionResult.inferredType);
DartType contextType =
positionalTypeContexts?[index] ?? const UnknownType();
ExpressionInferenceResult expressionResult =
inferExpression(expression, contextType, true);
if (contextType is! UnknownType) {
expressionResult =
ensureAssignableResult(contextType, expressionResult);
}
positionalTypes.add(
expressionResult.postCoercionType ?? expressionResult.inferredType);
positional[index] = expressionResult.expression;
}
} else {
@ -7436,12 +7450,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
for (int index = originalElementOrder.length - 1; index >= 0; index--) {
Object element = originalElementOrder[index];
if (element is NamedExpression) {
ExpressionInferenceResult expressionResult = inferExpression(
element.value,
namedTypeContexts?[element.name] ?? const UnknownType(),
true);
DartType contextType =
namedTypeContexts?[element.name] ?? const UnknownType();
ExpressionInferenceResult expressionResult =
inferExpression(element.value, contextType, true);
if (contextType is! UnknownType) {
expressionResult =
ensureAssignableResult(contextType, expressionResult);
}
Expression expression = expressionResult.expression;
DartType type = expressionResult.inferredType;
DartType type = expressionResult.postCoercionType ??
expressionResult.inferredType;
// TODO(johnniwinther): Should we use [isPureExpression] as is, make
// it include (simple) literals, or add a new predicate?
if (needsHoisting && !isPureExpression(expression)) {
@ -7462,12 +7481,17 @@ class InferenceVisitorImpl extends InferenceVisitorBase
}
nameIndex--;
} else {
ExpressionInferenceResult expressionResult = inferExpression(
element as Expression,
positionalTypeContexts?[positionalIndex] ?? const UnknownType(),
true);
DartType contextType =
positionalTypeContexts?[positionalIndex] ?? const UnknownType();
ExpressionInferenceResult expressionResult =
inferExpression(element as Expression, contextType, true);
if (contextType is! UnknownType) {
expressionResult =
ensureAssignableResult(contextType, expressionResult);
}
Expression expression = expressionResult.expression;
DartType type = expressionResult.inferredType;
DartType type = expressionResult.postCoercionType ??
expressionResult.inferredType;
// TODO(johnniwinther): Should we use [isPureExpression] as is, make
// it include (simple) literals, or add a new predicate?
if (needsHoisting && !isPureExpression(expression)) {

View file

@ -529,6 +529,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
DartType expressionType = inferenceResult.inferredType;
Expression expression = inferenceResult.expression;
Expression result;
DartType? postCoercionType;
switch (assignabilityResult.kind) {
case AssignabilityKind.assignable:
result = expression;
@ -540,6 +541,7 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
..isForNonNullableByDefault = isNonNullableByDefault
..isForDynamic = expressionType is DynamicType
..fileOffset = fileOffset;
postCoercionType = initialContextType;
break;
case AssignabilityKind.unassignable:
// Error: not assignable. Perform error recovery.
@ -624,7 +626,8 @@ abstract class InferenceVisitorBase implements InferenceVisitor {
if (!identical(result, expression)) {
flowAnalysis.forwardExpression(result, expression);
return new ExpressionInferenceResult(expressionType, result);
return new ExpressionInferenceResult(expressionType, result,
postCoercionType: postCoercionType);
} else {
return inferenceResult;
}

View file

@ -491,6 +491,9 @@ closures
clue
code
coerce
coerced
coercing
coercion
coincides
coinductively
collapses

View file

@ -0,0 +1,21 @@
// Copyright (c) 2022, 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.
class Callable {
void call() {}
}
T id<T>(T x) => x;
foo() {
// No static error.
// Inferred type of the record is (int, double, int Function(int), void Function()).
var c = Callable();
dynamic d = 3;
(num, double, int Function(int), void Function()) r = (d, 3, id, c);
({num x, double y, int Function(int) f, void Function() g}) r2 = (x: d, y: 3, f: id, g: c);
(num, double, {int Function(int) f, void Function() g}) r3 = (d, 3, f: id, g: c);
}
main() {}

View file

@ -0,0 +1,25 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
: super core::Object::•()
;
method call() → void {}
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
return x;
static method foo() → dynamic {
self::Callable c = new self::Callable::•();
dynamic d = 3;
(core::num, core::double, (core::int) → core::int, () → void) r = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, #C2, let final self::Callable #t1 = c in #t1 == null ?{() → void} null : #t1.{self::Callable::call}{() → void});
({required f: (core::int) → core::int, required g: () → void, required x: core::num, required y: core::double}) r2 = let final core::num #t2 = d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num in let final core::double #t3 = 3.0 in let final (core::int) → core::int #t4 = #C2 in ({f: #t4, g: let final self::Callable #t5 = c in #t5 == null ?{() → void} null : #t5.{self::Callable::call}{() → void}, x: #t2, y: #t3});
(core::num, core::double, {required f: (core::int) → core::int, required g: () → void}) r3 = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, {f: #C2, g: let final self::Callable #t6 = c in #t6 == null ?{() → void} null : #t6.{self::Callable::call}{() → void}});
}
static method main() → dynamic {}
constants {
#C1 = static-tearoff self::id
#C2 = instantiation #C1 <core::int>
}

View file

@ -0,0 +1,30 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
: super core::Object::•()
;
method call() → void {}
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
return x;
static method foo() → dynamic {
self::Callable c = new self::Callable::•();
dynamic d = 3;
(core::num, core::double, (core::int) → core::int, () → void) r = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, #C2, let final self::Callable #t1 = c in #t1 == null ?{() → void} null : #t1.{self::Callable::call}{() → void});
({required f: (core::int) → core::int, required g: () → void, required x: core::num, required y: core::double}) r2 = let final core::num #t2 = d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num in let final core::double #t3 = 3.0 in let final (core::int) → core::int #t4 = #C2 in ({f: #t4, g: let final self::Callable #t5 = c in #t5 == null ?{() → void} null : #t5.{self::Callable::call}{() → void}, x: #t2, y: #t3});
(core::num, core::double, {required f: (core::int) → core::int, required g: () → void}) r3 = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, {f: #C2, g: let final self::Callable #t6 = c in #t6 == null ?{() → void} null : #t6.{self::Callable::call}{() → void}});
}
static method main() → dynamic {}
constants {
#C1 = static-tearoff self::id
#C2 = instantiation #C1 <core::int>
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///pointwise_implicit_downcasts.dart:17:84 -> InstantiationConstant(id<int>)
Evaluated: VariableGet @ org-dartlang-testcase:///pointwise_implicit_downcasts.dart:17:78 -> DoubleConstant(3.0)
Extra constant evaluation: evaluated: 38, effectively constant: 2

View file

@ -0,0 +1,7 @@
class Callable {
void call() {}
}
T id<T>(T x) => x;
foo() {}
main() {}

View file

@ -0,0 +1,8 @@
T id<T>(T x) => x;
class Callable {
void call() {}
}
foo() {}
main() {}

View file

@ -0,0 +1,25 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
: super core::Object::•()
;
method call() → void {}
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
return x;
static method foo() → dynamic {
self::Callable c = new self::Callable::•();
dynamic d = 3;
(core::num, core::double, (core::int) → core::int, () → void) r = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, #C2, let final self::Callable #t1 = c in #t1 == null ?{() → void} null : #t1.{self::Callable::call}{() → void});
({required f: (core::int) → core::int, required g: () → void, required x: core::num, required y: core::double}) r2 = let final core::num #t2 = d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num in let final core::double #t3 = 3.0 in let final (core::int) → core::int #t4 = #C2 in ({f: #t4, g: let final self::Callable #t5 = c in #t5 == null ?{() → void} null : #t5.{self::Callable::call}{() → void}, x: #t2, y: #t3});
(core::num, core::double, {required f: (core::int) → core::int, required g: () → void}) r3 = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, {f: #C2, g: let final self::Callable #t6 = c in #t6 == null ?{() → void} null : #t6.{self::Callable::call}{() → void}});
}
static method main() → dynamic {}
constants {
#C1 = static-tearoff self::id
#C2 = instantiation #C1 <core::int*>
}

View file

@ -0,0 +1,25 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
: super core::Object::•()
;
method call() → void {}
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
return x;
static method foo() → dynamic {
self::Callable c = new self::Callable::•();
dynamic d = 3;
(core::num, core::double, (core::int) → core::int, () → void) r = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, #C2, let final self::Callable #t1 = c in #t1 == null ?{() → void} null : #t1.{self::Callable::call}{() → void});
({required f: (core::int) → core::int, required g: () → void, required x: core::num, required y: core::double}) r2 = let final core::num #t2 = d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num in let final core::double #t3 = 3.0 in let final (core::int) → core::int #t4 = #C2 in ({f: #t4, g: let final self::Callable #t5 = c in #t5 == null ?{() → void} null : #t5.{self::Callable::call}{() → void}, x: #t2, y: #t3});
(core::num, core::double, {required f: (core::int) → core::int, required g: () → void}) r3 = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, {f: #C2, g: let final self::Callable #t6 = c in #t6 == null ?{() → void} null : #t6.{self::Callable::call}{() → void}});
}
static method main() → dynamic {}
constants {
#C1 = static-tearoff self::id
#C2 = instantiation #C1 <core::int*>
}

View file

@ -0,0 +1,16 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
;
method call() → void
;
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
;
static method foo() → dynamic
;
static method main() → dynamic
;

View file

@ -0,0 +1,30 @@
library /*isNonNullableByDefault*/;
import self as self;
import "dart:core" as core;
class Callable extends core::Object {
synthetic constructor •() → self::Callable
: super core::Object::•()
;
method call() → void {}
}
static method id<T extends core::Object? = dynamic>(self::id::T% x) → self::id::T%
return x;
static method foo() → dynamic {
self::Callable c = new self::Callable::•();
dynamic d = 3;
(core::num, core::double, (core::int) → core::int, () → void) r = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, #C2, let final self::Callable #t1 = c in #t1 == null ?{() → void} null : #t1.{self::Callable::call}{() → void});
({required f: (core::int) → core::int, required g: () → void, required x: core::num, required y: core::double}) r2 = let final core::num #t2 = d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num in let final core::double #t3 = 3.0 in let final (core::int) → core::int #t4 = #C2 in ({f: #t4, g: let final self::Callable #t5 = c in #t5 == null ?{() → void} null : #t5.{self::Callable::call}{() → void}, x: #t2, y: #t3});
(core::num, core::double, {required f: (core::int) → core::int, required g: () → void}) r3 = (d as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.0, {f: #C2, g: let final self::Callable #t6 = c in #t6 == null ?{() → void} null : #t6.{self::Callable::call}{() → void}});
}
static method main() → dynamic {}
constants {
#C1 = static-tearoff self::id
#C2 = instantiation #C1 <core::int*>
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///pointwise_implicit_downcasts.dart:17:84 -> InstantiationConstant(id<int*>)
Evaluated: VariableGet @ org-dartlang-testcase:///pointwise_implicit_downcasts.dart:17:78 -> DoubleConstant(3.0)
Extra constant evaluation: evaluated: 38, effectively constant: 2

View file

@ -15,7 +15,7 @@ foo3() {
}
foo4() {
(num, num) r = (3 as dynamic, 3.5); // Error.
(num, num) r = (3 as dynamic, 3.5);
}
foo5((int, String?) r, (int, X) Function<X>() f) {

View file

@ -1,11 +1,4 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
// (num, num) r = (3 as dynamic, 3.5); // Error.
// ^
//
import self as self;
import "dart:core" as core;
@ -31,9 +24,7 @@ static method foo3() → dynamic {
} =>#t1;
}
static method foo4() → dynamic {
(core::num, core::num) r = invalid-expression "pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
(num, num) r = (3 as dynamic, 3.5); // Error.
^" in (3 as{ForNonNullableByDefault} dynamic, 3.5) as{TypeError,ForNonNullableByDefault} (core::num, core::num);
(core::num, core::num) r = ((3 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.5);
}
static method foo5((core::int, core::String?) r, <X extends core::Object? = dynamic>() → (core::int, X%) f) → dynamic {
r = f<core::String?>(){() → (core::int, core::String?)};

View file

@ -1,11 +1,4 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
// (num, num) r = (3 as dynamic, 3.5); // Error.
// ^
//
import self as self;
import "dart:core" as core;
@ -31,9 +24,7 @@ static method foo3() → dynamic {
} =>#t1;
}
static method foo4() → dynamic {
(core::num, core::num) r = invalid-expression "pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
(num, num) r = (3 as dynamic, 3.5); // Error.
^" in (3 as{ForNonNullableByDefault} dynamic, 3.5) as{TypeError,ForNonNullableByDefault} (core::num, core::num);
(core::num, core::num) r = ((3 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.5);
}
static method foo5((core::int, core::String?) r, <X extends core::Object? = dynamic>() → (core::int, X%) f) → dynamic {
r = f<core::String?>(){() → (core::int, core::String?)};
@ -53,4 +44,5 @@ Extra constant evaluation status:
Evaluated: RecordLiteral @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Evaluated: VariableGetImpl @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Evaluated: VariableGet @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Extra constant evaluation: evaluated: 24, effectively constant: 3
Evaluated: RecordLiteral @ org-dartlang-testcase:///simple_inference.dart:18:18 -> RecordConstant(const (3, 3.5))
Extra constant evaluation: evaluated: 25, effectively constant: 4

View file

@ -1,11 +1,4 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
// (num, num) r = (3 as dynamic, 3.5); // Error.
// ^
//
import self as self;
import "dart:core" as core;
@ -31,9 +24,7 @@ static method foo3() → dynamic {
} =>#t1;
}
static method foo4() → dynamic {
(core::num, core::num) r = invalid-expression "pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
(num, num) r = (3 as dynamic, 3.5); // Error.
^" in (3 as{ForNonNullableByDefault} dynamic, 3.5) as{TypeError,ForNonNullableByDefault} (core::num, core::num);
(core::num, core::num) r = ((3 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.5);
}
static method foo5((core::int, core::String?) r, <X extends core::Object? = dynamic>() → (core::int, X%) f) → dynamic {
r = f<core::String?>(){() → (core::int, core::String?)};

View file

@ -1,11 +1,4 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
// (num, num) r = (3 as dynamic, 3.5); // Error.
// ^
//
import self as self;
import "dart:core" as core;
@ -31,9 +24,7 @@ static method foo3() → dynamic {
} =>#t1;
}
static method foo4() → dynamic {
(core::num, core::num) r = invalid-expression "pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
(num, num) r = (3 as dynamic, 3.5); // Error.
^" in (3 as{ForNonNullableByDefault} dynamic, 3.5) as{TypeError,ForNonNullableByDefault} (core::num, core::num);
(core::num, core::num) r = ((3 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.5);
}
static method foo5((core::int, core::String?) r, <X extends core::Object? = dynamic>() → (core::int, X%) f) → dynamic {
r = f<core::String?>(){() → (core::int, core::String?)};

View file

@ -1,11 +1,4 @@
library /*isNonNullableByDefault*/;
//
// Problems in library:
//
// pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
// (num, num) r = (3 as dynamic, 3.5); // Error.
// ^
//
import self as self;
import "dart:core" as core;
@ -31,9 +24,7 @@ static method foo3() → dynamic {
} =>#t1;
}
static method foo4() → dynamic {
(core::num, core::num) r = invalid-expression "pkg/front_end/testcases/records/simple_inference.dart:18:18: Error: A value of type '(dynamic, double)' can't be assigned to a variable of type '(num, num)'.
(num, num) r = (3 as dynamic, 3.5); // Error.
^" in (3 as{ForNonNullableByDefault} dynamic, 3.5) as{TypeError,ForNonNullableByDefault} (core::num, core::num);
(core::num, core::num) r = ((3 as{ForNonNullableByDefault} dynamic) as{TypeError,ForDynamic,ForNonNullableByDefault} core::num, 3.5);
}
static method foo5((core::int, core::String?) r, <X extends core::Object? = dynamic>() → (core::int, X%) f) → dynamic {
r = f<core::String?>(){() → (core::int, core::String?)};
@ -53,4 +44,5 @@ Extra constant evaluation status:
Evaluated: RecordLiteral @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Evaluated: VariableGetImpl @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Evaluated: VariableGet @ org-dartlang-testcase:///simple_inference.dart:14:18 -> RecordConstant(const (3, 3.5))
Extra constant evaluation: evaluated: 24, effectively constant: 3
Evaluated: RecordLiteral @ org-dartlang-testcase:///simple_inference.dart:18:18 -> RecordConstant(const (3, 3.5))
Extra constant evaluation: evaluated: 25, effectively constant: 4