Implement context-aware type analysis for conditional-like expressions.

In the following expression types, the static type is computed using
the least upper bound ("LUB") of their subexpressions (adjusted as
appropriate to account for the null-shorting behaviors of `??` and
`??=`):

- Conditional expressions (`a ? b : c`)
- If-null expressions (`a ?? b`)
- If-null assignments (`a ??= b`)
- Switch expressions (`switch (s) { p0 => e0, ... }`)

This can lead to problems since the LUB computation sometimes produces
a greater bound than is strictly necessary (for example if there are
multiple candidate bounds at the same level of the class hierarchy,
the LUB algorithm will walk up the class hierarchy until it finds a
level at which there is a unique result). For a discussion of the kind
of problems that can arise, see
https://github.com/dart-lang/language/issues/1618.

This change improves the situation by changing the analysis of these
four expression types so that after computing a candidate static type
using LUB, if that static type does not satisfy the expression's
context, but the static types of all the subexpressions *do* satisfy
the expression's context, then the greatest closure of the context is
used as the static type instead of the LUB. This is the algorithm
proposed in
https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494.

This is theoretically a breaking change (since it can change code that
demotes a local variable into code that doesn't, and then the demotion
or lack of demotion can have follow-on effects in later code). So it
is implemented behind the `inference-update-3` experiment
flag. However, in practice it is minimally breaking; a test over all
of google3 found no test failures from turning the feature on.

Since one of these expression types (switch expressions) is
implemented in `package:_fe_analyzer_shared`, but the other three are
implemented separately in the `package:analyzer` and
`package:front_end`, this change required modifications to all three
packages. I've included tests for the new functionality, following the
testing style of each package. I've also included a comprehensive set
of language tests that fully exercises the feature regardless of how
it's implemented.

Since `package:front_end` has many different implementations of `??=`
depending on the form of the left hand side, I've tried to be quite
comprehensive in the language tests, covering each type of assignable
expression that might appear to the left of `??=`.

Change-Id: I13a6168b6edf6eac1e52ecdb3532985af19dbcdf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/353440
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Erik Ernst <eernst@google.com>
This commit is contained in:
Paul Berry 2024-03-04 17:19:00 +00:00 committed by Commit Queue
parent e79131df0f
commit b279238e48
307 changed files with 19021 additions and 138 deletions

View file

