Skip to content

Commit c52affb

Browse files
authored
Merge pull request #37440 from slavapestov/clean-up-generic-requirement-checking
Clean up duplicate logic for checking if generic requirements are satisfied
2 parents ccd3976 + 131d3f4 commit c52affb

Some content is hidden

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

41 files changed

+698
-610
lines changed

include/swift/AST/ActorIsolation.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class raw_ostream;
2525

2626
namespace swift {
2727
class DeclContext;
28+
class ModuleDecl;
2829
class NominalTypeDecl;
2930
class SubstitutionMap;
3031

@@ -33,7 +34,7 @@ class SubstitutionMap;
3334
bool areTypesEqual(Type type1, Type type2);
3435

3536
/// Determine whether the given type is suitable as a concurrent value type.
36-
bool isSendableType(const DeclContext *dc, Type type);
37+
bool isSendableType(ModuleDecl *module, Type type);
3738

3839
/// Describes the actor isolation of a given declaration, which determines
3940
/// the actors with which it can interact.

include/swift/AST/Requirement.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ class Requirement
7676

7777
ProtocolDecl *getProtocolDecl() const;
7878

79+
/// Determines if this substituted requirement is satisfied.
80+
///
81+
/// \param conditionalRequirements An out parameter initialized to an
82+
/// array of requirements that the caller must check to ensure this
83+
/// requirement is completely satisfied.
84+
bool isSatisfied(ArrayRef<Requirement> &conditionalRequirements) const;
85+
86+
/// Determines if this substituted requirement can ever be satisfied,
87+
/// possibly with additional substitutions.
88+
///
89+
/// For example, if 'T' is unconstrained, then a superclass requirement
90+
/// 'T : C' can be satisfied; however, if 'T' already has an unrelated
91+
/// superclass requirement, 'T : C' cannot be satisfied.
92+
bool canBeSatisfied() const;
93+
7994
SWIFT_DEBUG_DUMP;
8095
void dump(raw_ostream &out) const;
8196
void print(raw_ostream &os, const PrintOptions &opts) const;

include/swift/Sema/ConstraintSystem.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5269,17 +5269,9 @@ bool hasAppliedSelf(ConstraintSystem &cs, const OverloadChoice &choice);
52695269
bool hasAppliedSelf(const OverloadChoice &choice,
52705270
llvm::function_ref<Type(Type)> getFixedType);
52715271

5272-
/// Check whether type conforms to a given known protocol.
5273-
bool conformsToKnownProtocol(DeclContext *dc, Type type,
5274-
KnownProtocolKind protocol);
5275-
5276-
/// Check whether given type conforms to `RawPepresentable` protocol
5272+
/// Check whether given type conforms to `RawRepresentable` protocol
52775273
/// and return witness type.
52785274
Type isRawRepresentable(ConstraintSystem &cs, Type type);
5279-
/// Check whether given type conforms to a specific known kind
5280-
/// `RawPepresentable` protocol and return witness type.
5281-
Type isRawRepresentable(ConstraintSystem &cs, Type type,
5282-
KnownProtocolKind rawRepresentableProtocol);
52835275

52845276
/// Compute the type that shall stand in for dynamic 'Self' in a member
52855277
/// reference with a base of the given object type.

lib/AST/GenericSignature.cpp

Lines changed: 80 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -428,62 +428,28 @@ bool GenericSignatureImpl::areSameTypeParameterInContext(Type type1,
428428

429429
bool GenericSignatureImpl::isRequirementSatisfied(
430430
Requirement requirement) const {
431-
auto GSB = getGenericSignatureBuilder();
431+
if (requirement.getFirstType()->hasTypeParameter()) {
432+
auto *genericEnv = getGenericEnvironment();
432433

433-
auto firstType = requirement.getFirstType();
434-
auto canFirstType = getCanonicalTypeInContext(firstType);
434+
auto substituted = requirement.subst(
435+
[&](SubstitutableType *type) -> Type {
436+
if (auto *paramType = type->getAs<GenericTypeParamType>())
437+
return genericEnv->mapTypeIntoContext(paramType);
435438

436-
switch (requirement.getKind()) {
437-
case RequirementKind::Conformance: {
438-
auto *protocol = requirement.getProtocolDecl();
439-
440-
if (canFirstType->isTypeParameter())
441-
return requiresProtocol(canFirstType, protocol);
442-
else
443-
return (bool)GSB->lookupConformance(canFirstType, protocol);
444-
}
439+
return type;
440+
},
441+
LookUpConformanceInSignature(this));
445442

446-
case RequirementKind::SameType: {
447-
auto canSecondType = getCanonicalTypeInContext(requirement.getSecondType());
448-
return canFirstType->isEqual(canSecondType);
449-
}
450-
451-
case RequirementKind::Superclass: {
452-
auto requiredSuperclass =
453-
getCanonicalTypeInContext(requirement.getSecondType());
454-
455-
// The requirement could be in terms of type parameters like a user-written
456-
// requirement, but it could also be in terms of concrete types if it has
457-
// been substituted/otherwise 'resolved', so we need to handle both.
458-
auto baseType = canFirstType;
459-
if (baseType->isTypeParameter()) {
460-
auto directSuperclass = getSuperclassBound(baseType);
461-
if (!directSuperclass)
462-
return false;
463-
464-
baseType = getCanonicalTypeInContext(directSuperclass);
465-
}
443+
if (!substituted)
444+
return false;
466445

467-
return requiredSuperclass->isExactSuperclassOf(baseType);
446+
requirement = *substituted;
468447
}
469448

470-
case RequirementKind::Layout: {
471-
auto requiredLayout = requirement.getLayoutConstraint();
472-
473-
if (canFirstType->isTypeParameter()) {
474-
if (auto layout = getLayoutConstraint(canFirstType))
475-
return static_cast<bool>(layout.merge(requiredLayout));
476-
477-
return false;
478-
}
449+
// FIXME: Need to check conditional requirements here.
450+
ArrayRef<Requirement> conditionalRequirements;
479451

480-
// The requirement is on a concrete type, so it's either globally correct
481-
// or globally incorrect, independent of this generic context. The latter
482-
// case should be diagnosed elsewhere, so let's assume it's correct.
483-
return true;
484-
}
485-
}
486-
llvm_unreachable("unhandled kind");
452+
return requirement.isSatisfied(conditionalRequirements);
487453
}
488454

489455
SmallVector<Requirement, 4> GenericSignatureImpl::requirementsNotSatisfiedBy(
@@ -494,7 +460,7 @@ SmallVector<Requirement, 4> GenericSignatureImpl::requirementsNotSatisfiedBy(
494460
if (otherSig.getPointer() == this) return result;
495461

496462
// If there is no other signature, no requirements are satisfied.
497-
if (!otherSig){
463+
if (!otherSig) {
498464
const auto reqs = getRequirements();
499465
result.append(reqs.begin(), reqs.end());
500466
return result;
@@ -722,3 +688,67 @@ ProtocolDecl *Requirement::getProtocolDecl() const {
722688
assert(getKind() == RequirementKind::Conformance);
723689
return getSecondType()->castTo<ProtocolType>()->getDecl();
724690
}
691+
692+
bool
693+
Requirement::isSatisfied(ArrayRef<Requirement> &conditionalRequirements) const {
694+
switch (getKind()) {
695+
case RequirementKind::Conformance: {
696+
auto *proto = getProtocolDecl();
697+
auto *module = proto->getParentModule();
698+
auto conformance = module->lookupConformance(getFirstType(), proto);
699+
if (!conformance)
700+
return false;
701+
702+
conditionalRequirements = conformance.getConditionalRequirements();
703+
return true;
704+
}
705+
706+
case RequirementKind::Layout: {
707+
if (auto *archetypeType = getFirstType()->getAs<ArchetypeType>()) {
708+
auto layout = archetypeType->getLayoutConstraint();
709+
return (layout && layout.merge(getLayoutConstraint()));
710+
}
711+
712+
if (getLayoutConstraint()->isClass())
713+
return getFirstType()->satisfiesClassConstraint();
714+
715+
// TODO: Statically check other layout constraints, once they can
716+
// be spelled in Swift.
717+
return true;
718+
}
719+
720+
case RequirementKind::Superclass:
721+
return getSecondType()->isExactSuperclassOf(getFirstType());
722+
723+
case RequirementKind::SameType:
724+
return getFirstType()->isEqual(getSecondType());
725+
}
726+
727+
llvm_unreachable("Bad requirement kind");
728+
}
729+
730+
bool Requirement::canBeSatisfied() const {
731+
switch (getKind()) {
732+
case RequirementKind::Conformance:
733+
return getFirstType()->is<ArchetypeType>();
734+
735+
case RequirementKind::Layout: {
736+
if (auto *archetypeType = getFirstType()->getAs<ArchetypeType>()) {
737+
auto layout = archetypeType->getLayoutConstraint();
738+
return (!layout || layout.merge(getLayoutConstraint()));
739+
}
740+
741+
return false;
742+
}
743+
744+
case RequirementKind::Superclass:
745+
return (getFirstType()->isBindableTo(getSecondType()) ||
746+
getSecondType()->isBindableTo(getFirstType()));
747+
748+
case RequirementKind::SameType:
749+
return (getFirstType()->isBindableTo(getSecondType()) ||
750+
getSecondType()->isBindableTo(getFirstType()));
751+
}
752+
753+
llvm_unreachable("Bad requirement kind");
754+
}

lib/IDE/CodeCompletion.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,20 +2539,21 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
25392539

25402540
// If the reference is 'async', all types must be 'Sendable'.
25412541
if (implicitlyAsync && T) {
2542+
auto *M = CurrDeclContext->getParentModule();
25422543
if (isa<VarDecl>(VD)) {
2543-
if (!isSendableType(CurrDeclContext, T)) {
2544+
if (!isSendableType(M, T)) {
25442545
NotRecommended = NotRecommendedReason::CrossActorReference;
25452546
}
25462547
} else {
25472548
assert(isa<FuncDecl>(VD) || isa<SubscriptDecl>(VD));
25482549
// Check if the result and the param types are all 'Sendable'.
25492550
auto *AFT = T->castTo<AnyFunctionType>();
2550-
if (!isSendableType(CurrDeclContext, AFT->getResult())) {
2551+
if (!isSendableType(M, AFT->getResult())) {
25512552
NotRecommended = NotRecommendedReason::CrossActorReference;
25522553
} else {
25532554
for (auto &param : AFT->getParams()) {
25542555
Type paramType = param.getPlainType();
2555-
if (!isSendableType(CurrDeclContext, paramType)) {
2556+
if (!isSendableType(M, paramType)) {
25562557
NotRecommended = NotRecommendedReason::CrossActorReference;
25572558
break;
25582559
}

lib/IDE/IDETypeChecking.cpp

Lines changed: 34 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Module.h"
2323
#include "swift/AST/NameLookup.h"
2424
#include "swift/AST/ProtocolConformance.h"
25+
#include "swift/AST/Requirement.h"
2526
#include "swift/AST/SourceFile.h"
2627
#include "swift/AST/Types.h"
2728
#include "swift/Sema/IDETypeChecking.h"
@@ -169,13 +170,13 @@ struct SynthesizedExtensionAnalyzer::Implementation {
169170
bool Unmergable;
170171
unsigned InheritsCount;
171172
std::set<Requirement> Requirements;
172-
void addRequirement(GenericSignature GenericSig,
173-
Type First, Type Second, RequirementKind Kind) {
174-
CanType CanFirst = GenericSig->getCanonicalTypeInContext(First);
175-
CanType CanSecond;
176-
if (Second) CanSecond = GenericSig->getCanonicalTypeInContext(Second);
173+
void addRequirement(GenericSignature GenericSig, swift::Requirement Req) {
174+
auto First = Req.getFirstType();
175+
auto CanFirst = GenericSig->getCanonicalTypeInContext(First);
176+
auto Second = Req.getSecondType();
177+
auto CanSecond = GenericSig->getCanonicalTypeInContext(Second);
177178

178-
Requirements.insert({First, Second, Kind, CanFirst, CanSecond});
179+
Requirements.insert({First, Second, Req.getKind(), CanFirst, CanSecond});
179180
}
180181
bool operator== (const ExtensionMergeInfo& Another) const {
181182
// Trivially unmergeable.
@@ -289,84 +290,53 @@ struct SynthesizedExtensionAnalyzer::Implementation {
289290
ProtocolDecl *BaseProto = OwningExt->getInnermostDeclContext()
290291
->getSelfProtocolDecl();
291292
for (auto Req : Reqs) {
292-
auto Kind = Req.getKind();
293-
294-
// FIXME: Could do something here
295-
if (Kind == RequirementKind::Layout)
293+
// FIXME: Don't skip layout requirements.
294+
if (Req.getKind() == RequirementKind::Layout)
296295
continue;
297296

298-
Type First = Req.getFirstType();
299-
Type Second = Req.getSecondType();
300-
301297
// Skip protocol's Self : <Protocol> requirement.
302298
if (BaseProto &&
303299
Req.getKind() == RequirementKind::Conformance &&
304-
First->isEqual(BaseProto->getSelfInterfaceType()) &&
305-
Second->getAnyNominal() == BaseProto)
300+
Req.getFirstType()->isEqual(BaseProto->getSelfInterfaceType()) &&
301+
Req.getProtocolDecl() == BaseProto)
306302
continue;
307303

308304
if (!BaseType->isExistentialType()) {
309305
// Apply any substitutions we need to map the requirements from a
310306
// a protocol extension to an extension on the conforming type.
311-
First = First.subst(subMap);
312-
Second = Second.subst(subMap);
313-
314-
if (First->hasError() || Second->hasError()) {
307+
auto SubstReq = Req.subst(subMap);
308+
if (!SubstReq) {
315309
// Substitution with interface type bases can only fail
316310
// if a concrete type fails to conform to a protocol.
317311
// In this case, just give up on the extension altogether.
318312
return true;
319313
}
320-
}
321314

322-
assert(!First->hasArchetype() && !Second->hasArchetype());
323-
switch (Kind) {
324-
case RequirementKind::Conformance: {
325-
auto *M = DC->getParentModule();
326-
auto *Proto = Second->castTo<ProtocolType>()->getDecl();
327-
if (!First->isTypeParameter() &&
328-
M->conformsToProtocol(First, Proto).isInvalid())
329-
return true;
330-
if (M->conformsToProtocol(First, Proto).isInvalid())
331-
MergeInfo.addRequirement(GenericSig, First, Second, Kind);
332-
break;
315+
Req = *SubstReq;
333316
}
334317

335-
case RequirementKind::Superclass:
336-
// If the subject type of the requirement is still a type parameter,
337-
// we need to check if the contextual type could possibly be bound to
338-
// the superclass. If not, this extension isn't applicable.
339-
if (First->isTypeParameter()) {
340-
if (!Target->mapTypeIntoContext(First)->isBindableTo(
341-
Target->mapTypeIntoContext(Second))) {
342-
return true;
343-
}
344-
MergeInfo.addRequirement(GenericSig, First, Second, Kind);
345-
break;
346-
}
347-
348-
// If we've substituted in a concrete type for the subject, we can
349-
// check for an exact superclass match, and disregard the extension if
350-
// it missed.
351-
// FIXME: What if it ends being something like `C<U> : C<Int>`?
352-
// Arguably we should allow that to be mirrored with a U == Int
353-
// constraint.
354-
if (!Second->isExactSuperclassOf(First))
355-
return true;
356-
357-
break;
358-
359-
case RequirementKind::SameType:
360-
if (!First->isBindableTo(Second) &&
361-
!Second->isBindableTo(First)) {
318+
assert(!Req.getFirstType()->hasArchetype());
319+
assert(!Req.getSecondType()->hasArchetype());
320+
321+
auto *M = DC->getParentModule();
322+
auto SubstReq = Req.subst(
323+
[&](Type type) -> Type {
324+
if (type->isTypeParameter())
325+
return Target->mapTypeIntoContext(type);
326+
327+
return type;
328+
},
329+
LookUpConformanceInModule(M));
330+
if (!SubstReq)
331+
return true;
332+
333+
// FIXME: Need to handle conditional requirements here!
334+
ArrayRef<Requirement> conditionalRequirements;
335+
if (!SubstReq->isSatisfied(conditionalRequirements)) {
336+
if (!SubstReq->canBeSatisfied())
362337
return true;
363-
} else if (!First->isEqual(Second)) {
364-
MergeInfo.addRequirement(GenericSig, First, Second, Kind);
365-
}
366-
break;
367338

368-
case RequirementKind::Layout:
369-
llvm_unreachable("Handled above");
339+
MergeInfo.addRequirement(GenericSig, Req);
370340
}
371341
}
372342
return false;

0 commit comments

Comments
 (0)