analyzer: Allow generic function types as type arguments in const creations

Fixes https://github.com/dart-lang/sdk/issues/55459

Change-Id: I0903f3784a4f370981926c29561904d61420ef34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/369121
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Auto-Submit: Samuel Rawlins <srawlins@google.com>
This commit is contained in:
Sam Rawlins 2024-05-31 17:12:49 +00:00 committed by Commit Queue
parent d56cef5b3b
commit 43791032fc
2 changed files with 66 additions and 24 deletions

View file

@ -549,14 +549,20 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
return true;
}
/// Verify that the given [type] does not reference any type parameters.
/// Verify that the given [type] does not reference any type parameters which
/// are declared outside [type].
///
/// A generic function type is allowed to reference its own type parameter(s).
///
/// See [CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS].
void _checkForConstWithTypeParameters(
TypeAnnotation type, ErrorCode errorCode) {
TypeAnnotation type, ErrorCode errorCode,
{Set<TypeParameterElement>? allowedTypeParameters}) {
allowedTypeParameters = {...?allowedTypeParameters};
if (type is NamedType) {
// Should not be a type parameter.
if (type.element is TypeParameterElement) {
if (type.element is TypeParameterElement &&
!allowedTypeParameters.contains(type.element)) {
_errorReporter.atNode(
type,
errorCode,
@ -566,31 +572,38 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
// Check type arguments.
var typeArguments = type.typeArguments;
if (typeArguments != null) {
for (TypeAnnotation argument in typeArguments.arguments) {
_checkForConstWithTypeParameters(argument, errorCode);
for (var argument in typeArguments.arguments) {
_checkForConstWithTypeParameters(argument, errorCode,
allowedTypeParameters: allowedTypeParameters);
}
}
} else if (type is GenericFunctionType) {
var returnType = type.returnType;
if (returnType != null) {
_checkForConstWithTypeParameters(returnType, errorCode);
}
for (var parameter in type.parameters.parameters) {
// [parameter] cannot be a [DefaultFormalParameter], a
// [FieldFormalParameter], nor a [FunctionTypedFormalParameter].
if (parameter is SimpleFormalParameter) {
var parameterType = parameter.type;
if (parameterType != null) {
_checkForConstWithTypeParameters(parameterType, errorCode);
}
}
}
var typeParameters = type.typeParameters;
if (typeParameters != null) {
allowedTypeParameters.addAll(typeParameters.typeParameters
.map((tp) => tp.declaredElement)
.nonNulls);
for (var typeParameter in typeParameters.typeParameters) {
var bound = typeParameter.bound;
if (bound != null) {
_checkForConstWithTypeParameters(bound, errorCode);
_checkForConstWithTypeParameters(bound, errorCode,
allowedTypeParameters: allowedTypeParameters);
}
}
}
var returnType = type.returnType;
if (returnType != null) {
_checkForConstWithTypeParameters(returnType, errorCode,
allowedTypeParameters: allowedTypeParameters);
}
for (var parameter in type.parameters.parameters) {
// In a generic function type, [parameter] can only be a
// [SimpleFormalParameter].
if (parameter is SimpleFormalParameter) {
var parameterType = parameter.type;
if (parameterType != null) {
_checkForConstWithTypeParameters(parameterType, errorCode,
allowedTypeParameters: allowedTypeParameters);
}
}
}

View file

@ -26,11 +26,7 @@ void g() {
const [f as void Function<T>(T, [int])];
}
''', [
// This error is reported because the cast fails if the type on the right
// has type parameters.
// TODO(srawlins): Deduplicate these two errors.
error(CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE, 38, 31),
error(CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS, 60, 1),
]);
}
@ -355,6 +351,39 @@ class A<T> {
''');
}
test_indirect_functionType_typeParameter_nestedFunctionType() async {
await assertNoErrorsInCode('''
class A<T> {
const A();
void m() {
const A<void Function<U>(void Function<V>(U, V))>();
}
}
''');
}
test_indirect_functionType_typeParameter_referencedDirectly() async {
await assertNoErrorsInCode('''
class A<T> {
const A();
void m() {
const A<U Function<U>()>();
}
}
''');
}
test_indirect_functionType_typeParameter_typeArgumentOfReturnType() async {
await assertNoErrorsInCode('''
class A<T> {
const A();
void m() {
const A<List<U> Function<U>()>();
}
}
''');
}
test_indirect_functionType_typeParameter_typeParameterBound() async {
await assertErrorsInCode('''
class A<T> {