@@ -103,6 +103,13 @@ class AvailabilityScopeBuilder : private ASTWalker {
103103 return availability;
104104 }
105105
106+ const AvailabilityContext constrainCurrentAvailabilityWithContext (
107+ const AvailabilityContext &otherContext) {
108+ auto availability = getCurrentScope ()->getAvailabilityContext ();
109+ availability.constrainWithContext (otherContext, Context);
110+ return availability;
111+ }
112+
106113 void pushContext (AvailabilityScope *scope, ParentTy popAfterNode) {
107114 ContextInfo info;
108115 info.Scope = scope;
@@ -701,16 +708,16 @@ class AvailabilityScopeBuilder : private ASTWalker {
701708 // / There is no need for the caller to explicitly traverse the children
702709 // / of this node.
703710 void buildIfStmtRefinementContext (IfStmt *ifStmt) {
704- std::optional<AvailabilityRange> thenRange ;
705- std::optional<AvailabilityRange> elseRange ;
706- std::tie (thenRange, elseRange ) =
711+ std::optional<AvailabilityContext> thenContext ;
712+ std::optional<AvailabilityContext> elseContext ;
713+ std::tie (thenContext, elseContext ) =
707714 buildStmtConditionRefinementContext (ifStmt->getCond ());
708715
709- if (thenRange .has_value ()) {
716+ if (thenContext .has_value ()) {
710717 // Create a new context for the Then branch and traverse it in that new
711718 // context.
712719 auto availabilityContext =
713- constrainCurrentAvailabilityWithPlatformRange (thenRange. value () );
720+ constrainCurrentAvailabilityWithContext (*thenContext );
714721 auto *thenScope = AvailabilityScope::createForIfStmtThen (
715722 Context, ifStmt, getCurrentDeclContext (), getCurrentScope (),
716723 availabilityContext);
@@ -730,11 +737,11 @@ class AvailabilityScopeBuilder : private ASTWalker {
730737 // the current platform and minimum deployment target.
731738 // If we add a more precise version range lattice (i.e., one that can
732739 // support "<") we should create non-empty contexts for the Else branch.
733- if (elseRange .has_value ()) {
740+ if (elseContext .has_value ()) {
734741 // Create a new context for the Then branch and traverse it in that new
735742 // context.
736743 auto availabilityContext =
737- constrainCurrentAvailabilityWithPlatformRange (elseRange. value () );
744+ constrainCurrentAvailabilityWithContext (*elseContext );
738745 auto *elseScope = AvailabilityScope::createForIfStmtElse (
739746 Context, ifStmt, getCurrentDeclContext (), getCurrentScope (),
740747 availabilityContext);
@@ -749,14 +756,14 @@ class AvailabilityScopeBuilder : private ASTWalker {
749756 // / There is no need for the caller to explicitly traverse the children
750757 // / of this node.
751758 void buildWhileStmtRefinementContext (WhileStmt *whileStmt) {
752- std::optional<AvailabilityRange> bodyRange =
759+ std::optional<AvailabilityContext> bodyContext =
753760 buildStmtConditionRefinementContext (whileStmt->getCond ()).first ;
754761
755- if (bodyRange .has_value ()) {
762+ if (bodyContext .has_value ()) {
756763 // Create a new context for the body and traverse it in the new
757764 // context.
758765 auto availabilityContext =
759- constrainCurrentAvailabilityWithPlatformRange (bodyRange. value () );
766+ constrainCurrentAvailabilityWithContext (*bodyContext );
760767 auto *bodyScope = AvailabilityScope::createForWhileStmtBody (
761768 Context, whileStmt, getCurrentDeclContext (), getCurrentScope (),
762769 availabilityContext);
@@ -783,15 +790,15 @@ class AvailabilityScopeBuilder : private ASTWalker {
783790 // This is slightly tricky because, unlike our other control constructs,
784791 // the refined region is not lexically contained inside the construct
785792 // introducing the availability scope.
786- std::optional<AvailabilityRange> fallthroughRange ;
787- std::optional<AvailabilityRange> elseRange ;
788- std::tie (fallthroughRange, elseRange ) =
793+ std::optional<AvailabilityContext> fallthroughContext ;
794+ std::optional<AvailabilityContext> elseContext ;
795+ std::tie (fallthroughContext, elseContext ) =
789796 buildStmtConditionRefinementContext (guardStmt->getCond ());
790797
791798 if (Stmt *elseBody = guardStmt->getBody ()) {
792- if (elseRange .has_value ()) {
799+ if (elseContext .has_value ()) {
793800 auto availabilityContext =
794- constrainCurrentAvailabilityWithPlatformRange (elseRange. value () );
801+ constrainCurrentAvailabilityWithContext (*elseContext );
795802 auto *trueScope = AvailabilityScope::createForGuardStmtElse (
796803 Context, guardStmt, getCurrentDeclContext (), getCurrentScope (),
797804 availabilityContext);
@@ -804,12 +811,12 @@ class AvailabilityScopeBuilder : private ASTWalker {
804811
805812 auto *parentBrace = dyn_cast<BraceStmt>(Parent.getAsStmt ());
806813 assert (parentBrace && " Expected parent of GuardStmt to be BraceStmt" );
807- if (!fallthroughRange .has_value ())
814+ if (!fallthroughContext .has_value ())
808815 return ;
809816
810817 // Create a new context for the fallthrough.
811818 auto fallthroughAvailability =
812- constrainCurrentAvailabilityWithPlatformRange (fallthroughRange. value () );
819+ constrainCurrentAvailabilityWithContext (*fallthroughContext );
813820 auto *fallthroughScope = AvailabilityScope::createForGuardStmtFallthrough (
814821 Context, guardStmt, parentBrace, getCurrentDeclContext (),
815822 getCurrentScope (), fallthroughAvailability);
@@ -818,10 +825,11 @@ class AvailabilityScopeBuilder : private ASTWalker {
818825 }
819826
820827 // / Build the availability scopes for a StmtCondition and return a pair of
821- // / optional version ranges, the first for the true branch and the second
822- // / for the false branch. A value of `nullopt` for a given branch indicates
823- // / that the branch does not introduce a new scope.
824- std::pair<std::optional<AvailabilityRange>, std::optional<AvailabilityRange>>
828+ // / optional availability contexts, the first for the true branch and the
829+ // / second for the false branch. A value of `nullopt` for a given branch
830+ // / indicates that the branch does not introduce a new scope.
831+ std::pair<std::optional<AvailabilityContext>,
832+ std::optional<AvailabilityContext>>
825833 buildStmtConditionRefinementContext (StmtCondition cond) {
826834 if (Context.LangOpts .DisableAvailabilityChecking )
827835 return {};
@@ -835,8 +843,14 @@ class AvailabilityScopeBuilder : private ASTWalker {
835843 // for the StmtCondition.
836844 unsigned nestedCount = 0 ;
837845
838- // Tracks the potential version range when the condition is false.
839- auto falseFlow = AvailabilityRange::neverAvailable ();
846+ // Tracks the availability of the region in which the condition is false.
847+ // By default, we assume the false flow is unreachable and therefore start
848+ // with no platform versions available in the context. If we find that it
849+ // is actually reachable, the context must expand to cover the potentially
850+ // available range.
851+ auto falseFlowContext = getCurrentScope ()->getAvailabilityContext ();
852+ falseFlowContext.constrainWithPlatformRange (
853+ AvailabilityRange::neverAvailable (), Context);
840854
841855 AvailabilityScope *startingScope = getCurrentScope ();
842856
@@ -845,7 +859,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
845859
846860 for (StmtConditionElement element : cond) {
847861 auto *currentScope = getCurrentScope ();
848- auto currentInfo = currentScope->getPlatformAvailabilityRange ();
862+ auto currentContext = currentScope->getAvailabilityContext ();
849863
850864 // If the element is not a condition, walk it in the current scope.
851865 if (element.getKind () != StmtConditionElement::CK_Availability) {
@@ -854,7 +868,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
854868 // potentially be false, so conservatively combine the version
855869 // range of the current context with the accumulated false flow
856870 // of all other conjuncts.
857- falseFlow.unionWith (currentInfo);
871+ // FIXME: [availability] Fully union with current context.
872+ falseFlowContext.unionWithPlatformRange (
873+ currentContext.getPlatformRange (), Context);
858874
859875 element.walk (*this );
860876 continue ;
@@ -956,7 +972,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
956972 }
957973 }
958974
959- if (currentInfo.isContainedIn (newConstraint)) {
975+ auto newContext = currentContext;
976+ newContext.constrainWithPlatformRange (newConstraint, Context);
977+ if (currentContext.isContainedIn (newContext)) {
960978 // No need to actually create the availability scope if we know it is
961979 // useless.
962980 continue ;
@@ -967,38 +985,38 @@ class AvailabilityScopeBuilder : private ASTWalker {
967985 // context.
968986 // We could be more precise here if we enriched the lattice to include
969987 // ranges of the form [x, y).
970- falseFlow.unionWith (currentInfo);
988+ // FIXME: [availability] Fully union with current context.
989+ falseFlowContext.unionWithPlatformRange (currentContext.getPlatformRange (),
990+ Context);
971991
972- auto constrainedAvailability =
973- constrainCurrentAvailabilityWithPlatformRange (newConstraint);
974992 auto *scope = AvailabilityScope::createForConditionFollowingQuery (
975993 Context, query, lastElement, getCurrentDeclContext (), currentScope,
976- constrainedAvailability );
994+ newContext );
977995
978996 pushContext (scope, ParentTy ());
979997 ++nestedCount;
980998 }
981999
982- std::optional<AvailabilityRange > falseRefinement = std::nullopt ;
1000+ std::optional<AvailabilityContext > falseRefinement = std::nullopt ;
9831001 // The version range for the false branch should never have any versions
9841002 // that weren't possible when the condition started evaluating.
985- assert (
986- falseFlow. isContainedIn ( startingScope->getPlatformAvailabilityRange ()));
1003+ assert (falseFlowContext. isContainedIn (
1004+ startingScope->getAvailabilityContext ()));
9871005
988- // If the starting version range is not completely contained in the
989- // false flow version range then it must be the case that false flow range
990- // is strictly smaller than the starting range (because the false flow
991- // range *is* contained in the starting range ), so we should introduce a
1006+ // If the starting availability context is not completely contained in the
1007+ // false flow context then it must be the case that false flow context
1008+ // is strictly smaller than the starting context (because the false flow
1009+ // context *is* contained in the starting context ), so we should introduce a
9921010 // new availability scope for the false flow.
993- if (!startingScope->getPlatformAvailabilityRange ().isContainedIn (
994- falseFlow )) {
995- falseRefinement = falseFlow ;
1011+ if (!startingScope->getAvailabilityContext ().isContainedIn (
1012+ falseFlowContext )) {
1013+ falseRefinement = falseFlowContext ;
9961014 }
9971015
9981016 auto makeResult =
999- [isUnavailability](std::optional<AvailabilityRange > trueRefinement,
1000- std::optional<AvailabilityRange > falseRefinement) {
1001- if (isUnavailability.has_value () && isUnavailability. value () ) {
1017+ [isUnavailability](std::optional<AvailabilityContext > trueRefinement,
1018+ std::optional<AvailabilityContext > falseRefinement) {
1019+ if (isUnavailability.has_value () && * isUnavailability) {
10021020 // If this is an unavailability check, invert the result.
10031021 return std::make_pair (falseRefinement, trueRefinement);
10041022 }
@@ -1014,8 +1032,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
10141032
10151033 assert (getCurrentScope () == startingScope);
10161034
1017- return makeResult (nestedScope->getPlatformAvailabilityRange (),
1018- falseRefinement);
1035+ return makeResult (nestedScope->getAvailabilityContext (), falseRefinement);
10191036 }
10201037
10211038 // / Return the best active spec for the target platform or nullptr if no
0 commit comments