@@ -824,6 +824,77 @@ class AvailabilityScopeBuilder : private ASTWalker {
824
824
pushContext (fallthroughScope, parentBrace);
825
825
}
826
826
827
+ AvailabilityQuery buildAvailabilityQuery (
828
+ const SemanticAvailabilitySpec spec,
829
+ const std::optional<SemanticAvailabilitySpec> &variantSpec,
830
+ bool isUnavailability) {
831
+ auto domain = spec.getDomain ();
832
+
833
+ // Variant availability specfications are only supported for platform
834
+ // domains when compiling with a -target-variant.
835
+ if (!Context.LangOpts .TargetVariant )
836
+ ASSERT (!variantSpec);
837
+
838
+ auto runtimeRangeForSpec =
839
+ [](const std::optional<SemanticAvailabilitySpec> &spec)
840
+ -> std::optional<AvailabilityRange> {
841
+ if (!spec || spec->isWildcard () || !spec->getDomain ().isVersioned ())
842
+ return std::nullopt;
843
+
844
+ return AvailabilityRange (spec->getRuntimeVersion ());
845
+ };
846
+
847
+ auto primaryRange = runtimeRangeForSpec (spec);
848
+ auto variantRange = runtimeRangeForSpec (variantSpec);
849
+
850
+ switch (domain.getKind ()) {
851
+ case AvailabilityDomain::Kind::Embedded:
852
+ case AvailabilityDomain::Kind::SwiftLanguage:
853
+ case AvailabilityDomain::Kind::PackageDescription:
854
+ // These domains don't support queries.
855
+ llvm::report_fatal_error (" unsupported domain" );
856
+
857
+ case AvailabilityDomain::Kind::Universal:
858
+ DEBUG_ASSERT (spec.isWildcard ());
859
+
860
+ // If all of the specs that matched are '*', then the query trivially
861
+ // evaluates to "true" at compile time.
862
+ if (!variantRange)
863
+ return AvailabilityQuery::constant (domain, isUnavailability, true );
864
+
865
+ // Otherwise, generate a dynamic query for the variant spec. For example,
866
+ // when compiling zippered for macOS, this should generate a query that
867
+ // just checks the iOS version at runtime:
868
+ //
869
+ // if #available(iOS 18, *) { ... }
870
+ //
871
+ return AvailabilityQuery::dynamic (variantSpec->getDomain (),
872
+ isUnavailability, primaryRange,
873
+ variantRange);
874
+
875
+ case AvailabilityDomain::Kind::Platform:
876
+ // Platform checks are always dynamic. The SIL optimizer is responsible
877
+ // eliminating these checks when it can prove that they can never fail
878
+ // (due to the deployment target). We can't perform that analysis here
879
+ // because it may depend on inlining.
880
+ return AvailabilityQuery::dynamic (domain, isUnavailability, primaryRange,
881
+ variantRange);
882
+ case AvailabilityDomain::Kind::Custom:
883
+ auto customDomain = domain.getCustomDomain ();
884
+ ASSERT (customDomain);
885
+
886
+ switch (customDomain->getKind ()) {
887
+ case CustomAvailabilityDomain::Kind::Enabled:
888
+ return AvailabilityQuery::constant (domain, isUnavailability, true );
889
+ case CustomAvailabilityDomain::Kind::Disabled:
890
+ return AvailabilityQuery::constant (domain, isUnavailability, false );
891
+ case CustomAvailabilityDomain::Kind::Dynamic:
892
+ return AvailabilityQuery::dynamic (domain, isUnavailability,
893
+ primaryRange, variantRange);
894
+ }
895
+ }
896
+ }
897
+
827
898
// / Build the availability scopes for a StmtCondition and return a pair of
828
899
// / optional availability contexts, the first for the true branch and the
829
900
// / second for the false branch. A value of `nullopt` for a given branch
@@ -982,23 +1053,18 @@ class AvailabilityScopeBuilder : private ASTWalker {
982
1053
continue ;
983
1054
}
984
1055
985
- auto runtimeQueryRange = runtimeQueryRangeForSpec (*spec);
986
- query->setAvailableRange (runtimeQueryRange.getRawVersionRange ());
987
-
988
1056
// When compiling zippered for macCatalyst, we need to collect both
989
1057
// a macOS version (the target version) and an iOS/macCatalyst version
990
1058
// (the target-variant). These versions will both be passed to a runtime
991
1059
// entrypoint that will check either the macOS version or the iOS
992
1060
// version depending on the kind of process this code is loaded into.
993
- if (Context.LangOpts .TargetVariant ) {
994
- auto variantSpec =
995
- bestActiveSpecForQuery (query, /* ForTargetVariant*/ true );
996
- if (variantSpec) {
997
- auto variantQueryRange = runtimeQueryRangeForSpec (*variantSpec);
998
- query->setVariantAvailableRange (
999
- variantQueryRange.getRawVersionRange ());
1000
- }
1001
- }
1061
+ std::optional<SemanticAvailabilitySpec> variantSpec =
1062
+ (Context.LangOpts .TargetVariant )
1063
+ ? bestActiveSpecForQuery (query, /* ForTargetVariant*/ true )
1064
+ : std::nullopt;
1065
+
1066
+ query->setAvailabilityQuery (buildAvailabilityQuery (
1067
+ *spec, variantSpec, query->isUnavailability ()));
1002
1068
1003
1069
// Wildcards are expected to be "useless". There may be other specs in
1004
1070
// this query that are useful when compiling for other platforms.
@@ -1164,24 +1230,6 @@ class AvailabilityScopeBuilder : private ASTWalker {
1164
1230
}
1165
1231
}
1166
1232
1167
- // / Return the availability context for the given spec.
1168
- AvailabilityRange runtimeQueryRangeForSpec (SemanticAvailabilitySpec spec) {
1169
- if (spec.isWildcard ())
1170
- return AvailabilityRange::alwaysAvailable ();
1171
-
1172
- auto domain = spec.getDomain ();
1173
- if (domain.isVersioned ())
1174
- return AvailabilityRange (spec.getRuntimeVersion ());
1175
-
1176
- // If it's not a versioned domain, we must be querying whether a domain is
1177
- // present or absent.
1178
- if (domain.isCustom () && domain.getCustomDomain ()->getKind () ==
1179
- CustomAvailabilityDomain::Kind::Disabled)
1180
- return AvailabilityRange::neverAvailable ();
1181
-
1182
- return AvailabilityRange::alwaysAvailable ();
1183
- }
1184
-
1185
1233
// / For the given spec, returns a pair of availability ranges. The first range
1186
1234
// / is for the if/then flow and the second is for the if/else flow.
1187
1235
std::pair<std::optional<AvailabilityRange>, std::optional<AvailabilityRange>>
0 commit comments