From 5c3b7777e701a58ef970d4b871203feb3d8499cf Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 5 Jul 2024 22:34:13 -0700 Subject: [PATCH 1/8] Implement a SwiftIfConfig.BuildConfiguration that queries the ASTContext This concrete implementation of the BuildConfiguration allows the use of the SwiftIfConfig library's APIs where the build configuration comes from the compiler itself. --- include/swift/AST/ASTBridging.h | 68 ++++++++ include/swift/Basic/LangOptions.h | 11 ++ lib/AST/ASTBridging.cpp | 127 +++++++++++++++ lib/ASTGen/CMakeLists.txt | 2 + lib/ASTGen/Package.swift | 1 + .../ASTGen/CompilerBuildConfiguration.swift | 146 ++++++++++++++++++ 6 files changed, 355 insertions(+) create mode 100644 lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index d5ba948bfec2b..600065cd15138 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -190,6 +190,74 @@ bool BridgedASTContext_langOptsHasFeature(BridgedASTContext cContext, SWIFT_NAME("getter:BridgedASTContext.majorLanguageVersion(self:)") unsigned BridgedASTContext_majorLanguageVersion(BridgedASTContext cContext); +SWIFT_NAME("BridgedASTContext.langOptsCustomConditionSet(self:_:)") +bool BridgedASTContext_langOptsCustomConditionSet(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsHasFeatureNamed(self:_:)") +bool BridgedASTContext_langOptsHasFeatureNamed(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsHasAttributeNamed(self:_:)") +bool BridgedASTContext_langOptsHasAttributeNamed(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetOS(self:_:)") +bool BridgedASTContext_langOptsIsActiveTargetOS(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetArchitecture(self:_:)") +bool BridgedASTContext_langOptsIsActiveTargetArchitecture(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetEnvironment(self:_:)") +bool BridgedASTContext_langOptsIsActiveTargetEnvironment(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetRuntime(self:_:)") +bool BridgedASTContext_langOptsIsActiveTargetRuntime(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("BridgedASTContext.langOptsIsActiveTargetPtrAuth(self:_:)") +bool BridgedASTContext_langOptsIsActiveTargetPtrAuth(BridgedASTContext cContext, + BridgedStringRef cName); + +SWIFT_NAME("getter:BridgedASTContext.langOptsTargetPointerBitWidth(self:)") +unsigned BridgedASTContext_langOptsTargetPointerBitWidth(BridgedASTContext cContext); + +SWIFT_NAME("BridgedASTContext.langOptsGetTargetAtomicBitWidths(self:_:)") +SwiftInt BridgedASTContext_langOptsGetTargetAtomicBitWidths(BridgedASTContext cContext, + SwiftInt* _Nullable * _Nonnull cComponents); + +enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedEndianness : size_t { + EndianLittle, + EndianBig, +}; + +SWIFT_NAME("getter:BridgedASTContext.langOptsTargetEndianness(self:)") +BridgedEndianness BridgedASTContext_langOptsTargetEndianness(BridgedASTContext cContext); + +SWIFT_NAME("BridgedASTContext.langOptsGetLanguageVersion(self:_:)") +SwiftInt BridgedASTContext_langOptsGetLanguageVersion(BridgedASTContext cContext, + SwiftInt* _Nullable * _Nonnull cComponents); + +SWIFT_NAME("BridgedASTContext.langOptsGetCompilerVersion(self:_:)") +SwiftInt BridgedASTContext_langOptsGetCompilerVersion(BridgedASTContext cContext, + SwiftInt* _Nullable * _Nonnull cComponents); + +enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedCanImportVersion : size_t { + CanImportUnversioned, + CanImportVersion, + CanImportUnderlyingVersion, +}; + +SWIFT_NAME("BridgedASTContext.canImport(self:importPath:versionKind:versionComponents:numVersionComponents:)") +bool BridgedASTContext_canImport(BridgedASTContext cContext, + BridgedStringRef importPath, + BridgedCanImportVersion versionKind, + const SwiftInt * _Nullable versionComponents, + SwiftInt numVersionComponents); + //===----------------------------------------------------------------------===// // MARK: AST nodes //===----------------------------------------------------------------------===// diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 43d62e2c8ea99..e93b29731429b 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -730,18 +730,23 @@ namespace swift { switch (maxWidth) { case 128: AtomicBitWidths.emplace_back("_128"); + AtomicBitWidthValues.push_back(128); LLVM_FALLTHROUGH; case 64: AtomicBitWidths.emplace_back("_64"); + AtomicBitWidthValues.push_back(64); LLVM_FALLTHROUGH; case 32: AtomicBitWidths.emplace_back("_32"); + AtomicBitWidthValues.push_back(32); LLVM_FALLTHROUGH; case 16: AtomicBitWidths.emplace_back("_16"); + AtomicBitWidthValues.push_back(16); LLVM_FALLTHROUGH; case 8: AtomicBitWidths.emplace_back("_8"); + AtomicBitWidthValues.push_back(8); break; default: return; @@ -751,6 +756,11 @@ namespace swift { /// Removes all atomic bit widths. void clearAtomicBitWidths() { AtomicBitWidths.clear(); + AtomicBitWidthValues.clear(); + } + + llvm::ArrayRef getAtomicBitWidthValues() const { + return AtomicBitWidthValues; } /// Returns true if the given platform condition argument represents @@ -791,6 +801,7 @@ namespace swift { private: llvm::SmallVector AtomicBitWidths; + llvm::SmallVector AtomicBitWidthValues; llvm::SmallVector, 10> PlatformConditionValues; llvm::SmallVector CustomConditionalCompilationFlags; diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index 64ffa54a54348..b2021a0168c76 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -121,6 +121,133 @@ unsigned BridgedASTContext_majorLanguageVersion(BridgedASTContext cContext) { return cContext.unbridged().LangOpts.EffectiveLanguageVersion[0]; } +bool BridgedASTContext_langOptsCustomConditionSet(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts + .isCustomConditionalCompilationFlagSet(cName.unbridged()); +} + +bool BridgedASTContext_langOptsHasFeatureNamed(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.hasFeature(cName.unbridged()); +} + +bool BridgedASTContext_langOptsHasAttributeNamed(BridgedASTContext cContext, + BridgedStringRef cName) { + return hasAttribute(cContext.unbridged().LangOpts, cName.unbridged()); +} + +bool BridgedASTContext_langOptsIsActiveTargetOS(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.checkPlatformCondition( + PlatformConditionKind::OS, cName.unbridged()); +} + +bool BridgedASTContext_langOptsIsActiveTargetArchitecture(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.checkPlatformCondition( + PlatformConditionKind::Arch, cName.unbridged()); +} + +bool BridgedASTContext_langOptsIsActiveTargetEnvironment(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.checkPlatformCondition( + PlatformConditionKind::TargetEnvironment, cName.unbridged()); +} + +bool BridgedASTContext_langOptsIsActiveTargetRuntime(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.checkPlatformCondition( + PlatformConditionKind::Runtime, cName.unbridged()); +} + +bool BridgedASTContext_langOptsIsActiveTargetPtrAuth(BridgedASTContext cContext, + BridgedStringRef cName) { + return cContext.unbridged().LangOpts.checkPlatformCondition( + PlatformConditionKind::PtrAuth, cName.unbridged()); +} + +unsigned BridgedASTContext_langOptsTargetPointerBitWidth(BridgedASTContext cContext) { + return cContext.unbridged().LangOpts.Target.isArch64Bit() ? 64 + : cContext.unbridged().LangOpts.Target.isArch32Bit() ? 32 + : cContext.unbridged().LangOpts.Target.isArch16Bit() ? 16 + : 0; +} + +BridgedEndianness BridgedASTContext_langOptsTargetEndianness(BridgedASTContext cContext) { + return cContext.unbridged().LangOpts.Target.isLittleEndian() ? EndianLittle + : EndianBig; +} + +/// Convert an array of numbers into a form we can use in Swift. +namespace { + template + SwiftInt convertArray(const Arr &array, SwiftInt **cElements) { + SwiftInt numElements = array.size(); + *cElements = (SwiftInt *)malloc(sizeof(SwiftInt) * numElements); + for (SwiftInt i = 0; i != numElements; ++i) + (*cElements)[i] = array[i]; + return numElements; + } +} + +SwiftInt BridgedASTContext_langOptsGetLanguageVersion(BridgedASTContext cContext, + SwiftInt** cComponents) { + auto theVersion = cContext.unbridged().LangOpts.EffectiveLanguageVersion; + return convertArray(theVersion, cComponents); +} + +SWIFT_NAME("BridgedASTContext.langOptsGetCompilerVersion(self:_:)") +SwiftInt BridgedASTContext_langOptsGetCompilerVersion(BridgedASTContext cContext, + SwiftInt** cComponents) { + auto theVersion = version::Version::getCurrentLanguageVersion(); + return convertArray(theVersion, cComponents); +} + +SwiftInt BridgedASTContext_langOptsGetTargetAtomicBitWidths(BridgedASTContext cContext, + SwiftInt* _Nullable * _Nonnull cElements) { + return convertArray(cContext.unbridged().LangOpts.getAtomicBitWidthValues(), + cElements); +} + +bool BridgedASTContext_canImport(BridgedASTContext cContext, + BridgedStringRef importPath, + BridgedCanImportVersion versionKind, + const SwiftInt * _Nullable versionComponents, + SwiftInt numVersionComponents) { + // Map the version. + llvm::VersionTuple version; + switch (numVersionComponents) { + case 0: + break; + case 1: + version = llvm::VersionTuple(versionComponents[0]); + break; + case 2: + version = llvm::VersionTuple(versionComponents[0], versionComponents[1]); + break; + case 3: + version = llvm::VersionTuple(versionComponents[0], versionComponents[1], + versionComponents[2]); + break; + default: + version = llvm::VersionTuple(versionComponents[0], versionComponents[1], + versionComponents[2], versionComponents[3]); + break; + } + + // FIXME: The source location here is empty because build configurations + // are supposed to be completely separated from source code. We could re-plumb + // things to have any errors reported up thruough the "canImportModule" + // API. + ImportPath::Module::Builder builder( + cContext.unbridged(), importPath.unbridged(), /*separator=*/'.', + SourceLoc()); + return cContext.unbridged().canImportModule( + builder.get(), SourceLoc(), version, + versionKind == CanImportUnderlyingVersion); +} + //===----------------------------------------------------------------------===// // MARK: AST nodes //===----------------------------------------------------------------------===// diff --git a/lib/ASTGen/CMakeLists.txt b/lib/ASTGen/CMakeLists.txt index 6897f14d59a2e..267e394c907ad 100644 --- a/lib/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/CMakeLists.txt @@ -21,6 +21,7 @@ endif() add_pure_swift_host_library(swiftASTGen STATIC Sources/ASTGen/ASTGen.swift Sources/ASTGen/Bridge.swift + Sources/ASTGen/CompilerBuildConfiguration.swift Sources/ASTGen/DeclAttrs.swift Sources/ASTGen/Decls.swift Sources/ASTGen/Diagnostics.swift @@ -44,6 +45,7 @@ add_pure_swift_host_library(swiftASTGen STATIC swiftAST SWIFT_DEPENDENCIES _CompilerSwiftSyntax + _CompilerSwiftIfConfig _CompilerSwiftOperators _CompilerSwiftSyntaxBuilder _CompilerSwiftParser diff --git a/lib/ASTGen/Package.swift b/lib/ASTGen/Package.swift index 14b50c1e78e36..96ccb3e21d62e 100644 --- a/lib/ASTGen/Package.swift +++ b/lib/ASTGen/Package.swift @@ -59,6 +59,7 @@ let package = Package( dependencies: [ .product(name: "_SwiftCompilerPluginMessageHandling", package: "swift-syntax"), .product(name: "SwiftDiagnostics", package: "swift-syntax"), + .product(name: "SwiftIfConfig", package: "swift-syntax"), .product(name: "SwiftOperators", package: "swift-syntax"), .product(name: "SwiftParser", package: "swift-syntax"), .product(name: "SwiftParserDiagnostics", package: "swift-syntax"), diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift new file mode 100644 index 0000000000000..7118333a9d1fe --- /dev/null +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -0,0 +1,146 @@ +//===--- CompilerBuildConfiguration.swift --------------\------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022-2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import ASTBridging +import SwiftIfConfig + +/// A build configuration that uses the compiler's ASTContext to answer +/// queries. +struct CompilerBuildConfiguration: BuildConfiguration { + let ctx: BridgedASTContext + + func isCustomConditionSet(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsCustomConditionSet(nameRef) + } + } + + func hasFeature(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsHasFeatureNamed(nameRef) + } + } + + func hasAttribute(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsHasAttributeNamed(nameRef) + } + } + + func canImport(importPath: [String], version: CanImportVersion) throws -> Bool { + var importPathStr = importPath.joined(separator: ".") + + var versionComponents: [Int] + let cVersionKind: BridgedCanImportVersion + switch version { + case .unversioned: + cVersionKind = .CanImportUnversioned + versionComponents = [] + + case .version(let versionTuple): + cVersionKind = .CanImportVersion + versionComponents = versionTuple.components + + case .underlyingVersion(let versionTuple): + cVersionKind = .CanImportUnderlyingVersion + versionComponents = versionTuple.components + } + + return importPathStr.withBridgedString { bridgedImportPathStr in + versionComponents.withUnsafeBufferPointer { versionComponentsBuf in + ctx.canImport( + importPath: bridgedImportPathStr, + versionKind: cVersionKind, + versionComponents: versionComponentsBuf.baseAddress, + numVersionComponents: versionComponentsBuf.count + ) + } + } + } + + func isActiveTargetOS(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsIsActiveTargetOS(nameRef) + } + } + + func isActiveTargetArchitecture(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsIsActiveTargetArchitecture(nameRef) + } + } + + func isActiveTargetEnvironment(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsIsActiveTargetEnvironment(nameRef) + } + } + + func isActiveTargetRuntime(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsIsActiveTargetRuntime(nameRef) + } + } + + func isActiveTargetPointerAuthentication(name: String) throws -> Bool { + var name = name + return name.withBridgedString { nameRef in + ctx.langOptsIsActiveTargetPtrAuth(nameRef) + } + } + + var targetPointerBitWidth: Int { + Int(ctx.langOptsTargetPointerBitWidth) + } + + var targetAtomicBitWidths: [Int] { + var bitWidthsBuf: UnsafeMutablePointer? = nil + let count = ctx.langOptsGetTargetAtomicBitWidths(&bitWidthsBuf) + let bitWidths = Array(UnsafeMutableBufferPointer(start: bitWidthsBuf, count: count)) + bitWidthsBuf?.deallocate() + return bitWidths + } + + var endianness: Endianness { + switch ctx.langOptsTargetEndianness { + case .EndianBig: return .big + case .EndianLittle: return .little + } + } + + var languageVersion: VersionTuple { + var componentsBuf: UnsafeMutablePointer? = nil + let count = ctx.langOptsGetLanguageVersion(&componentsBuf) + let version = VersionTuple( + components: Array(UnsafeMutableBufferPointer(start: componentsBuf, count: count)) + ) + componentsBuf?.deallocate() + return version + } + + var compilerVersion: VersionTuple { + var componentsBuf: UnsafeMutablePointer? = nil + let count = ctx.langOptsGetCompilerVersion(&componentsBuf) + let version = VersionTuple( + components: Array(UnsafeMutableBufferPointer(start: componentsBuf, count: count)) + ) + componentsBuf?.deallocate() + return version + } +} From d06e40877f716c0e91988cb19f581764b2af4afd Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 6 Jul 2024 19:42:36 -0700 Subject: [PATCH 2/8] Re-implement SourceFile::getIfConfigClauseRanges on top of SwiftIfConfig The SwiftIfConfig library provides APIs for evaluating and extracting the active #if regions in source code. Use its "configured regions" API along with the ASTContext-backed build configuration to reimplement the extraction of active/inactive regions from the source. This approach has the benefit of being effectively stateless: where the existing solution relies on the C++ parser recording all of the `#if` clauses it sees as it is parsing (and then might have to sort them later), this version does a scan of source to collect the list without requiring any other state. The newer implementation is also conceptually cleaner, and can be shared with other clients that have their own take on the build configuration. The primary client of this information is the SourceKit request that identifies "inactive" regions within the source file, which IDEs can use to grey out inactive code within the current build configuration. There is also some profiling information that uses it. Those clients should be unaffected by this under-the-hood change. For the moment, I'm leaving the old code path in place for compiler builds that don't have swift-syntax. This should be considered temporary, and that code should be removed in favor of request'ifying this function and removing the incrementally-built state entirely. --- include/swift/AST/ASTBridging.h | 44 +++++++++++++ include/swift/AST/IfConfigClauseRangeInfo.h | 62 +++++++++++++++++++ include/swift/AST/SourceFile.h | 39 +----------- include/swift/Bridging/ASTGen.h | 5 ++ lib/AST/Module.cpp | 17 +++++ .../ASTGen/CompilerBuildConfiguration.swift | 50 +++++++++++++++ lib/ASTGen/Sources/ASTGen/SourceFile.swift | 6 ++ 7 files changed, 185 insertions(+), 38 deletions(-) create mode 100644 include/swift/AST/IfConfigClauseRangeInfo.h diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 600065cd15138..eedbf8b9ec9d8 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -26,6 +26,7 @@ #include "swift/AST/Attr.h" #include "swift/AST/DiagnosticConsumer.h" #include "swift/AST/DiagnosticEngine.h" +#include "swift/AST/IfConfigClauseRangeInfo.h" #include "swift/AST/Stmt.h" #endif @@ -1912,6 +1913,49 @@ BridgedParameterList BridgedParameterList_createParsed( BridgedASTContext cContext, BridgedSourceLoc cLeftParenLoc, BridgedArrayRef cParameters, BridgedSourceLoc cRightParenLoc); +//===----------------------------------------------------------------------===// +// MARK: #if handling +//===----------------------------------------------------------------------===// + +/// Bridged version of IfConfigClauseRangeInfo::ClauseKind. +enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedIfConfigClauseKind : size_t { + IfConfigActive, + IfConfigInactive, + IfConfigEnd +}; + +/// Bridged version of IfConfigClauseRangeInfo. +struct BridgedIfConfigClauseRangeInfo { + BridgedSourceLoc directiveLoc; + BridgedSourceLoc bodyLoc; + BridgedSourceLoc endLoc; + BridgedIfConfigClauseKind kind; + +#ifdef USED_IN_CPP_SOURCE + swift::IfConfigClauseRangeInfo unbridged() const { + swift::IfConfigClauseRangeInfo::ClauseKind clauseKind; + switch (kind) { + case IfConfigActive: + clauseKind = swift::IfConfigClauseRangeInfo::ActiveClause; + break; + + case IfConfigInactive: + clauseKind = swift::IfConfigClauseRangeInfo::InactiveClause; + break; + + case IfConfigEnd: + clauseKind = swift::IfConfigClauseRangeInfo::EndDirective; + break; + } + + return swift::IfConfigClauseRangeInfo(directiveLoc.unbridged(), + bodyLoc.unbridged(), + endLoc.unbridged(), + clauseKind); + } +#endif +}; + //===----------------------------------------------------------------------===// // MARK: Plugins //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/IfConfigClauseRangeInfo.h b/include/swift/AST/IfConfigClauseRangeInfo.h new file mode 100644 index 0000000000000..8f9acdf369286 --- /dev/null +++ b/include/swift/AST/IfConfigClauseRangeInfo.h @@ -0,0 +1,62 @@ +//===--- ASTBridging.h - header for the swift SILBridging module ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H +#define SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H + +#include "swift/Basic/SourceLoc.h" + +namespace swift { + +class SourceManager; + +/// Stores range information for a \c #if block in a SourceFile. +class IfConfigClauseRangeInfo final { +public: + enum ClauseKind { + // Active '#if', '#elseif', or '#else' clause. + ActiveClause, + // Inactive '#if', '#elseif', or '#else' clause. + InactiveClause, + // '#endif' directive. + EndDirective, + }; + +private: + /// Source location of '#if', '#elseif', etc. + SourceLoc DirectiveLoc; + /// Character source location of body starts. + SourceLoc BodyLoc; + /// Location of the end of the body. + SourceLoc EndLoc; + + ClauseKind Kind; + +public: + IfConfigClauseRangeInfo(SourceLoc DirectiveLoc, SourceLoc BodyLoc, + SourceLoc EndLoc, ClauseKind Kind) + : DirectiveLoc(DirectiveLoc), BodyLoc(BodyLoc), EndLoc(EndLoc), + Kind(Kind) { + assert(DirectiveLoc.isValid() && BodyLoc.isValid() && EndLoc.isValid()); + } + + SourceLoc getStartLoc() const { return DirectiveLoc; } + CharSourceRange getDirectiveRange(const SourceManager &SM) const; + CharSourceRange getWholeRange(const SourceManager &SM) const; + CharSourceRange getBodyRange(const SourceManager &SM) const; + + ClauseKind getKind() const { return Kind; } +}; + +} + +#endif /* SWIFT_AST_IFCONFIGCLAUSERANGEINFO_H */ diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 0ca7585aca36b..e7b2ea10403b8 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -15,6 +15,7 @@ #include "swift/AST/ASTNode.h" #include "swift/AST/FileUnit.h" +#include "swift/AST/IfConfigClauseRangeInfo.h" #include "swift/AST/Import.h" #include "swift/AST/SynthesizedFileUnit.h" #include "swift/Basic/Debug.h" @@ -48,44 +49,6 @@ enum class RestrictedImportKind { /// Import that limits the access level of imported entities. using ImportAccessLevel = std::optional>; -/// Stores range information for a \c #if block in a SourceFile. -class IfConfigClauseRangeInfo final { -public: - enum ClauseKind { - // Active '#if', '#elseif', or '#else' clause. - ActiveClause, - // Inactive '#if', '#elseif', or '#else' clause. - InactiveClause, - // '#endif' directive. - EndDirective, - }; - -private: - /// Source location of '#if', '#elseif', etc. - SourceLoc DirectiveLoc; - /// Character source location of body starts. - SourceLoc BodyLoc; - /// Location of the end of the body. - SourceLoc EndLoc; - - ClauseKind Kind; - -public: - IfConfigClauseRangeInfo(SourceLoc DirectiveLoc, SourceLoc BodyLoc, - SourceLoc EndLoc, ClauseKind Kind) - : DirectiveLoc(DirectiveLoc), BodyLoc(BodyLoc), EndLoc(EndLoc), - Kind(Kind) { - assert(DirectiveLoc.isValid() && BodyLoc.isValid() && EndLoc.isValid()); - } - - SourceLoc getStartLoc() const { return DirectiveLoc; } - CharSourceRange getDirectiveRange(const SourceManager &SM) const; - CharSourceRange getWholeRange(const SourceManager &SM) const; - CharSourceRange getBodyRange(const SourceManager &SM) const; - - ClauseKind getKind() const { return Kind; } -}; - /// A file containing Swift source code. /// /// This is a .swift or .sil file (or a virtual file, such as the contents of diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 14eaae3cd3ccd..6974f915af9b4 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -151,6 +151,11 @@ bool swift_ASTGen_parseRegexLiteral(BridgedStringRef inputPtr, BridgedSourceLoc diagLoc, BridgedDiagnosticEngine diagEngine); +intptr_t swift_ASTGen_configuredRegions( + BridgedASTContext astContext, + void *_Nonnull sourceFile, + BridgedIfConfigClauseRangeInfo *_Nullable *_Nonnull); + #ifdef __cplusplus } #endif diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 27389af3ddea7..7db90c2e25dc3 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -47,6 +47,7 @@ #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" +#include "swift/Bridging/ASTGen.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Token.h" #include "swift/Strings.h" @@ -2964,6 +2965,21 @@ void SourceFile::recordIfConfigClauseRangeInfo( } ArrayRef SourceFile::getIfConfigClauseRanges() const { +#if SWIFT_BUILD_SWIFT_SYNTAX + if (!IfConfigClauseRanges.IsSorted) { + IfConfigClauseRanges.Ranges.clear(); + + BridgedIfConfigClauseRangeInfo *regions; + intptr_t numRegions = swift_ASTGen_configuredRegions( + getASTContext(), getExportedSourceFile(), ®ions); + IfConfigClauseRanges.Ranges.reserve(numRegions); + for (intptr_t i = 0; i != numRegions; ++i) + IfConfigClauseRanges.Ranges.push_back(regions[i].unbridged()); + free(regions); + + IfConfigClauseRanges.IsSorted = true; + } +#else if (!IfConfigClauseRanges.IsSorted) { auto &SM = getASTContext().SourceMgr; // Sort the ranges if we need to. @@ -2987,6 +3003,7 @@ ArrayRef SourceFile::getIfConfigClauseRanges() const { IfConfigClauseRanges.Ranges.end()); IfConfigClauseRanges.IsSorted = true; } +#endif return IfConfigClauseRanges.Ranges; } diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift index 7118333a9d1fe..cb01247da5e41 100644 --- a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -12,6 +12,7 @@ import ASTBridging import SwiftIfConfig +import SwiftSyntax /// A build configuration that uses the compiler's ASTContext to answer /// queries. @@ -144,3 +145,52 @@ struct CompilerBuildConfiguration: BuildConfiguration { return version } } + +/// Extract the #if clause range information for the given source file. +@_cdecl("swift_ASTGen_configuredRegions") +public func configuredRegions( + astContext: BridgedASTContext, + sourceFilePtr: UnsafeRawPointer, + cRegionsOut: UnsafeMutablePointer?> +) -> Int { + let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1) + let configuration = CompilerBuildConfiguration(ctx: astContext) + let regions = sourceFilePtr.pointee.syntax.configuredRegions(in: configuration) + + var cRegions: [BridgedIfConfigClauseRangeInfo] = [] + for (ifConfig, state) in regions { + let kind: BridgedIfConfigClauseKind + switch state { + case .active: kind = .IfConfigActive + case .inactive, .unparsed: kind = .IfConfigInactive + } + + let bodyLoc: AbsolutePosition + if let elements = ifConfig.elements { + bodyLoc = elements.position + } else if let condition = ifConfig.condition { + bodyLoc = condition.endPosition + } else { + bodyLoc = ifConfig.endPosition + } + + cRegions.append( + .init( + directiveLoc: sourceFilePtr.pointee.sourceLoc( + at: ifConfig.poundKeyword.positionAfterSkippingLeadingTrivia + ), + bodyLoc: sourceFilePtr.pointee.sourceLoc(at: bodyLoc), + endLoc: sourceFilePtr.pointee.sourceLoc( + at: ifConfig.endPosition + ), + kind: kind + ) + ) + } + + let cRegionsBuf: UnsafeMutableBufferPointer = + .allocate(capacity: cRegions.count) + _ = cRegionsBuf.initialize(from: cRegions) + cRegionsOut.pointee = cRegionsBuf.baseAddress + return cRegionsBuf.count +} diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index 0c2ed36f39b71..9e8abf147a52c 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceFile.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceFile.swift @@ -44,6 +44,12 @@ public struct ExportedSourceFile { } return AbsolutePosition(utf8Offset: opaqueValue - sourceFileBaseAddress) } + + /// Retrieve a bridged source location for the given absolute position in + /// this source file. + public func sourceLoc(at position: AbsolutePosition) -> BridgedSourceLoc { + BridgedSourceLoc(at: position, in: buffer) + } } extension Parser.ExperimentalFeatures { From ca588b7aea5519c4a2bfd0c2aee6adcbfab2e8f6 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 7 Jul 2024 09:28:05 -0700 Subject: [PATCH 3/8] Insert `#endif` directives into `#if` config ranges list produced by SwiftIfConfig The SIL coverage map generation depends on the locations of the `#endif` directives, but the mapping from SwiftIfConfig's configured regions wasn't producing them. The information is implicitly available in the SwiftIfConfig configured regions, so reconstitute it as we translate regions. --- .../ASTGen/CompilerBuildConfiguration.swift | 87 ++++++++++++++++++- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift index cb01247da5e41..5467da6903a99 100644 --- a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -158,7 +158,61 @@ public func configuredRegions( let regions = sourceFilePtr.pointee.syntax.configuredRegions(in: configuration) var cRegions: [BridgedIfConfigClauseRangeInfo] = [] + + // Keep track of the enclosing #ifs so that we can emit and "#endif" directive + // right before moving on to the next #if (and at the end). + var ifConfigStack: [IfConfigDeclSyntax] = [] + + /// Emit the #endif location for the given #if declaration. + func flushSingleIfConfig(_ topIfConfigDecl: IfConfigDeclSyntax) { + cRegions.append( + .init( + directiveLoc: sourceFilePtr.pointee.sourceLoc( + at: topIfConfigDecl.poundEndif.positionAfterSkippingLeadingTrivia + ), + bodyLoc: sourceFilePtr.pointee.sourceLoc( + at: topIfConfigDecl.poundEndif.endPosition + ), + endLoc: sourceFilePtr.pointee.sourceLoc( + at: topIfConfigDecl.poundEndif.endPosition + ), + kind: .IfConfigEnd + ) + ) + } + + /// Push a new #if declaration into the stack so that we'll insert #endifs + /// in the right places. + func pushIfConfig(_ currentIfConfigDecl: IfConfigDeclSyntax) { + // Go through the current stack of #if declarations. + while let topIfConfig = ifConfigStack.last { + // If the top of the stack is the same as this #if, we're done. + if topIfConfig == currentIfConfigDecl { + return + } + + // If the top of the stack is not an ancestor of this #if, flush it + // and keep going. + if !topIfConfig.isAncestor(of: currentIfConfigDecl) { + flushSingleIfConfig(topIfConfig) + ifConfigStack.removeLast() + continue + } + + break + } + + // Add this #if to the stack. + ifConfigStack.append(currentIfConfigDecl) + } + + // Translate all of the configured regions. for (ifConfig, state) in regions { + // Note that we're handling an #if now. + if let currentIfConfigDecl = ifConfig.parent?.parent?.as(IfConfigDeclSyntax.self) { + pushIfConfig(currentIfConfigDecl) + } + let kind: BridgedIfConfigClauseKind switch state { case .active: kind = .IfConfigActive @@ -174,23 +228,50 @@ public func configuredRegions( bodyLoc = ifConfig.endPosition } + let endLoc: AbsolutePosition + if let nextToken = ifConfig.nextToken(viewMode: .sourceAccurate) { + endLoc = nextToken.positionAfterSkippingLeadingTrivia + } else { + endLoc = ifConfig.endPosition + } + cRegions.append( .init( directiveLoc: sourceFilePtr.pointee.sourceLoc( at: ifConfig.poundKeyword.positionAfterSkippingLeadingTrivia ), bodyLoc: sourceFilePtr.pointee.sourceLoc(at: bodyLoc), - endLoc: sourceFilePtr.pointee.sourceLoc( - at: ifConfig.endPosition - ), + endLoc: sourceFilePtr.pointee.sourceLoc(at: endLoc), kind: kind ) ) } + // Flush the remaining #ifs. + while let topIfConfig = ifConfigStack.popLast() { + flushSingleIfConfig(topIfConfig) + } + let cRegionsBuf: UnsafeMutableBufferPointer = .allocate(capacity: cRegions.count) _ = cRegionsBuf.initialize(from: cRegions) cRegionsOut.pointee = cRegionsBuf.baseAddress return cRegionsBuf.count } + +extension SyntaxProtocol { + /// Determine whether this node is an ancestor of the given `other` node. + func isAncestor(of other: some SyntaxProtocol) -> Bool { + var other = Syntax(other) + let selfSyntax = Syntax(self) + while let otherParent = other.parent { + if otherParent == selfSyntax { + return true + } + + other = otherParent + } + + return false + } +} From ddbbb5a71e83de225512a2252c7eb42165910c16 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 8 Jul 2024 14:45:13 -0700 Subject: [PATCH 4/8] Address code review comments and build failure --- include/swift/AST/IfConfigClauseRangeInfo.h | 2 +- lib/AST/ASTBridging.cpp | 2 +- lib/AST/Module.cpp | 4 ++++ lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift | 2 +- lib/CompilerSwiftSyntax/CMakeLists.txt | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/IfConfigClauseRangeInfo.h b/include/swift/AST/IfConfigClauseRangeInfo.h index 8f9acdf369286..a49d258e46ba5 100644 --- a/include/swift/AST/IfConfigClauseRangeInfo.h +++ b/include/swift/AST/IfConfigClauseRangeInfo.h @@ -1,4 +1,4 @@ -//===--- ASTBridging.h - header for the swift SILBridging module ----------===// +//===--- IfConfigClauseRangeInfo.h - header for #if clauses -=====---------===// // // This source file is part of the Swift.org open source project // diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index b2021a0168c76..19e2ec63856a6 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -238,7 +238,7 @@ bool BridgedASTContext_canImport(BridgedASTContext cContext, // FIXME: The source location here is empty because build configurations // are supposed to be completely separated from source code. We could re-plumb - // things to have any errors reported up thruough the "canImportModule" + // things to have any errors reported up through the "canImportModule" // API. ImportPath::Module::Builder builder( cContext.unbridged(), importPath.unbridged(), /*separator=*/'.', diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 7db90c2e25dc3..ae8f53c3eae76 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2960,8 +2960,12 @@ IfConfigClauseRangeInfo::getWholeRange(const SourceManager &SM) const { void SourceFile::recordIfConfigClauseRangeInfo( const IfConfigClauseRangeInfo &range) { +#if SWIFT_BUILD_SWIFT_SYNTAX + // Don't record ranges; they'll be extracted from swift-syntax when needed. +#else IfConfigClauseRanges.Ranges.push_back(range); IfConfigClauseRanges.IsSorted = false; +#endif } ArrayRef SourceFile::getIfConfigClauseRanges() const { diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift index 5467da6903a99..38c90b49f2774 100644 --- a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -1,4 +1,4 @@ -//===--- CompilerBuildConfiguration.swift --------------\------------------===// +//===--- CompilerBuildConfiguration.swift ---------------------------------===// // // This source file is part of the Swift.org open source project // diff --git a/lib/CompilerSwiftSyntax/CMakeLists.txt b/lib/CompilerSwiftSyntax/CMakeLists.txt index 79c1605d99324..3bf18992ad875 100644 --- a/lib/CompilerSwiftSyntax/CMakeLists.txt +++ b/lib/CompilerSwiftSyntax/CMakeLists.txt @@ -30,6 +30,7 @@ includeSwiftSyntax() set(compiler_swiftsyntax_libs _CompilerSwiftSyntax + _CompilerSwiftIfConfig _CompilerSwiftOperators _CompilerSwiftSyntaxBuilder _CompilerSwiftParser From 0c9a8a8b22918fb2080e77a167f5edc1449b4950 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 15 Jul 2024 09:52:04 -0700 Subject: [PATCH 5/8] Sink implementation of SourceFile::getIfConfigClauseRanges into Sema This implementation depends on ASTGen, which isn't linked as part of the AST library. --- lib/AST/Module.cpp | 71 -------------------------------------- lib/Sema/TypeCheckDecl.cpp | 71 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index ae8f53c3eae76..e4963de28845a 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -47,7 +47,6 @@ #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" -#include "swift/Bridging/ASTGen.h" #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Token.h" #include "swift/Strings.h" @@ -2968,76 +2967,6 @@ void SourceFile::recordIfConfigClauseRangeInfo( #endif } -ArrayRef SourceFile::getIfConfigClauseRanges() const { -#if SWIFT_BUILD_SWIFT_SYNTAX - if (!IfConfigClauseRanges.IsSorted) { - IfConfigClauseRanges.Ranges.clear(); - - BridgedIfConfigClauseRangeInfo *regions; - intptr_t numRegions = swift_ASTGen_configuredRegions( - getASTContext(), getExportedSourceFile(), ®ions); - IfConfigClauseRanges.Ranges.reserve(numRegions); - for (intptr_t i = 0; i != numRegions; ++i) - IfConfigClauseRanges.Ranges.push_back(regions[i].unbridged()); - free(regions); - - IfConfigClauseRanges.IsSorted = true; - } -#else - if (!IfConfigClauseRanges.IsSorted) { - auto &SM = getASTContext().SourceMgr; - // Sort the ranges if we need to. - llvm::sort( - IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs, - const IfConfigClauseRangeInfo &rhs) { - return SM.isBeforeInBuffer(lhs.getStartLoc(), rhs.getStartLoc()); - }); - - // Be defensive and eliminate duplicates in case we've parsed twice. - auto newEnd = llvm::unique( - IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs, - const IfConfigClauseRangeInfo &rhs) { - if (lhs.getStartLoc() != rhs.getStartLoc()) - return false; - assert(lhs.getBodyRange(SM) == rhs.getBodyRange(SM) && - "range changed on a re-parse?"); - return true; - }); - IfConfigClauseRanges.Ranges.erase(newEnd, - IfConfigClauseRanges.Ranges.end()); - IfConfigClauseRanges.IsSorted = true; - } -#endif - - return IfConfigClauseRanges.Ranges; -} - -ArrayRef -SourceFile::getIfConfigClausesWithin(SourceRange outer) const { - auto &SM = getASTContext().SourceMgr; - assert(SM.getRangeForBuffer(BufferID).contains(outer.Start) && - "Range not within this file?"); - - // First let's find the first #if that is after the outer start loc. - auto ranges = getIfConfigClauseRanges(); - auto lower = llvm::lower_bound( - ranges, outer.Start, - [&](const IfConfigClauseRangeInfo &range, SourceLoc loc) { - return SM.isBeforeInBuffer(range.getStartLoc(), loc); - }); - if (lower == ranges.end() || - SM.isBeforeInBuffer(outer.End, lower->getStartLoc())) { - return {}; - } - // Next let's find the first #if that's after the outer end loc. - auto upper = llvm::upper_bound( - ranges, outer.End, - [&](SourceLoc loc, const IfConfigClauseRangeInfo &range) { - return SM.isBeforeInBuffer(loc, range.getStartLoc()); - }); - return llvm::ArrayRef(lower, upper - lower); -} - void ModuleDecl::setPackageName(Identifier name) { Package = PackageUnit::create(name, *this, getASTContext()); } diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3c05eeb7798d4..7c595f2ea73aa 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -48,6 +48,7 @@ #include "swift/AST/TypeWalker.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/Defer.h" +#include "swift/Bridging/ASTGen.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" #include "swift/Sema/IDETypeChecking.h" @@ -3121,3 +3122,73 @@ LifetimeDependenceInfoRequest::evaluate(Evaluator &evaluator, AbstractFunctionDecl *decl) const { return LifetimeDependenceInfo::get(decl); } + +ArrayRef SourceFile::getIfConfigClauseRanges() const { +#if SWIFT_BUILD_SWIFT_SYNTAX + if (!IfConfigClauseRanges.IsSorted) { + IfConfigClauseRanges.Ranges.clear(); + + BridgedIfConfigClauseRangeInfo *regions; + intptr_t numRegions = swift_ASTGen_configuredRegions( + getASTContext(), getExportedSourceFile(), ®ions); + IfConfigClauseRanges.Ranges.reserve(numRegions); + for (intptr_t i = 0; i != numRegions; ++i) + IfConfigClauseRanges.Ranges.push_back(regions[i].unbridged()); + free(regions); + + IfConfigClauseRanges.IsSorted = true; + } +#else + if (!IfConfigClauseRanges.IsSorted) { + auto &SM = getASTContext().SourceMgr; + // Sort the ranges if we need to. + llvm::sort( + IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs, + const IfConfigClauseRangeInfo &rhs) { + return SM.isBeforeInBuffer(lhs.getStartLoc(), rhs.getStartLoc()); + }); + + // Be defensive and eliminate duplicates in case we've parsed twice. + auto newEnd = llvm::unique( + IfConfigClauseRanges.Ranges, [&](const IfConfigClauseRangeInfo &lhs, + const IfConfigClauseRangeInfo &rhs) { + if (lhs.getStartLoc() != rhs.getStartLoc()) + return false; + assert(lhs.getBodyRange(SM) == rhs.getBodyRange(SM) && + "range changed on a re-parse?"); + return true; + }); + IfConfigClauseRanges.Ranges.erase(newEnd, + IfConfigClauseRanges.Ranges.end()); + IfConfigClauseRanges.IsSorted = true; + } +#endif + + return IfConfigClauseRanges.Ranges; +} + +ArrayRef +SourceFile::getIfConfigClausesWithin(SourceRange outer) const { + auto &SM = getASTContext().SourceMgr; + assert(SM.getRangeForBuffer(BufferID).contains(outer.Start) && + "Range not within this file?"); + + // First let's find the first #if that is after the outer start loc. + auto ranges = getIfConfigClauseRanges(); + auto lower = llvm::lower_bound( + ranges, outer.Start, + [&](const IfConfigClauseRangeInfo &range, SourceLoc loc) { + return SM.isBeforeInBuffer(range.getStartLoc(), loc); + }); + if (lower == ranges.end() || + SM.isBeforeInBuffer(outer.End, lower->getStartLoc())) { + return {}; + } + // Next let's find the first #if that's after the outer end loc. + auto upper = llvm::upper_bound( + ranges, outer.End, + [&](SourceLoc loc, const IfConfigClauseRangeInfo &range) { + return SM.isBeforeInBuffer(loc, range.getStartLoc()); + }); + return llvm::ArrayRef(lower, upper - lower); +} From a844f4fa0bf8dfbcef962bc66d2b27a51c11d8bc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Jul 2024 08:48:35 -0700 Subject: [PATCH 6/8] Free memory allocated in Swift from Swift, not C++ --- include/swift/Bridging/ASTGen.h | 2 ++ .../Sources/ASTGen/CompilerBuildConfiguration.swift | 8 ++++++++ lib/Sema/TypeCheckDecl.cpp | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 6974f915af9b4..bb09e8b0097c6 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -155,6 +155,8 @@ intptr_t swift_ASTGen_configuredRegions( BridgedASTContext astContext, void *_Nonnull sourceFile, BridgedIfConfigClauseRangeInfo *_Nullable *_Nonnull); +void swift_ASTGen_freeConfiguredRegions( + BridgedIfConfigClauseRangeInfo *_Nullable regions, intptr_t numRegions); #ifdef __cplusplus } diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift index 38c90b49f2774..907c6bab32c58 100644 --- a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -275,3 +275,11 @@ extension SyntaxProtocol { return false } } + +@_cdecl("swift_ASTGen_freeConfiguredRegions") +public func freeConfiguredRegions( + regions: UnsafeMutablePointer?, + numRegions: Int +) { + UnsafeMutableBufferPointer(start: regions, count: numRegions).deallocate() +} diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 7c595f2ea73aa..642c80dc2d177 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -3134,7 +3134,7 @@ ArrayRef SourceFile::getIfConfigClauseRanges() const { IfConfigClauseRanges.Ranges.reserve(numRegions); for (intptr_t i = 0; i != numRegions; ++i) IfConfigClauseRanges.Ranges.push_back(regions[i].unbridged()); - free(regions); + swift_ASTGen_freeConfiguredRegions(regions, numRegions); IfConfigClauseRanges.IsSorted = true; } From d686a3e1c0a2b994b1f14dcf605f5ac1be219147 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 4 Aug 2024 05:57:37 -0700 Subject: [PATCH 7/8] Handle `$FeatureName` custom conditions in CompilerBuildConfiguration --- lib/AST/ASTBridging.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index 19e2ec63856a6..ace9e3aadcf97 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -123,8 +123,12 @@ unsigned BridgedASTContext_majorLanguageVersion(BridgedASTContext cContext) { bool BridgedASTContext_langOptsCustomConditionSet(BridgedASTContext cContext, BridgedStringRef cName) { - return cContext.unbridged().LangOpts - .isCustomConditionalCompilationFlagSet(cName.unbridged()); + ASTContext &ctx = cContext.unbridged(); + auto name = cName.unbridged(); + if (name.starts_with("$") && ctx.LangOpts.hasFeature(name.drop_front())) + return true; + + return ctx.LangOpts.isCustomConditionalCompilationFlagSet(name); } bool BridgedASTContext_langOptsHasFeatureNamed(BridgedASTContext cContext, From cfd0a3fc539aa5bd2e12ac75d38c609019b9e617 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sun, 4 Aug 2024 05:58:53 -0700 Subject: [PATCH 8/8] Thread a source location through canImport evaluation in CompilerBuildConfiguration Unlike all of the other build configuration checks, `canImport` has side effects including the emission of various diagnostics. Thread a source location through here so the diagnostics end up on the right line. --- include/swift/AST/ASTBridging.h | 3 ++- lib/AST/ASTBridging.cpp | 9 +++------ lib/AST/ASTContext.cpp | 5 ++++- .../Sources/ASTGen/CompilerBuildConfiguration.swift | 11 +++++++++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index eedbf8b9ec9d8..9fde6d693a6f0 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -252,9 +252,10 @@ enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedCanImportVersion : size_t { CanImportUnderlyingVersion, }; -SWIFT_NAME("BridgedASTContext.canImport(self:importPath:versionKind:versionComponents:numVersionComponents:)") +SWIFT_NAME("BridgedASTContext.canImport(self:importPath:location:versionKind:versionComponents:numVersionComponents:)") bool BridgedASTContext_canImport(BridgedASTContext cContext, BridgedStringRef importPath, + BridgedSourceLoc canImportLoc, BridgedCanImportVersion versionKind, const SwiftInt * _Nullable versionComponents, SwiftInt numVersionComponents); diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index ace9e3aadcf97..d8fee0be9ac68 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -216,6 +216,7 @@ SwiftInt BridgedASTContext_langOptsGetTargetAtomicBitWidths(BridgedASTContext cC bool BridgedASTContext_canImport(BridgedASTContext cContext, BridgedStringRef importPath, + BridgedSourceLoc canImportLoc, BridgedCanImportVersion versionKind, const SwiftInt * _Nullable versionComponents, SwiftInt numVersionComponents) { @@ -240,15 +241,11 @@ bool BridgedASTContext_canImport(BridgedASTContext cContext, break; } - // FIXME: The source location here is empty because build configurations - // are supposed to be completely separated from source code. We could re-plumb - // things to have any errors reported up through the "canImportModule" - // API. ImportPath::Module::Builder builder( cContext.unbridged(), importPath.unbridged(), /*separator=*/'.', - SourceLoc()); + canImportLoc.unbridged()); return cContext.unbridged().canImportModule( - builder.get(), SourceLoc(), version, + builder.get(), canImportLoc.unbridged(), version, versionKind == CanImportUnderlyingVersion); } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 6f6d313049852..cd00ac1d6105f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2598,7 +2598,10 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName, // The module version could not be parsed from the preferred source for // this query. Diagnose and treat the query as if it succeeded. auto mID = ModuleName[0]; - Diags.diagnose(mID.Loc, diag::cannot_find_project_version, + auto diagLoc = mID.Loc; + if (mID.Loc.isInvalid()) + diagLoc = loc; + Diags.diagnose(diagLoc, diag::cannot_find_project_version, getModuleVersionKindString(bestVersionInfo), mID.Item.str()); return true; } diff --git a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift index 907c6bab32c58..5ce979a849e8e 100644 --- a/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -11,13 +11,16 @@ //===----------------------------------------------------------------------===// import ASTBridging +import SwiftDiagnostics import SwiftIfConfig +import SwiftParser import SwiftSyntax /// A build configuration that uses the compiler's ASTContext to answer /// queries. struct CompilerBuildConfiguration: BuildConfiguration { let ctx: BridgedASTContext + let conditionLoc: BridgedSourceLoc func isCustomConditionSet(name: String) throws -> Bool { var name = name @@ -63,6 +66,7 @@ struct CompilerBuildConfiguration: BuildConfiguration { versionComponents.withUnsafeBufferPointer { versionComponentsBuf in ctx.canImport( importPath: bridgedImportPathStr, + location: conditionLoc, versionKind: cVersionKind, versionComponents: versionComponentsBuf.baseAddress, numVersionComponents: versionComponentsBuf.count @@ -154,12 +158,15 @@ public func configuredRegions( cRegionsOut: UnsafeMutablePointer?> ) -> Int { let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1) - let configuration = CompilerBuildConfiguration(ctx: astContext) + let configuration = CompilerBuildConfiguration( + ctx: astContext, + conditionLoc: sourceFilePtr.pointee.sourceLoc(at: AbsolutePosition(utf8Offset: 0)) + ) let regions = sourceFilePtr.pointee.syntax.configuredRegions(in: configuration) var cRegions: [BridgedIfConfigClauseRangeInfo] = [] - // Keep track of the enclosing #ifs so that we can emit and "#endif" directive + // Keep track of the enclosing #ifs so that we can emit an "#endif" directive // right before moving on to the next #if (and at the end). var ifConfigStack: [IfConfigDeclSyntax] = []