mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 15:57:17 +00:00
[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:
parent
0bf6b6ae64
commit
ee7aae43c0
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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: ");
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>>();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
}
|
|
@ -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
|
||||
;
|
|
@ -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}";
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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}";
|
||||
|
|
|
@ -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}";
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue