@@ -513,8 +513,8 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
513
513
return false ;
514
514
}
515
515
516
- bool swift::isMemberAvailableOnExistential (
517
- Type baseTy, const ValueDecl *member) {
516
+ ExistentialMemberAccessLimitation
517
+ swift::isMemberAvailableOnExistential ( Type baseTy, const ValueDecl *member) {
518
518
519
519
auto &ctx = member->getASTContext ();
520
520
auto existentialSig = ctx.getOpenedExistentialSignature (baseTy);
@@ -525,27 +525,68 @@ bool swift::isMemberAvailableOnExistential(
525
525
auto origParam = dc->getSelfInterfaceType ()->castTo <GenericTypeParamType>();
526
526
auto openedParam = existentialSig.SelfType ->castTo <GenericTypeParamType>();
527
527
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.
528
531
auto info = findGenericParameterReferences (
529
532
member, existentialSig.OpenedSig , origParam, openedParam,
530
533
std::nullopt);
531
534
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
+ }
535
568
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
+ }
541
576
}
542
577
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.
543
584
if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase (existentialSig,
544
585
member)) {
545
- return false ;
586
+ return ExistentialMemberAccessLimitation::Unsupported ;
546
587
}
547
588
548
- return true ;
589
+ return result ;
549
590
}
550
591
551
592
std::optional<std::tuple<GenericTypeParamType *, TypeVariableType *,
0 commit comments