Check type-variable bounds on generic methods

Closes #32711

Change-Id: I86c123f5a8b9eda393b276248cdd27d1b109354b
Reviewed-on: https://dart-review.googlesource.com/53201
Reviewed-by: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Johnni Winther 2018-05-03 09:36:58 +00:00
parent 794fe1e14f
commit d13bf49870
29 changed files with 484 additions and 113 deletions

View file

@ -1117,7 +1117,7 @@ class ResolutionPotentialSubtypeVisitor
* substitute for the bound of [typeVariable]. [bound] holds the bound against
* which [typeArgument] should be checked.
*/
typedef void CheckTypeVariableBound(GenericType type, DartType typeArgument,
typedef void CheckTypeVariableBound<T>(T context, DartType typeArgument,
TypeVariableType typeVariable, DartType bound);
class Types extends DartTypes {
@ -1248,14 +1248,16 @@ class Types extends DartTypes {
}
@override
void checkTypeVariableBounds(
covariant ResolutionInterfaceType type,
void checkTypeVariableBound(InterfaceType type, DartType typeArgument,
void checkTypeVariableBounds<T>(
T context,
List<DartType> typeArguments,
List<DartType> typeVariables,
void checkTypeVariableBound(T context, DartType typeArgument,
TypeVariableType typeVariable, DartType bound)) {
void f(DartType type, DartType typeArgument, TypeVariableType typeVariable,
void f(T context, DartType typeArgument, TypeVariableType typeVariable,
DartType bound) =>
checkTypeVariableBound(type, typeArgument, typeVariable, bound);
genericCheckTypeVariableBounds(type, f);
checkTypeVariableBound(context, typeArgument, typeVariable, bound);
genericCheckTypeVariableBounds(context, typeArguments, typeVariables, f);
}
/**
@ -1263,18 +1265,18 @@ class Types extends DartTypes {
* declared on [element]. Calls [checkTypeVariableBound] on each type
* argument and bound.
*/
void genericCheckTypeVariableBounds(
GenericType type, CheckTypeVariableBound checkTypeVariableBound) {
TypeDeclarationElement element = type.element;
List<ResolutionDartType> typeArguments = type.typeArguments;
List<ResolutionDartType> typeVariables = element.typeVariables;
void genericCheckTypeVariableBounds<T>(
T context,
List<DartType> typeArguments,
List<DartType> typeVariables,
CheckTypeVariableBound<T> checkTypeVariableBound) {
assert(typeVariables.length == typeArguments.length);
for (int index = 0; index < typeArguments.length; index++) {
ResolutionTypeVariableType typeVariable = typeVariables[index];
ResolutionDartType bound =
typeVariable.element.bound.substByContext(type);
typeVariable.element.bound.subst(typeArguments, typeVariables);
ResolutionDartType typeArgument = typeArguments[index];
checkTypeVariableBound(type, typeArgument, typeVariable, bound);
checkTypeVariableBound(context, typeArgument, typeVariable, bound);
}
}

View file

@ -1408,9 +1408,11 @@ abstract class DartTypes {
/// Checks the type arguments of [type] against the type variable bounds
/// declared on `type.element`. Calls [checkTypeVariableBound] on each type
/// argument and bound.
void checkTypeVariableBounds(
InterfaceType type,
void checkTypeVariableBound(InterfaceType type, DartType typeArgument,
void checkTypeVariableBounds<T>(
T context,
List<DartType> typeArguments,
List<DartType> typeVariables,
void checkTypeVariableBound(T context, DartType typeArgument,
TypeVariableType typeVariable, DartType bound));
/// Returns the [ClassEntity] which declares the type variables occurring in

View file

@ -19,6 +19,7 @@ import '../elements/types.dart';
import '../js/js.dart' as jsAst;
import '../js/js.dart' show js;
import '../js_emitter/js_emitter.dart' show Emitter;
import '../options.dart';
import '../universe/selector.dart';
import '../universe/world_builder.dart';
import '../world.dart' show ClosedWorld;
@ -139,8 +140,9 @@ abstract class RuntimeTypesNeedBuilder {
/// Computes the [RuntimeTypesNeed] for the data registered with this builder.
RuntimeTypesNeed computeRuntimeTypesNeed(
ResolutionWorldBuilder resolutionWorldBuilder, ClosedWorld closedWorld,
{bool enableTypeAssertions, bool strongMode});
ResolutionWorldBuilder resolutionWorldBuilder,
ClosedWorld closedWorld,
CompilerOptions options);
}
class TrivialRuntimeTypesNeedBuilder implements RuntimeTypesNeedBuilder {
@ -157,8 +159,9 @@ class TrivialRuntimeTypesNeedBuilder implements RuntimeTypesNeedBuilder {
@override
RuntimeTypesNeed computeRuntimeTypesNeed(
ResolutionWorldBuilder resolutionWorldBuilder, ClosedWorld closedWorld,
{bool enableTypeAssertions, bool strongMode}) {
ResolutionWorldBuilder resolutionWorldBuilder,
ClosedWorld closedWorld,
CompilerOptions options) {
return const TrivialRuntimeTypesNeed();
}
}
@ -200,8 +203,7 @@ abstract class RuntimeTypesChecksBuilder {
/// Computes the [RuntimeTypesChecks] for the data in this builder.
RuntimeTypesChecks computeRequiredChecks(
CodegenWorldBuilder codegenWorldBuilder,
{bool strongMode});
CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options);
bool get rtiChecksBuilderClosed;
}
@ -221,8 +223,7 @@ class TrivialRuntimeTypesChecksBuilder implements RuntimeTypesChecksBuilder {
@override
RuntimeTypesChecks computeRequiredChecks(
CodegenWorldBuilder codegenWorldBuilder,
{bool strongMode}) {
CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options) {
rtiChecksBuilderClosed = true;
Map<ClassEntity, ClassUse> classUseMap = <ClassEntity, ClassUse>{};
@ -235,7 +236,7 @@ class TrivialRuntimeTypesChecksBuilder implements RuntimeTypesChecksBuilder {
..typeArgument = true
..checkedTypeArgument = true
..functionType = _computeFunctionType(_elementEnvironment, cls,
strongMode: strongMode);
strongMode: options.strongMode);
classUseMap[cls] = classUse;
}
TypeChecks typeChecks = _substitutions._requiredChecks =
@ -1387,8 +1388,9 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
@override
RuntimeTypesNeed computeRuntimeTypesNeed(
ResolutionWorldBuilder resolutionWorldBuilder, ClosedWorld closedWorld,
{bool enableTypeAssertions, bool strongMode}) {
ResolutionWorldBuilder resolutionWorldBuilder,
ClosedWorld closedWorld,
CompilerOptions options) {
TypeVariableTests typeVariableTests = new TypeVariableTests(
closedWorld.elementEnvironment,
closedWorld.commonElements,
@ -1437,7 +1439,7 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
});
}
Set<Local> localFunctions = strongMode
Set<Local> localFunctions = options.strongMode
? resolutionWorldBuilder.localFunctions.toSet()
: resolutionWorldBuilder.localFunctionsWithFreeTypeVariables.toSet();
Set<FunctionEntity> closurizedMembers =
@ -1459,7 +1461,7 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
Set<Local> localFunctionsToRemove;
Set<FunctionEntity> closurizedMembersToRemove;
if (strongMode) {
if (options.strongMode) {
for (Local function in localFunctions) {
FunctionType functionType =
_elementEnvironment.getLocalFunctionType(function);
@ -1535,7 +1537,7 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
processChecks(typeVariableTests.explicitIsChecks);
processChecks(typeVariableTests.implicitIsChecks);
if (enableTypeAssertions) {
if (options.enableTypeAssertions) {
checkClosures();
}
@ -1555,6 +1557,29 @@ class RuntimeTypesNeedBuilderImpl extends _RuntimeTypesBase
}
}
if (options.parameterCheckPolicy.isEmitted) {
void checkFunction(Entity function, FunctionType type) {
for (FunctionTypeVariable typeVariable in type.typeVariables) {
DartType bound = typeVariable.bound;
if (!bound.isDynamic &&
!bound.isVoid &&
bound != closedWorld.commonElements.objectType) {
potentiallyNeedTypeArguments(function);
break;
}
}
}
for (FunctionEntity method in resolutionWorldBuilder.genericMethods) {
checkFunction(method, _elementEnvironment.getFunctionType(method));
}
for (Local function in resolutionWorldBuilder.genericLocalFunctions) {
checkFunction(
function, _elementEnvironment.getLocalFunctionType(function));
}
}
Set<Selector> selectorsNeedingTypeArguments = new Set<Selector>();
typeVariableTests
.forEachAppliedSelector((Selector selector, Set<Entity> targets) {
@ -1692,8 +1717,7 @@ class RuntimeTypesImpl extends _RuntimeTypesBase
}
RuntimeTypesChecks computeRequiredChecks(
CodegenWorldBuilder codegenWorldBuilder,
{bool strongMode}) {
CodegenWorldBuilder codegenWorldBuilder, CompilerOptions options) {
TypeVariableTests typeVariableTests = new TypeVariableTests(
_elementEnvironment, _commonElements, _types, codegenWorldBuilder,
forRtiNeeds: false);
@ -1786,7 +1810,7 @@ class RuntimeTypesImpl extends _RuntimeTypesBase
void processClass(ClassEntity cls) {
ClassFunctionType functionType = _computeFunctionType(
_elementEnvironment, cls,
strongMode: strongMode);
strongMode: options.strongMode);
if (functionType != null) {
ClassUse classUse =
classUseMap.putIfAbsent(cls, () => new ClassUse());
@ -1812,6 +1836,20 @@ class RuntimeTypesImpl extends _RuntimeTypesBase
liveClasses.forEach(processSuperClasses);
}
if (options.parameterCheckPolicy.isEmitted) {
for (FunctionEntity method in codegenWorldBuilder.genericMethods) {
if (_rtiNeed.methodNeedsTypeArguments(method)) {
for (TypeVariableType typeVariable
in _elementEnvironment.getFunctionTypeVariables(method)) {
DartType bound =
_elementEnvironment.getTypeVariableBound(typeVariable.element);
processCheckedType(bound);
liveTypeVisitor.visit(bound, true);
}
}
}
}
cachedRequiredChecks = _computeChecks(classUseMap);
rtiChecksBuilderClosed = true;
return new _RuntimeTypesChecks(this, cachedRequiredChecks);

View file

@ -13,6 +13,7 @@ import '../js/js.dart' show js;
import '../js_backend/namer.dart' show Namer;
import '../js_backend/native_data.dart';
import '../js_backend/interceptor_data.dart';
import '../js_backend/runtime_types.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../universe/selector.dart' show Selector;
import '../universe/world_builder.dart'
@ -28,6 +29,7 @@ class ParameterStubGenerator {
final CodeEmitterTask _emitterTask;
final Namer _namer;
final RuntimeTypesEncoder _rtiEncoder;
final NativeData _nativeData;
final InterceptorData _interceptorData;
final CodegenWorldBuilder _codegenWorldBuilder;
@ -37,6 +39,7 @@ class ParameterStubGenerator {
ParameterStubGenerator(
this._emitterTask,
this._namer,
this._rtiEncoder,
this._nativeData,
this._interceptorData,
this._codegenWorldBuilder,
@ -164,9 +167,11 @@ class ParameterStubGenerator {
for (TypeVariableType typeVariable
in _closedWorld.elementEnvironment.getFunctionTypeVariables(member)) {
if (selector.typeArgumentCount == 0) {
// TODO(32741): Insert the type variable bound instead of `null`.
targetArguments[count++] =
_emitter.constantReference(new NullConstantValue());
targetArguments[count++] = _rtiEncoder.getTypeRepresentation(
_emitter,
_closedWorld.elementEnvironment
.getTypeVariableBound(typeVariable.element),
(_) => _emitter.constantReference(new NullConstantValue()));
} else {
String jsName = '\$${typeVariable.element.name}';
stubParameters[parameterIndex++] = new jsAst.Parameter(jsName);

View file

@ -985,6 +985,7 @@ class ProgramBuilder {
ParameterStubGenerator generator = new ParameterStubGenerator(
_task,
_namer,
_rtiEncoder,
_nativeData,
_interceptorData,
_worldBuilder,

View file

@ -113,7 +113,7 @@ class TypeTestRegistry {
}
void computeRequiredTypeChecks(RuntimeTypesChecksBuilder rtiChecksBuilder) {
_rtiChecks = rtiChecksBuilder.computeRequiredChecks(_codegenWorldBuilder,
strongMode: _options.strongMode);
_rtiChecks =
rtiChecksBuilder.computeRequiredChecks(_codegenWorldBuilder, _options);
}
}

View file

@ -2113,9 +2113,7 @@ class KernelClosedWorld extends ClosedWorldBase
typesImplementedBySubclasses,
classHierarchyNodes,
classSets) {
computeRtiNeed(resolutionWorldBuilder, rtiNeedBuilder,
enableTypeAssertions: options.enableTypeAssertions,
strongMode: options.strongMode);
computeRtiNeed(resolutionWorldBuilder, rtiNeedBuilder, options);
}
@override

View file

@ -69,23 +69,20 @@ class _KernelDartTypes extends DartTypes {
}
@override
void checkTypeVariableBounds(
InterfaceType instantiatedType,
void checkTypeVariableBound(InterfaceType type, DartType typeArgument,
void checkTypeVariableBounds<T>(
T context,
List<DartType> typeArguments,
List<DartType> typeVariables,
void checkTypeVariableBound(T context, DartType typeArgument,
TypeVariableType typeVariable, DartType bound)) {
InterfaceType declaredType = getThisType(instantiatedType.element);
List<DartType> typeArguments = instantiatedType.typeArguments;
List<DartType> typeVariables = declaredType.typeArguments;
assert(typeVariables.length == typeArguments.length);
for (int index = 0; index < typeArguments.length; index++) {
DartType typeArgument = typeArguments[index];
TypeVariableType typeVariable = typeVariables[index];
DartType bound = substByContext(
elementMap.elementEnvironment
.getTypeVariableBound(typeVariable.element),
instantiatedType);
checkTypeVariableBound(
instantiatedType, typeArgument, typeVariable, bound);
DartType bound = elementMap.elementEnvironment
.getTypeVariableBound(typeVariable.element)
.subst(typeArguments, typeVariables);
checkTypeVariableBound(context, typeArgument, typeVariable, bound);
}
}

View file

@ -461,6 +461,9 @@ class CheckPolicy {
static const trusted = const CheckPolicy(isTrusted: true);
static const checked = const CheckPolicy(isEmitted: true);
static const ignored = const CheckPolicy(isIgnored: true);
String toString() => 'CheckPolicy(isTrusted=$isTrusted,'
'isEmitted=$isEmitted,isIgnored=$isIgnored)';
}
String _extractStringOption(

View file

@ -415,7 +415,8 @@ class TypeResolver {
}
}
types.genericCheckTypeVariableBounds(type, checkTypeVariableBound);
types.genericCheckTypeVariableBounds(type, type.typeArguments,
type.element.typeVariables, checkTypeVariableBound);
}
/**

View file

@ -1566,19 +1566,29 @@ class SsaAstGraphBuilder extends ast.Visitor
}
}
void assertIsSubtype(ast.Node node, ResolutionDartType subtype,
ResolutionDartType supertype, String message) {
void assertIsSubtype(
ast.Node node,
ResolutionDartType subtype,
ResolutionDartType supertype,
String prefix,
String infix,
String suffix) {
HInstruction subtypeInstruction = typeBuilder.analyzeTypeArgument(
localsHandler.substInContext(subtype), sourceElement);
HInstruction supertypeInstruction = typeBuilder.analyzeTypeArgument(
localsHandler.substInContext(supertype), sourceElement);
HInstruction messageInstruction =
graph.addConstantString(message, closedWorld);
HInstruction prefixInstruction =
graph.addConstantString(prefix, closedWorld);
HInstruction infixInstruction = graph.addConstantString(infix, closedWorld);
HInstruction suffixInstruction =
graph.addConstantString(suffix, closedWorld);
MethodElement element = commonElements.assertIsSubtype;
var inputs = <HInstruction>[
subtypeInstruction,
supertypeInstruction,
messageInstruction
prefixInstruction,
infixInstruction,
suffixInstruction,
];
HInstruction assertIsSubtype = new HInvokeStatic(element, inputs,
subtypeInstruction.instructionType, const <DartType>[]);
@ -3691,15 +3701,21 @@ class SsaAstGraphBuilder extends ast.Visitor
int subtypeRelation = types.computeSubtypeRelation(typeArgument, bound);
if (subtypeRelation == DartTypes.IS_SUBTYPE) return;
String message = "Can't create an instance of malbounded type '$type': "
"'${typeArgument}' is not a subtype of bound '${bound}' for "
"type variable '${typeVariable}' of type "
"${type == instance
? "'${type.element.thisType}'"
: "'${instance.element.thisType}' on the supertype "
"'${instance}' of '${type}'"
}.";
String prefix = "Can't create an instance of malbounded type '$type': '";
String infix = "' is not a subtype of bound '";
String suffix;
if (type == instance) {
suffix = "' of type variable '${typeVariable}' of type "
"'${type.element.thisType}'.";
} else {
suffix = "' of type variable '${typeVariable}' of type "
"'${instance.element.thisType}' on the supertype "
"'${instance}' of '${type}'.";
}
if (subtypeRelation == DartTypes.NOT_SUBTYPE) {
String message = "TypeError: $prefix$typeArgument$infix$bound$suffix";
generateTypeError(node, message);
definitelyFails = true;
return;
@ -3708,18 +3724,20 @@ class SsaAstGraphBuilder extends ast.Visitor
typeArgument, () => new Set<ResolutionDartType>());
if (!seenChecks.contains(bound)) {
seenChecks.add(bound);
assertIsSubtype(node, typeArgument, bound, message);
assertIsSubtype(node, typeArgument, bound, prefix, infix, suffix);
}
}
}
types.checkTypeVariableBounds(type, addTypeVariableBoundCheck);
types.checkTypeVariableBounds(type, type.typeArguments,
type.element.typeVariables, addTypeVariableBoundCheck);
if (definitelyFails) {
return true;
}
for (ResolutionInterfaceType supertype in type.element.allSupertypes) {
ResolutionInterfaceType instance = type.asInstanceOf(supertype.element);
types.checkTypeVariableBounds(instance, addTypeVariableBoundCheck);
types.checkTypeVariableBounds(instance, instance.typeArguments,
instance.element.typeVariables, addTypeVariableBoundCheck);
if (definitelyFails) {
return true;
}

View file

@ -991,6 +991,30 @@ class KernelSsaGraphBuilder extends ir.Visitor
function.positionalParameters.forEach(_handleParameter);
function.namedParameters.toList()..forEach(_handleParameter);
checkTypeVariableBounds(targetElement);
}
void checkTypeVariableBounds(FunctionEntity method) {
if (rtiNeed.methodNeedsTypeArguments(method)) {
ir.FunctionNode function = getFunctionNode(_elementMap, method);
for (ir.TypeParameter typeParameter in function.typeParameters) {
Local local = localsMap.getLocalTypeVariable(
new ir.TypeParameterType(typeParameter), _elementMap);
HInstruction newParameter = localsHandler.directLocals[local];
DartType bound = _getDartTypeIfValid(typeParameter.bound);
if (!bound.isDynamic &&
!bound.isVoid &&
bound != _commonElements.objectType) {
_assertIsType(
newParameter,
bound,
"The type argument '",
"' is not a subtype of the type variable bound '",
"' of type variable '${local.name}' in '${method.name}'.");
}
}
}
}
/// Builds a SSA graph for FunctionNodes of external methods.
@ -4250,15 +4274,21 @@ class KernelSsaGraphBuilder extends ir.Visitor
int subtypeRelation = types.computeSubtypeRelation(typeArgument, bound);
if (subtypeRelation == DartTypes.IS_SUBTYPE) return;
String message = "Can't create an instance of malbounded type '$type': "
"'${typeArgument}' is not a subtype of bound '${bound}' for "
"type variable '${typeVariable}' of type "
"${type == instance
? "'${types.getThisType(type.element)}'"
: "'${types.getThisType(instance.element)}' on the supertype "
"'${instance}' of '${type}'"
}.";
String prefix = "Can't create an instance of malbounded type '$type': '";
String infix = "' is not a subtype of bound '";
String suffix;
if (type == instance) {
suffix = "' type variable '${typeVariable}' of type "
"'${types.getThisType(type.element)}'.";
} else {
suffix = "' type variable '${typeVariable}' of type "
"'${types.getThisType(instance.element)}' on the supertype "
"'${instance}' of '${type}'.";
}
if (subtypeRelation == DartTypes.NOT_SUBTYPE) {
String message = "TypeError: $prefix$typeArgument$infix$bound$suffix";
generateTypeError(message, sourceInformation);
knownInvalidBounds = true;
return;
@ -4267,19 +4297,29 @@ class KernelSsaGraphBuilder extends ir.Visitor
seenChecksMap.putIfAbsent(typeArgument, () => new Set<DartType>());
if (!seenChecks.contains(bound)) {
seenChecks.add(bound);
_assertIsSubtype(typeArgument, bound, message);
_assertIsSubtype(typeArgument, bound, prefix, infix, suffix);
}
}
}
types.checkTypeVariableBounds(type, _addTypeVariableBoundCheck);
types.checkTypeVariableBounds(
type,
type.typeArguments,
_elementMap.elementEnvironment.getThisType(type.element).typeArguments,
_addTypeVariableBoundCheck);
if (knownInvalidBounds) {
return true;
}
for (InterfaceType supertype
in types.getSupertypes(constructor.enclosingClass)) {
InterfaceType instance = types.asInstanceOf(type, supertype.element);
types.checkTypeVariableBounds(instance, _addTypeVariableBoundCheck);
types.checkTypeVariableBounds(
instance,
instance.typeArguments,
_elementMap.elementEnvironment
.getThisType(instance.element)
.typeArguments,
_addTypeVariableBoundCheck);
if (knownInvalidBounds) {
return true;
}
@ -4287,22 +4327,33 @@ class KernelSsaGraphBuilder extends ir.Visitor
return false;
}
void _assertIsSubtype(DartType subtype, DartType supertype, String message) {
void _assertIsSubtype(DartType subtype, DartType supertype, String prefix,
String infix, String suffix) {
HInstruction subtypeInstruction = typeBuilder.analyzeTypeArgument(
localsHandler.substInContext(subtype), sourceElement);
_assertIsType(subtypeInstruction, supertype, prefix, infix, suffix);
registry?.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
}
void _assertIsType(HInstruction subtypeInstruction, DartType supertype,
String prefix, String infix, String suffix) {
HInstruction supertypeInstruction = typeBuilder.analyzeTypeArgument(
localsHandler.substInContext(supertype), sourceElement);
HInstruction messageInstruction =
graph.addConstantString(message, closedWorld);
HInstruction prefixInstruction =
graph.addConstantString(prefix, closedWorld);
HInstruction infixInstruction = graph.addConstantString(infix, closedWorld);
HInstruction suffixInstruction =
graph.addConstantString(suffix, closedWorld);
FunctionEntity element = commonElements.assertIsSubtype;
var inputs = <HInstruction>[
subtypeInstruction,
supertypeInstruction,
messageInstruction
prefixInstruction,
infixInstruction,
suffixInstruction
];
HInstruction assertIsSubtype = new HInvokeStatic(element, inputs,
subtypeInstruction.instructionType, const <DartType>[]);
registry?.registerTypeVariableBoundsSubtypeCheck(subtype, supertype);
add(assertIsSubtype);
}
@ -5606,9 +5657,11 @@ class KernelTypeBuilder extends TypeBuilder {
GlobalLocalsMap _globalLocalsMap;
KernelTypeBuilder(
GraphBuilder builder, this._elementMap, this._globalLocalsMap)
KernelSsaGraphBuilder builder, this._elementMap, this._globalLocalsMap)
: super(builder);
KernelSsaGraphBuilder get builder => super.builder;
ClassTypeVariableAccess computeTypeVariableAccess(MemberEntity member) {
return _elementMap.getClassTypeVariableAccessForMember(member);
}
@ -5625,6 +5678,7 @@ class KernelTypeBuilder extends TypeBuilder {
potentiallyCheckOrTrustTypeOfParameter(
argument, localsMap.getLocalType(_elementMap, parameter));
});
builder.checkTypeVariableBounds(function);
}
}

View file

@ -104,6 +104,11 @@ class KernelImpactBuilder extends ir.Visitor {
registerSeenClasses(node.returnType);
node.positionalParameters.forEach(handleParameter);
node.namedParameters.forEach(handleParameter);
if (_options.strongMode) {
for (ir.TypeParameter parameter in node.typeParameters) {
checkType(parameter.bound, TypeUseKind.PARAMETER_CHECK);
}
}
}
ResolutionImpact buildField(ir.Field field) {

View file

@ -618,6 +618,23 @@ abstract class CodegenWorldBuilderImpl extends WorldBuilderBase
_instanceMemberUsage.forEach(processMemberUse);
return functions;
}
@override
Iterable<FunctionEntity> get genericMethods {
List<FunctionEntity> functions = <FunctionEntity>[];
void processMemberUse(Entity member, AbstractUsage memberUsage) {
if (member is FunctionEntity &&
memberUsage.hasUse &&
_elementEnvironment.getFunctionTypeVariables(member).isNotEmpty) {
functions.add(member);
}
}
_instanceMemberUsage.forEach(processMemberUse);
_staticMemberUsage.forEach(processMemberUse);
return functions;
}
}
class ElementCodegenWorldBuilderImpl extends CodegenWorldBuilderImpl {

View file

@ -975,6 +975,22 @@ abstract class ResolutionWorldBuilderBase extends WorldBuilderBase
return classHierarchyBuilder.isInheritedInSubtypeOf(
member.enclosingClass, type);
}
@override
Iterable<FunctionEntity> get genericMethods {
List<FunctionEntity> functions = <FunctionEntity>[];
void processMemberUse(Entity member, AbstractUsage memberUsage) {
if (member is FunctionEntity &&
memberUsage.hasUse &&
_elementEnvironment.getFunctionTypeVariables(member).isNotEmpty) {
functions.add(member);
}
}
_memberUsage.forEach(processMemberUse);
return functions;
}
}
abstract class KernelResolutionWorldBuilderBase

View file

@ -293,6 +293,9 @@ abstract class WorldBuilder {
/// Live generic local functions.
Iterable<Local> get genericLocalFunctions;
/// Live generic methods.
Iterable<FunctionEntity> get genericMethods;
/// Live user-defined 'noSuchMethod' implementations.
Iterable<FunctionEntity> get userNoSuchMethods;

View file

@ -1338,9 +1338,7 @@ class ClosedWorldImpl extends ClosedWorldBase with ClosedWorldRtiNeedMixin {
typesImplementedBySubclasses,
classHierarchyNodes,
classSets) {
computeRtiNeed(resolutionWorldBuilder, rtiNeedBuilder,
enableTypeAssertions: options.enableTypeAssertions,
strongMode: options.strongMode);
computeRtiNeed(resolutionWorldBuilder, rtiNeedBuilder, options);
}
bool checkClass(ClassElement cls) => cls.isDeclaration;
@ -1470,11 +1468,9 @@ abstract class ClosedWorldRtiNeedMixin implements ClosedWorld {
RuntimeTypesNeed _rtiNeed;
void computeRtiNeed(ResolutionWorldBuilder resolutionWorldBuilder,
RuntimeTypesNeedBuilder rtiNeedBuilder,
{bool enableTypeAssertions, bool strongMode}) {
RuntimeTypesNeedBuilder rtiNeedBuilder, CompilerOptions options) {
_rtiNeed = rtiNeedBuilder.computeRuntimeTypesNeed(
resolutionWorldBuilder, this,
enableTypeAssertions: enableTypeAssertions, strongMode: strongMode);
resolutionWorldBuilder, this, options);
}
RuntimeTypesNeed get rtiNeed => _rtiNeed;

View file

@ -630,11 +630,16 @@ Object assertSubtype(
}
/// Checks that the type represented by [subtype] is a subtype of [supertype].
/// If not a type error with [message] is thrown.
/// If not a type error is thrown using [prefix], [infix], [suffix] and the
/// runtime types [subtype] and [supertype] to generate the error message.
///
/// Called from generated code.
assertIsSubtype(var subtype, var supertype, String message) {
assertIsSubtype(
var subtype, var supertype, String prefix, String infix, String suffix) {
if (!isSubtype(subtype, supertype)) {
String message = "TypeError: "
"$prefix${runtimeTypeToString(subtype)}$infix"
"${runtimeTypeToString(supertype)}$suffix";
throwTypeError(message);
}
}

View file

@ -113,6 +113,8 @@ main(args) {
new Class2().method5<int>(0);
new Class3().method6<int>(0);
dynamic c3 = args != null ? new Class3() : new Class2();
// TODO(johnniwinther): Expected bounds should be `dynamic` when CFE supports
// instantiate-to-bound.
c3.method6(0); // Missing type arguments.
try {
dynamic c2 = args == null ? new Class3() : new Class2();
@ -160,8 +162,8 @@ Class3.method6:
"foo" is int = false
Class3.method6:
0 is dynamic = true
"foo" is dynamic = true
0 is Object = true
"foo" is Object = true
Class2.method6:
0 is int = true

View file

@ -123,8 +123,8 @@ Future<Compiler> runWithD8(
print(out);
if (expectedOutput != null) {
Expect.equals(0, runResult.exitCode);
Expect.stringEquals(
expectedOutput, runResult.stdout.replaceAll('\r\n', '\n'));
Expect.stringEquals(expectedOutput.trim(),
runResult.stdout.replaceAll('\r\n', '\n').trim());
}
return result.compiler;
}

View file

@ -0,0 +1,89 @@
// Copyright (c) 2018, 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.
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/commandline_options.dart';
import '../kernel/compiler_helper.dart';
const String SOURCE1 = r'''
import 'package:expect/expect.dart';
class A {}
class B {}
method1<T extends A>() {}
class C {
method2<T, S extends T>() {}
}
main() {
bool printError(e) {
print(e); return true;
}
method3<T extends B>() {}
dynamic m1 = method1;
dynamic m2 = new C().method2;
dynamic m3 = method3;
m1<A>();
Expect.throws(() => m1<B>(), printError);
m2<A, A>();
Expect.throws(() => m2<B, A>(), printError);
m3<B>();
Expect.throws(() => m3<A>(), printError);
}
''';
const String OUTPUT1 = r'''
TypeError: The type argument 'B' is not a subtype of the type variable bound 'A' of type variable 'T' in 'method1'.
TypeError: The type argument 'A' is not a subtype of the type variable bound 'B' of type variable 'S' in 'method2'.
TypeError: The type argument 'A' is not a subtype of the type variable bound 'B' of type variable 'T' in 'call'.
''';
const String SOURCE2 = r'''
import 'package:expect/expect.dart';
class A {}
class B {}
class C<T extends A>{
}
class D<T> extends C<T> {}
main() {
bool printError(e) {
print(e); return true;
}
new C<A>();
Expect.throws(() => new C<B>(), printError);
new D<A>();
Expect.throws(() => new D<B>(), printError);
}
''';
const String OUTPUT2 = r'''
TypeError: Can't create an instance of malbounded type 'C<B>': 'B' is not a subtype of bound 'A' type variable 'C.T' of type 'C<C.T>'.
TypeError: Can't create an instance of malbounded type 'D<B>': 'B' is not a subtype of bound 'A' type variable 'C.T' of type 'C<C.T>' on the supertype 'C<B>' of 'D<B>'.
''';
main(List<String> args) {
asyncTest(() async {
await runWithD8(memorySourceFiles: {
'main.dart': SOURCE1
}, options: [
Flags.strongMode,
], expectedOutput: OUTPUT1, printJs: args.contains('-v'));
await runWithD8(
memorySourceFiles: {'main.dart': SOURCE2},
options: [Flags.enableCheckedMode],
expectedOutput: OUTPUT2,
printJs: args.contains('-v'));
});
}

View file

@ -2,7 +2,6 @@
// 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.
/*!strong.class: A:*/
/*strong.class: A:explicit=[A]*/
class A {
/*element: A.instanceMethod:deps=[B.instanceMethod]*/
@ -10,6 +9,7 @@ class A {
}
class B {
/*element: B.instanceMethod:*/
instanceMethod<T>(A a, t) => a.instanceMethod<T>(t);
}

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
class A {
/*element: A.instanceMethod:*/
instanceMethod<T>(t) => t;
}

View file

@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
class A {
/*element: A.instanceMethod:*/
instanceMethod<T>(t) => t;
}

View file

@ -0,0 +1,78 @@
// Copyright (c) 2018, 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.
import 'package:expect/expect.dart';
/*strong.class: Class1a:explicit=[Class1a]*/
class Class1a {}
class Class1b extends Class1a {}
/*strong.class: Class2a:explicit=[Class2a<num>],needsArgs*/
class Class2a<T> {}
class Class2b<T> extends Class2a<T> {}
/*strong.element: method1:needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method1<T extends Class1a>() => null;
/*strong.element: method2:needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method2<T extends Class2a<num>>() => null;
method3<T>() => null;
class Class3 {
/*strong.element: Class3.method4:needsArgs,selectors=[Selector(call, method4, arity=0, types=1)]*/
method4<T extends Class1a>() => null;
/*strong.element: Class3.method5:needsArgs,selectors=[Selector(call, method5, arity=0, types=1)]*/
method5<T extends Class2a<num>>() => null;
method6<T>() => null;
}
main() {
/*strong.needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method7<T extends Class1a>() => null;
/*strong.needsArgs,selectors=[Selector(call, call, arity=0, types=1)]*/
method8<T extends Class2a<num>>() => null;
/**/
method9<T>() => null;
dynamic f1 = method1;
dynamic f2 = method2;
dynamic f3 = method3;
dynamic c3 = new Class3();
dynamic f7 = method7;
dynamic f8 = method8;
dynamic f9 = method9;
f1<Class1b>();
f2<Class2b<int>>();
f3<int>();
c3.method4<Class1b>();
c3.method5<Class2b<int>>();
c3.method6<int>();
f7<Class1b>();
f8<Class2b<int>>();
f9<int>();
if (typeAssertionsEnabled) {
Expect.throws(/*needsSignature*/ () => f1<Class2a<num>>());
Expect.throws(/*needsSignature*/ () => f2<Class2b<String>>());
Expect.throws(/*needsSignature*/ () => c3.method4<Class2a<num>>());
Expect.throws(/*needsSignature*/ () => c3.method5<Class2b<String>>());
Expect.throws(/*needsSignature*/ () => f7<Class2a<num>>());
Expect.throws(/*needsSignature*/ () => f8<Class2b<String>>());
} else {
f1<Class2a<num>>();
f2<Class2b<String>>();
c3.method4<Class2a<num>>();
c3.method5<Class2b<String>>();
f7<Class2a<num>>();
f8<Class2b<String>>();
}
}

View file

@ -0,0 +1,46 @@
// Copyright (c) 2018, 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.
// dart2jsOptions=--strong
import 'package:expect/expect.dart';
class A {}
class B extends A {}
class C {}
class D<T> {}
class E<T> extends D<T> {}
class F<T> {}
void f1<T extends A>() {
print('f1<$T>');
}
void f2<S, T extends D<S>>() {
print('f2<$S,$T>');
}
main() {
dynamic f = f1;
f<A>();
f<B>();
Expect.throws(() => f<C>(), (e) {
print(e);
return true;
});
f();
f = f2;
f<A, D<A>>();
f<A, E<A>>();
Expect.throws(() => f<A, F<A>>(), (e) {
print(e);
return true;
});
f();
}

View file

@ -16,6 +16,7 @@ bigint_from_test: RuntimeError # Issue 32589
date_time11_test: RuntimeError, Pass # Fails when US is on winter time, issue 31285.
iterable_where_type_test: RuntimeError # issue 31718
list_unmodifiable_test: Pass, RuntimeError # Issue 28712
apply_generic_function_test: RuntimeError # Issue 32691
[ $compiler != dartdevc ]
error_stack_trace_test/static: MissingCompileTimeError

View file

@ -1,7 +1,6 @@
// Copyright (c) 2018, 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.
// dart2jsOptions=-Ddart.isdart2js=true
import "package:expect/expect.dart";
@ -22,10 +21,11 @@ check(expected, actual) {
print('a: $expected');
print('b: $actual');
if (((actual[0] == Object && expected[0] == dynamic) ||
(actual[0] == dynamic && expected[0] == Object)) &&
!const bool.fromEnvironment('dart.isdart2js')) {
(actual[0] == dynamic && expected[0] == Object))) {
// TODO(32483): dartdevk sometimes defaults type to 'Object' when 'dynamic'
// is required. Remove this hack when fixed.
// TODO(31581): dart2js needs instantiate-to-bound to generic 'dynamic'
// instead of 'Object'.
actual = actual.toList()..[0] = expected[0];
print('b*: $actual');
}

View file

@ -278,9 +278,7 @@ constructor_redirect1_negative_test/none: MissingCompileTimeError
constructor_redirect2_negative_test: Crash # Stack Overflow
constructor_redirect2_test/01: MissingCompileTimeError
constructor_redirect_test/01: Crash # Assertion failure: Cannot find value Instance of 'ThisLocal' in (local(A.named2#x), local(A.named2#y), local(A.named2#z)) for j:constructor(A.named2).
covariance_type_parameter_test/01: Crash # NoSuchMethodError: The method 'hasSubclass' was called on null.
covariance_type_parameter_test/02: Crash # NoSuchMethodError: The method 'hasSubclass' was called on null.
covariance_type_parameter_test/03: Crash # NoSuchMethodError: The method 'hasSubclass' was called on null.
covariant_override/runtime_check_test: RuntimeError
covariant_subtyping_test: RuntimeError
cyclic_constructor_test/01: Crash # Stack Overflow
@ -633,9 +631,7 @@ constructor_named_arguments_test/none: RuntimeError
constructor_redirect1_negative_test/01: Crash # Stack Overflow
constructor_redirect2_negative_test: Crash # Stack Overflow
constructor_redirect_test/01: Crash # Assertion failure: Cannot find value Instance of 'ThisLocal' in (local(A.named2#x), local(A.named2#y), local(A.named2#z)) for j:constructor(A.named2).
covariance_type_parameter_test/01: RuntimeError
covariance_type_parameter_test/02: RuntimeError
covariance_type_parameter_test/03: RuntimeError
covariant_override/tear_off_type_test: RuntimeError
covariant_subtyping_test: Crash # Unsupported operation: Unsupported type parameter type node E.
cyclic_constructor_test/01: Crash # Stack Overflow
@ -1053,9 +1049,7 @@ constructor_named_arguments_test/none: RuntimeError
constructor_redirect1_negative_test/01: Crash # Stack Overflow
constructor_redirect2_negative_test: Crash # Issue 30856
constructor_redirect_test/01: Crash # Assertion failure: Cannot find value Instance of 'ThisLocal' in (local(A.named2#x), local(A.named2#y), local(A.named2#z)) for j:constructor(A.named2).
covariance_type_parameter_test/01: RuntimeError
covariance_type_parameter_test/02: RuntimeError
covariance_type_parameter_test/03: RuntimeError
covariant_override/tear_off_type_test: RuntimeError
covariant_subtyping_test: RuntimeError
cyclic_constructor_test/01: Crash # Issue 30856
@ -1640,9 +1634,7 @@ constructor_named_arguments_test/none: RuntimeError
constructor_redirect1_negative_test/01: Crash # Stack Overflow
constructor_redirect2_negative_test: Crash # Issue 30856
constructor_redirect_test/01: Crash # Assertion failure: Cannot find value Instance of 'ThisLocal' in (local(A.named2#x), local(A.named2#y), local(A.named2#z)) for j:constructor(A.named2).
covariance_type_parameter_test/01: RuntimeError
covariance_type_parameter_test/02: RuntimeError
covariance_type_parameter_test/03: RuntimeError
covariant_override/tear_off_type_test: RuntimeError
covariant_subtyping_test: RuntimeError
cyclic_constructor_test/01: Crash # Issue 30856