mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 22:49:53 +00:00
[vm/tfa] Use pragma to pass type arguments to exact return types.
Change-Id: Ie2a1de07653c53e8b9506c54a0a66a5fe6503b9a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129160 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
parent
e3a20bf361
commit
445ae73b32
|
@ -10,6 +10,8 @@ import 'package:kernel/core_types.dart' show CoreTypes;
|
|||
const kEntryPointPragmaName = "vm:entry-point";
|
||||
const kExactResultTypePragmaName = "vm:exact-result-type";
|
||||
const kNonNullableResultType = "vm:non-nullable-result-type";
|
||||
const kResultTypeUsesPassedTypeArguments =
|
||||
"result-type-uses-passed-type-arguments";
|
||||
|
||||
abstract class ParsedPragma {}
|
||||
|
||||
|
@ -22,7 +24,9 @@ class ParsedEntryPointPragma extends ParsedPragma {
|
|||
|
||||
class ParsedResultTypeByTypePragma extends ParsedPragma {
|
||||
final DartType type;
|
||||
ParsedResultTypeByTypePragma(this.type);
|
||||
final bool resultTypeUsesPassedTypeArguments;
|
||||
ParsedResultTypeByTypePragma(
|
||||
this.type, this.resultTypeUsesPassedTypeArguments);
|
||||
}
|
||||
|
||||
class ParsedResultTypeByPathPragma extends ParsedPragma {
|
||||
|
@ -97,9 +101,17 @@ class ConstantPragmaAnnotationParser extends PragmaAnnotationParser {
|
|||
case kExactResultTypePragmaName:
|
||||
if (options == null) return null;
|
||||
if (options is TypeLiteralConstant) {
|
||||
return new ParsedResultTypeByTypePragma(options.type);
|
||||
return new ParsedResultTypeByTypePragma(options.type, false);
|
||||
} else if (options is StringConstant) {
|
||||
return new ParsedResultTypeByPathPragma(options.value);
|
||||
} else if (options is ListConstant &&
|
||||
options.entries.length == 2 &&
|
||||
options.entries[0] is TypeLiteralConstant &&
|
||||
options.entries[1] is StringConstant &&
|
||||
(options.entries[1] as StringConstant).value ==
|
||||
kResultTypeUsesPassedTypeArguments) {
|
||||
return new ParsedResultTypeByTypePragma(
|
||||
(options.entries[0] as TypeLiteralConstant).type, true);
|
||||
}
|
||||
throw "ERROR: Unsupported option to '$kExactResultTypePragmaName' "
|
||||
"pragma: $options";
|
||||
|
|
|
@ -928,10 +928,10 @@ class GenericInterfacesInfoImpl implements GenericInterfacesInfo {
|
|||
final cachedFlattenedTypeArgs = <Class, List<DartType>>{};
|
||||
final cachedFlattenedTypeArgsForNonGeneric = <Class, List<Type>>{};
|
||||
|
||||
RuntimeTypeTranslator closedTypeTranslator;
|
||||
RuntimeTypeTranslatorImpl closedTypeTranslator;
|
||||
|
||||
GenericInterfacesInfoImpl(this.hierarchy) {
|
||||
closedTypeTranslator = RuntimeTypeTranslator.forClosedTypes(this);
|
||||
closedTypeTranslator = RuntimeTypeTranslatorImpl.forClosedTypes(this);
|
||||
}
|
||||
|
||||
List<DartType> flattenedTypeArgumentsFor(Class klass, {bool useCache: true}) {
|
||||
|
|
|
@ -191,9 +191,12 @@ class NativeCodeOracle {
|
|||
|
||||
/// Simulate the execution of a native method by adding its entry points
|
||||
/// using [entryPointsListener]. Returns result type of the native method.
|
||||
Type handleNativeProcedure(Member member,
|
||||
EntryPointsListener entryPointsListener, TypesBuilder typesBuilder) {
|
||||
Type returnType = null;
|
||||
TypeExpr handleNativeProcedure(
|
||||
Member member,
|
||||
EntryPointsListener entryPointsListener,
|
||||
TypesBuilder typesBuilder,
|
||||
RuntimeTypeTranslator translator) {
|
||||
TypeExpr returnType = null;
|
||||
bool nullable = null;
|
||||
|
||||
for (var annotation in member.annotations) {
|
||||
|
@ -214,6 +217,13 @@ class NativeCodeOracle {
|
|||
var type = pragma.type;
|
||||
if (type is InterfaceType) {
|
||||
returnType = entryPointsListener.addAllocatedClass(type.classNode);
|
||||
if (pragma.resultTypeUsesPassedTypeArguments) {
|
||||
returnType = translator.instantiateConcreteType(
|
||||
returnType,
|
||||
member.function.typeParameters
|
||||
.map((t) => TypeParameterType(t, Nullability.legacy))
|
||||
.toList());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
throw "ERROR: Invalid return type for native method: ${pragma.type}";
|
||||
|
|
|
@ -312,14 +312,12 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
Join _returnValue;
|
||||
Parameter _receiver;
|
||||
ConstantAllocationCollector constantAllocationCollector;
|
||||
RuntimeTypeTranslator _translator;
|
||||
RuntimeTypeTranslatorImpl _translator;
|
||||
StaticTypeContext _staticTypeContext;
|
||||
|
||||
// Currently only used for factory constructors.
|
||||
Map<TypeParameter, TypeExpr> _fnTypeVariables;
|
||||
|
||||
Member _privateListConstructor;
|
||||
|
||||
SummaryCollector(
|
||||
this.target,
|
||||
this._environment,
|
||||
|
@ -330,8 +328,6 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
this._genericInterfacesInfo) {
|
||||
assertx(_genericInterfacesInfo != null);
|
||||
constantAllocationCollector = new ConstantAllocationCollector(this);
|
||||
_privateListConstructor =
|
||||
_environment.coreTypes.index.getMember('dart:core', '_List', '');
|
||||
}
|
||||
|
||||
Summary createSummary(Member member,
|
||||
|
@ -361,7 +357,7 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
_summary = new Summary();
|
||||
}
|
||||
|
||||
_translator = new RuntimeTypeTranslator(
|
||||
_translator = new RuntimeTypeTranslatorImpl(
|
||||
_summary, _receiver, null, _genericInterfacesInfo);
|
||||
|
||||
if (fieldSummaryType == FieldSummaryType.kInitializer) {
|
||||
|
@ -405,7 +401,7 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
isReceiver: true);
|
||||
}
|
||||
|
||||
_translator = new RuntimeTypeTranslator(
|
||||
_translator = new RuntimeTypeTranslatorImpl(
|
||||
_summary, _receiver, _fnTypeVariables, _genericInterfacesInfo);
|
||||
|
||||
// Handle forwarding stubs. We need to check types against the types of
|
||||
|
@ -475,30 +471,17 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
}
|
||||
|
||||
if (function.body == null) {
|
||||
if (member == _privateListConstructor) {
|
||||
// We have an intrinsic summary for the _List factory to ensure the
|
||||
// correct type arguments are used.
|
||||
// TODO(39769): Find a way to generalize this to other classes.
|
||||
_returnValue.values.add(_translator.instantiateConcreteType(
|
||||
_nativeCodeOracle.handleNativeProcedure(
|
||||
member, _entryPointsListener, _typesBuilder),
|
||||
[
|
||||
TypeParameterType(
|
||||
function.typeParameters.first, Nullability.legacy)
|
||||
]));
|
||||
TypeExpr type = _nativeCodeOracle.handleNativeProcedure(
|
||||
member, _entryPointsListener, _typesBuilder, _translator);
|
||||
if (type is! ConcreteType && type is! Statement) {
|
||||
// Runtime type could be more precise than static type, so
|
||||
// calculate intersection.
|
||||
final runtimeType = _translator.translate(function.returnType);
|
||||
final typeCheck = new TypeCheck(type, runtimeType, function, type);
|
||||
_summary.add(typeCheck);
|
||||
_returnValue.values.add(typeCheck);
|
||||
} else {
|
||||
Type type = _nativeCodeOracle.handleNativeProcedure(
|
||||
member, _entryPointsListener, _typesBuilder);
|
||||
if (type is! ConcreteType) {
|
||||
// Runtime type could be more precise than static type, so
|
||||
// calculate intersection.
|
||||
final runtimeType = _translator.translate(function.returnType);
|
||||
final typeCheck = new TypeCheck(type, runtimeType, function, type);
|
||||
_summary.add(typeCheck);
|
||||
_returnValue.values.add(typeCheck);
|
||||
} else {
|
||||
_returnValue.values.add(type);
|
||||
}
|
||||
_returnValue.values.add(type);
|
||||
}
|
||||
} else {
|
||||
_visit(function.body);
|
||||
|
@ -1520,19 +1503,20 @@ class SummaryCollector extends RecursiveVisitor<TypeExpr> {
|
|||
}
|
||||
}
|
||||
|
||||
class RuntimeTypeTranslator extends DartTypeVisitor<TypeExpr> {
|
||||
class RuntimeTypeTranslatorImpl extends DartTypeVisitor<TypeExpr>
|
||||
implements RuntimeTypeTranslator {
|
||||
final Summary summary;
|
||||
final Map<TypeParameter, TypeExpr> functionTypeVariables;
|
||||
final Map<DartType, TypeExpr> typesCache = <DartType, TypeExpr>{};
|
||||
final TypeExpr receiver;
|
||||
final GenericInterfacesInfo genericInterfacesInfo;
|
||||
|
||||
RuntimeTypeTranslator(this.summary, this.receiver, this.functionTypeVariables,
|
||||
this.genericInterfacesInfo) {}
|
||||
RuntimeTypeTranslatorImpl(this.summary, this.receiver,
|
||||
this.functionTypeVariables, this.genericInterfacesInfo) {}
|
||||
|
||||
// Create a type translator which can be used only for types with no free type
|
||||
// variables.
|
||||
RuntimeTypeTranslator.forClosedTypes(this.genericInterfacesInfo)
|
||||
RuntimeTypeTranslatorImpl.forClosedTypes(this.genericInterfacesInfo)
|
||||
: summary = null,
|
||||
functionTypeVariables = null,
|
||||
receiver = null {}
|
||||
|
|
|
@ -102,6 +102,10 @@ abstract class TypesBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class RuntimeTypeTranslator {
|
||||
TypeExpr instantiateConcreteType(ConcreteType type, List<DartType> typeArgs);
|
||||
}
|
||||
|
||||
/// Abstract interface to type hierarchy information used by types.
|
||||
abstract class TypeHierarchy extends TypesBuilder
|
||||
implements GenericInterfacesInfo {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
main() {
|
||||
dynamic x = List<int>(10);
|
||||
x[0] + 10;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
library #lib;
|
||||
import self as self;
|
||||
import "dart:core" as core;
|
||||
|
||||
static method main() → dynamic {
|
||||
dynamic x = [@vm.inferred-type.metadata=dart.core::_List<dart.core::int*>] core::_List::•<core::int*>(10);
|
||||
[@vm.direct-call.metadata=dart.core::_IntegerImplementation::+??] [@vm.direct-call.metadata=dart.core::_List::[]] [@vm.inferred-type.metadata=int?] x.[](0).+(10);
|
||||
}
|
|
@ -45,6 +45,8 @@ Since those annotations side-step the normal type system, they are unsafe and we
|
|||
therefore restrict those annotations to only have an affect inside dart:
|
||||
libraries.
|
||||
|
||||
See also https://github.com/dart-lang/sdk/issues/35244.
|
||||
|
||||
### Providing an exact result type
|
||||
|
||||
```dart
|
||||
|
@ -69,6 +71,14 @@ Note that since `null` is an instance of the `Null` type, which is a subtype of
|
|||
any other, exactness of the annotated result type implies that the result must
|
||||
be non-null.
|
||||
|
||||
It is also possible to specify the type arguments of the result type if they are
|
||||
the same as the type arguments passed to the method itself. This is primarily
|
||||
useful for factory constructors:
|
||||
|
||||
```dart
|
||||
@pragma("vm:exact-result-type", [<type>, "result-type-uses-passed-type-arguments"])
|
||||
```
|
||||
|
||||
#### Examples for exact result types
|
||||
|
||||
```dart
|
||||
|
@ -92,6 +102,12 @@ class C {
|
|||
@pragma('vm:exact-result-type', "dart:core#_Smi")
|
||||
final int intValue;
|
||||
}
|
||||
|
||||
class D<T> {
|
||||
@pragma("vm:exact-result-type",
|
||||
[D, "result-type-uses-passed-type-arguments"])
|
||||
factory D(); // returns an instance of D<T>
|
||||
}
|
||||
```
|
||||
|
||||
### Declaring a result type non-nullable
|
||||
|
|
|
@ -61,6 +61,14 @@ intptr_t MethodRecognizer::ResultCidFromPragma(
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (option.IsArray()) {
|
||||
const Array& array = Array::Cast(option);
|
||||
if (array.Length() > 0) {
|
||||
const Object& type = Object::Handle(Array::Cast(option).At(0));
|
||||
if (type.IsType()) {
|
||||
return Type::Cast(type).type_class_id();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
@pragma("vm:entry-point")
|
||||
class _List<E> extends FixedLengthListBase<E> {
|
||||
@pragma("vm:exact-result-type", _List) // <- cannot use _List<T> here.
|
||||
@pragma(
|
||||
"vm:exact-result-type", [_List, "result-type-uses-passed-type-arguments"])
|
||||
@pragma("vm:prefer-inline")
|
||||
factory _List(length) native "List_allocate";
|
||||
|
||||
|
|
|
@ -106,7 +106,8 @@ class _GrowableList<T> extends ListBase<T> {
|
|||
return new _GrowableList<T>._withData(data);
|
||||
}
|
||||
|
||||
@pragma("vm:exact-result-type", _GrowableList)
|
||||
@pragma("vm:exact-result-type",
|
||||
[_GrowableList, "result-type-uses-passed-type-arguments"])
|
||||
factory _GrowableList._withData(_List data) native "GrowableList_allocate";
|
||||
|
||||
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
@pragma("vm:entry-point")
|
||||
class _List<E> extends FixedLengthListBase<E> {
|
||||
@pragma("vm:exact-result-type", _List) // <- cannot use _List<T> here.
|
||||
@pragma(
|
||||
"vm:exact-result-type", [_List, "result-type-uses-passed-type-arguments"])
|
||||
@pragma("vm:prefer-inline")
|
||||
factory _List(length) native "List_allocate";
|
||||
|
||||
|
|
|
@ -106,7 +106,8 @@ class _GrowableList<T> extends ListBase<T> {
|
|||
return new _GrowableList<T>._withData(data);
|
||||
}
|
||||
|
||||
@pragma("vm:exact-result-type", _GrowableList)
|
||||
@pragma("vm:exact-result-type",
|
||||
[_GrowableList, "result-type-uses-passed-type-arguments"])
|
||||
factory _GrowableList._withData(_List data) native "GrowableList_allocate";
|
||||
|
||||
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
||||
|
|
Loading…
Reference in a new issue