Skip to content

Commit b2d3184

Browse files
committed
Sema: Requirement inference from generic typealias in tuple extension
1 parent e773f68 commit b2d3184

File tree

5 files changed

+77
-19
lines changed

5 files changed

+77
-19
lines changed

lib/AST/Decl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7865,7 +7865,13 @@ TupleType *BuiltinTupleDecl::getTupleSelfType(const ExtensionDecl *owner) const
78657865
auto &ctx = getASTContext();
78667866

78677867
// Get the generic parameter type 'each T'.
7868-
auto *genericParams = owner->getGenericParams();
7868+
GenericParamList *genericParams;
7869+
if (owner != nullptr) {
7870+
genericParams = owner->getGenericParams();
7871+
} else {
7872+
genericParams = getGenericParams();
7873+
}
7874+
78697875
assert(genericParams != nullptr);
78707876
assert(genericParams->getParams().size() == 1);
78717877
assert(genericParams->getOuterParameters() == nullptr);
@@ -7887,7 +7893,7 @@ Type DeclContext::getSelfInterfaceType() const {
78877893

78887894
if (auto *nominalDecl = getSelfNominalTypeDecl()) {
78897895
if (auto *builtinTupleDecl = dyn_cast<BuiltinTupleDecl>(nominalDecl))
7890-
return builtinTupleDecl->getTupleSelfType(cast<ExtensionDecl>(this));
7896+
return builtinTupleDecl->getTupleSelfType(dyn_cast<ExtensionDecl>(this));
78917897

78927898
if (isa<ProtocolDecl>(nominalDecl)) {
78937899
auto *genericParams = nominalDecl->getGenericParams();

lib/Sema/TypeCheckDecl.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,6 +2938,12 @@ bool TypeChecker::isPassThroughTypealias(TypeAliasDecl *typealias,
29382938
// If neither is generic at this level, we have a pass-through typealias.
29392939
if (!typealias->isGeneric()) return true;
29402940

2941+
if (isa<BuiltinTupleDecl>(nominal) &&
2942+
typealias->getUnderlyingType()->isEqual(
2943+
nominal->getSelfInterfaceType())) {
2944+
return true;
2945+
}
2946+
29412947
auto boundGenericType = typealias->getUnderlyingType()
29422948
->getAs<BoundGenericType>();
29432949
if (!boundGenericType) return false;
@@ -2995,8 +3001,9 @@ ExtendedTypeRequest::evaluate(Evaluator &eval, ExtensionDecl *ext) const {
29953001
: extendedNominal->getDeclaredType();
29963002
}
29973003

2998-
if (underlyingType->is<TupleType>())
2999-
extendedType = underlyingType;
3004+
if (underlyingType->is<TupleType>()) {
3005+
return extendedType;
3006+
}
30003007
}
30013008
}
30023009

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3496,6 +3496,27 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
34963496
checkAccessControl(EED);
34973497
}
34983498

3499+
/// The extended type must be '(repeat each Element)' or a generic
3500+
/// typealias with that underlying type.
3501+
static bool isValidExtendedTypeForTupleExtension(ExtensionDecl *ED) {
3502+
auto extType = ED->getExtendedType();
3503+
auto selfType = ED->getSelfInterfaceType();
3504+
3505+
// The extended type must be '(repeat each Element)'.
3506+
if (extType->is<UnboundGenericType>()) {
3507+
auto *extDecl = extType->getAnyGeneric();
3508+
if (!extDecl->getDeclaredInterfaceType()->isEqual(selfType))
3509+
return false;
3510+
} else if (extType->is<TupleType>()) {
3511+
if (!extType->isEqual(selfType))
3512+
return false;
3513+
} else {
3514+
assert(false && "Huh?");
3515+
}
3516+
3517+
return true;
3518+
}
3519+
34993520
static void checkTupleExtension(ExtensionDecl *ED) {
35003521
auto *nominal = ED->getExtendedNominal();
35013522
if (!nominal || !isa<BuiltinTupleDecl>(nominal))
@@ -3507,14 +3528,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
35073528
ED->diagnose(diag::experimental_tuple_extension);
35083529
}
35093530

3510-
auto extType = ED->getExtendedType();
3511-
3512-
// The extended type must be '(repeat each Element)'.
3513-
if (extType->is<TupleType>()) {
3514-
auto selfType = ED->getSelfInterfaceType();
3515-
if (!extType->isEqual(selfType)) {
3516-
ED->diagnose(diag::tuple_extension_wrong_type, selfType);
3517-
}
3531+
if (!isValidExtendedTypeForTupleExtension(ED)) {
3532+
ED->diagnose(diag::tuple_extension_wrong_type,
3533+
ED->getSelfInterfaceType());
35183534
}
35193535

35203536
// Make sure we declare conformance to exactly one protocol.
@@ -3526,6 +3542,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
35263542

35273543
auto *protocol = protocols[0];
35283544

3545+
// Validate the generic signature.
35293546
auto genericSig = ED->getGenericSignature();
35303547

35313548
// We have a single parameter pack by construction, if we

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,6 @@ static void collectAdditionalExtensionRequirements(
571571
if (type->is<ErrorType>())
572572
return;
573573

574-
// Tuple extensions cannot introduce requirements via this mechanism.
575574
if (type->is<TupleType>())
576575
return;
577576

@@ -605,6 +604,8 @@ static void collectAdditionalExtensionRequirements(
605604
if (!nominal) {
606605
type = typealias->getUnderlyingType();
607606
nominal = type->getNominalOrBoundGenericNominal();
607+
if (!nominal && type->is<TupleType>())
608+
nominal = type->getASTContext().getBuiltinTupleDecl();
608609
}
609610

610611
// If we have a bound generic type, add same-type requirements for each of
@@ -773,17 +774,23 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
773774
}
774775
}
775776
} else if (auto *ext = dyn_cast<ExtensionDecl>(GC)) {
776-
parentSig = ext->getExtendedNominal()->getGenericSignatureOfContext();
777-
genericParams = nullptr;
778-
779777
collectAdditionalExtensionRequirements(ext->getExtendedType(), sameTypeReqs);
780778

779+
auto *extendedNominal = ext->getExtendedNominal();
780+
781+
if (isa<BuiltinTupleDecl>(extendedNominal)) {
782+
genericParams = ext->getGenericParams();
783+
} else {
784+
parentSig = extendedNominal->getGenericSignatureOfContext();
785+
genericParams = nullptr;
786+
}
787+
781788
// Re-use the signature of the type being extended by default.
782789
// For tuple extensions, always build a new signature to get
783790
// the right sugared types, since we don't want to expose the
784791
// name of the generic parameter of BuiltinTupleDecl itself.
785792
if (sameTypeReqs.empty() && !ext->getTrailingWhereClause() &&
786-
!isa<BuiltinTupleDecl>(ext->getExtendedNominal())) {
793+
!isa<BuiltinTupleDecl>(extendedNominal)) {
787794
return parentSig;
788795
}
789796

test/Generics/tuple-conformances.swift

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ extension () {
1111
// expected-error@-1 {{type 'Nested' cannot be nested in tuple extension}}
1212
}
1313

14+
typealias BadTuple<each Horse> = (repeat each Horse, Int)
15+
extension BadTuple {}
16+
// expected-error@-1 {{tuple extension must be written as extension of '(repeat each Horse)'}}
17+
// expected-error@-2 {{tuple extension must declare conformance to exactly one protocol}}
18+
1419
typealias Tuple<each Element> = (repeat each Element)
1520

1621
protocol Q {}
@@ -28,8 +33,8 @@ protocol Base1 {}
2833
protocol Derived1: Base1 {}
2934

3035
extension Tuple: Derived1 where repeat each Element: Derived1 {}
31-
// expected-error@-1 {{conditional conformance of type '(repeat each Element)' to protocol 'Derived1' does not imply conformance to inherited protocol 'Base1'}} // FIXME: crappy error
32-
// expected-note@-2 {{did you mean to explicitly state the conformance like 'extension (repeat each Element): Base1 where ...'?}}
36+
// expected-error@-1 {{conditional conformance of type '(repeat each Element)' to protocol 'Derived1' does not imply conformance to inherited protocol 'Base1'}}
37+
// expected-note@-2 {{did you mean to explicitly state the conformance like 'extension Tuple: Base1 where ...'?}}
3338
// expected-error@-3 {{tuple extension must declare conformance to exactly one protocol}}
3439

3540
protocol Base2 {}
@@ -38,6 +43,22 @@ protocol Derived2: Base2 {}
3843
extension Tuple: Derived2 {}
3944
// expected-error@-1 {{tuple extension must declare conformance to exactly one protocol}} // FIXME: crappy error
4045

46+
////
47+
48+
protocol P2 {}
49+
50+
typealias FancyTuple1<each Cat: P2> = (repeat each Cat)
51+
extension FancyTuple1: P2 {}
52+
53+
protocol P3 {}
54+
55+
typealias FancyTuple2<each Cat: C> = (repeat each Cat)
56+
extension FancyTuple2: P3 {}
57+
// expected-error@-1 {{tuple extension cannot require that 'each Cat' subclasses 'C'}}
58+
// expected-error@-2 {{tuple extension must require that 'each Cat' conforms to 'P3'}}
59+
60+
////
61+
4162
protocol P {
4263
associatedtype A
4364
associatedtype B

0 commit comments

Comments
 (0)