Skip to content

Commit f43166a

Browse files
Merge pull request #77851 from AnthonyLatsis/day-2
2 parents 99db002 + eb4d4df commit f43166a

17 files changed

+1945
-1198
lines changed

include/swift/AST/Type.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,13 @@ enum class ForeignRepresentableKind : uint8_t {
190190
/// therefore, the result type is in covariant position relative to the function
191191
/// type.
192192
struct TypePosition final {
193-
enum : uint8_t { Covariant, Contravariant, Invariant, Shape };
193+
enum : uint8_t {
194+
Covariant = 1 << 0,
195+
Contravariant = 1 << 1,
196+
Invariant = 1 << 2,
197+
Shape = 1 << 3,
198+
Last_Position = Shape,
199+
};
194200

195201
private:
196202
decltype(Covariant) kind;
@@ -213,6 +219,10 @@ struct TypePosition final {
213219

214220
operator decltype(kind)() const { return kind; }
215221
};
222+
enum : unsigned {
223+
NumTypePositions =
224+
countBitsUsed(static_cast<unsigned>(TypePosition::Last_Position))
225+
};
216226

217227
/// Type - This is a simple value object that contains a pointer to a type
218228
/// class. This is potentially sugared. We use this throughout the codebase

lib/Sema/CSSimplify.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9914,10 +9914,16 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
99149914
const auto isUnsupportedExistentialMemberAccess = [&] {
99159915
// We may not be able to derive a well defined type for an existential
99169916
// member access if the member's signature references 'Self'.
9917-
if (instanceTy->isExistentialType() &&
9918-
decl->getDeclContext()->getSelfProtocolDecl()) {
9919-
if (!isMemberAvailableOnExistential(instanceTy, decl)) {
9917+
if (instanceTy->isExistentialType()) {
9918+
switch (isMemberAvailableOnExistential(instanceTy, decl)) {
9919+
case ExistentialMemberAccessLimitation::Unsupported:
9920+
// TODO: Write-only accesses are not supported yet.
9921+
case ExistentialMemberAccessLimitation::WriteOnly:
99209922
return true;
9923+
9924+
case ExistentialMemberAccessLimitation::ReadOnly:
9925+
case ExistentialMemberAccessLimitation::None:
9926+
break;
99219927
}
99229928
}
99239929

lib/Sema/OpenedExistentials.cpp

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,10 @@ using namespace swift;
2929

3030
GenericParameterReferenceInfo &
3131
GenericParameterReferenceInfo::operator|=(const GenericParameterReferenceInfo &other) {
32-
hasCovariantSelfResult |= other.hasCovariantSelfResult;
33-
if (other.selfRef > selfRef) {
34-
selfRef = other.selfRef;
35-
}
36-
if (other.assocTypeRef > assocTypeRef) {
37-
assocTypeRef = other.assocTypeRef;
38-
}
32+
DirectRefs |= other.DirectRefs;
33+
DepMemberTyRefs |= other.DepMemberTyRefs;
34+
HasCovariantGenericParamResult |= other.HasCovariantGenericParamResult;
35+
3936
return *this;
4037
}
4138

@@ -296,9 +293,9 @@ findGenericParameterReferencesRec(CanGenericSignature genericSig,
296293
// A direct reference to 'Self'.
297294
if (type->is<GenericTypeParamType>()) {
298295
if (position == TypePosition::Covariant && canBeCovariantResult)
299-
return GenericParameterReferenceInfo::forCovariantResult();
296+
return GenericParameterReferenceInfo::forCovariantGenericParamResult();
300297

301-
return GenericParameterReferenceInfo::forSelfRef(position);
298+
return GenericParameterReferenceInfo::forDirectRef(position);
302299
}
303300

304301
if (origParam != openedParam) {
@@ -332,7 +329,7 @@ findGenericParameterReferencesRec(CanGenericSignature genericSig,
332329
}
333330

334331
// A reference to an associated type rooted on 'Self'.
335-
return GenericParameterReferenceInfo::forAssocTypeRef(position);
332+
return GenericParameterReferenceInfo::forDependentMemberTypeRef(position);
336333
}
337334

338335
GenericParameterReferenceInfo
@@ -393,7 +390,7 @@ bool HasSelfOrAssociatedTypeRequirementsRequest::evaluate(
393390
// For value members, look at their type signatures.
394391
if (auto valueMember = dyn_cast<ValueDecl>(member)) {
395392
const auto info = findExistentialSelfReferences(valueMember);
396-
if (info.selfRef > TypePosition::Covariant || info.assocTypeRef) {
393+
if (info.hasNonCovariantRef() || info.hasDependentMemberTypeRef()) {
397394
return true;
398395
}
399396
}
@@ -516,39 +513,81 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
516513
return false;
517514
}
518515

519-
bool swift::isMemberAvailableOnExistential(
520-
Type baseTy, const ValueDecl *member) {
516+
ExistentialMemberAccessLimitation
517+
swift::isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member) {
518+
auto *dc = member->getDeclContext();
519+
if (!dc->getSelfProtocolDecl()) {
520+
return ExistentialMemberAccessLimitation::None;
521+
}
521522

522523
auto &ctx = member->getASTContext();
523524
auto existentialSig = ctx.getOpenedExistentialSignature(baseTy);
524525

525-
auto *dc = member->getDeclContext();
526-
ASSERT(dc->getSelfProtocolDecl());
527-
528526
auto origParam = dc->getSelfInterfaceType()->castTo<GenericTypeParamType>();
529527
auto openedParam = existentialSig.SelfType->castTo<GenericTypeParamType>();
530528

529+
// An accessor or non-storage member is not available if its interface type
530+
// contains a non-covariant reference to a 'Self'-rooted type parameter in the
531+
// context of the base type's existential signature.
531532
auto info = findGenericParameterReferences(
532533
member, existentialSig.OpenedSig, origParam, openedParam,
533534
std::nullopt);
534535

535-
if (info.selfRef > TypePosition::Covariant ||
536-
info.assocTypeRef > TypePosition::Covariant) {
537-
return false;
538-
}
536+
auto result = ExistentialMemberAccessLimitation::None;
537+
if (!info) {
538+
// Nothing to do.
539+
} else if (info.hasRef(TypePosition::Invariant)) {
540+
// An invariant reference is decisive.
541+
result = ExistentialMemberAccessLimitation::Unsupported;
542+
} else if (isa<AbstractFunctionDecl>(member)) {
543+
// Anything non-covariant is decisive for functions.
544+
if (info.hasRef(TypePosition::Contravariant)) {
545+
result = ExistentialMemberAccessLimitation::Unsupported;
546+
}
547+
} else {
548+
const auto isGetterUnavailable = info.hasRef(TypePosition::Contravariant);
549+
auto isSetterUnavailable = true;
550+
551+
if (isa<VarDecl>(member)) {
552+
// For properties, the setter is unavailable if the interface type has a
553+
// covariant reference, which becomes contravariant is the setter.
554+
isSetterUnavailable = info.hasRef(TypePosition::Covariant);
555+
} else {
556+
// For subscripts specifically, we must scan the setter directly because
557+
// whether a covariant reference in the interface type becomes
558+
// contravariant in the setter depends on the location of the reference
559+
// (in the indices or the result type).
560+
auto *setter =
561+
cast<SubscriptDecl>(member)->getAccessor(AccessorKind::Set);
562+
const auto setterInfo = setter ? findGenericParameterReferences(
563+
setter, existentialSig.OpenedSig,
564+
origParam, openedParam, std::nullopt)
565+
: GenericParameterReferenceInfo();
566+
567+
isSetterUnavailable = setterInfo.hasRef(TypePosition::Contravariant);
568+
}
539569

540-
// FIXME: Appropriately diagnose assignments instead.
541-
if (auto *const storageDecl = dyn_cast<AbstractStorageDecl>(member)) {
542-
if (info.hasCovariantSelfResult && storageDecl->supportsMutation())
543-
return false;
570+
if (isGetterUnavailable && isSetterUnavailable) {
571+
result = ExistentialMemberAccessLimitation::Unsupported;
572+
} else if (isGetterUnavailable) {
573+
result = ExistentialMemberAccessLimitation::WriteOnly;
574+
} else if (isSetterUnavailable) {
575+
result = ExistentialMemberAccessLimitation::ReadOnly;
576+
}
544577
}
545578

579+
// If the member access is not supported whatsoever, we are done.
580+
if (result == ExistentialMemberAccessLimitation::Unsupported)
581+
return result;
582+
583+
// Before proceeding with the result, see if we find a generic requirement
584+
// that cannot be satisfied; if we do, the member is unavailable after all.
546585
if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase(existentialSig,
547586
member)) {
548-
return false;
587+
return ExistentialMemberAccessLimitation::Unsupported;
549588
}
550589

551-
return true;
590+
return result;
552591
}
553592

554593
std::optional<std::tuple<GenericTypeParamType *, TypeVariableType *,
@@ -686,8 +725,7 @@ swift::canOpenExistentialCallArgument(ValueDecl *callee, unsigned paramIdx,
686725
callee, existentialSig.OpenedSig, genericParam,
687726
existentialSig.SelfType->castTo<GenericTypeParamType>(),
688727
/*skipParamIdx=*/paramIdx);
689-
if (referenceInfo.selfRef > TypePosition::Covariant ||
690-
referenceInfo.assocTypeRef > TypePosition::Covariant)
728+
if (referenceInfo.hasNonCovariantRef())
691729
return std::nullopt;
692730

693731
return std::make_tuple(genericParam, paramTypeVar, argTy, adjustments);
@@ -898,4 +936,4 @@ Type swift::typeEraseOpenedArchetypesFromEnvironment(
898936

899937
return Type();
900938
});
901-
}
939+
}

lib/Sema/OpenedExistentials.h

Lines changed: 80 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -22,52 +22,82 @@ namespace swift {
2222
class CanGenericSignature;
2323
class GenericTypeParamType;
2424

25-
/// Describes the least favorable positions at which a requirement refers
26-
/// to a given generic parameter in terms of variance, for use in the
27-
/// is-inheritable and is-available-existential checks.
25+
/// Stores the variance positions at which a type references a specific
26+
/// generic parameter.
2827
class GenericParameterReferenceInfo final {
29-
using OptionalTypePosition = OptionalEnum<decltype(TypePosition::Covariant)>;
28+
static constexpr unsigned NumTypePositionBits = 4;
29+
static_assert(NumTypePositions <= NumTypePositionBits,
30+
"Not enough bits to store all cases");
31+
32+
uint8_t DirectRefs : NumTypePositionBits;
33+
uint8_t DepMemberTyRefs : NumTypePositionBits;
34+
35+
/// Whether there is a reference to the generic parameter at hand in covariant
36+
/// result type position. This position is the uncurried interface type of a
37+
/// declaration, stipped of any optionality. For example, this is true for
38+
/// 'Self' in 'func foo(Int) -> () -> Self?'.
39+
bool HasCovariantGenericParamResult;
40+
41+
GenericParameterReferenceInfo(uint8_t DirectRefs, uint8_t DepMemberTyRefs,
42+
bool HasCovariantGenericParamResult)
43+
: DirectRefs(DirectRefs), DepMemberTyRefs(DepMemberTyRefs),
44+
HasCovariantGenericParamResult(HasCovariantGenericParamResult) {}
3045

3146
public:
32-
/// Whether the uncurried interface type of the declaration, stripped of any
33-
/// optionality, is a direct reference to the generic parameter at hand. For
34-
/// example, "func foo(x: Int) -> () -> Self?" has a covariant 'Self' result.
35-
bool hasCovariantSelfResult;
47+
GenericParameterReferenceInfo()
48+
: GenericParameterReferenceInfo(0, 0, false) {}
3649

37-
OptionalTypePosition selfRef;
38-
OptionalTypePosition assocTypeRef;
50+
/// A direct reference to the generic parameter.
51+
static GenericParameterReferenceInfo forDirectRef(TypePosition pos) {
52+
return GenericParameterReferenceInfo(pos, 0, false);
53+
}
3954

40-
/// A reference to 'Self'.
41-
static GenericParameterReferenceInfo forSelfRef(TypePosition position) {
42-
return GenericParameterReferenceInfo(false, position, std::nullopt);
55+
/// A direct reference to the generic parameter in covariant result type
56+
/// position.
57+
static GenericParameterReferenceInfo forCovariantGenericParamResult() {
58+
return GenericParameterReferenceInfo(TypePosition::Covariant, 0, true);
4359
}
4460

45-
/// A reference to the generic parameter in covariant result position.
46-
static GenericParameterReferenceInfo forCovariantResult() {
47-
return GenericParameterReferenceInfo(true, TypePosition::Covariant,
48-
std::nullopt);
61+
/// A reference to a dependent member type rooted on the generic parameter.
62+
static GenericParameterReferenceInfo
63+
forDependentMemberTypeRef(TypePosition pos) {
64+
return GenericParameterReferenceInfo(0, pos, false);
4965
}
5066

51-
/// A reference to 'Self' through an associated type.
52-
static GenericParameterReferenceInfo forAssocTypeRef(TypePosition position) {
53-
return GenericParameterReferenceInfo(false, std::nullopt, position);
67+
bool hasDirectRef(std::optional<TypePosition> pos = std::nullopt) const {
68+
if (!pos) {
69+
return DirectRefs;
70+
}
71+
72+
return DirectRefs & pos.value();
5473
}
5574

56-
GenericParameterReferenceInfo &operator|=(const GenericParameterReferenceInfo &other);
75+
bool hasDependentMemberTypeRef(
76+
std::optional<TypePosition> pos = std::nullopt) const {
77+
if (!pos) {
78+
return DepMemberTyRefs;
79+
}
5780

58-
explicit operator bool() const {
59-
return hasCovariantSelfResult || selfRef || assocTypeRef;
81+
return DepMemberTyRefs & pos.value();
6082
}
6183

62-
GenericParameterReferenceInfo()
63-
: hasCovariantSelfResult(false), selfRef(std::nullopt),
64-
assocTypeRef(std::nullopt) {}
65-
66-
private:
67-
GenericParameterReferenceInfo(bool hasCovariantSelfResult, OptionalTypePosition selfRef,
68-
OptionalTypePosition assocTypeRef)
69-
: hasCovariantSelfResult(hasCovariantSelfResult), selfRef(selfRef),
70-
assocTypeRef(assocTypeRef) {}
84+
bool hasRef(std::optional<TypePosition> pos = std::nullopt) const {
85+
return hasDirectRef(pos) || hasDependentMemberTypeRef(pos);
86+
}
87+
88+
bool hasNonCovariantRef() const {
89+
const uint8_t notCovariant = ~TypePosition::Covariant;
90+
return (DirectRefs & notCovariant) || (DepMemberTyRefs & notCovariant);
91+
}
92+
93+
bool hasCovariantGenericParamResult() const {
94+
return HasCovariantGenericParamResult;
95+
}
96+
97+
GenericParameterReferenceInfo &
98+
operator|=(const GenericParameterReferenceInfo &other);
99+
100+
explicit operator bool() const { return hasRef(); }
71101
};
72102

73103
/// Find references to the given generic parameter in the type signature of the
@@ -84,12 +114,23 @@ findGenericParameterReferences(const ValueDecl *value, CanGenericSignature sig,
84114
/// Find references to 'Self' in the type signature of this declaration.
85115
GenericParameterReferenceInfo findExistentialSelfReferences(const ValueDecl *value);
86116

87-
/// Determine whether referencing the given member on the
88-
/// given existential base type is supported. This is the case only if the
89-
/// type of the member, spelled in the context of \p baseTy, does not contain
90-
/// 'Self' or 'Self'-rooted dependent member types in non-covariant position.
91-
bool isMemberAvailableOnExistential(Type baseTy,
92-
const ValueDecl *member);
117+
/// Describes the limitation on accessing a protocol member on a value of
118+
/// existential type.
119+
enum class ExistentialMemberAccessLimitation : uint8_t {
120+
/// The member can be freely accessed on the existential.
121+
None,
122+
/// The storage member is available only for reads.
123+
ReadOnly,
124+
/// The storage member is available only for writes.
125+
WriteOnly,
126+
/// Accessing the member on the existential is not supported.
127+
Unsupported,
128+
};
129+
130+
/// Compute the limitations on accessing the given member on a value of the
131+
/// given existential base type.
132+
ExistentialMemberAccessLimitation
133+
isMemberAvailableOnExistential(Type baseTy, const ValueDecl *member);
93134

94135
/// Flags that should be applied to the existential argument type after
95136
/// opening.
@@ -138,4 +179,4 @@ Type typeEraseOpenedArchetypesFromEnvironment(Type type,
138179

139180
} // end namespace swift
140181

141-
#endif
182+
#endif

0 commit comments

Comments
 (0)