Skip to content

Commit 7dc36c3

Browse files
authored
Merge pull request #40714 from slavapestov/parametrized-protocol-type
Parametrized protocol types
2 parents b49fc4f + 06e58d2 commit 7dc36c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+709
-32
lines changed

include/swift/AST/Attr.def

+4-1
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,10 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor,
657657
APIBreakingToAdd | APIBreakingToRemove,
658658
118)
659659

660-
// 119 is unused
660+
SIMPLE_DECL_ATTR(_primaryAssociatedType,
661+
PrimaryAssociatedType, OnAssociatedType | UserInaccessible |
662+
APIStableToAdd | ABIStableToAdd | APIBreakingToRemove | ABIStableToRemove,
663+
119)
661664

662665
SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks,
663666
OnFunc | UserInaccessible | NotSerialized | OnNominalType |

include/swift/AST/Decl.h

+5
Original file line numberDiff line numberDiff line change
@@ -4372,6 +4372,11 @@ class ProtocolDecl final : public NominalTypeDecl {
43724372
/// a protocol having nested types (ObjC protocols).
43734373
ArrayRef<AssociatedTypeDecl *> getAssociatedTypeMembers() const;
43744374

4375+
/// Returns the primary associated type, or nullptr if there isn't one. This is
4376+
/// the associated type that is parametrized with a same-type requirement in a
4377+
/// parametrized protocol type of the form SomeProtocol<SomeArgType>.
4378+
AssociatedTypeDecl *getPrimaryAssociatedType() const;
4379+
43754380
/// Returns a protocol requirement with the given name, or nullptr if the
43764381
/// name has multiple overloads, or no overloads at all.
43774382
ValueDecl *getSingleRequirement(DeclName name) const;

include/swift/AST/DiagnosticsSema.def

+8
Original file line numberDiff line numberDiff line change
@@ -2773,6 +2773,8 @@ ERROR(inheritance_from_non_protocol_or_class,none,
27732773
"inheritance from non-protocol, non-class type %0", (Type))
27742774
ERROR(inheritance_from_non_protocol,none,
27752775
"inheritance from non-protocol type %0", (Type))
2776+
ERROR(inheritance_from_parametrized_protocol,none,
2777+
"cannot inherit from parametrized protocol type %0", (Type))
27762778
ERROR(superclass_not_first,none,
27772779
"superclass %0 must appear first in the inheritance clause", (Type))
27782780
ERROR(superclass_not_open,none,
@@ -3714,6 +3716,12 @@ ERROR(not_a_generic_definition,none,
37143716
"cannot specialize a non-generic definition", ())
37153717
ERROR(not_a_generic_type,none,
37163718
"cannot specialize non-generic type %0", (Type))
3719+
ERROR(parametrized_protocol_not_supported,none,
3720+
"protocol type with generic argument can only be used as a generic constraint", ())
3721+
ERROR(protocol_does_not_have_primary_assoc_type,none,
3722+
"cannot specialize protocol type %0", (Type))
3723+
ERROR(protocol_cannot_have_multiple_generic_arguments,none,
3724+
"protocol type %0 can only be specialized with exactly one argument", (Type))
37173725
ERROR(cannot_specialize_self,none,
37183726
"cannot specialize 'Self'", ())
37193727
NOTE(specialize_explicit_type_instead,none,

include/swift/AST/ExistentialLayout.h

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ namespace swift {
2626
class ProtocolType;
2727
class ProtocolCompositionType;
2828

29+
struct PrimaryAssociatedTypeRequirement {
30+
AssociatedTypeDecl *AssocType;
31+
Type Argument;
32+
};
33+
2934
struct ExistentialLayout {
3035
enum Kind { Class, Error, Opaque };
3136

@@ -37,6 +42,7 @@ struct ExistentialLayout {
3742

3843
ExistentialLayout(ProtocolType *type);
3944
ExistentialLayout(ProtocolCompositionType *type);
45+
ExistentialLayout(ParametrizedProtocolType *type);
4046

4147
/// The explicit superclass constraint, if any.
4248
Type explicitSuperclass;
@@ -108,6 +114,10 @@ struct ExistentialLayout {
108114

109115
/// Zero or more protocol constraints from a ProtocolCompositionType
110116
ArrayRef<Type> protocols;
117+
118+
/// Zero or more primary associated type requirements from a
119+
/// ParametrizedProtocolType
120+
ArrayRef<PrimaryAssociatedTypeRequirement> sameTypeRequirements;
111121
};
112122

113123
}

include/swift/AST/TypeCheckRequests.h

+20
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,26 @@ class ExistentialRequiresAnyRequest :
313313
void cacheResult(bool value) const;
314314
};
315315

316+
/// Find the primary associated type of the given protocol.
317+
class PrimaryAssociatedTypeRequest :
318+
public SimpleRequest<PrimaryAssociatedTypeRequest,
319+
AssociatedTypeDecl *(ProtocolDecl *),
320+
RequestFlags::Cached> {
321+
public:
322+
using SimpleRequest::SimpleRequest;
323+
324+
private:
325+
friend SimpleRequest;
326+
327+
// Evaluation.
328+
AssociatedTypeDecl *
329+
evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;
330+
331+
public:
332+
// Caching.
333+
bool isCached() const { return true; }
334+
};
335+
316336
class PolymorphicEffectRequirementsRequest :
317337
public SimpleRequest<PolymorphicEffectRequirementsRequest,
318338
PolymorphicEffectRequirementList(EffectKind, ProtocolDecl *),

include/swift/AST/TypeCheckerTypeIDZone.def

+3
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ SWIFT_REQUEST(TypeChecker, PropertyWrapperTypeInfoRequest,
233233
NoLocationInfo)
234234
SWIFT_REQUEST(TypeChecker, ProtocolRequiresClassRequest, bool(ProtocolDecl *),
235235
SeparatelyCached, NoLocationInfo)
236+
SWIFT_REQUEST(TypeChecker, PrimaryAssociatedTypeRequest,
237+
AssociatedTypeDecl *(ProtocolDecl *),
238+
Cached, NoLocationInfo)
236239
SWIFT_REQUEST(TypeChecker, RequirementRequest,
237240
Requirement(WhereClauseOwner, unsigned, TypeResolutionStage),
238241
Cached, HasNearestLocation)

include/swift/AST/TypeDifferenceVisitor.h

+9
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,15 @@ class CanTypeDifferenceVisitor : public CanTypePairVisitor<Impl, bool> {
339339
type1->getMembers(), type2->getMembers());
340340
}
341341

342+
bool visitParametrizedProtocolType(CanParametrizedProtocolType type1,
343+
CanParametrizedProtocolType type2) {
344+
if (asImpl().visit(type1.getBaseType(), type2.getBaseType()))
345+
return true;
346+
347+
return asImpl().visit(type1.getArgumentType(),
348+
type2.getArgumentType());
349+
}
350+
342351
bool visitExistentialType(CanExistentialType type1,
343352
CanExistentialType type2) {
344353
return asImpl().visit(type1.getConstraintType(),

include/swift/AST/TypeMatcher.h

+21
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,27 @@ class TypeMatcher {
305305
TRIVIAL_CASE(SILBoxType)
306306
TRIVIAL_CASE(ProtocolCompositionType)
307307

308+
bool visitParametrizedProtocolType(CanParametrizedProtocolType firstParametrizedProto,
309+
Type secondType,
310+
Type sugaredFirstType) {
311+
if (auto secondParametrizedProto = secondType->getAs<ParametrizedProtocolType>()) {
312+
if (!this->visit(firstParametrizedProto.getBaseType(),
313+
secondParametrizedProto->getBaseType(),
314+
sugaredFirstType->castTo<ParametrizedProtocolType>()
315+
->getBaseType())) {
316+
return false;
317+
}
318+
319+
return this->visit(firstParametrizedProto.getArgumentType(),
320+
secondParametrizedProto->getArgumentType(),
321+
sugaredFirstType->castTo<ParametrizedProtocolType>()
322+
->getArgumentType());
323+
}
324+
325+
return mismatch(firstParametrizedProto.getPointer(), secondType,
326+
sugaredFirstType);
327+
}
328+
308329
bool visitExistentialType(CanExistentialType firstExistential,
309330
Type secondType,
310331
Type sugaredFirstType) {

include/swift/AST/TypeNodes.def

+1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ ARTIFICIAL_TYPE(SILBlockStorage, Type)
161161
ARTIFICIAL_TYPE(SILBox, Type)
162162
ARTIFICIAL_TYPE(SILToken, Type)
163163
TYPE(ProtocolComposition, Type)
164+
TYPE(ParametrizedProtocol, Type)
164165
TYPE(Existential, Type)
165166
TYPE(LValue, Type)
166167
TYPE(InOut, Type)

include/swift/AST/Types.h

+64
Original file line numberDiff line numberDiff line change
@@ -5243,6 +5243,70 @@ class ProtocolCompositionType final : public TypeBase,
52435243
BEGIN_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)
52445244
END_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)
52455245

5246+
/// ParametrizedProtocolType - A type that constrains the primary associated
5247+
/// type of a protocol to an argument type.
5248+
///
5249+
/// Written like a bound generic type, eg Sequence<Int>.
5250+
///
5251+
/// For now, these are only supported in generic requirement-like contexts:
5252+
/// - Inheritance clauses of protocols, generic parameters, associated types
5253+
/// - Conformance requirements in where clauses
5254+
/// - Extensions
5255+
///
5256+
/// Assuming that the primary associated type of Sequence is Element, the
5257+
/// desugaring is that T : Sequence<Int> is equivalent to
5258+
///
5259+
/// \code
5260+
/// T : Sequence where T.Element == Int.
5261+
/// \endcode
5262+
class ParametrizedProtocolType final : public TypeBase,
5263+
public llvm::FoldingSetNode {
5264+
friend struct ExistentialLayout;
5265+
5266+
ProtocolType *Base;
5267+
AssociatedTypeDecl *AssocType;
5268+
Type Arg;
5269+
5270+
public:
5271+
/// Retrieve an instance of a protocol composition type with the
5272+
/// given set of members.
5273+
static Type get(const ASTContext &C, ProtocolType *base,
5274+
Type arg);
5275+
5276+
ProtocolType *getBaseType() const {
5277+
return Base;
5278+
}
5279+
5280+
AssociatedTypeDecl *getAssocType() const {
5281+
return AssocType;
5282+
}
5283+
5284+
Type getArgumentType() const {
5285+
return Arg;
5286+
}
5287+
5288+
void Profile(llvm::FoldingSetNodeID &ID) {
5289+
Profile(ID, Base, Arg);
5290+
}
5291+
static void Profile(llvm::FoldingSetNodeID &ID,
5292+
ProtocolType *base,
5293+
Type arg);
5294+
5295+
// Implement isa/cast/dyncast/etc.
5296+
static bool classof(const TypeBase *T) {
5297+
return T->getKind() == TypeKind::ParametrizedProtocol;
5298+
}
5299+
5300+
private:
5301+
ParametrizedProtocolType(const ASTContext *ctx,
5302+
ProtocolType *base, Type arg,
5303+
RecursiveTypeProperties properties);
5304+
};
5305+
BEGIN_CAN_TYPE_WRAPPER(ParametrizedProtocolType, Type)
5306+
PROXY_CAN_TYPE_SIMPLE_GETTER(getBaseType)
5307+
PROXY_CAN_TYPE_SIMPLE_GETTER(getArgumentType)
5308+
END_CAN_TYPE_WRAPPER(ParametrizedProtocolType, Type)
5309+
52465310
/// An existential type, spelled with \c any .
52475311
///
52485312
/// In Swift 5 mode, a plain protocol name in type

include/swift/Basic/LangOptions.h

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ namespace swift {
317317
/// keyword.
318318
bool EnableExplicitExistentialTypes = true;
319319

320+
/// Enable support for protocol types parametrized by primary
321+
/// associated type.
322+
bool EnableParametrizedProtocolTypes = false;
323+
320324
/// Enable experimental flow-sensitive concurrent captures.
321325
bool EnableExperimentalFlowSensitiveConcurrentCaptures = false;
322326

include/swift/Option/FrontendOptions.td

+4
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ def enable_explicit_existential_types :
506506
Flag<["-"], "enable-explicit-existential-types">,
507507
HelpText<"Enable experimental support for explicit existential types">;
508508

509+
def enable_parametrized_protocol_types :
510+
Flag<["-"], "enable-parametrized-protocol-types">,
511+
HelpText<"Enable experimental support for primary associated types and parametrized protocols">;
512+
509513
def enable_deserialization_recovery :
510514
Flag<["-"], "enable-deserialization-recovery">,
511515
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;

lib/AST/ASTContext.cpp

+29-1
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ struct ASTContext::Implementation {
405405
llvm::FoldingSet<UnboundGenericType> UnboundGenericTypes;
406406
llvm::FoldingSet<BoundGenericType> BoundGenericTypes;
407407
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
408+
llvm::FoldingSet<ParametrizedProtocolType> ParametrizedProtocolTypes;
408409
llvm::FoldingSet<LayoutConstraintInfo> LayoutConstraints;
409410
llvm::DenseMap<std::pair<OpaqueTypeDecl *, SubstitutionMap>,
410411
GenericEnvironment *> OpaqueArchetypeEnvironments;
@@ -3240,10 +3241,37 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Members,
32403241
Members,
32413242
HasExplicitAnyObject,
32423243
properties);
3243-
C.getImpl().getArena(arena).ProtocolCompositionTypes.InsertNode(compTy, InsertPos);
3244+
C.getImpl().getArena(arena).ProtocolCompositionTypes.InsertNode(
3245+
compTy, InsertPos);
32443246
return compTy;
32453247
}
32463248

3249+
Type ParametrizedProtocolType::get(const ASTContext &C,
3250+
ProtocolType *baseTy,
3251+
Type argTy) {
3252+
bool isCanonical = baseTy->isCanonical();
3253+
RecursiveTypeProperties properties = baseTy->getRecursiveProperties();
3254+
properties |= argTy->getRecursiveProperties();
3255+
isCanonical &= argTy->isCanonical();
3256+
3257+
auto arena = getArena(properties);
3258+
3259+
void *InsertPos = nullptr;
3260+
llvm::FoldingSetNodeID ID;
3261+
ParametrizedProtocolType::Profile(ID, baseTy, argTy);
3262+
3263+
if (auto paramTy
3264+
= C.getImpl().getArena(arena).ParametrizedProtocolTypes
3265+
.FindNodeOrInsertPos(ID, InsertPos))
3266+
return paramTy;
3267+
3268+
auto paramTy = new (C, arena) ParametrizedProtocolType(
3269+
isCanonical ? &C : nullptr, baseTy, argTy, properties);
3270+
C.getImpl().getArena(arena).ParametrizedProtocolTypes.InsertNode(
3271+
paramTy, InsertPos);
3272+
return paramTy;
3273+
}
3274+
32473275
ReferenceStorageType *ReferenceStorageType::get(Type T,
32483276
ReferenceOwnership ownership,
32493277
const ASTContext &C) {

lib/AST/ASTDumper.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -3942,6 +3942,14 @@ namespace {
39423942
PrintWithColorRAII(OS, ParenthesisColor) << ')';
39433943
}
39443944

3945+
void visitParametrizedProtocolType(ParametrizedProtocolType *T,
3946+
StringRef label) {
3947+
printCommon(label, "parametrized_protocol_type");
3948+
printRec("base", T->getBaseType());
3949+
printRec("arg", T->getArgumentType());
3950+
PrintWithColorRAII(OS, ParenthesisColor) << ')';
3951+
}
3952+
39453953
void visitExistentialType(ExistentialType *T,
39463954
StringRef label) {
39473955
printCommon(label, "existential_type");

lib/AST/ASTMangler.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1266,6 +1266,11 @@ void ASTMangler::appendType(Type type, GenericSignature sig,
12661266
return appendExistentialLayout(layout, sig, forDecl);
12671267
}
12681268

1269+
case TypeKind::ParametrizedProtocol: {
1270+
llvm::errs() << "Not implemented\n";
1271+
abort();
1272+
}
1273+
12691274
case TypeKind::Existential: {
12701275
auto constraint = cast<ExistentialType>(tybase)->getConstraintType();
12711276
return appendType(constraint, sig, forDecl);

lib/AST/ASTPrinter.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -5877,6 +5877,13 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
58775877
}
58785878
}
58795879

5880+
void visitParametrizedProtocolType(ParametrizedProtocolType *T) {
5881+
visit(T->getBaseType());
5882+
Printer << "<";
5883+
visit(T->getArgumentType());
5884+
Printer << ">";
5885+
}
5886+
58805887
void visitExistentialType(ExistentialType *T) {
58815888
if (Options.PrintExplicitAny)
58825889
Printer << "any ";

lib/AST/Decl.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -5284,6 +5284,12 @@ bool ProtocolDecl::existentialRequiresAny() const {
52845284
ExistentialRequiresAnyRequest{const_cast<ProtocolDecl *>(this)}, true);
52855285
}
52865286

5287+
AssociatedTypeDecl *ProtocolDecl::getPrimaryAssociatedType() const {
5288+
return evaluateOrDefault(getASTContext().evaluator,
5289+
PrimaryAssociatedTypeRequest{const_cast<ProtocolDecl *>(this)},
5290+
nullptr);
5291+
}
5292+
52875293
StringRef ProtocolDecl::getObjCRuntimeName(
52885294
llvm::SmallVectorImpl<char> &buffer) const {
52895295
// If there is an 'objc' attribute with a name, use that name.

lib/AST/GenericSignatureBuilder.cpp

+24-1
Original file line numberDiff line numberDiff line change
@@ -4502,6 +4502,7 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
45024502
// Check whether we have a reasonable constraint type at all.
45034503
if (!constraintType->is<ProtocolType>() &&
45044504
!constraintType->is<ProtocolCompositionType>() &&
4505+
!constraintType->is<ParametrizedProtocolType>() &&
45054506
!constraintType->getClassOrBoundGenericClass()) {
45064507
if (source.getLoc().isValid() && !constraintType->hasError()) {
45074508
Impl->HadAnyError = true;
@@ -4514,8 +4515,30 @@ ConstraintResult GenericSignatureBuilder::addTypeRequirement(
45144515
return ConstraintResult::Conflicting;
45154516
}
45164517

4518+
// Parametrized protocol requirements.
4519+
if (auto *paramProtoType = constraintType->getAs<ParametrizedProtocolType>()) {
4520+
bool anyErrors = false;
4521+
4522+
auto *protoDecl = paramProtoType->getBaseType()->getDecl();
4523+
4524+
if (isErrorResult(addConformanceRequirement(resolvedSubject, protoDecl,
4525+
source)))
4526+
anyErrors = true;
4527+
4528+
auto *assocType = paramProtoType->getAssocType();
4529+
auto depType = DependentMemberType::get(
4530+
resolvedSubject.getDependentType(*this), assocType);
4531+
if (isErrorResult(addSameTypeRequirement(Type(depType),
4532+
paramProtoType->getArgumentType(),
4533+
source,
4534+
UnresolvedHandlingKind::GenerateConstraints)))
4535+
anyErrors = true;
4536+
4537+
return anyErrors ? ConstraintResult::Conflicting
4538+
: ConstraintResult::Resolved;
4539+
45174540
// Protocol requirements.
4518-
if (constraintType->isExistentialType()) {
4541+
} else if (constraintType->isExistentialType()) {
45194542
bool anyErrors = false;
45204543
auto layout = constraintType->getExistentialLayout();
45214544

0 commit comments

Comments
 (0)