Skip to content

[Diagnostics] Fix missing any warnings for type aliases. #41481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4705,8 +4705,8 @@ ERROR(any_not_existential,none,
"'any' has no effect on %select{concrete type|type parameter}0 %1",
(bool, Type))
ERROR(existential_requires_any,none,
"protocol %0 as a type must be explicitly marked as 'any'",
(Identifier))
"%select{protocol |}1%0 as a type must be explicitly marked as 'any'",
(Type, bool))

ERROR(nonisolated_let,none,
"'nonisolated' is meaningless on 'let' declarations because "
Expand Down
50 changes: 33 additions & 17 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4148,29 +4148,29 @@ class ExistentialTypeVisitor
if (proto->existentialRequiresAny()) {
Ctx.Diags.diagnose(comp->getNameLoc(),
diag::existential_requires_any,
proto->getName())
proto->getDeclaredInterfaceType(),
/*isAlias=*/false)
.limitBehavior(DiagnosticBehavior::Warning);
}
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
type.findIf([&](Type type) -> bool {
if (T->isInvalid())
return false;
if (type->isExistentialType()) {
auto layout = type->getExistentialLayout();
for (auto *proto : layout.getProtocols()) {
auto *protoDecl = proto->getDecl();
if (!protoDecl->existentialRequiresAny())
continue;
// If this is a type alias to a constraint type, the type
// alias name must be prefixed with 'any' to be used as an
// existential type.
if (type->isConstraintType()) {
auto layout = type->getExistentialLayout();
for (auto *proto : layout.getProtocols()) {
auto *protoDecl = proto->getDecl();
if (!protoDecl->existentialRequiresAny())
continue;

Ctx.Diags.diagnose(comp->getNameLoc(),
diag::existential_requires_any,
protoDecl->getName())
.limitBehavior(DiagnosticBehavior::Warning);
}
Ctx.Diags.diagnose(comp->getNameLoc(),
diag::existential_requires_any,
alias->getDeclaredInterfaceType(),
/*isAlias=*/true)
.limitBehavior(DiagnosticBehavior::Warning);
}
return false;
});
}
}
}

Expand Down Expand Up @@ -4198,6 +4198,9 @@ void TypeChecker::checkExistentialTypes(Decl *decl) {
} else if (auto *genericDecl = dyn_cast<GenericTypeDecl>(decl)) {
checkExistentialTypes(ctx, genericDecl->getGenericParams());
checkExistentialTypes(ctx, genericDecl->getTrailingWhereClause());
if (auto *typeAlias = dyn_cast<TypeAliasDecl>(decl)) {
checkExistentialTypes(ctx, typeAlias);
}
} else if (auto *assocType = dyn_cast<AssociatedTypeDecl>(decl)) {
checkExistentialTypes(ctx, assocType->getTrailingWhereClause());
} else if (auto *extDecl = dyn_cast<ExtensionDecl>(decl)) {
Expand Down Expand Up @@ -4227,6 +4230,19 @@ void TypeChecker::checkExistentialTypes(ASTContext &ctx, Stmt *stmt) {
stmt->walk(visitor);
}

void TypeChecker::checkExistentialTypes(ASTContext &ctx,
TypeAliasDecl *typeAlias) {
if (!typeAlias || !typeAlias->getUnderlyingTypeRepr())
return;

// A type alias to a plain constraint type is allowed.
if (typeAlias->getUnderlyingType()->isConstraintType())
return;

ExistentialTypeVisitor visitor(ctx, /*checkStatements=*/true);
typeAlias->getUnderlyingTypeRepr()->walk(visitor);
}

void TypeChecker::checkExistentialTypes(
ASTContext &ctx, TrailingWhereClause *whereClause) {
if (whereClause == nullptr)
Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ void checkExistentialTypes(Decl *decl);
/// Check for invalid existential types in the given statement.
void checkExistentialTypes(ASTContext &ctx, Stmt *stmt);

/// Check for invalid existential types in the underlying type of
/// the given type alias.
void checkExistentialTypes(ASTContext &ctx, TypeAliasDecl *typeAlias);

/// Check for invalid existential types in the given generic requirement
/// list.
void checkExistentialTypes(ASTContext &ctx,
Expand Down
21 changes: 21 additions & 0 deletions test/type/explicit_existential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,24 @@ func testAnyTypeExpr() {

func hasInvalidExistential(_: any DoesNotExistIHope) {}
// expected-error@-1 {{cannot find type 'DoesNotExistIHope' in scope}}

protocol Input {
associatedtype A
}
protocol Output {
associatedtype A
}

// expected-warning@+2{{protocol 'Input' as a type must be explicitly marked as 'any'}}
// expected-warning@+1{{protocol 'Output' as a type must be explicitly marked as 'any'}}
typealias InvalidFunction = (Input) -> Output
func testInvalidFunctionAlias(fn: InvalidFunction) {}

typealias ExistentialFunction = (any Input) -> any Output
func testFunctionAlias(fn: ExistentialFunction) {}

typealias Constraint = Input
func testConstraintAlias(x: Constraint) {} // expected-warning{{'Constraint' (aka 'Input') as a type must be explicitly marked as 'any'}}

typealias Existential = any Input
func testExistentialAlias(x: Existential, y: any Constraint) {}