Skip to content

Commit bc8657b

Browse files
authored
Merge pull request #39067 from kavon/revert-unlock-existential
Revert "Merge pull request #33767 ..."
2 parents 4681d41 + c0607b3 commit bc8657b

31 files changed

+572
-825
lines changed

include/swift/AST/Decl.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
514514
IsComputingSemanticMembers : 1
515515
);
516516

517-
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+8+16,
517+
SWIFT_INLINE_BITFIELD_FULL(ProtocolDecl, NominalTypeDecl, 1+1+1+1+1+1+1+1+1+1+1+8+16,
518518
/// Whether the \c RequiresClass bit is valid.
519519
RequiresClassValid : 1,
520520

@@ -527,6 +527,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
527527
/// Whether the existential of this protocol conforms to itself.
528528
ExistentialConformsToSelf : 1,
529529

530+
/// Whether the \c ExistentialTypeSupported bit is valid.
531+
ExistentialTypeSupportedValid : 1,
532+
533+
/// Whether the existential of this protocol can be represented.
534+
ExistentialTypeSupported : 1,
535+
530536
/// True if the protocol has requirements that cannot be satisfied (e.g.
531537
/// because they could not be imported from Objective-C).
532538
HasMissingRequirements : 1,
@@ -4145,6 +4151,21 @@ class ProtocolDecl final : public NominalTypeDecl {
41454151
Bits.ProtocolDecl.ExistentialConformsToSelf = result;
41464152
}
41474153

4154+
/// Returns the cached result of \c existentialTypeSupported or \c None if it
4155+
/// hasn't yet been computed.
4156+
Optional<bool> getCachedExistentialTypeSupported() {
4157+
if (Bits.ProtocolDecl.ExistentialTypeSupportedValid)
4158+
return Bits.ProtocolDecl.ExistentialTypeSupported;
4159+
4160+
return None;
4161+
}
4162+
4163+
/// Caches the result of \c existentialTypeSupported
4164+
void setCachedExistentialTypeSupported(bool supported) {
4165+
Bits.ProtocolDecl.ExistentialTypeSupportedValid = true;
4166+
Bits.ProtocolDecl.ExistentialTypeSupported = supported;
4167+
}
4168+
41484169
bool hasLazyRequirementSignature() const {
41494170
return Bits.ProtocolDecl.HasLazyRequirementSignature;
41504171
}
@@ -4154,6 +4175,7 @@ class ProtocolDecl final : public NominalTypeDecl {
41544175
friend class RequirementSignatureRequest;
41554176
friend class ProtocolRequiresClassRequest;
41564177
friend class ExistentialConformsToSelfRequest;
4178+
friend class ExistentialTypeSupportedRequest;
41574179
friend class InheritedProtocolsRequest;
41584180

41594181
public:
@@ -4242,6 +4264,12 @@ class ProtocolDecl final : public NominalTypeDecl {
42424264
/// contain 'Self' in 'parameter' or 'other' position.
42434265
bool isAvailableInExistential(const ValueDecl *decl) const;
42444266

4267+
/// Determine whether we are allowed to refer to an existential type
4268+
/// conforming to this protocol. This is only permitted if the types of
4269+
/// all the members do not contain any associated types, and do not
4270+
/// contain 'Self' in 'parameter' or 'other' position.
4271+
bool existentialTypeSupported() const;
4272+
42454273
/// Returns a list of protocol requirements that must be assessed to
42464274
/// determine a concrete's conformance effect polymorphism kind.
42474275
PolymorphicEffectRequirementList getPolymorphicEffectRequirements(

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,9 @@ NOTE(object_literal_resolve_import,none,
920920

921921
ERROR(use_local_before_declaration,none,
922922
"use of local variable %0 before its declaration", (DeclNameRef))
923+
ERROR(unsupported_existential_type,none,
924+
"protocol %0 can only be used as a generic constraint because it has "
925+
"Self or associated type requirements", (Identifier))
923926

924927
ERROR(decl_does_not_exist_in_module,none,
925928
"%select{%error|type|struct|class|enum|protocol|variable|function}0 "

include/swift/AST/EducationalNotes.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
// EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...)
2323

24-
EDUCATIONAL_NOTES(could_not_use_member_on_existential,
25-
"existential-member-access-limitations.md")
24+
EDUCATIONAL_NOTES(unsupported_existential_type,
25+
"associated-type-requirements.md")
2626

2727
EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral, "temporary-pointers.md")
2828
EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral_warning,

include/swift/AST/TypeCheckRequests.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,32 @@ class ExistentialConformsToSelfRequest :
287287
void cacheResult(bool value) const;
288288
};
289289

290+
/// Determine whether we are allowed to refer to an existential type conforming
291+
/// to this protocol.
292+
class ExistentialTypeSupportedRequest :
293+
public SimpleRequest<ExistentialTypeSupportedRequest,
294+
bool(ProtocolDecl *),
295+
RequestFlags::SeparatelyCached> {
296+
public:
297+
using SimpleRequest::SimpleRequest;
298+
299+
private:
300+
friend SimpleRequest;
301+
302+
// Evaluation.
303+
bool evaluate(Evaluator &evaluator, ProtocolDecl *decl) const;
304+
305+
public:
306+
// Cycle handling.
307+
void diagnoseCycle(DiagnosticEngine &diags) const;
308+
void noteCycleStep(DiagnosticEngine &diags) const;
309+
310+
// Separate caching.
311+
bool isCached() const { return true; }
312+
Optional<bool> getCachedResult() const;
313+
void cacheResult(bool value) const;
314+
};
315+
290316
class PolymorphicEffectRequirementsRequest :
291317
public SimpleRequest<PolymorphicEffectRequirementsRequest,
292318
PolymorphicEffectRequirementList(EffectKind, ProtocolDecl *),

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ SWIFT_REQUEST(TypeChecker, EnumRawTypeRequest,
8787
Type(EnumDecl *), Cached, NoLocationInfo)
8888
SWIFT_REQUEST(TypeChecker, ExistentialConformsToSelfRequest,
8989
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
90+
SWIFT_REQUEST(TypeChecker, ExistentialTypeSupportedRequest,
91+
bool(ProtocolDecl *), SeparatelyCached, NoLocationInfo)
9092
SWIFT_REQUEST(TypeChecker, ExtendedTypeRequest, Type(ExtensionDecl *), Cached,
9193
NoLocationInfo)
9294
SWIFT_REQUEST(TypeChecker, ResultBuilderTypeRequest, Type(ValueDecl *),

lib/AST/Decl.cpp

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5008,31 +5008,24 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
50085008
return findProtocolSelfReferences(proto, selfType->getSelfType(), position);
50095009
}
50105010

5011-
if (auto *const nominal = type->getAs<NominalOrBoundGenericNominalType>()) {
5011+
// Most bound generic types are invariant.
5012+
if (auto *const bgt = type->getAs<BoundGenericType>()) {
50125013
auto info = SelfReferenceInfo();
50135014

5014-
// Don't forget to look in the parent.
5015-
if (const auto parent = nominal->getParent()) {
5016-
info |= findProtocolSelfReferences(proto, parent, position);
5017-
}
5018-
5019-
// Most bound generic types are invariant.
5020-
if (auto *const bgt = type->getAs<BoundGenericType>()) {
5021-
if (bgt->isArray()) {
5022-
// Swift.Array preserves variance in its Value type.
5023-
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
5024-
position);
5025-
} else if (bgt->isDictionary()) {
5026-
// Swift.Dictionary preserves variance in its Element type.
5027-
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
5015+
if (bgt->isArray()) {
5016+
// Swift.Array preserves variance in its Value type.
5017+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
5018+
position);
5019+
} else if (bgt->isDictionary()) {
5020+
// Swift.Dictionary preserves variance in its Element type.
5021+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().front(),
5022+
SelfReferencePosition::Invariant);
5023+
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
5024+
position);
5025+
} else {
5026+
for (auto paramType : bgt->getGenericArgs()) {
5027+
info |= findProtocolSelfReferences(proto, paramType,
50285028
SelfReferencePosition::Invariant);
5029-
info |= findProtocolSelfReferences(proto, bgt->getGenericArgs().back(),
5030-
position);
5031-
} else {
5032-
for (auto paramType : bgt->getGenericArgs()) {
5033-
info |= findProtocolSelfReferences(proto, paramType,
5034-
SelfReferencePosition::Invariant);
5035-
}
50365029
}
50375030
}
50385031

@@ -5044,16 +5037,6 @@ findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
50445037
if (type->is<OpaqueTypeArchetypeType>())
50455038
return SelfReferenceInfo::forSelfRef(SelfReferencePosition::Invariant);
50465039

5047-
// Protocol compositions preserve variance.
5048-
if (auto *comp = type->getAs<ProtocolCompositionType>()) {
5049-
// 'Self' may be referenced only in a superclass component.
5050-
if (const auto superclass = comp->getSuperclass()) {
5051-
return findProtocolSelfReferences(proto, superclass, position);
5052-
}
5053-
5054-
return SelfReferenceInfo();
5055-
}
5056-
50575040
// A direct reference to 'Self'.
50585041
if (proto->getSelfInterfaceType()->isEqual(type))
50595042
return SelfReferenceInfo::forSelfRef(position);
@@ -5153,6 +5136,11 @@ bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
51535136
return true;
51545137
}
51555138

5139+
bool ProtocolDecl::existentialTypeSupported() const {
5140+
return evaluateOrDefault(getASTContext().evaluator,
5141+
ExistentialTypeSupportedRequest{const_cast<ProtocolDecl *>(this)}, true);
5142+
}
5143+
51565144
StringRef ProtocolDecl::getObjCRuntimeName(
51575145
llvm::SmallVectorImpl<char> &buffer) const {
51585146
// If there is an 'objc' attribute with a name, use that name.

lib/AST/TypeCheckRequests.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,31 @@ void ExistentialConformsToSelfRequest::cacheResult(bool value) const {
253253
decl->setCachedExistentialConformsToSelf(value);
254254
}
255255

256+
//----------------------------------------------------------------------------//
257+
// existentialTypeSupported computation.
258+
//----------------------------------------------------------------------------//
259+
260+
void ExistentialTypeSupportedRequest::diagnoseCycle(DiagnosticEngine &diags) const {
261+
auto decl = std::get<0>(getStorage());
262+
diags.diagnose(decl, diag::circular_protocol_def, decl->getName());
263+
}
264+
265+
void ExistentialTypeSupportedRequest::noteCycleStep(DiagnosticEngine &diags) const {
266+
auto requirement = std::get<0>(getStorage());
267+
diags.diagnose(requirement, diag::kind_declname_declared_here,
268+
DescriptiveDeclKind::Protocol, requirement->getName());
269+
}
270+
271+
Optional<bool> ExistentialTypeSupportedRequest::getCachedResult() const {
272+
auto decl = std::get<0>(getStorage());
273+
return decl->getCachedExistentialTypeSupported();
274+
}
275+
276+
void ExistentialTypeSupportedRequest::cacheResult(bool value) const {
277+
auto decl = std::get<0>(getStorage());
278+
decl->setCachedExistentialTypeSupported(value);
279+
}
280+
256281
//----------------------------------------------------------------------------//
257282
// isFinal computation.
258283
//----------------------------------------------------------------------------//

lib/Sema/CSSimplify.cpp

Lines changed: 26 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7322,7 +7322,7 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
73227322
// Dig out the instance type and figure out what members of the instance type
73237323
// we are going to see.
73247324
auto baseTy = candidate.getBaseType();
7325-
const auto baseObjTy = baseTy->getRValueType();
7325+
auto baseObjTy = baseTy->getRValueType();
73267326

73277327
bool hasInstanceMembers = false;
73287328
bool hasInstanceMethods = false;
@@ -7369,6 +7369,18 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
73697369
hasInstanceMethods = true;
73707370
}
73717371

7372+
// If our base is an existential type, we can't make use of any
7373+
// member whose signature involves associated types.
7374+
if (instanceTy->isExistentialType()) {
7375+
if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) {
7376+
if (!proto->isAvailableInExistential(decl)) {
7377+
result.addUnviable(candidate,
7378+
MemberLookupResult::UR_UnavailableInExistential);
7379+
return;
7380+
}
7381+
}
7382+
}
7383+
73727384
// If the invocation's argument expression has a favored type,
73737385
// use that information to determine whether a specific overload for
73747386
// the candidate should be favored.
@@ -7388,20 +7400,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
73887400
}
73897401
}
73907402

7391-
const auto isUnsupportedExistentialMemberAccess = [&] {
7392-
// If our base is an existential type, we can't make use of any
7393-
// member whose signature involves associated types.
7394-
if (instanceTy->isExistentialType()) {
7395-
if (auto *proto = decl->getDeclContext()->getSelfProtocolDecl()) {
7396-
if (!proto->isAvailableInExistential(decl)) {
7397-
return true;
7398-
}
7399-
}
7400-
}
7401-
7402-
return false;
7403-
};
7404-
74057403
// See if we have an instance method, instance member or static method,
74067404
// and check if it can be accessed on our base type.
74077405

@@ -7415,35 +7413,20 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
74157413
? candidate
74167414
: OverloadChoice(instanceTy, decl,
74177415
FunctionRefKind::SingleApply);
7418-
7419-
const bool invalidMethodRef = isa<FuncDecl>(decl) && !hasInstanceMethods;
7420-
const bool invalidMemberRef = !isa<FuncDecl>(decl) && !hasInstanceMembers;
7421-
7422-
if (invalidMethodRef || invalidMemberRef) {
7423-
// If this is definitely an invalid way to reference a method or member
7424-
// on the metatype, let's stop here.
7425-
result.addUnviable(choice,
7426-
MemberLookupResult::UR_InstanceMemberOnType);
7427-
return;
7428-
} else if (isUnsupportedExistentialMemberAccess()) {
7429-
// If the member reference itself is legal, but it turns out to be an
7430-
// unsupported existential member access, do not make further
7431-
// assumptions about the correctness of a potential call -- let
7432-
// the unsupported member access error prevail.
7433-
result.addUnviable(candidate,
7434-
MemberLookupResult::UR_UnavailableInExistential);
7416+
// If this is an instance member referenced from metatype
7417+
// let's add unviable result to the set because it could be
7418+
// either curried reference or an invalid call.
7419+
//
7420+
// New candidate shouldn't affect performance because such
7421+
// choice would only be attempted when solver is in diagnostic mode.
7422+
result.addUnviable(choice, MemberLookupResult::UR_InstanceMemberOnType);
7423+
7424+
bool invalidMethodRef = isa<FuncDecl>(decl) && !hasInstanceMethods;
7425+
bool invalidMemberRef = !isa<FuncDecl>(decl) && !hasInstanceMembers;
7426+
// If this is definitely an invalid way to reference a method or member
7427+
// on the metatype, let's stop here.
7428+
if (invalidMethodRef || invalidMemberRef)
74357429
return;
7436-
} else {
7437-
// Otherwise, still add an unviable result to the set, because it
7438-
// could be an invalid call that was supposed to be performed on an
7439-
// instance of the type.
7440-
//
7441-
// New candidate shouldn't affect performance because such
7442-
// choice would only be attempted when solver is in diagnostic mode.
7443-
result.addUnviable(choice,
7444-
MemberLookupResult::UR_InstanceMemberOnType);
7445-
7446-
}
74477430
}
74487431

