@@ -3486,26 +3486,82 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
3486
3486
checkAccessControl (EED);
3487
3487
}
3488
3488
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) {
3493
3490
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
+ }
3494
3499
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);
3499
3507
}
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 ];
3500
3518
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 ());
3505
3549
}
3506
3550
}
3507
3551
}
3508
3552
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
+
3509
3565
if (nominal == nullptr ) {
3510
3566
const bool wasAlreadyInvalid = ED->isInvalid ();
3511
3567
ED->setInvalid ();
@@ -3633,6 +3689,8 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
3633
3689
TypeChecker::checkDistributedActor (SF, nominal);
3634
3690
3635
3691
diagnoseIncompatibleProtocolsForMoveOnlyType (ED);
3692
+
3693
+ checkTupleExtension (ED);
3636
3694
}
3637
3695
3638
3696
void visitTopLevelCodeDecl (TopLevelCodeDecl *TLCD) {
0 commit comments