Skip to content

Commit 6a76d85

Browse files
DougGregorandrurogerz
authored andcommitted
Use the SwiftIfConfig library for evaluating #if conditions.
Replace the C++ implementation of #if condition operator folding, validation, and evaluation with the corresponding facilities in the SwiftIfConfig library. Leave the C++ implementation in place for now to permit the compiler to continue building without swift-syntax.
1 parent f5781b0 commit 6a76d85

File tree

2 files changed

+118
-2
lines changed

2 files changed

+118
-2
lines changed

lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import ASTBridging
1414
import SwiftDiagnostics
15-
import SwiftIfConfig
15+
@_spi(Compiler) import SwiftIfConfig
1616
import SwiftParser
1717
import SwiftSyntax
1818

@@ -109,6 +109,13 @@ struct CompilerBuildConfiguration: BuildConfiguration {
109109

110110
func isActiveTargetRuntime(name: String) throws -> Bool {
111111
var name = name
112+
113+
// Complain if the provided runtime isn't one of the known values.
114+
switch name {
115+
case "_Native", "_ObjC", "_multithreaded": break
116+
default: throw IfConfigError.unexpectedRuntimeCondition
117+
}
118+
112119
return name.withBridgedString { nameRef in
113120
ctx.langOptsIsActiveTargetRuntime(nameRef)
114121
}
@@ -161,6 +168,18 @@ struct CompilerBuildConfiguration: BuildConfiguration {
161168
}
162169
}
163170

171+
enum IfConfigError: Error, CustomStringConvertible {
172+
case unexpectedRuntimeCondition
173+
174+
var description: String {
175+
switch self {
176+
case .unexpectedRuntimeCondition:
177+
return "unexpected argument for the '_runtime' condition; expected '_Native' or '_ObjC'"
178+
}
179+
}
180+
}
181+
182+
164183
/// Extract the #if clause range information for the given source file.
165184
@_cdecl("swift_ASTGen_configuredRegions")
166185
public func configuredRegions(
@@ -301,3 +320,53 @@ public func freeConfiguredRegions(
301320
) {
302321
UnsafeMutableBufferPointer(start: regions, count: numRegions).deallocate()
303322
}
323+
324+
/// Evaluate the #if condition at ifClauseLocationPtr.
325+
@_cdecl("swift_ASTGen_evaluatePoundIfCondition")
326+
public func evaluatePoundIfCondition(
327+
astContext: BridgedASTContext,
328+
diagEnginePtr: UnsafeMutableRawPointer,
329+
sourceFileBuffer: BridgedStringRef,
330+
ifConditionText: BridgedStringRef,
331+
shouldEvaluate: Bool
332+
) -> Int {
333+
// Retrieve the #if condition that we're evaluating here.
334+
// FIXME: Use 'ExportedSourceFile' when C++ parser is replaced.
335+
let textBuffer = UnsafeBufferPointer<UInt8>(start: ifConditionText.data, count: ifConditionText.count)
336+
var parser = Parser(textBuffer)
337+
let conditionExpr = ExprSyntax.parse(from: &parser)
338+
339+
let isActive: Bool
340+
let syntaxErrorsAllowed: Bool
341+
let diagnostics: [Diagnostic]
342+
if shouldEvaluate {
343+
// Evaluate the condition against the compiler's build configuration.
344+
let configuration = CompilerBuildConfiguration(
345+
ctx: astContext,
346+
sourceBuffer: textBuffer
347+
)
348+
349+
let state: IfConfigRegionState
350+
(state, syntaxErrorsAllowed, diagnostics) = IfConfigRegionState.evaluating(conditionExpr, in: configuration)
351+
isActive = (state == .active)
352+
} else {
353+
// Don't evaluate the condition, because we know it's inactive. Determine
354+
// whether syntax errors are permitted within this region according to the
355+
// condition.
356+
isActive = false
357+
(syntaxErrorsAllowed, diagnostics) = IfConfigClauseSyntax.syntaxErrorsAllowed(conditionExpr)
358+
}
359+
360+
// Render the diagnostics.
361+
for diagnostic in diagnostics {
362+
emitDiagnostic(
363+
diagnosticEngine: BridgedDiagnosticEngine(raw: diagEnginePtr),
364+
sourceFileBuffer: UnsafeBufferPointer(start: sourceFileBuffer.data, count: sourceFileBuffer.count),
365+
sourceFileBufferOffset: ifConditionText.data! - sourceFileBuffer.data!,
366+
diagnostic: diagnostic,
367+
diagnosticSeverity: diagnostic.diagMessage.severity
368+
)
369+
}
370+
371+
return (isActive ? 0x1 : 0) | (syntaxErrorsAllowed ? 0x2 : 0)
372+
}

lib/Parse/ParseIfConfig.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "swift/Parse/Parser.h"
1818

19+
#include "swift/AST/ASTBridging.h"
1920
#include "swift/AST/ASTVisitor.h"
2021
#include "swift/AST/DiagnosticSuppression.h"
2122
#include "swift/Basic/Assertions.h"
@@ -766,6 +767,44 @@ static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) {
766767

767768
} // end anonymous namespace
768769

770+
extern "C" intptr_t swift_ASTGen_evaluatePoundIfCondition(
771+
BridgedASTContext astContext,
772+
void *_Nonnull diagEngine,
773+
BridgedStringRef sourceFileBuffer,
774+
BridgedStringRef conditionText,
775+
bool);
776+
777+
/// Call into the Swift implementation of #if condition evaluation.
778+
///
779+
/// \returns std::nullopt if the Swift implementation is not available, or
780+
/// a pair (isActive, allowSyntaxErrors) describing whether the evaluated
781+
/// condition indicates that the region is active and whether, if inactive,
782+
/// the code in that region is allowed to have syntax errors.
783+
static std::optional<std::pair<bool, bool>> evaluateWithSwiftIfConfig(
784+
Parser &parser,
785+
SourceRange conditionRange,
786+
bool shouldEvaluate
787+
) {
788+
#if SWIFT_BUILD_SWIFT_SYNTAX
789+
// FIXME: When we migrate to SwiftParser, use the parsed syntax tree.
790+
auto &sourceMgr = parser.Context.SourceMgr;
791+
StringRef sourceFileText =
792+
sourceMgr.getEntireTextForBuffer(*parser.SF.getBufferID());
793+
StringRef conditionText =
794+
sourceMgr.extractText(Lexer::getCharSourceRangeFromSourceRange(
795+
sourceMgr, conditionRange));
796+
intptr_t evalResult = swift_ASTGen_evaluatePoundIfCondition(
797+
parser.Context, &parser.Context.Diags, sourceFileText, conditionText,
798+
shouldEvaluate
799+
);
800+
801+
bool isActive = (evalResult & 0x01) != 0;
802+
bool allowSyntaxErrors = (evalResult & 0x02) != 0;
803+
return std::pair(isActive, allowSyntaxErrors);
804+
#else
805+
return std::nullopt;
806+
#endif
807+
}
769808

770809
/// Parse and populate a #if ... #endif directive.
771810
/// Delegate callback function to parse elements in the blocks.
@@ -842,7 +881,15 @@ Result Parser::parseIfConfigRaw(
842881
if (result.isNull())
843882
return makeParserError();
844883
Condition = result.get();
845-
if (validateIfConfigCondition(Condition, Context, Diags)) {
884+
if (std::optional<std::pair<bool, bool>> evalResult =
885+
evaluateWithSwiftIfConfig(*this,
886+
Condition->getSourceRange(),
887+
shouldEvaluate)) {
888+
if (!foundActive) {
889+
isActive = evalResult->first;
890+
isVersionCondition = evalResult->second;
891+
}
892+
} else if (validateIfConfigCondition(Condition, Context, Diags)) {
846893
// Error in the condition;
847894
isActive = false;
848895
isVersionCondition = false;

0 commit comments

Comments
 (0)