Skip to content

Commit e6f9642

Browse files
authored
Merge pull request #75688 from DougGregor/swiftifconfig-replace-cpp
Use the SwiftIfConfig library for evaluating #if conditions
2 parents da597be + 225e562 commit e6f9642

19 files changed

+236
-89
lines changed

include/swift/AST/ParseRequests.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,23 @@ class IDEInspectionSecondPassRequest
175175
readDependencySource(const evaluator::DependencyRecorder &) const;
176176
};
177177

178+
class EvaluateIfConditionRequest
179+
: public SimpleRequest<EvaluateIfConditionRequest,
180+
std::pair<bool, bool>(SourceFile *, SourceRange conditionRange,
181+
bool shouldEvaluate),
182+
RequestFlags::Uncached> {
183+
public:
184+
using SimpleRequest::SimpleRequest;
185+
186+
private:
187+
friend SimpleRequest;
188+
189+
// Evaluation.
190+
std::pair<bool, bool> evaluate(
191+
Evaluator &evaluator, SourceFile *SF, SourceRange conditionRange,
192+
bool shouldEvaluate) const;
193+
};
194+
178195
/// The zone number for the parser.
179196
#define SWIFT_TYPEID_ZONE Parse
180197
#define SWIFT_TYPEID_HEADER "swift/AST/ParseTypeIDZone.def"

include/swift/AST/ParseTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ SWIFT_REQUEST(Parse, ParseTopLevelDeclsRequest,
3131
SWIFT_REQUEST(Parse, ExportedSourceFileRequest,
3232
void *(const SourceFile *), Cached,
3333
NoLocationInfo)
34+
SWIFT_REQUEST(Parse, EvaluateIfConditionRequest,
35+
(std::pair<bool, bool>)(SourceFile *, SourceRange, bool), Uncached,
36+
NoLocationInfo)

include/swift/Basic/SourceLoc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,15 @@ class SourceRange {
163163
}
164164

165165
SWIFT_DEBUG_DUMPER(dump(const SourceManager &SM));
166+
167+
friend size_t hash_value(SourceRange range) {
168+
return llvm::hash_combine(range.Start, range.End);
169+
}
170+
171+
friend void simple_display(raw_ostream &OS, const SourceRange &loc) {
172+
// Nothing meaningful to print.
173+
}
174+
166175
};
167176

168177
/// A half-open character-based source range.

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: 30 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,26 @@ static Expr *findAnyLikelySimulatorEnvironmentTest(Expr *Condition) {
766767

767768
} // end anonymous namespace
768769

770+
/// Call into the Swift implementation of #if condition evaluation.
771+
///
772+
/// \returns std::nullopt if the Swift implementation is not available, or
773+
/// a pair (isActive, allowSyntaxErrors) describing whether the evaluated
774+
/// condition indicates that the region is active and whether, if inactive,
775+
/// the code in that region is allowed to have syntax errors.
776+
static std::optional<std::pair<bool, bool>> evaluateWithSwiftIfConfig(
777+
Parser &parser,
778+
SourceRange conditionRange,
779+
bool shouldEvaluate
780+
) {
781+
#if SWIFT_BUILD_SWIFT_SYNTAX
782+
return evaluateOrDefault(
783+
parser.Context.evaluator,
784+
EvaluateIfConditionRequest{&parser.SF, conditionRange, shouldEvaluate},
785+
std::pair(false, false));
786+
#else
787+
return std::nullopt;
788+
#endif
789+
}
769790

