@@ -103,6 +103,13 @@ class AvailabilityScopeBuilder : private ASTWalker {
103
103
return availability;
104
104
}
105
105
106
+ const AvailabilityContext constrainCurrentAvailabilityWithContext (
107
+ const AvailabilityContext &otherContext) {
108
+ auto availability = getCurrentScope ()->getAvailabilityContext ();
109
+ availability.constrainWithContext (otherContext, Context);
110
+ return availability;
111
+ }
112
+
106
113
void pushContext (AvailabilityScope *scope, ParentTy popAfterNode) {
107
114
ContextInfo info;
108
115
info.Scope = scope;
@@ -701,16 +708,16 @@ class AvailabilityScopeBuilder : private ASTWalker {
701
708
// / There is no need for the caller to explicitly traverse the children
702
709
// / of this node.
703
710
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 ) =
707
714
buildStmtConditionRefinementContext (ifStmt->getCond ());
708
715
709
- if (thenRange .has_value ()) {
716
+ if (thenContext .has_value ()) {
710
717
// Create a new context for the Then branch and traverse it in that new
711
718
// context.
712
719
auto availabilityContext =
713
- constrainCurrentAvailabilityWithPlatformRange (thenRange. value () );
720
+ constrainCurrentAvailabilityWithContext (*thenContext );
714
721
auto *thenScope = AvailabilityScope::createForIfStmtThen (
715
722
Context, ifStmt, getCurrentDeclContext (), getCurrentScope (),
716
723
availabilityContext);
@@ -730,11 +737,11 @@ class AvailabilityScopeBuilder : private ASTWalker {
730
737
// the current platform and minimum deployment target.
731
738
// If we add a more precise version range lattice (i.e., one that can
732
739
// support "<") we should create non-empty contexts for the Else branch.
733
- if (elseRange .has_value ()) {
740
+ if (elseContext .has_value ()) {
734
741
// Create a new context for the Then branch and traverse it in that new
735
742
// context.
736
743
auto availabilityContext =
737
- constrainCurrentAvailabilityWithPlatformRange (elseRange. value () );
744
+ constrainCurrentAvailabilityWithContext (*elseContext );
738
745
auto *elseScope = AvailabilityScope::createForIfStmtElse (
739
746
Context, ifStmt, getCurrentDeclContext (), getCurrentScope (),
740
747
availabilityContext);
@@ -749,14 +756,14 @@ class AvailabilityScopeBuilder : private ASTWalker {
749
756
// / There is no need for the caller to explicitly traverse the children
750
757
// / of this node.
751
758
void buildWhileStmtRefinementContext (WhileStmt *whileStmt) {
752
- std::optional<AvailabilityRange> bodyRange =
759
+ std::optional<AvailabilityContext> bodyContext =
753
760
buildStmtConditionRefinementContext (whileStmt->getCond ()).first ;
754
761
755
- if (bodyRange .has_value ()) {
762
+ if (bodyContext .has_value ()) {
756
763
// Create a new context for the body and traverse it in the new
757
764
// context.
758
765
auto availabilityContext =
759
- constrainCurrentAvailabilityWithPlatformRange (bodyRange. value () );
766
+ constrainCurrentAvailabilityWithContext (*bodyContext );
760
767
auto *bodyScope = AvailabilityScope::createForWhileStmtBody (
761
768
Context, whileStmt, getCurrentDeclContext (), getCurrentScope (),
762
769
availabilityContext);
@@ -783,15 +790,15 @@ class AvailabilityScopeBuilder : private ASTWalker {
783
790
// This is slightly tricky because, unlike our other control constructs,
784
791
// the refined region is not lexically contained inside the construct
785
792
// 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 ) =
789
796
buildStmtConditionRefinementContext (guardStmt->getCond ());
790
797
791
798
if (Stmt *elseBody = guardStmt->getBody ()) {
792
- if (elseRange .has_value ()) {
799
+ if (elseContext .has_value ()) {
793
800
auto availabilityContext =
794
- constrainCurrentAvailabilityWithPlatformRange (elseRange. value () );
801
+ constrainCurrentAvailabilityWithContext (*elseContext );
795
802
auto *trueScope = AvailabilityScope::createForGuardStmtElse (
796
803
Context, guardStmt, getCurrentDeclContext (), getCurrentScope (),
797
804
availabilityContext);
@@ -804,12 +811,12 @@ class AvailabilityScopeBuilder : private ASTWalker {
804
811
805
812
auto *parentBrace = dyn_cast<BraceStmt>(Parent.getAsStmt ());
806
813
assert (parentBrace && " Expected parent of GuardStmt to be BraceStmt" );
807
- if (!fallthroughRange .has_value ())
814
+ if (!fallthroughContext .has_value ())
808
815
return ;
809
816
810
817
// Create a new context for the fallthrough.
811
818
auto fallthroughAvailability =
812
- constrainCurrentAvailabilityWithPlatformRange (fallthroughRange. value () );
819
+ constrainCurrentAvailabilityWithContext (*fallthroughContext );
813
820
auto *fallthroughScope = AvailabilityScope::createForGuardStmtFallthrough (
814
821
Context, guardStmt, parentBrace, getCurrentDeclContext (),
815
822
getCurrentScope (), fallthroughAvailability);
@@ -818,10 +825,11 @@ class AvailabilityScopeBuilder : private ASTWalker {
818
825
}
819
826
820
827
// / 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>>
825
833
buildStmtConditionRefinementContext (StmtCondition cond) {
826
834
if (Context.LangOpts .DisableAvailabilityChecking )
827
835
return {};
@@ -835,8 +843,14 @@ class AvailabilityScopeBuilder : private ASTWalker {
835
843
// for the StmtCondition.
836
844
unsigned nestedCount = 0 ;
837
845
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);
840
854
841
855
AvailabilityScope *startingScope = getCurrentScope ();
842
856
@@ -845,7 +859,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
845
859
846
860
for (StmtConditionElement element : cond) {
847
861
auto *currentScope = getCurrentScope ();
848
- auto currentInfo = currentScope->getPlatformAvailabilityRange ();
862
+ auto currentContext = currentScope->getAvailabilityContext ();
849
863
850
864
// If the element is not a condition, walk it in the current scope.
851
865
if (element.getKind () != StmtConditionElement::CK_Availability) {
@@ -854,7 +868,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
854
868
// potentially be false, so conservatively combine the version
855
869
// range of the current context with the accumulated false flow
856
870
// of all other conjuncts.
857
- falseFlow.unionWith (currentInfo);
871
+ // FIXME: [availability] Fully union with current context.
872
+ falseFlowContext.unionWithPlatformRange (
873
+ currentContext.getPlatformRange (), Context);
858
874
859
875
element.walk (*this );
860
876
continue ;
@@ -956,7 +972,9 @@ class AvailabilityScopeBuilder : private ASTWalker {
956
972
}
957
973
}
958
974
959
- if (currentInfo.isContainedIn (newConstraint)) {
975
+ auto newContext = currentContext;
976
+ newContext.constrainWithPlatformRange (newConstraint, Context);
977
+ if (currentContext.isContainedIn (newContext)) {
960
978
// No need to actually create the availability scope if we know it is
961
979
// useless.
962
980
continue ;
@@ -967,38 +985,38 @@ class AvailabilityScopeBuilder : private ASTWalker {
967
985
// context.
968
986
// We could be more precise here if we enriched the lattice to include
969
987
// 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);
971
991
972
- auto constrainedAvailability =
973
- constrainCurrentAvailabilityWithPlatformRange (newConstraint);
974
992
auto *scope = AvailabilityScope::createForConditionFollowingQuery (
975
993
Context, query, lastElement, getCurrentDeclContext (), currentScope,
976
- constrainedAvailability );
994
+ newContext );
977
995
978
996
pushContext (scope, ParentTy ());
979
997
++nestedCount;
980
998
}
981
999
982
- std::optional<AvailabilityRange > falseRefinement = std::nullopt;
1000
+ std::optional<AvailabilityContext > falseRefinement = std::nullopt;
983
1001
// The version range for the false branch should never have any versions
984
1002
// that weren't possible when the condition started evaluating.
985
- assert (
986
- falseFlow. isContainedIn ( startingScope->getPlatformAvailabilityRange ()));
1003
+ assert (falseFlowContext. isContainedIn (
1004
+ startingScope->getAvailabilityContext ()));
987
1005
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
992
1010
// 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 ;
996
1014
}
997
1015
998
1016
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) {
1002
1020
// If this is an unavailability check, invert the result.
1003
1021
return std::make_pair (falseRefinement, trueRefinement);
1004
1022
}
@@ -1014,8 +1032,7 @@ class AvailabilityScopeBuilder : private ASTWalker {
1014
1032
1015
1033
assert (getCurrentScope () == startingScope);
1016
1034
1017
- return makeResult (nestedScope->getPlatformAvailabilityRange (),
1018
- falseRefinement);
1035
+ return makeResult (nestedScope->getAvailabilityContext (), falseRefinement);
1019
1036
}
1020
1037
1021
1038
// / Return the best active spec for the target platform or nullptr if no
0 commit comments