diff --git a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart index 9a2ae384558..207b69e208f 100644 --- a/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/procedure_builder.dart @@ -4,12 +4,6 @@ library fasta.procedure_builder; -// Note: we're deliberately using AsyncMarker and ProcedureKind from kernel -// outside the kernel-specific builders. This is simpler than creating -// additional enums. -import 'package:kernel/ast.dart' - show AsyncMarker, ProcedureKind, VariableDeclaration; - import 'package:kernel/type_algebra.dart' show containsTypeVariable, substitute; import 'package:kernel/type_algebra.dart'; @@ -30,6 +24,7 @@ import 'package:kernel/ast.dart' show Arguments, AsyncMarker, + Block, Class, Constructor, ConstructorInvocation, @@ -45,6 +40,7 @@ import 'package:kernel/ast.dart' Procedure, ProcedureKind, RedirectingInitializer, + ReturnStatement, Statement, StaticInvocation, StringLiteral, @@ -53,6 +49,7 @@ import 'package:kernel/ast.dart' TypeParameter, TypeParameterType, VariableDeclaration, + VariableGet, setParents; import '../../scanner/token.dart' show Token; @@ -235,7 +232,7 @@ abstract class FunctionBuilder extends MemberBuilder { FunctionNode function; - Statement actualBody; + Statement _body; FunctionBuilder get actualOrigin; @@ -247,7 +244,7 @@ abstract class FunctionBuilder extends MemberBuilder { // newBody.fileOffset, fileUri); // } // } - actualBody = newBody; + _body = newBody; if (function != null) { // A forwarding semi-stub is a method that is abstract in the source code, // but which needs to have a forwarding stub body in order to ensure that @@ -264,18 +261,18 @@ abstract class FunctionBuilder extends MemberBuilder { } void setRedirectingFactoryBody(Member target, List typeArguments) { - if (actualBody != null) { - unexpected("null", "${actualBody.runtimeType}", charOffset, fileUri); + if (_body != null) { + unexpected("null", "${_body.runtimeType}", charOffset, fileUri); } - actualBody = new RedirectingFactoryBody(target, typeArguments); - function.body = actualBody; - actualBody?.parent = function; + _body = new RedirectingFactoryBody(target, typeArguments); + function.body = _body; + _body?.parent = function; if (isPatch) { actualOrigin.setRedirectingFactoryBody(target, typeArguments); } } - Statement get body => actualBody ??= new EmptyStatement(); + Statement get body => _body ??= new EmptyStatement(); bool get isNative => nativeMethodName != null; @@ -506,10 +503,63 @@ class ProcedureBuilder extends FunctionBuilder { AsyncMarker get asyncModifier => actualAsyncModifier; Statement get body { - if (actualBody == null && !isAbstract && !isExternal) { - actualBody = new EmptyStatement(); + if (_body == null && !isAbstract && !isExternal) { + _body = new EmptyStatement(); } - return actualBody; + return _body; + } + + /// If this is an extension instance setter, wrap the setter body to return + /// the rhs value from the method. + /// + /// That is, this setter + /// + /// extension E on A { + /// void set property(B value) { + /// value++; + /// } + /// } + /// + /// is converted into this top level method + /// + /// B E|property(A #this, B value) { + /// final #t1 = value; + /// value++; + /// return #t1; + /// } + /// + void _updateExtensionSetterBody() { + if (isExtensionInstanceMember && isSetter) { + // TODO(johnniwinther): Avoid the synthetic variable if the parameter is + // never modified. + VariableDeclaration value = procedure.function.positionalParameters[1]; + procedure.function.returnType = value.type; + Statement body = procedure.function.body; + List statements = []; + Block block = new Block(statements); + VariableDeclaration variableDeclaration = + new VariableDeclarationJudgment.forValue( + new VariableGet(value)..fileOffset = procedure.fileOffset) + ..type = value.type; + statements.add(variableDeclaration); + if (body is Block) { + statements.addAll(body.statements); + } else { + statements.add(body); + } + ReturnStatement returnStatement = new ReturnStatement( + new VariableGet(variableDeclaration) + ..fileOffset = procedure.fileEndOffset); + statements.add(returnStatement); + setParents(block.statements, block); + procedure.function.body = block; + block.parent = procedure.function; + } + } + + void set body(Statement newBody) { + super.body = newBody; + _updateExtensionSetterBody(); } void set asyncModifier(AsyncMarker newModifier) { @@ -896,12 +946,12 @@ class RedirectingFactoryBuilder extends ProcedureBuilder { nativeMethodName); @override - Statement get body => actualBody; + Statement get body => _body; @override void setRedirectingFactoryBody(Member target, List typeArguments) { - if (actualBody != null) { - unexpected("null", "${actualBody.runtimeType}", charOffset, fileUri); + if (_body != null) { + unexpected("null", "${_body.runtimeType}", charOffset, fileUri); } // Ensure that constant factories only have constant targets/bodies. @@ -910,9 +960,9 @@ class RedirectingFactoryBuilder extends ProcedureBuilder { noLength, fileUri); } - actualBody = new RedirectingFactoryBody(target, typeArguments); - function.body = actualBody; - actualBody?.parent = function; + _body = new RedirectingFactoryBody(target, typeArguments); + function.body = _body; + _body?.parent = function; if (isPatch) { if (function.typeParameters != null) { Map substitution = {}; 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 059726f851a..a9f23b580e7 100644 --- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart +++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart @@ -577,8 +577,8 @@ class InferenceVisitor // To replicate analyzer behavior, we base type inference on the write // member. TODO(paulberry): would it be better to use the read member // when doing compound assignment? - FunctionType calleeType = inferrer.getCalleeFunctionType( - inferrer.getCalleeType(writeTarget, receiverType), false); + FunctionType calleeType = + inferrer.getFunctionType(writeTarget, receiverType, false); DartType expectedIndexTypeForWrite; DartType indexContext = const UnknownType(); DartType writeContext = const UnknownType(); @@ -604,8 +604,8 @@ class InferenceVisitor if (read != null) { ObjectAccessTarget readMember = inferrer .findMethodInvocationMember(receiverType, read, instrumented: false); - FunctionType calleeFunctionType = inferrer.getCalleeFunctionType( - inferrer.getCalleeType(readMember, receiverType), false); + FunctionType calleeFunctionType = + inferrer.getFunctionType(readMember, receiverType, false); inferrer.ensureAssignable( getPositionalParameterType(calleeFunctionType, 0), indexType, @@ -1681,7 +1681,7 @@ class InferenceVisitor if (node.read != null) { ObjectAccessTarget readTarget = inferrer .findPropertyGetMember(receiverType, node.read, instrumented: false); - readType = inferrer.getCalleeType(readTarget, receiverType); + readType = inferrer.getGetterType(readTarget, receiverType); inferrer.handlePropertyGetContravariance( node.receiver, readTarget, @@ -1702,8 +1702,21 @@ class InferenceVisitor DartType inferredType = node._inferRhs(inferrer, readType, writeContext).inferredType; node.nullAwareGuard?.staticType = node.inferredType; - node._replaceWithDesugared(); - return new ExpressionInferenceResult(inferredType); + Expression replacement; + if (writeTarget.isExtensionMember) { + node.parent.replaceChild( + node, + replacement = inferrer.helper.forest.createStaticInvocation( + node.fileOffset, + writeTarget.member, + inferrer.helper.forest.createArguments( + node.fileOffset, [node.receiver, node.rhs]))); + inferrer.storeInferredType(replacement, inferredType); + } else { + node._replaceWithDesugared(); + } + + return new ExpressionInferenceResult(inferredType, replacement); } @override @@ -1711,7 +1724,7 @@ class InferenceVisitor PropertyGet node, DartType typeContext) { return inferrer.inferPropertyGet( node, node.receiver, node.fileOffset, typeContext, - desugaredGet: node); + desugaredGet: node, allowExtensionMethods: true); } void visitRedirectingInitializerJudgment( 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 7f71186fb83..0b45db82b6d 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 @@ -293,7 +293,7 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment { Expression read; /// The expression appearing on the RHS of the assignment. - final Expression rhs; + Expression rhs; /// The expression that performs the write (e.g. `a.[]=(b, a.[](b) + 1)` in /// `++a[b]`). @@ -367,8 +367,8 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment { combinerTarget.member, readType); } DartType rhsType; - FunctionType combinerType = inferrer.getCalleeFunctionType( - inferrer.getCalleeType(combinerTarget, readType), false); + FunctionType combinerType = + inferrer.getFunctionType(combinerTarget, readType, false); if (isPreIncDec || isPostIncDec) { rhsType = inferrer.coreTypes.intClass.rawType; } else { @@ -378,8 +378,12 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment { assert(identical(combiner.arguments.positional.first, rhs)); // Analyzer uses a null context for the RHS here. // TODO(paulberry): improve on this. - inferrer.inferExpression(rhs, const UnknownType(), true); - rhsType = getInferredType(rhs, inferrer); + ExpressionInferenceResult rhsResult = + inferrer.inferExpression(rhs, const UnknownType(), true); + if (rhsResult.replacement != null) { + rhs = rhsResult.replacement; + } + rhsType = rhsResult.inferredType; // Do not use rhs after this point because it may be a Shadow node // that has been replaced in the tree with its desugaring. DartType expectedType = getPositionalParameterType(combinerType, 0); @@ -413,6 +417,9 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment { ExpressionInferenceResult rhsResult = inferrer.inferExpression( rhs, writeContext ?? const UnknownType(), true, isVoidAllowed: true); + if (rhsResult.replacement != null) { + rhs = rhsResult.replacement; + } DartType rhsType = rhsResult.inferredType; Expression replacedRhs = inferrer.ensureAssignable( writeContext, rhsType, rhs, writeOffset, diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart index c6901035117..0e0668c46cc 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/inference_helper.dart @@ -8,11 +8,15 @@ import 'package:kernel/core_types.dart' show CoreTypes; import '../fasta_codes.dart' show LocatedMessage, Message; +import '../kernel/forest.dart'; + abstract class InferenceHelper { CoreTypes get coreTypes; Uri get uri; + Forest get forest; + set transformSetLiterals(bool value); Expression buildProblem(Message message, int charOffset, int length, 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 596d7ff19ed..48aaa50c05b 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 @@ -67,6 +67,10 @@ import '../../base/instrumentation.dart' InstrumentationValueForType, InstrumentationValueForTypeArgs; +import '../builder/extension_builder.dart'; +import '../builder/member_builder.dart'; +import '../builder/procedure_builder.dart'; + import '../fasta_codes.dart' show LocatedMessage, @@ -649,7 +653,7 @@ abstract class TypeInferrerImpl extends TypeInferrer { PropertyGet tearOff = new PropertyGet(new VariableGet(t), callName, callMember) ..fileOffset = fileOffset; - actualType = getCalleeTypeForMemberTarget(callMember, actualType); + actualType = getGetterTypeForMemberTarget(callMember, actualType); ConditionalExpression conditional = new ConditionalExpression( nullCheck, new NullLiteral()..fileOffset = fileOffset, @@ -743,7 +747,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { Expression expression, Expression receiver, bool setter: false, - bool instrumented: true}) { + bool instrumented: true, + bool includeExtensionMethods: false}) { assert(receiverType != null && isKnown(receiverType)); receiverType = resolveTypeParameter(receiverType); @@ -757,15 +762,39 @@ abstract class TypeInferrerImpl extends TypeInferrer { : coreTypes.objectClass; Member interfaceMember = _getInterfaceMember(classNode, name, setter, fileOffset); + ObjectAccessTarget target = interfaceMember != null + ? new ObjectAccessTarget.interfaceMember(interfaceMember) + : const ObjectAccessTarget.unresolved(); if (instrumented && receiverType != const DynamicType() && - interfaceMember != null) { + target.isInstanceMember) { instrumentation?.record(uri, fileOffset, 'target', - new InstrumentationValueForMember(interfaceMember)); + new InstrumentationValueForMember(target.member)); + } + + if (target.isUnresolved && includeExtensionMethods) { + // TODO(johnniwinther): Find the most specific method. + library.scope.forEachExtension((ExtensionBuilder extensionBuilder) { + MemberBuilder memberBuilder = + extensionBuilder.lookupLocalMember(name.name, setter: setter); + if (memberBuilder != null && !memberBuilder.isStatic) { + ProcedureBuilder procedureBuilder = memberBuilder; + if (procedureBuilder != null) { + DartType onType = extensionBuilder.extension.onType; + // TODO(johnniwinther): Handle generic extensions. + if (typeSchemaEnvironment.isSubtypeOf(receiverType, onType)) { + target = new ObjectAccessTarget.extensionMember( + procedureBuilder.target, + procedureBuilder.kind, + extensionBuilder.typeParameters?.length ?? 0); + } + } + } + }); } if (!isTopLevel && - interfaceMember == null && + target.isUnresolved && receiverType is! DynamicType && receiverType is! InvalidType && !(receiverType == coreTypes.functionClass.rawType && @@ -783,9 +812,7 @@ abstract class TypeInferrerImpl extends TypeInferrer { fileOffset, length))); } - return interfaceMember != null - ? new ObjectAccessTarget.interfaceMember(interfaceMember) - : const ObjectAccessTarget.unresolved(); + return target; } /// Finds a member of [receiverType] called [name] and records it in @@ -801,7 +828,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { errorTemplate: templateUndefinedMethod, expression: methodInvocation, receiver: methodInvocation.receiver, - instrumented: instrumented); + instrumented: instrumented, + includeExtensionMethods: true); if (interfaceTarget.isInstanceMember) { Member interfaceMember = interfaceTarget.member; if (receiverType == const DynamicType() && @@ -893,7 +921,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { expression: propertySet, receiver: propertySet.receiver, setter: true, - instrumented: instrumented); + instrumented: instrumented, + includeExtensionMethods: true); if (writeTarget.isInstanceMember) { if (instrumented && instrumentation != null && @@ -919,53 +948,72 @@ abstract class TypeInferrerImpl extends TypeInferrer { } } - FunctionType getCalleeFunctionType(DartType calleeType, bool followCall) { - if (calleeType is FunctionType) { - return calleeType; - } else if (followCall && calleeType is InterfaceType) { - Member member = - _getInterfaceMember(calleeType.classNode, callName, false, -1); - DartType callType = getCalleeTypeForMemberTarget(member, calleeType); - if (callType is FunctionType) { - return callType; - } - } - return unknownFunction; - } - - DartType getCalleeType(ObjectAccessTarget target, DartType receiverType) { + /// Returns the type of [target] when accessed as a getter on [receiverType]. + /// + /// For instance + /// + /// class Class { + /// T method() {} + /// T getter => null; + /// } + /// + /// Class c = ... + /// c.method; // The getter type is `int Function()`. + /// c.getter; // The getter type is `int`. + /// + DartType getGetterType(ObjectAccessTarget target, DartType receiverType) { switch (target.kind) { case ObjectAccessTargetKind.callFunction: return receiverType; case ObjectAccessTargetKind.unresolved: return const DynamicType(); case ObjectAccessTargetKind.instanceMember: - return getCalleeTypeForMemberTarget(target.member, receiverType); - default: - throw unhandled( - target.runtimeType.toString(), 'getCalleeType', null, null); + return getGetterTypeForMemberTarget(target.member, receiverType); + case ObjectAccessTargetKind.extensionMember: + switch (target.extensionMethodKind) { + case ProcedureKind.Method: + FunctionType functionType = target.member.function.functionType; + // TODO(johnniwinther): Handle tear off of generic extensions. + return new FunctionType( + functionType.positionalParameters.skip(1).toList(), + functionType.returnType, + namedParameters: functionType.namedParameters, + typeParameters: functionType.typeParameters + .skip(target.extensionTypeParameterCount) + .toList(), + requiredParameterCount: + functionType.requiredParameterCount - 1); + case ProcedureKind.Getter: + return target.member.function.returnType; + case ProcedureKind.Setter: + case ProcedureKind.Factory: + case ProcedureKind.Operator: + break; + } } + throw unhandled('$target', 'getGetterType', null, null); } - DartType getCalleeTypeForMemberTarget( + /// Returns the getter type of [member] on a receiver of type [receiverType]. + /// + /// For instance + /// + /// class Class { + /// T method() {} + /// T getter => null; + /// } + /// + /// Class c = ... + /// c.method; // The getter type is `int Function()`. + /// c.getter; // The getter type is `int`. + /// + DartType getGetterTypeForMemberTarget( Member interfaceMember, DartType receiverType) { - if (interfaceMember == null) { - return const DynamicType(); - } Class memberClass = interfaceMember.enclosingClass; - DartType calleeType; - if (interfaceMember is Procedure) { - if (interfaceMember.kind == ProcedureKind.Getter) { - calleeType = interfaceMember.function.returnType; - } else { - calleeType = interfaceMember.function.functionType; - } - } else if (interfaceMember is Field) { - calleeType = interfaceMember.type; - } else { - throw unhandled( - interfaceMember.runtimeType.toString(), 'getCalleeType', null, null); - } + assert( + interfaceMember is kernel.Field || interfaceMember is kernel.Procedure, + "Unexpected interface member $interfaceMember."); + DartType calleeType = interfaceMember.getterType; if (memberClass.typeParameters.isNotEmpty) { receiverType = resolveTypeParameter(receiverType); if (receiverType is InterfaceType) { @@ -978,6 +1026,67 @@ abstract class TypeInferrerImpl extends TypeInferrer { return calleeType; } + /// Returns the type of [target] when accessed as an invocation on + /// [receiverType]. + /// + /// If the target is known not to be invokable [unknownFunction] is returned. + /// + /// For instance + /// + /// class Class { + /// T method() {} + /// T Function() getter1 => null; + /// T getter2 => null; + /// } + /// + /// Class c = ... + /// c.method; // The getter type is `int Function()`. + /// c.getter1; // The getter type is `int Function()`. + /// c.getter2; // The getter type is [unknownFunction]. + /// + FunctionType getFunctionType( + ObjectAccessTarget target, DartType receiverType, bool followCall) { + switch (target.kind) { + case ObjectAccessTargetKind.callFunction: + return _getFunctionType(receiverType, followCall); + case ObjectAccessTargetKind.unresolved: + return unknownFunction; + case ObjectAccessTargetKind.instanceMember: + return _getFunctionType( + getGetterTypeForMemberTarget(target.member, receiverType), + followCall); + case ObjectAccessTargetKind.extensionMember: + switch (target.extensionMethodKind) { + case ProcedureKind.Method: + return target.member.function.functionType; + case ProcedureKind.Getter: + // TODO(johnniwinther): Handle implicit .call on extension getter. + return _getFunctionType(target.member.function.returnType, false); + case ProcedureKind.Setter: + case ProcedureKind.Factory: + case ProcedureKind.Operator: + break; + } + } + throw unhandled('$target', 'getFunctionType', null, null); + } + + FunctionType _getFunctionType(DartType calleeType, bool followCall) { + if (calleeType is FunctionType) { + return calleeType; + } else if (followCall && calleeType is InterfaceType) { + Member member = + _getInterfaceMember(calleeType.classNode, callName, false, -1); + if (member != null) { + DartType callType = getGetterTypeForMemberTarget(member, calleeType); + if (callType is FunctionType) { + return callType; + } + } + } + return unknownFunction; + } + DartType getDerivedTypeArgumentOf(DartType type, Class class_) { if (type is InterfaceType) { InterfaceType typeAsInstanceOfClass = @@ -1033,11 +1142,22 @@ abstract class TypeInferrerImpl extends TypeInferrer { } } return setterType; + case ObjectAccessTargetKind.extensionMember: + switch (target.extensionMethodKind) { + case ProcedureKind.Setter: + // TODO(johnniwinther): Is this valid if the method is generic? + return target.member.function.positionalParameters[1].type; + case ProcedureKind.Method: + case ProcedureKind.Getter: + case ProcedureKind.Factory: + case ProcedureKind.Operator: + break; + } + // TODO(johnniwinther): Compute the right setter type. + return const DynamicType(); case ObjectAccessTargetKind.callFunction: - default: - throw unhandled( - target.runtimeType.toString(), 'getSetterType', null, null); } + throw unhandled(target.runtimeType.toString(), 'getSetterType', null, null); } DartType getTypeArgumentOf(DartType type, Class class_) { @@ -1580,9 +1700,9 @@ abstract class TypeInferrerImpl extends TypeInferrer { target.member is Procedure && typeSchemaEnvironment.isOverloadedArithmeticOperatorAndType( target.member, receiverType); - DartType calleeType = getCalleeType(target, receiverType); + DartType calleeType = getGetterType(target, receiverType); FunctionType functionType = - getCalleeFunctionType(calleeType, !isImplicitCall); + getFunctionType(target, receiverType, !isImplicitCall); if (!target.isUnresolved && calleeType is! DynamicType && @@ -1601,6 +1721,18 @@ abstract class TypeInferrerImpl extends TypeInferrer { desugaredInvocation, arguments, expression); + Expression replacement; + if (target.isExtensionMember) { + expression.parent.replaceChild( + expression, + replacement = helper.forest.createStaticInvocation( + expression.fileOffset, + target.member, + arguments = helper.forest.createArguments( + arguments.fileOffset, [receiver, ...arguments.positional], + named: arguments.named, + types: getExplicitTypeArguments(arguments)))); + } DartType inferredType = inferInvocation(typeContext, fileOffset, functionType, functionType.returnType, arguments, isOverloadedArithmeticOperator: isOverloadedArithmeticOperator, @@ -1659,7 +1791,7 @@ abstract class TypeInferrerImpl extends TypeInferrer { inferred: getExplicitTypeArguments(arguments) == null); } - return new ExpressionInferenceResult(inferredType); + return new ExpressionInferenceResult(inferredType, replacement); } @override @@ -1682,7 +1814,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { {VariableDeclaration receiverVariable, PropertyGet desugaredGet, ObjectAccessTarget readTarget, - Name propertyName}) { + Name propertyName, + bool allowExtensionMethods: false}) { // First infer the receiver so we can look up the getter that was invoked. DartType receiverType; if (receiver == null) { @@ -1698,7 +1831,8 @@ abstract class TypeInferrerImpl extends TypeInferrer { readTarget = findInterfaceMember(receiverType, propertyName, fileOffset, errorTemplate: templateUndefinedGetter, expression: expression, - receiver: receiver); + receiver: receiver, + includeExtensionMethods: allowExtensionMethods); if (readTarget.isInstanceMember) { if (instrumentation != null && receiverType == const DynamicType()) { instrumentation.record(uri, desugaredGet.fileOffset, 'target', @@ -1707,18 +1841,38 @@ abstract class TypeInferrerImpl extends TypeInferrer { desugaredGet.interfaceTarget = readTarget.member; } } - DartType inferredType = getCalleeType(readTarget, receiverType); + DartType inferredType = getGetterType(readTarget, receiverType); Expression replacedExpression = handlePropertyGetContravariance(receiver, readTarget, desugaredGet, expression, inferredType, fileOffset); + Expression replacement; if (readTarget.isInstanceMember) { Member member = readTarget.member; if (member is Procedure && member.kind == ProcedureKind.Method) { inferredType = instantiateTearOff(inferredType, typeContext, replacedExpression); } + } else if (readTarget.isExtensionMember) { + switch (readTarget.extensionMethodKind) { + case kernel.ProcedureKind.Getter: + expression.parent.replaceChild( + expression, + replacement = expression = helper.forest.createStaticInvocation( + expression.fileOffset, + readTarget.member, + helper.forest + .createArguments(expression.fileOffset, [receiver]))); + break; + case kernel.ProcedureKind.Method: + // TODO(johnniwinther): Handle extension method tearoff. + case kernel.ProcedureKind.Setter: + case kernel.ProcedureKind.Factory: + case kernel.ProcedureKind.Operator: + unhandled('$readTarget', "inferPropertyGet", fileOffset, uri); + break; + } } storeInferredType(expression, inferredType); - return new ExpressionInferenceResult(inferredType); + return new ExpressionInferenceResult(inferredType, replacement); } /// Modifies a type as appropriate when inferring a closure return type. @@ -1800,7 +1954,7 @@ abstract class TypeInferrerImpl extends TypeInferrer { if (interfaceMember is Field || interfaceMember is Procedure && interfaceMember.kind == ProcedureKind.Getter) { - DartType getType = getCalleeType(target, receiverType); + DartType getType = getGetterType(target, receiverType); if (getType is DynamicType) { return MethodContravarianceCheckKind.none; } @@ -2121,6 +2275,7 @@ enum ObjectAccessTargetKind { instanceMember, callFunction, unresolved, + extensionMember, } /// Result for performing an access on an object, like `o.foo`, `o.foo()` and @@ -2138,6 +2293,11 @@ class ObjectAccessTarget { ObjectAccessTargetKind.instanceMember, member); } + /// Creates an access to the extension [member]. + factory ObjectAccessTarget.extensionMember( + Member member, ProcedureKind kind, int typeParameters) = + ExtensionAccessTarget; + /// Creates an access to a 'call' method on a function, i.e. a function /// invocation. const ObjectAccessTarget.callFunction() @@ -2153,11 +2313,42 @@ class ObjectAccessTarget { /// Returns `true` if this is an access to an instance member. bool get isInstanceMember => kind == ObjectAccessTargetKind.instanceMember; + /// Returns `true` if this is an access to an extension member. + bool get isExtensionMember => kind == ObjectAccessTargetKind.extensionMember; + /// Returns `true` if this is an access to the 'call' method on a function. bool get isCallFunction => kind == ObjectAccessTargetKind.callFunction; /// Returns `true` if this is an access without a known target. bool get isUnresolved => kind == ObjectAccessTargetKind.unresolved; + /// Returns the original procedure kind, if this is an extension method + /// target. + /// + /// This is need because getters, setters, and methods are converted into + /// top level methods, but access and invocation should still be treated as + /// if they are the original procedure kind. + ProcedureKind get extensionMethodKind => + throw new UnsupportedError('ObjectAccessTarget.extensionMethodKind'); + + /// Returns the number of type parameters of an extension method that comes + /// from the extension declaration. + int get extensionTypeParameterCount => throw new UnsupportedError( + 'ObjectAccessTarget.extensionTypeParameterCount'); + + @override String toString() => 'ObjectAccessTarget($kind,$member)'; } + +class ExtensionAccessTarget extends ObjectAccessTarget { + final ProcedureKind extensionMethodKind; + final int extensionTypeParameterCount; + + ExtensionAccessTarget( + Member member, this.extensionMethodKind, this.extensionTypeParameterCount) + : super.internal(ObjectAccessTargetKind.extensionMember, member); + + @override + String toString() => + 'ExtensionAccessTarget($kind,$member,$extensionMethodKind)'; +} diff --git a/pkg/front_end/test/spell_checking_list_common.txt b/pkg/front_end/test/spell_checking_list_common.txt index 06b4bc0a3ff..e332a42d510 100644 --- a/pkg/front_end/test/spell_checking_list_common.txt +++ b/pkg/front_end/test/spell_checking_list_common.txt @@ -1412,6 +1412,7 @@ investigate invocation invocation's invocations +invokable invoke invoked invokes diff --git a/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.expect b/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.expect index eff68252939..f650cf81204 100644 --- a/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.expect +++ b/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.expect @@ -72,8 +72,10 @@ static method Extension|get#tearOffGetterNamed(final self::Class* #this) → dyn return ({dynamic value = #C1}) → dynamic => self::Extension|writeSetterNamed(#this, value: value); static method Extension|get#property(final self::Class* #this) → dynamic return #this.{self::Class::field}; -static method Extension|set#property(final self::Class* #this, dynamic value) → void { +static method Extension|set#property(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; #this.{self::Class::field} = value; + return #t1; } static method Extension|invocations(final self::Class* #this, dynamic value) → dynamic { self::Extension|readGetter(#this); @@ -126,8 +128,10 @@ static method GenericExtension|genericWriteSetterNamed<#T extends core::Object* } static method GenericExtension|get#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this) → self::GenericExtension|get#property::#T* return #this.{self::GenericClass::field}; -static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this, self::GenericExtension|set#property::#T* value) → void { +static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this, self::GenericExtension|set#property::#T* value) → self::GenericExtension|set#property::#T* { + final self::GenericExtension|set#property::#T* #t2 = value; #this.{self::GenericClass::field} = value; + return #t2; } static method GenericExtension|get#tearOffGetterNoArgs<#T extends core::Object* = dynamic>(final self::GenericClass* #this) → dynamic return () → self::GenericExtension|get#tearOffGetterNoArgs::#T* => self::GenericExtension|readGetter(#this); diff --git a/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.transformed.expect index eff68252939..f650cf81204 100644 --- a/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/direct_instance_access.dart.strong.transformed.expect @@ -72,8 +72,10 @@ static method Extension|get#tearOffGetterNamed(final self::Class* #this) → dyn return ({dynamic value = #C1}) → dynamic => self::Extension|writeSetterNamed(#this, value: value); static method Extension|get#property(final self::Class* #this) → dynamic return #this.{self::Class::field}; -static method Extension|set#property(final self::Class* #this, dynamic value) → void { +static method Extension|set#property(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; #this.{self::Class::field} = value; + return #t1; } static method Extension|invocations(final self::Class* #this, dynamic value) → dynamic { self::Extension|readGetter(#this); @@ -126,8 +128,10 @@ static method GenericExtension|genericWriteSetterNamed<#T extends core::Object* } static method GenericExtension|get#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this) → self::GenericExtension|get#property::#T* return #this.{self::GenericClass::field}; -static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this, self::GenericExtension|set#property::#T* value) → void { +static method GenericExtension|set#property<#T extends core::Object* = dynamic>(final self::GenericClass* #this, self::GenericExtension|set#property::#T* value) → self::GenericExtension|set#property::#T* { + final self::GenericExtension|set#property::#T* #t2 = value; #this.{self::GenericClass::field} = value; + return #t2; } static method GenericExtension|get#tearOffGetterNoArgs<#T extends core::Object* = dynamic>(final self::GenericClass* #this) → dynamic return () → self::GenericExtension|get#tearOffGetterNoArgs::#T* => self::GenericExtension|readGetter(#this); diff --git a/pkg/front_end/testcases/extensions/extension_methods.dart.strong.expect b/pkg/front_end/testcases/extensions/extension_methods.dart.strong.expect index ebd3f2467e9..b1e71de7811 100644 --- a/pkg/front_end/testcases/extensions/extension_methods.dart.strong.expect +++ b/pkg/front_end/testcases/extensions/extension_methods.dart.strong.expect @@ -1,13 +1,4 @@ library; -// -// Problems in library: -// -// pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'. -// - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'. -// Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'. -// var result = c.one + c.two; -// ^^^ -// import self as self; import "dart:core" as core; import "package:expect/expect.dart" as exp; @@ -28,10 +19,6 @@ static method E|get#two(final self::C* #this) → core::int* return 2; static method main() → dynamic { self::C* c = new self::C::•(); - core::num* result = c.{self::C::one}.{core::num::+}(invalid-expression "pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'. - - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'. -Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'. - var result = c.one + c.two; - ^^^" as{TypeError} core::num*); + core::int* result = c.{self::C::one}.{core::num::+}(self::E|get#two(c)); exp::Expect::equals(result, 3); } diff --git a/pkg/front_end/testcases/extensions/extension_methods.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/extension_methods.dart.strong.transformed.expect index ebd3f2467e9..b1e71de7811 100644 --- a/pkg/front_end/testcases/extensions/extension_methods.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/extension_methods.dart.strong.transformed.expect @@ -1,13 +1,4 @@ library; -// -// Problems in library: -// -// pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'. -// - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'. -// Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'. -// var result = c.one + c.two; -// ^^^ -// import self as self; import "dart:core" as core; import "package:expect/expect.dart" as exp; @@ -28,10 +19,6 @@ static method E|get#two(final self::C* #this) → core::int* return 2; static method main() → dynamic { self::C* c = new self::C::•(); - core::num* result = c.{self::C::one}.{core::num::+}(invalid-expression "pkg/front_end/testcases/extensions/extension_methods.dart:19:26: Error: The getter 'two' isn't defined for the class 'C'. - - 'C' is from 'pkg/front_end/testcases/extensions/extension_methods.dart'. -Try correcting the name to the name of an existing getter, or defining a getter or field named 'two'. - var result = c.one + c.two; - ^^^" as{TypeError} core::num*); + core::int* result = c.{self::C::one}.{core::num::+}(self::E|get#two(c)); exp::Expect::equals(result, 3); } diff --git a/pkg/front_end/testcases/extensions/instance_access.dart b/pkg/front_end/testcases/extensions/instance_access.dart new file mode 100644 index 00000000000..249f7127a83 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access.dart @@ -0,0 +1,111 @@ +// 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 Class1 { + int field; + + Class1(this.field); + + String toString() => 'Class1($field)'; +} + +class Class2 { + int field; + + Class2(this.field); + + String toString() => 'Class2($field)'; +} + +extension Extension1 on Class1 { + int method() { + print('Extension1.method on $this'); + return field; + } + int genericMethod(T t) { + print('Extension1.genericMethod<$T>($t) on $this'); + return field + t; + } + int get property { + print('Extension1.property get on $this'); + return field; + } + set property(int value) { + field = value; + print('Extension1.property set($value) on $this'); + value++; + } +} + + +extension Extension2 on Class2 { + int method() { + print('Extension2.method on $this'); + return field + 3; + } + int genericMethod(T t) { + print('Extension2.genericMethod<$T>($t) on $this'); + return field + t + 4; + } + int get property { + print('Extension2.property get on $this'); + return field + 5; + } + set property(int value) { + print('Extension2.property set($value) on $this'); + value++; + field = value; + } +} + +main() { + testExtension1(); + testExtension2(); +} + +testExtension1() { + Class1 c0 = new Class1(0); + Class1 c1 = new Class1(1); + expect(0, c0.method()); + expect(1, c1.method()); + expect(42, c0.genericMethod(42)); + expect(43, c0.genericMethod(43)); + expect(88, c1.genericMethod(87)); + expect(89, c1.genericMethod(88)); + expect(0, c0.property); + expect(42, c0.property = 42); + expect(1, c1.property); + expect(87, c0.property = 87); + expect(27, c0.property = c1.property = 27); + expect(37, c1.property = c0.property = 37); + expect(77, c1.property = c0.property = c1.property = 77); + expect(67, c0.property = c1.property = c0.property = 67); +} + +testExtension2() { + Class2 c0 = new Class2(0); + Class2 c1 = new Class2(1); + expect(3, c0.method()); + expect(4, c1.method()); + expect(46, c0.genericMethod(42)); + expect(47, c0.genericMethod(43)); + expect(92, c1.genericMethod(87)); + expect(93, c1.genericMethod(88)); + expect(5, c0.property); + expect(42, c0.property = 42); + expect(48, c0.property); + expect(6, c1.property); + expect(43, c1.property = 43); + expect(49, c1.property); + expect(49, c0.property = c1.property); + expect(55, c1.property = c0.property); + expect(61, c1.property = c0.property = c1.property); + expect(67, c0.property = c1.property = c0.property); +} + +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/instance_access.dart.outline.expect b/pkg/front_end/testcases/extensions/instance_access.dart.outline.expect new file mode 100644 index 00000000000..1c0a78d1c07 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access.dart.outline.expect @@ -0,0 +1,54 @@ +library; +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class1* + ; + method toString() → core::String* + ; +} +class Class2 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class2* + ; + method toString() → core::String* + ; +} +extension Extension1 on self::Class1* { + method method = self::Extension1|method; + method genericMethod = self::Extension1|genericMethod; + get property = self::Extension1|get#property; + set property = self::Extension1|set#property; +} +extension Extension2 on self::Class2* { + method method = self::Extension2|method; + method genericMethod = self::Extension2|genericMethod; + get property = self::Extension2|get#property; + set property = self::Extension2|set#property; +} +static method Extension1|method(final self::Class1* #this) → core::int* + ; +static method Extension1|genericMethod(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int* + ; +static method Extension1|get#property(final self::Class1* #this) → core::int* + ; +static method Extension1|set#property(final self::Class1* #this, core::int* value) → void + ; +static method Extension2|method(final self::Class2* #this) → core::int* + ; +static method Extension2|genericMethod(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int* + ; +static method Extension2|get#property(final self::Class2* #this) → core::int* + ; +static method Extension2|set#property(final self::Class2* #this, core::int* value) → void + ; +static method main() → dynamic + ; +static method testExtension1() → dynamic + ; +static method testExtension2() → dynamic + ; +static method expect(dynamic expected, dynamic actual) → dynamic + ; diff --git a/pkg/front_end/testcases/extensions/instance_access.dart.strong.expect b/pkg/front_end/testcases/extensions/instance_access.dart.strong.expect new file mode 100644 index 00000000000..f2752e00c82 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access.dart.strong.expect @@ -0,0 +1,117 @@ +library; +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class1* + : self::Class1::field = field, super core::Object::•() + ; + method toString() → core::String* + return "Class1(${this.{self::Class1::field}})"; +} +class Class2 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class2* + : self::Class2::field = field, super core::Object::•() + ; + method toString() → core::String* + return "Class2(${this.{self::Class2::field}})"; +} +extension Extension1 on self::Class1* { + method method = self::Extension1|method; + method genericMethod = self::Extension1|genericMethod; + get property = self::Extension1|get#property; + set property = self::Extension1|set#property; +} +extension Extension2 on self::Class2* { + method method = self::Extension2|method; + method genericMethod = self::Extension2|genericMethod; + get property = self::Extension2|get#property; + set property = self::Extension2|set#property; +} +static method Extension1|method(final self::Class1* #this) → core::int* { + core::print("Extension1.method on ${#this}"); + return #this.{self::Class1::field}; +} +static method Extension1|genericMethod(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int* { + core::print("Extension1.genericMethod<${self::Extension1|genericMethod::T*}>(${t}) on ${#this}"); + return #this.{self::Class1::field}.{core::num::+}(t) as{TypeError} core::int*; +} +static method Extension1|get#property(final self::Class1* #this) → core::int* { + core::print("Extension1.property get on ${#this}"); + return #this.{self::Class1::field}; +} +static method Extension1|set#property(final self::Class1* #this, core::int* value) → core::int* { + final core::int* #t1 = value; + #this.{self::Class1::field} = value; + core::print("Extension1.property set(${value}) on ${#this}"); + value = value.{core::num::+}(1); + return #t1; +} +static method Extension2|method(final self::Class2* #this) → core::int* { + core::print("Extension2.method on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(3); +} +static method Extension2|genericMethod(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int* { + core::print("Extension2.genericMethod<${self::Extension2|genericMethod::T*}>(${t}) on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(t).{core::num::+}(4) as{TypeError} core::int*; +} +static method Extension2|get#property(final self::Class2* #this) → core::int* { + core::print("Extension2.property get on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(5); +} +static method Extension2|set#property(final self::Class2* #this, core::int* value) → core::int* { + final core::int* #t2 = value; + core::print("Extension2.property set(${value}) on ${#this}"); + value = value.{core::num::+}(1); + #this.{self::Class2::field} = value; + return #t2; +} +static method main() → dynamic { + self::testExtension1(); + self::testExtension2(); +} +static method testExtension1() → dynamic { + self::Class1* c0 = new self::Class1::•(0); + self::Class1* c1 = new self::Class1::•(1); + self::expect(0, self::Extension1|method(c0)); + self::expect(1, self::Extension1|method(c1)); + self::expect(42, self::Extension1|genericMethod(c0, 42)); + self::expect(43, self::Extension1|genericMethod(c0, 43)); + self::expect(88, self::Extension1|genericMethod(c1, 87)); + self::expect(89, self::Extension1|genericMethod(c1, 88)); + self::expect(0, self::Extension1|get#property(c0)); + self::expect(42, self::Extension1|set#property(c0, 42)); + self::expect(1, self::Extension1|get#property(c1)); + self::expect(87, self::Extension1|set#property(c0, 87)); + self::expect(27, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 27))); + self::expect(37, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 37))); + self::expect(77, self::Extension1|set#property(c1, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 77)))); + self::expect(67, self::Extension1|set#property(c0, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 67)))); +} +static method testExtension2() → dynamic { + self::Class2* c0 = new self::Class2::•(0); + self::Class2* c1 = new self::Class2::•(1); + self::expect(3, self::Extension2|method(c0)); + self::expect(4, self::Extension2|method(c1)); + self::expect(46, self::Extension2|genericMethod(c0, 42)); + self::expect(47, self::Extension2|genericMethod(c0, 43)); + self::expect(92, self::Extension2|genericMethod(c1, 87)); + self::expect(93, self::Extension2|genericMethod(c1, 88)); + self::expect(5, self::Extension2|get#property(c0)); + self::expect(42, self::Extension2|set#property(c0, 42)); + self::expect(48, self::Extension2|get#property(c0)); + self::expect(6, self::Extension2|get#property(c1)); + self::expect(43, self::Extension2|set#property(c1, 43)); + self::expect(49, self::Extension2|get#property(c1)); + self::expect(49, self::Extension2|set#property(c0, self::Extension2|get#property(c1))); + self::expect(55, self::Extension2|set#property(c1, self::Extension2|get#property(c0))); + self::expect(61, self::Extension2|set#property(c1, self::Extension2|set#property(c0, self::Extension2|get#property(c1)))); + self::expect(67, self::Extension2|set#property(c0, self::Extension2|set#property(c1, self::Extension2|get#property(c0)))); +} +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/instance_access.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/instance_access.dart.strong.transformed.expect new file mode 100644 index 00000000000..f2752e00c82 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access.dart.strong.transformed.expect @@ -0,0 +1,117 @@ +library; +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class1* + : self::Class1::field = field, super core::Object::•() + ; + method toString() → core::String* + return "Class1(${this.{self::Class1::field}})"; +} +class Class2 extends core::Object { + field core::int* field; + constructor •(core::int* field) → self::Class2* + : self::Class2::field = field, super core::Object::•() + ; + method toString() → core::String* + return "Class2(${this.{self::Class2::field}})"; +} +extension Extension1 on self::Class1* { + method method = self::Extension1|method; + method genericMethod = self::Extension1|genericMethod; + get property = self::Extension1|get#property; + set property = self::Extension1|set#property; +} +extension Extension2 on self::Class2* { + method method = self::Extension2|method; + method genericMethod = self::Extension2|genericMethod; + get property = self::Extension2|get#property; + set property = self::Extension2|set#property; +} +static method Extension1|method(final self::Class1* #this) → core::int* { + core::print("Extension1.method on ${#this}"); + return #this.{self::Class1::field}; +} +static method Extension1|genericMethod(final self::Class1* #this, self::Extension1|genericMethod::T* t) → core::int* { + core::print("Extension1.genericMethod<${self::Extension1|genericMethod::T*}>(${t}) on ${#this}"); + return #this.{self::Class1::field}.{core::num::+}(t) as{TypeError} core::int*; +} +static method Extension1|get#property(final self::Class1* #this) → core::int* { + core::print("Extension1.property get on ${#this}"); + return #this.{self::Class1::field}; +} +static method Extension1|set#property(final self::Class1* #this, core::int* value) → core::int* { + final core::int* #t1 = value; + #this.{self::Class1::field} = value; + core::print("Extension1.property set(${value}) on ${#this}"); + value = value.{core::num::+}(1); + return #t1; +} +static method Extension2|method(final self::Class2* #this) → core::int* { + core::print("Extension2.method on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(3); +} +static method Extension2|genericMethod(final self::Class2* #this, self::Extension2|genericMethod::T* t) → core::int* { + core::print("Extension2.genericMethod<${self::Extension2|genericMethod::T*}>(${t}) on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(t).{core::num::+}(4) as{TypeError} core::int*; +} +static method Extension2|get#property(final self::Class2* #this) → core::int* { + core::print("Extension2.property get on ${#this}"); + return #this.{self::Class2::field}.{core::num::+}(5); +} +static method Extension2|set#property(final self::Class2* #this, core::int* value) → core::int* { + final core::int* #t2 = value; + core::print("Extension2.property set(${value}) on ${#this}"); + value = value.{core::num::+}(1); + #this.{self::Class2::field} = value; + return #t2; +} +static method main() → dynamic { + self::testExtension1(); + self::testExtension2(); +} +static method testExtension1() → dynamic { + self::Class1* c0 = new self::Class1::•(0); + self::Class1* c1 = new self::Class1::•(1); + self::expect(0, self::Extension1|method(c0)); + self::expect(1, self::Extension1|method(c1)); + self::expect(42, self::Extension1|genericMethod(c0, 42)); + self::expect(43, self::Extension1|genericMethod(c0, 43)); + self::expect(88, self::Extension1|genericMethod(c1, 87)); + self::expect(89, self::Extension1|genericMethod(c1, 88)); + self::expect(0, self::Extension1|get#property(c0)); + self::expect(42, self::Extension1|set#property(c0, 42)); + self::expect(1, self::Extension1|get#property(c1)); + self::expect(87, self::Extension1|set#property(c0, 87)); + self::expect(27, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 27))); + self::expect(37, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 37))); + self::expect(77, self::Extension1|set#property(c1, self::Extension1|set#property(c0, self::Extension1|set#property(c1, 77)))); + self::expect(67, self::Extension1|set#property(c0, self::Extension1|set#property(c1, self::Extension1|set#property(c0, 67)))); +} +static method testExtension2() → dynamic { + self::Class2* c0 = new self::Class2::•(0); + self::Class2* c1 = new self::Class2::•(1); + self::expect(3, self::Extension2|method(c0)); + self::expect(4, self::Extension2|method(c1)); + self::expect(46, self::Extension2|genericMethod(c0, 42)); + self::expect(47, self::Extension2|genericMethod(c0, 43)); + self::expect(92, self::Extension2|genericMethod(c1, 87)); + self::expect(93, self::Extension2|genericMethod(c1, 88)); + self::expect(5, self::Extension2|get#property(c0)); + self::expect(42, self::Extension2|set#property(c0, 42)); + self::expect(48, self::Extension2|get#property(c0)); + self::expect(6, self::Extension2|get#property(c1)); + self::expect(43, self::Extension2|set#property(c1, 43)); + self::expect(49, self::Extension2|get#property(c1)); + self::expect(49, self::Extension2|set#property(c0, self::Extension2|get#property(c1))); + self::expect(55, self::Extension2|set#property(c1, self::Extension2|get#property(c0))); + self::expect(61, self::Extension2|set#property(c1, self::Extension2|set#property(c0, self::Extension2|get#property(c1)))); + self::expect(67, self::Extension2|set#property(c0, self::Extension2|set#property(c1, self::Extension2|get#property(c0)))); +} +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/instance_access.dart.type_promotion.expect b/pkg/front_end/testcases/extensions/instance_access.dart.type_promotion.expect new file mode 100644 index 00000000000..052b27420a9 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access.dart.type_promotion.expect @@ -0,0 +1,6 @@ +pkg/front_end/testcases/extensions/instance_access.dart:37:10: Context: Write to value@755 + value++; + ^^ +pkg/front_end/testcases/extensions/instance_access.dart:57:10: Context: Write to value@1217 + value++; + ^^ diff --git a/pkg/front_end/testcases/extensions/instance_access_of_static.dart b/pkg/front_end/testcases/extensions/instance_access_of_static.dart new file mode 100644 index 00000000000..a8b0b2da5b6 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access_of_static.dart @@ -0,0 +1,32 @@ +// 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 Class1 { +} + +extension Extension1 on Class1 { + static staticMethod() { + print('Extension1.staticMethod()'); + } + + static get staticProperty { + print('Extension1.staticProperty()'); + } + static set staticProperty(int value) { + print('Extension1.staticProperty($value)'); + value++; + } + + static var staticField = 42; +} + +main() { + Class1 c = new Class1(); + c.staticMethod(); + c.staticMethod; + c.staticProperty; + c.staticProperty = 42; + c.staticField; + c.staticField = 42; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/extensions/instance_access_of_static.dart.outline.expect b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.outline.expect new file mode 100644 index 00000000000..731aa090ee2 --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.outline.expect @@ -0,0 +1,23 @@ +library; +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + synthetic constructor •() → self::Class1* + ; +} +extension Extension1 on self::Class1* { + static method staticMethod = self::Extension1|staticMethod; + static get staticProperty = get self::Extension1|staticProperty; + static field staticField = self::Extension1|staticField; + static set staticProperty = set self::Extension1|staticProperty; +} +static field core::int* Extension1|staticField; +static method Extension1|staticMethod() → dynamic + ; +static get Extension1|staticProperty() → dynamic + ; +static set Extension1|staticProperty(core::int* value) → void + ; +static method main() → dynamic + ; diff --git a/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.expect b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.expect new file mode 100644 index 00000000000..75191b4ff2c --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.expect @@ -0,0 +1,98 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'. +// c.staticMethod(); +// ^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'. +// c.staticMethod; +// ^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'. +// c.staticProperty; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'. +// c.staticProperty = 42; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'. +// c.staticField; +// ^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'. +// c.staticField = 42; +// ^^^^^^^^^^^ +// +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + synthetic constructor •() → self::Class1* + : super core::Object::•() + ; +} +extension Extension1 on self::Class1* { + static method staticMethod = self::Extension1|staticMethod; + static get staticProperty = get self::Extension1|staticProperty; + static field staticField = self::Extension1|staticField; + static set staticProperty = set self::Extension1|staticProperty; +} +static field core::int* Extension1|staticField = 42; +static method Extension1|staticMethod() → dynamic { + core::print("Extension1.staticMethod()"); +} +static get Extension1|staticProperty() → dynamic { + core::print("Extension1.staticProperty()"); +} +static set Extension1|staticProperty(core::int* value) → void { + core::print("Extension1.staticProperty(${value})"); + value = value.{core::num::+}(1); +} +static method main() → dynamic { + self::Class1* c = new self::Class1::•(); + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'. + c.staticMethod(); + ^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'. + c.staticMethod; + ^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'. + c.staticProperty; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'. + c.staticProperty = 42; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'. + c.staticField; + ^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'. + c.staticField = 42; + ^^^^^^^^^^^"; +} diff --git a/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.transformed.expect new file mode 100644 index 00000000000..75191b4ff2c --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.strong.transformed.expect @@ -0,0 +1,98 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'. +// c.staticMethod(); +// ^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'. +// c.staticMethod; +// ^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'. +// c.staticProperty; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'. +// c.staticProperty = 42; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'. +// c.staticField; +// ^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'. +// - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +// Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'. +// c.staticField = 42; +// ^^^^^^^^^^^ +// +import self as self; +import "dart:core" as core; + +class Class1 extends core::Object { + synthetic constructor •() → self::Class1* + : super core::Object::•() + ; +} +extension Extension1 on self::Class1* { + static method staticMethod = self::Extension1|staticMethod; + static get staticProperty = get self::Extension1|staticProperty; + static field staticField = self::Extension1|staticField; + static set staticProperty = set self::Extension1|staticProperty; +} +static field core::int* Extension1|staticField = 42; +static method Extension1|staticMethod() → dynamic { + core::print("Extension1.staticMethod()"); +} +static get Extension1|staticProperty() → dynamic { + core::print("Extension1.staticProperty()"); +} +static set Extension1|staticProperty(core::int* value) → void { + core::print("Extension1.staticProperty(${value})"); + value = value.{core::num::+}(1); +} +static method main() → dynamic { + self::Class1* c = new self::Class1::•(); + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:26:5: Error: The method 'staticMethod' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing method, or defining a method named 'staticMethod'. + c.staticMethod(); + ^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:27:5: Error: The getter 'staticMethod' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticMethod'. + c.staticMethod; + ^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:28:5: Error: The getter 'staticProperty' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticProperty'. + c.staticProperty; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:29:5: Error: The setter 'staticProperty' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticProperty'. + c.staticProperty = 42; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:30:5: Error: The getter 'staticField' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing getter, or defining a getter or field named 'staticField'. + c.staticField; + ^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/instance_access_of_static.dart:31:5: Error: The setter 'staticField' isn't defined for the class 'Class1'. + - 'Class1' is from 'pkg/front_end/testcases/extensions/instance_access_of_static.dart'. +Try correcting the name to the name of an existing setter, or defining a setter or field named 'staticField'. + c.staticField = 42; + ^^^^^^^^^^^"; +} diff --git a/pkg/front_end/testcases/extensions/instance_access_of_static.dart.type_promotion.expect b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.type_promotion.expect new file mode 100644 index 00000000000..296766044bd --- /dev/null +++ b/pkg/front_end/testcases/extensions/instance_access_of_static.dart.type_promotion.expect @@ -0,0 +1,3 @@ +pkg/front_end/testcases/extensions/instance_access_of_static.dart:18:10: Context: Write to value@447 + value++; + ^^ diff --git a/pkg/front_end/testcases/extensions/other_kinds.dart.strong.expect b/pkg/front_end/testcases/extensions/other_kinds.dart.strong.expect index 6b24546463d..4c4e60c6df8 100644 --- a/pkg/front_end/testcases/extensions/other_kinds.dart.strong.expect +++ b/pkg/front_end/testcases/extensions/other_kinds.dart.strong.expect @@ -30,8 +30,10 @@ extension A2 on self::A1* { static field core::int* A2|staticField = self::A1::getStaticField(); static method A2|get#instanceProperty(final self::A1* #this) → core::int* return #this.{self::A1::getInstanceField}(); -static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → void { +static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → core::int* { + final core::int* #t1 = value; #this.{self::A1::setInstanceField}(value); + return #t1; } static method A2|+(final self::A1* #this, core::int* value) → core::int* { return #this.{self::A1::getInstanceField}().{core::num::+}(value); diff --git a/pkg/front_end/testcases/extensions/other_kinds.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/other_kinds.dart.strong.transformed.expect index 6b24546463d..4c4e60c6df8 100644 --- a/pkg/front_end/testcases/extensions/other_kinds.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/other_kinds.dart.strong.transformed.expect @@ -30,8 +30,10 @@ extension A2 on self::A1* { static field core::int* A2|staticField = self::A1::getStaticField(); static method A2|get#instanceProperty(final self::A1* #this) → core::int* return #this.{self::A1::getInstanceField}(); -static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → void { +static method A2|set#instanceProperty(final self::A1* #this, core::int* value) → core::int* { + final core::int* #t1 = value; #this.{self::A1::setInstanceField}(value); + return #t1; } static method A2|+(final self::A1* #this, core::int* value) → core::int* { return #this.{self::A1::getInstanceField}().{core::num::+}(value); diff --git a/pkg/front_end/testcases/extensions/static_access.dart b/pkg/front_end/testcases/extensions/static_access.dart index c31ac015bfd..170f77e078c 100644 --- a/pkg/front_end/testcases/extensions/static_access.dart +++ b/pkg/front_end/testcases/extensions/static_access.dart @@ -10,6 +10,10 @@ extension Extension on Class { static get property => 42; static set property(value) {} static var field; + + instanceMethod() {} + get instanceProperty => 42; + set instanceProperty(value) {} } main() { @@ -22,4 +26,4 @@ main() { Extension.property = 42; Extension.field; Extension.field = 42; -} \ No newline at end of file +} diff --git a/pkg/front_end/testcases/extensions/static_access.dart.outline.expect b/pkg/front_end/testcases/extensions/static_access.dart.outline.expect index 4e9dc86a461..f98e8cba50d 100644 --- a/pkg/front_end/testcases/extensions/static_access.dart.outline.expect +++ b/pkg/front_end/testcases/extensions/static_access.dart.outline.expect @@ -11,7 +11,10 @@ extension Extension on self::Class* { static method genericMethod = self::Extension|genericMethod; static get property = get self::Extension|property; static field field = self::Extension|field; + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; static set property = set self::Extension|property; + set instanceProperty = self::Extension|set#instanceProperty; } static field dynamic Extension|field; static method Extension|method() → dynamic @@ -22,5 +25,11 @@ static get Extension|property() → dynamic ; static set Extension|property(dynamic value) → void ; +static method Extension|instanceMethod(final self::Class* #this) → dynamic + ; +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + ; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → void + ; static method main() → dynamic ; diff --git a/pkg/front_end/testcases/extensions/static_access.dart.strong.expect b/pkg/front_end/testcases/extensions/static_access.dart.strong.expect index 9056365a923..a45669aa440 100644 --- a/pkg/front_end/testcases/extensions/static_access.dart.strong.expect +++ b/pkg/front_end/testcases/extensions/static_access.dart.strong.expect @@ -12,7 +12,10 @@ extension Extension on self::Class* { static method genericMethod = self::Extension|genericMethod; static get property = get self::Extension|property; static field field = self::Extension|field; + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; static set property = set self::Extension|property; + set instanceProperty = self::Extension|set#instanceProperty; } static field dynamic Extension|field; static method Extension|method() → dynamic {} @@ -20,6 +23,13 @@ static method Extension|genericMethod(self::E static get Extension|property() → dynamic return 42; static set Extension|property(dynamic value) → void {} +static method Extension|instanceMethod(final self::Class* #this) → dynamic {} +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + return 42; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; + return #t1; +} static method main() → dynamic { self::Extension|method(); self::Extension|genericMethod(42); diff --git a/pkg/front_end/testcases/extensions/static_access.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/static_access.dart.strong.transformed.expect index 9056365a923..a45669aa440 100644 --- a/pkg/front_end/testcases/extensions/static_access.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extensions/static_access.dart.strong.transformed.expect @@ -12,7 +12,10 @@ extension Extension on self::Class* { static method genericMethod = self::Extension|genericMethod; static get property = get self::Extension|property; static field field = self::Extension|field; + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; static set property = set self::Extension|property; + set instanceProperty = self::Extension|set#instanceProperty; } static field dynamic Extension|field; static method Extension|method() → dynamic {} @@ -20,6 +23,13 @@ static method Extension|genericMethod(self::E static get Extension|property() → dynamic return 42; static set Extension|property(dynamic value) → void {} +static method Extension|instanceMethod(final self::Class* #this) → dynamic {} +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + return 42; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; + return #t1; +} static method main() → dynamic { self::Extension|method(); self::Extension|genericMethod(42); diff --git a/pkg/front_end/testcases/extensions/static_access_of_instance.dart b/pkg/front_end/testcases/extensions/static_access_of_instance.dart new file mode 100644 index 00000000000..606a73564e3 --- /dev/null +++ b/pkg/front_end/testcases/extensions/static_access_of_instance.dart @@ -0,0 +1,18 @@ +// 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 Class {} + +extension Extension on Class { + instanceMethod() {} + get instanceProperty => 42; + set instanceProperty(value) {} +} + +main() { + Extension.instanceMethod(); + Extension.instanceMethod; + Extension.instanceProperty; + Extension.instanceProperty = 42; +} \ No newline at end of file diff --git a/pkg/front_end/testcases/extensions/static_access_of_instance.dart.outline.expect b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.outline.expect new file mode 100644 index 00000000000..6e6c7d2a994 --- /dev/null +++ b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.outline.expect @@ -0,0 +1,21 @@ +library; +import self as self; +import "dart:core" as core; + +class Class extends core::Object { + synthetic constructor •() → self::Class* + ; +} +extension Extension on self::Class* { + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; + set instanceProperty = self::Extension|set#instanceProperty; +} +static method Extension|instanceMethod(final self::Class* #this) → dynamic + ; +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + ; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → void + ; +static method main() → dynamic + ; diff --git a/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.expect b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.expect new file mode 100644 index 00000000000..50f9f2ffab2 --- /dev/null +++ b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.expect @@ -0,0 +1,54 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'. +// Extension.instanceMethod(); +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'. +// Extension.instanceMethod; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'. +// Extension.instanceProperty; +// ^^^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'. +// Extension.instanceProperty = 42; +// ^^^^^^^^^^^^^^^^ +// +import self as self; +import "dart:core" as core; + +class Class extends core::Object { + synthetic constructor •() → self::Class* + : super core::Object::•() + ; +} +extension Extension on self::Class* { + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; + set instanceProperty = self::Extension|set#instanceProperty; +} +static method Extension|instanceMethod(final self::Class* #this) → dynamic {} +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + return 42; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; + return #t1; +} +static method main() → dynamic { + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'. + Extension.instanceMethod(); + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'. + Extension.instanceMethod; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'. + Extension.instanceProperty; + ^^^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'. + Extension.instanceProperty = 42; + ^^^^^^^^^^^^^^^^"; +} diff --git a/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.transformed.expect b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.transformed.expect new file mode 100644 index 00000000000..50f9f2ffab2 --- /dev/null +++ b/pkg/front_end/testcases/extensions/static_access_of_instance.dart.strong.transformed.expect @@ -0,0 +1,54 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'. +// Extension.instanceMethod(); +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'. +// Extension.instanceMethod; +// ^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'. +// Extension.instanceProperty; +// ^^^^^^^^^^^^^^^^ +// +// pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'. +// Extension.instanceProperty = 42; +// ^^^^^^^^^^^^^^^^ +// +import self as self; +import "dart:core" as core; + +class Class extends core::Object { + synthetic constructor •() → self::Class* + : super core::Object::•() + ; +} +extension Extension on self::Class* { + method instanceMethod = self::Extension|instanceMethod; + get instanceProperty = self::Extension|get#instanceProperty; + set instanceProperty = self::Extension|set#instanceProperty; +} +static method Extension|instanceMethod(final self::Class* #this) → dynamic {} +static method Extension|get#instanceProperty(final self::Class* #this) → dynamic + return 42; +static method Extension|set#instanceProperty(final self::Class* #this, dynamic value) → dynamic { + final dynamic #t1 = value; + return #t1; +} +static method main() → dynamic { + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:14:13: Error: Method not found: 'Extension.instanceMethod'. + Extension.instanceMethod(); + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:15:13: Error: Getter not found: 'instanceMethod'. + Extension.instanceMethod; + ^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:16:13: Error: Getter not found: 'instanceProperty'. + Extension.instanceProperty; + ^^^^^^^^^^^^^^^^"; + invalid-expression "pkg/front_end/testcases/extensions/static_access_of_instance.dart:17:13: Error: Setter not found: 'instanceProperty'. + Extension.instanceProperty = 42; + ^^^^^^^^^^^^^^^^"; +} diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status index 3cd71a8d51b..06b3473727c 100644 --- a/pkg/front_end/testcases/strong.status +++ b/pkg/front_end/testcases/strong.status @@ -6,7 +6,8 @@ # Kernel ASTs directly, that is, code in pkg/fasta/lib/src/kernel/ with # strong-mode enabled. -extensions/extension_methods: RuntimeError +extensions/instance_access_of_static: RuntimeError +extensions/static_access_of_instance: RuntimeError general/abstract_members: TypeCheckError general/accessors: RuntimeError general/ambiguous_exports: RuntimeError # Expected, this file exports two main methods. diff --git a/pkg/front_end/testcases/text_serialization.status b/pkg/front_end/testcases/text_serialization.status index 3a7d40663ee..a1bfd3e9de0 100644 --- a/pkg/front_end/testcases/text_serialization.status +++ b/pkg/front_end/testcases/text_serialization.status @@ -11,11 +11,14 @@ expression/main: TextSerializationFailure # Was: Pass extensions/explicit_this: TextSerializationFailure extensions/extension_methods: TextSerializationFailure extensions/implicit_this: TextSerializationFailure +extensions/instance_access: TextSerializationFailure +extensions/instance_access_of_static: TextSerializationFailure extensions/direct_instance_access: TextSerializationFailure extensions/direct_static_access: TextSerializationFailure extensions/instance_members: TextSerializationFailure extensions/other_kinds: TextSerializationFailure extensions/static_access: TextSerializationFailure +extensions/static_access_of_instance: TextSerializationFailure extensions/type_variables: TextSerializationFailure extensions/use_this: TextSerializationFailure general/abstract_members: TypeCheckError