74497432
// If the underlying type of a typealias is fully concrete, it is legal
@@ -7494,12 +7477,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
74947477
}
74957478
}
74967479

7497-
if (isUnsupportedExistentialMemberAccess()) {
7498-
result.addUnviable(candidate,
7499-
MemberLookupResult::UR_UnavailableInExistential);
7500-
return;
7501-
}
7502-
75037480
// If we have an rvalue base, make sure that the result isn't 'mutating'
75047481
// (only valid on lvalues).
75057482
if (!baseTy->is<AnyMetatypeType>() &&

lib/Sema/MiscDiagnostics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3329,6 +3329,8 @@ static void checkSwitch(ASTContext &ctx, const SwitchStmt *stmt) {
33293329
// We want to warn about "case .Foo, .Bar where 1 != 100:" since the where
33303330
// clause only applies to the second case, and this is surprising.
33313331
for (auto cs : stmt->getCases()) {
3332+
TypeChecker::checkUnsupportedProtocolType(ctx, cs);
3333+
33323334
// The case statement can have multiple case items, each can have a where.
33333335
// If we find a "where", and there is a preceding item without a where, and
33343336
// if they are on the same source line, then warn.
@@ -4745,6 +4747,8 @@ void swift::performSyntacticExprDiagnostics(const Expr *E,
47454747
void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
47464748
auto &ctx = DC->getASTContext();
47474749

4750+
TypeChecker::checkUnsupportedProtocolType(ctx, const_cast<Stmt *>(S));
4751+
47484752
if (auto switchStmt = dyn_cast<SwitchStmt>(S))
47494753
checkSwitch(ctx, switchStmt);
47504754

lib/Sema/TypeCheckDecl.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,34 @@ ExistentialConformsToSelfRequest::evaluate(Evaluator &evaluator,
672672
return true;
673673
}
674674

675+
bool
676+
ExistentialTypeSupportedRequest::evaluate(Evaluator &evaluator,
677+
ProtocolDecl *decl) const {
678+
// ObjC protocols can always be existential.
679+
if (decl->isObjC())
680+
return true;
681+
682+
for (auto member : decl->getMembers()) {
683+
// Existential types cannot be used if the protocol has an associated type.
684+
if (isa<AssociatedTypeDecl>(member))
685+
return false;
686+
687+
// For value members, look at their type signatures.
688+
if (auto valueMember = dyn_cast<ValueDecl>(member)) {
689+
if (!decl->isAvailableInExistential(valueMember))
690+
return false;
691+
}
692+
}
693+
694+
// Check whether all of the inherited protocols support existential types.
695+
for (auto proto : decl->getInheritedProtocols()) {
696+
if (!proto->existentialTypeSupported())
697+
return false;
698+
}
699+
700+
return true;
701+
}
702+
675703
bool
676704
IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
677705
if (isa<ClassDecl>(decl))

0 commit comments

Comments
 (0)