770791
/// Parse and populate a #if ... #endif directive.
771792
/// Delegate callback function to parse elements in the blocks.
@@ -842,7 +863,15 @@ Result Parser::parseIfConfigRaw(
842863
if (result.isNull())
843864
return makeParserError();
844865
Condition = result.get();
845-
if (validateIfConfigCondition(Condition, Context, Diags)) {
866+
if (std::optional<std::pair<bool, bool>> evalResult =
867+
evaluateWithSwiftIfConfig(*this,
868+
Condition->getSourceRange(),
869+
shouldEvaluate)) {
870+
if (!foundActive) {
871+
isActive = evalResult->first;
872+
isVersionCondition = evalResult->second;
873+
}
874+
} else if (validateIfConfigCondition(Condition, Context, Diags)) {
846875
// Error in the condition;
847876
isActive = false;
848877
isVersionCondition = false;

lib/Sema/TypeChecker.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "TypeCheckType.h"
2424
#include "CodeSynthesis.h"
2525
#include "MiscDiagnostics.h"
26+
#include "swift/AST/ASTBridging.h"
2627
#include "swift/AST/ASTWalker.h"
2728
#include "swift/AST/ASTVisitor.h"
2829
#include "swift/AST/Attr.h"
@@ -765,3 +766,31 @@ bool TypeChecker::diagnoseInvalidFunctionType(
765766

766767
return hadAnyError;
767768
}
769+
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+
std::pair<bool, bool> EvaluateIfConditionRequest::evaluate(
778+
Evaluator &evaluator, SourceFile *sourceFile, SourceRange conditionRange,
779+
bool shouldEvaluate
780+
) const {
781+
// FIXME: When we migrate to SwiftParser, use the parsed syntax tree.
782+
ASTContext &ctx = sourceFile->getASTContext();
783+
auto &sourceMgr = ctx.SourceMgr;
784+
StringRef sourceFileText =
785+
sourceMgr.getEntireTextForBuffer(*sourceFile->getBufferID());
786+
StringRef conditionText =
787+
sourceMgr.extractText(Lexer::getCharSourceRangeFromSourceRange(
788+
sourceMgr, conditionRange));
789+
intptr_t evalResult = swift_ASTGen_evaluatePoundIfCondition(
790+
ctx, &ctx.Diags, sourceFileText, conditionText, shouldEvaluate
791+
);
792+
793+
bool isActive = (evalResult & 0x01) != 0;
794+
bool allowSyntaxErrors = (evalResult & 0x02) != 0;
795+
return std::pair(isActive, allowSyntaxErrors);
796+
}

test/Parse/ConditionalCompilation/basicParseErrors.swift

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var x = 0
88
var y = 0
99
#endif
1010

11-
#if foo(BAR) // expected-error {{unexpected platform condition (expected 'os', 'arch', or 'swift')}}
11+
#if foo(BAR) // expected-error {{invalid conditional compilation expression}}
1212
var z = 0
1313
#endif
1414

@@ -27,7 +27,7 @@ func h() {}
2727
#else /* aaa */
2828
#endif /* bbb */
2929

30-
#if foo.bar() // expected-error {{unexpected platform condition (expected 'os', 'arch', or 'swift')}}
30+
#if foo.bar() // expected-error {{invalid conditional compilation expression}}
3131
.baz()
3232

3333
#endif
@@ -67,30 +67,23 @@ struct S {
6767
#else
6868
#endif
6969

70-
#if os(ios) // expected-warning {{unknown operating system for build configuration 'os'}} expected-note{{did you mean 'iOS'?}} {{8-11=iOS}}
70+
#if os(ios)
7171
#endif
7272

73-
#if os(uOS) // expected-warning {{unknown operating system for build configuration 'os'}} expected-note{{did you mean 'iOS'?}} {{8-11=iOS}}
73+
#if os(uOS)
7474
#endif
7575

76-
#if os(xxxxxxd) // expected-warning {{unknown operating system for build configuration 'os'}}
77-
// expected-note@-1{{did you mean 'Linux'?}} {{8-15=Linux}}
78-
// expected-note@-2{{did you mean 'FreeBSD'?}} {{8-15=FreeBSD}}
79-
// expected-note@-3{{did you mean 'Android'?}} {{8-15=Android}}
80-
// expected-note@-4{{did you mean 'OSX'?}} {{8-15=OSX}}
81-
// expected-note@-5{{did you mean 'OpenBSD'?}} {{8-15=OpenBSD}}
82-
// expected-note@-6{{did you mean 'xrOS'?}} {{8-15=xrOS}}
76+
#if os(xxxxxxd)
8377
#endif
8478

85-
#if os(bisionos) // expected-warning {{unknown operating system for build configuration 'os'}}
86-
// expected-note@-1{{did you mean 'visionOS'?}} {{8-16=visionOS}}
79+
#if os(bisionos)
8780

8881
#endif
8982

90-
#if arch(arn) // expected-warning {{unknown architecture for build configuration 'arch'}} expected-note{{did you mean 'arm'?}} {{10-13=arm}}
83+
#if arch(arn)
9184
#endif
9285

93-
#if _endian(mid) // expected-warning {{unknown endianness for build configuration '_endian'}} expected-note{{did you mean 'big'?}} {{13-16=big}}
86+
#if _endian(mid) // expected-warning {{unknown endianness for build configuration '_endian'}}
9487
#endif
9588

9689
LABEL: #if true // expected-error {{expected statement}}
@@ -104,7 +97,7 @@ func fn_j() {}
10497
#endif
10598
fn_j() // OK
10699

107-
#if foo || bar || nonExistent() // expected-error {{expected argument to platform condition}}
100+
#if foo || bar || nonExistent() // expected-error {{invalid conditional compilation expression}}
108101
#endif
109102

110103
#if FOO = false
@@ -123,47 +116,33 @@ undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}}
123116
#endif
124117

125118
/// Invalid platform condition arguments don't invalidate the whole condition.
119+
// expected-warning@+1{{unknown endianness}}
126120
#if !arch(arn) && !os(ystem) && !_endian(ness)
127-
// expected-warning@-1 {{unknown architecture for build configuration 'arch'}}
128-
// expected-note@-2 {{did you mean 'arm'?}} {{11-14=arm}}
129-
// expected-warning@-3 {{unknown operating system for build configuration 'os'}}
130-
// expected-note@-4 {{did you mean 'OSX'?}} {{23-28=OSX}}
131-
// expected-note@-5 {{did you mean 'PS4'?}} {{23-28=PS4}}
132-
// expected-note@-6 {{did you mean 'none'?}} {{23-28=none}}
133-
// expected-warning@-7 {{unknown endianness for build configuration '_endian'}}
134-
// expected-note@-8 {{did you mean 'big'?}} {{42-46=big}}
135121
func fn_k() {}
136122
#endif
137123
fn_k()
138124

139125
#if os(cillator) || arch(i3rm)
140-
// expected-warning@-1 {{unknown operating system for build configuration 'os'}}
141-
// expected-note@-2 {{did you mean 'macOS'?}} {{8-16=macOS}}
142-
// expected-note@-3 {{did you mean 'iOS'?}} {{8-16=iOS}}
143-
// expected-warning@-4 {{unknown architecture for build configuration 'arch'}}
144-
// expected-note@-5 {{did you mean 'arm'?}} {{26-30=arm}}
145-
// expected-note@-6 {{did you mean 'i386'?}} {{26-30=i386}}
146-
// expected-note@-7 {{did you mean 'visionOS'?}} {{8-16=visionOS}}
147126
func undefinedFunc() // ignored.
148127
#endif
149128
undefinedFunc() // expected-error {{cannot find 'undefinedFunc' in scope}}
150129

151-
#if os(simulator) // expected-warning {{unknown operating system for build configuration 'os'}} expected-note {{did you mean 'targetEnvironment'}} {{5-7=targetEnvironment}}
130+
#if os(simulator)
152131
#endif
153132

154-
#if arch(iOS) // expected-warning {{unknown architecture for build configuration 'arch'}} expected-note {{did you mean 'os'}} {{5-9=os}}
133+
#if arch(iOS)
155134
#endif
156135

157-
#if _endian(arm64) // expected-warning {{unknown endianness for build configuration '_endian'}} expected-note {{did you mean 'arch'}} {{5-12=arch}}
136+
#if _endian(arm64) // expected-warning {{unknown endianness for build configuration '_endian'}}
158137
#endif
159138

160-
#if targetEnvironment(_ObjC) // expected-warning {{unknown target environment for build configuration 'targetEnvironment'}} expected-note {{did you mean 'macabi'}} {{23-28=macabi}}
139+
#if targetEnvironment(_ObjC)
161140
#endif
162141

163-
#if os(iOS) || os(simulator) // expected-warning {{unknown operating system for build configuration 'os'}} expected-note {{did you mean 'targetEnvironment'}} {{16-18=targetEnvironment}}
142+
#if os(iOS) || os(simulator)
164143
#endif
165144

166-
#if arch(ios) // expected-warning {{unknown architecture for build configuration 'arch'}} expected-note {{did you mean 'os'}} {{5-9=os}} expected-note {{did you mean 'iOS'}} {{10-13=iOS}}
145+
#if arch(ios)
167146
#endif
168147

169148
#if FOO
@@ -179,5 +158,5 @@ if true {}
179158

180159
// rdar://83017601 Make sure we don't crash
181160
#if canImport()
182-
// expected-error@-1 {{expected argument to platform condition}}
161+
// expected-error@-1 {{'canImport' requires a module name}}
183162
#endif

test/Parse/ConditionalCompilation/can_import_in_inactive.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#endif
2222

2323
#if canImport(SomeModule)
24-
// CHECK: :[[@LINE-1]]:15: error: could not find module 'SomeModule' for target '{{.*}}'; found: i386
24+
// CHECK: :[[@LINE-1]]:{{.*}}: error: could not find module 'SomeModule' for target '{{.*}}'; found: i386
2525
#endif
2626

2727
import SomeModule

test/Parse/ConditionalCompilation/can_import_submodule.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@ import A.B.C
4848
#endif
4949

5050

51-
#if canImport(A(_:).B) // expected-error@:15 {{unexpected platform condition argument: expected module name}}
51+
#if canImport(A(_:).B) // expected-error@:15 {{expected module name}}
5252
#endif
5353

54-
#if canImport(A.B(c: "arg")) // expected-error@:15 {{unexpected platform condition argument: expected module name}}
54+
#if canImport(A.B(c: "arg")) // expected-error@:15 {{expected module name}}
5555
#endif
5656

57-
#if canImport(A(b: 1, c: 2).B.C) // expected-error@:15 {{unexpected platform condition argument: expected module name}}
57+
#if canImport(A(b: 1, c: 2).B.C) // expected-error@:15 {{expected module name}}
5858
#endif
5959

60-
#if canImport(A.B("arg")(3).C) // expected-error@:15 {{unexpected platform condition argument: expected module name}}
60+
#if canImport(A.B("arg")(3).C) // expected-error@:15 {{expected module name}}
6161
#endif
6262

63-
#if canImport(A.B.C()) // expected-error@:15 {{unexpected platform condition argument: expected module name}}
63+
#if canImport(A.B.C()) // expected-error@:15 {{expected module name}}
6464
#endif

0 commit comments

Comments
 (0)