@@ -513,8 +513,8 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
513513 return false ;
514514}
515515
516- bool swift::isMemberAvailableOnExistential (
517- Type baseTy, const ValueDecl *member) {
516+ ExistentialMemberAccessLimitation
517+ swift::isMemberAvailableOnExistential ( Type baseTy, const ValueDecl *member) {
518518
519519 auto &ctx = member->getASTContext ();
520520 auto existentialSig = ctx.getOpenedExistentialSignature (baseTy);
@@ -525,27 +525,68 @@ bool swift::isMemberAvailableOnExistential(
525525 auto origParam = dc->getSelfInterfaceType ()->castTo <GenericTypeParamType>();
526526 auto openedParam = existentialSig.SelfType ->castTo <GenericTypeParamType>();
527527
528+ // An accessor or non-storage member is not available if its interface type
529+ // contains a non-covariant reference to a 'Self'-rooted type parameter in the
530+ // context of the base type's existential signature.
528531 auto info = findGenericParameterReferences (
529532 member, existentialSig.OpenedSig , origParam, openedParam,
530533 std::nullopt );
531534
532- if (info.hasNonCovariantRef ()) {
533- return false ;
534- }
535+ auto result = ExistentialMemberAccessLimitation::None;
536+ if (!info) {
537+ // Nothing to do.
538+ } else if (info.hasRef (TypePosition::Invariant)) {
539+ // An invariant reference is decisive.
540+ result = ExistentialMemberAccessLimitation::Unsupported;
541+ } else if (isa<AbstractFunctionDecl>(member)) {
542+ // Anything non-covariant is decisive for functions.
543+ if (info.hasRef (TypePosition::Contravariant)) {
544+ result = ExistentialMemberAccessLimitation::Unsupported;
545+ }
546+ } else {
547+ const auto isGetterUnavailable = info.hasRef (TypePosition::Contravariant);
548+ auto isSetterUnavailable = true ;
549+
550+ if (isa<VarDecl>(member)) {
551+ // For properties, the setter is unavailable if the interface type has a
552+ // covariant reference, which becomes contravariant is the setter.
553+ isSetterUnavailable = info.hasRef (TypePosition::Covariant);
554+ } else {
555+ // For subscripts specifically, we must scan the setter directly because
556+ // whether a covariant reference in the interface type becomes
557+ // contravariant in the setter depends on the location of the reference
558+ // (in the indices or the result type).
559+ auto *setter =
560+ cast<SubscriptDecl>(member)->getAccessor (AccessorKind::Set);
561+ const auto setterInfo = setter ? findGenericParameterReferences (
562+ setter, existentialSig.OpenedSig ,
563+ origParam, openedParam, std::nullopt )
564+ : GenericParameterReferenceInfo ();
565+
566+ isSetterUnavailable = setterInfo.hasRef (TypePosition::Contravariant);
567+ }
535568
536- // FIXME: Appropriately diagnose assignments instead.
537- if (auto *const storageDecl = dyn_cast<AbstractStorageDecl>(member)) {
538- if (info.hasCovariantGenericParamResult () &&
539- storageDecl->supportsMutation ())
540- return false ;
569+ if (isGetterUnavailable && isSetterUnavailable) {
570+ result = ExistentialMemberAccessLimitation::Unsupported;
571+ } else if (isGetterUnavailable) {
572+ result = ExistentialMemberAccessLimitation::WriteOnly;
573+ } else if (isSetterUnavailable) {
574+ result = ExistentialMemberAccessLimitation::ReadOnly;
575+ }
541576 }
542577
578+ // If the member access is not supported whatsoever, we are done.
579+ if (result == ExistentialMemberAccessLimitation::Unsupported)
580+ return result;
581+
582+ // Before proceeding with the result, see if we find a generic requirement
583+ // that cannot be satisfied; if we do, the member is unavailable after all.
543584 if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase (existentialSig,
544585 member)) {
545- return false ;
586+ return ExistentialMemberAccessLimitation::Unsupported ;
546587 }
547588
548- return true ;
589+ return result ;
549590}
550591
551592std::optional<std::tuple<GenericTypeParamType *, TypeVariableType *,
0 commit comments