Skip to content

Commit 3af616f

Browse files
committed
[Diagnostics] When replacing an existential parameter type with a type parameter,
insert 'some' instead of an explicit type parameter. Replacing 'any' with 'some' allows the code to compile without further changes, such as naming an explicit type parameter, and is future-proofed for same-type requirements on primary associated types instead of needing additional logic to add a 'where' clause.
1 parent d1a9c63 commit 3af616f

File tree

2 files changed

+72
-97
lines changed

2 files changed

+72
-97
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3972,8 +3972,11 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
39723972

39733973
// If the base expression is a reference to a function or subscript
39743974
// parameter, offer a fixit that replaces the existential parameter type with
3975-
// its generic equivalent, e.g. func foo(p: any P) → func foo<T: P>(p: T).
3976-
// FIXME: Add an option to use 'some' vs. an explicit generic parameter.
3975+
// its generic equivalent, e.g. func foo(p: any P) → func foo(p: some P).
3976+
// Replacing 'any' with 'some' allows the code to compile without further
3977+
// changes, such as naming an explicit type parameter, and is future-proofed
3978+
// for same-type requirements on primary associated types instead of needing
3979+
// a where clause.
39773980

39783981
if (!PD || !PD->getDeclContext()->getAsDecl())
39793982
return true;
@@ -4022,70 +4025,42 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
40224025
if (PD->isInOut())
40234026
return true;
40244027

4025-
constexpr StringRef GPNamePlaceholder = "<#generic parameter name#>";
4026-
SourceRange TyReplacementRange;
4027-
SourceRange RemoveAnyRange;
4028-
SourceLoc GPDeclLoc;
4029-
std::string GPDeclStr;
4030-
{
4031-
llvm::raw_string_ostream OS(GPDeclStr);
4032-
auto *const GC = PD->getDeclContext()->getAsDecl()->getAsGenericContext();
4033-
if (GC->getParsedGenericParams()) {
4034-
GPDeclLoc = GC->getParsedGenericParams()->getRAngleLoc();
4035-
OS << ", ";
4036-
} else {
4037-
GPDeclLoc =
4038-
isa<AbstractFunctionDecl>(GC)
4039-
? cast<AbstractFunctionDecl>(GC)->getParameters()->getLParenLoc()
4040-
: cast<SubscriptDecl>(GC)->getIndices()->getLParenLoc();
4041-
OS << "<";
4042-
}
4043-
OS << GPNamePlaceholder << ": ";
4044-
4045-
auto *TR = PD->getTypeRepr()->getWithoutParens();
4046-
if (auto *STR = dyn_cast<SpecifierTypeRepr>(TR)) {
4047-
TR = STR->getBase()->getWithoutParens();
4048-
}
4049-
if (auto *ETR = dyn_cast<ExistentialTypeRepr>(TR)) {
4050-
TR = ETR->getConstraint();
4051-
RemoveAnyRange = SourceRange(ETR->getAnyLoc(), TR->getStartLoc());
4052-
TR = TR->getWithoutParens();
4053-
}
4054-
if (auto *MTR = dyn_cast<MetatypeTypeRepr>(TR)) {
4055-
TR = MTR->getBase();
4056-
4057-
// (P & Q).Type -> T.Type
4058-
// (P).Type -> (T).Type
4059-
// ((P & Q)).Type -> ((T)).Type
4060-
if (auto *TTR = dyn_cast<TupleTypeRepr>(TR)) {
4061-
assert(TTR->isParenType());
4062-
if (!isa<CompositionTypeRepr>(TTR->getElementType(0))) {
4063-
TR = TR->getWithoutParens();
4064-
}
4065-
}
4066-
}
4067-
TyReplacementRange = TR->getSourceRange();
4068-
4069-
// Strip any remaining parentheses and print the conformance constraint.
4070-
TR->getWithoutParens()->print(OS);
4071-
4072-
if (!GC->getParsedGenericParams()) {
4073-
OS << ">";
4074-
}
4028+
auto *typeRepr = PD->getTypeRepr()->getWithoutParens();
4029+
if (auto *STR = dyn_cast<SpecifierTypeRepr>(typeRepr)) {
4030+
typeRepr = STR->getBase()->getWithoutParens();
40754031
}
40764032

4077-
// First, replace the constraint type with the generic parameter type
4078-
// placeholder.
4079-
Diag.fixItReplace(TyReplacementRange, GPNamePlaceholder);
4033+
SourceRange anyRange;
4034+
TypeRepr *constraintRepr = typeRepr;
4035+
if (auto *existentialRepr = dyn_cast<ExistentialTypeRepr>(typeRepr)) {
4036+
constraintRepr = existentialRepr->getConstraint()->getWithoutParens();
4037+
auto anyStart = existentialRepr->getAnyLoc();
4038+
auto anyEnd = existentialRepr->getConstraint()->getStartLoc();
4039+
anyRange = SourceRange(anyStart, anyEnd);
4040+
}
40804041

4081-
// Remove 'any' if needed, using a character-based removal to pick up
4082-
// whitespaces between it and its constraint repr.
4083-
if (RemoveAnyRange.isValid()) {
4084-
Diag.fixItRemoveChars(RemoveAnyRange.Start, RemoveAnyRange.End);
4042+
bool needsParens = false;
4043+
while (auto *metatype = dyn_cast<MetatypeTypeRepr>(constraintRepr)) {
4044+
// The generic equivalent of 'any P.Type' is '(some P).Type'
4045+
constraintRepr = metatype->getBase()->getWithoutParens();
4046+
if (isa<SimpleIdentTypeRepr>(constraintRepr))
4047+
needsParens = !isa<TupleTypeRepr>(metatype->getBase());
40854048
}
40864049

4087-
// Finally, insert the generic parameter declaration.
4088-
Diag.fixItInsert(GPDeclLoc, GPDeclStr);
4050+
std::string fix;
4051+
llvm::raw_string_ostream OS(fix);
4052+
if (needsParens)
4053+
OS << "(";
4054+
OS << "some ";
4055+
constraintRepr->print(OS);
4056+
if (needsParens)
4057+
OS << ")";
4058+
4059+
// When removing 'any', use a character-based removal to pick up
4060+
// whitespaces between it and its constraint repr.
4061+
Diag
4062+
.fixItReplace(constraintRepr->getSourceRange(), fix)
4063+
.fixItRemoveChars(anyRange.Start, anyRange.End);
40894064

40904065
return true;
40914066
}

0 commit comments

Comments
 (0)