[cfe] Handle explicit extension index access

+ avoid implicit extension for [] or []= when the other is defined on
the receiver.

Change-Id: I99c76a66bfb4f72c396097b2f347aad9f0a42f33
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118940
Reviewed-by: Jens Johansen <jensj@google.com>
This commit is contained in:
Johnni Winther 2019-09-26 10:36:51 +00:00 committed by commit-bot@chromium.org
parent 0bf6b6ae64
commit ee7aae43c0
23 changed files with 1623 additions and 97 deletions

View file

@ -10,6 +10,8 @@ import 'package:kernel/ast.dart';
import '../builder/declaration_builder.dart';
import '../builder/extension_builder.dart';
import '../constant_context.dart' show ConstantContext;
import '../dill/dill_library_builder.dart' show DillLibraryBuilder;
@ -23,8 +25,7 @@ import '../messages.dart' as messages show getLocationFromUri;
import '../modifier.dart'
show Modifier, constMask, covariantMask, finalMask, lateMask;
import '../names.dart'
show callName, emptyName, indexGetName, indexSetName, minusName, plusName;
import '../names.dart' show callName, emptyName, minusName, plusName;
import '../parser.dart'
show
@ -1869,11 +1870,19 @@ class BodyBuilder extends ScopeListener<JumpTarget>
}
return new ThisPropertyAccessGenerator(this, token, n, getter, setter);
} else if (declaration.isExtensionInstanceMember) {
ExtensionBuilder extensionBuilder = declarationBuilder;
Builder setter =
_getCorrespondingSetterBuilder(scope, declaration, name, charOffset);
// TODO(johnniwinther): Check for constantContext like below?
return new ExtensionInstanceAccessGenerator.fromBuilder(this, name,
extensionThis, extensionTypeParameters, declaration, token, setter);
return new ExtensionInstanceAccessGenerator.fromBuilder(
this,
extensionBuilder.extension,
name,
extensionThis,
extensionTypeParameters,
declaration,
token,
setter);
} else if (declaration.isRegularMethod) {
assert(declaration.isStatic || declaration.isTopLevel);
return new StaticAccessGenerator(
@ -3247,17 +3256,21 @@ class BodyBuilder extends ScopeListener<JumpTarget>
@override
void handleIndexedExpression(
Token openSquareBracket, Token closeSquareBracket) {
assert(checkState(openSquareBracket, [
unionOfKinds([ValueKind.Expression, ValueKind.Generator]),
unionOfKinds(
[ValueKind.Expression, ValueKind.Generator, ValueKind.Initializer])
]));
debugEvent("IndexedExpression");
Expression index = popForValue();
Object receiver = pop();
if (receiver is ThisAccessGenerator && receiver.isSuper) {
push(new SuperIndexedAccessGenerator(
this,
openSquareBracket,
index,
lookupInstanceMember(indexGetName, isSuper: true),
lookupInstanceMember(indexSetName, isSuper: true)));
if (receiver is Generator) {
push(receiver.buildIndexedAccess(index, openSquareBracket));
} else if (receiver is Expression) {
push(IndexedAccessGenerator.make(
this, openSquareBracket, receiver, index, null, null));
} else {
assert(receiver is Initializer);
push(IndexedAccessGenerator.make(
this, openSquareBracket, toValue(receiver), index, null, null));
}

View file

@ -82,8 +82,6 @@ import 'kernel_builder.dart'
import 'kernel_shadow_ast.dart';
import '../type_inference/type_inferrer.dart';
/// A generator represents a subexpression for which we can't yet build an
/// expression because we don't yet know the context in which it's used.
///
@ -178,6 +176,11 @@ abstract class Generator {
bool voidContext: false,
Procedure interfaceTarget});
/// Returns a [Generator] or [Expression] representing an index access
/// (e.g. `a[b]`) with the generator on the receiver and [index] as the
/// index expression.
Generator buildIndexedAccess(Expression index, Token token);
/// Returns a [Expression] representing a compile-time error.
///
/// At runtime, an exception will be thrown.
@ -412,6 +415,12 @@ class VariableUseGenerator extends Generator {
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -451,9 +460,6 @@ class PropertyAccessGenerator extends Generator {
final Member setter;
/// Synthetic variable created for [receiver] if need.
VariableDeclaration _receiverVariable;
PropertyAccessGenerator(ExpressionGeneratorHelper helper, Token token,
this.receiver, this.name, this.getter, this.setter)
: super(helper, token);
@ -464,11 +470,6 @@ class PropertyAccessGenerator extends Generator {
@override
String get _plainNameForRead => name.name;
receiverAccess() {
_receiverVariable ??= new VariableDeclaration.forValue(receiver);
return new VariableGet(_receiverVariable)..fileOffset = fileOffset;
}
@override
Expression doInvocation(int offset, Arguments arguments) {
return _helper.buildMethodInvocation(receiver, name, arguments, offset);
@ -477,8 +478,6 @@ class PropertyAccessGenerator extends Generator {
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", _receiverVariable: ");
printNodeOn(_receiverVariable, sink, syntheticNames: syntheticNames);
sink.write(", receiver: ");
printNodeOn(receiver, sink, syntheticNames: syntheticNames);
sink.write(", name: ");
@ -576,6 +575,12 @@ class PropertyAccessGenerator extends Generator {
return new PropertyPostIncDec(variable, read, write)..fileOffset = offset;
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
/// Creates a [Generator] for the access of property [name] on [receiver].
static Generator make(
ExpressionGeneratorHelper helper,
@ -748,6 +753,12 @@ class ThisPropertyAccessGenerator extends Generator {
interfaceTarget: interfaceTarget);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -863,6 +874,12 @@ class NullAwarePropertyAccessGenerator extends Generator {
return unsupported("doInvocation", offset, _uri);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -995,6 +1012,12 @@ class SuperPropertyAccessGenerator extends Generator {
}
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1096,6 +1119,12 @@ class IndexedAccessGenerator extends Generator {
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1221,6 +1250,12 @@ class ThisIndexedAccessGenerator extends Generator {
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1323,6 +1358,12 @@ class SuperIndexedAccessGenerator extends Generator {
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1530,6 +1571,12 @@ class StaticAccessGenerator extends Generator {
}
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1561,6 +1608,8 @@ class StaticAccessGenerator extends Generator {
///
/// These can only occur within an extension instance member.
class ExtensionInstanceAccessGenerator extends Generator {
final Extension extension;
/// The original name of the target.
final String targetName;
@ -1600,6 +1649,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
ExtensionInstanceAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extension,
this.targetName,
this.readTarget,
this.invokeTarget,
@ -1613,6 +1663,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
factory ExtensionInstanceAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Extension extension,
String targetName,
VariableDeclaration extensionThis,
List<TypeParameter> extensionTypeParameters,
@ -1648,6 +1699,7 @@ class ExtensionInstanceAccessGenerator extends Generator {
return new ExtensionInstanceAccessGenerator(
helper,
token,
extension,
targetName,
readTarget,
invokeTarget,
@ -1711,9 +1763,10 @@ class ExtensionInstanceAccessGenerator extends Generator {
write = _makeInvalidWrite(value);
} else {
write = new ExtensionSet(
extension,
_createExtensionTypeArguments(),
_helper.createVariableGet(extensionThis, fileOffset),
new ExtensionAccessTarget(writeTarget, null, ProcedureKind.Setter,
_createExtensionTypeArguments()),
writeTarget,
value,
forEffect: forEffect,
readOnlyReceiver: true);
@ -1798,6 +1851,12 @@ class ExtensionInstanceAccessGenerator extends Generator {
}
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -1832,6 +1891,8 @@ class ExtensionInstanceAccessGenerator extends Generator {
/// }
///
class ExplicitExtensionInstanceAccessGenerator extends Generator {
final Extension extension;
/// The name of the original target;
final String targetName;
@ -1875,6 +1936,7 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
ExplicitExtensionInstanceAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extension,
this.targetName,
this.readTarget,
this.invokeTarget,
@ -1892,6 +1954,7 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
factory ExplicitExtensionInstanceAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Token token,
Extension extension,
Builder getterBuilder,
Builder setterBuilder,
Expression receiver,
@ -1936,6 +1999,7 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
return new ExplicitExtensionInstanceAccessGenerator(
helper,
token,
extension,
targetName,
readTarget,
invokeTarget,
@ -1947,7 +2011,7 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
}
@override
String get _debugName => "InstanceExtensionAccessGenerator";
String get _debugName => "ExplicitExtensionIndexedAccessGenerator";
@override
String get _plainNameForRead => targetName;
@ -2016,12 +2080,8 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
write = _makeInvalidWrite(value);
} else {
write = new ExtensionSet(
receiver,
new ExtensionAccessTarget(writeTarget, null, ProcedureKind.Setter,
_createExtensionTypeArguments()),
value,
readOnlyReceiver: readOnlyReceiver,
forEffect: forEffect);
extension, explicitTypeArguments, receiver, writeTarget, value,
readOnlyReceiver: readOnlyReceiver, forEffect: forEffect);
}
write.fileOffset = offset;
return write;
@ -2203,6 +2263,12 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
}
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -2215,8 +2281,204 @@ class ExplicitExtensionInstanceAccessGenerator extends Generator {
}
}
class ExplicitExtensionIndexedAccessGenerator extends Generator {
final Extension extension;
/// The static [Member] generated for the [] operation.
///
/// This can be `null` if the extension doesn't have an [] method.
final Procedure readTarget;
/// The static [Member] generated for the []= operation.
///
/// This can be `null` if the extension doesn't have an []= method.
final Procedure writeTarget;
/// The expression holding the receiver value for the explicit extension
/// access, that is, `a` in `Extension<int>(a)[index]`.
final Expression receiver;
/// The index expression;
final Expression index;
/// The type arguments explicitly passed to the explicit extension access,
/// like `<int>` in `Extension<int>(a)[b]`.
final List<DartType> explicitTypeArguments;
/// The number of type parameters declared on the extension declaration.
final int extensionTypeParameterCount;
ExplicitExtensionIndexedAccessGenerator(
ExpressionGeneratorHelper helper,
Token token,
this.extension,
this.readTarget,
this.writeTarget,
this.receiver,
this.index,
this.explicitTypeArguments,
this.extensionTypeParameterCount)
: assert(readTarget != null || writeTarget != null),
assert(receiver != null),
super(helper, token);
factory ExplicitExtensionIndexedAccessGenerator.fromBuilder(
ExpressionGeneratorHelper helper,
Token token,
Extension extension,
Builder getterBuilder,
Builder setterBuilder,
Expression receiver,
Expression index,
List<DartType> explicitTypeArguments,
int extensionTypeParameterCount) {
Procedure readTarget;
if (getterBuilder != null) {
if (getterBuilder is AccessErrorBuilder) {
AccessErrorBuilder error = getterBuilder;
getterBuilder = error.builder;
// We should only see an access error here if we've looked up a setter
// when not explicitly looking for a setter.
assert(getterBuilder is MemberBuilder);
} else if (getterBuilder is MemberBuilder) {
MemberBuilder procedureBuilder = getterBuilder;
readTarget = procedureBuilder.member;
} else {
return unhandled(
"${getterBuilder.runtimeType}",
"InstanceExtensionAccessGenerator.fromBuilder",
offsetForToken(token),
helper.uri);
}
}
Procedure writeTarget;
if (setterBuilder is MemberBuilder) {
MemberBuilder memberBuilder = setterBuilder;
writeTarget = memberBuilder.member;
}
return new ExplicitExtensionIndexedAccessGenerator(
helper,
token,
extension,
readTarget,
writeTarget,
receiver,
index,
explicitTypeArguments,
extensionTypeParameterCount);
}
List<DartType> _createExtensionTypeArguments() {
return explicitTypeArguments ?? const <DartType>[];
}
String get _plainNameForRead => "[]";
String get _debugName => "ExplicitExtensionIndexedAccessGenerator";
@override
Expression buildSimpleRead() {
if (readTarget == null) {
return _makeInvalidRead();
}
return _helper.buildExtensionMethodInvocation(
fileOffset,
readTarget,
_forest.createArgumentsForExtensionMethod(
fileOffset, extensionTypeParameterCount, 0, receiver,
extensionTypeArguments: _createExtensionTypeArguments(),
positionalArguments: <Expression>[index]),
isTearOff: false);
}
@override
Expression buildAssignment(Expression value, {bool voidContext: false}) {
if (writeTarget == null) {
return _makeInvalidWrite(value);
}
if (voidContext) {
return _helper.buildExtensionMethodInvocation(
fileOffset,
writeTarget,
_forest.createArgumentsForExtensionMethod(
fileOffset, extensionTypeParameterCount, 0, receiver,
extensionTypeArguments: _createExtensionTypeArguments(),
positionalArguments: <Expression>[index, value]),
isTearOff: false);
} else {
return new ExtensionIndexSet(
extension, explicitTypeArguments, receiver, writeTarget, index, value)
..fileOffset = fileOffset;
}
}
@override
Expression buildIfNullAssignment(Expression value, DartType type, int offset,
{bool voidContext: false}) {
return new IfNullExtensionIndexSet(extension, explicitTypeArguments,
receiver, readTarget, writeTarget, index, value,
readOffset: fileOffset,
testOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext)
..fileOffset = offset;
}
Expression buildCompoundAssignment(Name binaryOperator, Expression value,
{int offset: TreeNode.noOffset,
bool voidContext: false,
Procedure interfaceTarget,
bool isPreIncDec: false,
bool isPostIncDec: false}) {
return new CompoundExtensionIndexSet(extension, explicitTypeArguments,
receiver, readTarget, writeTarget, index, binaryOperator, value,
readOffset: fileOffset,
binaryOffset: offset,
writeOffset: fileOffset,
forEffect: voidContext,
forPostIncDec: isPostIncDec);
}
@override
Expression buildPostfixIncrement(Name binaryOperator,
{int offset = TreeNode.noOffset,
bool voidContext = false,
Procedure interfaceTarget}) {
Expression value = _forest.createIntLiteral(1, null)..fileOffset = offset;
return buildCompoundAssignment(binaryOperator, value,
offset: offset,
voidContext: voidContext,
interfaceTarget: interfaceTarget,
isPostIncDec: true);
}
@override
Expression doInvocation(int offset, Arguments arguments) {
return _helper.buildMethodInvocation(
buildSimpleRead(), callName, arguments, offset,
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
sink.write(", index: ");
printNodeOn(index, sink, syntheticNames: syntheticNames);
sink.write(", readTarget: ");
printQualifiedNameOn(readTarget, sink, syntheticNames: syntheticNames);
sink.write(", writeTarget: ");
printQualifiedNameOn(writeTarget, sink, syntheticNames: syntheticNames);
}
}
/// A [ExplicitExtensionAccessGenerator] represents a subexpression whose
/// prefix is a forced extension resolution.
/// prefix is an explicit extension application.
///
/// For instance
///
@ -2308,6 +2570,7 @@ class ExplicitExtensionAccessGenerator extends Generator {
new ExplicitExtensionInstanceAccessGenerator.fromBuilder(
_helper,
token,
extensionBuilder.extension,
getter,
setter,
receiver,
@ -2339,6 +2602,26 @@ class ExplicitExtensionAccessGenerator extends Generator {
messageExplicitExtensionAsLvalue, fileOffset, lengthForToken(token));
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
Builder getter = extensionBuilder.lookupLocalMember(indexGetName.name);
Builder setter = extensionBuilder.lookupLocalMember(indexSetName.name);
if (getter == null && setter == null) {
return new UnresolvedNameGenerator(_helper, token, indexGetName);
}
return new ExplicitExtensionIndexedAccessGenerator.fromBuilder(
_helper,
token,
extensionBuilder.extension,
getter,
setter,
receiver,
index,
explicitTypeArguments,
extensionBuilder.typeParameters?.length ?? 0);
}
@override
void printOn(StringSink sink) {
sink.write(", extensionBuilder: ");
@ -2423,6 +2706,12 @@ class LoadLibraryGenerator extends Generator {
return builder.createLoadLibrary(offset, _forest, arguments);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
sink.write(", builder: ");
@ -2569,6 +2858,12 @@ class DeferredAccessGenerator extends Generator {
offsetForToken(suffixGenerator.token));
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
sink.write(", prefixGenerator: ");
@ -2888,6 +3183,14 @@ class ReadOnlyAccessGenerator extends Generator {
isImplicitCall: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
// TODO(johnniwinther): The read-only quality of the variable should be
// passed on to the generator.
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
NameSystem syntheticNames = new NameSystem();
@ -3012,6 +3315,12 @@ abstract class ErroneousExpressionGenerator extends Generator {
}
return buildError(arguments);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
}
class UnresolvedNameGenerator extends ErroneousExpressionGenerator {
@ -3088,6 +3397,12 @@ class UnresolvedNameGenerator extends ErroneousExpressionGenerator {
return buildError(_forest.createArguments(fileOffset, <Expression>[value]),
isSetter: true);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
}
class UnlinkedGenerator extends Generator {
@ -3207,6 +3522,12 @@ class UnlinkedGenerator extends Generator {
Expression doInvocation(int offset, Arguments arguments) {
return unsupported("doInvocation", offset, _uri);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
}
abstract class ContextAwareGenerator extends Generator {
@ -3269,6 +3590,12 @@ abstract class ContextAwareGenerator extends Generator {
return _helper.buildProblem(messageIllegalAssignmentToNonAssignable,
fileOffset, lengthForToken(token));
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
}
class DelayedAssignment extends ContextAwareGenerator {
@ -3509,6 +3836,12 @@ class PrefixUseGenerator extends Generator {
@override
Expression _makeInvalidWrite(Expression value) => _makeInvalidRead();
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
sink.write(", prefix: ");
@ -3597,6 +3930,12 @@ class UnexpectedQualifiedUseGenerator extends Generator {
return result;
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
@override
void printOn(StringSink sink) {
sink.write(", prefixGenerator: ");
@ -3699,6 +4038,12 @@ class ParserErrorGenerator extends Generator {
Constness constness) {
return buildProblem();
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return new IndexedAccessGenerator(
_helper, token, buildSimpleRead(), index, null, null);
}
}
/// A [ThisAccessGenerator] represents a subexpression whose prefix is `this`
@ -3921,6 +4266,20 @@ class ThisAccessGenerator extends Generator {
return buildAssignmentError();
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
if (isSuper) {
return new SuperIndexedAccessGenerator(
_helper,
token,
index,
_helper.lookupInstanceMember(indexGetName, isSuper: true),
_helper.lookupInstanceMember(indexSetName, isSuper: true));
} else {
return new ThisIndexedAccessGenerator(_helper, token, index, null, null);
}
}
Expression buildAssignmentError() {
return _helper.buildProblem(
isSuper ? messageCannotAssignToSuper : messageNotAnLvalue,
@ -4055,6 +4414,11 @@ class SendAccessGenerator extends Generator with IncompleteSendGenerator {
return unsupported("doInvocation", offset, _uri);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return unsupported("buildIndexedAccess", offsetForToken(token), _uri);
}
@override
void printOn(StringSink sink) {
sink.write(", name: ");
@ -4129,6 +4493,11 @@ class IncompletePropertyAccessGenerator extends Generator
return unsupported("doInvocation", offset, _uri);
}
@override
Generator buildIndexedAccess(Expression index, Token token) {
return unsupported("buildIndexedAccess", offsetForToken(token), _uri);
}
@override
void printOn(StringSink sink) {
sink.write(", name: ");

View file

@ -41,6 +41,8 @@ class InferenceVisitor
switch (node.kind) {
case InternalExpressionKind.Cascade:
return visitCascade(node, typeContext);
case InternalExpressionKind.CompoundExtensionIndexSet:
return visitCompoundExtensionIndexSet(node, typeContext);
case InternalExpressionKind.CompoundIndexSet:
return visitCompoundIndexSet(node, typeContext);
case InternalExpressionKind.CompoundPropertySet:
@ -49,12 +51,16 @@ class InferenceVisitor
return visitCompoundSuperIndexSet(node, typeContext);
case InternalExpressionKind.DeferredCheck:
return visitDeferredCheck(node, typeContext);
case InternalExpressionKind.ExtensionIndexSet:
return visitExtensionIndexSet(node, typeContext);
case InternalExpressionKind.ExtensionTearOff:
return visitExtensionTearOff(node, typeContext);
case InternalExpressionKind.ExtensionSet:
return visitExtensionSet(node, typeContext);
case InternalExpressionKind.IfNull:
return visitIfNull(node, typeContext);
case InternalExpressionKind.IfNullExtensionIndexSet:
return visitIfNullExtensionIndexSet(node, typeContext);
case InternalExpressionKind.IfNullIndexSet:
return visitIfNullIndexSet(node, typeContext);
case InternalExpressionKind.IfNullPropertySet:
@ -408,14 +414,25 @@ class InferenceVisitor
ExpressionInferenceResult visitExtensionSet(
ExtensionSet node, DartType typeContext) {
// Since the variable is not used in the body we don't need to type infer
// it. We can just type infer the body.
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
inferrer.ensureAssignable(receiverType, receiverResult.inferredType,
node.receiver, node.receiver.fileOffset);
ObjectAccessTarget target = new ExtensionAccessTarget(
node.target, null, ProcedureKind.Setter, extensionTypeArguments);
DartType valueType =
inferrer.getSetterType(node.target, receiverResult.inferredType);
inferrer.getSetterType(target, receiverResult.inferredType);
ExpressionInferenceResult valueResult = inferrer.inferExpression(
node.value, const UnknownType(), true,
@ -442,9 +459,9 @@ class InferenceVisitor
receiver = createVariableGet(receiverVariable);
}
Expression assignment = new StaticInvocation(
node.target.member,
node.target,
new Arguments(<Expression>[receiver, value],
types: node.target.inferredExtensionTypeArguments)
types: extensionTypeArguments)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset;
@ -2293,9 +2310,9 @@ class InferenceVisitor
Expression assignment;
if (indexSetTarget.isMissing) {
assignment = inferrer.helper.buildProblem(
templateSuperclassHasNoMethod.withArguments('[]='),
templateSuperclassHasNoMethod.withArguments(indexSetName.name),
node.fileOffset,
'[]='.length);
noLength);
} else {
assert(indexSetTarget.isInstanceMember);
inferrer.instrumentation?.record(inferrer.uri, node.fileOffset, 'target',
@ -2320,6 +2337,85 @@ class InferenceVisitor
return new ExpressionInferenceResult(inferredType, replacement);
}
ExpressionInferenceResult visitExtensionIndexSet(
ExtensionIndexSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
inferrer.ensureAssignable(receiverType, receiverResult.inferredType,
node.receiver, node.receiver.fileOffset);
VariableDeclaration receiverVariable =
createVariable(node.receiver, receiverType);
ObjectAccessTarget target = new ExtensionAccessTarget(
node.setter, null, ProcedureKind.Operator, extensionTypeArguments);
DartType indexType = inferrer.getIndexKeyType(target, receiverType);
DartType valueType = inferrer.getIndexSetValueType(target, receiverType);
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, indexType, true, isVoidAllowed: true);
inferrer.ensureAssignable(
indexType, indexResult.inferredType, node.index, node.index.fileOffset);
VariableDeclaration indexVariable =
createVariable(node.index, indexResult.inferredType);
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
inferrer.ensureAssignable(
valueType, valueResult.inferredType, node.value, node.value.fileOffset);
VariableDeclaration valueVariable =
createVariable(node.value, valueResult.inferredType);
// The inferred type is that inferred type of the value expression and not
// the type of the value parameter.
DartType inferredType = valueResult.inferredType;
Expression replacement;
Expression assignment;
if (target.isMissing) {
assignment = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(
indexSetName.name, receiverType),
node.fileOffset,
noLength);
} else {
assert(target.isExtensionMember);
assignment = new StaticInvocation(
target.member,
new Arguments(<Expression>[
createVariableGet(receiverVariable),
createVariableGet(indexVariable),
createVariableGet(valueVariable)
], types: target.inferredExtensionTypeArguments)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset;
}
VariableDeclaration assignmentVariable =
createVariable(assignment, const VoidType());
node.replaceWith(replacement = new Let(
receiverVariable,
createLet(
indexVariable,
createLet(
valueVariable,
createLet(
assignmentVariable, createVariableGet(valueVariable)))))
..fileOffset = node.fileOffset);
return new ExpressionInferenceResult(inferredType, replacement);
}
ExpressionInferenceResult visitIfNullIndexSet(
IfNullIndexSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
@ -2461,18 +2557,18 @@ class InferenceVisitor
Expression inner;
if (node.forEffect) {
// Encode `o[a] ??= b`, if `node.readOnlyReceiver` is false, as:
// Encode `Extension(o)[a] ??= b`, if `node.readOnlyReceiver` is false,
// as:
//
// let v1 = o in
// let v2 = a in
// let v3 = v1[v2] in
// v3 == null ? v1.[]=(v2, b) : null
// let receiverVariable = o in
// let indexVariable = a in
// receiverVariable[indexVariable] == null
// ? receiverVariable.[]=(indexVariable, b) : null
//
// and if `node.readOnlyReceiver` is true as:
//
// let v2 = a in
// let v3 = o[v2] in
// v3 == null ? o.[]=(v2, b) : null
// let indexVariable = a in
// o[indexVariable] == null ? o.[]=(indexVariable, b) : null
//
MethodInvocation equalsNull =
createEqualsNull(node.testOffset, read, equalsMember);
@ -2481,26 +2577,28 @@ class InferenceVisitor
..fileOffset = node.testOffset;
inner = createLet(indexVariable, conditional);
} else {
// Encode `o[a] ??= b` as, if `node.readOnlyReceiver` is false, as:
// Encode `Extension(o)[a] ??= b` as, if `node.readOnlyReceiver` is false,
// as:
//
// let v1 = o in
// let v2 = a in
// let v3 = v1[v2] in
// v3 == null
// ? (let v4 = b in
// let _ = v1.[]=(v2, v4) in
// v4)
// : v3
// let receiverVariable = o in
// let indexVariable = a in
// let readVariable = receiverVariable[indexVariable] in
// readVariable == null
// ? (let valueVariable = b in
// let writeVariable =
// receiverVariable.[]=(indexVariable, valueVariable) in
// valueVariable)
// : readVariable
//
// and if `node.readOnlyReceiver` is true as:
//
// let v2 = a in
// let v3 = o[v2] in
// v3 == null
// ? (let v4 = b in
// let _ = o.[]=(v2, v4) in
// v4)
// : v3
// let indexVariable = a in
// let readVariable = o[indexVariable] in
// readVariable == null
// ? (let valueVariable = b in
// let writeVariable = o.[]=(indexVariable, valueVariable) in
// valueVariable)
// : readVariable
//
//
assert(valueVariable != null);
@ -2671,6 +2769,171 @@ class InferenceVisitor
return new ExpressionInferenceResult(inferredType, replacement);
}
ExpressionInferenceResult visitIfNullExtensionIndexSet(
IfNullExtensionIndexSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
inferrer.ensureAssignable(receiverType, receiverResult.inferredType,
node.receiver, node.receiver.fileOffset);
VariableDeclaration receiverVariable =
createVariable(node.receiver, receiverType);
ObjectAccessTarget readTarget = node.getter != null
? new ExtensionAccessTarget(
node.getter, null, ProcedureKind.Operator, extensionTypeArguments)
: const ObjectAccessTarget.missing();
DartType readType = inferrer.getReturnType(readTarget, receiverType);
DartType readIndexType = inferrer.getIndexKeyType(readTarget, receiverType);
Member equalsMember = inferrer
.findInterfaceMember(readType, equalsName, node.testOffset)
.member;
ObjectAccessTarget writeTarget = node.setter != null
? new ExtensionAccessTarget(
node.setter, null, ProcedureKind.Operator, extensionTypeArguments)
: const ObjectAccessTarget.missing();
DartType writeIndexType =
inferrer.getIndexKeyType(writeTarget, receiverType);
DartType valueType =
inferrer.getIndexSetValueType(writeTarget, receiverType);
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, readIndexType, true, isVoidAllowed: true);
VariableDeclaration indexVariable =
createVariable(node.index, indexResult.inferredType);
VariableGet readIndex = createVariableGet(indexVariable);
inferrer.ensureAssignable(readIndexType, indexResult.inferredType,
readIndex, readIndex.fileOffset);
VariableGet writeIndex = createVariableGet(indexVariable);
inferrer.ensureAssignable(writeIndexType, indexResult.inferredType,
writeIndex, writeIndex.fileOffset);
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
inferrer.ensureAssignable(
valueType, valueResult.inferredType, node.value, node.value.fileOffset);
DartType inferredType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(readType, valueResult.inferredType);
Expression read;
if (readTarget.isMissing) {
read = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(
indexGetName.name, receiverType),
node.readOffset,
noLength);
} else {
assert(readTarget.isExtensionMember);
read = new StaticInvocation(
readTarget.member,
new Arguments(<Expression>[
createVariableGet(receiverVariable),
readIndex,
], types: readTarget.inferredExtensionTypeArguments)
..fileOffset = node.readOffset)
..fileOffset = node.readOffset;
}
VariableDeclaration valueVariable;
Expression valueExpression;
if (node.forEffect) {
valueExpression = node.value;
} else {
valueVariable = createVariable(node.value, valueResult.inferredType);
valueExpression = createVariableGet(valueVariable);
}
Expression write;
if (writeTarget.isMissing) {
write = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(
indexSetName.name, receiverType),
node.writeOffset,
noLength);
} else {
assert(writeTarget.isExtensionMember);
write = new StaticInvocation(
writeTarget.member,
new Arguments(<Expression>[
createVariableGet(receiverVariable),
writeIndex,
valueExpression
], types: writeTarget.inferredExtensionTypeArguments)
..fileOffset = node.writeOffset)
..fileOffset = node.writeOffset;
}
Expression inner;
if (node.forEffect) {
// Encode `Extension(o)[a] ??= b` as:
//
// let receiverVariable = o;
// let indexVariable = a in
// receiverVariable[indexVariable] == null
// ? receiverVariable.[]=(indexVariable, b) : null
//
MethodInvocation equalsNull =
createEqualsNull(node.testOffset, read, equalsMember);
ConditionalExpression conditional = new ConditionalExpression(equalsNull,
write, new NullLiteral()..fileOffset = node.testOffset, inferredType)
..fileOffset = node.testOffset;
inner = createLet(indexVariable, conditional);
} else {
// Encode `Extension(o)[a] ??= b` as:
//
// let receiverVariable = o;
// let indexVariable = a in
// let readVariable = receiverVariable[indexVariable] in
// readVariable == null
// ? (let valueVariable = b in
// let writeVariable =
// receiverVariable.[]=(indexVariable, valueVariable) in
// valueVariable)
// : readVariable
//
assert(valueVariable != null);
VariableDeclaration readVariable = createVariable(read, readType);
MethodInvocation equalsNull = createEqualsNull(
node.testOffset, createVariableGet(readVariable), equalsMember);
VariableDeclaration writeVariable =
createVariable(write, const VoidType());
ConditionalExpression conditional = new ConditionalExpression(
equalsNull,
createLet(valueVariable,
createLet(writeVariable, createVariableGet(valueVariable))),
createVariableGet(readVariable),
inferredType)
..fileOffset = node.fileOffset;
inner = createLet(indexVariable, createLet(readVariable, conditional));
}
Expression replacement = new Let(receiverVariable, inner)
..fileOffset = node.fileOffset;
node.replaceWith(replacement);
return new ExpressionInferenceResult(inferredType, replacement);
}
ExpressionInferenceResult visitCompoundIndexSet(
CompoundIndexSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
@ -3393,6 +3656,247 @@ class InferenceVisitor
node.forPostIncDec ? readType : binaryType, replacement);
}
ExpressionInferenceResult visitCompoundExtensionIndexSet(
CompoundExtensionIndexSet node, DartType typeContext) {
ExpressionInferenceResult receiverResult = inferrer.inferExpression(
node.receiver, const UnknownType(), true,
isVoidAllowed: false);
List<DartType> extensionTypeArguments =
inferrer.computeExtensionTypeArgument(node.extension,
node.explicitTypeArguments, receiverResult.inferredType);
ObjectAccessTarget readTarget = node.getter != null
? new ExtensionAccessTarget(
node.getter, null, ProcedureKind.Operator, extensionTypeArguments)
: const ObjectAccessTarget.missing();
DartType receiverType = inferrer.getPositionalParameterTypeForTarget(
readTarget, receiverResult.inferredType, 0);
inferrer.ensureAssignable(receiverType, receiverResult.inferredType,
node.receiver, node.receiver.fileOffset);
VariableDeclaration receiverVariable =
createVariable(node.receiver, receiverType);
DartType readType = inferrer.getReturnType(readTarget, receiverType);
DartType readIndexType = inferrer.getPositionalParameterTypeForTarget(
readTarget, receiverType, 0);
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, readIndexType, true, isVoidAllowed: true);
VariableDeclaration indexVariable =
createVariable(node.index, indexResult.inferredType);
Expression readIndex = createVariableGet(indexVariable);
Expression readIndexReplacement = inferrer.ensureAssignable(readIndexType,
indexResult.inferredType, readIndex, readIndex.fileOffset);
if (readIndexReplacement != null) {
readIndex = readIndexReplacement;
}
Expression read;
if (readTarget.isMissing) {
read = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(
indexGetName.name, receiverType),
node.readOffset,
noLength);
} else {
assert(readTarget.isExtensionMember);
read = new StaticInvocation(
readTarget.member,
new Arguments(<Expression>[
createVariableGet(receiverVariable),
readIndex,
], types: readTarget.inferredExtensionTypeArguments)
..fileOffset = node.readOffset)
..fileOffset = node.readOffset;
}
VariableDeclaration leftVariable;
Expression left;
if (node.forEffect) {
left = read;
} else if (node.forPostIncDec) {
leftVariable = createVariable(read, readType);
left = createVariableGet(leftVariable);
} else {
left = read;
}
ObjectAccessTarget binaryTarget = inferrer.findInterfaceMember(
readType, node.binaryName, node.binaryOffset,
includeExtensionMethods: true);
MethodContravarianceCheckKind binaryCheckKind =
inferrer.preCheckInvocationContravariance(readType, binaryTarget,
isThisReceiver: false);
DartType binaryType = inferrer.getReturnType(binaryTarget, readType);
DartType rhsType =
inferrer.getPositionalParameterTypeForTarget(binaryTarget, readType, 0);
ExpressionInferenceResult rhsResult =
inferrer.inferExpression(node.rhs, rhsType, true, isVoidAllowed: true);
inferrer.ensureAssignable(
rhsType, rhsResult.inferredType, node.rhs, node.rhs.fileOffset);
if (inferrer.isOverloadedArithmeticOperatorAndType(
binaryTarget, readType)) {
binaryType = inferrer.typeSchemaEnvironment
.getTypeOfOverloadedArithmetic(readType, rhsResult.inferredType);
}
Expression binary;
if (binaryTarget.isMissing) {
binary = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(node.binaryName.name, readType),
node.binaryOffset,
node.binaryName.name.length);
} else if (binaryTarget.isExtensionMember) {
binary = new StaticInvocation(
binaryTarget.member,
new Arguments(<Expression>[
left,
node.rhs,
], types: binaryTarget.inferredExtensionTypeArguments)
..fileOffset = node.binaryOffset)
..fileOffset = node.binaryOffset;
} else {
binary = new MethodInvocation(
left,
node.binaryName,
new Arguments(<Expression>[
node.rhs,
])
..fileOffset = node.binaryOffset,
binaryTarget.member)
..fileOffset = node.binaryOffset;
if (binaryCheckKind == MethodContravarianceCheckKind.checkMethodReturn) {
if (inferrer.instrumentation != null) {
inferrer.instrumentation.record(inferrer.uri, node.binaryOffset,
'checkReturn', new InstrumentationValueForType(readType));
}
binary = new AsExpression(binary, binaryType)
..isTypeError = true
..fileOffset = node.binaryOffset;
}
}
ObjectAccessTarget writeTarget = node.setter != null
? new ExtensionAccessTarget(
node.setter, null, ProcedureKind.Operator, extensionTypeArguments)
: const ObjectAccessTarget.missing();
DartType writeIndexType = inferrer.getPositionalParameterTypeForTarget(
writeTarget, receiverType, 0);
Expression writeIndex = createVariableGet(indexVariable);
Expression writeIndexReplacement = inferrer.ensureAssignable(writeIndexType,
indexResult.inferredType, writeIndex, writeIndex.fileOffset);
if (writeIndexReplacement != null) {
writeIndex = writeIndexReplacement;
}
DartType valueType =
inferrer.getIndexSetValueType(writeTarget, inferrer.thisType);
Expression binaryReplacement = inferrer.ensureAssignable(
valueType, binaryType, binary, node.fileOffset);
if (binaryReplacement != null) {
binary = binaryReplacement;
}
VariableDeclaration valueVariable;
Expression valueExpression;
if (node.forEffect || node.forPostIncDec) {
valueExpression = binary;
} else {
valueVariable = createVariable(binary, binaryType);
valueExpression = createVariableGet(valueVariable);
}
Expression write;
if (writeTarget.isMissing) {
write = inferrer.helper.buildProblem(
templateUndefinedMethod.withArguments(
indexSetName.name, receiverType),
node.writeOffset,
noLength);
} else {
assert(writeTarget.isExtensionMember);
write = new StaticInvocation(
writeTarget.member,
new Arguments(<Expression>[
createVariableGet(receiverVariable),
writeIndex,
valueExpression
], types: writeTarget.inferredExtensionTypeArguments)
..fileOffset = node.writeOffset)
..fileOffset = node.writeOffset;
}
Expression inner;
if (node.forEffect) {
assert(leftVariable == null);
assert(valueVariable == null);
// Encode `Extension(o)[a] += b` as:
//
// let receiverVariable = o in
// let indexVariable = a in
// receiverVariable.[]=(receiverVariable, o.[](indexVariable) + b)
//
inner = createLet(indexVariable, write);
} else if (node.forPostIncDec) {
// Encode `Extension(o)[a]++` as:
//
// let receiverVariable = o in
// let indexVariable = a in
// let leftVariable = receiverVariable.[](indexVariable)
// let writeVariable =
// receiverVariable.[]=(indexVariable, leftVariable + 1) in
// leftVariable
//
assert(leftVariable != null);
assert(valueVariable == null);
VariableDeclaration writeVariable =
createVariable(write, const VoidType());
inner = createLet(
indexVariable,
createLet(leftVariable,
createLet(writeVariable, createVariableGet(leftVariable))));
} else {
// Encode `Extension(o)[a] += b` as:
//
// let receiverVariable = o in
// let indexVariable = a in
// let valueVariable = receiverVariable.[](indexVariable) + b
// let writeVariable =
// receiverVariable.[]=(indexVariable, valueVariable) in
// valueVariable
//
assert(leftVariable == null);
assert(valueVariable != null);
VariableDeclaration writeVariable =
createVariable(write, const VoidType());
inner = createLet(
indexVariable,
createLet(valueVariable,
createLet(writeVariable, createVariableGet(valueVariable))));
}
Expression replacement = new Let(receiverVariable, inner)
..fileOffset = node.fileOffset;
node.replaceWith(replacement);
return new ExpressionInferenceResult(
node.forPostIncDec ? readType : binaryType, replacement);
}
@override
ExpressionInferenceResult visitNullLiteral(
NullLiteral node, DartType typeContext) {

View file

@ -184,13 +184,16 @@ class ClassInferenceInfo {
enum InternalExpressionKind {
Cascade,
CompoundExtensionIndexSet,
CompoundIndexSet,
CompoundPropertySet,
CompoundSuperIndexSet,
DeferredCheck,
ExtensionIndexSet,
ExtensionTearOff,
ExtensionSet,
IfNull,
IfNullExtensionIndexSet,
IfNullIndexSet,
IfNullPropertySet,
IfNullSet,
@ -1602,6 +1605,77 @@ class SuperIndexSet extends InternalExpression {
}
}
/// Internal expression representing an extension index set expression.
///
/// An extension index set expression of the form `Extension(o)[a] = b` used
/// for value is encoded as the expression:
///
/// let receiverVariable = o
/// let indexVariable = a in
/// let valueVariable = b in '
/// let writeVariable =
/// receiverVariable.[]=(indexVariable, valueVariable) in
/// valueVariable
///
/// An extension index set expression used for effect is encoded as
///
/// o.[]=(a, b)
///
/// using [StaticInvocation].
///
class ExtensionIndexSet extends InternalExpression {
final Extension extension;
final List<DartType> explicitTypeArguments;
/// The receiver of the extension access.
Expression receiver;
/// The []= member.
Member setter;
/// The index expression of the operation.
Expression index;
/// The value expression of the operation.
Expression value;
ExtensionIndexSet(this.extension, this.explicitTypeArguments, this.receiver,
this.setter, this.index, this.value)
: assert(explicitTypeArguments == null ||
explicitTypeArguments.length == extension.typeParameters.length) {
receiver?.parent = this;
index?.parent = this;
value?.parent = this;
}
@override
InternalExpressionKind get kind => InternalExpressionKind.ExtensionIndexSet;
@override
void visitChildren(Visitor<dynamic> v) {
receiver?.accept(v);
index?.accept(v);
value?.accept(v);
}
@override
void transformChildren(Transformer v) {
if (receiver != null) {
receiver = receiver.accept<TreeNode>(v);
receiver?.parent = this;
}
if (index != null) {
index = index.accept<TreeNode>(v);
index?.parent = this;
}
if (value != null) {
value = value.accept<TreeNode>(v);
value?.parent = this;
}
}
}
/// Internal expression representing an if-null index assignment.
///
/// An if-null index assignment of the form `o[a] ??= b` is, if used for value,
@ -1769,6 +1843,99 @@ class IfNullSuperIndexSet extends InternalExpression {
}
}
/// Internal expression representing an if-null super index set expression.
///
/// An if-null super index set expression of the form `super[a] ??= b` is, if
/// used for value, encoded as the expression:
///
/// let v1 = a in
/// let v2 = super.[](v1) in
/// v2 == null
/// ? (let v3 = b in
/// let _ = super.[]=(v1, v3) in
/// v3)
/// : v2
///
/// and, if used for effect, encoded as the expression:
///
/// let v1 = a in
/// let v2 = super.[](v1) in
/// v2 == null ? super.[]=(v1, b) : null
///
class IfNullExtensionIndexSet extends InternalExpression {
final Extension extension;
final List<DartType> explicitTypeArguments;
/// The extension receiver;
Expression receiver;
/// The [] member;
Member getter;
/// The []= member;
Member setter;
/// The index expression of the operation.
Expression index;
/// The value expression of the operation.
Expression value;
/// The file offset for the [] operation.
final int readOffset;
/// The file offset for the == operation.
final int testOffset;
/// The file offset for the []= operation.
final int writeOffset;
/// If `true`, the expression is only need for effect and not for its value.
final bool forEffect;
IfNullExtensionIndexSet(this.extension, this.explicitTypeArguments,
this.receiver, this.getter, this.setter, this.index, this.value,
{this.readOffset, this.testOffset, this.writeOffset, this.forEffect})
: assert(explicitTypeArguments == null ||
explicitTypeArguments.length == extension.typeParameters.length),
assert(readOffset != null),
assert(testOffset != null),
assert(writeOffset != null),
assert(forEffect != null) {
receiver?.parent = this;
index?.parent = this;
value?.parent = this;
}
@override
InternalExpressionKind get kind =>
InternalExpressionKind.IfNullExtensionIndexSet;
@override
void visitChildren(Visitor<dynamic> v) {
receiver?.accept(v);
index?.accept(v);
value?.accept(v);
}
@override
void transformChildren(Transformer v) {
if (receiver != null) {
receiver = receiver.accept<TreeNode>(v);
receiver?.parent = this;
}
if (index != null) {
index = index.accept<TreeNode>(v);
index?.parent = this;
}
if (value != null) {
value = value.accept<TreeNode>(v);
value?.parent = this;
}
}
}
/// Internal expression representing a compound index assignment.
///
/// An if-null index assignment of the form `o[a] += b` is, if used for value,
@ -2113,6 +2280,117 @@ class CompoundSuperIndexSet extends InternalExpression {
}
}
/// Internal expression representing a compound extension index assignment.
///
/// An compound extension index assignment of the form `Extension(o)[a] += b`
/// is, if used for value, encoded as the expression:
///
/// let receiverVariable = o;
/// let indexVariable = a in
/// let valueVariable = receiverVariable.[](indexVariable) + b
/// let writeVariable =
/// receiverVariable.[]=(indexVariable, valueVariable) in
/// valueVariable
///
/// and, if used for effect, encoded as the expression:
///
/// let receiverVariable = o;
/// let indexVariable = a in
/// receiverVariable.[]=(indexVariable,
/// receiverVariable.[](indexVariable) + b)
///
class CompoundExtensionIndexSet extends InternalExpression {
final Extension extension;
final List<DartType> explicitTypeArguments;
Expression receiver;
/// The [] member.
Member getter;
/// The []= member.
Member setter;
/// The index expression of the operation.
Expression index;
/// The name of the binary operation.
Name binaryName;
/// The right-hand side of the binary expression.
Expression rhs;
/// The file offset for the [] operation.
final int readOffset;
/// The file offset for the []= operation.
final int writeOffset;
/// The file offset for the binary operation.
final int binaryOffset;
/// If `true`, the expression is only need for effect and not for its value.
final bool forEffect;
/// If `true`, the expression is a post-fix inc/dec expression.
final bool forPostIncDec;
CompoundExtensionIndexSet(
this.extension,
this.explicitTypeArguments,
this.receiver,
this.getter,
this.setter,
this.index,
this.binaryName,
this.rhs,
{this.readOffset,
this.binaryOffset,
this.writeOffset,
this.forEffect,
this.forPostIncDec})
: assert(explicitTypeArguments == null ||
explicitTypeArguments.length == extension.typeParameters.length),
assert(readOffset != null),
assert(binaryOffset != null),
assert(writeOffset != null),
assert(forEffect != null),
assert(forPostIncDec != null) {
receiver?.parent = this;
index?.parent = this;
rhs?.parent = this;
fileOffset = binaryOffset;
}
@override
InternalExpressionKind get kind =>
InternalExpressionKind.CompoundExtensionIndexSet;
@override
void visitChildren(Visitor<dynamic> v) {
receiver?.accept(v);
index?.accept(v);
rhs?.accept(v);
}
@override
void transformChildren(Transformer v) {
if (receiver != null) {
receiver = receiver.accept<TreeNode>(v);
receiver?.parent = this;
}
if (index != null) {
index = index.accept<TreeNode>(v);
index?.parent = this;
}
if (rhs != null) {
rhs = rhs.accept<TreeNode>(v);
rhs?.parent = this;
}
}
}
/// Internal expression representing an assignment to an extension setter.
///
/// An extension set of the form `receiver.target = value` is, if used for
@ -2135,11 +2413,15 @@ class CompoundSuperIndexSet extends InternalExpression {
///
// TODO(johnniwinther): Rename read-only to side-effect-free.
class ExtensionSet extends InternalExpression {
final Extension extension;
final List<DartType> explicitTypeArguments;
/// The receiver for the assignment.
Expression receiver;
/// The extension member called for the assignment.
ObjectAccessTarget target;
Member target;
/// The right-hand side value of the assignment.
Expression value;
@ -2152,9 +2434,12 @@ class ExtensionSet extends InternalExpression {
/// variable.
final bool readOnlyReceiver;
ExtensionSet(this.receiver, this.target, this.value,
ExtensionSet(this.extension, this.explicitTypeArguments, this.receiver,
this.target, this.value,
{this.readOnlyReceiver, this.forEffect})
: assert(readOnlyReceiver != null),
: assert(explicitTypeArguments == null ||
explicitTypeArguments.length == extension.typeParameters.length),
assert(readOnlyReceiver != null),
assert(forEffect != null) {
receiver?.parent = this;
value?.parent = this;

View file

@ -47,6 +47,8 @@ abstract class ValueKind {
const _SingleValueKind<List<type.FormalParameterBuilder>>(
NullValue.FormalParameters);
static const ValueKind Generator = const _SingleValueKind<type.Generator>();
static const ValueKind Initializer =
const _SingleValueKind<type.Initializer>();
static const ValueKind MethodBody = const _SingleValueKind<type.MethodBody>();
static const ValueKind Modifiers =
const _SingleValueKind<List<type.Modifier>>();

View file

@ -66,7 +66,7 @@ import '../kernel/kernel_shadow_ast.dart'
import '../kernel/type_algorithms.dart' show hasAnyTypeVariables;
import '../names.dart' show callName, unaryMinusName;
import '../names.dart';
import '../problems.dart' show unexpected, unhandled;
@ -656,10 +656,29 @@ abstract class TypeInferrerImpl extends TypeInferrer {
return type is InterfaceType && type.classNode == coreTypes.nullClass;
}
List<DartType> _inferExtensionTypeArguments(
List<TypeParameter> typeParameters,
DartType onType,
DartType receiverType) {
/// Computes the type arguments for an access to an extension instance member
/// on [extension] with the static [receiverType]. If [explicitTypeArguments]
/// are provided, these are returned, otherwise type arguments are inferred
/// using [receiverType].
List<DartType> computeExtensionTypeArgument(Extension extension,
List<DartType> explicitTypeArguments, DartType receiverType) {
if (explicitTypeArguments != null) {
assert(explicitTypeArguments.length == extension.typeParameters.length);
return explicitTypeArguments;
} else if (extension.typeParameters.isEmpty) {
assert(explicitTypeArguments == null);
return const <DartType>[];
} else {
return inferExtensionTypeArguments(extension, receiverType);
}
}
/// Infers the type arguments for an access to an extension instance member
/// on [extension] with the static [receiverType].
List<DartType> inferExtensionTypeArguments(
Extension extension, DartType receiverType) {
List<TypeParameter> typeParameters = extension.typeParameters;
DartType onType = extension.onType;
List<DartType> inferredTypes =
new List<DartType>.filled(typeParameters.length, const UnknownType());
typeSchemaEnvironment.inferGenericFunctionOrType(
@ -719,8 +738,23 @@ abstract class TypeInferrerImpl extends TypeInferrer {
if (target.isUnresolved &&
receiverType is! DynamicType &&
includeExtensionMethods) {
Name otherName = name;
bool otherIsSetter;
if (name == indexGetName) {
// [] must be checked against []=.
otherName = indexSetName;
otherIsSetter = false;
} else if (name == indexSetName) {
// []= must be checked against [].
otherName = indexGetName;
otherIsSetter = false;
} else {
otherName = name;
otherIsSetter = !setter;
}
Member otherMember =
_getInterfaceMember(classNode, name, !setter, fileOffset);
_getInterfaceMember(classNode, otherName, otherIsSetter, fileOffset);
if (otherMember != null) {
// If we're looking for `foo` and `foo=` can be found or vice-versa then
// extension methods should not be found.
@ -730,12 +764,12 @@ abstract class TypeInferrerImpl extends TypeInferrer {
ExtensionAccessCandidate bestSoFar;
List<ExtensionAccessCandidate> noneMoreSpecific = [];
library.scope.forEachExtension((ExtensionBuilder extensionBuilder) {
MemberBuilder getterBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: false);
MemberBuilder setterBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: true);
if ((getterBuilder != null && !getterBuilder.isStatic) ||
(setterBuilder != null && !setterBuilder.isStatic)) {
MemberBuilder thisBuilder =
extensionBuilder.lookupLocalMember(name.name, setter: setter);
MemberBuilder otherBuilder = extensionBuilder
.lookupLocalMember(otherName.name, setter: otherIsSetter);
if ((thisBuilder != null && !thisBuilder.isStatic) ||
(otherBuilder != null && !otherBuilder.isStatic)) {
DartType onType;
DartType onTypeInstantiateToBounds;
List<DartType> inferredTypeArguments;
@ -746,10 +780,8 @@ abstract class TypeInferrerImpl extends TypeInferrer {
} else {
List<TypeParameter> typeParameters =
extensionBuilder.extension.typeParameters;
inferredTypeArguments = _inferExtensionTypeArguments(
extensionBuilder.extension.typeParameters,
extensionBuilder.extension.onType,
receiverType);
inferredTypeArguments = inferExtensionTypeArguments(
extensionBuilder.extension, receiverType);
Substitution inferredSubstitution =
Substitution.fromPairs(typeParameters, inferredTypeArguments);
@ -774,17 +806,14 @@ abstract class TypeInferrerImpl extends TypeInferrer {
}
if (typeSchemaEnvironment.isSubtypeOf(receiverType, onType)) {
MemberBuilder memberBuilder =
setter ? setterBuilder : getterBuilder;
ExtensionAccessCandidate candidate = new ExtensionAccessCandidate(
onType,
onTypeInstantiateToBounds,
memberBuilder != null
thisBuilder != null
? new ObjectAccessTarget.extensionMember(
memberBuilder.procedure,
memberBuilder.extensionTearOff,
memberBuilder.kind,
thisBuilder.procedure,
thisBuilder.extensionTearOff,
thisBuilder.kind,
inferredTypeArguments)
: const ObjectAccessTarget.missing(),
isPlatform: extensionBuilder.library.uri.scheme == 'dart');
@ -1154,6 +1183,19 @@ abstract class TypeInferrerImpl extends TypeInferrer {
throw unhandled('$target', 'getFunctionType', null, null);
}
/// Returns the type of the receiver argument in an access to an extension
/// member on [extension] with the given extension [typeArguments].
DartType getExtensionReceiverType(
Extension extension, List<DartType> typeArguments) {
DartType receiverType = extension.onType;
if (extension.typeParameters.isNotEmpty) {
Substitution substitution =
Substitution.fromPairs(extension.typeParameters, typeArguments);
return substitution.substituteType(receiverType);
}
return receiverType;
}
/// Returns the return type of the invocation of [target] on [receiverType].
// TODO(johnniwinther): Cleanup [getFunctionType], [getReturnType],
// [getIndexKeyType] and [getIndexSetValueType]. We shouldn't need that many.

View file

@ -127,7 +127,7 @@ main() {
" promotedType: void)",
new VariableUseGenerator(helper, token, variable, type));
check(
"PropertyAccessGenerator(offset: 4, _receiverVariable: null,"
"PropertyAccessGenerator(offset: 4,"
" receiver: expression, name: bar, getter: $uri::myGetter,"
" setter: $uri::mySetter)",
new PropertyAccessGenerator(

View file

@ -71,6 +71,12 @@ extension Extension on Class {
}
}
class GenericClass<T> {}
extension GenericExtension<T> on GenericClass<T> {
set setter(T value) {}
}
main() {
var c = new Class();
expect(null, c.field);
@ -191,9 +197,10 @@ main() {
new Class().testInternal();
GenericClass<int> genericClass = new GenericClass<int>();
expect(1, GenericExtension(genericClass).setter = 1);
}
expect(expected, actual) {
if (expected != actual) {
throw 'Mismatch: expected=$expected, actual=$actual';

View file

@ -7,6 +7,10 @@ class Class extends core::Object {
synthetic constructor •() → self::Class*
;
}
class GenericClass<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::GenericClass<self::GenericClass::T*>*
;
}
extension Extension on self::Class* {
get simpleSetter = self::Extension|get#simpleSetter;
get mutatingSetter = self::Extension|get#mutatingSetter;
@ -19,6 +23,9 @@ extension Extension on self::Class* {
set setterWithReturn = self::Extension|set#setterWithReturn;
set setterWithClosure = self::Extension|set#setterWithClosure;
}
extension GenericExtension<T extends core::Object* = dynamic> on self::GenericClass<T*>* {
set setter = self::GenericExtension|set#setter;
}
static method Extension|get#simpleSetter(final self::Class* #this) → core::int*
;
static method Extension|set#simpleSetter(final self::Class* #this, core::int* value) → void
@ -39,6 +46,8 @@ static method Extension|testInternal(final self::Class* #this) → dynamic
;
static method Extension|get#testInternal(final self::Class* #this) → () →* dynamic
return () → dynamic => self::Extension|testInternal(#this);
static method GenericExtension|set#setter<T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#setter::T*>* #this, self::GenericExtension|set#setter::T* value) → void
;
static method main() → dynamic
;
static method expect(dynamic expected, dynamic actual) → dynamic

View file

@ -8,6 +8,11 @@ class Class extends core::Object {
: super core::Object::•()
;
}
class GenericClass<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::GenericClass<self::GenericClass::T*>*
: super core::Object::•()
;
}
extension Extension on self::Class* {
get simpleSetter = self::Extension|get#simpleSetter;
get mutatingSetter = self::Extension|get#mutatingSetter;
@ -20,6 +25,9 @@ extension Extension on self::Class* {
set setterWithReturn = self::Extension|set#setterWithReturn;
set setterWithClosure = self::Extension|set#setterWithClosure;
}
extension GenericExtension<T extends core::Object* = dynamic> on self::GenericClass<T*>* {
set setter = self::GenericExtension|set#setter;
}
static method Extension|get#simpleSetter(final self::Class* #this) → core::int*
return #this.{self::Class::field};
static method Extension|set#simpleSetter(final self::Class* #this, core::int* value) → void {
@ -76,6 +84,7 @@ static method Extension|testInternal(final self::Class* #this) → dynamic {
}
static method Extension|get#testInternal(final self::Class* #this) → () →* dynamic
return () → dynamic => self::Extension|testInternal(#this);
static method GenericExtension|set#setter<T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#setter::T*>* #this, self::GenericExtension|set#setter::T* value) → void {}
static method main() → dynamic {
self::Class* c = new self::Class::•();
self::expect(null, c.{self::Class::field});
@ -184,6 +193,8 @@ static method main() → dynamic {
let final self::Class* #t129 = c in #t129.{core::Object::==}(null) ?{core::Null?} null : #t129.{self::Class::field} = null;
self::expect(2, let final self::Class* #t130 = c in #t130.{core::Object::==}(null) ?{core::int*} null : let final core::int* #t131 = self::Extension|get#simpleSetter(#t130) in #t131.{core::num::==}(null) ?{core::int*} let final core::int* #t132 = 2 in let final void #t133 = self::Extension|set#simpleSetter(#t130, #t132) in #t132 : #t131);
self::Extension|testInternal(new self::Class::•());
self::GenericClass<core::int*>* genericClass = new self::GenericClass::•<core::int*>();
self::expect(1, let final self::GenericClass<core::int*>* #t134 = genericClass in let final core::int* #t135 = 1 in let final void #t136 = self::GenericExtension|set#setter<core::int*>(#t134, #t135) in #t135);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {

View file

@ -8,6 +8,11 @@ class Class extends core::Object {
: super core::Object::•()
;
}
class GenericClass<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::GenericClass<self::GenericClass::T*>*
: super core::Object::•()
;
}
extension Extension on self::Class* {
get simpleSetter = self::Extension|get#simpleSetter;
get mutatingSetter = self::Extension|get#mutatingSetter;
@ -20,6 +25,9 @@ extension Extension on self::Class* {
set setterWithReturn = self::Extension|set#setterWithReturn;
set setterWithClosure = self::Extension|set#setterWithClosure;
}
extension GenericExtension<T extends core::Object* = dynamic> on self::GenericClass<T*>* {
set setter = self::GenericExtension|set#setter;
}
static method Extension|get#simpleSetter(final self::Class* #this) → core::int*
return #this.{self::Class::field};
static method Extension|set#simpleSetter(final self::Class* #this, core::int* value) → void {
@ -76,6 +84,7 @@ static method Extension|testInternal(final self::Class* #this) → dynamic {
}
static method Extension|get#testInternal(final self::Class* #this) → () →* dynamic
return () → dynamic => self::Extension|testInternal(#this);
static method GenericExtension|set#setter<T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#setter::T*>* #this, self::GenericExtension|set#setter::T* value) → void {}
static method main() → dynamic {
self::Class* c = new self::Class::•();
self::expect(null, c.{self::Class::field});
@ -184,6 +193,8 @@ static method main() → dynamic {
let final self::Class* #t129 = c in #t129.{core::Object::==}(null) ?{core::Null?} null : #t129.{self::Class::field} = null;
self::expect(2, let final self::Class* #t130 = c in #t130.{core::Object::==}(null) ?{core::int*} null : let final core::int* #t131 = self::Extension|get#simpleSetter(#t130) in #t131.{core::num::==}(null) ?{core::int*} let final core::int* #t132 = 2 in let final void #t133 = self::Extension|set#simpleSetter(#t130, #t132) in #t132 : #t131);
self::Extension|testInternal(new self::Class::•());
self::GenericClass<core::int*>* genericClass = new self::GenericClass::•<core::int*>();
self::expect(1, let final self::GenericClass<core::int*>* #t134 = genericClass in let final core::int* #t135 = 1 in let final void #t136 = self::GenericExtension|set#setter<core::int*>(#t134, #t135) in #t135);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {

View file

@ -0,0 +1,21 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
class GenericClass<T> {}
extension GenericExtension<T> on GenericClass<T> {
set setter(T value) {}
}
error() {
GenericClass<int> genericClass = new GenericClass<int>();
expect(null, GenericExtension<double>(genericClass).setter = null);
}
expect(expected, actual) {
if (expected != actual) {
throw 'Mismatch: expected=$expected, actual=$actual';
}
}

View file

@ -0,0 +1,17 @@
library;
import self as self;
import "dart:core" as core;
class GenericClass<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::GenericClass<self::GenericClass::T*>*
;
}
extension GenericExtension<T extends core::Object* = dynamic> on self::GenericClass<T*>* {
set setter = self::GenericExtension|set#setter;
}
static method GenericExtension|set#setter<T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#setter::T*>* #this, self::GenericExtension|set#setter::T* value) → void
;
static method error() → dynamic
;
static method expect(dynamic expected, dynamic actual) → dynamic
;

View file

@ -0,0 +1,35 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/extensions/extension_setter_error.dart:13:41: Error: A value of type 'GenericClass<int>' can't be assigned to a variable of type 'GenericClass<double>'.
// - 'GenericClass' is from 'pkg/front_end/testcases/extensions/extension_setter_error.dart'.
// Try changing the type of the left hand side, or casting the right hand side to 'GenericClass<double>'.
// expect(null, GenericExtension<double>(genericClass).setter = null);
// ^
//
import self as self;
import "dart:core" as core;
class GenericClass<T extends core::Object* = dynamic> extends core::Object {
synthetic constructor •() → self::GenericClass<self::GenericClass::T*>*
: super core::Object::•()
;
}
extension GenericExtension<T extends core::Object* = dynamic> on self::GenericClass<T*>* {
set setter = self::GenericExtension|set#setter;
}
static method GenericExtension|set#setter<T extends core::Object* = dynamic>(final self::GenericClass<self::GenericExtension|set#setter::T*>* #this, self::GenericExtension|set#setter::T* value) → void {}
static method error() → dynamic {
self::GenericClass<core::int*>* genericClass = new self::GenericClass::•<core::int*>();
self::expect(null, let final self::GenericClass<core::int*>* #t1 = let final<BottomType> #t2 = invalid-expression "pkg/front_end/testcases/extensions/extension_setter_error.dart:13:41: Error: A value of type 'GenericClass<int>' can't be assigned to a variable of type 'GenericClass<double>'.
- 'GenericClass' is from 'pkg/front_end/testcases/extensions/extension_setter_error.dart'.
Try changing the type of the left hand side, or casting the right hand side to 'GenericClass<double>'.
expect(null, GenericExtension<double>(genericClass).setter = null);
^" in genericClass as{TypeError} self::GenericClass<core::double*>* in let final core::Null? #t3 = null in let final void #t4 = self::GenericExtension|set#setter<core::double*>(#t1, #t3) in #t3);
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";
}
}

View file

@ -15,6 +15,12 @@ extension Extension<K, V> on MapLike<K, V> {
}
main() {
implicit();
explicitWithTypeArguments();
explicitInferredTypeArguments();
}
implicit() {
MapLike<int, String> map1 = new MapLike();
expect(null, map1[0]);
map1.put(0, '0');
@ -41,7 +47,64 @@ main() {
expect(6, map2[0]);
expect(5, --map2[0]);
expect(5, map2[0]);
}
explicitWithTypeArguments() {
MapLike<int, String> map1 = new MapLike();
expect(null, Extension<int, String>(map1)[0]);
map1.put(0, '0');
expect('0', Extension<int, String>(map1)[0]);
expect(null, Extension<int, String>(map1)[1]);
Extension<int, String>(map1)[1] = '1';
expect('1', Extension<int, String>(map1)[1]);
expect('2', Extension<int, String>(map1)[1] = '2');
expect('2', Extension<int, String>(map1)[1]);
Extension<int, String>(map1)[1] ??= '3';
expect('2', Extension<int, String>(map1)[1]);
expect('2', Extension<int, String>(map1)[1] ??= '4');
expect('2', Extension<int, String>(map1)[1]);
Extension<int, String>(map1)[2] ??= '2';
expect('2', Extension<int, String>(map1)[2]);
expect('3', Extension<int, String>(map1)[3] ??= '3');
expect('3', Extension<int, String>(map1)[3]);
MapLike<int, int> map2 = new MapLike();
expect(1, Extension<int, int>(map2)[0] = 1);
expect(3, Extension<int, int>(map2)[0] += 2);
expect(5, Extension<int, int>(map2)[0] += 2);
expect(5, Extension<int, int>(map2)[0]++);
expect(6, Extension<int, int>(map2)[0]);
expect(5, --Extension<int, int>(map2)[0]);
expect(5, Extension<int, int>(map2)[0]);
}
explicitInferredTypeArguments() {
MapLike<int, String> map1 = new MapLike();
expect(null, Extension(map1)[0]);
map1.put(0, '0');
expect('0', Extension(map1)[0]);
expect(null, Extension(map1)[1]);
Extension(map1)[1] = '1';
expect('1', Extension(map1)[1]);
expect('2', Extension(map1)[1] = '2');
expect('2', Extension(map1)[1]);
Extension(map1)[1] ??= '3';
expect('2', Extension(map1)[1]);
expect('2', Extension(map1)[1] ??= '4');
expect('2', Extension(map1)[1]);
Extension(map1)[2] ??= '2';
expect('2', Extension(map1)[2]);
expect('3', Extension(map1)[3] ??= '3');
expect('3', Extension(map1)[3]);
MapLike<int, int> map2 = new MapLike();
expect(1, Extension(map2)[0] = 1);
expect(3, Extension(map2)[0] += 2);
expect(5, Extension(map2)[0] += 2);
expect(5, Extension(map2)[0]++);
expect(6, Extension(map2)[0]);
expect(5, --Extension(map2)[0]);
expect(5, Extension(map2)[0]);
}
expect(expected, actual) {

View file

@ -21,5 +21,11 @@ static method Extension|[]=<K extends core::Object* = dynamic, V extends core::O
;
static method main() → dynamic
;
static method implicit() → dynamic
;
static method explicitWithTypeArguments() → dynamic
;
static method explicitInferredTypeArguments() → dynamic
;
static method expect(dynamic expected, dynamic actual) → dynamic
;

View file

@ -21,6 +21,11 @@ static method Extension|[]<K extends core::Object* = dynamic, V extends core::Ob
static method Extension|[]=<K extends core::Object* = dynamic, V extends core::Object* = dynamic>(final self::MapLike<self::Extension|[]=::K*, self::Extension|[]=::V*>* #this, self::Extension|[]=::K* key, self::Extension|[]=::V* value) → void
return #this.{self::MapLike::put}(key, value);
static method main() → dynamic {
self::implicit();
self::explicitWithTypeArguments();
self::explicitInferredTypeArguments();
}
static method implicit() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
@ -47,6 +52,60 @@ static method main() → dynamic {
self::expect(5, let final self::MapLike<core::int*, core::int*>* #t39 = map2 in let final core::int* #t40 = 0 in let final core::int* #t41 = self::Extension|[]<core::int*, core::int*>(#t39, #t40).{core::num::-}(1) in let final void #t42 = self::Extension|[]=<core::int*, core::int*>(#t39, #t40, #t41) in #t41);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method explicitWithTypeArguments() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
self::expect("0", self::Extension|[]<core::int*, core::String*>(map1, 0));
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 1));
self::Extension|[]=<core::int*, core::String*>(map1, 1, "1");
self::expect("1", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t43 = map1 in let final core::int* #t44 = 1 in let final core::String* #t45 = "2" in let final void #t46 = self::Extension|[]=<core::int*, core::String*>(#t43, #t44, #t45) in #t45);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t47 = map1 in let final core::int* #t48 = 1 in self::Extension|[]<core::int*, core::String*>(#t47, #t48).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t47, #t48, "3") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t49 = map1 in let final core::int* #t50 = 1 in let final core::String* #t51 = self::Extension|[]<core::int*, core::String*>(#t49, #t50) in #t51.{core::String::==}(null) ?{core::String*} let final core::String* #t52 = "4" in let final void #t53 = self::Extension|[]=<core::int*, core::String*>(#t49, #t50, #t52) in #t52 : #t51);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t54 = map1 in let final core::int* #t55 = 2 in self::Extension|[]<core::int*, core::String*>(#t54, #t55).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t54, #t55, "2") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 2));
self::expect("3", let final self::MapLike<core::int*, core::String*>* #t56 = map1 in let final core::int* #t57 = 3 in let final core::String* #t58 = self::Extension|[]<core::int*, core::String*>(#t56, #t57) in #t58.{core::String::==}(null) ?{core::String*} let final core::String* #t59 = "3" in let final void #t60 = self::Extension|[]=<core::int*, core::String*>(#t56, #t57, #t59) in #t59 : #t58);
self::expect("3", self::Extension|[]<core::int*, core::String*>(map1, 3));
self::MapLike<core::int*, core::int*>* map2 = new self::MapLike::•<core::int*, core::int*>();
self::expect(1, let final self::MapLike<core::int*, core::int*>* #t61 = map2 in let final core::int* #t62 = 0 in let final core::int* #t63 = 1 in let final void #t64 = self::Extension|[]=<core::int*, core::int*>(#t61, #t62, #t63) in #t63);
self::expect(3, let final core::Object* #t65 = map2 in let final core::int* #t66 = 0 in let final core::int* #t67 = self::Extension|[]<core::int*, core::int*>(#t65, #t66).{core::num::+}(2) in let final void #t68 = self::Extension|[]=<core::int*, core::int*>(#t65, #t66, #t67) in #t67);
self::expect(5, let final core::Object* #t69 = map2 in let final core::int* #t70 = 0 in let final core::int* #t71 = self::Extension|[]<core::int*, core::int*>(#t69, #t70).{core::num::+}(2) in let final void #t72 = self::Extension|[]=<core::int*, core::int*>(#t69, #t70, #t71) in #t71);
self::expect(5, let final core::Object* #t73 = map2 in let final core::int* #t74 = 0 in let final core::int* #t75 = self::Extension|[]<core::int*, core::int*>(#t73, #t74) in let final void #t76 = self::Extension|[]=<core::int*, core::int*>(#t73, #t74, #t75.{core::num::+}(1)) in #t75);
self::expect(6, self::Extension|[]<core::int*, core::int*>(map2, 0));
self::expect(5, let final core::Object* #t77 = map2 in let final core::int* #t78 = 0 in let final core::int* #t79 = self::Extension|[]<core::int*, core::int*>(#t77, #t78).{core::num::-}(1) in let final void #t80 = self::Extension|[]=<core::int*, core::int*>(#t77, #t78, #t79) in #t79);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method explicitInferredTypeArguments() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
self::expect("0", self::Extension|[]<core::int*, core::String*>(map1, 0));
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 1));
self::Extension|[]=<core::int*, core::String*>(map1, 1, "1");
self::expect("1", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t81 = map1 in let final core::int* #t82 = 1 in let final core::String* #t83 = "2" in let final void #t84 = self::Extension|[]=<core::int*, core::String*>(#t81, #t82, #t83) in #t83);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t85 = map1 in let final core::int* #t86 = 1 in self::Extension|[]<core::int*, core::String*>(#t85, #t86).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t85, #t86, "3") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t87 = map1 in let final core::int* #t88 = 1 in let final core::String* #t89 = self::Extension|[]<core::int*, core::String*>(#t87, #t88) in #t89.{core::String::==}(null) ?{core::String*} let final core::String* #t90 = "4" in let final void #t91 = self::Extension|[]=<core::int*, core::String*>(#t87, #t88, #t90) in #t90 : #t89);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t92 = map1 in let final core::int* #t93 = 2 in self::Extension|[]<core::int*, core::String*>(#t92, #t93).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t92, #t93, "2") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 2));
self::expect("3", let final self::MapLike<core::int*, core::String*>* #t94 = map1 in let final core::int* #t95 = 3 in let final core::String* #t96 = self::Extension|[]<core::int*, core::String*>(#t94, #t95) in #t96.{core::String::==}(null) ?{core::String*} let final core::String* #t97 = "3" in let final void #t98 = self::Extension|[]=<core::int*, core::String*>(#t94, #t95, #t97) in #t97 : #t96);
self::expect("3", self::Extension|[]<core::int*, core::String*>(map1, 3));
self::MapLike<core::int*, core::int*>* map2 = new self::MapLike::•<core::int*, core::int*>();
self::expect(1, let final self::MapLike<core::int*, core::int*>* #t99 = map2 in let final core::int* #t100 = 0 in let final core::int* #t101 = 1 in let final void #t102 = self::Extension|[]=<core::int*, core::int*>(#t99, #t100, #t101) in #t101);
self::expect(3, let final core::Object* #t103 = map2 in let final core::int* #t104 = 0 in let final core::int* #t105 = self::Extension|[]<core::int*, core::int*>(#t103, #t104).{core::num::+}(2) in let final void #t106 = self::Extension|[]=<core::int*, core::int*>(#t103, #t104, #t105) in #t105);
self::expect(5, let final core::Object* #t107 = map2 in let final core::int* #t108 = 0 in let final core::int* #t109 = self::Extension|[]<core::int*, core::int*>(#t107, #t108).{core::num::+}(2) in let final void #t110 = self::Extension|[]=<core::int*, core::int*>(#t107, #t108, #t109) in #t109);
self::expect(5, let final core::Object* #t111 = map2 in let final core::int* #t112 = 0 in let final core::int* #t113 = self::Extension|[]<core::int*, core::int*>(#t111, #t112) in let final void #t114 = self::Extension|[]=<core::int*, core::int*>(#t111, #t112, #t113.{core::num::+}(1)) in #t113);
self::expect(6, self::Extension|[]<core::int*, core::int*>(map2, 0));
self::expect(5, let final core::Object* #t115 = map2 in let final core::int* #t116 = 0 in let final core::int* #t117 = self::Extension|[]<core::int*, core::int*>(#t115, #t116).{core::num::-}(1) in let final void #t118 = self::Extension|[]=<core::int*, core::int*>(#t115, #t116, #t117) in #t117);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";

View file

@ -21,6 +21,11 @@ static method Extension|[]<K extends core::Object* = dynamic, V extends core::Ob
static method Extension|[]=<K extends core::Object* = dynamic, V extends core::Object* = dynamic>(final self::MapLike<self::Extension|[]=::K*, self::Extension|[]=::V*>* #this, self::Extension|[]=::K* key, self::Extension|[]=::V* value) → void
return #this.{self::MapLike::put}(key, value);
static method main() → dynamic {
self::implicit();
self::explicitWithTypeArguments();
self::explicitInferredTypeArguments();
}
static method implicit() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
@ -47,6 +52,60 @@ static method main() → dynamic {
self::expect(5, let final self::MapLike<core::int*, core::int*>* #t39 = map2 in let final core::int* #t40 = 0 in let final core::int* #t41 = self::Extension|[]<core::int*, core::int*>(#t39, #t40).{core::num::-}(1) in let final void #t42 = self::Extension|[]=<core::int*, core::int*>(#t39, #t40, #t41) in #t41);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method explicitWithTypeArguments() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
self::expect("0", self::Extension|[]<core::int*, core::String*>(map1, 0));
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 1));
self::Extension|[]=<core::int*, core::String*>(map1, 1, "1");
self::expect("1", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t43 = map1 in let final core::int* #t44 = 1 in let final core::String* #t45 = "2" in let final void #t46 = self::Extension|[]=<core::int*, core::String*>(#t43, #t44, #t45) in #t45);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t47 = map1 in let final core::int* #t48 = 1 in self::Extension|[]<core::int*, core::String*>(#t47, #t48).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t47, #t48, "3") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t49 = map1 in let final core::int* #t50 = 1 in let final core::String* #t51 = self::Extension|[]<core::int*, core::String*>(#t49, #t50) in #t51.{core::String::==}(null) ?{core::String*} let final core::String* #t52 = "4" in let final void #t53 = self::Extension|[]=<core::int*, core::String*>(#t49, #t50, #t52) in #t52 : #t51);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t54 = map1 in let final core::int* #t55 = 2 in self::Extension|[]<core::int*, core::String*>(#t54, #t55).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t54, #t55, "2") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 2));
self::expect("3", let final self::MapLike<core::int*, core::String*>* #t56 = map1 in let final core::int* #t57 = 3 in let final core::String* #t58 = self::Extension|[]<core::int*, core::String*>(#t56, #t57) in #t58.{core::String::==}(null) ?{core::String*} let final core::String* #t59 = "3" in let final void #t60 = self::Extension|[]=<core::int*, core::String*>(#t56, #t57, #t59) in #t59 : #t58);
self::expect("3", self::Extension|[]<core::int*, core::String*>(map1, 3));
self::MapLike<core::int*, core::int*>* map2 = new self::MapLike::•<core::int*, core::int*>();
self::expect(1, let final self::MapLike<core::int*, core::int*>* #t61 = map2 in let final core::int* #t62 = 0 in let final core::int* #t63 = 1 in let final void #t64 = self::Extension|[]=<core::int*, core::int*>(#t61, #t62, #t63) in #t63);
self::expect(3, let final core::Object* #t65 = map2 in let final core::int* #t66 = 0 in let final core::int* #t67 = self::Extension|[]<core::int*, core::int*>(#t65, #t66).{core::num::+}(2) in let final void #t68 = self::Extension|[]=<core::int*, core::int*>(#t65, #t66, #t67) in #t67);
self::expect(5, let final core::Object* #t69 = map2 in let final core::int* #t70 = 0 in let final core::int* #t71 = self::Extension|[]<core::int*, core::int*>(#t69, #t70).{core::num::+}(2) in let final void #t72 = self::Extension|[]=<core::int*, core::int*>(#t69, #t70, #t71) in #t71);
self::expect(5, let final core::Object* #t73 = map2 in let final core::int* #t74 = 0 in let final core::int* #t75 = self::Extension|[]<core::int*, core::int*>(#t73, #t74) in let final void #t76 = self::Extension|[]=<core::int*, core::int*>(#t73, #t74, #t75.{core::num::+}(1)) in #t75);
self::expect(6, self::Extension|[]<core::int*, core::int*>(map2, 0));
self::expect(5, let final core::Object* #t77 = map2 in let final core::int* #t78 = 0 in let final core::int* #t79 = self::Extension|[]<core::int*, core::int*>(#t77, #t78).{core::num::-}(1) in let final void #t80 = self::Extension|[]=<core::int*, core::int*>(#t77, #t78, #t79) in #t79);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method explicitInferredTypeArguments() → dynamic {
self::MapLike<core::int*, core::String*>* map1 = new self::MapLike::•<core::int*, core::String*>();
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 0));
map1.{self::MapLike::put}(0, "0");
self::expect("0", self::Extension|[]<core::int*, core::String*>(map1, 0));
self::expect(null, self::Extension|[]<core::int*, core::String*>(map1, 1));
self::Extension|[]=<core::int*, core::String*>(map1, 1, "1");
self::expect("1", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t81 = map1 in let final core::int* #t82 = 1 in let final core::String* #t83 = "2" in let final void #t84 = self::Extension|[]=<core::int*, core::String*>(#t81, #t82, #t83) in #t83);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t85 = map1 in let final core::int* #t86 = 1 in self::Extension|[]<core::int*, core::String*>(#t85, #t86).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t85, #t86, "3") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
self::expect("2", let final self::MapLike<core::int*, core::String*>* #t87 = map1 in let final core::int* #t88 = 1 in let final core::String* #t89 = self::Extension|[]<core::int*, core::String*>(#t87, #t88) in #t89.{core::String::==}(null) ?{core::String*} let final core::String* #t90 = "4" in let final void #t91 = self::Extension|[]=<core::int*, core::String*>(#t87, #t88, #t90) in #t90 : #t89);
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 1));
let final self::MapLike<core::int*, core::String*>* #t92 = map1 in let final core::int* #t93 = 2 in self::Extension|[]<core::int*, core::String*>(#t92, #t93).{core::String::==}(null) ?{core::String*} self::Extension|[]=<core::int*, core::String*>(#t92, #t93, "2") : null;
self::expect("2", self::Extension|[]<core::int*, core::String*>(map1, 2));
self::expect("3", let final self::MapLike<core::int*, core::String*>* #t94 = map1 in let final core::int* #t95 = 3 in let final core::String* #t96 = self::Extension|[]<core::int*, core::String*>(#t94, #t95) in #t96.{core::String::==}(null) ?{core::String*} let final core::String* #t97 = "3" in let final void #t98 = self::Extension|[]=<core::int*, core::String*>(#t94, #t95, #t97) in #t97 : #t96);
self::expect("3", self::Extension|[]<core::int*, core::String*>(map1, 3));
self::MapLike<core::int*, core::int*>* map2 = new self::MapLike::•<core::int*, core::int*>();
self::expect(1, let final self::MapLike<core::int*, core::int*>* #t99 = map2 in let final core::int* #t100 = 0 in let final core::int* #t101 = 1 in let final void #t102 = self::Extension|[]=<core::int*, core::int*>(#t99, #t100, #t101) in #t101);
self::expect(3, let final core::Object* #t103 = map2 in let final core::int* #t104 = 0 in let final core::int* #t105 = self::Extension|[]<core::int*, core::int*>(#t103, #t104).{core::num::+}(2) in let final void #t106 = self::Extension|[]=<core::int*, core::int*>(#t103, #t104, #t105) in #t105);
self::expect(5, let final core::Object* #t107 = map2 in let final core::int* #t108 = 0 in let final core::int* #t109 = self::Extension|[]<core::int*, core::int*>(#t107, #t108).{core::num::+}(2) in let final void #t110 = self::Extension|[]=<core::int*, core::int*>(#t107, #t108, #t109) in #t109);
self::expect(5, let final core::Object* #t111 = map2 in let final core::int* #t112 = 0 in let final core::int* #t113 = self::Extension|[]<core::int*, core::int*>(#t111, #t112) in let final void #t114 = self::Extension|[]=<core::int*, core::int*>(#t111, #t112, #t113.{core::num::+}(1)) in #t113);
self::expect(6, self::Extension|[]<core::int*, core::int*>(map2, 0));
self::expect(5, let final core::Object* #t115 = map2 in let final core::int* #t116 = 0 in let final core::int* #t117 = self::Extension|[]<core::int*, core::int*>(#t115, #t116).{core::num::-}(1) in let final void #t118 = self::Extension|[]=<core::int*, core::int*>(#t115, #t116, #t117) in #t117);
self::expect(5, self::Extension|[]<core::int*, core::int*>(map2, 0));
}
static method expect(dynamic expected, dynamic actual) → dynamic {
if(!expected.{core::Object::==}(actual)) {
throw "Mismatch: expected=${expected}, actual=${actual}";

View file

@ -4,7 +4,7 @@ library test;
//
// pkg/front_end/testcases/inference/unresolved_super.dart:16:37: Error: Superclass has no method named '[]='.
// var /*@type=dynamic*/ v5 = super[0] = /*@typeArgs=dynamic*/ f();
// ^^^
// ^
//
import self as self;
import "dart:core" as core;
@ -16,7 +16,7 @@ class C extends core::Object {
method test() → void {
dynamic v5 = let final core::int* #t1 = 0 in let final dynamic #t2 = self::f<dynamic>() in let final void #t3 = invalid-expression "pkg/front_end/testcases/inference/unresolved_super.dart:16:37: Error: Superclass has no method named '[]='.
var /*@type=dynamic*/ v5 = super[0] = /*@typeArgs=dynamic*/ f();
^^^" in #t2;
^" in #t2;
}
}
static method f<T extends core::Object* = dynamic>() → self::f::T*

View file

@ -4,7 +4,7 @@ library test;
//
// pkg/front_end/testcases/inference/unresolved_super.dart:16:37: Error: Superclass has no method named '[]='.
// var /*@type=dynamic*/ v5 = super[0] = /*@typeArgs=dynamic*/ f();
// ^^^
// ^
//
import self as self;
import "dart:core" as core;
@ -16,7 +16,7 @@ class C extends core::Object {
method test() → void {
dynamic v5 = let final core::int* #t1 = 0 in let final dynamic #t2 = self::f<dynamic>() in let final void #t3 = invalid-expression "pkg/front_end/testcases/inference/unresolved_super.dart:16:37: Error: Superclass has no method named '[]='.
var /*@type=dynamic*/ v5 = super[0] = /*@typeArgs=dynamic*/ f();
^^^" in #t2;
^" in #t2;
}
}
static method f<T extends core::Object* = dynamic>() → self::f::T*

View file

@ -6,6 +6,7 @@
# Kernel ASTs directly, that is, code in pkg/fasta/lib/src/kernel/ with
# strong-mode enabled.
extensions/extension_setter_error: TypeCheckError
extensions/instance_access_of_static: RuntimeError
extensions/invalid_explicit_access: RuntimeError
extensions/static_access_of_instance: RuntimeError

View file

@ -24,6 +24,7 @@ extensions/explicit_invalid_access: TextSerializationFailure
extensions/explicit_this: TextSerializationFailure
extensions/extension_methods: TextSerializationFailure
extensions/extension_setter: TextSerializationFailure
extensions/extension_setter_error: TypeCheckError
extensions/getter_setter_conflict: TextSerializationFailure
extensions/if_null: TextSerializationFailure
extensions/implicit_extension_inference: TextSerializationFailure

View file

@ -153,11 +153,17 @@ void test1() {
c1b.m1 += 0;
// ^^
// [analyzer] COMPILE_TIME_ERROR.AMBIGUOUS_EXTENSION_MEMBER_ACCESS
// ^^
// [cfe] unspecified
// ^^
// [cfe] unspecified
c1b.m1++;
// ^^
// [analyzer] COMPILE_TIME_ERROR.AMBIGUOUS_EXTENSION_MEMBER_ACCESS
// ^^
// [cfe] unspecified
// ^^
// [cfe] unspecified
c1b.m2;
@ -165,7 +171,6 @@ void test1() {
// [analyzer] COMPILE_TIME_ERROR.AMBIGUOUS_EXTENSION_MEMBER_ACCESS
// [cfe] unspecified
c1b[0];
// ^^
// [analyzer] unspecified
@ -179,11 +184,17 @@ void test1() {
c1b[0] += 0;
// ^^
// [analyzer] unspecified
// ^
// [cfe] unspecified
// ^
// [cfe] unspecified
c1b[0]++;
// ^^
// [analyzer] unspecified
// ^
// [cfe] unspecified
// ^
// [cfe] unspecified
}