@ -1692,79 +1692,117 @@ mixin TypeAnalyzer<
SwitchExpressionResult<Type, Error> analyzeSwitchExpression(
Expression node, Expression scrutinee, int numCases, TypeSchema schema) {
// Stack: ()
// The static type of a switch expression `E` of the form `switch (e0) { p1
// => e1, p2 => e2, ... pn => en }` with context type `K` is computed as
// follows:
//
// - The scrutinee (`e0`) is first analyzed with context type `_`.
Type expressionType = analyzeExpression(scrutinee, operations.unknownType);
// Stack: (Expression)
handleSwitchScrutinee(expressionType);
flow.switchStatement_expressionEnd(null, scrutinee, expressionType);
Type? lubType;
// - If the switch expression has no cases, its static type is `Never`.
Map<int, Error>? nonBooleanGuardErrors;
Map<int, Type>? guardTypes;
for (int i = 0; i < numCases; i++) {
// Stack: (Expression, i * ExpressionCase)
SwitchExpressionMemberInfo<Node, Expression, Variable> memberInfo =
getSwitchExpressionMemberInfo(node, i);
flow.switchStatement_beginAlternatives();
flow.switchStatement_beginAlternative();
handleSwitchBeforeAlternative(node, caseIndex: i, subIndex: 0);
Node? pattern = memberInfo.head.pattern;
Expression? guard;
if (pattern != null) {
Map<String, List<Variable>> componentVariables = {};
Map<String, int> patternVariablePromotionKeys = {};
dispatchPattern(
new MatchContext<Node, Expression, Pattern, Type, Variable>(
isFinal: false,
switchScrutinee: scrutinee,
componentVariables: componentVariables,
patternVariablePromotionKeys: patternVariablePromotionKeys,
),
pattern,
);
_finishJoinedPatternVariables(
memberInfo.head.variables,
componentVariables,
patternVariablePromotionKeys,
location: JoinedPatternVariableLocation.singlePattern,
);
// Stack: (Expression, i * ExpressionCase, Pattern)
guard = memberInfo.head.guard;
bool hasGuard = guard != null;
if (hasGuard) {
Type guardType = analyzeExpression(
guard, operations.typeToSchema(operations.boolType));
Error? nonBooleanGuardError = _checkGuardType(guard, guardType);
(guardTypes ??= {})[i] = guardType;
if (nonBooleanGuardError != null) {
(nonBooleanGuardErrors ??= {})[i] = nonBooleanGuardError;
Type staticType;
if (numCases == 0) {
staticType = operations.neverType;
} else {
// - Otherwise, for each case `pi => ei`, let `Ti` be the type of `ei`
// inferred with context type `K`.
// - Let `T` be the least upper bound of the static types of all the case
// expressions.
// - Let `S` be the greatest closure of `K`.
Type? t;
Type s = operations.greatestClosure(schema);
bool allCasesSatisfyContext = true;
for (int i = 0; i < numCases; i++) {
// Stack: (Expression, i * ExpressionCase)
SwitchExpressionMemberInfo<Node, Expression, Variable> memberInfo =
getSwitchExpressionMemberInfo(node, i);
flow.switchStatement_beginAlternatives();
flow.switchStatement_beginAlternative();
handleSwitchBeforeAlternative(node, caseIndex: i, subIndex: 0);
Node? pattern = memberInfo.head.pattern;
Expression? guard;
if (pattern != null) {
Map<String, List<Variable>> componentVariables = {};
Map<String, int> patternVariablePromotionKeys = {};
dispatchPattern(
new MatchContext<Node, Expression, Pattern, Type, Variable>(
isFinal: false,
switchScrutinee: scrutinee,
componentVariables: componentVariables,
patternVariablePromotionKeys: patternVariablePromotionKeys,
),
pattern,
);
_finishJoinedPatternVariables(
memberInfo.head.variables,
componentVariables,
patternVariablePromotionKeys,
location: JoinedPatternVariableLocation.singlePattern,
);
// Stack: (Expression, i * ExpressionCase, Pattern)
guard = memberInfo.head.guard;
bool hasGuard = guard != null;
if (hasGuard) {
Type guardType = analyzeExpression(
guard, operations.typeToSchema(operations.boolType));
Error? nonBooleanGuardError = _checkGuardType(guard, guardType);
(guardTypes ??= {})[i] = guardType;
if (nonBooleanGuardError != null) {
(nonBooleanGuardErrors ??= {})[i] = nonBooleanGuardError;
}
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
} else {
handleNoGuard(node, i);
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
}
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
handleCaseHead(node, caseIndex: i, subIndex: 0);
} else {
handleNoGuard(node, i);
// Stack: (Expression, i * ExpressionCase, Pattern, Expression)
handleDefault(node, caseIndex: i, subIndex: 0);
}
handleCaseHead(node, caseIndex: i, subIndex: 0);
} else {
handleDefault(node, caseIndex: i, subIndex: 0);
flow.switchStatement_endAlternative(guard, {});
flow.switchStatement_endAlternatives(null, hasLabels: false);
// Stack: (Expression, i * ExpressionCase, CaseHead)
Type ti = analyzeExpression(memberInfo.expression, schema);
if (allCasesSatisfyContext && !operations.isSubtypeOf(ti, s)) {
allCasesSatisfyContext = false;
}
flow.switchStatement_afterCase();
// Stack: (Expression, i * ExpressionCase, CaseHead, Expression)
if (t == null) {
t = ti;
} else {
t = operations.lub(t, ti);
}
finishExpressionCase(node, i);
// Stack: (Expression, (i + 1) * ExpressionCase)
}
flow.switchStatement_endAlternative(guard, {});
flow.switchStatement_endAlternatives(null, hasLabels: false);
// Stack: (Expression, i * ExpressionCase, CaseHead)
Type type = analyzeExpression(memberInfo.expression, schema);
flow.switchStatement_afterCase();
// Stack: (Expression, i * ExpressionCase, CaseHead, Expression)
if (lubType == null) {
lubType = type;
} else {
lubType = operations.lub(lubType, type);
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!this.options.inferenceUpdate3Enabled) {
staticType = t!;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (operations.isSubtypeOf(t!, s)) {
staticType = t;
} else
// - Otherwise, if `Ti <: S` for all `i`, then the type of `E` is `S`.
if (allCasesSatisfyContext) {
staticType = s;
} else
// - Otherwise, the type of `E` is `T`.
{
staticType = t;
}
finishExpressionCase(node, i);
// Stack: (Expression, (i + 1) * ExpressionCase)
}
lubType ??= operations.neverType;
// Stack: (Expression, numCases * ExpressionCase)
flow.switchStatement_end(true);
return new SwitchExpressionResult(
type: lubType,
type: staticType,
nonBooleanGuardErrors: nonBooleanGuardErrors,
guardTypes: guardTypes);
}
@ -2620,6 +2658,10 @@ class TypeAnalyzerOptions {
final bool patternsEnabled;
final bool inferenceUpdate3Enabled;
TypeAnalyzerOptions(
{required this.nullSafetyEnabled, required this.patternsEnabled});
{required this.nullSafetyEnabled,
required this.patternsEnabled,
required this.inferenceUpdate3Enabled});
}

View file

@ -103,6 +103,10 @@ abstract interface class TypeAnalyzerOperations<Variable extends Object,
/// Computes the greatest lower bound of [type1] and [type2].
Type glb(Type type1, Type type2);
/// Returns the greatest closure of [schema] with respect to the unknown type
/// (`_`).
Type greatestClosure(TypeSchema schema);
/// Queries whether [type] is an "always-exhaustive" type (as defined in the
/// patterns spec). Exhaustive types are types for which the switch statement
/// is required to be exhaustive when patterns support is enabled.

View file

@ -1638,6 +1638,8 @@ class Harness {
late final FlowAnalysis<Node, Statement, Expression, Var, Type> flow;
bool? _inferenceUpdate3Enabled;
bool? _patternsEnabled;
Type? _thisType;
@ -1652,7 +1654,8 @@ class Harness {
this,
TypeAnalyzerOptions(
nullSafetyEnabled: !operations.legacy,
patternsEnabled: patternsEnabled));
patternsEnabled: patternsEnabled,
inferenceUpdate3Enabled: inferenceUpdate3Enabled));
/// Indicates whether initializers of implicitly typed variables should be
/// accounted for by SSA analysis. (In an ideal world, they always would be,
@ -1663,6 +1666,9 @@ class Harness {
bool _fieldPromotionEnabled = true;
bool get inferenceUpdate3Enabled =>
_inferenceUpdate3Enabled ?? !operations.legacy;
MiniIRBuilder get irBuilder => typeAnalyzer._irBuilder;
bool get patternsEnabled => _patternsEnabled ?? !operations.legacy;
@ -1697,6 +1703,10 @@ class Harness {
operations.addExtensionTypeErasure(type, representation);
}
void addLub(String type1, String type2, String resultType) {
operations.addLub(type1, type2, resultType);
}
/// Updates the harness so that when member [memberName] is looked up on type
/// [targetType], a member is found having the given [type].
///
@ -1740,6 +1750,11 @@ class Harness {
_fieldPromotionEnabled = false;
}
void disableInferenceUpdate3() {
assert(!_started);
_inferenceUpdate3Enabled = false;
}
void disablePatterns() {
assert(!_started);
_patternsEnabled = false;
@ -2755,6 +2770,10 @@ class MiniAstOperations
_extensionTypeErasure[type] = Type(representation);
}
void addLub(String type1, String type2, String resultType) {
_lubs['$type1, $type2'] = Type(resultType);
}
void addPromotionException(String from, String to, String result) {
(_promotionExceptions[from] ??= {})[to] = result;
}
@ -2859,6 +2878,12 @@ class MiniAstOperations
return _glbs[query] ?? fail('Unknown glb query: $query');
}
@override
Type greatestClosure(TypeSchema schema) {
var type = schema.toType();
return type.closureWithRespectToUnknown(covariant: true) ?? type;
}
@override
bool isAlwaysExhaustiveType(Type type) {
var query = type.type;

View file

@ -22,6 +22,19 @@ class FunctionType extends Type {
FunctionType(this.returnType, this.positionalParameters) : super._();
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
Type? newReturnType =
returnType.closureWithRespectToUnknown(covariant: covariant);
List<Type>? newPositionalParameters =
positionalParameters.closureWithRespectToUnknown(covariant: !covariant);
if (newReturnType == null && newPositionalParameters == null) {
return null;
}
return FunctionType(newReturnType ?? returnType,
newPositionalParameters ?? positionalParameters);
}
@override
Type? recursivelyDemote({required bool covariant}) {
Type? newReturnType = returnType.recursivelyDemote(covariant: covariant);
@ -78,6 +91,14 @@ class PrimaryType extends Type {
bool get isInterfaceType => !namedNonInterfaceTypes.contains(name);
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
List<Type>? newArgs =
args.closureWithRespectToUnknown(covariant: covariant);
if (newArgs == null) return null;
return PrimaryType(name, args: newArgs);
}
@override
Type? recursivelyDemote({required bool covariant}) {
List<Type>? newArgs = args.recursivelyDemote(covariant: covariant);
@ -107,6 +128,14 @@ class PromotedTypeVariableType extends Type {
PromotedTypeVariableType(this.innerType, this.promotion) : super._();
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
var newPromotion =
promotion.closureWithRespectToUnknown(covariant: covariant);
if (newPromotion == null) return null;
return PromotedTypeVariableType(innerType, newPromotion);
}
@override
Type? recursivelyDemote({required bool covariant}) =>
covariant ? innerType : new PrimaryType('Never');
@ -131,6 +160,16 @@ class QuestionType extends Type {
QuestionType(this.innerType) : super._();
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
Type? newInnerType =
innerType.closureWithRespectToUnknown(covariant: covariant);
if (newInnerType == null) return null;
if (newInnerType is QuestionType) return newInnerType;
if (newInnerType is StarType) return QuestionType(newInnerType.innerType);
return QuestionType(newInnerType);
}
@override
Type? recursivelyDemote({required bool covariant}) {
Type? newInnerType = innerType.recursivelyDemote(covariant: covariant);
@ -157,6 +196,30 @@ class RecordType extends Type {
required this.named,
}) : super._();
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
List<Type>? newPositional;
for (var i = 0; i < positional.length; i++) {
var newType =
positional[i].closureWithRespectToUnknown(covariant: covariant);
if (newType != null) {
newPositional ??= positional.toList();
newPositional[i] = newType;
}
}
Map<String, Type>? newNamed =
_closureWithRespectToUnknownNamed(covariant: covariant);
if (newPositional == null && newNamed == null) {
return null;
}
return RecordType(
positional: newPositional ?? positional,
named: newNamed ?? named,
);
}
@override
Type? recursivelyDemote({required bool covariant}) {
List<Type>? newPositional;
@ -179,6 +242,19 @@ class RecordType extends Type {
);
}
Map<String, Type>? _closureWithRespectToUnknownNamed(
{required bool covariant}) {
Map<String, Type> newNamed = {};
bool hasChanged = false;
for (var entry in named.entries) {
var value = entry.value;
var newType = value.closureWithRespectToUnknown(covariant: covariant);
if (newType != null) hasChanged = true;
newNamed[entry.key] = newType ?? value;
}
return hasChanged ? newNamed : null;
}
Map<String, Type>? _recursivelyDemoteNamed({required bool covariant}) {
Map<String, Type> newNamed = {};
bool hasChanged = false;
@ -216,6 +292,16 @@ class StarType extends Type {
StarType(this.innerType) : super._();
@override
Type? closureWithRespectToUnknown({required bool covariant}) {
Type? newInnerType =
innerType.closureWithRespectToUnknown(covariant: covariant);
if (newInnerType == null) return null;
if (newInnerType is StarType) return newInnerType;
if (newInnerType is QuestionType) return newInnerType;
return StarType(newInnerType);
}
@override
Type? recursivelyDemote({required bool covariant}) {
Type? newInnerType = innerType.recursivelyDemote(covariant: covariant);
@ -273,6 +359,13 @@ abstract class Type {
return other is Type && this.type == other.type;
}
/// Finds the nearest type that doesn't involve the unknown type (`_`).
///
/// If [covariant] is `true`, a supertype will be returned (replacing `_` with
/// `Object?`); otherwise a subtype will be returned (replacing `_` with
/// `Never`).
Type? closureWithRespectToUnknown({required bool covariant});
/// Finds the nearest type that doesn't involve any type parameter promotion.
/// If `covariant` is `true`, a supertype will be returned (replacing promoted
/// type parameters with their unpromoted counterparts); otherwise a subtype
@ -768,6 +861,10 @@ class TypeSystem {
class UnknownType extends Type {
const UnknownType() : super._();
@override
Type closureWithRespectToUnknown({required bool covariant}) =>
covariant ? Type('Object?') : Type('Never');
@override
Type? recursivelyDemote({required bool covariant}) => null;
@ -997,6 +1094,23 @@ class _TypeParser {
}
extension on List<Type> {
/// Calls [Type.closureWithRespectToUnknown] to translate every list member
/// into a type that doesn't involve the unknown type (`_`). If no type would
/// be changed by this operation, returns `null`.
List<Type>? closureWithRespectToUnknown({required bool covariant}) {
List<Type>? newList;
for (int i = 0; i < length; i++) {
Type type = this[i];
Type? newType = type.closureWithRespectToUnknown(covariant: covariant);
if (newList == null) {
if (newType == null) continue;
newList = sublist(0, i);
}
newList.add(newType ?? type);
}
return newList;
}
/// Calls [Type.recursivelyDemote] to translate every list member into a type
/// that doesn't involve any type promotion. If no type would be changed by
/// this operation, returns `null`.

View file

@ -375,4 +375,202 @@ main() {
expect(Type('_').recursivelyDemote(covariant: false), isNull);
});
});
group('closureWithRespectToUnknown:', () {
test('UnknownType:', () {
expect(Type('_').closureWithRespectToUnknown(covariant: true)!.type,
'Object?');
expect(Type('_').closureWithRespectToUnknown(covariant: false)!.type,
'Never');
});
group('FunctionType:', () {
group('return type:', () {
test('unchanged', () {
expect(
Type('int Function()')
.closureWithRespectToUnknown(covariant: true),
isNull);
expect(
Type('int Function()')
.closureWithRespectToUnknown(covariant: false),
isNull);
});
test('covariant', () {
expect(
Type('_ Function()')
.closureWithRespectToUnknown(covariant: true)!
.type,
'Object? Function()');
});
test('contravariant', () {
expect(
Type('_ Function()')
.closureWithRespectToUnknown(covariant: false)!
.type,
'Never Function()');
});
});
group('positional parameters:', () {
test('unchanged', () {
expect(
Type('void Function(int, String)')
.closureWithRespectToUnknown(covariant: true),
isNull);
expect(
Type('void Function(int, String)')
.closureWithRespectToUnknown(covariant: false),
isNull);
});
test('covariant', () {
expect(
Type('void Function(_, String)')
.closureWithRespectToUnknown(covariant: true)!
.type,
'void Function(Never, String)');
});
test('contravariant', () {
expect(
Type('void Function(_, String)')
.closureWithRespectToUnknown(covariant: false)!
.type,
'void Function(Object?, String)');
});
});
});
group('NonFunctionType', () {
test('unchanged', () {
expect(
Type('int').closureWithRespectToUnknown(covariant: true), isNull);
expect(
Type('int').closureWithRespectToUnknown(covariant: false), isNull);
});
group('type parameters:', () {
test('unchanged', () {
expect(
Type('Map<int, String>')
.closureWithRespectToUnknown(covariant: true),
isNull);
expect(
Type('Map<int, String>')
.closureWithRespectToUnknown(covariant: false),
isNull);
});
test('covariant', () {
expect(
Type('Map<_, String>')
.closureWithRespectToUnknown(covariant: true)!
.type,
'Map<Object?, String>');
});
test('contravariant', () {
expect(
Type('Map<_, String>')
.closureWithRespectToUnknown(covariant: false)!
.type,
'Map<Never, String>');
});
});
});
group('QuestionType:', () {
test('unchanged', () {
expect(
Type('int?').closureWithRespectToUnknown(covariant: true), isNull);
expect(
Type('int?').closureWithRespectToUnknown(covariant: false), isNull);
});
test('covariant', () {
expect(Type('_?').closureWithRespectToUnknown(covariant: true)!.type,
'Object?');
});
test('contravariant', () {
// Note: we don't normalize `Never?` to `Null`.
expect(Type('_?').closureWithRespectToUnknown(covariant: false)!.type,
'Never?');
});
});
group('RecordType:', () {
test('unchanged', () {
expect(
Type('(int, {double a})')
.closureWithRespectToUnknown(covariant: true),
isNull);
expect(
Type('(int, {double a})')
.closureWithRespectToUnknown(covariant: false),
isNull);
});
group('changed:', () {
group('positional:', () {
test('covariant', () {
expect(
Type('(_, {double a})')
.closureWithRespectToUnknown(covariant: true)!
.type,
'(Object?, {double a})',
);
});
test('contravariant', () {
expect(
Type('(_, {double a})')
.closureWithRespectToUnknown(covariant: false)!
.type,
'(Never, {double a})',
);
});
});
group('named:', () {
test('covariant', () {
expect(
Type('(double, {_ a})')
.closureWithRespectToUnknown(covariant: true)!
.type,
'(double, {Object? a})',
);
});
test('contravariant', () {
expect(
Type('(double, {_ a})')
.closureWithRespectToUnknown(covariant: false)!
.type,
'(double, {Never a})',
);
});
});
});
});
group('StarType:', () {
test('unchanged', () {
expect(
Type('int*').closureWithRespectToUnknown(covariant: true), isNull);
expect(
Type('int*').closureWithRespectToUnknown(covariant: false), isNull);
});
test('covariant', () {
expect(Type('_*').closureWithRespectToUnknown(covariant: true)!.type,
'Object?');
});
test('contravariant', () {
expect(Type('_*').closureWithRespectToUnknown(covariant: false)!.type,
'Never*');
});
});
});
}

View file

@ -501,6 +501,87 @@ main() {
});
});
});
group('Inference update 3:', () {
void setupTypesForLub() {
// Class hierarchy:
// A
// /\
// / \
// B1<T> B2<T>
// | \ / |
// | \/ |
// | /\ |
// | / \ |
// C1<T> C2<T>
h.addSuperInterfaces('A', (_) => [Type('Object')]);
h.addSuperInterfaces('B1', (_) => [Type('A'), Type('Object')]);
h.addSuperInterfaces('B2', (_) => [Type('A'), Type('Object')]);
h.addSuperInterfaces(
'C1',
(args) => [
PrimaryType('B1', args: args),
PrimaryType('B2', args: args),
Type('A'),
Type('Object')
]);
h.addSuperInterfaces(
'C2',
(args) => [
PrimaryType('B1', args: args),
PrimaryType('B2', args: args),
Type('A'),
Type('Object')
]);
h.addLub('C1<Object?>', 'C2<Object?>', 'A');
h.addLub('C1<int>', 'C2<double>', 'A');
h.addLub('B2<Object?>', 'C1<Object?>', 'B2<Object?>');
}
test("Context used instead of LUB if LUB doesn't satisfy context", () {
setupTypesForLub();
h.run([
switchExpr(expr('int'), [
intLiteral(0).pattern.thenExpr(expr('C1<Object?>')),
wildcard().thenExpr(expr('C2<Object?>')),
]).checkType('B1<Object?>').inTypeSchema('B1<Object?>'),
]);
});
test('Context is converted to a type using greatest closure', () {
setupTypesForLub();
h.run([
switchExpr(expr('int'), [
intLiteral(0).pattern.thenExpr(expr('C1<int>')),
wildcard().thenExpr(expr('C2<double>')),
]).checkType('B1<Object?>').inTypeSchema('B1<_>'),
]);
});
test("Context not used if one of the branches doesn't satisfy context",
() {
setupTypesForLub();
h.run([
switchExpr(expr('int'), [
intLiteral(0).pattern.thenExpr(expr('C1<Object?>')),
wildcard().thenExpr(expr('B2<Object?>')),
]).checkType('B2<Object?>').inTypeSchema('B1<Object?>'),
]);
});
test(
"when disabled, LUB always used, even if it doesn't satisfy "
"context", () {
setupTypesForLub();
h.disableInferenceUpdate3();
h.run([
switchExpr(expr('int'), [
intLiteral(0).pattern.thenExpr(expr('C1<Object?>')),
wildcard().thenExpr(expr('C2<Object?>')),
]).checkType('A').inTypeSchema('B1<Object?>'),
]);
});
});
});
group('Map:', () {

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
@ -11,6 +12,7 @@ import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
@ -39,7 +41,8 @@ class AssignmentExpressionResolver {
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
void resolve(AssignmentExpressionImpl node) {
void resolve(AssignmentExpressionImpl node,
{required DartType? contextType}) {
var operator = node.operator.type;
var hasRead = operator != TokenType.EQ;
var isIfNull = operator == TokenType.QUESTION_QUESTION_EQ;
@ -97,7 +100,8 @@ class AssignmentExpressionResolver {
right = _resolver.popRewrite()!;
var whyNotPromoted = flow?.whyNotPromoted(right);
_resolveTypes(node, whyNotPromoted: whyNotPromoted);
_resolveTypes(node,
whyNotPromoted: whyNotPromoted, contextType: contextType);
if (flow != null) {
if (writeElement is PromotableElement) {
@ -254,7 +258,8 @@ class AssignmentExpressionResolver {
}
void _resolveTypes(AssignmentExpressionImpl node,
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted,
required DartType? contextType}) {
DartType assignedType;
var rightHandSide = node.rightHandSide;
@ -287,12 +292,38 @@ class AssignmentExpressionResolver {
DartType nodeType;
if (operator == TokenType.QUESTION_QUESTION_EQ) {
var leftType = node.readType!;
// The LHS value will be used only if it is non-null.
leftType = _typeSystem.promoteToNonNull(leftType);
nodeType = _typeSystem.leastUpperBound(leftType, assignedType);
// - An if-null assignment `E` of the form `lvalue ??= e` with context type
// `K` is analyzed as follows:
//
// - Let `T1` be the read type the lvalue.
var t1 = node.readType!;
// - Let `T2` be the type of `e` inferred with context type `T1`.
var t2 = assignedType;
// - Let `T` be `UP(NonNull(T1), T2)`.
var nonNullT1 = _typeSystem.promoteToNonNull(t1);
var t = _typeSystem.leastUpperBound(nonNullT1, t2);
// - Let `S` be the greatest closure of `K`.
var s = _typeSystem
.greatestClosureOfSchema(contextType ?? UnknownInferredType.instance);
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!_resolver.definingLibrary.featureSet
.isEnabled(Feature.inference_update_3)) {
nodeType = t;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (_typeSystem.isSubtypeOf(t, s)) {
nodeType = t;
} else
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of
// `E` is `S`.
if (_typeSystem.isSubtypeOf(nonNullT1, s) &&
_typeSystem.isSubtypeOf(t2, s)) {
nodeType = s;
} else
// - Otherwise, the type of `E` is `T`.
{
nodeType = t;
}
} else {
nodeType = assignedType;
}

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
@ -153,31 +154,65 @@ class BinaryExpressionResolver {
var right = node.rightOperand;
var flow = _resolver.flowAnalysis.flow;
// An if-null expression `E` of the form `e1 ?? e2` with context type `K` is
// analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K?`.
var leftContextType = contextType;
if (leftContextType != null) {
leftContextType = _typeSystem.makeNullable(leftContextType);
}
_resolver.analyzeExpression(left, leftContextType);
left = _resolver.popRewrite()!;
var leftType = left.typeOrThrow;
var t1 = left.typeOrThrow;
var rightContextType = contextType;
if (rightContextType == null ||
rightContextType is DynamicType ||
rightContextType is InvalidType ||
rightContextType is UnknownInferredType) {
rightContextType = leftType;
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If `K` is `_`, `J = T1`.
DartType j;
if (contextType == null ||
contextType is DynamicType ||
contextType is InvalidType ||
contextType is UnknownInferredType) {
j = t1;
} else
// - Otherwise, `J = K`.
{
j = contextType;
}
flow?.ifNullExpression_rightBegin(left, leftType);
_resolver.analyzeExpression(right, rightContextType);
flow?.ifNullExpression_rightBegin(left, t1);
_resolver.analyzeExpression(right, j);
right = _resolver.popRewrite()!;
flow?.ifNullExpression_end();
var t2 = right.typeOrThrow;
var rightType = right.typeOrThrow;
var promotedLeftType = _typeSystem.promoteToNonNull(leftType);
var staticType = _typeSystem.leastUpperBound(promotedLeftType, rightType);
// - Let `T` be `UP(NonNull(T1), T2)`.
var nonNullT1 = _typeSystem.promoteToNonNull(t1);
var t = _typeSystem.leastUpperBound(nonNullT1, t2);
// - Let `S` be the greatest closure of `K`.
var s = _typeSystem
.greatestClosureOfSchema(contextType ?? UnknownInferredType.instance);
DartType staticType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!_resolver.definingLibrary.featureSet
.isEnabled(Feature.inference_update_3)) {
staticType = t;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (_typeSystem.isSubtypeOf(t, s)) {
staticType = t;
} else
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of `E` is
// `S`.
if (_typeSystem.isSubtypeOf(nonNullT1, s) &&
_typeSystem.isSubtypeOf(t2, s)) {
staticType = s;
} else
// - Otherwise, the type of `E` is `T`.
{
staticType = t;
}
_inferenceHelper.recordStaticType(node, staticType);

View file

@ -482,6 +482,10 @@ class TypeSystemOperations
return typeSystem.greatestLowerBound(type1, type2);
}
@override
DartType greatestClosure(DartType schema) =>
typeSystem.greatestClosureOfSchema(schema);
@override
bool isAlwaysExhaustiveType(DartType type) {
return typeSystem.isAlwaysExhaustive(type);

View file

@ -355,7 +355,9 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
options = TypeAnalyzerOptions(
nullSafetyEnabled: true,
patternsEnabled:
definingLibrary.featureSet.isEnabled(Feature.patterns)) {
definingLibrary.featureSet.isEnabled(Feature.patterns),
inferenceUpdate3Enabled: definingLibrary.featureSet
.isEnabled(Feature.inference_update_3)) {
nullableDereferenceVerifier = NullableDereferenceVerifier(
typeSystem: typeSystem,
errorReporter: errorReporter,
@ -1829,7 +1831,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
void visitAssignmentExpression(AssignmentExpression node,
{DartType? contextType}) {
checkUnreachableNode(node);
_assignmentExpressionResolver.resolve(node as AssignmentExpressionImpl);
_assignmentExpressionResolver.resolve(node as AssignmentExpressionImpl,
contextType: contextType);
_insertImplicitCallReference(
insertGenericFunctionInstantiation(node, contextType: contextType),
contextType: contextType);
@ -2036,7 +2039,8 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
}
elseExpression = popRewrite()!;
typeAnalyzer.visitConditionalExpression(node as ConditionalExpressionImpl);
typeAnalyzer.visitConditionalExpression(node as ConditionalExpressionImpl,
contextType: contextType);
if (flow != null) {
flow.conditional_end(
node, node.typeOrThrow, elseExpression, elseExpression.typeOrThrow);

View file

@ -2,11 +2,13 @@
// 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.
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/invocation_inference_helper.dart';
import 'package:analyzer/src/generated/resolver.dart';
@ -83,16 +85,38 @@ class StaticTypeAnalyzer {
_inferenceHelper.recordStaticType(node, node.target.typeOrThrow);
}
/// The Dart Language Specification, 12.19: <blockquote> ... a conditional expression <i>c</i> of
/// the form <i>e<sub>1</sub> ? e<sub>2</sub> : e<sub>3</sub></i> ...
///
/// It is a static type warning if the type of e<sub>1</sub> may not be assigned to `bool`.
///
/// The static type of <i>c</i> is the least upper bound of the static type of <i>e<sub>2</sub></i>
/// and the static type of <i>e<sub>3</sub></i>.</blockquote>
void visitConditionalExpression(covariant ConditionalExpressionImpl node) {
DartType staticType = _typeSystem.leastUpperBound(
node.thenExpression.typeOrThrow, node.elseExpression.typeOrThrow);
void visitConditionalExpression(covariant ConditionalExpressionImpl node,
{required DartType? contextType}) {
// A conditional expression `E` of the form `b ? e1 : e2` with context type
// `K` is analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K`
var t1 = node.thenExpression.typeOrThrow;
// - Let `T2` be the type of `e2` inferred with context type `K`
var t2 = node.elseExpression.typeOrThrow;
// - Let `T` be `UP(T1, T2)`
var t = _typeSystem.leastUpperBound(t1, t2);
// - Let `S` be the greatest closure of `K`
var s = _typeSystem
.greatestClosureOfSchema(contextType ?? UnknownInferredType.instance);
DartType staticType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!_resolver.definingLibrary.featureSet
.isEnabled(Feature.inference_update_3)) {
staticType = t;
} else
// - If `T <: S` then the type of `E` is `T`
if (_typeSystem.isSubtypeOf(t, s)) {
staticType = t;
} else
// - Otherwise, if `T1 <: S` and `T2 <: S`, then the type of `E` is `S`
if (_typeSystem.isSubtypeOf(t1, s) && _typeSystem.isSubtypeOf(t2, s)) {
staticType = s;
} else
// - Otherwise, the type of `E` is `T`
{
staticType = t;
}
_inferenceHelper.recordStaticType(node, staticType);
}

View file

@ -2,15 +2,19 @@
// 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.
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
import 'node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AssignmentExpressionResolutionTest);
defineReflectiveTests(InferenceUpdate3Test);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@ -313,6 +317,36 @@ AssignmentExpression
''');
}
test_ifNull_lubUsedEvenIfItDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
f(Object? o1, Object? o2, List<num> listNum) {
if (o1 is Iterable<int>? && o2 is Iterable<num>) {
o2 = (o1 ??= listNum);
}
}
''');
assertResolvedNodeText(findNode.assignment('o1 ??= listNum'), r'''
AssignmentExpression
leftHandSide: SimpleIdentifier
token: o1
staticElement: self::@function::f::@parameter::o1
staticType: null
operator: ??=
rightHandSide: SimpleIdentifier
token: listNum
parameter: <null>
staticElement: self::@function::f::@parameter::listNum
staticType: List<num>
readElement: self::@function::f::@parameter::o1
readType: Iterable<int>?
writeElement: self::@function::f::@parameter::o1
writeType: Object?
staticElement: <null>
staticType: Object
''');
}
test_importPrefix_deferred_topLevelVariable_simple() async {
newFile('$testPackageLibPath/a.dart', '''
var v = 0;
@ -5050,3 +5084,116 @@ AssignmentExpression
''');
}
}
@reflectiveTest
class InferenceUpdate3Test extends PubPackageResolutionTest {
@override
List<String> get experiments =>
super.experiments..add(EnableString.inference_update_3);
test_ifNull_contextIsConvertedToATypeUsingGreatestClosure() async {
await assertNoErrorsInCode('''
class A {}
class B1<T> extends A {}
class B2<T> extends A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
void contextB1<T>(B1<T> b1) {}
f(Object? o, C2<double> c2) {
if (o is C1<int>?) {
contextB1(o ??= c2);
}
}
''');
assertResolvedNodeText(
findNode.assignment('o ??= c2'), r'''AssignmentExpression
leftHandSide: SimpleIdentifier
token: o
staticElement: self::@function::f::@parameter::o
staticType: null
operator: ??=
rightHandSide: SimpleIdentifier
token: c2
parameter: <null>
staticElement: self::@function::f::@parameter::c2
staticType: C2<double>
parameter: ParameterMember
base: root::@parameter::b1
substitution: {T: Object?}
readElement: self::@function::f::@parameter::o
readType: C1<int>?
writeElement: self::@function::f::@parameter::o
writeType: Object?
staticElement: <null>
staticType: B1<Object?>
''');
}
test_ifNull_contextNotUsedIfLhsDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
f(Object? o1, Object? o2, int? i) {
if (o1 is int? && o2 is double?) {
o1 = (o2 ??= i);
}
}
''');
assertResolvedNodeText(
findNode.assignment('o2 ??= i'), r'''AssignmentExpression
leftHandSide: SimpleIdentifier
token: o2
staticElement: self::@function::f::@parameter::o2
staticType: null
operator: ??=
rightHandSide: SimpleIdentifier
token: i
parameter: <null>
staticElement: self::@function::f::@parameter::i
staticType: int?
readElement: self::@function::f::@parameter::o2
readType: double?
writeElement: self::@function::f::@parameter::o2
writeType: Object?
staticElement: <null>
staticType: num?
''');
}
test_ifNull_contextUsedInsteadOfLubIfLubDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
void contextB1(B1 b1) {}
f(Object? o, C2 c2) {
if (o is C1?) {
contextB1(o ??= c2);
}
}
''');
assertResolvedNodeText(findNode.assignment('o ??= c2'), r'''
AssignmentExpression
leftHandSide: SimpleIdentifier
token: o
staticElement: self::@function::f::@parameter::o
staticType: null
operator: ??=
rightHandSide: SimpleIdentifier
token: c2
parameter: <null>
staticElement: self::@function::f::@parameter::c2
staticType: C2
parameter: self::@function::contextB1::@parameter::b1
readElement: self::@function::f::@parameter::o
readType: C1?
writeElement: self::@function::f::@parameter::o
writeType: Object?
staticElement: <null>
staticType: B1
''');
}
}

View file

@ -2,15 +2,19 @@
// 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.
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
import 'node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(BinaryExpressionResolutionTest);
defineReflectiveTests(InferenceUpdate3Test);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@ -260,6 +264,39 @@ BinaryExpression
''');
}
test_ifNull_lubUsedEvenIfItDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(C1? c1, C2 c2, Object? o) {
if (o is B1) {
o = c1 ?? c2;
}
}
''');
assertResolvedNodeText(findNode.binary('c1 ?? c2'), r'''
BinaryExpression
leftOperand: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1?
operator: ??
rightOperand: SimpleIdentifier
token: c2
parameter: <null>
staticElement: self::@function::f::@parameter::c2
staticType: C2
parameter: <null>
staticElement: <null>
staticInvokeType: null
staticType: A
''');
}
test_ifNull_nullableInt_int() async {
await assertNoErrorsInCode(r'''
void f(int? x, int y) {
@ -1934,3 +1971,135 @@ BinaryExpression
''');
}
}
@reflectiveTest
class InferenceUpdate3Test extends PubPackageResolutionTest {
@override
List<String> get experiments =>
super.experiments..add(EnableString.inference_update_3);
test_ifNull_contextIsConvertedToATypeUsingGreatestClosure() async {
await assertNoErrorsInCode('''
class A {}
class B1<T> extends A {}
class B2<T> extends A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
void contextB1<T>(B1<T> b1) {}
f(C1<int>? c1, C2<double> c2) {
contextB1(c1 ?? c2);
}
''');
assertResolvedNodeText(findNode.binary('c1 ?? c2'), r'''BinaryExpression
leftOperand: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1<int>?
operator: ??
rightOperand: SimpleIdentifier
token: c2
parameter: <null>
staticElement: self::@function::f::@parameter::c2
staticType: C2<double>
parameter: ParameterMember
base: root::@parameter::b1
substitution: {T: Object?}
staticElement: <null>
staticInvokeType: null
staticType: B1<Object?>
''');
}
test_ifNull_contextNotUsedIfLhsDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(B2? b2, C1 c1, Object? o) {
if (o is B1) {
o = b2 ?? c1;
}
}
''');
assertResolvedNodeText(findNode.binary('b2 ?? c1'), r'''BinaryExpression
leftOperand: SimpleIdentifier
token: b2
staticElement: self::@function::f::@parameter::b2
staticType: B2?
operator: ??
rightOperand: SimpleIdentifier
token: c1
parameter: <null>
staticElement: self::@function::f::@parameter::c1
staticType: C1
parameter: <null>
staticElement: <null>
staticInvokeType: null
staticType: B2
''');
}
test_ifNull_contextNotUsedIfRhsDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(C1? c1, B2 b2, Object? o) {
if (o is B1) {
o = c1 ?? b2;
}
}
''');
assertResolvedNodeText(findNode.binary('c1 ?? b2'), r'''BinaryExpression
leftOperand: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1?
operator: ??
rightOperand: SimpleIdentifier
token: b2
parameter: <null>
staticElement: self::@function::f::@parameter::b2
staticType: B2
parameter: <null>
staticElement: <null>
staticInvokeType: null
staticType: B2
''');
}
test_ifNull_contextUsedInsteadOfLubIfLubDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
B1 f(C1? c1, C2 c2) => c1 ?? c2;
''');
assertResolvedNodeText(findNode.binary('c1 ?? c2'), r'''
BinaryExpression
leftOperand: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1?
operator: ??
rightOperand: SimpleIdentifier
token: c2
parameter: <null>
staticElement: self::@function::f::@parameter::c2
staticType: C2
staticElement: <null>
staticInvokeType: null
staticType: B1
''');
}
}

View file

@ -2,15 +2,19 @@
// 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.
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'context_collection_resolution.dart';
import 'node_text_expectations.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ConditionalExpressionResolutionTest);
defineReflectiveTests(InferenceUpdate3Test);
defineReflectiveTests(UpdateNodeTextExpectations);
});
}
@ -101,6 +105,41 @@ ConditionalExpression
''');
}
test_ifNull_lubUsedEvenIfItDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(bool b, C1 c1, C2 c2, Object? o) {
if (o is B1) {
o = b ? c1 : c2;
}
}
''');
assertResolvedNodeText(findNode.conditionalExpression('b ? c1 : c2'), r'''
ConditionalExpression
condition: SimpleIdentifier
token: b
staticElement: self::@function::f::@parameter::b
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1
colon: :
elseExpression: SimpleIdentifier
token: c2
staticElement: self::@function::f::@parameter::c2
staticType: C2
parameter: <null>
staticType: A
''');
}
test_issue49692() async {
await assertErrorsInCode('''
T f<T>(T t, bool b) {
@ -283,3 +322,146 @@ void f(bool a, int b, int c) {
assertType(findNode.simple('d)'), 'int');
}
}
@reflectiveTest
class InferenceUpdate3Test extends PubPackageResolutionTest {
@override
List<String> get experiments =>
super.experiments..add(EnableString.inference_update_3);
test_contextIsConvertedToATypeUsingGreatestClosure() async {
await assertNoErrorsInCode('''
class A {}
class B1<T> extends A {}
class B2<T> extends A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
void contextB1<T>(B1<T> b1) {}
f(bool b, C1<int> c1, C2<double> c2) {
contextB1(b ? c1 : c2);
}
''');
assertResolvedNodeText(
findNode.conditionalExpression('b ? c1 : c2'), r'''ConditionalExpression
condition: SimpleIdentifier
token: b
staticElement: self::@function::f::@parameter::b
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1<int>
colon: :
elseExpression: SimpleIdentifier
token: c2
staticElement: self::@function::f::@parameter::c2
staticType: C2<double>
parameter: ParameterMember
base: root::@parameter::b1
substitution: {T: Object?}
staticType: B1<Object?>
''');
}
test_contextNotUsedIfLhsDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(bool b, B2 b2, C1 c1, Object? o) {
if (o is B1) {
o = b ? b2 : c1;
}
}
''');
assertResolvedNodeText(
findNode.conditionalExpression('b ? b2 : c1'), r'''ConditionalExpression
condition: SimpleIdentifier
token: b
staticElement: self::@function::f::@parameter::b
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: b2
staticElement: self::@function::f::@parameter::b2
staticType: B2
colon: :
elseExpression: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1
parameter: <null>
staticType: B2
''');
}
test_contextNotUsedIfRhsDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
f(bool b, C1 c1, B2 b2, Object? o) {
if (o is B1) {
o = b ? c1 : b2;
}
}
''');
assertResolvedNodeText(
findNode.conditionalExpression('b ? c1 : b2'), r'''ConditionalExpression
condition: SimpleIdentifier
token: b
staticElement: self::@function::f::@parameter::b
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1
colon: :
elseExpression: SimpleIdentifier
token: b2
staticElement: self::@function::f::@parameter::b2
staticType: B2
parameter: <null>
staticType: B2
''');
}
test_contextUsedInsteadOfLubIfLubDoesNotSatisfyContext() async {
await assertNoErrorsInCode('''
class A {}
class B1 extends A {}
class B2 extends A {}
class C1 implements B1, B2 {}
class C2 implements B1, B2 {}
B1 f(bool b, C1 c1, C2 c2) => b ? c1 : c2;
''');
assertResolvedNodeText(findNode.conditionalExpression('b ? c1 : c2'), r'''
ConditionalExpression
condition: SimpleIdentifier
token: b
staticElement: self::@function::f::@parameter::b
staticType: bool
question: ?
thenExpression: SimpleIdentifier
token: c1
staticElement: self::@function::f::@parameter::c1
staticType: C1
colon: :
elseExpression: SimpleIdentifier
token: c2
staticElement: self::@function::f::@parameter::c2
staticType: C2
staticType: B1
''');
}
}

View file

@ -169,7 +169,9 @@ class InferenceVisitorImpl extends InferenceVisitorBase
: options = new TypeAnalyzerOptions(
nullSafetyEnabled: inferrer.libraryBuilder.isNonNullableByDefault,
patternsEnabled:
inferrer.libraryBuilder.libraryFeatures.patterns.isEnabled),
inferrer.libraryBuilder.libraryFeatures.patterns.isEnabled,
inferenceUpdate3Enabled: inferrer
.libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled),
super(inferrer, helper);
@override
@ -1015,10 +1017,18 @@ class InferenceVisitorImpl extends InferenceVisitorBase
node.condition = condition..parent = node;
flowAnalysis.conditional_thenBegin(node.condition, node);
bool isThenReachable = flowAnalysis.isReachable;
// A conditional expression `E` of the form `b ? e1 : e2` with context
// type `K` is analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K`
ExpressionInferenceResult thenResult =
inferExpression(node.then, typeContext, isVoidAllowed: true);
node.then = thenResult.expression..parent = node;
registerIfUnreachableForTesting(node.then, isReachable: isThenReachable);
DartType t1 = thenResult.inferredType;
// - Let `T2` be the type of `e2` inferred with context type `K`
flowAnalysis.conditional_elseBegin(node.then, thenResult.inferredType);
bool isOtherwiseReachable = flowAnalysis.isReachable;
ExpressionInferenceResult otherwiseResult =
@ -1026,9 +1036,37 @@ class InferenceVisitorImpl extends InferenceVisitorBase
node.otherwise = otherwiseResult.expression..parent = node;
registerIfUnreachableForTesting(node.otherwise,
isReachable: isOtherwiseReachable);
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
thenResult.inferredType, otherwiseResult.inferredType,
DartType t2 = otherwiseResult.inferredType;
// - Let `T` be `UP(T1, T2)`
DartType t = typeSchemaEnvironment.getStandardUpperBound(t1, t2,
isNonNullableByDefault: isNonNullableByDefault);
// - Let `S` be the greatest closure of `K`
DartType s = computeGreatestClosure(typeContext);
DartType inferredType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled) {
inferredType = t;
} else
// - If `T <: S` then the type of `E` is `T`
if (typeSchemaEnvironment.isSubtypeOf(
t, s, SubtypeCheckMode.withNullabilities)) {
inferredType = t;
} else
// - Otherwise, if `T1 <: S` and `T2 <: S`, then the type of `E` is `S`
if (typeSchemaEnvironment.isSubtypeOf(
t1, s, SubtypeCheckMode.withNullabilities) &&
typeSchemaEnvironment.isSubtypeOf(
t2, s, SubtypeCheckMode.withNullabilities)) {
inferredType = s;
} else
// - Otherwise, the type of `E` is `T`
{
inferredType = t;
}
flowAnalysis.conditional_end(
node, inferredType, node.otherwise, otherwiseResult.inferredType);
node.staticType = inferredType;
@ -1872,48 +1910,78 @@ class InferenceVisitorImpl extends InferenceVisitorBase
ExpressionInferenceResult visitIfNullExpression(
IfNullExpression node, DartType typeContext) {
// To infer `e0 ?? e1` in context `K`:
// - Infer `e0` in context `K?` to get `T0`
// An if-null expression `E` of the form `e1 ?? e2` with context type `K` is
// analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K?`.
ExpressionInferenceResult lhsResult = inferExpression(
node.left, computeNullable(typeContext),
isVoidAllowed: false);
DartType t1 = lhsResult.inferredType;
reportNonNullableInNullAwareWarningIfNeeded(
lhsResult.inferredType, "??", lhsResult.expression.fileOffset);
t1, "??", lhsResult.expression.fileOffset);
// This ends any shorting in `node.left`.
Expression left = lhsResult.expression;
flowAnalysis.ifNullExpression_rightBegin(node.left, lhsResult.inferredType);
flowAnalysis.ifNullExpression_rightBegin(node.left, t1);
// - Let `J = T0` if `K` is `_`, otherwise `K`.
// - Infer `e1` in context `J` to get `T1`
ExpressionInferenceResult rhsResult;
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If `K` is `_`, `J = T1`.
DartType j;
if (typeContext is UnknownType) {
rhsResult = inferExpression(node.right, lhsResult.inferredType,
isVoidAllowed: true);
} else {
rhsResult = inferExpression(node.right, typeContext, isVoidAllowed: true);
j = t1;
} else
// - Otherwise, `J = K`.
{
j = typeContext;
}
ExpressionInferenceResult rhsResult =
inferExpression(node.right, j, isVoidAllowed: true);
DartType t2 = rhsResult.inferredType;
flowAnalysis.ifNullExpression_end();
// - Then the inferred type is UP(NonNull(T0), T1).
DartType originalLhsType = lhsResult.inferredType;
DartType nonNullableLhsType = originalLhsType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableLhsType, rhsResult.inferredType,
// - Let `T` be `UP(NonNull(T1), T2)`.
DartType nonNullT1 = t1.toNonNull();
DartType t = typeSchemaEnvironment.getStandardUpperBound(nonNullT1, t2,
isNonNullableByDefault: isNonNullableByDefault);
// - Let `S` be the greatest closure of `K`.
DartType s = computeGreatestClosure(typeContext);
DartType inferredType;
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled) {
inferredType = t;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (typeSchemaEnvironment.isSubtypeOf(
t, s, SubtypeCheckMode.withNullabilities)) {
inferredType = t;
} else
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of `E` is
// `S`.
if (typeSchemaEnvironment.isSubtypeOf(
nonNullT1, s, SubtypeCheckMode.withNullabilities) &&
typeSchemaEnvironment.isSubtypeOf(
t2, s, SubtypeCheckMode.withNullabilities)) {
inferredType = s;
} else
// - Otherwise, the type of `E` is `T`.
{
inferredType = t;
}
Expression replacement;
if (left is ThisExpression) {
replacement = left;
} else {
VariableDeclaration variable =
createVariable(left, lhsResult.inferredType);
VariableDeclaration variable = createVariable(left, t1);
Expression equalsNull = createEqualsNull(createVariableGet(variable),
fileOffset: lhsResult.expression.fileOffset);
VariableGet variableGet = createVariableGet(variable);
if (isNonNullableByDefault &&
!identical(nonNullableLhsType, originalLhsType)) {
variableGet.promotedType = nonNullableLhsType;
if (isNonNullableByDefault && !identical(nonNullT1, t1)) {
variableGet.promotedType = nonNullT1;
}
ConditionalExpression conditional = new ConditionalExpression(
equalsNull, rhsResult.expression, variableGet, inferredType)
@ -5165,9 +5233,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
Expression write = writeResult.expression;
DartType nonNullableReadType = readType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, writeType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: writeType,
typeContext: typeContext);
Expression replacement;
if (node.forEffect) {
@ -5207,6 +5276,43 @@ class InferenceVisitorImpl extends InferenceVisitorBase
inferredType, replacement, nullAwareGuards);
}
DartType _analyzeIfNullTypes(
{required DartType nonNullableReadType,
required DartType rhsType,
required DartType typeContext}) {
// - An if-null assignment `E` of the form `lvalue ??= e` with context type
// `K` is analyzed as follows:
//
// - Let `T1` be the read type the lvalue.
// - Let `T2` be the type of `e` inferred with context type `T1`.
DartType t2 = rhsType;
// - Let `T` be `UP(NonNull(T1), T2)`.
DartType nonNullT1 = nonNullableReadType;
DartType t = typeSchemaEnvironment.getStandardUpperBound(nonNullT1, t2,
isNonNullableByDefault: isNonNullableByDefault);
// - Let `S` be the greatest closure of `K`.
DartType s = computeGreatestClosure(typeContext);
// If `inferenceUpdate3` is not enabled, then the type of `E` is `T`.
if (!libraryBuilder.libraryFeatures.inferenceUpdate3.isEnabled) {
return t;
} else
// - If `T <: S`, then the type of `E` is `T`.
if (typeSchemaEnvironment.isSubtypeOf(
t, s, SubtypeCheckMode.withNullabilities)) {
return t;
}
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of
// `E` is `S`.
if (typeSchemaEnvironment.isSubtypeOf(
nonNullT1, s, SubtypeCheckMode.withNullabilities) &&
typeSchemaEnvironment.isSubtypeOf(
t2, s, SubtypeCheckMode.withNullabilities)) {
return s;
}
// - Otherwise, the type of `E` is `T`.
return t;
}
ExpressionInferenceResult visitIfNullSet(
IfNullSet node, DartType typeContext) {
ExpressionInferenceResult readResult =
@ -5225,9 +5331,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
DartType originalReadType = readType;
DartType nonNullableReadType = originalReadType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, writeResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: writeResult.inferredType,
typeContext: typeContext);
Expression replacement;
if (node.forEffect) {
@ -5599,9 +5706,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, valueResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: valueResult.inferredType,
typeContext: typeContext);
VariableDeclaration? valueVariable;
Expression? returnedValue;
@ -5768,9 +5876,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, valueResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: valueResult.inferredType,
typeContext: typeContext);
VariableDeclaration? valueVariable;
Expression? returnedValue;
@ -5928,9 +6037,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, valueResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: valueResult.inferredType,
typeContext: typeContext);
VariableDeclaration? valueVariable;
Expression? returnedValue;
@ -7625,9 +7735,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
DartType inferredType = typeSchemaEnvironment.getStandardUpperBound(
nonNullableReadType, valueResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault);
DartType inferredType = _analyzeIfNullTypes(
nonNullableReadType: nonNullableReadType,
rhsType: valueResult.inferredType,
typeContext: typeContext);
Expression replacement;
if (node.forEffect) {

View file

@ -32,6 +32,7 @@ import '../source/source_library_builder.dart'
import 'factor_type.dart';
import 'type_inferrer.dart';
import 'type_schema.dart';
import 'type_schema_elimination.dart' as type_schema_elimination;
import 'type_schema_environment.dart' show TypeSchemaEnvironment;
/// Visitor to check whether a given type mentions any of a class's type
@ -572,6 +573,11 @@ class OperationsCfe
return factorType(typeEnvironment, from, what);
}
@override
DartType greatestClosure(DartType schema) =>
type_schema_elimination.greatestClosure(
schema, const DynamicType(), const NeverType.nonNullable());
@override
bool isAlwaysExhaustiveType(DartType type) {
return computeIsAlwaysExhaustiveType(type, typeEnvironment.coreTypes);

View file

@ -534,6 +534,7 @@ ef
effects
efficient
efficiently
ei
eight
eighth
elected

View file

@ -423,6 +423,7 @@ incrementally
increments
indention
indents
indexable
ing
inhibit
inlinable

View file

@ -0,0 +1,39 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using conditional expressions.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
test(bool b) {
var c1Int = C1<int>();
var c2Double = C2<double>();
contextB1(b ? c1Int : c2Double);
var iterableInt = <int>[] as Iterable<int>;
var listNum = <num>[];
contextIterable<num>(b ? iterableInt : listNum);
}
main() {
test(true);
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method test(core::bool b) → dynamic {
self::C1<core::int> c1Int = new self::C1::•<core::int>();
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(b ?{self::B1<dynamic>} c1Int : c2Double);
core::Iterable<core::int> iterableInt = <core::int>[] as core::Iterable<core::int>;
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(b ?{core::Iterable<core::num>} iterableInt : listNum);
}
static method main() → dynamic {
self::test(true);
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method test(core::bool b) → dynamic {
self::C1<core::int> c1Int = new self::C1::•<core::int>();
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(b ?{self::B1<dynamic>} c1Int : c2Double);
core::Iterable<core::int> iterableInt = core::_GrowableList::•<core::int>(0) as{Unchecked} core::Iterable<core::int>;
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(b ?{core::Iterable<core::num>} iterableInt : listNum);
}
static method main() → dynamic {
self::test(true);
}

View file

@ -0,0 +1,17 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
test(bool b) {}
main() {}

View file

@ -0,0 +1,17 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
main() {}
test(bool b) {}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method test(core::bool b) → dynamic {
self::C1<core::int> c1Int = new self::C1::•<core::int>();
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(b ?{self::B1<dynamic>} c1Int : c2Double);
core::Iterable<core::int> iterableInt = <core::int>[] as core::Iterable<core::int>;
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(b ?{core::Iterable<core::num>} iterableInt : listNum);
}
static method main() → dynamic {
self::test(true);
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method test(core::bool b) → dynamic {
self::C1<core::int> c1Int = new self::C1::•<core::int>();
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(b ?{self::B1<dynamic>} c1Int : c2Double);
core::Iterable<core::int> iterableInt = <core::int>[] as core::Iterable<core::int>;
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(b ?{core::Iterable<core::num>} iterableInt : listNum);
}
static method main() → dynamic {
self::test(true);
}

View file

@ -0,0 +1,32 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static method test(core::bool b) → dynamic
;
static method main() → dynamic
;

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method test(core::bool b) → dynamic {
self::C1<core::int> c1Int = new self::C1::•<core::int>();
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(b ?{self::B1<dynamic>} c1Int : c2Double);
core::Iterable<core::int> iterableInt = core::_GrowableList::•<core::int>(0) as{Unchecked} core::Iterable<core::int>;
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(b ?{core::Iterable<core::num>} iterableInt : listNum);
}
static method main() → dynamic {
self::test(true);
}

View file

@ -0,0 +1 @@
--enable-experiment=inference-update-3

View file

@ -0,0 +1,48 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is an ordinary index expression that
// refers to operators defined in an extension, using null aware extension
// syntax.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
class Indexable<ReadType, WriteType> {
final ReadType _value;
Indexable(this._value);
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
main() {
var c2Double = C2<double>();
contextB1(Extension(Indexable<C1<int>?, Object?>(null))[0] ??= c2Double);
var listNum = <num>[];
contextIterable<num>(
Extension(Indexable<Iterable<int>?, Object?>(null))[0] ??= listNum);
}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?> #t1 = new self::Indexable::•<self::C1<core::int>?, core::Object?>(null) in let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1, #t2) in #t3 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?> #t6 = new self::Indexable::•<core::Iterable<core::int>?, core::Object?>(null) in let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6, #t7) in #t8 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,60 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?> #t1 = new self::Indexable::•<self::C1<core::int>?, core::Object?>(null) in let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1, #t2) in #t3 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?> #t6 = new self::Indexable::•<core::Iterable<core::int>?, core::Object?>(null) in let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6, #t7) in #t8 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:43:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:43:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:47:59 -> IntConstant(0)
Extra constant evaluation: evaluated: 47, effectively constant: 4

View file

@ -0,0 +1,25 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
class Indexable<ReadType, WriteType> {
final ReadType _value;
Indexable(this._value);
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
main() {}

View file

@ -0,0 +1,25 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
class Indexable<ReadType, WriteType> {
Indexable(this._value);
final ReadType _value;
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
main() {}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?> #t1 = new self::Indexable::•<self::C1<core::int>?, core::Object?>(null) in let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1, #t2) in #t3 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?> #t6 = new self::Indexable::•<core::Iterable<core::int>?, core::Object?>(null) in let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6, #t7) in #t8 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?> #t1 = new self::Indexable::•<self::C1<core::int>?, core::Object?>(null) in let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1, #t2) in #t3 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?> #t6 = new self::Indexable::•<core::Iterable<core::int>?, core::Object?>(null) in let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6, #t7) in #t8 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,43 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
;
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,60 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?> #t1 = new self::Indexable::•<self::C1<core::int>?, core::Object?>(null) in let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1, #t2) in #t3 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?> #t6 = new self::Indexable::•<core::Iterable<core::int>?, core::Object?>(null) in let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6, #t7) in #t8 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:43:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:43:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_index_expression_test.dart:47:59 -> IntConstant(0)
Extra constant evaluation: evaluated: 47, effectively constant: 4

View file

@ -0,0 +1,52 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a null-aware index expression that
// refers to operators defined in an extension, using explicit extension syntax.
/// Ensures a context type of `Iterable<T>?` for the operand, or `Iterable<_>?`
/// if no type argument is supplied.
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>?` for the operand, or `B1<_>?` if no type
/// argument is supplied.
B1<T>? contextB1Question<T>(B1<T>? x) => x;
class Indexable<ReadType, WriteType> {
final ReadType _value;
Indexable(this._value);
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
ReadType value) =>
Indexable<ReadType, WriteType>(value);
main() {
var c2Double = C2<double>();
contextB1Question(
Extension(maybeIndexable<C1<int>?, Object?>(null))?[0] ??= c2Double);
var listNum = <num>[];
contextIterableQuestion<num>(
Extension(maybeIndexable<Iterable<int>?, Object?>(null))?[0] ??= listNum);
}

View file

@ -0,0 +1,54 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
return new self::Indexable::•<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>(value);
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?>? #t1 = self::maybeIndexable<self::C1<core::int>?, core::Object?>(null) in #t1 == null ?{self::B1<dynamic>?} null : let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2) in #t3 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?>? #t6 = self::maybeIndexable<core::Iterable<core::int>?, core::Object?>(null) in #t6 == null ?{core::Iterable<core::num>?} null : let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7) in #t8 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,62 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
return new self::Indexable::•<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>(value);
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?>? #t1 = self::maybeIndexable<self::C1<core::int>?, core::Object?>(null) in #t1 == null ?{self::B1<dynamic>?} null : let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2) in #t3 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterableQuestion<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?>? #t6 = self::maybeIndexable<core::Iterable<core::int>?, core::Object?>(null) in #t6 == null ?{core::Iterable<core::num>?} null : let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7) in #t8 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:51:65 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:51:65 -> IntConstant(0)
Extra constant evaluation: evaluated: 55, effectively constant: 4

View file

@ -0,0 +1,29 @@
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T>? contextB1Question<T>(B1<T>? x) => x;
class Indexable<ReadType, WriteType> {
final ReadType _value;
Indexable(this._value);
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
ReadType value) =>
Indexable<ReadType, WriteType>(value);
main() {}

View file

@ -0,0 +1,29 @@
B1<T>? contextB1Question<T>(B1<T>? x) => x;
Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
ReadType value) =>
Indexable<ReadType, WriteType>(value);
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
class Indexable<ReadType, WriteType> {
Indexable(this._value);
final ReadType _value;
}
extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
ReadType operator [](int index) => _value;
operator []=(int index, WriteType value) {}
}
main() {}

View file

@ -0,0 +1,54 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
return new self::Indexable::•<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>(value);
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?>? #t1 = self::maybeIndexable<self::C1<core::int>?, core::Object?>(null) in #t1 == null ?{self::B1<dynamic>?} null : let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2) in #t3 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?>? #t6 = self::maybeIndexable<core::Iterable<core::int>?, core::Object?>(null) in #t6 == null ?{core::Iterable<core::num>?} null : let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7) in #t8 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,54 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
return new self::Indexable::•<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>(value);
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?>? #t1 = self::maybeIndexable<self::C1<core::int>?, core::Object?>(null) in #t1 == null ?{self::B1<dynamic>?} null : let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2) in #t3 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?>? #t6 = self::maybeIndexable<core::Iterable<core::int>?, core::Object?>(null) in #t6 == null ?{core::Iterable<core::num>?} null : let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7) in #t8 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}

View file

@ -0,0 +1,45 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
;
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void
;
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
;
static method main() → dynamic
;

View file

@ -0,0 +1,62 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Indexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> extends core::Object {
final field self::Indexable::ReadType% _value;
constructor •(self::Indexable::ReadType% _value) → self::Indexable<self::Indexable::ReadType%, self::Indexable::WriteType%>
: self::Indexable::_value = _value, super core::Object::•()
;
}
extension Extension<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic> on self::Indexable<ReadType%, WriteType%> {
operator [] = self::Extension|[];
operator []= = self::Extension|[]=;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|[]<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]::ReadType%, self::Extension|[]::WriteType%> #this, core::int index) → self::Extension|[]::ReadType%
return #this.{self::Indexable::_value}{self::Extension|[]::ReadType%};
static extension-member method Extension|[]=<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(lowered final self::Indexable<self::Extension|[]=::ReadType%, self::Extension|[]=::WriteType%> #this, core::int index, self::Extension|[]=::WriteType% value) → void {}
static method maybeIndexable<ReadType extends core::Object? = dynamic, WriteType extends core::Object? = dynamic>(self::maybeIndexable::ReadType% value) → self::Indexable<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>?
return new self::Indexable::•<self::maybeIndexable::ReadType%, self::maybeIndexable::WriteType%>(value);
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final self::Indexable<self::C1<core::int>?, core::Object?>? #t1 = self::maybeIndexable<self::C1<core::int>?, core::Object?>(null) in #t1 == null ?{self::B1<dynamic>?} null : let final core::int #t2 = 0 in let final self::C1<core::int>? #t3 = self::Extension|[]<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2) in #t3 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t4 = c2Double in let final void #t5 = self::Extension|[]=<self::C1<core::int>?, core::Object?>(#t1{self::Indexable<self::C1<core::int>?, core::Object?>}, #t2, #t4) in #t4 : #t3{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterableQuestion<core::num>(let final self::Indexable<core::Iterable<core::int>?, core::Object?>? #t6 = self::maybeIndexable<core::Iterable<core::int>?, core::Object?>(null) in #t6 == null ?{core::Iterable<core::num>?} null : let final core::int #t7 = 0 in let final core::Iterable<core::int>? #t8 = self::Extension|[]<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7) in #t8 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t9 = listNum in let final void #t10 = self::Extension|[]=<core::Iterable<core::int>?, core::Object?>(#t6{self::Indexable<core::Iterable<core::int>?, core::Object?>}, #t7, #t9) in #t9 : #t8{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:47:59 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:51:65 -> IntConstant(0)
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_index_expression_test.dart:51:65 -> IntConstant(0)
Extra constant evaluation: evaluated: 55, effectively constant: 4

View file

@ -0,0 +1,43 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a null-aware access to an extension
// property, using explicit extension syntax.
/// Ensures a context type of `Iterable<T>?` for the operand, or `Iterable<_>?`
/// if no type argument is supplied.
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>?` for the operand, or `B1<_>?` if no type
/// argument is supplied.
B1<T>? contextB1Question<T>(B1<T>? x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
}
main() {
var s = '' as String?;
var c2Double = C2<double>();
contextB1Question(Extension(s)?.pC1IntQuestion ??= c2Double);
var listNum = <num>[];
contextIterableQuestion<num>(Extension(s)?.pIterableIntQuestion ??= listNum);
}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
core::String? s = "" as core::String?;
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final core::String? #t1 = s in #t1 == null ?{self::B1<dynamic>?} null : let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1{core::String}) in #t2 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1{core::String}, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final core::String? #t5 = s in #t5 == null ?{core::Iterable<core::num>?} null : let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5{core::String}) in #t6 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5{core::String}, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,57 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
core::String? s = "" as{Unchecked} core::String?;
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final core::String? #t1 = s in #t1 == null ?{self::B1<dynamic>?} null : let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1{core::String}) in #t2 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1{core::String}, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterableQuestion<core::num>(let final core::String? #t5 = s in #t5 == null ?{core::Iterable<core::num>?} null : let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5{core::String}) in #t6 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5{core::String}, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: AsExpression @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_property_test.dart:36:14 -> StringConstant("")
Extra constant evaluation: evaluated: 45, effectively constant: 1

View file

@ -0,0 +1,22 @@
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T>? contextB1Question<T>(B1<T>? x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
}
main() {}

View file

@ -0,0 +1,22 @@
B1<T>? contextB1Question<T>(B1<T>? x) => x;
Iterable<T>? contextIterableQuestion<T>(Iterable<T>? x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
}
main() {}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
core::String? s = "" as core::String?;
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final core::String? #t1 = s in #t1 == null ?{self::B1<dynamic>?} null : let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1{core::String}) in #t2 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1{core::String}, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final core::String? #t5 = s in #t5 == null ?{core::Iterable<core::num>?} null : let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5{core::String}) in #t6 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5{core::String}, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,52 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
core::String? s = "" as core::String?;
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final core::String? #t1 = s in #t1 == null ?{self::B1<dynamic>?} null : let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1{core::String}) in #t2 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1{core::String}, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterableQuestion<core::num>(let final core::String? #t5 = s in #t5 == null ?{core::Iterable<core::num>?} null : let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5{core::String}) in #t6 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5{core::String}, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,57 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterableQuestion<T extends core::Object? = dynamic>(core::Iterable<self::contextIterableQuestion::T%>? x) → core::Iterable<self::contextIterableQuestion::T%>?
return x;
static method contextB1Question<T extends core::Object? = dynamic>(self::B1<self::contextB1Question::T%>? x) → self::B1<self::contextB1Question::T%>?
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
core::String? s = "" as{Unchecked} core::String?;
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1Question<dynamic>(let final core::String? #t1 = s in #t1 == null ?{self::B1<dynamic>?} null : let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1{core::String}) in #t2 == null ?{self::B1<dynamic>?} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1{core::String}, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterableQuestion<core::num>(let final core::String? #t5 = s in #t5 == null ?{core::Iterable<core::num>?} null : let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5{core::String}) in #t6 == null ?{core::Iterable<core::num>?} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5{core::String}, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: AsExpression @ org-dartlang-testcase:///if_null_assignment_explicit_extension_null_aware_property_test.dart:36:14 -> StringConstant("")
Extra constant evaluation: evaluated: 45, effectively constant: 1

View file

@ -0,0 +1,41 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is an access to an extension property,
// using explicit extension syntax.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
}
main() {
var c2Double = C2<double>();
contextB1(Extension('').pC1IntQuestion ??= c2Double);
var listNum = <num>[];
contextIterable<num>(Extension('').pIterableIntQuestion ??= listNum);
}

View file

@ -0,0 +1,51 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = "" in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = "" in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,59 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = "" in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = "" in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:37:23 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:37:23 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:40:34 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:40:34 -> StringConstant("")
Extra constant evaluation: evaluated: 36, effectively constant: 4

View file

@ -0,0 +1,22 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
}
main() {}

View file

@ -0,0 +1,22 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
}
main() {}

View file

@ -0,0 +1,51 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = "" in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = "" in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,51 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = "" in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = "" in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void
;
static method main() → dynamic
;

View file

@ -0,0 +1,59 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static method main() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = "" in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = "" in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
Extra constant evaluation status:
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:37:23 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:37:23 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:40:34 -> StringConstant("")
Evaluated: VariableGet @ org-dartlang-testcase:///if_null_assignment_explicit_extension_property_test.dart:40:34 -> StringConstant("")
Extra constant evaluation: evaluated: 36, effectively constant: 4

View file

@ -0,0 +1,48 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a property of the current
// extension, accessed through explicit `this` using explicit extension syntax.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {
var c2Double = C2<double>();
contextB1(Extension(this).pC1IntQuestion ??= c2Double);
// K=Iterable<num>, T1=Iterable<int>?, and T2=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2 <: S, hence the type of E is Iterable<num>.
var listNum = <num>[];
contextIterable<num>(Extension(this).pIterableIntQuestion ??= listNum);
}
}
main() {
''.test();
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,23 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,23 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,50 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|test(lowered final core::String #this) → dynamic
;
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic
;

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,45 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a property of the current class,
// accessed through explicit `this`.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
class Test {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {
var c2Double = C2<double>();
contextB1(this.pC1IntQuestion ??= c2Double);
var listNum = <num>[];
contextIterable<num>(this.pIterableIntQuestion ??= listNum);
}
}
main() {
Test().test();
}

View file

@ -0,0 +1,53 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
: super core::Object::•()
;
get pC1IntQuestion() → self::C1<core::int>?
return null;
set pC1IntQuestion(core::Object? value) → void {}
get pIterableIntQuestion() → core::Iterable<core::int>?
return null;
set pIterableIntQuestion(core::Object? value) → void {}
method test() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = this.{self::Test::pC1IntQuestion}{self::C1<core::int>?} in #t1 == null ?{self::B1<dynamic>} this.{self::Test::pC1IntQuestion} = c2Double : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t2 = this.{self::Test::pIterableIntQuestion}{core::Iterable<core::int>?} in #t2 == null ?{core::Iterable<core::num>} this.{self::Test::pIterableIntQuestion} = listNum : #t2{core::Iterable<core::int>});
}
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method main() → dynamic {
new self::Test::•().{self::Test::test}(){() → dynamic};
}

View file

@ -0,0 +1,53 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
: super core::Object::•()
;
get pC1IntQuestion() → self::C1<core::int>?
return null;
set pC1IntQuestion(core::Object? value) → void {}
get pIterableIntQuestion() → core::Iterable<core::int>?
return null;
set pIterableIntQuestion(core::Object? value) → void {}
method test() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = this.{self::Test::pC1IntQuestion}{self::C1<core::int>?} in #t1 == null ?{self::B1<dynamic>} this.{self::Test::pC1IntQuestion} = c2Double : #t1{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t2 = this.{self::Test::pIterableIntQuestion}{core::Iterable<core::int>?} in #t2 == null ?{core::Iterable<core::num>} this.{self::Test::pIterableIntQuestion} = listNum : #t2{core::Iterable<core::int>});
}
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method main() → dynamic {
new self::Test::•().{self::Test::test}(){() → dynamic};
}

View file

@ -0,0 +1,23 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
class Test {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,23 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
class Test {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,53 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
: super core::Object::•()
;
get pC1IntQuestion() → self::C1<core::int>?
return null;
set pC1IntQuestion(core::Object? value) → void {}
get pIterableIntQuestion() → core::Iterable<core::int>?
return null;
set pIterableIntQuestion(core::Object? value) → void {}
method test() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = this.{self::Test::pC1IntQuestion}{self::C1<core::int>?} in #t1 == null ?{self::B1<dynamic>} this.{self::Test::pC1IntQuestion} = c2Double : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t2 = this.{self::Test::pIterableIntQuestion}{core::Iterable<core::int>?} in #t2 == null ?{core::Iterable<core::num>} this.{self::Test::pIterableIntQuestion} = listNum : #t2{core::Iterable<core::int>});
}
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method main() → dynamic {
new self::Test::•().{self::Test::test}(){() → dynamic};
}

View file

@ -0,0 +1,53 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
: super core::Object::•()
;
get pC1IntQuestion() → self::C1<core::int>?
return null;
set pC1IntQuestion(core::Object? value) → void {}
get pIterableIntQuestion() → core::Iterable<core::int>?
return null;
set pIterableIntQuestion(core::Object? value) → void {}
method test() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = this.{self::Test::pC1IntQuestion}{self::C1<core::int>?} in #t1 == null ?{self::B1<dynamic>} this.{self::Test::pC1IntQuestion} = c2Double : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t2 = this.{self::Test::pIterableIntQuestion}{core::Iterable<core::int>?} in #t2 == null ?{core::Iterable<core::num>} this.{self::Test::pIterableIntQuestion} = listNum : #t2{core::Iterable<core::int>});
}
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method main() → dynamic {
new self::Test::•().{self::Test::test}(){() → dynamic};
}

View file

@ -0,0 +1,44 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
;
get pC1IntQuestion() → self::C1<core::int>?
;
set pC1IntQuestion(core::Object? value) → void
;
get pIterableIntQuestion() → core::Iterable<core::int>?
;
set pIterableIntQuestion(core::Object? value) → void
;
method test() → dynamic
;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static method main() → dynamic
;

View file

@ -0,0 +1,53 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
class Test extends core::Object {
synthetic constructor •() → self::Test
: super core::Object::•()
;
get pC1IntQuestion() → self::C1<core::int>?
return null;
set pC1IntQuestion(core::Object? value) → void {}
get pIterableIntQuestion() → core::Iterable<core::int>?
return null;
set pIterableIntQuestion(core::Object? value) → void {}
method test() → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = this.{self::Test::pC1IntQuestion}{self::C1<core::int>?} in #t1 == null ?{self::B1<dynamic>} this.{self::Test::pC1IntQuestion} = c2Double : #t1{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t2 = this.{self::Test::pIterableIntQuestion}{core::Iterable<core::int>?} in #t2 == null ?{core::Iterable<core::num>} this.{self::Test::pIterableIntQuestion} = listNum : #t2{core::Iterable<core::int>});
}
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static method main() → dynamic {
new self::Test::•().{self::Test::test}(){() → dynamic};
}

View file

@ -0,0 +1,45 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a property of the current
// extension, accessed through explicit `this`.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {
var c2Double = C2<double>();
contextB1(this.pC1IntQuestion ??= c2Double);
var listNum = <num>[];
contextIterable<num>(this.pIterableIntQuestion ??= listNum);
}
}
main() {
''.test();
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,23 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,23 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,50 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|test(lowered final core::String #this) → dynamic
;
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic
;

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final core::String #t1 = #this in let final self::C1<core::int>? #t2 = self::Extension|get#pC1IntQuestion(#t1) in #t2 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t3 = c2Double in let final void #t4 = self::Extension|set#pC1IntQuestion(#t1, #t3) in #t3 : #t2{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::String #t5 = #this in let final core::Iterable<core::int>? #t6 = self::Extension|get#pIterableIntQuestion(#t5) in #t6 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t7 = listNum in let final void #t8 = self::Extension|set#pIterableIntQuestion(#t5, #t7) in #t7 : #t6{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,45 @@
// Copyright (c) 2024, 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 the functionality proposed in
// https://github.com/dart-lang/language/issues/1618#issuecomment-1507241494,
// using if-null assignments whose target is a property of the current
// extension, accessed through implicit `this`.
/// Ensures a context type of `Iterable<T>` for the operand, or `Iterable<_>` if
/// no type argument is supplied.
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
/// Ensures a context type of `B1<T>` for the operand, or `B1<_>` if no type
/// argument is supplied.
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {
var c2Double = C2<double>();
contextB1(pC1IntQuestion ??= c2Double);
var listNum = <num>[];
contextIterable<num>(pIterableIntQuestion ??= listNum);
}
}
main() {
''.test();
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = self::Extension|get#pC1IntQuestion(#this) in #t1 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t2 = c2Double in let final void #t3 = self::Extension|set#pC1IntQuestion(#this, #t2) in #t2 : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t4 = self::Extension|get#pIterableIntQuestion(#this) in #t4 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t5 = listNum in let final void #t6 = self::Extension|set#pIterableIntQuestion(#this, #t5) in #t5 : #t4{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = self::Extension|get#pC1IntQuestion(#this) in #t1 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t2 = c2Double in let final void #t3 = self::Extension|set#pC1IntQuestion(#this, #t2) in #t2 : #t1{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t4 = self::Extension|get#pIterableIntQuestion(#this) in #t4 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t5 = listNum in let final void #t6 = self::Extension|set#pIterableIntQuestion(#this, #t5) in #t5 : #t4{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,23 @@
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
B1<T> contextB1<T>(B1<T> x) => x;
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
set pC1IntQuestion(Object? value) {}
Iterable<int>? get pIterableIntQuestion => null;
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,23 @@
B1<T> contextB1<T>(B1<T> x) => x;
Iterable<T> contextIterable<T>(Iterable<T> x) => x;
class A {}
class B1<T> implements A {}
class B2<T> implements A {}
class C1<T> implements B1<T>, B2<T> {}
class C2<T> implements B1<T>, B2<T> {}
extension Extension on String {
C1<int>? get pC1IntQuestion => null;
Iterable<int>? get pIterableIntQuestion => null;
set pC1IntQuestion(Object? value) {}
set pIterableIntQuestion(Object? value) {}
test() {}
}
main() {}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = self::Extension|get#pC1IntQuestion(#this) in #t1 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t2 = c2Double in let final void #t3 = self::Extension|set#pC1IntQuestion(#this, #t2) in #t2 : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t4 = self::Extension|get#pIterableIntQuestion(#this) in #t4 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t5 = listNum in let final void #t6 = self::Extension|set#pIterableIntQuestion(#this, #t5) in #t5 : #t4{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = self::Extension|get#pC1IntQuestion(#this) in #t1 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t2 = c2Double in let final void #t3 = self::Extension|set#pC1IntQuestion(#this, #t2) in #t2 : #t1{self::C1<core::int>});
core::List<core::num> listNum = <core::num>[];
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t4 = self::Extension|get#pIterableIntQuestion(#this) in #t4 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t5 = listNum in let final void #t6 = self::Extension|set#pIterableIntQuestion(#this, #t5) in #t5 : #t4{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

View file

@ -0,0 +1,50 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void
;
static extension-member method Extension|test(lowered final core::String #this) → dynamic
;
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic
;

View file

@ -0,0 +1,58 @@
library;
import self as self;
import "dart:core" as core;
class A extends core::Object {
synthetic constructor •() → self::A
: super core::Object::•()
;
}
class B1<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B1<self::B1::T%>
: super core::Object::•()
;
}
class B2<T extends core::Object? = dynamic> extends core::Object implements self::A {
synthetic constructor •() → self::B2<self::B2::T%>
: super core::Object::•()
;
}
class C1<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C1::T%>, self::B2<self::C1::T%> {
synthetic constructor •() → self::C1<self::C1::T%>
: super core::Object::•()
;
}
class C2<T extends core::Object? = dynamic> extends core::Object implements self::B1<self::C2::T%>, self::B2<self::C2::T%> {
synthetic constructor •() → self::C2<self::C2::T%>
: super core::Object::•()
;
}
extension Extension on core::String {
get pC1IntQuestion = self::Extension|get#pC1IntQuestion;
get pIterableIntQuestion = self::Extension|get#pIterableIntQuestion;
method test = self::Extension|test;
method tearoff test = self::Extension|get#test;
set pC1IntQuestion = self::Extension|set#pC1IntQuestion;
set pIterableIntQuestion = self::Extension|set#pIterableIntQuestion;
}
static method contextIterable<T extends core::Object? = dynamic>(core::Iterable<self::contextIterable::T%> x) → core::Iterable<self::contextIterable::T%>
return x;
static method contextB1<T extends core::Object? = dynamic>(self::B1<self::contextB1::T%> x) → self::B1<self::contextB1::T%>
return x;
static extension-member method Extension|get#pC1IntQuestion(lowered final core::String #this) → self::C1<core::int>?
return null;
static extension-member method Extension|set#pC1IntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|get#pIterableIntQuestion(lowered final core::String #this) → core::Iterable<core::int>?
return null;
static extension-member method Extension|set#pIterableIntQuestion(lowered final core::String #this, core::Object? value) → void {}
static extension-member method Extension|test(lowered final core::String #this) → dynamic {
self::C2<core::double> c2Double = new self::C2::•<core::double>();
self::contextB1<dynamic>(let final self::C1<core::int>? #t1 = self::Extension|get#pC1IntQuestion(#this) in #t1 == null ?{self::B1<dynamic>} let final self::C2<core::double> #t2 = c2Double in let final void #t3 = self::Extension|set#pC1IntQuestion(#this, #t2) in #t2 : #t1{self::C1<core::int>});
core::List<core::num> listNum = core::_GrowableList::•<core::num>(0);
self::contextIterable<core::num>(let final core::Iterable<core::int>? #t4 = self::Extension|get#pIterableIntQuestion(#this) in #t4 == null ?{core::Iterable<core::num>} let final core::List<core::num> #t5 = listNum in let final void #t6 = self::Extension|set#pIterableIntQuestion(#this, #t5) in #t5 : #t4{core::Iterable<core::int>});
}
static extension-member method Extension|get#test(lowered final core::String #this) → () → dynamic
return () → dynamic => self::Extension|test(#this);
static method main() → dynamic {
self::Extension|test("");
}

Some files were not shown because too many files have changed in this diff Show more