diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 95579605f2928..6498d881a0657 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -39,8 +39,8 @@ class ASTMangler : public Mangler { bool OptimizeProtocolNames = true; /// If enabled, use Objective-C runtime names when mangling @objc Swift - /// protocols. - bool UseObjCProtocolNames = false; + /// protocols and classes. + bool UseObjCRuntimeNames = false; /// If enabled, non-canonical types are allowed and type alias types get a /// special mangling. diff --git a/include/swift/AST/DiagnosticsCommon.def b/include/swift/AST/DiagnosticsCommon.def index 77212755457db..e7de3a3e203c8 100644 --- a/include/swift/AST/DiagnosticsCommon.def +++ b/include/swift/AST/DiagnosticsCommon.def @@ -139,6 +139,14 @@ ERROR(sdk_node_unrecognized_decl_kind,none, ERROR(sdk_node_unrecognized_accessor_kind,none, "unrecognized accessor kind '%0' in SDK node", (StringRef)) +// Emitted from ModuleDecl::computeMagicFileStringMap() +WARNING(pound_source_location_creates_pound_file_conflicts,none, + "'#sourceLocation' directive produces '#file' string of '%0', which " + "conflicts with '#file' strings produced by other paths in the module", + (StringRef)) +NOTE(fixit_correct_source_location_file,none, + "change file in '#sourceLocation' to '%0'", (StringRef)) + //------------------------------------------------------------------------------ // MARK: Circular reference diagnostics //------------------------------------------------------------------------------ diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 618ebdbb061e9..e3e22833503a7 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -109,6 +109,38 @@ enum class SourceFileKind { Interface ///< Came from a .swiftinterface file, representing another module. }; +/// Contains information about where a particular path is used in +/// \c SourceFiles. +struct SourceFilePathInfo { + struct Comparator { + bool operator () (SourceLoc lhs, SourceLoc rhs) const { + return lhs.getOpaquePointerValue() < + rhs.getOpaquePointerValue(); + } + }; + + SourceLoc physicalFileLoc{}; + std::set virtualFileLocs{}; // std::set for sorting + + SourceFilePathInfo() = default; + + void merge(const SourceFilePathInfo &other) { + if (other.physicalFileLoc.isValid()) { + assert(!physicalFileLoc.isValid()); + physicalFileLoc = other.physicalFileLoc; + } + + for (auto &elem : other.virtualFileLocs) { + virtualFileLocs.insert(elem); + } + } + + bool operator == (const SourceFilePathInfo &other) const { + return physicalFileLoc == other.physicalFileLoc && + virtualFileLocs == other.virtualFileLocs; + } +}; + /// Discriminator for resilience strategy. enum class ResilienceStrategy : unsigned { /// Public nominal types: fragile @@ -257,6 +289,24 @@ class ModuleDecl : public DeclContext, public TypeDecl { void addFile(FileUnit &newFile); void removeFile(FileUnit &existingFile); + /// Creates a map from \c #filePath strings to corresponding \c #file + /// strings, diagnosing any conflicts. + /// + /// A given \c #filePath string always maps to exactly one \c #file string, + /// but it is possible for \c #sourceLocation directives to introduce + /// duplicates in the opposite direction. If there are such conflicts, this + /// method will diagnose the conflict and choose a "winner" among the paths + /// in a reproducible way. The \c bool paired with the \c #file string is + /// \c true for paths which did not have a conflict or won a conflict, and + /// \c false for paths which lost a conflict. Thus, if you want to generate a + /// reverse mapping, you should drop or special-case the \c #file strings that + /// are paired with \c false. + /// + /// Note that this returns an empty StringMap if concise \c #file strings are + /// disabled. Users should fall back to using the file path in this case. + llvm::StringMap> + computeMagicFileStringMap(bool shouldDiagnose) const; + /// Add a file declaring a cross-import overlay. void addCrossImportOverlayFile(StringRef file); diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index ee67826873c2e..6db87ca56d98f 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -321,6 +321,12 @@ class SourceFile final : public FileUnit { /// forwarded on to IRGen. ASTStage_t ASTStage = Unprocessed; + /// Virtual filenames declared by #sourceLocation(file:) directives in this + /// file. + llvm::SmallVector, 0> VirtualFilenames; + + llvm::StringMap getInfoForUsedFilePaths() const; + SourceFile(ModuleDecl &M, SourceFileKind K, Optional bufferID, ImplicitModuleImportKind ModImpKind, bool KeepParsedTokens = false, bool KeepSyntaxTree = false, ParsingOptions parsingOpts = {}); diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index 6a8a59d3b4731..f6232810d7680 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -264,6 +264,8 @@ struct BorrowScopeIntroducingValueKind { llvm::raw_ostream &operator<<(llvm::raw_ostream &os, BorrowScopeIntroducingValueKind kind); +struct InteriorPointerOperand; + /// A higher level construct for working with values that represent the /// introduction of a new borrow scope. /// @@ -337,6 +339,15 @@ struct BorrowScopeIntroducingValue { void print(llvm::raw_ostream &os) const; SWIFT_DEBUG_DUMP { print(llvm::dbgs()); } + /// Visit each of the interior pointer uses of this underlying borrow + /// introduced value. These object -> address projections and any transitive + /// address uses must be treated as liveness requiring uses of the guaranteed + /// value and we can not shrink the scope beyond that point. Returns true if + /// we were able to understand all uses and thus guarantee we found all + /// interior pointer uses. Returns false otherwise. + bool visitInteriorPointerOperands( + function_ref func) const; + private: /// Internal constructor for failable static constructor. Please do not expand /// its usage since it assumes the code passed in is well formed. @@ -375,6 +386,87 @@ getSingleBorrowIntroducingValue(SILValue value); Optional getSingleBorrowIntroducingValue(SILValue inputValue); +struct InteriorPointerOperandKind { + using UnderlyingKindTy = std::underlying_type::type; + + enum Kind : UnderlyingKindTy { + RefElementAddr = UnderlyingKindTy(SILInstructionKind::RefElementAddrInst), + RefTailAddr = UnderlyingKindTy(SILInstructionKind::RefTailAddrInst), + }; + + Kind value; + + InteriorPointerOperandKind(Kind newValue) : value(newValue) {} + InteriorPointerOperandKind(const InteriorPointerOperandKind &other) + : value(other.value) {} + operator Kind() const { return value; } + + static Optional get(Operand *use) { + switch (use->getUser()->getKind()) { + default: + return None; + case SILInstructionKind::RefElementAddrInst: + return InteriorPointerOperandKind(RefElementAddr); + case SILInstructionKind::RefTailAddrInst: + return InteriorPointerOperandKind(RefTailAddr); + } + } + + void print(llvm::raw_ostream &os) const; + SWIFT_DEBUG_DUMP; +}; + +/// A mixed object->address projection that projects a memory location out of an +/// object with guaranteed ownership. All transitive address uses of the +/// interior pointer must be within the lifetime of the guaranteed lifetime. As +/// such, these must be treated as implicit uses of the parent guaranteed value. +struct InteriorPointerOperand { + Operand *operand; + InteriorPointerOperandKind kind; + + InteriorPointerOperand(Operand *op) + : operand(op), kind(*InteriorPointerOperandKind::get(op)) {} + + /// If value is a borrow introducer return it after doing some checks. + static Optional get(Operand *op) { + auto kind = InteriorPointerOperandKind::get(op); + if (!kind) + return None; + return InteriorPointerOperand(op, *kind); + } + + /// Return the end scope of all borrow introducers of the parent value of this + /// projection. Returns true if we were able to find all borrow introducing + /// values. + bool visitBaseValueScopeEndingUses(function_ref func) const { + SmallVector introducers; + if (!getAllBorrowIntroducingValues(operand->get(), introducers)) + return false; + for (const auto &introducer : introducers) { + if (!introducer.isLocalScope()) + continue; + introducer.visitLocalScopeEndingUses(func); + } + return true; + } + + SILValue getProjectedAddress() const { + switch (kind) { + case InteriorPointerOperandKind::RefElementAddr: + return cast(operand->getUser()); + case InteriorPointerOperandKind::RefTailAddr: + return cast(operand->getUser()); + } + llvm_unreachable("Covered switch isn't covered?!"); + } + +private: + /// Internal constructor for failable static constructor. Please do not expand + /// its usage since it assumes the code passed in is well formed. + InteriorPointerOperand(Operand *op, InteriorPointerOperandKind kind) + : operand(op), kind(kind) {} +}; + } // namespace swift #endif diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 94854a004c555..a7c0548068a37 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -699,7 +699,7 @@ void ASTMangler::appendDeclName(const ValueDecl *decl) { break; } } else if (auto objCName = - getOverriddenSwiftProtocolObjCName(decl, UseObjCProtocolNames)) { + getOverriddenSwiftProtocolObjCName(decl, UseObjCRuntimeNames)) { // @objc Swift protocols should be mangled as Objective-C protocols, // so append the Objective-C runtime name. appendIdentifier(*objCName); @@ -1298,7 +1298,7 @@ void ASTMangler::appendBoundGenericArgs(Type type, bool &isFirstArgList) { genericArgs = boundType->getGenericArgs(); if (Type parent = boundType->getParent()) { GenericTypeDecl *decl = boundType->getAnyGeneric(); - if (!getSpecialManglingContext(decl, UseObjCProtocolNames)) + if (!getSpecialManglingContext(decl, UseObjCRuntimeNames)) appendBoundGenericArgs(parent->getDesugaredType(), isFirstArgList); } } @@ -1610,7 +1610,7 @@ ASTMangler::getSpecialManglingContext(const ValueDecl *decl, /// This is the top-level entrypoint for mangling . void ASTMangler::appendContextOf(const ValueDecl *decl) { // Check for a special mangling context. - if (auto context = getSpecialManglingContext(decl, UseObjCProtocolNames)) { + if (auto context = getSpecialManglingContext(decl, UseObjCRuntimeNames)) { switch (*context) { case ClangImporterContext: return appendOperator("SC"); @@ -1857,7 +1857,10 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol, appendContextOf(protocol); auto *clangDecl = protocol->getClangDecl(); - if (auto *clangProto = cast_or_null(clangDecl)) + auto clangProto = cast_or_null(clangDecl); + if (clangProto && UseObjCRuntimeNames) + appendIdentifier(clangProto->getObjCRuntimeNameAsString()); + else if (clangProto) appendIdentifier(clangProto->getName()); else appendDeclName(protocol); @@ -1928,15 +1931,25 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { if (!namedDecl) return false; - appendIdentifier(namedDecl->getName()); + // Mangle ObjC classes using their runtime names. + auto interface = dyn_cast(namedDecl); + auto protocol = dyn_cast(namedDecl); + + if (UseObjCRuntimeNames && interface) { + appendIdentifier(interface->getObjCRuntimeNameAsString()); + } else if (UseObjCRuntimeNames && protocol) { + appendIdentifier(protocol->getObjCRuntimeNameAsString()); + } else { + appendIdentifier(namedDecl->getName()); + } // The important distinctions to maintain here are Objective-C's various // namespaces: protocols, tags (struct/enum/union), and unqualified names. // We continue to mangle "class" the standard Swift way because it feels // weird to call that an alias, but they're really in the same namespace. - if (isa(namedDecl)) { + if (interface) { appendOperator("C"); - } else if (isa(namedDecl)) { + } else if (protocol) { appendOperator("P"); } else if (isa(namedDecl)) { // Note: This includes enums, but that's okay. A Clang enum is not always diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index 6467d3f146bdb..dde1dad387b29 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1955,6 +1955,144 @@ static void performAutoImport( SF.addImports(Imports); } +llvm::StringMap +SourceFile::getInfoForUsedFilePaths() const { + llvm::StringMap result; + + if (BufferID != -1) { + result[getFilename()].physicalFileLoc = + getASTContext().SourceMgr.getLocForBufferStart(BufferID); + } + + for (auto &vpath : VirtualFilenames) { + result[vpath.Item].virtualFileLocs.insert(vpath.Loc); + } + + return result; +} + +/// Returns a map of filenames to a map of file paths to SourceFilePathInfo +/// instances, for all SourceFiles in the module. +static llvm::StringMap> +getInfoForUsedFileNames(const ModuleDecl *module) { + llvm::StringMap> result; + + for (auto *file : module->getFiles()) { + auto *sourceFile = dyn_cast(file); + if (!sourceFile) continue; + + for (auto &pair : sourceFile->getInfoForUsedFilePaths()) { + StringRef fullPath = pair.first(); + StringRef fileName = llvm::sys::path::filename(fullPath); + auto &info = pair.second; + + result[fileName][fullPath].merge(info); + } + } + + return result; +} + +static void +computeMagicFileString(const ModuleDecl *module, StringRef name, + SmallVectorImpl &result) { + result.assign(module->getNameStr().begin(), module->getNameStr().end()); + result.push_back('/'); + result.append(name.begin(), name.end()); +} + +static StringRef +resolveMagicNameConflicts(const ModuleDecl *module, StringRef fileString, + const llvm::StringMap &paths, + bool shouldDiagnose) { + assert(paths.size() > 1); + assert(module->getASTContext().LangOpts.EnableConcisePoundFile); + + /// The path we consider to be "correct"; we will emit fix-its changing the + /// other paths to match this one. + StringRef winner = ""; + + // First, select a winner. + for (const auto &pathPair : paths) { + // If there is a physical file with this name, we use its path and stop + // looking. + if (pathPair.second.physicalFileLoc.isValid()) { + winner = pathPair.first(); + break; + } + + // Otherwise, we favor the lexicographically "smaller" path. + if (winner.empty() || winner > pathPair.first()) { + winner = pathPair.first(); + } + } + + // If we're not diagnosing, that's all we need to do. + if (!shouldDiagnose) + return winner; + + SmallString<64> winnerLiteral; + llvm::raw_svector_ostream winnerLiteralStream{winnerLiteral}; + swift::printAsQuotedString(winnerLiteralStream, winner); + + auto &diags = module->getASTContext().Diags; + + // Diagnose the conflict at each #sourceLocation that specifies it. + for (const auto &pathPair : paths) { + bool isWinner = (pathPair.first() == winner); + + // Don't diagnose #sourceLocations that match the physical file. + if (pathPair.second.physicalFileLoc.isValid()) { + assert(isWinner && "physical files should always win; duplicate name?"); + continue; + } + + for (auto loc : pathPair.second.virtualFileLocs) { + diags.diagnose(loc, + diag::pound_source_location_creates_pound_file_conflicts, + fileString); + + // Offer a fix-it unless it would be tautological. + if (!isWinner) + diags.diagnose(loc, diag::fixit_correct_source_location_file, winner) + .fixItReplace(loc, winnerLiteral); + } + } + + return winner; +} + +llvm::StringMap> +ModuleDecl::computeMagicFileStringMap(bool shouldDiagnose) const { + llvm::StringMap> result; + SmallString<64> scratch; + + if (!getASTContext().LangOpts.EnableConcisePoundFile) + return result; + + for (auto &namePair : getInfoForUsedFileNames(this)) { + computeMagicFileString(this, namePair.first(), scratch); + auto &infoForPaths = namePair.second; + + assert(!infoForPaths.empty()); + + // TODO: In the future, we'd like to handle these conflicts gracefully by + // generating a unique `#file` string for each conflicting name. For now, we + // will simply warn about conflicts. + StringRef winner = infoForPaths.begin()->first(); + if (infoForPaths.size() > 1) + winner = resolveMagicNameConflicts(this, scratch, infoForPaths, + shouldDiagnose); + + for (auto &pathPair : infoForPaths) { + result[pathPair.first()] = + std::make_pair(scratch.str().str(), pathPair.first() == winner); + } + } + + return result; +} + SourceFile::SourceFile(ModuleDecl &M, SourceFileKind K, Optional bufferID, ImplicitModuleImportKind ModImpKind, diff --git a/lib/IRGen/IRGenMangler.cpp b/lib/IRGen/IRGenMangler.cpp index 54e3934a807ef..7dbac2eaf2f82 100644 --- a/lib/IRGen/IRGenMangler.cpp +++ b/lib/IRGen/IRGenMangler.cpp @@ -83,7 +83,7 @@ IRGenMangler::withSymbolicReferences(IRGenModule &IGM, llvm::function_ref body) { Mod = IGM.getSwiftModule(); OptimizeProtocolNames = false; - UseObjCProtocolNames = true; + UseObjCRuntimeNames = true; llvm::SaveAndRestore AllowSymbolicReferencesLocally(AllowSymbolicReferences); diff --git a/lib/Immediate/CMakeLists.txt b/lib/Immediate/CMakeLists.txt index 195ef7e7cc00f..142d649c3a312 100644 --- a/lib/Immediate/CMakeLists.txt +++ b/lib/Immediate/CMakeLists.txt @@ -5,6 +5,8 @@ add_swift_host_library(swiftImmediate STATIC executionengine linker mcjit + orcjit + jitlink transformutils) target_link_libraries(swiftImmediate PRIVATE swiftFrontend diff --git a/lib/Immediate/Immediate.cpp b/lib/Immediate/Immediate.cpp index 4037c9efabf8e..ba1025563c6e3 100644 --- a/lib/Immediate/Immediate.cpp +++ b/lib/Immediate/Immediate.cpp @@ -15,7 +15,6 @@ // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "swift-immediate" #include "swift/Immediate/Immediate.h" #include "ImmediateImpl.h" @@ -31,7 +30,8 @@ #include "swift/SILOptimizer/PassManager/Passes.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" -#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/LLVMContext.h" @@ -40,6 +40,8 @@ #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Support/Path.h" +#define DEBUG_TYPE "swift-immediate" + #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX @@ -246,10 +248,10 @@ int swift::RunImmediately(CompilerInstance &CI, const auto PSPs = CI.getPrimarySpecificPathsForAtMostOnePrimary(); // FIXME: We shouldn't need to use the global context here, but // something is persisting across calls to performIRGeneration. - auto ModuleOwner = performIRGeneration( + auto ModuleCtx = std::make_unique(); + auto Module = performIRGeneration( IRGenOpts, swiftModule, std::move(SM), swiftModule->getName().str(), - PSPs, getGlobalLLVMContext(), ArrayRef()); - auto *Module = ModuleOwner.get(); + PSPs, *ModuleCtx, ArrayRef()); if (Context.hadError()) return -1; @@ -288,7 +290,6 @@ int swift::RunImmediately(CompilerInstance &CI, (*emplaceProcessArgs)(argBuf.data(), CmdLine.size()); - SmallVector InitFns; if (autolinkImportedModules(swiftModule, IRGenOpts)) return -1; @@ -297,41 +298,76 @@ int swift::RunImmediately(CompilerInstance &CI, PMBuilder.Inliner = llvm::createFunctionInliningPass(200); // Build the ExecutionEngine. - llvm::EngineBuilder builder(std::move(ModuleOwner)); - std::string ErrorMsg; llvm::TargetOptions TargetOpt; std::string CPU; std::string Triple; std::vector Features; std::tie(TargetOpt, CPU, Features, Triple) = getIRTargetOptions(IRGenOpts, swiftModule->getASTContext()); - builder.setRelocationModel(llvm::Reloc::PIC_); - builder.setTargetOptions(TargetOpt); - builder.setMCPU(CPU); - builder.setMAttrs(Features); - builder.setErrorStr(&ErrorMsg); - builder.setEngineKind(llvm::EngineKind::JIT); - llvm::ExecutionEngine *EE = builder.create(); - if (!EE) { - llvm::errs() << "Error loading JIT: " << ErrorMsg; - return -1; + + std::unique_ptr JIT; + + { + auto JITOrErr = + llvm::orc::LLJITBuilder() + .setJITTargetMachineBuilder( + llvm::orc::JITTargetMachineBuilder(llvm::Triple(Triple)) + .setRelocationModel(llvm::Reloc::PIC_) + .setOptions(std::move(TargetOpt)) + .setCPU(std::move(CPU)) + .addFeatures(Features) + .setCodeGenOptLevel(llvm::CodeGenOpt::Default)) + .create(); + + if (!JITOrErr) { + llvm::logAllUnhandledErrors(JITOrErr.takeError(), llvm::errs(), ""); + return -1; + } else + JIT = std::move(*JITOrErr); + } + + { + // Get a generator for the process symbols and attach it to the main + // JITDylib. + if (auto G = llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(Module->getDataLayout().getGlobalPrefix())) + JIT->getMainJITDylib().addGenerator(std::move(*G)); + else { + logAllUnhandledErrors(G.takeError(), llvm::errs(), ""); + return -1; + } } LLVM_DEBUG(llvm::dbgs() << "Module to be executed:\n"; Module->dump()); - EE->finalizeObject(); - - // Run the generated program. - for (auto InitFn : InitFns) { - LLVM_DEBUG(llvm::dbgs() << "Running initialization function " - << InitFn->getName() << '\n'); - EE->runFunctionAsMain(InitFn, CmdLine, nullptr); + { + auto TSM = llvm::orc::ThreadSafeModule(std::move(Module), std::move(ModuleCtx)); + if (auto Err = JIT->addIRModule(std::move(TSM))) { + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), ""); + return -1; + } + } + + using MainFnTy = int(*)(int, char*[]); + + if (auto Err = JIT->initialize(JIT->getMainJITDylib())) { + llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), ""); + return -1; + } + + MainFnTy JITMain = nullptr; + if (auto MainFnOrErr = JIT->lookup("main")) + JITMain = llvm::jitTargetAddressToFunction(MainFnOrErr->getAddress()); + else { + logAllUnhandledErrors(MainFnOrErr.takeError(), llvm::errs(), ""); + return -1; } LLVM_DEBUG(llvm::dbgs() << "Running static constructors\n"); - EE->runStaticConstructorsDestructors(false); + if (auto Err = JIT->initialize(JIT->getMainJITDylib())) { + logAllUnhandledErrors(std::move(Err), llvm::errs(), ""); + return -1; + } LLVM_DEBUG(llvm::dbgs() << "Running main\n"); - llvm::Function *EntryFn = Module->getFunction("main"); - return EE->runFunctionAsMain(EntryFn, CmdLine, nullptr); + return llvm::orc::runAsMain(JITMain, CmdLine); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 57941fe0a80f5..5b3d0b5a05591 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -4822,7 +4822,8 @@ ParserStatus Parser::parseLineDirective(bool isLine) { getStringLiteralIfNotInterpolated(Loc, "'#sourceLocation'"); if (!Filename.hasValue()) return makeParserError(); - consumeToken(tok::string_literal); + SourceLoc filenameLoc = consumeToken(tok::string_literal); + SF.VirtualFilenames.emplace_back(*Filename, filenameLoc); if (parseToken(tok::comma, diag::sourceLocation_expected, ",") || parseSpecificIdentifier("line", diag::sourceLocation_expected, diff --git a/lib/SIL/OwnershipUtils.cpp b/lib/SIL/OwnershipUtils.cpp index 64e0ea16a9714..ff3f4e71a6d93 100644 --- a/lib/SIL/OwnershipUtils.cpp +++ b/lib/SIL/OwnershipUtils.cpp @@ -13,6 +13,7 @@ #include "swift/SIL/OwnershipUtils.h" #include "swift/Basic/Defer.h" #include "swift/SIL/LinearLifetimeChecker.h" +#include "swift/SIL/Projection.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILInstruction.h" @@ -397,6 +398,51 @@ bool BorrowScopeIntroducingValue::visitLocalScopeTransitiveEndingUses( return foundError; } +bool BorrowScopeIntroducingValue::visitInteriorPointerOperands( + function_ref func) const { + SmallVector worklist(value->getUses()); + while (!worklist.empty()) { + auto *op = worklist.pop_back_val(); + + if (auto interiorPointer = InteriorPointerOperand::get(op)) { + func(*interiorPointer); + continue; + } + + auto *user = op->getUser(); + if (isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || + isa(user) || + isa(user)) { + continue; + } + + // These are interior pointers that have not had support yet added for them. + if (isa(user) || + isa(user)) { + continue; + } + + // Look through object. + if (auto *svi = dyn_cast(user)) { + if (Projection::isObjectProjection(svi)) { + for (SILValue result : user->getResults()) { + llvm::copy(result->getUses(), std::back_inserter(worklist)); + } + continue; + } + } + + return false; + } + + return true; +} + //===----------------------------------------------------------------------===// // Introducer Searching Routines //===----------------------------------------------------------------------===// diff --git a/lib/SIL/SILOwnershipVerifier.cpp b/lib/SIL/SILOwnershipVerifier.cpp index 5634039d81bac..9fb86036133bf 100644 --- a/lib/SIL/SILOwnershipVerifier.cpp +++ b/lib/SIL/SILOwnershipVerifier.cpp @@ -24,6 +24,7 @@ #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/Dominance.h" #include "swift/SIL/DynamicCasts.h" +#include "swift/SIL/InstructionUtils.h" #include "swift/SIL/LinearLifetimeChecker.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/PrettyStackTrace.h" @@ -164,10 +165,12 @@ class SILValueOwnershipChecker { bool isSubobjectProjectionWithLifetimeEndingUses( SILValue value, const SmallVectorImpl &lifetimeEndingUsers) const; - bool - discoverImplicitRegularUsers(const BorrowScopeOperand &initialScopedOperand, - SmallVectorImpl &implicitRegularUsers, - bool isGuaranteed); + bool discoverBorrowOperandImplicitRegularUsers( + const BorrowScopeOperand &initialScopedOperand, + SmallVectorImpl &implicitRegularUsers, bool isGuaranteed); + bool discoverInteriorPointerOperandImplicitRegularUsers( + const InteriorPointerOperand &interiorPointerOperand, + SmallVectorImpl &implicitRegularUsers); }; } // end anonymous namespace @@ -240,7 +243,7 @@ bool SILValueOwnershipChecker::isCompatibleDefUse( } /// Returns true if an error was found. -bool SILValueOwnershipChecker::discoverImplicitRegularUsers( +bool SILValueOwnershipChecker::discoverBorrowOperandImplicitRegularUsers( const BorrowScopeOperand &initialScopedOperand, SmallVectorImpl &implicitRegularUsers, bool isGuaranteed) { if (!initialScopedOperand.areAnyUserResultsBorrowIntroducers()) { @@ -288,6 +291,115 @@ bool SILValueOwnershipChecker::discoverImplicitRegularUsers( return foundError; } +bool SILValueOwnershipChecker:: + discoverInteriorPointerOperandImplicitRegularUsers( + const InteriorPointerOperand &interiorPointerOperand, + SmallVectorImpl &implicitRegularUsers) { + SILValue projectedAddress = interiorPointerOperand.getProjectedAddress(); + SmallVector worklist(projectedAddress->getUses()); + + bool foundError = false; + + while (!worklist.empty()) { + auto *op = worklist.pop_back_val(); + + // Skip type dependent operands. + if (op->isTypeDependent()) + continue; + + // Before we do anything, add this operand to our implicit regular user + // list. + implicitRegularUsers.push_back(op); + + // Then update the worklist with new things to find if we recognize this + // inst and then continue. If we fail, we emit an error at the bottom of the + // loop that we didn't recognize the user. + auto *user = op->getUser(); + + // First, eliminate "end point uses" that we just need to check liveness at + // and do not need to check transitive uses of. + if (isa(user) || isa(user) || + isIncidentalUse(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user) || + isa(user)) { + continue; + } + + // Then handle users that we need to look at transitive uses of. + if (Projection::isAddressProjection(user) || + isa(user) || + isa(user) || + isa(user) || isa(user) || + isa(user) || isa(user)) { + for (SILValue r : user->getResults()) { + llvm::copy(r->getUses(), std::back_inserter(worklist)); + } + continue; + } + + if (auto *builtin = dyn_cast(user)) { + if (auto kind = builtin->getBuiltinKind()) { + if (*kind == BuiltinValueKind::TSanInoutAccess) { + continue; + } + } + } + + // If we have a load_borrow, add it's end scope to the liveness requirement. + if (auto *lbi = dyn_cast(user)) { + transform(lbi->getEndBorrows(), std::back_inserter(implicitRegularUsers), + [](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; }); + continue; + } + + // TODO: Merge this into the full apply site code below. + if (auto *beginApply = dyn_cast(user)) { + // TODO: Just add this to implicit regular user list? + llvm::copy(beginApply->getTokenResult()->getUses(), + std::back_inserter(implicitRegularUsers)); + continue; + } + + if (auto fas = FullApplySite::isa(user)) { + continue; + } + + if (auto *mdi = dyn_cast(user)) { + // If this is the base, just treat it as a liveness use. + if (op->get() == mdi->getBase()) { + continue; + } + + // If we are the value use, look through it. + llvm::copy(mdi->getValue()->getUses(), std::back_inserter(worklist)); + continue; + } + + // We were unable to recognize this user, so return true that we failed. + handleError([&] { + llvm::errs() + << "Function: " << op->getUser()->getFunction()->getName() << "\n" + << "Could not recognize address user of interior pointer operand!\n" + << "Interior Pointer Operand: " + << *interiorPointerOperand.operand->getUser() + << "Address User: " << *op->getUser(); + }); + foundError = true; + } + + // We were able to recognize all of the uses of the address, so return false + // that we did not find any errors. + return foundError; +} + bool SILValueOwnershipChecker::gatherNonGuaranteedUsers( SmallVectorImpl &lifetimeEndingUsers, SmallVectorImpl &nonLifetimeEndingUsers, @@ -348,8 +460,8 @@ bool SILValueOwnershipChecker::gatherNonGuaranteedUsers( // initial end scope instructions without any further work. // // Maybe: Is borrow scope non-local? - foundError |= discoverImplicitRegularUsers(*initialScopedOperand, - implicitRegularUsers, false); + foundError |= discoverBorrowOperandImplicitRegularUsers( + *initialScopedOperand, implicitRegularUsers, false); } return foundError; @@ -441,11 +553,19 @@ bool SILValueOwnershipChecker::gatherUsers( if (auto scopedOperand = BorrowScopeOperand::get(op)) { assert(!scopedOperand->consumesGuaranteedValues()); - foundError |= discoverImplicitRegularUsers(*scopedOperand, - implicitRegularUsers, true); + foundError |= discoverBorrowOperandImplicitRegularUsers( + *scopedOperand, implicitRegularUsers, true); + } + + // Next see if our use is an interior pointer operand. If we have an + // interior pointer, we need to add all of its address uses as "implicit + // regular users" of our consumed value. + if (auto interiorPointerOperand = InteriorPointerOperand::get(op)) { + foundError |= discoverInteriorPointerOperandImplicitRegularUsers( + *interiorPointerOperand, implicitRegularUsers); } - // And then add the op to the non lifetime ending user list. + // Finally add the op to the non lifetime ending user list. LLVM_DEBUG(llvm::dbgs() << " Regular User: " << *user); nonLifetimeEndingUsers.push_back(op); continue; diff --git a/lib/SIL/SILPrinter.cpp b/lib/SIL/SILPrinter.cpp index e7c6e9b224214..17302665529ff 100644 --- a/lib/SIL/SILPrinter.cpp +++ b/lib/SIL/SILPrinter.cpp @@ -47,6 +47,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/FileSystem.h" +#include using namespace swift; @@ -2750,6 +2751,57 @@ printSILCoverageMaps(SILPrintContext &Ctx, M->print(Ctx); } +using MagicFileStringMap = + llvm::StringMap>; + +static void +printMagicFileStringMapEntry(SILPrintContext &Ctx, + const MagicFileStringMap::MapEntryTy &entry) { + auto &OS = Ctx.OS(); + OS << "// '" << std::get<0>(entry.second) + << "' => '" << entry.first() << "'"; + + if (!std::get<1>(entry.second)) + OS << " (alternate)"; + + OS << "\n"; +} + +static void printMagicFileStringMap(SILPrintContext &Ctx, + const MagicFileStringMap map) { + if (map.empty()) + return; + + Ctx.OS() << "\n\n// Mappings from '#file' to '#filePath':\n"; + + if (Ctx.sortSIL()) { + llvm::SmallVector keys; + llvm::copy(map.keys(), std::back_inserter(keys)); + + llvm::sort(keys, [&](StringRef leftKey, StringRef rightKey) -> bool { + const auto &leftValue = map.find(leftKey)->second; + const auto &rightValue = map.find(rightKey)->second; + + // Lexicographically earlier #file strings sort earlier. + if (std::get<0>(leftValue) != std::get<0>(rightValue)) + return std::get<0>(leftValue) < std::get<0>(rightValue); + + // Conflict winners sort before losers. + if (std::get<1>(leftValue) != std::get<1>(rightValue)) + return std::get<1>(leftValue); + + // Finally, lexicographically earlier #filePath strings sort earlier. + return leftKey < rightKey; + }); + + for (auto key : keys) + printMagicFileStringMapEntry(Ctx, *map.find(key)); + } else { + for (const auto &entry : map) + printMagicFileStringMapEntry(Ctx, entry); + } +} + void SILProperty::print(SILPrintContext &Ctx) const { PrintOptions Options = PrintOptions::printSIL(); @@ -2864,6 +2916,10 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printSILProperties(PrintCtx, getPropertyList()); printExternallyVisibleDecls(PrintCtx, externallyVisible.getArrayRef()); + if (M) + printMagicFileStringMap( + PrintCtx, M->computeMagicFileStringMap(/*shouldDiagnose=*/false)); + OS << "\n\n"; } diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index ed4f400cdc812..f5e80b018c416 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -50,7 +50,9 @@ using namespace Lowering; //===----------------------------------------------------------------------===// SILGenModule::SILGenModule(SILModule &M, ModuleDecl *SM) - : M(M), Types(M.Types), SwiftModule(SM), TopLevelSGF(nullptr) { + : M(M), Types(M.Types), SwiftModule(SM), TopLevelSGF(nullptr), + MagicFileStringsByFilePath( + SM->computeMagicFileStringMap(/*shouldDiagnose=*/true)) { const SILOptions &Opts = M.getOptions(); if (!Opts.UseProfile.empty()) { auto ReaderOrErr = llvm::IndexedInstrProfReader::create(Opts.UseProfile); diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 11774bc0f0a9e..3c1235cf807ac 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -131,6 +131,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { ASTContext &getASTContext() { return M.getASTContext(); } + llvm::StringMap> + MagicFileStringsByFilePath; + static DeclName getMagicFunctionName(SILDeclRef ref); static DeclName getMagicFunctionName(DeclContext *dc); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index b116ac3518d40..00d6601a940f2 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -4892,14 +4892,11 @@ StringRef SILGenFunction::getMagicFilePathString(SourceLoc loc) { std::string SILGenFunction::getMagicFileString(SourceLoc loc) { auto path = getMagicFilePathString(loc); - if (!getASTContext().LangOpts.EnableConcisePoundFile) - return path.str(); + auto result = SGM.MagicFileStringsByFilePath.find(path); + if (result != SGM.MagicFileStringsByFilePath.end()) + return std::get<0>(result->second); - auto value = llvm::sys::path::filename(path).str(); - value += " ("; - value += getModule().getSwiftModule()->getNameStr(); - value += ")"; - return value; + return path.str(); } /// Emit an application of the given allocating initializer. diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 49190a50e8f3b..d701dd5800030 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2315,7 +2315,8 @@ namespace { cast(pattern)->getSubPattern(), locator, Type(), bindPatternVarsOneWay); CS.addConstraint( - ConstraintKind::Conversion, subPatternType, openedType, locator); + ConstraintKind::Conversion, subPatternType, openedType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); return setType(openedType); } @@ -2352,7 +2353,7 @@ namespace { Type eltTy = getTypeForPattern( tupleElt.getPattern(), - locator.withPathElement(LocatorPathElt::TupleElement(i)), + locator, externalEltType, bindPatternVarsOneWay); tupleTypeElts.push_back(TupleTypeElt(eltTy, tupleElt.getLabel())); @@ -2368,7 +2369,8 @@ namespace { CS.getConstraintLocator(locator), TVO_CanBindToNoEscape); CS.addConstraint( ConstraintKind::OptionalObject, externalPatternType, - objVar, locator); + objVar, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); externalPatternType = objVar; } @@ -2386,7 +2388,9 @@ namespace { Type castType = resolveTypeReferenceInExpression(isPattern->getCastTypeLoc()); - castType = CS.openUnboundGenericType(castType, locator); + castType = CS.openUnboundGenericType( + castType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); Type subPatternType = getTypeForPattern(isPattern->getSubPattern(), locator, castType, @@ -2395,7 +2399,8 @@ namespace { // Make sure we can cast from the subpattern type to the type we're // checking; if it's impossible, fail. CS.addConstraint( - ConstraintKind::CheckedCast, subPatternType, castType, locator); + ConstraintKind::CheckedCast, subPatternType, castType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); return setType(subPatternType); } @@ -2422,7 +2427,9 @@ namespace { // Resolve the parent type. Type parentType = resolveTypeReferenceInExpression(enumPattern->getParentType()); - parentType = CS.openUnboundGenericType(parentType, locator); + parentType = CS.openUnboundGenericType( + parentType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); // Perform member lookup into the parent's metatype. Type parentMetaType = MetatypeType::get(parentType); @@ -2433,8 +2440,9 @@ namespace { // Parent type needs to be convertible to the pattern type; this // accounts for cases where the pattern type is existential. - CS.addConstraint(ConstraintKind::Conversion, parentType, patternType, - locator); + CS.addConstraint( + ConstraintKind::Conversion, parentType, patternType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); baseType = parentType; } else { @@ -2470,8 +2478,9 @@ namespace { ConstraintKind::Equal, functionType, memberType, locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); - CS.addConstraint(ConstraintKind::Conversion, outputType, baseType, - locator); + CS.addConstraint( + ConstraintKind::Conversion, outputType, baseType, + locator.withPathElement(LocatorPathElt::PatternMatch(pattern))); } return setType(patternType); @@ -4265,10 +4274,18 @@ bool ConstraintSystem::generateConstraints( // For any pattern variable that has a parent variable (i.e., another // pattern variable with the same name in the same case), require that // the types be equivalent. - pattern->forEachVariable([&](VarDecl *var) { + pattern->forEachNode([&](Pattern *pattern) { + auto namedPattern = dyn_cast(pattern); + if (!namedPattern) + return; + + auto var = namedPattern->getDecl(); if (auto parentVar = var->getParentVarDecl()) { addConstraint( - ConstraintKind::Equal, getType(parentVar), getType(var), locator); + ConstraintKind::Equal, getType(parentVar), getType(var), + getConstraintLocator( + locator, + LocatorPathElt::PatternMatch(namedPattern))); } }); } diff --git a/stdlib/public/core/String.swift b/stdlib/public/core/String.swift index 98da656640188..3ac1c1b45f03e 100644 --- a/stdlib/public/core/String.swift +++ b/stdlib/public/core/String.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -428,53 +428,49 @@ extension String { return } - /// Creates a new String with the specified capacity in UTF-8 code units then - /// calls the given closure with a buffer covering the String's uninitialized - /// memory. + /// Creates a new string with the specified capacity in UTF-8 code units, and + /// then calls the given closure with a buffer covering the string's + /// uninitialized memory. /// /// The closure should return the number of initialized code units, /// or 0 if it couldn't initialize the buffer (for example if the /// requested capacity was too small). /// /// This method replaces ill-formed UTF-8 sequences with the Unicode - /// replacement character (`"\u{FFFD}"`); This may require resizing + /// replacement character (`"\u{FFFD}"`). This may require resizing /// the buffer beyond its original capacity. /// /// The following examples use this initializer with the contents of two - /// different `UInt8` arrays---the first with well-formed UTF-8 code unit - /// sequences and the second with an ill-formed sequence at the end. + /// different `UInt8` arrays---the first with a well-formed UTF-8 code unit + /// sequence, and the second with an ill-formed sequence at the end. /// - /// let validUTF8: [UInt8] = [67, 97, 102, -61, -87, 0] - /// let s = String(unsafeUninitializedCapacity: validUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(validUTF8) + /// let validUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3, 0xA9] + /// let invalidUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3] + /// + /// let cafe1 = String(unsafeUninitializedCapacity: validUTF8.count) { + /// _ = $0.initialize(from: validUTF8) /// return validUTF8.count - /// }) - /// // Prints "Café" + /// } + /// // cafe1 == "Café" /// - /// let invalidUTF8: [UInt8] = [67, 97, 102, -61, 0] - /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(invalidUTF8) + /// let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) { + /// _ = $0.initialize(from: invalidUTF8) /// return invalidUTF8.count - /// }) - /// // Prints "Caf�" + /// } + /// // cafe2 == "Caf�" /// - /// let s = String(unsafeUninitializedCapacity: invalidUTF8.count, - /// initializingUTF8With: { ptr in - /// ptr.initializeFrom(invalidUTF8) + /// let empty = String(unsafeUninitializedCapacity: 16) { _ in + /// // Can't initialize the buffer (e.g. the capacity is too small). /// return 0 - /// }) - /// // Prints "" + /// } + /// // empty == "" /// /// - Parameters: /// - capacity: The number of UTF-8 code units worth of memory to allocate - /// for the String. - /// - initializer: A closure that initializes elements and sets the count of - /// the new String - /// - Parameters: - /// - buffer: A buffer covering uninitialized memory with room for the - /// specified number of UTF-8 code units. + /// for the string (excluding the null terminator). + /// - initializer: A closure that accepts a buffer covering uninitialized + /// memory with room for `capacity` UTF-8 code units, initializes + /// that memory, and returns the number of initialized elements. @inline(__always) @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) public init( @@ -484,14 +480,14 @@ extension String { ) throws -> Int ) rethrows { self = try String( - uninitializedCapacity: capacity, + _uninitializedCapacity: capacity, initializingUTF8With: initializer ) } @inline(__always) internal init( - uninitializedCapacity capacity: Int, + _uninitializedCapacity capacity: Int, initializingUTF8With initializer: ( _ buffer: UnsafeMutableBufferPointer ) throws -> Int @@ -800,7 +796,7 @@ extension String { public func lowercased() -> String { if _fastPath(_guts.isFastASCII) { return _guts.withFastUTF8 { utf8 in - return String(uninitializedCapacity: utf8.count) { buffer in + return String(_uninitializedCapacity: utf8.count) { buffer in for i in 0 ..< utf8.count { buffer[i] = _lowercaseASCII(utf8[i]) } @@ -860,7 +856,7 @@ extension String { public func uppercased() -> String { if _fastPath(_guts.isFastASCII) { return _guts.withFastUTF8 { utf8 in - return String(uninitializedCapacity: utf8.count) { buffer in + return String(_uninitializedCapacity: utf8.count) { buffer in for i in 0 ..< utf8.count { buffer[i] = _uppercaseASCII(utf8[i]) } diff --git a/stdlib/public/core/StringGutsRangeReplaceable.swift b/stdlib/public/core/StringGutsRangeReplaceable.swift index cac671c749404..5424c5d648d0d 100644 --- a/stdlib/public/core/StringGutsRangeReplaceable.swift +++ b/stdlib/public/core/StringGutsRangeReplaceable.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 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 @@ -100,7 +100,7 @@ extension _StringGuts { @inline(never) // slow-path private mutating func _foreignGrow(_ n: Int) { - let newString = String(uninitializedCapacity: n) { buffer in + let newString = String(_uninitializedCapacity: n) { buffer in guard let count = _foreignCopyUTF8(into: buffer) else { fatalError("String capacity was smaller than required") } @@ -164,7 +164,7 @@ extension _StringGuts { @inline(never) @_effects(readonly) private func _foreignConvertedToSmall() -> _SmallString { - let smol = String(uninitializedCapacity: _SmallString.capacity) { buffer in + let smol = String(_uninitializedCapacity: _SmallString.capacity) { buffer in guard let count = _foreignCopyUTF8(into: buffer) else { fatalError("String capacity was smaller than required") } diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift index 0228d666f5257..c52e2dca2cb69 100644 --- a/stdlib/public/core/StringStorage.swift +++ b/stdlib/public/core/StringStorage.swift @@ -485,8 +485,8 @@ extension __StringStorage { // Check that capacity end matches our notion of unused storage, and also // checks that breadcrumbs were dutifully aligned. - _internalInvariant(UnsafeMutablePointer(_realCapacityEnd) - == unusedStorage.baseAddress! + (unusedStorage.count + 1)) + // _internalInvariant(UnsafeMutablePointer(_realCapacityEnd) + // == unusedStorage.baseAddress! + (unusedStorage.count + 1)) } #endif // INTERNAL_CHECKS_ENABLED } diff --git a/test/Constraints/function_builder_diags.swift b/test/Constraints/function_builder_diags.swift index bcf8fe2a06049..17579bbc2273c 100644 --- a/test/Constraints/function_builder_diags.swift +++ b/test/Constraints/function_builder_diags.swift @@ -466,3 +466,17 @@ func testCaseMutabilityMismatches(e: E3) { } } } + +// Check for type equivalence among different case variables with the same name. +func testCaseVarTypes(e: E3) { + // FIXME: Terrible diagnostic + tuplify(true) { c in // expected-error{{type of expression is ambiguous without more context}} + "testSwitch" + switch e { + case .a(let x, let y), + .c(let x, let y): + x + y + "a" + } + } +} diff --git a/test/IRGen/Inputs/objc_runtime_name_clang_attr.h b/test/IRGen/Inputs/objc_runtime_name_clang_attr.h new file mode 100644 index 0000000000000..657faabe4f3ed --- /dev/null +++ b/test/IRGen/Inputs/objc_runtime_name_clang_attr.h @@ -0,0 +1,11 @@ +@import Foundation; + +__attribute__((objc_runtime_name("ObjCRuntimeNameIsDifferent"))) +@interface ObjCRuntimeNamed: NSObject + +@end + +__attribute__((objc_runtime_name("ObjCProtoRuntimeNameIsDifferent"))) +@protocol ObjCProtoRuntimeNamed + +@end diff --git a/test/IRGen/objc_runtime_name_clang_attr.swift b/test/IRGen/objc_runtime_name_clang_attr.swift new file mode 100644 index 0000000000000..e42ff40cd5fb1 --- /dev/null +++ b/test/IRGen/objc_runtime_name_clang_attr.swift @@ -0,0 +1,19 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-ir -import-objc-header %S/Inputs/objc_runtime_name_clang_attr.h %s | %FileCheck %s +// REQUIRES: objc_interop + +// Use the runtime name for runtime instantiation +// CHECK-LABEL: @"$sSo16ObjCRuntimeNamedCSgMD" = {{.*}}@"symbolic So26ObjCRuntimeNameIsDifferentCSg" +public func getMetadata() -> Any.Type { + return ObjCRuntimeNamed?.self +} +// CHECK-LABEL: @"$sSo21ObjCProtoRuntimeNamed_pSgMD" = {{.*}}@"symbolic So31ObjCProtoRuntimeNameIsDifferent_pSg" +public func getMetadata2() -> Any.Type { + return ObjCProtoRuntimeNamed?.self +} + +// Use the source name for symbols to avoid breaking ABI. +// CHECK-LABEL: define{{.*}}3fooyySo16ObjCRuntimeNamedCF +public func foo(_: ObjCRuntimeNamed) {} + +// CHECK-LABEL: define{{.*}}3fooyySo21ObjCProtoRuntimeNamed_pF +public func foo(_: ObjCProtoRuntimeNamed) {} diff --git a/test/Interpreter/SDK/autolinking.swift b/test/Interpreter/SDK/autolinking.swift index c8cbaa8143e84..74391d2669a84 100644 --- a/test/Interpreter/SDK/autolinking.swift +++ b/test/Interpreter/SDK/autolinking.swift @@ -7,7 +7,7 @@ // RUN: not %target-jit-run -lLinkMe %s 2>&1 // RUN: %target-jit-run -lLinkMe -DUSE_DIRECTLY %s -L %t 2>&1 -// RUN: not --crash %target-jit-run -DUSE_DIRECTLY -lLinkMe %s 2>&1 +// RUN: not %target-jit-run -DUSE_DIRECTLY -lLinkMe %s 2>&1 // REQUIRES: executable_test diff --git a/test/SILGen/Inputs/magic_identifier_file_conflicting_other.swift b/test/SILGen/Inputs/magic_identifier_file_conflicting_other.swift new file mode 100644 index 0000000000000..602b96c9b589a --- /dev/null +++ b/test/SILGen/Inputs/magic_identifier_file_conflicting_other.swift @@ -0,0 +1,8 @@ +// This file matches test/SILGen/magic_identifier_file_conflicting.swift. +// It should be compiled with -verify. + +// We should diagnose cross-file conflicts. +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_c.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to 'first/other_file_c.swift'}} {{23-50="first/other_file_c.swift"}} +#sourceLocation(file: "second/other_file_c.swift", line: 1) +#sourceLocation() diff --git a/test/SILGen/magic_identifier_file.swift b/test/SILGen/magic_identifier_file.swift index 7f1620f4f3dc3..7e67de96909bc 100644 --- a/test/SILGen/magic_identifier_file.swift +++ b/test/SILGen/magic_identifier_file.swift @@ -8,26 +8,30 @@ func directUse() { // BOTH-LABEL: sil {{.*}} @$s3Foo9directUseyyF print(#file) // ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" -// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +// CONCISE: string_literal utf8 "Foo/magic_identifier_file.swift" } func indirectUse() { // BOTH-LABEL: sil {{.*}} @$s3Foo11indirectUseyyF fatalError() // ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" -// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +// CONCISE: string_literal utf8 "Foo/magic_identifier_file.swift" } func forceUnwrap(_ x: ()?) { // BOTH-LABEL: sil {{.*}} @$s3Foo11forceUnwrapyyytSgF _ = x! // ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" -// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +// CONCISE: string_literal utf8 "Foo/magic_identifier_file.swift" } func forceTry(_ fn: () throws -> ()) { // BOTH-LABEL: sil {{.*}} @$s3Foo8forceTryyyyyKXEF try! fn() // ABSOLUTE: string_literal utf8 "SOURCE_DIR/test/SILGen/magic_identifier_file.swift" -// CONCISE: string_literal utf8 "magic_identifier_file.swift (Foo)" +// CONCISE: string_literal utf8 "Foo/magic_identifier_file.swift" } + +// CONCISE-LABEL: // Mappings from '#file' to '#filePath': +// CONCISE: // 'Foo/magic_identifier_file.swift' => 'SOURCE_DIR/test/SILGen/magic_identifier_file.swift' + diff --git a/test/SILGen/magic_identifier_file_conflicting.swift.gyb b/test/SILGen/magic_identifier_file_conflicting.swift.gyb new file mode 100644 index 0000000000000..4dcdea991b9d5 --- /dev/null +++ b/test/SILGen/magic_identifier_file_conflicting.swift.gyb @@ -0,0 +1,101 @@ +// RUN: %empty-directory(%t) +// RUN: %gyb -D TEMPDIR=%t %s -o %t/magic_identifier_file_conflicting.swift + +// We want to check both the diagnostics and the SIL output. +// RUN: %target-swift-emit-silgen -verify -emit-sorted-sil -enable-experimental-concise-pound-file -module-name Foo %t/magic_identifier_file_conflicting.swift %S/Inputs/magic_identifier_file_conflicting_other.swift | %FileCheck %s + +// FIXME: Make this test work on Windows. There's no fundamental reason it +// can't; we just need someone with a Windows machine to do it so we +// don't have a 30-minute debug cycle. +// UNSUPPORTED: OS=windows-msvc + +%{ +TEMPDIR_ESC = TEMPDIR.replace('\\', '\\\\') +def fixit_loc(start_col, orig_suffix): + """ + Computes a "start-end" string for a fix-it replacing a string literal which + starts at start_col and joins orig_suffix to TEMPDIR with a slash. + """ + original = '"{0}/{1}"'.format(TEMPDIR, orig_suffix) + end_col = start_col + len(original) + return '{0}-{1}'.format(start_col, end_col) +}% + +// +// Same name as a physical file +// + +// There should be no warning for the exact same path. +// no-warning +#sourceLocation(file: "${TEMPDIR_ESC}/magic_identifier_file_conflicting.swift", line: 10) +#sourceLocation() + +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/magic_identifier_file_conflicting.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to '${TEMPDIR_ESC}/magic_identifier_file_conflicting.swift'}} {{${fixit_loc(23, "other_path_b/magic_identifier_file_conflicting.swift")}="${TEMPDIR_ESC}/magic_identifier_file_conflicting.swift"}} +#sourceLocation(file: "${TEMPDIR_ESC}/other_path_b/magic_identifier_file_conflicting.swift", line: 20) +#sourceLocation() + +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/magic_identifier_file_conflicting.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to '${TEMPDIR_ESC}/magic_identifier_file_conflicting.swift'}} {{23-64="${TEMPDIR_ESC}/magic_identifier_file_conflicting.swift"}} +#sourceLocation(file: "magic_identifier_file_conflicting.swift", line: 30) +#sourceLocation() + +// +// No physical file with the same name +// + +// There shoud be no warning for a purely virtual file. +// no-warning +#sourceLocation(file: "other_file_a.swift", line: 40) +#sourceLocation() + +// Even if you use it twice. +// no-warning +#sourceLocation(file: "other_file_a.swift", line: 50) +#sourceLocation() + +// But there should be warnings for different-path, same-name virtual files. +// The lexicographically first path should be treated as canonical--we diagnose +// but don't offer a fix-it. +// expected-warning@+1 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_b.swift', which conflicts with '#file' strings produced by other paths in the module}} +#sourceLocation(file: "first/other_file_b.swift", line: 60) +#sourceLocation() + +// Subsequent paths should fix-it to the first one. +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_b.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to 'first/other_file_b.swift'}} {{23-50="first/other_file_b.swift"}} +#sourceLocation(file: "second/other_file_b.swift", line: 70) +#sourceLocation() + +// Even if there's more than one. +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_b.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to 'first/other_file_b.swift'}} {{23-49="first/other_file_b.swift"}} +#sourceLocation(file: "third/other_file_b.swift", line: 80) +#sourceLocation() + +// Even if one is duplicated. +// expected-warning@+2 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_b.swift', which conflicts with '#file' strings produced by other paths in the module}} +// expected-note@+1 {{change file in '#sourceLocation' to 'first/other_file_b.swift'}} {{23-49="first/other_file_b.swift"}} +#sourceLocation(file: "third/other_file_b.swift", line: 90) +#sourceLocation() + +// We should diagnose cross-file conflicts. +// expected-warning@+1 {{'#sourceLocation' directive produces '#file' string of 'Foo/other_file_c.swift', which conflicts with '#file' strings produced by other paths in the module}} +#sourceLocation(file: "first/other_file_c.swift", line: 100) +#sourceLocation() + +// +// Check '#file' => '#filePath' mapping table +// + +// CHECK-LABEL: // Mappings from '#file' to '#filePath': +// CHECK-NEXT: // 'Foo/magic_identifier_file_conflicting.swift' => 'BUILD_DIR{{[/\\]}}test-{{[^/]+}}{{[/\\]}}SILGen{{[/\\]}}Output{{[/\\]}}magic_identifier_file_conflicting.swift.gyb.tmp{{[/\\]}}magic_identifier_file_conflicting.swift' +// CHECK-NEXT: // 'Foo/magic_identifier_file_conflicting.swift' => 'BUILD_DIR{{[/\\]}}test-{{[^/]+}}{{[/\\]}}SILGen{{[/\\]}}Output{{[/\\]}}magic_identifier_file_conflicting.swift.gyb.tmp{{[/\\]}}other_path_b{{[/\\]}}magic_identifier_file_conflicting.swift' (alternate) +// CHECK-NEXT: // 'Foo/magic_identifier_file_conflicting.swift' => 'magic_identifier_file_conflicting.swift' (alternate) +// CHECK-NEXT: // 'Foo/magic_identifier_file_conflicting_other.swift' => 'SOURCE_DIR{{[/\\]}}test{{[/\\]}}SILGen{{[/\\]}}Inputs{{[/\\]}}magic_identifier_file_conflicting_other.swift' +// CHECK-NEXT: // 'Foo/other_file_a.swift' => 'other_file_a.swift' +// CHECK-NEXT: // 'Foo/other_file_b.swift' => 'first/other_file_b.swift' +// CHECK-NEXT: // 'Foo/other_file_b.swift' => 'second/other_file_b.swift' (alternate) +// CHECK-NEXT: // 'Foo/other_file_b.swift' => 'third/other_file_b.swift' (alternate) +// CHECK-NEXT: // 'Foo/other_file_c.swift' => 'first/other_file_c.swift' +// CHECK-NEXT: // 'Foo/other_file_c.swift' => 'second/other_file_c.swift' (alternate) diff --git a/test/SILOptimizer/mem-behavior-all.sil b/test/SILOptimizer/mem-behavior-all.sil index 7efe6ae8a436a..b0d5cb051b156 100644 --- a/test/SILOptimizer/mem-behavior-all.sil +++ b/test/SILOptimizer/mem-behavior-all.sil @@ -19,9 +19,9 @@ class Parent { // CHECK-LABEL: @testLetSideEffects // // The store does not affect the let-load (ref_element_addr). -// CHECK: PAIR #35. -// CHECK-NEXT: %7 = begin_access [modify] [static] %1 : $*Builtin.Int32 -// CHECK-NEXT: %11 = ref_element_addr %10 : $C, #C.prop +// CHECK: PAIR #25. +// CHECK-NEXT: %6 = begin_access [modify] [static] %1 : $*Builtin.Int32 +// CHECK-NEXT: %10 = ref_element_addr %9 : $C, #C.prop // CHECK-NEXT: r=0,w=0,se=0 // // Any unknown instructions with side effects does affect the let-load. @@ -38,7 +38,6 @@ bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32): %borrow1 = begin_borrow %0 : $Parent %childAdr = ref_element_addr %borrow1 : $Parent, #Parent.child %child = load_borrow %childAdr : $*C - end_borrow %borrow1 : $Parent %three = integer_literal $Builtin.Int32, 3 %access = begin_access [modify] [static] %1 : $*Builtin.Int32 @@ -50,6 +49,7 @@ bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32): %val = load [trivial] %propAdr : $*Builtin.Int32 end_borrow %borrow2 : $C end_borrow %child : $C + end_borrow %borrow1 : $Parent destroy_value %0 : $Parent return %val : $Builtin.Int32 } diff --git a/test/stdlib/NewStringAppending.swift b/test/stdlib/NewStringAppending.swift index ca61306e1f6d8..d79ba25668841 100644 --- a/test/stdlib/NewStringAppending.swift +++ b/test/stdlib/NewStringAppending.swift @@ -8,6 +8,10 @@ // // Memory allocator specifics also vary across platforms. // REQUIRES: CPU=x86_64, OS=macosx +// UNSUPPORTED: use_os_stdlib + +// TODO: Adapt capacity checks into a non-FileCheck more exhuastive test that +// can run on older OSes and match their behavior... import Foundation import Swift diff --git a/test/stdlib/StringCreate.swift b/test/stdlib/StringCreate.swift index 25903f6b7b536..be0798b74fa7a 100644 --- a/test/stdlib/StringCreate.swift +++ b/test/stdlib/StringCreate.swift @@ -6,28 +6,20 @@ defer { runAllTests() } var StringCreateTests = TestSuite("StringCreateTests") -enum SimpleString: String { +enum SimpleString: String, CaseIterable { case smallASCII = "abcdefg" case smallUnicode = "abéÏ𓀀" case largeASCII = "012345678901234567890" case largeUnicode = "abéÏ012345678901234567890𓀀" case emoji = "😀😃🤢🤮👩🏿‍🎤🧛🏻‍♂️🧛🏻‍♂️👩‍👩‍👦‍👦" + case empty = "" } -let simpleStrings: [String] = [ - SimpleString.smallASCII.rawValue, - SimpleString.smallUnicode.rawValue, - SimpleString.largeASCII.rawValue, - SimpleString.largeUnicode.rawValue, - SimpleString.emoji.rawValue, - "", -] - extension String { var utf32: [UInt32] { return unicodeScalars.map { $0.value } } } -StringCreateTests.test("String(decoding:as)") { +StringCreateTests.test("String(decoding:as:)") { func validateDecodingAs(_ str: String) { // Non-contiguous (maybe) storage expectEqual(str, String(decoding: str.utf8, as: UTF8.self)) @@ -41,8 +33,8 @@ StringCreateTests.test("String(decoding:as)") { } - for str in simpleStrings { - validateDecodingAs(str) + for simpleString in SimpleString.allCases { + validateDecodingAs(simpleString.rawValue) } // Corner-case: UBP with null pointer (https://bugs.swift.org/browse/SR-9869) @@ -54,3 +46,37 @@ StringCreateTests.test("String(decoding:as)") { "", String(decoding: UnsafeBufferPointer(_empty: ()), as: UTF32.self)) } +if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { + StringCreateTests.test("String(unsafeUninitializedCapacity:initializingUTF8With:)") { + for simpleString in SimpleString.allCases { + let expected = simpleString.rawValue + let expectedUTF8 = expected.utf8 + let actual = String(unsafeUninitializedCapacity: expectedUTF8.count) { + _ = $0.initialize(from: expectedUTF8) + return expectedUTF8.count + } + expectEqual(expected, actual) + } + + let validUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3, 0xA9] + let invalidUTF8: [UInt8] = [0x43, 0x61, 0x66, 0xC3] + + let cafe1 = String(unsafeUninitializedCapacity: validUTF8.count) { + _ = $0.initialize(from: validUTF8) + return validUTF8.count + } + expectEqual("Café", cafe1) + + let cafe2 = String(unsafeUninitializedCapacity: invalidUTF8.count) { + _ = $0.initialize(from: invalidUTF8) + return invalidUTF8.count + } + expectEqual("Caf�", cafe2) + + let empty = String(unsafeUninitializedCapacity: 16) { _ in + // Can't initialize the buffer (e.g. the capacity is too small). + return 0 + } + expectTrue(empty.isEmpty) + } +} diff --git a/utils/gyb.py b/utils/gyb.py index 2df9a0311dbb6..b0f6e78c63d5a 100755 --- a/utils/gyb.py +++ b/utils/gyb.py @@ -1208,12 +1208,13 @@ def succ(a): help='''Bindings to be set in the template's execution context''') parser.add_argument( - 'file', type=argparse.FileType(), + 'file', type=argparse.FileType('rb'), help='Path to GYB template file (defaults to stdin)', nargs='?', - default=sys.stdin) + default=sys.stdin) # FIXME: stdin not binary mode on Windows parser.add_argument( - '-o', dest='target', type=argparse.FileType('w'), - help='Output file (defaults to stdout)', default=sys.stdout) + '-o', dest='target', type=argparse.FileType('wb'), + help='Output file (defaults to stdout)', + default=sys.stdout) # FIXME: stdout not binary mode on Windows parser.add_argument( '--test', action='store_true', default=False, help='Run a self-test')