Skip to content

Commit 4ee517f

Browse files
committed
AST: Refactor SelfReferenceKind in preparation for #33767
This will enable us to allow members containing references to Self or associated types only in covariant position to be used on an existential
1 parent 826f17e commit 4ee517f

File tree

3 files changed

+165
-126
lines changed

3 files changed

+165
-126
lines changed

include/swift/AST/Decl.h

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3741,54 +3741,79 @@ class ClassDecl final : public NominalTypeDecl {
37413741
}
37423742
};
37433743

3744+
/// A convenience wrapper around the \c SelfReferencePosition::Kind enum.
3745+
struct SelfReferencePosition final {
3746+
enum Kind : uint8_t { None, Covariant, Contravariant, Invariant };
37443747

3745-
/// Describes whether a requirement refers to 'Self', for use in the
3746-
/// is-inheritable and is-available-existential checks.
3747-
struct SelfReferenceKind {
3748-
bool result;
3749-
bool parameter;
3750-
bool other;
3748+
private:
3749+
Kind kind;
37513750

3752-
/// The type does not refer to 'Self' at all.
3753-
static SelfReferenceKind None() {
3754-
return SelfReferenceKind(false, false, false);
3751+
public:
3752+
SelfReferencePosition(Kind kind) : kind(kind) {}
3753+
3754+
SelfReferencePosition flipped() const {
3755+
switch (kind) {
3756+
case None:
3757+
case Invariant:
3758+
return *this;
3759+
case Covariant:
3760+
return Contravariant;
3761+
case Contravariant:
3762+
return Covariant;
3763+
}
37553764
}
37563765

3757-
/// The type refers to 'Self', but only as the type of a property or
3758-
/// the result type of a method/subscript.
3759-
static SelfReferenceKind Result() {
3760-
return SelfReferenceKind(true, false, false);
3761-
}
3766+
explicit operator bool() const { return kind > None; }
37623767

3763-
/// The type refers to 'Self', but only as the parameter type
3764-
/// of a method/subscript.
3765-
static SelfReferenceKind Parameter() {
3766-
return SelfReferenceKind(false, true, false);
3767-
}
3768+
operator Kind() const { return kind; }
3769+
};
37683770

3769-
/// The type refers to 'Self' in a position that is invariant.
3770-
static SelfReferenceKind Other() {
3771-
return SelfReferenceKind(false, false, true);
3771+
/// Describes the least favorable positions at which a requirement refers
3772+
/// to 'Self' in terms of variance, for use in the is-inheritable and
3773+
/// is-available-existential checks.
3774+
struct SelfReferenceInfo final {
3775+
using Position = SelfReferencePosition;
3776+
3777+
bool hasCovariantSelfResult;
3778+
Position selfRef;
3779+
Position assocTypeRef;
3780+
3781+
/// A reference to 'Self'.
3782+
static SelfReferenceInfo forSelfRef(Position position) {
3783+
assert(position);
3784+
return SelfReferenceInfo(false, position, Position::None);
37723785
}
37733786

3774-
SelfReferenceKind flip() const {
3775-
return SelfReferenceKind(parameter, result, other);
3787+
/// A reference to 'Self' through an associated type.
3788+
static SelfReferenceInfo forAssocTypeRef(Position position) {
3789+
assert(position);
3790+
return SelfReferenceInfo(false, Position::None, position);
37763791
}
37773792

3778-
SelfReferenceKind operator|=(SelfReferenceKind kind) {
3779-
result |= kind.result;
3780-
parameter |= kind.parameter;
3781-
other |= kind.other;
3793+
SelfReferenceInfo operator|=(const SelfReferenceInfo &pos) {
3794+
hasCovariantSelfResult |= pos.hasCovariantSelfResult;
3795+
if (pos.selfRef > selfRef) {
3796+
selfRef = pos.selfRef;
3797+
}
3798+
if (pos.assocTypeRef > assocTypeRef) {
3799+
assocTypeRef = pos.assocTypeRef;
3800+
}
37823801
return *this;
37833802
}
37843803

3785-
operator bool() const {
3786-
return result || parameter || other;
3804+
explicit operator bool() const {
3805+
return hasCovariantSelfResult || selfRef || assocTypeRef;
37873806
}
37883807

3808+
SelfReferenceInfo()
3809+
: hasCovariantSelfResult(false), selfRef(Position::None),
3810+
assocTypeRef(Position::None) {}
3811+
37893812
private:
3790-
SelfReferenceKind(bool result, bool parameter, bool other)
3791-
: result(result), parameter(parameter), other(other) { }
3813+
SelfReferenceInfo(bool hasCovariantSelfResult, Position selfRef,
3814+
Position assocTypeRef)
3815+
: hasCovariantSelfResult(hasCovariantSelfResult), selfRef(selfRef),
3816+
assocTypeRef(assocTypeRef) {}
37923817
};
37933818

37943819
/// The set of known protocols for which derived conformances are supported.
@@ -3970,15 +3995,12 @@ class ProtocolDecl final : public NominalTypeDecl {
39703995

39713996
/// Find direct Self references within the given requirement.
39723997
///
3973-
/// \param allowCovariantParameters If true, 'Self' is assumed to be
3974-
/// covariant anywhere; otherwise, only in the return type of the top-level
3975-
/// function type.
3976-
///
3977-
/// \param skipAssocTypes If true, associated types of 'Self' are ignored;
3978-
/// otherwise, they count as an 'other' usage of 'Self'.
3979-
SelfReferenceKind findProtocolSelfReferences(const ValueDecl *decl,
3980-
bool allowCovariantParameters,
3981-
bool skipAssocTypes) const;
3998+
/// \param treatNonResultCovariantSelfAsInvariant If true, 'Self' is only
3999+
/// assumed to be covariant in a top-level non-function type, or in the
4000+
/// eventual result type of a top-level function type.
4001+
SelfReferenceInfo
4002+
findProtocolSelfReferences(const ValueDecl *decl,
4003+
bool treatNonResultCovariantSelfAsInvariant) const;
39824004

39834005
/// Determine whether we are allowed to refer to an existential type
39844006
/// conforming to this protocol. This is only permitted if the type of

lib/AST/Decl.cpp

Lines changed: 94 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4699,148 +4699,172 @@ bool ProtocolDecl::existentialConformsToSelf() const {
46994699
}
47004700

47014701
/// Classify usages of Self in the given type.
4702-
static SelfReferenceKind
4702+
///
4703+
/// \param position The position we are currently at, in terms of variance.
4704+
static SelfReferenceInfo
47034705
findProtocolSelfReferences(const ProtocolDecl *proto, Type type,
4704-
bool skipAssocTypes) {
4706+
SelfReferencePosition position) {
47054707
// Tuples preserve variance.
47064708
if (auto tuple = type->getAs<TupleType>()) {
4707-
auto kind = SelfReferenceKind::None();
4709+
auto info = SelfReferenceInfo();
47084710
for (auto &elt : tuple->getElements()) {
4709-
kind |= findProtocolSelfReferences(proto, elt.getType(), skipAssocTypes);
4711+
info |= findProtocolSelfReferences(proto, elt.getType(), position);
47104712
}
4711-
return kind;
4713+
4714+
// A covariant Self result inside a tuple will not be bona fide.
4715+
info.hasCovariantSelfResult = false;
4716+
4717+
return info;
47124718
}
47134719

47144720
// Function preserve variance in the result type, and flip variance in
47154721
// the parameter type.
47164722
if (auto funcTy = type->getAs<AnyFunctionType>()) {
4717-
auto inputKind = SelfReferenceKind::None();
4723+
auto inputInfo = SelfReferenceInfo();
47184724
for (auto param : funcTy->getParams()) {
47194725
// inout parameters are invariant.
47204726
if (param.isInOut()) {
4721-
if (findProtocolSelfReferences(proto, param.getPlainType(),
4722-
skipAssocTypes)) {
4723-
return SelfReferenceKind::Other();
4724-
}
4727+
inputInfo |= findProtocolSelfReferences(
4728+
proto, param.getPlainType(), SelfReferencePosition::Invariant);
4729+
continue;
47254730
}
4726-
inputKind |= findProtocolSelfReferences(proto, param.getParameterType(),
4727-
skipAssocTypes);
4731+
inputInfo |= findProtocolSelfReferences(proto, param.getParameterType(),
4732+
position.flipped());
47284733
}
4729-
auto resultKind = findProtocolSelfReferences(proto, funcTy->getResult(),
4730-
skipAssocTypes);
47314734

4732-
auto kind = inputKind.flip();
4733-
kind |= resultKind;
4734-
return kind;
4735+
// A covariant Self result inside a parameter will not be bona fide.
4736+
inputInfo.hasCovariantSelfResult = false;
4737+
4738+
auto resultInfo =
4739+
findProtocolSelfReferences(proto, funcTy->getResult(), position);
4740+
if (resultInfo.selfRef == SelfReferencePosition::Covariant) {
4741+
resultInfo.hasCovariantSelfResult = true;
4742+
}
4743+
return inputInfo |= resultInfo;
47354744
}
47364745

47374746
// Metatypes preserve variance.
47384747
if (auto metaTy = type->getAs<MetatypeType>()) {
47394748
return findProtocolSelfReferences(proto, metaTy->getInstanceType(),
4740-
skipAssocTypes);
4749+
position);
47414750
}
47424751

47434752
// Optionals preserve variance.
47444753
if (auto optType = type->getOptionalObjectType()) {
4745-
return findProtocolSelfReferences(proto, optType,
4746-
skipAssocTypes);
4754+
return findProtocolSelfReferences(proto, optType, position);
47474755
}
47484756

47494757
// DynamicSelfType preserves variance.
47504758
// FIXME: This shouldn't ever appear in protocol requirement
47514759
// signatures.
47524760
if (auto selfType = type->getAs<DynamicSelfType>()) {
4753-
return findProtocolSelfReferences(proto, selfType->getSelfType(),
4754-
skipAssocTypes);
4761+
return findProtocolSelfReferences(proto, selfType->getSelfType(), position);
47554762
}
47564763

47574764
// Bound generic types are invariant.
47584765
if (auto boundGenericType = type->getAs<BoundGenericType>()) {
4766+
auto info = SelfReferenceInfo();
47594767
for (auto paramType : boundGenericType->getGenericArgs()) {
4760-
if (findProtocolSelfReferences(proto, paramType,
4761-
skipAssocTypes)) {
4762-
return SelfReferenceKind::Other();
4763-
}
4768+
info |= findProtocolSelfReferences(proto, paramType,
4769+
SelfReferencePosition::Invariant);
47644770
}
4771+
4772+
return info;
47654773
}
47664774

4767-
// A direct reference to 'Self' is covariant.
4775+
// A direct reference to 'Self'.
47684776
if (proto->getSelfInterfaceType()->isEqual(type))
4769-
return SelfReferenceKind::Result();
4777+
return SelfReferenceInfo::forSelfRef(position);
47704778

4771-
// Special handling for associated types.
4772-
if (!skipAssocTypes && type->is<DependentMemberType>()) {
4779+
// A reference to an associated type rooted on 'Self'.
4780+
if (type->is<DependentMemberType>()) {
47734781
type = type->getRootGenericParam();
47744782
if (proto->getSelfInterfaceType()->isEqual(type))
4775-
return SelfReferenceKind::Other();
4783+
return SelfReferenceInfo::forAssocTypeRef(position);
47764784
}
47774785

4778-
return SelfReferenceKind::None();
4786+
return SelfReferenceInfo();
47794787
}
47804788

47814789
/// Find Self references within the given requirement.
4782-
SelfReferenceKind
4783-
ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
4784-
bool allowCovariantParameters,
4785-
bool skipAssocTypes) const {
4790+
SelfReferenceInfo ProtocolDecl::findProtocolSelfReferences(
4791+
const ValueDecl *value, bool treatNonResultCovariantSelfAsInvariant) const {
47864792
// Types never refer to 'Self'.
47874793
if (isa<TypeDecl>(value))
4788-
return SelfReferenceKind::None();
4794+
return SelfReferenceInfo();
47894795

47904796
auto type = value->getInterfaceType();
47914797

47924798
// Skip invalid declarations.
47934799
if (type->hasError())
4794-
return SelfReferenceKind::None();
4800+
return SelfReferenceInfo();
47954801

4796-
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
4797-
// Skip the 'self' parameter.
4798-
type = type->castTo<AnyFunctionType>()->getResult();
4802+
if (isa<AbstractFunctionDecl>(value) || isa<SubscriptDecl>(value)) {
4803+
// For a method, skip the 'self' parameter.
4804+
if (isa<AbstractFunctionDecl>(value))
4805+
type = type->castTo<AnyFunctionType>()->getResult();
47994806

4800-
// Methods of non-final classes can only contain a covariant 'Self'
4801-
// as a function result type.
4802-
if (!allowCovariantParameters) {
4803-
auto inputKind = SelfReferenceKind::None();
4804-
for (auto param : type->castTo<AnyFunctionType>()->getParams()) {
4805-
// inout parameters are invariant.
4806-
if (param.isInOut()) {
4807-
if (::findProtocolSelfReferences(this, param.getPlainType(),
4808-
skipAssocTypes)) {
4809-
return SelfReferenceKind::Other();
4810-
}
4811-
}
4812-
inputKind |= ::findProtocolSelfReferences(this, param.getParameterType(),
4813-
skipAssocTypes);
4807+
auto inputInfo = SelfReferenceInfo();
4808+
for (auto param : type->castTo<AnyFunctionType>()->getParams()) {
4809+
// inout parameters are invariant.
4810+
if (param.isInOut()) {
4811+
inputInfo |= ::findProtocolSelfReferences(
4812+
this, param.getPlainType(), SelfReferencePosition::Invariant);
4813+
continue;
48144814
}
4815+
inputInfo |= ::findProtocolSelfReferences(
4816+
this, param.getParameterType(), SelfReferencePosition::Contravariant);
4817+
}
4818+
4819+
// A covariant Self result inside a parameter will not be bona fide.
4820+
inputInfo.hasCovariantSelfResult = false;
48154821

4816-
if (inputKind.parameter)
4817-
return SelfReferenceKind::Other();
4822+
// FIXME: Rather than having a special flag for the is-inheritable check,
4823+
// ensure non-result covariant Self is always diagnosed during type
4824+
// resolution.
4825+
//
4826+
// Methods of non-final classes can only contain a covariant 'Self'
4827+
// as their result type.
4828+
if (treatNonResultCovariantSelfAsInvariant &&
4829+
inputInfo.selfRef == SelfReferencePosition::Covariant) {
4830+
inputInfo.selfRef = SelfReferencePosition::Invariant;
48184831
}
48194832

4820-
return ::findProtocolSelfReferences(this, type,
4821-
skipAssocTypes);
4833+
auto resultInfo = ::findProtocolSelfReferences(
4834+
this, type->castTo<AnyFunctionType>()->getResult(),
4835+
SelfReferencePosition::Covariant);
4836+
if (resultInfo.selfRef == SelfReferencePosition::Covariant) {
4837+
resultInfo.hasCovariantSelfResult = true;
4838+
}
4839+
4840+
return inputInfo |= resultInfo;
48224841
} else {
4823-
assert(isa<AbstractStorageDecl>(value));
4842+
assert(isa<VarDecl>(value));
48244843

4825-
return ::findProtocolSelfReferences(this, type,
4826-
skipAssocTypes);
4844+
auto info = ::findProtocolSelfReferences(this, type,
4845+
SelfReferencePosition::Covariant);
4846+
if (info.selfRef == SelfReferencePosition::Covariant) {
4847+
info.hasCovariantSelfResult = true;
4848+
}
4849+
4850+
return info;
48274851
}
48284852

4829-
return SelfReferenceKind::None();
4853+
return SelfReferenceInfo();
48304854
}
48314855

48324856
bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
4833-
// If the member type uses 'Self' in non-covariant position,
4834-
// we cannot use the existential type.
4835-
auto selfKind = findProtocolSelfReferences(decl,
4836-
/*allowCovariantParameters=*/true,
4837-
/*skipAssocTypes=*/false);
4838-
if (selfKind.parameter || selfKind.other)
4857+
// If the member type references 'Self' in non-covariant position, or an
4858+
// associated type in any position, we cannot use the existential type.
4859+
const auto info = findProtocolSelfReferences(
4860+
decl, /*treatNonResultCovariantSelfAsInvariant=*/false);
4861+
if (info.selfRef > SelfReferencePosition::Covariant || info.assocTypeRef) {
48394862
return false;
4863+
}
48404864

48414865
// FIXME: Appropriately diagnose assignments instead.
48424866
if (auto *const storageDecl = dyn_cast<AbstractStorageDecl>(decl)) {
4843-
if (selfKind.result && storageDecl->supportsMutation())
4867+
if (info.hasCovariantSelfResult && storageDecl->supportsMutation())
48444868
return false;
48454869
}
48464870

0 commit comments

Comments
 (0)