diff --git a/pkg/compiler/lib/src/inferrer/builder.dart b/pkg/compiler/lib/src/inferrer/builder.dart index 900f45376e5..c5df0f707c1 100644 --- a/pkg/compiler/lib/src/inferrer/builder.dart +++ b/pkg/compiler/lib/src/inferrer/builder.dart @@ -2798,17 +2798,9 @@ class ElementGraphBuilder extends ast.Visitor } } - return inferrer.registerCalledSelector( - callType, - node, - selector, - mask, - receiverType, - outermostElement, - arguments, - sideEffects, - inLoop, - isConditional); + return inferrer.registerCalledSelector(callType, node, selector, mask, + receiverType, outermostElement, arguments, sideEffects, + inLoop: inLoop, isConditional: isConditional); } TypeInformation handleDynamicInvoke(ast.Send node) { diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart index a2739301b5b..ac087dd97d7 100644 --- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart +++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart @@ -12,6 +12,9 @@ import '../elements/types.dart'; import '../kernel/element_map.dart'; import '../options.dart'; import '../types/constants.dart'; +import '../types/types.dart'; +import '../universe/selector.dart'; +import '../universe/side_effects.dart'; import '../world.dart'; import 'inferrer_engine.dart'; import 'locals_handler.dart'; @@ -35,6 +38,8 @@ class KernelTypeGraphBuilder extends ir.Visitor { final KernelToElementMapForBuilding _elementMap; final KernelToLocalsMap _localsMap; LocalsHandler _locals; + final GlobalTypeInferenceElementData _memberData; + SideEffects _sideEffects = new SideEffects.empty(); TypeInformation _returnType; @@ -48,7 +53,8 @@ class KernelTypeGraphBuilder extends ir.Visitor { this._elementMap, this._localsMap, [this._locals]) - : this._types = _inferrer.types { + : this._types = _inferrer.types, + this._memberData = _inferrer.dataOfMember(_analyzedMember) { if (_locals != null) return; FieldInitializationScope fieldScope = @@ -59,6 +65,10 @@ class KernelTypeGraphBuilder extends ir.Visitor { _inferrer, _types, _options, _analyzedNode, fieldScope); } + int _loopLevel = 0; + + bool get inLoop => _loopLevel > 0; + TypeInformation run() { if (_analyzedMember.isField) { if (_analyzedNode == null || _analyzedNode is ir.NullLiteral) { @@ -254,4 +264,62 @@ class KernelTypeGraphBuilder extends ir.Visitor { _locals.update(local, rhsType, node, type); return rhsType; } + + ArgumentsTypes analyzeArguments(ir.Arguments arguments) { + List positional = []; + Map named; + for (ir.Expression argument in arguments.positional) { + positional.add(argument.accept(this)); + } + for (ir.NamedExpression argument in arguments.named) { + named ??= {}; + named[argument.name] = argument.value.accept(this); + } + + /// TODO(johnniwinther): Track `isThisExposed`. + return new ArgumentsTypes(positional, named); + } + + @override + TypeInformation visitMethodInvocation(ir.MethodInvocation node) { + TypeInformation receiverType = visit(node.receiver); + Selector selector = _elementMap.getSelector(node); + TypeMask mask = _memberData.typeOfSend(node); + + ArgumentsTypes arguments = analyzeArguments(node.arguments); + if (selector.name == '==' || selector.name == '!=') { + if (_types.isNull(receiverType)) { + // TODO(johnniwinther): Add null check. + return _types.boolType; + } else if (_types.isNull(arguments.positional[0])) { + // TODO(johnniwinther): Add null check. + return _types.boolType; + } + } + return handleDynamicInvoke( + CallType.access, node, selector, mask, receiverType, arguments); + } + + TypeInformation handleDynamicInvoke( + CallType callType, + ir.Node node, + Selector selector, + TypeMask mask, + TypeInformation receiverType, + ArgumentsTypes arguments) { + assert(receiverType != null); + if (_types.selectorNeedsUpdate(receiverType, mask)) { + mask = receiverType == _types.dynamicType + ? null + : _types.newTypedSelector(receiverType, mask); + _inferrer.updateSelectorInMember( + _analyzedMember, callType, node, selector, mask); + } + + // TODO(johnniwinther): Refine receiver on non-captured locals. + + return _inferrer.registerCalledSelector(callType, node, selector, mask, + receiverType, _analyzedMember, arguments, _sideEffects, + inLoop: inLoop, isConditional: false); + } } diff --git a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart index b04c4ab7124..085ac470f30 100644 --- a/pkg/compiler/lib/src/inferrer/inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/inferrer_engine.dart @@ -123,9 +123,9 @@ abstract class InferrerEngine { Iterable getCallersOf(MemberEntity element); // TODO(johnniwinther): Make this private again. - GlobalTypeInferenceElementData dataOfMember(MemberEntity element); + GlobalTypeInferenceElementData dataOfMember(MemberEntity element); - GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element); + GlobalTypeInferenceElementData lookupDataOfMember(MemberEntity element); bool checkIfExposesThis(ConstructorEntity element); @@ -194,8 +194,8 @@ abstract class InferrerEngine { MemberEntity caller, ArgumentsTypes arguments, SideEffects sideEffects, - bool inLoop, - bool isConditional); + {bool inLoop, + bool isConditional}); /// Update the assignments to parameters in the graph. [remove] tells whether /// assignments must be added or removed. If [init] is false, parameters are @@ -1004,8 +1004,8 @@ abstract class InferrerEngineImpl extends InferrerEngine { MemberEntity caller, ArgumentsTypes arguments, SideEffects sideEffects, - bool inLoop, - bool isConditional) { + {bool inLoop, + bool isConditional}) { if (selector.isClosureCall) { return registerCalledClosure(node, selector, mask, receiverType, caller, arguments, sideEffects, inLoop); @@ -1105,7 +1105,7 @@ abstract class InferrerEngineImpl extends InferrerEngine { } TypeInformation typeOfMemberWithSelector( - covariant MemberElement element, Selector selector) { + MemberEntity element, Selector selector) { if (element.name == Identifiers.noSuchMethod_ && selector.name != element.name) { // An invocation can resolve to a [noSuchMethod], in which case @@ -1119,11 +1119,11 @@ abstract class InferrerEngineImpl extends InferrerEngine { : types.functionType; } else if (element.isField) { return typeOfMember(element); - } else if (Elements.isUnresolved(element)) { - return types.dynamicType; - } else { - assert(element.isGetter); + } else if (element.isGetter) { return returnTypeOfMember(element); + } else { + assert(element is MemberElement && Elements.isUnresolved(element)); + return types.dynamicType; } } else if (element.isGetter || element.isField) { assert(selector.isCall || selector.isSetter); diff --git a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart index 21dc8ec5feb..bee3b4d2e48 100644 --- a/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart +++ b/pkg/compiler/lib/src/inferrer/kernel_inferrer_engine.dart @@ -205,7 +205,7 @@ class KernelInferrerEngine extends InferrerEngineImpl { @override GlobalTypeInferenceElementData createElementData() { - throw new UnimplementedError('KernelInferrerEngine.createElementData'); + return new KernelGlobalTypeInferenceElementData(); } } @@ -343,3 +343,81 @@ class KernelTypeSystemStrategy implements TypeSystemStrategy { } } } + +class KernelGlobalTypeInferenceElementData + extends GlobalTypeInferenceElementData { + // TODO(johnniwinther): Rename this together with [typeOfSend]. + Map _sendMap; + + @override + TypeMask typeOfSend(ir.Node node) { + if (_sendMap == null) return null; + return _sendMap[node]; + } + + @override + void setCurrentTypeMask(ir.Node node, TypeMask mask) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.setCurrentTypeMask'); + } + + @override + void setMoveNextTypeMask(ir.Node node, TypeMask mask) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.setMoveNextTypeMask'); + } + + @override + void setIteratorTypeMask(ir.Node node, TypeMask mask) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.setIteratorTypeMask'); + } + + @override + TypeMask typeOfIteratorCurrent(ir.Node node) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.typeOfIteratorCurrent'); + } + + @override + TypeMask typeOfIteratorMoveNext(ir.Node node) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.typeOfIteratorMoveNext'); + } + + @override + TypeMask typeOfIterator(ir.Node node) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.typeOfIterator'); + } + + @override + void setOperatorTypeMaskInComplexSendSet(ir.Node node, TypeMask mask) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.setOperatorTypeMaskInComplexSendSet'); + } + + @override + void setGetterTypeMaskInComplexSendSet(ir.Node node, TypeMask mask) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.setGetterTypeMaskInComplexSendSet'); + } + + @override + void setTypeMask(ir.Node node, TypeMask mask) { + _sendMap ??= {}; + _sendMap[node] = mask; + } + + @override + TypeMask typeOfOperator(ir.Node node) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.typeOfOperator'); + } + + @override + TypeMask typeOfGetter(ir.Node node) { + throw new UnsupportedError( + 'KernelGlobalTypeInferenceElementData.typeOfGetter'); + } +} diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart index 25632dbe81d..3e5635ae185 100644 --- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart +++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart @@ -6,6 +6,8 @@ library compiler.src.inferrer.type_graph_nodes; import 'dart:collection' show IterableBase; +import 'package:kernel/ast.dart' as ir; + import '../common.dart'; import '../common/names.dart' show Identifiers; import '../constants/values.dart'; @@ -382,12 +384,13 @@ abstract class MemberTypeInformation extends ElementTypeInformation * to enable counting the global number of call sites of [element]. * * A call site is either an AST [ast.Node], an [Element] (see uses of - * [synthesizeForwardingCall] in [SimpleTypeInferrerVisitor]). + * [synthesizeForwardingCall] in [SimpleTypeInferrerVisitor]) or an IR + * [ir.Node]. * * The global information is summarized in [cleanup], after which [_callers] * is set to `null`. */ - Map> _callers; + Map> _callers; MemberTypeInformation._internal(this._member) : super._internal(null) { assert(_checkMember(_member)); @@ -401,12 +404,12 @@ abstract class MemberTypeInformation extends ElementTypeInformation String get debugName => '$member'; - void addCall(MemberEntity caller, Spannable node) { - _callers ??= >{}; + void addCall(MemberEntity caller, Object node) { + _callers ??= >{}; _callers.putIfAbsent(caller, () => new Setlet()).add(node); } - void removeCall(MemberEntity caller, node) { + void removeCall(MemberEntity caller, Object node) { if (_callers == null) return; Setlet calls = _callers[caller]; if (calls == null) return; @@ -831,7 +834,7 @@ bool validCallType(CallType callType, Object call) { case CallType.complex: return call is ast.SendSet; case CallType.access: - return call is ast.Send; + return call is ast.Send || call is ir.Node; case CallType.forIn: return call is ast.ForIn; } @@ -863,7 +866,8 @@ abstract class CallSiteTypeInformation extends TypeInformation assert(_checkCaller(caller)); // [_call] is either an AST node or a constructor element in case of a // a forwarding constructor _call. - assert(_call is ast.Node || _call is ConstructorElement); + assert( + _call is ast.Node || _call is ConstructorElement || _call is ir.Node); } bool _checkCaller(MemberEntity caller) { diff --git a/pkg/compiler/lib/src/types/types.dart b/pkg/compiler/lib/src/types/types.dart index 03d72ea2d9c..d950348f726 100644 --- a/pkg/compiler/lib/src/types/types.dart +++ b/pkg/compiler/lib/src/types/types.dart @@ -152,6 +152,7 @@ class GlobalTypeInferenceParameterResultImpl /// Internal data used during type-inference to store intermediate results about /// a single element. abstract class GlobalTypeInferenceElementData { + // TODO(johnniwinther): Remove this. Maybe split by access/invoke. TypeMask typeOfSend(T node); TypeMask typeOfGetter(T node); TypeMask typeOfOperator(T node); diff --git a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart index b5b61af76e0..c9b36793c4e 100644 --- a/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart +++ b/tests/compiler/dart2js/equivalence/id_equivalence_helper.dart @@ -133,17 +133,15 @@ class IdData { IdValue value = expectedMap[id]; if (data.value != value || value == null && data.value.value != '') { String expected = value?.toString() ?? ''; + int offset = getOffsetFromId(id); annotations - .putIfAbsent(data.sourceSpan.begin, () => []) + .putIfAbsent(offset, () => []) .add('${expected} | ${data.value}'); } }); expectedMap.forEach((Id id, IdValue expected) { if (!actualMap.containsKey(id)) { - int offset = compiler.reporter - .spanFromSpannable( - computeSpannable(elementEnvironment, mainUri, id)) - .begin; + int offset = getOffsetFromId(id); annotations.putIfAbsent(offset, () => []).add('${expected} | ---'); } }); @@ -171,6 +169,12 @@ class IdData { }); return withAnnotations(annotations); } + + int getOffsetFromId(Id id) { + return compiler.reporter + .spanFromSpannable(computeSpannable(elementEnvironment, mainUri, id)) + .begin; + } } /// Check code for all test files int [data] using [computeFromAst] and diff --git a/tests/compiler/dart2js/inference/data/locals.dart b/tests/compiler/dart2js/inference/data/locals.dart index eefa25bc290..4ef0128215a 100644 --- a/tests/compiler/dart2js/inference/data/locals.dart +++ b/tests/compiler/dart2js/inference/data/locals.dart @@ -7,6 +7,8 @@ main() { uninitializedLocal(); initializedLocal(); updatedLocal(); + invokeLocal(); + postfixLocal(); } /*element: uninitializedLocal:[null]*/ @@ -27,3 +29,18 @@ updatedLocal() { local2 = 0; return local2; } + +/*element: invokeLocal:[null]*/ +invokeLocal() { + var local2 = 0; + local2. /*invoke: [exact=JSUInt31]*/ toString(); + return null; +} + +/*element: postfixLocal:[null]*/ +postfixLocal() { + // ignore: UNUSED_LOCAL_VARIABLE + var local2 = 0; + local2 /*invoke: [exact=JSUInt31]*/ ++; + return null; +}