From ee7aae43c0c5d13120f64ff39e96906951d5a042 Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Thu, 26 Sep 2019 10:36:51 +0000 Subject: [PATCH] [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 --- .../lib/src/fasta/kernel/body_builder.dart | 35 +- .../fasta/kernel/expression_generator.dart | 413 ++++++++++++- .../src/fasta/kernel/inference_visitor.dart | 566 +++++++++++++++++- .../src/fasta/kernel/kernel_shadow_ast.dart | 291 ++++++++- .../lib/src/fasta/source/value_kinds.dart | 2 + .../fasta/type_inference/type_inferrer.dart | 88 ++- .../test/fasta/generator_to_string_test.dart | 2 +- .../extensions/extension_setter.dart | 9 +- .../extension_setter.dart.outline.expect | 9 + .../extension_setter.dart.strong.expect | 11 + ...sion_setter.dart.strong.transformed.expect | 11 + .../extensions/extension_setter_error.dart | 21 + ...extension_setter_error.dart.outline.expect | 17 + .../extension_setter_error.dart.strong.expect | 35 ++ pkg/front_end/testcases/extensions/index.dart | 63 ++ .../extensions/index.dart.outline.expect | 6 + .../extensions/index.dart.strong.expect | 59 ++ .../index.dart.strong.transformed.expect | 59 ++ .../unresolved_super.dart.strong.expect | 4 +- ...olved_super.dart.strong.transformed.expect | 4 +- pkg/front_end/testcases/strong.status | 1 + .../testcases/text_serialization.status | 1 + ...xtension_getter_setter_conflicts_test.dart | 13 +- 23 files changed, 1623 insertions(+), 97 deletions(-) create mode 100644 pkg/front_end/testcases/extensions/extension_setter_error.dart create mode 100644 pkg/front_end/testcases/extensions/extension_setter_error.dart.outline.expect create mode 100644 pkg/front_end/testcases/extensions/extension_setter_error.dart.strong.expect diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart index b793495fbc9..dd4884bc232 100644 --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart @@ -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 } 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 @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)); } diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart index 74545f88853..d3c22498f02 100644 --- a/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart +++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator.dart @@ -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 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(a)[index]`. + final Expression receiver; + + /// The index expression; + final Expression index; + + /// The type arguments explicitly passed to the explicit extension access, + /// like `` in `Extension(a)[b]`. + final List 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 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 _createExtensionTypeArguments() { + return explicitTypeArguments ?? const []; + } + + 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: [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: [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, [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: "); diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart index affa5092401..816cc1e2bf0 100644 --- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart +++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart @@ -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 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([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 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([ + 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 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([ + 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([ + 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 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([ + 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([ + left, + node.rhs, + ], types: binaryTarget.inferredExtensionTypeArguments) + ..fileOffset = node.binaryOffset) + ..fileOffset = node.binaryOffset; + } else { + binary = new MethodInvocation( + left, + node.binaryName, + new Arguments([ + 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([ + 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) { diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart index fa7cf7b755c..627fbc50c23 100644 --- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart @@ -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 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 v) { + receiver?.accept(v); + index?.accept(v); + value?.accept(v); + } + + @override + void transformChildren(Transformer v) { + if (receiver != null) { + receiver = receiver.accept(v); + receiver?.parent = this; + } + if (index != null) { + index = index.accept(v); + index?.parent = this; + } + if (value != null) { + value = value.accept(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 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 v) { + receiver?.accept(v); + index?.accept(v); + value?.accept(v); + } + + @override + void transformChildren(Transformer v) { + if (receiver != null) { + receiver = receiver.accept(v); + receiver?.parent = this; + } + if (index != null) { + index = index.accept(v); + index?.parent = this; + } + if (value != null) { + value = value.accept(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 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 v) { + receiver?.accept(v); + index?.accept(v); + rhs?.accept(v); + } + + @override + void transformChildren(Transformer v) { + if (receiver != null) { + receiver = receiver.accept(v); + receiver?.parent = this; + } + if (index != null) { + index = index.accept(v); + index?.parent = this; + } + if (rhs != null) { + rhs = rhs.accept(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 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; diff --git a/pkg/front_end/lib/src/fasta/source/value_kinds.dart b/pkg/front_end/lib/src/fasta/source/value_kinds.dart index 08ecbf07554..2047483b3d1 100644 --- a/pkg/front_end/lib/src/fasta/source/value_kinds.dart +++ b/pkg/front_end/lib/src/fasta/source/value_kinds.dart @@ -47,6 +47,8 @@ abstract class ValueKind { const _SingleValueKind>( NullValue.FormalParameters); static const ValueKind Generator = const _SingleValueKind(); + static const ValueKind Initializer = + const _SingleValueKind(); static const ValueKind MethodBody = const _SingleValueKind(); static const ValueKind Modifiers = const _SingleValueKind>(); diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart index d44038c1569..faf4afd8d17 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart @@ -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 _inferExtensionTypeArguments( - List 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 computeExtensionTypeArgument(Extension extension, + List explicitTypeArguments, DartType receiverType) { + if (explicitTypeArguments != null) { + assert(explicitTypeArguments.length == extension.typeParameters.length); + return explicitTypeArguments; + } else if (extension.typeParameters.isEmpty) { + assert(explicitTypeArguments == null); + return const []; + } else { + return inferExtensionTypeArguments(extension, receiverType); + } + } + + /// Infers the type arguments for an access to an extension instance member + /// on [extension] with the static [receiverType]. + List inferExtensionTypeArguments( + Extension extension, DartType receiverType) { + List typeParameters = extension.typeParameters; + DartType onType = extension.onType; List inferredTypes = new List.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 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 inferredTypeArguments; @@ -746,10 +780,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { } else { List 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 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. diff --git a/pkg/front_end/test/fasta/generator_to_string_test.dart b/pkg/front_end/test/fasta/generator_to_string_test.dart index fe1bbc01f3e..b7aeac8f9d1 100644 --- a/pkg/front_end/test/fasta/generator_to_string_test.dart +++ b/pkg/front_end/test/fasta/generator_to_string_test.dart @@ -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( diff --git a/pkg/front_end/testcases/extensions/extension_setter.dart b/pkg/front_end/testcases/extensions/extension_setter.dart index de0ed9e85b5..79ab6cefb42 100644 --- a/pkg/front_end/testcases/extensions/extension_setter.dart +++ b/pkg/front_end/testcases/extensions/extension_setter.dart @@ -71,6 +71,12 @@ extension Extension on Class { } } +class GenericClass {} + +extension GenericExtension on GenericClass { + set setter(T value) {} +} + main() { var c = new Class(); expect(null, c.field); @@ -191,9 +197,10 @@ main() { new Class().testInternal(); + GenericClass genericClass = new GenericClass(); + expect(1, GenericExtension(genericClass).setter = 1); } - expect(expected, actual) { if (expected != actual) { throw 'Mismatch: expected=$expected, actual=$actual'; diff --git a/pkg/front_end/testcases/extensions/extension_setter.dart.outline.expect b/pkg/front_end/testcases/extensions/extension_setter.dart.outline.expect index 15b272ed6f7..0c3a704445f 100644 --- a/pkg/front_end/testcases/extensions/extension_setter.dart.outline.expect +++ b/pkg/front_end/testcases/extensions/extension_setter.dart.outline.expect @@ -7,6 +7,10 @@ class Class extends core::Object { synthetic constructor •() → self::Class* ; } +class GenericClass extends core::Object { + synthetic constructor •() → self::GenericClass* + ; +} 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 on self::GenericClass* { + 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(final self::GenericClass* #this, self::GenericExtension|set#setter::T* value) → void + ; static method main() → dynamic ; static method expect(dynamic expected, dynamic actual) → dynamic diff --git a/pkg/front_end/testcases/extensions/extension_setter.dart.strong.expect b/pkg/front_end/testcases/extensions/extension_setter.dart.strong.expect index f487199b19d..0eb278333ab 100644 --- a/pkg/front_end/testcases/extensions/extension_setter.dart.strong.expect +++ b/pkg/front_end/testcases/extensions/extension_setter.dart.strong.expect @@ -8,6 +8,11 @@ class Class extends core::Object { : super core::Object::•() ; } +class GenericClass extends core::Object { + synthetic constructor •() → self::GenericClass* + : 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 on self::GenericClass* { + 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(final self::GenericClass* #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* genericClass = new self::GenericClass::•(); + self::expect(1, let final self::GenericClass* #t134 = genericClass in let final core::int* #t135 = 1 in let final void #t136 = self::GenericExtension|set#setter(#t134, #t135) in #t135); } static method expect(dynamic expected, dynamic actual) → dynamic { if(!expected.{core::Object::==}(actual)) { diff --git a/pkg/front_end/testcases/extensions/extension_setter.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/extension_setter.dart.strong.transformed.expect index f487199b19d..0eb278333ab 100644 --- a/pkg/front_end/testcases/extensions/extension_setter.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/extension_setter.dart.strong.transformed.expect @@ -8,6 +8,11 @@ class Class extends core::Object { : super core::Object::•() ; } +class GenericClass extends core::Object { + synthetic constructor •() → self::GenericClass* + : 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 on self::GenericClass* { + 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(final self::GenericClass* #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* genericClass = new self::GenericClass::•(); + self::expect(1, let final self::GenericClass* #t134 = genericClass in let final core::int* #t135 = 1 in let final void #t136 = self::GenericExtension|set#setter(#t134, #t135) in #t135); } static method expect(dynamic expected, dynamic actual) → dynamic { if(!expected.{core::Object::==}(actual)) { diff --git a/pkg/front_end/testcases/extensions/extension_setter_error.dart b/pkg/front_end/testcases/extensions/extension_setter_error.dart new file mode 100644 index 00000000000..db67de0b07e --- /dev/null +++ b/pkg/front_end/testcases/extensions/extension_setter_error.dart @@ -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 {} + +extension GenericExtension on GenericClass { + set setter(T value) {} +} + +error() { + GenericClass genericClass = new GenericClass(); + expect(null, GenericExtension(genericClass).setter = null); +} + + +expect(expected, actual) { + if (expected != actual) { + throw 'Mismatch: expected=$expected, actual=$actual'; + } +} \ No newline at end of file diff --git a/pkg/front_end/testcases/extensions/extension_setter_error.dart.outline.expect b/pkg/front_end/testcases/extensions/extension_setter_error.dart.outline.expect new file mode 100644 index 00000000000..bdf566f00c2 --- /dev/null +++ b/pkg/front_end/testcases/extensions/extension_setter_error.dart.outline.expect @@ -0,0 +1,17 @@ +library; +import self as self; +import "dart:core" as core; + +class GenericClass extends core::Object { + synthetic constructor •() → self::GenericClass* + ; +} +extension GenericExtension on self::GenericClass* { + set setter = self::GenericExtension|set#setter; +} +static method GenericExtension|set#setter(final self::GenericClass* #this, self::GenericExtension|set#setter::T* value) → void + ; +static method error() → dynamic + ; +static method expect(dynamic expected, dynamic actual) → dynamic + ; diff --git a/pkg/front_end/testcases/extensions/extension_setter_error.dart.strong.expect b/pkg/front_end/testcases/extensions/extension_setter_error.dart.strong.expect new file mode 100644 index 00000000000..569b235267f --- /dev/null +++ b/pkg/front_end/testcases/extensions/extension_setter_error.dart.strong.expect @@ -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' can't be assigned to a variable of type 'GenericClass'. +// - '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'. +// expect(null, GenericExtension(genericClass).setter = null); +// ^ +// +import self as self; +import "dart:core" as core; + +class GenericClass extends core::Object { + synthetic constructor •() → self::GenericClass* + : super core::Object::•() + ; +} +extension GenericExtension on self::GenericClass* { + set setter = self::GenericExtension|set#setter; +} +static method GenericExtension|set#setter(final self::GenericClass* #this, self::GenericExtension|set#setter::T* value) → void {} +static method error() → dynamic { + self::GenericClass* genericClass = new self::GenericClass::•(); + self::expect(null, let final self::GenericClass* #t1 = let final #t2 = invalid-expression "pkg/front_end/testcases/extensions/extension_setter_error.dart:13:41: Error: A value of type 'GenericClass' can't be assigned to a variable of type 'GenericClass'. + - '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'. + expect(null, GenericExtension(genericClass).setter = null); + ^" in genericClass as{TypeError} self::GenericClass* in let final core::Null? #t3 = null in let final void #t4 = self::GenericExtension|set#setter(#t1, #t3) in #t3); +} +static method expect(dynamic expected, dynamic actual) → dynamic { + if(!expected.{core::Object::==}(actual)) { + throw "Mismatch: expected=${expected}, actual=${actual}"; + } +} diff --git a/pkg/front_end/testcases/extensions/index.dart b/pkg/front_end/testcases/extensions/index.dart index 582d0675b98..0e1961744a0 100644 --- a/pkg/front_end/testcases/extensions/index.dart +++ b/pkg/front_end/testcases/extensions/index.dart @@ -15,6 +15,12 @@ extension Extension on MapLike { } main() { + implicit(); + explicitWithTypeArguments(); + explicitInferredTypeArguments(); +} + +implicit() { MapLike 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 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 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]); +} + +explicitInferredTypeArguments() { + MapLike 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 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) { diff --git a/pkg/front_end/testcases/extensions/index.dart.outline.expect b/pkg/front_end/testcases/extensions/index.dart.outline.expect index c9b2e9d0bae..f94d2d29c45 100644 --- a/pkg/front_end/testcases/extensions/index.dart.outline.expect +++ b/pkg/front_end/testcases/extensions/index.dart.outline.expect @@ -21,5 +21,11 @@ static method Extension|[]=(final self::MapLike* #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* map1 = new self::MapLike::•(); self::expect(null, self::Extension|[](map1, 0)); map1.{self::MapLike::put}(0, "0"); @@ -47,6 +52,60 @@ static method main() → dynamic { self::expect(5, let final self::MapLike* #t39 = map2 in let final core::int* #t40 = 0 in let final core::int* #t41 = self::Extension|[](#t39, #t40).{core::num::-}(1) in let final void #t42 = self::Extension|[]=(#t39, #t40, #t41) in #t41); self::expect(5, self::Extension|[](map2, 0)); } +static method explicitWithTypeArguments() → dynamic { + self::MapLike* map1 = new self::MapLike::•(); + self::expect(null, self::Extension|[](map1, 0)); + map1.{self::MapLike::put}(0, "0"); + self::expect("0", self::Extension|[](map1, 0)); + self::expect(null, self::Extension|[](map1, 1)); + self::Extension|[]=(map1, 1, "1"); + self::expect("1", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t43 = map1 in let final core::int* #t44 = 1 in let final core::String* #t45 = "2" in let final void #t46 = self::Extension|[]=(#t43, #t44, #t45) in #t45); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t47 = map1 in let final core::int* #t48 = 1 in self::Extension|[](#t47, #t48).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t47, #t48, "3") : null; + self::expect("2", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t49 = map1 in let final core::int* #t50 = 1 in let final core::String* #t51 = self::Extension|[](#t49, #t50) in #t51.{core::String::==}(null) ?{core::String*} let final core::String* #t52 = "4" in let final void #t53 = self::Extension|[]=(#t49, #t50, #t52) in #t52 : #t51); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t54 = map1 in let final core::int* #t55 = 2 in self::Extension|[](#t54, #t55).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t54, #t55, "2") : null; + self::expect("2", self::Extension|[](map1, 2)); + self::expect("3", let final self::MapLike* #t56 = map1 in let final core::int* #t57 = 3 in let final core::String* #t58 = self::Extension|[](#t56, #t57) in #t58.{core::String::==}(null) ?{core::String*} let final core::String* #t59 = "3" in let final void #t60 = self::Extension|[]=(#t56, #t57, #t59) in #t59 : #t58); + self::expect("3", self::Extension|[](map1, 3)); + self::MapLike* map2 = new self::MapLike::•(); + self::expect(1, let final self::MapLike* #t61 = map2 in let final core::int* #t62 = 0 in let final core::int* #t63 = 1 in let final void #t64 = self::Extension|[]=(#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|[](#t65, #t66).{core::num::+}(2) in let final void #t68 = self::Extension|[]=(#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|[](#t69, #t70).{core::num::+}(2) in let final void #t72 = self::Extension|[]=(#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|[](#t73, #t74) in let final void #t76 = self::Extension|[]=(#t73, #t74, #t75.{core::num::+}(1)) in #t75); + self::expect(6, self::Extension|[](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|[](#t77, #t78).{core::num::-}(1) in let final void #t80 = self::Extension|[]=(#t77, #t78, #t79) in #t79); + self::expect(5, self::Extension|[](map2, 0)); +} +static method explicitInferredTypeArguments() → dynamic { + self::MapLike* map1 = new self::MapLike::•(); + self::expect(null, self::Extension|[](map1, 0)); + map1.{self::MapLike::put}(0, "0"); + self::expect("0", self::Extension|[](map1, 0)); + self::expect(null, self::Extension|[](map1, 1)); + self::Extension|[]=(map1, 1, "1"); + self::expect("1", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t81 = map1 in let final core::int* #t82 = 1 in let final core::String* #t83 = "2" in let final void #t84 = self::Extension|[]=(#t81, #t82, #t83) in #t83); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t85 = map1 in let final core::int* #t86 = 1 in self::Extension|[](#t85, #t86).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t85, #t86, "3") : null; + self::expect("2", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t87 = map1 in let final core::int* #t88 = 1 in let final core::String* #t89 = self::Extension|[](#t87, #t88) in #t89.{core::String::==}(null) ?{core::String*} let final core::String* #t90 = "4" in let final void #t91 = self::Extension|[]=(#t87, #t88, #t90) in #t90 : #t89); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t92 = map1 in let final core::int* #t93 = 2 in self::Extension|[](#t92, #t93).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t92, #t93, "2") : null; + self::expect("2", self::Extension|[](map1, 2)); + self::expect("3", let final self::MapLike* #t94 = map1 in let final core::int* #t95 = 3 in let final core::String* #t96 = self::Extension|[](#t94, #t95) in #t96.{core::String::==}(null) ?{core::String*} let final core::String* #t97 = "3" in let final void #t98 = self::Extension|[]=(#t94, #t95, #t97) in #t97 : #t96); + self::expect("3", self::Extension|[](map1, 3)); + self::MapLike* map2 = new self::MapLike::•(); + self::expect(1, let final self::MapLike* #t99 = map2 in let final core::int* #t100 = 0 in let final core::int* #t101 = 1 in let final void #t102 = self::Extension|[]=(#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|[](#t103, #t104).{core::num::+}(2) in let final void #t106 = self::Extension|[]=(#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|[](#t107, #t108).{core::num::+}(2) in let final void #t110 = self::Extension|[]=(#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|[](#t111, #t112) in let final void #t114 = self::Extension|[]=(#t111, #t112, #t113.{core::num::+}(1)) in #t113); + self::expect(6, self::Extension|[](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|[](#t115, #t116).{core::num::-}(1) in let final void #t118 = self::Extension|[]=(#t115, #t116, #t117) in #t117); + self::expect(5, self::Extension|[](map2, 0)); +} static method expect(dynamic expected, dynamic actual) → dynamic { if(!expected.{core::Object::==}(actual)) { throw "Mismatch: expected=${expected}, actual=${actual}"; diff --git a/pkg/front_end/testcases/extensions/index.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/index.dart.strong.transformed.expect index 4f14d3dc3e6..cc8481e516e 100644 --- a/pkg/front_end/testcases/extensions/index.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/index.dart.strong.transformed.expect @@ -21,6 +21,11 @@ static method Extension|[](final self::MapLike* #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* map1 = new self::MapLike::•(); self::expect(null, self::Extension|[](map1, 0)); map1.{self::MapLike::put}(0, "0"); @@ -47,6 +52,60 @@ static method main() → dynamic { self::expect(5, let final self::MapLike* #t39 = map2 in let final core::int* #t40 = 0 in let final core::int* #t41 = self::Extension|[](#t39, #t40).{core::num::-}(1) in let final void #t42 = self::Extension|[]=(#t39, #t40, #t41) in #t41); self::expect(5, self::Extension|[](map2, 0)); } +static method explicitWithTypeArguments() → dynamic { + self::MapLike* map1 = new self::MapLike::•(); + self::expect(null, self::Extension|[](map1, 0)); + map1.{self::MapLike::put}(0, "0"); + self::expect("0", self::Extension|[](map1, 0)); + self::expect(null, self::Extension|[](map1, 1)); + self::Extension|[]=(map1, 1, "1"); + self::expect("1", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t43 = map1 in let final core::int* #t44 = 1 in let final core::String* #t45 = "2" in let final void #t46 = self::Extension|[]=(#t43, #t44, #t45) in #t45); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t47 = map1 in let final core::int* #t48 = 1 in self::Extension|[](#t47, #t48).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t47, #t48, "3") : null; + self::expect("2", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t49 = map1 in let final core::int* #t50 = 1 in let final core::String* #t51 = self::Extension|[](#t49, #t50) in #t51.{core::String::==}(null) ?{core::String*} let final core::String* #t52 = "4" in let final void #t53 = self::Extension|[]=(#t49, #t50, #t52) in #t52 : #t51); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t54 = map1 in let final core::int* #t55 = 2 in self::Extension|[](#t54, #t55).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t54, #t55, "2") : null; + self::expect("2", self::Extension|[](map1, 2)); + self::expect("3", let final self::MapLike* #t56 = map1 in let final core::int* #t57 = 3 in let final core::String* #t58 = self::Extension|[](#t56, #t57) in #t58.{core::String::==}(null) ?{core::String*} let final core::String* #t59 = "3" in let final void #t60 = self::Extension|[]=(#t56, #t57, #t59) in #t59 : #t58); + self::expect("3", self::Extension|[](map1, 3)); + self::MapLike* map2 = new self::MapLike::•(); + self::expect(1, let final self::MapLike* #t61 = map2 in let final core::int* #t62 = 0 in let final core::int* #t63 = 1 in let final void #t64 = self::Extension|[]=(#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|[](#t65, #t66).{core::num::+}(2) in let final void #t68 = self::Extension|[]=(#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|[](#t69, #t70).{core::num::+}(2) in let final void #t72 = self::Extension|[]=(#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|[](#t73, #t74) in let final void #t76 = self::Extension|[]=(#t73, #t74, #t75.{core::num::+}(1)) in #t75); + self::expect(6, self::Extension|[](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|[](#t77, #t78).{core::num::-}(1) in let final void #t80 = self::Extension|[]=(#t77, #t78, #t79) in #t79); + self::expect(5, self::Extension|[](map2, 0)); +} +static method explicitInferredTypeArguments() → dynamic { + self::MapLike* map1 = new self::MapLike::•(); + self::expect(null, self::Extension|[](map1, 0)); + map1.{self::MapLike::put}(0, "0"); + self::expect("0", self::Extension|[](map1, 0)); + self::expect(null, self::Extension|[](map1, 1)); + self::Extension|[]=(map1, 1, "1"); + self::expect("1", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t81 = map1 in let final core::int* #t82 = 1 in let final core::String* #t83 = "2" in let final void #t84 = self::Extension|[]=(#t81, #t82, #t83) in #t83); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t85 = map1 in let final core::int* #t86 = 1 in self::Extension|[](#t85, #t86).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t85, #t86, "3") : null; + self::expect("2", self::Extension|[](map1, 1)); + self::expect("2", let final self::MapLike* #t87 = map1 in let final core::int* #t88 = 1 in let final core::String* #t89 = self::Extension|[](#t87, #t88) in #t89.{core::String::==}(null) ?{core::String*} let final core::String* #t90 = "4" in let final void #t91 = self::Extension|[]=(#t87, #t88, #t90) in #t90 : #t89); + self::expect("2", self::Extension|[](map1, 1)); + let final self::MapLike* #t92 = map1 in let final core::int* #t93 = 2 in self::Extension|[](#t92, #t93).{core::String::==}(null) ?{core::String*} self::Extension|[]=(#t92, #t93, "2") : null; + self::expect("2", self::Extension|[](map1, 2)); + self::expect("3", let final self::MapLike* #t94 = map1 in let final core::int* #t95 = 3 in let final core::String* #t96 = self::Extension|[](#t94, #t95) in #t96.{core::String::==}(null) ?{core::String*} let final core::String* #t97 = "3" in let final void #t98 = self::Extension|[]=(#t94, #t95, #t97) in #t97 : #t96); + self::expect("3", self::Extension|[](map1, 3)); + self::MapLike* map2 = new self::MapLike::•(); + self::expect(1, let final self::MapLike* #t99 = map2 in let final core::int* #t100 = 0 in let final core::int* #t101 = 1 in let final void #t102 = self::Extension|[]=(#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|[](#t103, #t104).{core::num::+}(2) in let final void #t106 = self::Extension|[]=(#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|[](#t107, #t108).{core::num::+}(2) in let final void #t110 = self::Extension|[]=(#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|[](#t111, #t112) in let final void #t114 = self::Extension|[]=(#t111, #t112, #t113.{core::num::+}(1)) in #t113); + self::expect(6, self::Extension|[](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|[](#t115, #t116).{core::num::-}(1) in let final void #t118 = self::Extension|[]=(#t115, #t116, #t117) in #t117); + self::expect(5, self::Extension|[](map2, 0)); +} static method expect(dynamic expected, dynamic actual) → dynamic { if(!expected.{core::Object::==}(actual)) { throw "Mismatch: expected=${expected}, actual=${actual}"; diff --git a/pkg/front_end/testcases/inference/unresolved_super.dart.strong.expect b/pkg/front_end/testcases/inference/unresolved_super.dart.strong.expect index d7d9a115a45..1f4d51bed88 100644 --- a/pkg/front_end/testcases/inference/unresolved_super.dart.strong.expect +++ b/pkg/front_end/testcases/inference/unresolved_super.dart.strong.expect @@ -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() 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() → self::f::T* diff --git a/pkg/front_end/testcases/inference/unresolved_super.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/unresolved_super.dart.strong.transformed.expect index d7d9a115a45..1f4d51bed88 100644 --- a/pkg/front_end/testcases/inference/unresolved_super.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/inference/unresolved_super.dart.strong.transformed.expect @@ -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() 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() → self::f::T* diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status index 91148bebcb3..e02ba42f83e 100644 --- a/pkg/front_end/testcases/strong.status +++ b/pkg/front_end/testcases/strong.status @@ -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 diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status index f72565ad1b7..db5b3680f4c 100644 --- a/pkg/front_end/testcases/text_serialization.status +++ b/pkg/front_end/testcases/text_serialization.status @@ -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 diff --git a/tests/language_2/extension_methods/static_extension_getter_setter_conflicts_test.dart b/tests/language_2/extension_methods/static_extension_getter_setter_conflicts_test.dart index 664916f9c87..ad98f03d1af 100644 --- a/tests/language_2/extension_methods/static_extension_getter_setter_conflicts_test.dart +++ b/tests/language_2/extension_methods/static_extension_getter_setter_conflicts_test.dart @@ -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 }