Skip to content

Commit fa6f1b6

Browse files
authored
Merge pull request #33439 from xymus/expand-avail
[Sema] Define availability specification macros with a frontend flag
2 parents a7a47e5 + 5ed2616 commit fa6f1b6

File tree

13 files changed

+432
-16
lines changed

13 files changed

+432
-16
lines changed

include/swift/AST/AvailabilitySpec.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec {
8585

8686
SourceRange VersionSrcRange;
8787

88+
// Location of the macro expanded to create this spec.
89+
SourceLoc MacroLoc;
90+
8891
public:
8992
PlatformVersionConstraintAvailabilitySpec(PlatformKind Platform,
9093
SourceLoc PlatformLoc,
@@ -117,6 +120,10 @@ class PlatformVersionConstraintAvailabilitySpec : public AvailabilitySpec {
117120

118121
SourceRange getSourceRange() const;
119122

123+
// Location of the macro expanded to create this spec.
124+
SourceLoc getMacroLoc() const { return MacroLoc; }
125+
void setMacroLoc(SourceLoc loc) { MacroLoc = loc; }
126+
120127
void print(raw_ostream &OS, unsigned Indent) const;
121128

122129
static bool classof(const AvailabilitySpec *Spec) {

include/swift/AST/DiagnosticsParse.def

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,22 @@ WARNING(attr_availability_nonspecific_platform_unexpected_version,none,
14311431
"unexpected version number in '%0' attribute for non-specific platform "
14321432
"'*'", (StringRef))
14331433

1434+
// availability macro
1435+
ERROR(attr_availability_wildcard_in_macro, none,
1436+
"future platforms identified by '*' cannot be used in "
1437+
"an availability macro definition", ())
1438+
ERROR(attr_availability_missing_macro_name,none,
1439+
"expected an identifier to begin an availability macro definition", ())
1440+
ERROR(attr_availability_expected_colon_macro,none,
1441+
"expected ':' after '%0' in availability macro definition",
1442+
(StringRef))
1443+
ERROR(attr_availability_unknown_version,none,
1444+
"reference to undefined version '%0' for availability macro '%1'",
1445+
(StringRef, StringRef))
1446+
ERROR(attr_availability_duplicate,none,
1447+
"duplicate definition of availability macro '%0' for version '%1'",
1448+
(StringRef, StringRef))
1449+
14341450
// originallyDefinedIn
14351451
ERROR(originally_defined_in_missing_rparen,none,
14361452
"expected ')' in @_originallyDefinedIn argument list", ())

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4932,6 +4932,10 @@ WARNING(public_decl_needs_availability, none,
49324932
"public declarations should have an availability attribute when building "
49334933
"with -require-explicit-availability", ())
49344934

4935+
ERROR(availability_macro_in_inlinable, none,
4936+
"availability macro cannot be used in inlinable %0",
4937+
(DescriptiveDeclKind))
4938+
49354939
// This doesn't display as an availability diagnostic, but it's
49364940
// implemented there and fires when these subscripts are marked
49374941
// unavailable, so it seems appropriate to put it here.

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ namespace swift {
111111
/// when using RequireExplicitAvailability.
112112
std::string RequireExplicitAvailabilityTarget;
113113

114+
// Availability macros definitions to be expanded at parsing.
115+
SmallVector<StringRef, 4> AvailabilityMacros;
116+
114117
/// If false, '#file' evaluates to the full path rather than a
115118
/// human-readable string.
116119
bool EnableConcisePoundFile = false;

include/swift/Option/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,11 @@ def require_explicit_availability_target : Separate<["-"], "require-explicit-ava
401401
HelpText<"Suggest fix-its adding @available(<target>, *) to public declarations without availability">,
402402
MetaVarName<"<target>">;
403403

404+
def define_availability : Separate<["-"], "define-availability">,
405+
Flags<[FrontendOption, NoInteractiveOption]>,
406+
HelpText<"Define an availability macro in the format 'macroName : iOS 13.0, macOS 10.15'">,
407+
MetaVarName<"<macro>">;
408+
404409
def module_name : Separate<["-"], "module-name">,
405410
Flags<[FrontendOption, ModuleInterfaceOption]>,
406411
HelpText<"Name of the module to build">;

include/swift/Parse/Parser.h

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,22 @@ class Parser {
397397
/// Current syntax parsing context where call backs should be directed to.
398398
SyntaxParsingContext *SyntaxContext;
399399

400+
/// Maps of macro name and version to availability specifications.
401+
typedef llvm::DenseMap<llvm::VersionTuple,
402+
SmallVector<AvailabilitySpec *, 4>>
403+
AvailabilityMacroVersionMap;
404+
typedef llvm::DenseMap<StringRef, AvailabilityMacroVersionMap>
405+
AvailabilityMacroMap;
406+
407+
/// Cache of the availability macros parsed from the command line arguments.
408+
/// Organized as two nested \c DenseMap keyed first on the macro name then
409+
/// the macro version. This structure allows to peek at macro names before
410+
/// parsing a version tuple.
411+
AvailabilityMacroMap AvailabilityMacros;
412+
413+
/// Has \c AvailabilityMacros been computed?
414+
bool AvailabilityMacrosComputed = false;
415+
400416
public:
401417
Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags,
402418
SILParserStateBase *SIL, PersistentParserState *PersistentState,
@@ -1682,9 +1698,38 @@ class Parser {
16821698
//===--------------------------------------------------------------------===//
16831699
// Availability Specification Parsing
16841700

1685-
/// Parse a comma-separated list of availability specifications.
1701+
/// Parse a comma-separated list of availability specifications. Try to
1702+
/// expand availability macros when /p ParsingMacroDefinition is false.
1703+
ParserStatus
1704+
parseAvailabilitySpecList(SmallVectorImpl<AvailabilitySpec *> &Specs,
1705+
bool ParsingMacroDefinition = false);
1706+
1707+
/// Does the current matches an argument macro name? Parsing compiler
1708+
/// arguments as required without consuming tokens from the source file
1709+
/// parser.
1710+
bool peekAvailabilityMacroName();
1711+
1712+
/// Try to parse a reference to an availability macro and append its result
1713+
/// to \p Specs. If the current token doesn't match a macro name, return
1714+
/// a success without appending anything to \c Specs.
1715+
ParserStatus
1716+
parseAvailabilityMacro(SmallVectorImpl<AvailabilitySpec *> &Specs);
1717+
1718+
/// Parse the availability macros definitions passed as arguments.
1719+
void parseAllAvailabilityMacroArguments();
1720+
1721+
/// Result of parsing an availability macro definition.
1722+
struct AvailabilityMacroDefinition {
1723+
StringRef Name;
1724+
llvm::VersionTuple Version;
1725+
SmallVector<AvailabilitySpec *, 4> Specs;
1726+
};
1727+
1728+
/// Parse an availability macro definition from a command line argument.
1729+
/// This function should be called on a Parser set up on the command line
1730+
/// argument code.
16861731
ParserStatus
1687-
parseAvailabilitySpecList(SmallVectorImpl<AvailabilitySpec *> &Specs);
1732+
parseAvailabilityMacroDefinition(AvailabilityMacroDefinition &Result);
16881733

16891734
ParserResult<AvailabilitySpec> parseAvailabilitySpec();
16901735
ParserResult<PlatformVersionConstraintAvailabilitySpec>

lib/Frontend/CompilerInvocation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
498498
}
499499
}
500500

501+
for (const Arg *A : Args.filtered(OPT_define_availability)) {
502+
Opts.AvailabilityMacros.push_back(A->getValue());
503+
}
504+
501505
if (const Arg *A = Args.getLastArg(OPT_value_recursion_threshold)) {
502506
unsigned threshold;
503507
if (StringRef(A->getValue()).getAsInteger(10, threshold)) {

lib/Parse/ParseDecl.cpp

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,125 @@ void Parser::parseObjCSelector(SmallVector<Identifier, 4> &Names,
13721372
}
13731373
}
13741374

1375+
bool Parser::peekAvailabilityMacroName() {
1376+
parseAllAvailabilityMacroArguments();
1377+
AvailabilityMacroMap Map = AvailabilityMacros;
1378+
1379+
StringRef MacroName = Tok.getText();
1380+
return Map.find(MacroName) != Map.end();
1381+
}
1382+
1383+
ParserStatus
1384+
Parser::parseAvailabilityMacro(SmallVectorImpl<AvailabilitySpec *> &Specs) {
1385+
// Get the macros from the compiler arguments.
1386+
parseAllAvailabilityMacroArguments();
1387+
AvailabilityMacroMap Map = AvailabilityMacros;
1388+
1389+
StringRef MacroName = Tok.getText();
1390+
auto NameMatch = Map.find(MacroName);
1391+
if (NameMatch == Map.end())
1392+
return makeParserSuccess(); // No match, it could be a standard platform.
1393+
1394+
consumeToken();
1395+
1396+
llvm::VersionTuple Version;
1397+
SourceRange VersionRange;
1398+
if (Tok.isAny(tok::integer_literal, tok::floating_literal)) {
1399+
if (parseVersionTuple(Version, VersionRange,
1400+
diag::avail_query_expected_version_number))
1401+
return makeParserError();
1402+
}
1403+
1404+
auto VersionMatch = NameMatch->getSecond().find(Version);
1405+
if (VersionMatch == NameMatch->getSecond().end()) {
1406+
diagnose(PreviousLoc, diag::attr_availability_unknown_version,
1407+
Version.getAsString(), MacroName);
1408+
return makeParserError(); // Failed to match the version, that's an error.
1409+
}
1410+
1411+
// Make a copy of the specs to add the macro source location
1412+
// for the diagnostic about the use of macros in inlinable code.
1413+
SourceLoc MacroLoc = Tok.getLoc();
1414+
for (auto *Spec : VersionMatch->getSecond())
1415+
if (auto *PlatformVersionSpec =
1416+
dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) {
1417+
auto SpecCopy =
1418+
new (Context) PlatformVersionConstraintAvailabilitySpec(
1419+
*PlatformVersionSpec);
1420+
SpecCopy->setMacroLoc(MacroLoc);
1421+
Specs.push_back(SpecCopy);
1422+
}
1423+
1424+
return makeParserSuccess();
1425+
}
1426+
1427+
void Parser::parseAllAvailabilityMacroArguments() {
1428+
1429+
if (AvailabilityMacrosComputed) return;
1430+
1431+
AvailabilityMacroMap Map;
1432+
1433+
SourceManager &SM = Context.SourceMgr;
1434+
const LangOptions &LangOpts = Context.LangOpts;
1435+
1436+
for (StringRef macro: LangOpts.AvailabilityMacros) {
1437+
1438+
// Create temporary parser.
1439+
int bufferID = SM.addMemBufferCopy(macro,
1440+
"-define-availability argument");
1441+
swift::ParserUnit PU(SM,
1442+
SourceFileKind::Main, bufferID,
1443+
LangOpts,
1444+
TypeCheckerOptions(), "unknown");
1445+
1446+
ForwardingDiagnosticConsumer PDC(Context.Diags);
1447+
PU.getDiagnosticEngine().addConsumer(PDC);
1448+
1449+
// Parse the argument.
1450+
AvailabilityMacroDefinition ParsedMacro;
1451+
ParserStatus Status =
1452+
PU.getParser().parseAvailabilityMacroDefinition(ParsedMacro);
1453+
if (Status.isError())
1454+
continue;
1455+
1456+
// Copy the Specs to the requesting ASTContext from the temporary context
1457+
// that parsed the argument.
1458+
auto SpecsCopy = SmallVector<AvailabilitySpec*, 4>();
1459+
for (auto *Spec : ParsedMacro.Specs)
1460+
if (auto *PlatformVersionSpec =
1461+
dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) {
1462+
auto SpecCopy =
1463+
new (Context) PlatformVersionConstraintAvailabilitySpec(
1464+
*PlatformVersionSpec);
1465+
SpecsCopy.push_back(SpecCopy);
1466+
}
1467+
1468+
ParsedMacro.Specs = SpecsCopy;
1469+
1470+
// Find the macro info by name.
1471+
AvailabilityMacroVersionMap MacroDefinition;
1472+
auto NameMatch = Map.find(ParsedMacro.Name);
1473+
if (NameMatch != Map.end()) {
1474+
MacroDefinition = NameMatch->getSecond();
1475+
}
1476+
1477+
// Set the macro info by version.
1478+
auto PreviousEntry =
1479+
MacroDefinition.insert({ParsedMacro.Version, ParsedMacro.Specs});
1480+
if (!PreviousEntry.second) {
1481+
diagnose(PU.getParser().PreviousLoc, diag::attr_availability_duplicate,
1482+
ParsedMacro.Name, ParsedMacro.Version.getAsString());
1483+
}
1484+
1485+
// Save back the macro spec.
1486+
Map.erase(ParsedMacro.Name);
1487+
Map.insert({ParsedMacro.Name, MacroDefinition});
1488+
}
1489+
1490+
AvailabilityMacros = Map;
1491+
AvailabilityMacrosComputed = true;
1492+
}
1493+
13751494
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
13761495
DeclAttrKind DK) {
13771496
// Ok, it is a valid attribute, eat it, and then process it.
@@ -1975,7 +2094,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
19752094
StringRef Platform = Tok.getText();
19762095

19772096
if (Platform != "*" &&
1978-
peekToken().isAny(tok::integer_literal, tok::floating_literal)) {
2097+
(peekToken().isAny(tok::integer_literal, tok::floating_literal) ||
2098+
peekAvailabilityMacroName())) {
19792099
// We have the short form of available: @available(iOS 8.0.1, *)
19802100
SmallVector<AvailabilitySpec *, 5> Specs;
19812101
ParserStatus Status = parseAvailabilitySpecList(Specs);

0 commit comments

Comments
 (0)