Skip to content

Commit 653c00c

Browse files
committed
Sema: More tuple conformance diagnostics
1 parent d5cdfb2 commit 653c00c

File tree

2 files changed

+79
-12
lines changed

2 files changed

+79
-12
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2355,6 +2355,15 @@ ERROR(experimental_tuple_extension,none,
23552355
())
23562356
ERROR(tuple_extension_wrong_type,none,
23572357
"tuple extension must be written as extension of %0", (Type))
2358+
ERROR(tuple_extension_one_conformance,none,
2359+
"tuple extension must declare conformance to exactly one protocol", ())
2360+
ERROR(tuple_extension_extra_requirement,none,
2361+
"tuple extension cannot require that %0 "
2362+
"%select{conforms to|subclasses|is the same type as|%error|%error}1 %2",
2363+
(Type, unsigned, Type))
2364+
ERROR(tuple_extension_missing_requirement,none,
2365+
"tuple extension must require that %0 conforms to %1", (Type, Type))
2366+
23582367
ERROR(extension_metatype,none,
23592368
"cannot extend a metatype %0", (Type))
23602369
ERROR(extension_placeholder,none,

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3486,26 +3486,82 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34863486
checkAccessControl(EED);
34873487
}
34883488

3489-
void visitExtensionDecl(ExtensionDecl *ED) {
3490-
// Produce any diagnostics for the extended type.
3491-
auto extType = ED->getExtendedType();
3492-
3489+
static void checkTupleExtension(ExtensionDecl *ED) {
34933490
auto *nominal = ED->getExtendedNominal();
3491+
if (!nominal || !isa<BuiltinTupleDecl>(nominal))
3492+
return;
3493+
3494+
auto &ctx = ED->getASTContext();
3495+
3496+
if (!ctx.LangOpts.hasFeature(Feature::TupleConformances)) {
3497+
ED->diagnose(diag::experimental_tuple_extension);
3498+
}
34943499

3495-
// Diagnose experimental tuple extensions.
3496-
if (nominal && isa<BuiltinTupleDecl>(nominal)) {
3497-
if (!getASTContext().LangOpts.hasFeature(Feature::TupleConformances)) {
3498-
ED->diagnose(diag::experimental_tuple_extension);
3500+
auto extType = ED->getExtendedType();
3501+
3502+
// The extended type must be '(repeat each Element)'.
3503+
if (extType->is<TupleType>()) {
3504+
auto selfType = ED->getSelfInterfaceType();
3505+
if (!extType->isEqual(selfType)) {
3506+
ED->diagnose(diag::tuple_extension_wrong_type, selfType);
34993507
}
3508+
}
3509+
3510+
// Make sure we declare conformance to exactly one protocol.
3511+
auto protocols = ED->getLocalProtocols();
3512+
if (protocols.size() != 1) {
3513+
ED->diagnose(diag::tuple_extension_one_conformance);
3514+
return;
3515+
}
3516+
3517+
auto *protocol = protocols[0];
35003518

3501-
if (extType->is<TupleType>()) {
3502-
auto selfType = ED->getSelfInterfaceType();
3503-
if (!extType->isEqual(selfType)) {
3504-
ED->diagnose(diag::tuple_extension_wrong_type, selfType);
3519+
auto genericSig = ED->getGenericSignature();
3520+
3521+
// We have a single parameter pack by construction, if we
3522+
// get this far.
3523+
auto params = genericSig.getGenericParams();
3524+
assert(params.size() == 1);
3525+
assert(params[0]->isParameterPack());
3526+
3527+
// Make sure we have a single conditional requirement,
3528+
// 'repeat each Element: P', where 'Element' is our pack
3529+
// and 'P' is the protocol above, and nothing else.
3530+
bool foundRequirement = false;
3531+
auto reqs = genericSig.getRequirements();
3532+
for (auto req : reqs) {
3533+
if (req.getKind() == RequirementKind::Conformance &&
3534+
req.getFirstType()->isEqual(params[0]) &&
3535+
req.getProtocolDecl() == protocol) {
3536+
assert(!foundRequirement);
3537+
foundRequirement = true;
3538+
} else {
3539+
if (req.getKind() == RequirementKind::Layout) {
3540+
ED->diagnose(diag::tuple_extension_extra_requirement,
3541+
req.getFirstType(),
3542+
unsigned(RequirementKind::Conformance),
3543+
ctx.getAnyObjectType());
3544+
} else {
3545+
ED->diagnose(diag::tuple_extension_extra_requirement,
3546+
req.getFirstType(),
3547+
unsigned(req.getKind()),
3548+
req.getSecondType());
35053549
}
35063550
}
35073551
}
35083552

3553+
if (!foundRequirement) {
3554+
ED->diagnose(diag::tuple_extension_missing_requirement,
3555+
params[0], protocol->getDeclaredInterfaceType());
3556+
}
3557+
}
3558+
3559+
void visitExtensionDecl(ExtensionDecl *ED) {
3560+
// Produce any diagnostics for the extended type.
3561+
auto extType = ED->getExtendedType();
3562+
3563+
auto *nominal = ED->getExtendedNominal();
3564+
35093565
if (nominal == nullptr) {
35103566
const bool wasAlreadyInvalid = ED->isInvalid();
35113567
ED->setInvalid();
@@ -3633,6 +3689,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
36333689
TypeChecker::checkDistributedActor(SF, nominal);
36343690

36353691
diagnoseIncompatibleProtocolsForMoveOnlyType(ED);
3692+
3693+
checkTupleExtension(ED);
36363694
}
36373695

36383696
void visitTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {

0 commit comments

Comments
 (0)