diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index d5ba948bfec2b..9fde6d693a6f0 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 @@ -190,6 +191,75 @@ 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:location:versionKind:versionComponents:numVersionComponents:)") +bool BridgedASTContext_canImport(BridgedASTContext cContext, + BridgedStringRef importPath, + BridgedSourceLoc canImportLoc, + BridgedCanImportVersion versionKind, + const SwiftInt * _Nullable versionComponents, + SwiftInt numVersionComponents); + //===----------------------------------------------------------------------===// // MARK: AST nodes //===----------------------------------------------------------------------===// @@ -1844,6 +1914,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..a49d258e46ba5 --- /dev/null +++ b/include/swift/AST/IfConfigClauseRangeInfo.h @@ -0,0 +1,62 @@ +//===--- IfConfigClauseRangeInfo.h - header for #if clauses -=====---------===// +// +// 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/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/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 14eaae3cd3ccd..bb09e8b0097c6 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -151,6 +151,13 @@ bool swift_ASTGen_parseRegexLiteral(BridgedStringRef inputPtr, BridgedSourceLoc diagLoc, BridgedDiagnosticEngine diagEngine); +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 } #endif diff --git a/lib/AST/ASTBridging.cpp b/lib/AST/ASTBridging.cpp index 64ffa54a54348..d8fee0be9ac68 100644 --- a/lib/AST/ASTBridging.cpp +++ b/lib/AST/ASTBridging.cpp @@ -121,6 +121,134 @@ unsigned BridgedASTContext_majorLanguageVersion(BridgedASTContext cContext) { return cContext.unbridged().LangOpts.EffectiveLanguageVersion[0]; } +bool BridgedASTContext_langOptsCustomConditionSet(BridgedASTContext cContext, + BridgedStringRef cName) { + 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, + 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, + BridgedSourceLoc canImportLoc, + 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; + } + + ImportPath::Module::Builder builder( + cContext.unbridged(), importPath.unbridged(), /*separator=*/'.', + canImportLoc.unbridged()); + return cContext.unbridged().canImportModule( + builder.get(), canImportLoc.unbridged(), version, + versionKind == CanImportUnderlyingVersion); +} + //===----------------------------------------------------------------------===// // MARK: AST nodes //===----------------------------------------------------------------------===// 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/AST/Module.cpp b/lib/AST/Module.cpp index 27389af3ddea7..e4963de28845a 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -2959,62 +2959,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; -} - -ArrayRef SourceFile::getIfConfigClauseRanges() const { - 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; - } - - 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); +#endif } void ModuleDecl::setPackageName(Identifier name) { 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..5ce979a849e8e --- /dev/null +++ b/lib/ASTGen/Sources/ASTGen/CompilerBuildConfiguration.swift @@ -0,0 +1,292 @@ +//===--- 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 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 + 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, + location: conditionLoc, + 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 + } +} + +/// 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, + 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 an "#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 + 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 + } + + 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: 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 + } +} + +@_cdecl("swift_ASTGen_freeConfiguredRegions") +public func freeConfiguredRegions( + regions: UnsafeMutablePointer?, + numRegions: Int +) { + UnsafeMutableBufferPointer(start: regions, count: numRegions).deallocate() +} 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 { 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 diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 3c05eeb7798d4..642c80dc2d177 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()); + swift_ASTGen_freeConfiguredRegions(regions, numRegions); + + 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); +}