@@ -460,6 +460,9 @@ class TypeRefinementContextBuilder : private ASTWalker {
460460 // Adds in a TRC that covers the entire declaration.
461461 if (auto DeclTRC = getNewContextForSignatureOfDecl (D)) {
462462 pushContext (DeclTRC, D);
463+
464+ // Possibly use this as an effective parent context later.
465+ recordEffectiveParentContext (D, DeclTRC);
463466 }
464467
465468 // Create TRCs that cover only the body of the declaration.
@@ -542,46 +545,63 @@ class TypeRefinementContextBuilder : private ASTWalker {
542545 return nullptr ;
543546 }
544547
545- // A decl only introduces a new context when it either has explicit
546- // availability or requires the deployment target.
547- bool HasExplicitAvailability = hasActiveAvailableAttribute (D, Context);
548- bool ConstrainToDeploymentTarget =
549- shouldConstrainSignatureToDeploymentTarget (D);
550- if (!HasExplicitAvailability && !ConstrainToDeploymentTarget)
551- return nullptr ;
548+ // Declarations with an explicit availability attribute always get a TRC.
549+ if (hasActiveAvailableAttribute (D, Context)) {
550+ AvailabilityContext DeclaredAvailability =
551+ swift::AvailabilityInference::availableRange (D, Context);
552552
553- // We require a valid range in order to be able to query for the TRC
554- // corresponding to a given SourceLoc.
555- // If this assert fires, it means we have probably synthesized an implicit
556- // declaration without location information. The appropriate fix is
557- // probably to gin up a source range for the declaration when synthesizing
558- // it.
559- assert (D->getSourceRange ().isValid ());
553+ return TypeRefinementContext::createForDecl (
554+ Context, D, getCurrentTRC (),
555+ getEffectiveAvailabilityForDeclSignature (D, DeclaredAvailability),
556+ DeclaredAvailability, refinementSourceRangeForDecl (D));
557+ }
560558
561- // The potential versions in the declaration are constrained by both
562- // the declared availability of the declaration and the potential versions
563- // of its lexical context.
564- AvailabilityContext ExplicitDeclInfo =
565- swift::AvailabilityInference::availableRange (D, Context);
566- AvailabilityContext DeclInfo = ExplicitDeclInfo;
567- DeclInfo.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
568-
569- if (ConstrainToDeploymentTarget)
570- DeclInfo.intersectWith (AvailabilityContext::forDeploymentTarget (Context));
571-
572- SourceRange Range = refinementSourceRangeForDecl (D);
573- TypeRefinementContext *NewTRC;
574- if (HasExplicitAvailability)
575- NewTRC = TypeRefinementContext::createForDecl (
576- Context, D, getCurrentTRC (), DeclInfo, ExplicitDeclInfo, Range);
577- else
578- NewTRC = TypeRefinementContext::createForAPIBoundary (
579- Context, D, getCurrentTRC (), DeclInfo, Range);
559+ // Declarations without explicit availability get a TRC if they are
560+ // effectively less available than the surrounding context. For example, an
561+ // internal property in a public struct can be effectively less available
562+ // than the containing struct decl because the internal property will only
563+ // be accessed by code running at the deployment target or later.
564+ AvailabilityContext CurrentAvailability =
565+ getCurrentTRC ()->getAvailabilityInfo ();
566+ AvailabilityContext EffectiveAvailability =
567+ getEffectiveAvailabilityForDeclSignature (D, CurrentAvailability);
568+ if (CurrentAvailability.isSupersetOf (EffectiveAvailability))
569+ return TypeRefinementContext::createForDeclImplicit (
570+ Context, D, getCurrentTRC (), EffectiveAvailability,
571+ refinementSourceRangeForDecl (D));
572+
573+ return nullptr ;
574+ }
580575
581- // Possibly use this as an effective parent context later.
582- recordEffectiveParentContext (D, NewTRC);
576+ AvailabilityContext getEffectiveAvailabilityForDeclSignature (
577+ Decl *D, const AvailabilityContext BaseAvailability) {
578+ AvailabilityContext EffectiveAvailability = BaseAvailability;
579+
580+ // As a special case, extension decls are treated as effectively as
581+ // available as the nominal type they extend, up to the deployment target.
582+ // This rule is a convenience for library authors who have written
583+ // extensions without specifying availabilty on the extension itself.
584+ if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
585+ auto *Nominal = ED->getExtendedNominal ();
586+ if (Nominal && !hasActiveAvailableAttribute (D, Context)) {
587+ EffectiveAvailability.intersectWith (
588+ swift::AvailabilityInference::availableRange (Nominal, Context));
589+
590+ // We want to require availability to be specified on extensions of
591+ // types that would be potentially unavailable to the module containing
592+ // the extension, so limit the effective availability to the deployment
593+ // target.
594+ EffectiveAvailability.unionWith (
595+ AvailabilityContext::forDeploymentTarget (Context));
596+ }
597+ }
583598
584- return NewTRC;
599+ EffectiveAvailability.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
600+ if (shouldConstrainSignatureToDeploymentTarget (D))
601+ EffectiveAvailability.intersectWith (
602+ AvailabilityContext::forDeploymentTarget (Context));
603+
604+ return EffectiveAvailability;
585605 }
586606
587607 // / Checks whether the entire declaration, including its signature, should be
@@ -607,6 +627,14 @@ class TypeRefinementContextBuilder : private ASTWalker {
607627 // / provides a convenient place to specify the refined range when it is
608628 // / different than the declaration's source range.
609629 SourceRange refinementSourceRangeForDecl (Decl *D) {
630+ // We require a valid range in order to be able to query for the TRC
631+ // corresponding to a given SourceLoc.
632+ // If this assert fires, it means we have probably synthesized an implicit
633+ // declaration without location information. The appropriate fix is
634+ // probably to gin up a source range for the declaration when synthesizing
635+ // it.
636+ assert (D->getSourceRange ().isValid ());
637+
610638 if (auto *storageDecl = dyn_cast<AbstractStorageDecl>(D)) {
611639 // Use the declaration's availability for the context when checking
612640 // the bodies of its accessors.
@@ -642,25 +670,26 @@ class TypeRefinementContextBuilder : private ASTWalker {
642670 return D->getSourceRange ();
643671 }
644672
645- TypeRefinementContext *createAPIBoundaryContext (Decl *D, SourceRange range) {
646- AvailabilityContext DeploymentTargetInfo =
647- AvailabilityContext::forDeploymentTarget (Context);
648- DeploymentTargetInfo.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
649-
650- return TypeRefinementContext::createForAPIBoundary (
651- Context, D, getCurrentTRC (), DeploymentTargetInfo, range);
652- }
653-
654673 void buildContextsForBodyOfDecl (Decl *D) {
655674 // Are we already constrained by the deployment target? If not, adding
656675 // new contexts won't change availability.
657676 if (isCurrentTRCContainedByDeploymentTarget ())
658677 return ;
659678
679+ // A lambda that creates an implicit decl TRC specifying the deployment
680+ // target for `range` in decl `D`.
681+ auto createContext = [this ](Decl *D, SourceRange range) {
682+ AvailabilityContext Availability =
683+ AvailabilityContext::forDeploymentTarget (Context);
684+ Availability.intersectWith (getCurrentTRC ()->getAvailabilityInfo ());
685+
686+ return TypeRefinementContext::createForDeclImplicit (
687+ Context, D, getCurrentTRC (), Availability, range);
688+ };
689+
660690 // Top level code always uses the deployment target.
661691 if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
662- auto *topLevelTRC =
663- createAPIBoundaryContext (tlcd, tlcd->getSourceRange ());
692+ auto *topLevelTRC = createContext (tlcd, tlcd->getSourceRange ());
664693 pushContext (topLevelTRC, D);
665694 return ;
666695 }
@@ -670,8 +699,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
670699 if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
671700 if (!afd->isImplicit () && afd->getBodySourceRange ().isValid () &&
672701 afd->getResilienceExpansion () != ResilienceExpansion::Minimal) {
673- auto *functionBodyTRC =
674- createAPIBoundaryContext (afd, afd->getBodySourceRange ());
702+ auto *functionBodyTRC = createContext (afd, afd->getBodySourceRange ());
675703 pushContext (functionBodyTRC, D);
676704 }
677705 return ;
@@ -693,8 +721,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
693721 // Create a TRC for the init written in the source. The ASTWalker
694722 // won't visit these expressions so instead of pushing these onto the
695723 // stack we build them directly.
696- auto *initTRC =
697- createAPIBoundaryContext (vd, initExpr->getSourceRange ());
724+ auto *initTRC = createContext (vd, initExpr->getSourceRange ());
698725 TypeRefinementContextBuilder (initTRC, Context).build (initExpr);
699726 }
700727
@@ -710,7 +737,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
710737 // example, property wrapper initializers that takes block arguments
711738 // are not handled correctly because of this (rdar://77841331).
712739 for (auto *wrapper : vd->getAttachedPropertyWrappers ()) {
713- createAPIBoundaryContext (vd, wrapper->getRange ());
740+ createContext (vd, wrapper->getRange ());
714741 }
715742 }
716743
0 commit comments