diff --git a/docs/ErrorHandlingRationale.rst b/docs/ErrorHandlingRationale.rst index 71dd3e4de5fc3..a55956b868639 100644 --- a/docs/ErrorHandlingRationale.rst +++ b/docs/ErrorHandlingRationale.rst @@ -1180,7 +1180,7 @@ Haskell Haskell provides three different common error-propagation mechanisms. The first is that, like many other functional languages, it supports -manual propagation with a ``Maybe`` type. A function can return ``None`` +manual propagation with a ``Maybe`` type. A function can return ``Nothing`` to indicate that it couldn't produce a more useful result. This is the most common failure method for functions in the functional subset of the library. diff --git a/lib/IRGen/AllocStackHoisting.cpp b/lib/IRGen/AllocStackHoisting.cpp index 1b2b996621127..a1b027974e675 100644 --- a/lib/IRGen/AllocStackHoisting.cpp +++ b/lib/IRGen/AllocStackHoisting.cpp @@ -12,15 +12,15 @@ #define DEBUG_TYPE "alloc-stack-hoisting" -#include "swift/IRGen/IRGenSILPasses.h" #include "swift/AST/Availability.h" -#include "swift/SILOptimizer/Analysis/Analysis.h" -#include "swift/SILOptimizer/PassManager/Passes.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/IRGen/IRGenSILPasses.h" #include "swift/SIL/DebugUtils.h" +#include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" -#include "swift/SIL/SILArgument.h" +#include "swift/SILOptimizer/Analysis/Analysis.h" +#include "swift/SILOptimizer/PassManager/Passes.h" +#include "swift/SILOptimizer/PassManager/Transforms.h" #include "IRGenModule.h" #include "NonFixedTypeInfo.h" @@ -124,7 +124,7 @@ insertDeallocStackAtEndOf(SmallVectorImpl &FunctionExits, /// Hack to workaround a clang LTO bug. LLVM_ATTRIBUTE_NOINLINE -void moveAllocStackToBeginningOfBlock(AllocStackInst* AS, SILBasicBlock *BB) { +void moveAllocStackToBeginningOfBlock(AllocStackInst *AS, SILBasicBlock *BB) { AS->moveFront(BB); } @@ -149,7 +149,8 @@ void Partition::assignStackLocation( // Rewrite all the other alloc_stacks in the partition to use the assigned // location. for (auto *AllocStack : Elts) { - if (AssignedLoc == AllocStack) continue; + if (AssignedLoc == AllocStack) + continue; eraseDeallocStacks(AllocStack); AllocStack->replaceAllUsesWith(AssignedLoc); AllocStack->eraseFromParent(); @@ -277,8 +278,8 @@ void MergeStackSlots::mergeSlots() { // Check if we can add it to an existing partition that we have show to be // non-interfering. for (auto &CandidateP : DisjointPartitions) { - // If the candidate partition is empty (the very first time we look at an - // alloc_stack) we can just add the alloc_stack. + // If the candidate partition is empty (the very first time we look at + // an alloc_stack) we can just add the alloc_stack. if (CandidateP.Elts.empty()) { CandidateP.Elts.push_back(CurAllocStack); FoundAPartition = true; @@ -316,7 +317,6 @@ void MergeStackSlots::mergeSlots() { } } - namespace { /// Hoist alloc_stack instructions to the entry block and merge them. class HoistAllocStack { @@ -437,8 +437,7 @@ class AllocStackHoisting : public SILFunctionTransform { auto *F = getFunction(); auto *Mod = getIRGenModule(); assert(Mod && "This pass must be run as part of an IRGen pipeline"); - bool Changed = HoistAllocStack(F, *Mod).run(); - if (Changed) { + if (HoistAllocStack(F, *Mod).run()) { PM->invalidateAnalysis(F, SILAnalysis::InvalidationKind::Instructions); } } diff --git a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp index 1fd6a2630522b..da85284179126 100644 --- a/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp +++ b/lib/SILOptimizer/FunctionSignatureTransforms/FunctionSignatureOpts.cpp @@ -20,12 +20,12 @@ /// /// Another possibility is to implement these optimizations as separate passes, /// but then we would send slightly different functions to the pass pipeline -/// multiple times through notifyPassManagerOfFunction. +/// multiple times through notifyPassManagerOfFunction. /// /// TODO: Optimize function with generic parameters. /// /// TODO: Improve epilogue release matcher, i.e. do a data flow instead of -/// only finding releases in the return block. +/// only finding releases in the return block. /// //===----------------------------------------------------------------------===// @@ -34,7 +34,6 @@ #include "swift/SIL/DebugUtils.h" #include "swift/SIL/SILCloner.h" #include "swift/SIL/SILFunction.h" -#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "swift/SIL/SILValue.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/CallerAnalysis.h" @@ -44,6 +43,7 @@ #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SILOptimizer/Utils/SILInliner.h" +#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "swift/SILOptimizer/Utils/SpecializationMangler.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/CommandLine.h" @@ -54,7 +54,8 @@ using namespace swift; STATISTIC(NumFunctionSignaturesOptimized, "Total func sig optimized"); STATISTIC(NumDeadArgsEliminated, "Total dead args eliminated"); STATISTIC(NumOwnedConvertedToGuaranteed, "Total owned args -> guaranteed args"); -STATISTIC(NumOwnedConvertedToNotOwnedResult, "Total owned result -> not owned result"); +STATISTIC(NumOwnedConvertedToNotOwnedResult, + "Total owned result -> not owned result"); STATISTIC(NumSROAArguments, "Total SROA arguments optimized"); using SILParameterInfoList = llvm::SmallVector; @@ -228,9 +229,9 @@ static bool usesGenerics(SILFunction *F, if (&BB != &*F->begin()) { // Scan types of all BB arguments. Ignore the entry BB, because // it is handled in a special way. - Arg->getType().getASTType().visit(FindArchetypesAndGenericTypes); - if (UsesGenerics) - return UsesGenerics; + Arg->getType().getASTType().visit(FindArchetypesAndGenericTypes); + if (UsesGenerics) + return UsesGenerics; } } // Scan types of all operands. @@ -356,17 +357,17 @@ FunctionSignatureTransformDescriptor::createOptimizedSILFunctionType() { // The set of used archetypes is complete now. if (!UsesGenerics) { // None of the generic type parameters are used. - LLVM_DEBUG(llvm::dbgs() << "None of generic parameters are used by " - << F->getName() << "\n"; - llvm::dbgs() << "Interface params:\n"; - for (auto Param : InterfaceParams) { - Param.getType().dump(); - } - - llvm::dbgs() << "Interface results:\n"; - for (auto Result : InterfaceResults) { - Result.getType().dump(); - }); + LLVM_DEBUG( + llvm::dbgs() << "None of generic parameters are used by " + << F->getName() << "\n"; + llvm::dbgs() << "Interface params:\n"; + for (auto Param + : InterfaceParams) { Param.getType().dump(); } + + llvm::dbgs() + << "Interface results:\n"; + for (auto Result + : InterfaceResults) { Result.getType().dump(); }); } } @@ -574,8 +575,7 @@ void FunctionSignatureTransform::createFunctionSignatureOptimizedFunction() { // Produce a substitutions list and a set of substituted SIL types // required for creating a new SIL function. Subs = F->getForwardingSubstitutionMap(); - auto SubstCalleeType = - GenCalleeType->substGenericArgs(M, Subs); + auto SubstCalleeType = GenCalleeType->substGenericArgs(M, Subs); SubstCalleeSILType = SILType::getPrimitiveObjectType(SubstCalleeType); SILFunctionConventions Conv(SubstCalleeType, M); ResultType = Conv.getSILResultType(); @@ -619,19 +619,6 @@ bool FunctionSignatureTransform::run(bool hasCaller) { bool Changed = false; SILFunction *F = TransformDescriptor.OriginalFunction; - // Never repeat the same function signature optimization on the same function. - // Multiple function signature optimizations are composed by successively - // optmizing the newly created functions. Each optimization creates a new - // level of thunk. Those should all be ultimately inlined away. - // - // This happens, for example, when a new reference to the original function is - // discovered during devirtualization. That will cause the original function - // (now and FSO thunk) to be pushed back on the function pass pipeline. - if (F->isThunk() == IsSignatureOptimizedThunk) { - LLVM_DEBUG(llvm::dbgs() << " FSO already performed on this thunk\n"); - return false; - } - if (!hasCaller && (F->getDynamicallyReplacedFunction() || canBeCalledIndirectly(F->getRepresentation()))) { LLVM_DEBUG(llvm::dbgs() << " function has no caller -> abort\n"); @@ -690,10 +677,7 @@ bool FunctionSignatureTransform::run(bool hasCaller) { // After this optimization CapturePropagation can replace the partial_apply by a // direct reference to the specialized function. bool FunctionSignatureTransform::removeDeadArgs(int minPartialAppliedArgs) { - if (minPartialAppliedArgs < 1) - return false; - - if (!DeadArgumentAnalyzeParameters()) + if (minPartialAppliedArgs < 1 || !DeadArgumentAnalyzeParameters()) return false; SILFunction *F = TransformDescriptor.OriginalFunction; @@ -737,16 +721,15 @@ bool FunctionSignatureTransform::removeDeadArgs(int minPartialAppliedArgs) { namespace { class FunctionSignatureOpts : public SILFunctionTransform { - + /// If true, perform a special kind of dead argument elimination to enable /// removal of partial_apply instructions where all partially applied /// arguments are dead. bool OptForPartialApply; public: - - FunctionSignatureOpts(bool OptForPartialApply) : - OptForPartialApply(OptForPartialApply) { } + FunctionSignatureOpts(bool OptForPartialApply) + : OptForPartialApply(OptForPartialApply) {} void run() override { auto *F = getFunction(); @@ -763,15 +746,15 @@ class FunctionSignatureOpts : public SILFunctionTransform { return; // This is the function to optimize. - LLVM_DEBUG(llvm::dbgs() << "*** FSO on function: " << F->getName() - << " ***\n"); + LLVM_DEBUG(llvm::dbgs() + << "*** FSO on function: " << F->getName() << " ***\n"); // Check the signature of F to make sure that it is a function that we // can specialize. These are conditions independent of the call graph. // No need for CallerAnalysis if we are not optimizing for partial // applies. - if (!OptForPartialApply && - !canSpecializeFunction(F, nullptr, OptForPartialApply)) { + if (!(OptForPartialApply || + canSpecializeFunction(F, nullptr, OptForPartialApply))) { LLVM_DEBUG(llvm::dbgs() << " cannot specialize function -> abort\n"); return; } @@ -786,7 +769,20 @@ class FunctionSignatureOpts : public SILFunctionTransform { LLVM_DEBUG(llvm::dbgs() << " cannot specialize function -> abort\n"); return; } - + // Never repeat the same function signature optimization on the same + // function. Multiple function signature optimizations are composed by + // successively optmizing the newly created functions. Each optimization + // creates a new level of thunk. Those should all be ultimately inlined + // away. + // + // This happens, for example, when a new reference to the original function + // is discovered during devirtualization. That will cause the original + // function (now and FSO thunk) to be pushed back on the function pass + // pipeline. + if (F->isThunk() == IsSignatureOptimizedThunk) { + LLVM_DEBUG(llvm::dbgs() << " FSO already performed on this thunk\n"); + return false; + } // Ok, we think we can perform optimization. Now perform a quick check auto *RCIA = getAnalysis(); auto *EA = PM->getAnalysis(); @@ -795,8 +791,8 @@ class FunctionSignatureOpts : public SILFunctionTransform { // going to change, make sure the mangler is aware of all the changes done // to the function. auto P = Demangle::SpecializationPass::FunctionSignatureOpts; - Mangle::FunctionSignatureSpecializationMangler Mangler(P, - F->isSerialized(), F); + Mangle::FunctionSignatureSpecializationMangler Mangler(P, F->isSerialized(), + F); /// Keep a map between the exploded argument index and the original argument /// index. @@ -823,7 +819,7 @@ class FunctionSignatureOpts : public SILFunctionTransform { FunctionSignatureTransform FST(FuncBuilder, F, RCIA, EA, Mangler, AIM, ArgumentDescList, ResultDescList); - bool Changed = false; + bool Changed; if (OptForPartialApply) { Changed = FST.removeDeadArgs(FuncInfo.getMinPartialAppliedArgs()); } else { @@ -853,7 +849,6 @@ class FunctionSignatureOpts : public SILFunctionTransform { restartPassPipeline(); } } - }; } // end anonymous namespace diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 2c6cb3daf5db9..36e560ff7c711 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -123,7 +123,8 @@ static void addMandatoryOptPipeline(SILPassPipelinePlan &P) { // Diagnostic ConstantPropagation must be rerun on deserialized functions // because it is sensitive to the assert configuration. - // Consequently, certain optimization passes beyond this point will also rerun. + // Consequently, certain optimization passes beyond this point will also + // rerun. P.addDiagnosticConstantPropagation(); // Now that we have emitted constant propagation diagnostics, try to eliminate @@ -253,7 +254,8 @@ void addHighLevelLoopOptPasses(SILPassPipelinePlan &P) { } // Perform classic SSA optimizations. -void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { +void addSSAFunctionPasses(SILPassPipelinePlan &P, + OptimizationLevelKind OpLevel) { // Promote box allocations to stack allocations. P.addAllocBoxToStack(); @@ -281,7 +283,20 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { P.addSILCombine(); // Mainly for Array.append(contentsOf) optimization. - P.addArrayElementPropagation(); + // Perform a round of array optimization in the mid-level pipeline after + // potentially inlining calls to Array append. In that situation, the + // high-level pipeline does ArrayElementPropagation and the mid-level pipeline + // handles uniqueness hoisting. Do this as late as possible before inlining + // because it must run between runs of the inliner when the pipeline restarts. + if (OpLevel == OptimizationLevelKind::MidLevel) { + P.addHighLevelLICM(); + P.addArrayCountPropagation(); + P.addABCOpt(); + P.addDCE(); + P.addCOWArrayOpts(); + P.addDCE(); + P.addSwiftArrayOpts(); + } // Specialize opaque archetypes. // This can expose oportunities for the generic specializer. @@ -302,21 +317,6 @@ void addSSAPasses(SILPassPipelinePlan &P, OptimizationLevelKind OpLevel) { P.addEarlyInliner(); break; case OptimizationLevelKind::MidLevel: - P.addGlobalOpt(); - P.addLetPropertiesOpt(); - // It is important to serialize before any of the @_semantics - // functions are inlined, because otherwise the information about - // uses of such functions inside the module is lost, - // which reduces the ability of the compiler to optimize clients - // importing this module. - P.addSerializeSILPass(); - - // Now strip any transparent functions that still have ownership. - if (P.getOptions().StripOwnershipAfterSerialization) - P.addOwnershipModelEliminator(); - - if (P.getOptions().StopOptimizationAfterSerialization) - return; // Does inline semantics-functions (except "availability"), but not // global-init functions. @@ -410,11 +410,11 @@ static void addHighLevelEarlyLoopOptPipeline(SILPassPipelinePlan &P) { P.startPipeline("HighLevel+EarlyLoopOpt"); // FIXME: update this to be a function pass. P.addEagerSpecializer(); - addSSAPasses(P, OptimizationLevelKind::HighLevel); + addSSAFunctionPasses(P, OptimizationLevelKind::HighLevel); addHighLevelLoopOptPasses(P); } -static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) { +static void addMidLevelModulePipeline(SILPassPipelinePlan &P) { P.startPipeline("MidModulePasses+StackPromote"); P.addDeadFunctionElimination(); P.addPerformanceSILLinker(); @@ -423,13 +423,24 @@ static void addMidModulePassesStackPromotePassPipeline(SILPassPipelinePlan &P) { // Do the first stack promotion on high-level SIL. P.addStackPromotion(); + + P.addGlobalOpt(); + P.addLetPropertiesOpt(); + // It is important to serialize before any of the @_semantics + // functions are inlined, because otherwise the information about + // uses of such functions inside the module is lost, + // which reduces the ability of the compiler to optimize clients + // importing this module. + P.addSerializeSILPass(); + + // Now strip any transparent functions that still have ownership. + if (P.getOptions().StripOwnershipAfterSerialization) + P.addOwnershipModelEliminator(); } -static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) { +static void addMidLevelFunctionPipeline(SILPassPipelinePlan &P) { P.startPipeline("MidLevel"); - addSSAPasses(P, OptimizationLevelKind::MidLevel); - if (P.getOptions().StopOptimizationAfterSerialization) - return true; + addSSAFunctionPasses(P, OptimizationLevelKind::MidLevel); // Specialize partially applied functions with dead arguments as a preparation // for CapturePropagation. @@ -438,7 +449,6 @@ static bool addMidLevelPassPipeline(SILPassPipelinePlan &P) { // Run loop unrolling after inlining and constant propagation, because loop // trip counts may have became constant. P.addLoopUnroll(); - return false; } static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { @@ -447,8 +457,8 @@ static void addClosureSpecializePassPipeline(SILPassPipelinePlan &P) { P.addDeadStoreElimination(); P.addDeadObjectElimination(); - // These few passes are needed to cleanup between loop unrolling and GlobalOpt. - // This is needed to fully optimize static small String constants. + // These few passes are needed to cleanup between loop unrolling and + // GlobalOpt. This is needed to fully optimize static small String constants. P.addSimplifyCFG(); P.addSILCombine(); P.addPerformanceConstantPropagation(); @@ -492,7 +502,7 @@ static void addLowLevelPassPipeline(SILPassPipelinePlan &P) { // Should be after FunctionSignatureOpts and before the last inliner. P.addReleaseDevirtualizer(); - addSSAPasses(P, OptimizationLevelKind::LowLevel); + addSSAFunctionPasses(P, OptimizationLevelKind::LowLevel); P.addDeadObjectElimination(); P.addObjectOutliner(); @@ -618,9 +628,13 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) { addHighLevelEarlyLoopOptPipeline(P); addMidModulePassesStackPromotePassPipeline(P); - // Run an iteration of the mid-level SSA passes. - if (addMidLevelPassPipeline(P)) + addMidLevelModulePipeline(P); + if (Options.StopOptimizationAfterSerialization) { return P; + } + + // Run an iteration of the mid-level SSA function passes. + addMidLevelFunctionPipeline(P); // Perform optimizations that specialize. addClosureSpecializePassPipeline(P); @@ -657,7 +671,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { P.startPipeline("Serialization"); P.addSerializeSILPass(); - // And then strip ownership... + // Now strip any transparent functions that still have ownership. if (Options.StripOwnershipAfterSerialization) P.addOwnershipModelEliminator(); @@ -732,17 +746,18 @@ void SILPassPipelinePlan::print(llvm::raw_ostream &os) { // rather than use the yaml writer interface. We want to use the yaml reader // interface to be resilient against slightly different forms of yaml. os << "[\n"; - interleave(getPipelines(), - [&](const SILPassPipeline &Pipeline) { - os << " [\n"; - - os << " \"" << Pipeline.Name << "\""; - for (PassKind Kind : getPipelinePasses(Pipeline)) { - os << ",\n [\"" << PassKindID(Kind) << "\"," - << "\"" << PassKindTag(Kind) << "\"]"; - } - }, - [&] { os << "\n ],\n"; }); + interleave( + getPipelines(), + [&](const SILPassPipeline &Pipeline) { + os << " [\n"; + + os << " \"" << Pipeline.Name << "\""; + for (PassKind Kind : getPipelinePasses(Pipeline)) { + os << ",\n [\"" << PassKindID(Kind) << "\"," + << "\"" << PassKindTag(Kind) << "\"]"; + } + }, + [&] { os << "\n ],\n"; }); os << "\n ]\n"; os << ']'; } diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index f09294ffdac7a..1efdc95d46a9e 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -35,13 +35,13 @@ llvm::cl::opt PrintShortestPathInfo( "print-shortest-path-info", llvm::cl::init(false), llvm::cl::desc("Print shortest-path information for inlining")); -llvm::cl::opt EnableSILInliningOfGenerics( - "sil-inline-generics", llvm::cl::init(false), - llvm::cl::desc("Enable inlining of generics")); +llvm::cl::opt + EnableSILInliningOfGenerics("sil-inline-generics", llvm::cl::init(false), + llvm::cl::desc("Enable inlining of generics")); llvm::cl::opt EnableSILAggressiveInlining("sil-aggressive-inline", llvm::cl::init(false), - llvm::cl::desc("Enable aggressive inlining")); + llvm::cl::desc("Enable aggressive inlining")); //===----------------------------------------------------------------------===// // Performance Inliner @@ -100,6 +100,9 @@ class SILPerformanceInliner { /// The benefit of a onFastPath builtin. FastPathBuiltinBenefit = RemovedCallBenefit + 40, + /// The benefit of inlining a function with a semantic call site. + SemanticCallBenefit = RemovedCallBenefit + 50, + /// The benefit of being able to devirtualize a call. DevirtualizedCallBenefit = RemovedCallBenefit + 300, @@ -185,11 +188,11 @@ class SILPerformanceInliner { public: SILPerformanceInliner(SILOptFunctionBuilder &FuncBuilder, - InlineSelection WhatToInline, DominanceAnalysis *DA, + InlineSelection WhatToInline, DominanceAnalysis *DA, SILLoopAnalysis *LA, SideEffectAnalysis *SEA, OptimizationMode OptMode, OptRemark::Emitter &ORE) : FuncBuilder(FuncBuilder), WhatToInline(WhatToInline), DA(DA), LA(LA), - SEA(SEA), CBI(DA), ORE(ORE), OptMode(OptMode) {} + SEA(SEA), CBI(DA), ORE(ORE), OptMode(OptMode) {} bool inlineCallsIntoFunction(SILFunction *F); }; @@ -218,9 +221,9 @@ bool SILPerformanceInliner::profileBasedDecision( auto callerCount = bbIt->getSecond(); if (callerCount < 1) { // Never called - do not inline - LLVM_DEBUG(dumpCaller(AI.getFunction()); - llvm::dbgs() << "profiled decision: NO, " - "reason= Never Called.\n"); + LLVM_DEBUG(dumpCaller(AI.getFunction()); llvm::dbgs() + << "profiled decision: NO, " + "reason= Never Called.\n"); return false; } auto calleeCount = Callee->getEntryCount(); @@ -246,8 +249,8 @@ bool SILPerformanceInliner::profileBasedDecision( return false; } LLVM_DEBUG(dumpCaller(AI.getFunction()); - llvm::dbgs() << "profiled decision: YES, reason=IHF " - << callerCount << '\n'); + llvm::dbgs() + << "profiled decision: YES, reason=IHF " << callerCount << '\n'); } // We're gonna inline! NumCallerBlocks += Callee->size(); @@ -286,16 +289,20 @@ bool SILPerformanceInliner::isProfitableToInline( isClassMethodAtOsize = true; } // Use command line option to control inlining in Osize mode. - const uint64_t CallerBaseBenefitReductionFactor = AI.getFunction()->getModule().getOptions().CallerBaseBenefitReductionFactor; + const uint64_t CallerBaseBenefitReductionFactor = + AI.getFunction() + ->getModule() + .getOptions() + .CallerBaseBenefitReductionFactor; BaseBenefit = BaseBenefit / CallerBaseBenefitReductionFactor; } // It is always OK to inline a simple call. // TODO: May be consider also the size of the callee? if (isPureCall(AI, SEA)) { - LLVM_DEBUG(dumpCaller(AI.getFunction()); - llvm::dbgs() << " pure-call decision " << Callee->getName() - << '\n'); + LLVM_DEBUG(dumpCaller(AI.getFunction()); llvm::dbgs() + << " pure-call decision " + << Callee->getName() << '\n'); return true; } @@ -348,13 +355,25 @@ bool SILPerformanceInliner::isProfitableToInline( CalleeCost += (int)instructionInlineCost(I); if (FullApplySite FAI = FullApplySite::isa(&I)) { + // Call sites into semantic calls need to be inlined into the parent + // scope for optimization based on those semantics to kick in. This may + // mean the call can be hoisted out of a loop for example. Do this only + // after the top-level scope is fully specialized, otherwise it could + // actually prevent inlining of callers. + + SILFunction *Callee = FAI.getReferencedFunction(); + + if (!IsGeneric && Callee && Callee->hasSemanticsAttrs()) { + BlockW.updateBenefit(Benefit, SemanticCallBenefit); + } + // Check if the callee is passed as an argument. If so, increase the // threshold, because inlining will (probably) eliminate the closure. SILInstruction *def = constTracker.getDefInCaller(FAI.getCallee()); if (def && (isa(def) || isa(def))) BlockW.updateBenefit(Benefit, RemovedClosureBenefit); // Check if inlining the callee would allow for further - // optimizations like devirtualization or generic specialization. + // optimizations like devirtualization or generic specialization. if (!def) def = dyn_cast_or_null(FAI.getCallee()); @@ -363,12 +382,13 @@ bool SILPerformanceInliner::isProfitableToInline( auto Subs = FAI.getSubstitutionMap(); - // Bail if it is not a generic call or inlining of generics is forbidden. - if (!EnableSILInliningOfGenerics || !Subs.hasAnySubstitutableParams()) + // Ignore anything else that is not a generic call or if inlining of + // generics is forbidden. + if (!(EnableSILInliningOfGenerics && Subs.hasAnySubstitutableParams())) continue; - if (!isa(def) && !isa(def) && - !isa(def)) + if (!(isa(def) || isa(def) || + isa(def))) continue; // It is a generic call inside the callee. Check if after inlining @@ -441,7 +461,8 @@ bool SILPerformanceInliner::isProfitableToInline( } } // Don't count costs in blocks which are dead after inlining. - SILBasicBlock *takenBlock = constTracker.getTakenBlock(block->getTerminator()); + SILBasicBlock *takenBlock = + constTracker.getTakenBlock(block->getTerminator()); if (takenBlock) { BlockW.updateBenefit(Benefit, RemovedTerminatorBenefit); domOrder.pushChildrenIf(block, [=](SILBasicBlock *child) { @@ -474,9 +495,8 @@ bool SILPerformanceInliner::isProfitableToInline( // cubic function on the number of caller blocks. This starts to prevent // inlining at about 800 - 1000 caller blocks. if (NumCallerBlocks < BlockLimitMaxIntNumerator) - Benefit -= - (NumCallerBlocks * NumCallerBlocks) / BlockLimitDenominator * - NumCallerBlocks / BlockLimitDenominator; + Benefit -= (NumCallerBlocks * NumCallerBlocks) / BlockLimitDenominator * + NumCallerBlocks / BlockLimitDenominator; else // The calculation in the if branch would overflow if we performed it. Benefit = 0; @@ -494,7 +514,7 @@ bool SILPerformanceInliner::isProfitableToInline( // This is the final inlining decision. if (CalleeCost > Benefit) { - ORE.emit([&]() { + OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { using namespace OptRemark; return RemarkMissed("NoInlinedCost", *AI.getInstruction()) << "Not profitable to inline function " << NV("Callee", Callee) @@ -510,11 +530,10 @@ bool SILPerformanceInliner::isProfitableToInline( llvm::dbgs() << " decision {c=" << CalleeCost << ", b=" << Benefit << ", l=" << SPA->getScopeLength(CalleeEntry, 0) - << ", c-w=" << CallerWeight - << ", bb=" << Callee->size() - << ", c-bb=" << NumCallerBlocks + << ", c-w=" << CallerWeight << ", bb=" + << Callee->size() << ", c-bb=" << NumCallerBlocks << "} " << Callee->getName() << '\n'); - ORE.emit([&]() { + OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() { using namespace OptRemark; return RemarkPassed("Inlined", *AI.getInstruction()) << NV("Callee", Callee) << " inlined into " @@ -533,8 +552,7 @@ bool SILPerformanceInliner::isProfitableToInline( /// It returns None if the decision cannot be made without a more complex /// analysis. static Optional shouldInlineGeneric(FullApplySite AI) { - assert(AI.hasSubstitutions() && - "Expected a generic apply"); + assert(AI.hasSubstitutions() && "Expected a generic apply"); SILFunction *Callee = AI.getReferencedFunctionOrNull(); @@ -584,9 +602,9 @@ bool SILPerformanceInliner::decideInWarmBlock( SILFunction *Callee = AI.getReferencedFunctionOrNull(); if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent()) { - LLVM_DEBUG(dumpCaller(AI.getFunction()); - llvm::dbgs() << " always-inline decision " - << Callee->getName() << '\n'); + LLVM_DEBUG(dumpCaller(AI.getFunction()); llvm::dbgs() + << " always-inline decision " + << Callee->getName() << '\n'); return true; } @@ -607,9 +625,9 @@ bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI, } if (Callee->getInlineStrategy() == AlwaysInline || Callee->isTransparent()) { - LLVM_DEBUG(dumpCaller(AI.getFunction()); - llvm::dbgs() << " always-inline decision " - << Callee->getName() << '\n'); + LLVM_DEBUG(dumpCaller(AI.getFunction()); llvm::dbgs() + << " always-inline decision " + << Callee->getName() << '\n'); return true; } @@ -633,8 +651,9 @@ bool SILPerformanceInliner::decideInColdBlock(FullApplySite AI, /// Why can't we just add the weight when we call isProfitableToInline? Because /// the additional weight is for _another_ function than the current handled /// callee. -static void addWeightCorrection(FullApplySite FAS, - llvm::DenseMap &WeightCorrections) { +static void +addWeightCorrection(FullApplySite FAS, + llvm::DenseMap &WeightCorrections) { SILFunction *Callee = FAS.getReferencedFunctionOrNull(); if (Callee && Callee->hasSemanticsAttr("array.uninitialized")) { // We want to inline the argument to an array.uninitialized call, because @@ -700,13 +719,15 @@ calculateBBWeights(SILFunction *Caller, DominanceInfo *DT, assert(BBToWeightMap.find(currBB) != BBToWeightMap.end() && "Expected to find block in map"); auto currCount = succ.getCount(); - if (!currCount) { + if (currCount) { + auto currCountVal = currCount.getValue(); + countSum += currCountVal; + BBToWeightMap[currBB] += currCountVal; + + } else { ++blocksWithoutCount; continue; } - auto currCountVal = currCount.getValue(); - countSum += currCountVal; - BBToWeightMap[currBB] += currCountVal; } if (countSum < bbCount) { // inaccurate profile - fill in the gaps for BBs without a count: @@ -750,7 +771,6 @@ void SILPerformanceInliner::collectAppliesToInline( // Compute the shortest-path analysis for the caller. ShortestPathAnalysis *SPA = getSPA(Caller, LI); SPA->analyze(CBI, [&](FullApplySite FAS) -> int { - // This closure returns the length of a called function. // At this occasion we record additional weight increases. @@ -836,7 +856,7 @@ void SILPerformanceInliner::collectAppliesToInline( } } - domOrder.pushChildrenIf(block, [&] (SILBasicBlock *child) { + domOrder.pushChildrenIf(block, [&](SILBasicBlock *child) { if (CBI.isSlowPath(block, child)) { // Handle cold blocks separately. visitColdBlocks(InitialCandidates, child, DT); @@ -952,7 +972,6 @@ void SILPerformanceInliner::visitColdBlocks( } } - //===----------------------------------------------------------------------===// // Performance Inliner Pass //===----------------------------------------------------------------------===// @@ -965,8 +984,8 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { std::string PassName; public: - SILPerformanceInlinerPass(InlineSelection WhatToInline, StringRef LevelName): - WhatToInline(WhatToInline), PassName(LevelName) { + SILPerformanceInlinerPass(InlineSelection WhatToInline, StringRef LevelName) + : WhatToInline(WhatToInline), PassName(LevelName) { PassName.append(" Performance Inliner"); } @@ -984,7 +1003,7 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { SILOptFunctionBuilder FuncBuilder(*this); SILPerformanceInliner Inliner(FuncBuilder, WhatToInline, DA, LA, SEA, - OptMode, ORE); + OptMode, ORE); assert(getFunction()->isDefinition() && "Expected only functions with bodies!"); @@ -998,7 +1017,6 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { restartPassPipeline(); } } - }; } // end anonymous namespace @@ -1006,7 +1024,7 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { /// the @_semantics, @_effects or global_init attributes. SILTransform *swift::createEarlyInliner() { return new SILPerformanceInlinerPass( - InlineSelection::NoSemanticsAndGlobalInit, "Early"); + InlineSelection::NoSemanticsAndGlobalInit, "Early"); } /// Create an inliner pass that does not inline functions that are marked with diff --git a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp index 043815f8b813c..c6084c0ef1195 100644 --- a/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp +++ b/lib/SILOptimizer/Utils/PerformanceInlinerUtils.cpp @@ -13,6 +13,12 @@ #include "swift/SILOptimizer/Utils/PerformanceInlinerUtils.h" #include "swift/AST/Module.h" #include "swift/SILOptimizer/Utils/Local.h" +#include "llvm/Support/CommandLine.h" + +llvm::cl::opt + SILInlineNeverFuns("sil-inline-never-functions", llvm::cl::init(""), + llvm::cl::desc("Never inline functions whose name " + "includes this string.")); //===----------------------------------------------------------------------===// // ConstantTracker @@ -64,7 +70,8 @@ SILValue ConstantTracker::getStoredValue(SILInstruction *loadInst, SILInstruction *store = links[loadInst]; if (!store && callerTracker) store = callerTracker->links[loadInst]; - if (!store) return SILValue(); + if (!store) + return SILValue(); assert(isa(loadInst) || isa(loadInst)); @@ -145,112 +152,115 @@ SILInstruction *ConstantTracker::getDef(SILValue val, } } -ConstantTracker::IntConst ConstantTracker::getBuiltinConst(BuiltinInst *BI, int depth) { +ConstantTracker::IntConst ConstantTracker::getBuiltinConst(BuiltinInst *BI, + int depth) { const BuiltinInfo &Builtin = BI->getBuiltinInfo(); OperandValueArrayRef Args = BI->getArguments(); switch (Builtin.ID) { - default: break; + default: + break; - // Fold comparison predicates. + // Fold comparison predicates. #define BUILTIN(id, name, Attrs) -#define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload) \ -case BuiltinValueKind::id: +#define BUILTIN_BINARY_PREDICATE(id, name, attrs, overload) \ + case BuiltinValueKind::id: #include "swift/AST/Builtins.def" { IntConst lhs = getIntConst(Args[0], depth); IntConst rhs = getIntConst(Args[1], depth); if (lhs.isValid && rhs.isValid) { - return IntConst(constantFoldComparison(lhs.value, rhs.value, - Builtin.ID), - lhs.isFromCaller || rhs.isFromCaller); + return IntConst( + constantFoldComparison(lhs.value, rhs.value, Builtin.ID), + lhs.isFromCaller || rhs.isFromCaller); } break; } - case BuiltinValueKind::SAddOver: - case BuiltinValueKind::UAddOver: - case BuiltinValueKind::SSubOver: - case BuiltinValueKind::USubOver: - case BuiltinValueKind::SMulOver: - case BuiltinValueKind::UMulOver: { - IntConst lhs = getIntConst(Args[0], depth); - IntConst rhs = getIntConst(Args[1], depth); - if (lhs.isValid && rhs.isValid) { - bool IgnoredOverflow; - return IntConst(constantFoldBinaryWithOverflow(lhs.value, rhs.value, - IgnoredOverflow, - getLLVMIntrinsicIDForBuiltinWithOverflow(Builtin.ID)), - lhs.isFromCaller || rhs.isFromCaller); - } - break; + case BuiltinValueKind::SAddOver: + case BuiltinValueKind::UAddOver: + case BuiltinValueKind::SSubOver: + case BuiltinValueKind::USubOver: + case BuiltinValueKind::SMulOver: + case BuiltinValueKind::UMulOver: { + IntConst lhs = getIntConst(Args[0], depth); + IntConst rhs = getIntConst(Args[1], depth); + if (lhs.isValid && rhs.isValid) { + bool IgnoredOverflow; + return IntConst(constantFoldBinaryWithOverflow( + lhs.value, rhs.value, IgnoredOverflow, + getLLVMIntrinsicIDForBuiltinWithOverflow(Builtin.ID)), + lhs.isFromCaller || rhs.isFromCaller); } - - case BuiltinValueKind::SDiv: - case BuiltinValueKind::SRem: - case BuiltinValueKind::UDiv: - case BuiltinValueKind::URem: { - IntConst lhs = getIntConst(Args[0], depth); - IntConst rhs = getIntConst(Args[1], depth); - if (lhs.isValid && rhs.isValid && rhs.value != 0) { - bool IgnoredOverflow; - return IntConst(constantFoldDiv(lhs.value, rhs.value, - IgnoredOverflow, Builtin.ID), - lhs.isFromCaller || rhs.isFromCaller); - } - break; + break; + } + + case BuiltinValueKind::SDiv: + case BuiltinValueKind::SRem: + case BuiltinValueKind::UDiv: + case BuiltinValueKind::URem: { + IntConst lhs = getIntConst(Args[0], depth); + IntConst rhs = getIntConst(Args[1], depth); + if (lhs.isValid && rhs.isValid && rhs.value != 0) { + bool IgnoredOverflow; + return IntConst( + constantFoldDiv(lhs.value, rhs.value, IgnoredOverflow, Builtin.ID), + lhs.isFromCaller || rhs.isFromCaller); } - - case BuiltinValueKind::And: - case BuiltinValueKind::AShr: - case BuiltinValueKind::LShr: - case BuiltinValueKind::Or: - case BuiltinValueKind::Shl: - case BuiltinValueKind::Xor: { - IntConst lhs = getIntConst(Args[0], depth); - IntConst rhs = getIntConst(Args[1], depth); - if (lhs.isValid && rhs.isValid) { - return IntConst(constantFoldBitOperation(lhs.value, rhs.value, - Builtin.ID), - lhs.isFromCaller || rhs.isFromCaller); - } - break; + break; + } + + case BuiltinValueKind::And: + case BuiltinValueKind::AShr: + case BuiltinValueKind::LShr: + case BuiltinValueKind::Or: + case BuiltinValueKind::Shl: + case BuiltinValueKind::Xor: { + IntConst lhs = getIntConst(Args[0], depth); + IntConst rhs = getIntConst(Args[1], depth); + if (lhs.isValid && rhs.isValid) { + return IntConst( + constantFoldBitOperation(lhs.value, rhs.value, Builtin.ID), + lhs.isFromCaller || rhs.isFromCaller); } - - case BuiltinValueKind::Trunc: - case BuiltinValueKind::ZExt: - case BuiltinValueKind::SExt: - case BuiltinValueKind::TruncOrBitCast: - case BuiltinValueKind::ZExtOrBitCast: - case BuiltinValueKind::SExtOrBitCast: { - IntConst val = getIntConst(Args[0], depth); - if (val.isValid) { - return IntConst(constantFoldCast(val.value, Builtin), val.isFromCaller); - } - break; + break; + } + + case BuiltinValueKind::Trunc: + case BuiltinValueKind::ZExt: + case BuiltinValueKind::SExt: + case BuiltinValueKind::TruncOrBitCast: + case BuiltinValueKind::ZExtOrBitCast: + case BuiltinValueKind::SExtOrBitCast: { + IntConst val = getIntConst(Args[0], depth); + if (val.isValid) { + return IntConst(constantFoldCast(val.value, Builtin), val.isFromCaller); } + break; + } } return IntConst(); } // Tries to evaluate the integer constant of a value. The \p depth is used // to limit the complexity. -ConstantTracker::IntConst ConstantTracker::getIntConst(SILValue val, int depth) { +ConstantTracker::IntConst ConstantTracker::getIntConst(SILValue val, + int depth) { // Don't spend too much time with constant evaluation. if (depth >= 10) return IntConst(); - + SILInstruction *I = getDef(val); if (!I) return IntConst(); - + if (auto *IL = dyn_cast(I)) { return IntConst(IL->getValue(), IL->getFunction() != F); } if (auto *BI = dyn_cast(I)) { if (constCache.count(BI) != 0) return constCache[BI]; - + IntConst builtinConst = getBuiltinConst(BI, depth + 1); constCache[BI] = builtinConst; return builtinConst; @@ -325,8 +335,8 @@ int ShortestPathAnalysis::getEntryDistFromPreds(const SILBasicBlock *BB, for (SILBasicBlock *Pred : BB->getPredecessorBlocks()) { BlockInfo *PredInfo = getBlockInfo(Pred); Distances &PDists = PredInfo->getDistances(LoopDepth); - int DistFromEntry = PDists.DistFromEntry + PredInfo->Length + - PDists.LoopHeaderLength; + int DistFromEntry = + PDists.DistFromEntry + PredInfo->Length + PDists.LoopHeaderLength; assert(DistFromEntry >= 0); if (DistFromEntry < MinDist) MinDist = DistFromEntry; @@ -366,8 +376,8 @@ static SILBasicBlock *detectLoopBypassPreheader(SILLoop *Loop) { if (!CBR) return nullptr; - SILBasicBlock *Succ = (CBR->getTrueBB() == Pred ? CBR->getFalseBB() : - CBR->getTrueBB()); + SILBasicBlock *Succ = + (CBR->getTrueBB() == Pred ? CBR->getFalseBB() : CBR->getTrueBB()); for (SILBasicBlock *PredOfSucc : Succ->getPredecessorBlocks()) { SILBasicBlock *Exiting = PredOfSucc->getSinglePredecessorBlock(); @@ -379,7 +389,8 @@ static SILBasicBlock *detectLoopBypassPreheader(SILLoop *Loop) { return nullptr; } -void ShortestPathAnalysis::analyzeLoopsRecursively(SILLoop *Loop, int LoopDepth) { +void ShortestPathAnalysis::analyzeLoopsRecursively(SILLoop *Loop, + int LoopDepth) { if (LoopDepth >= MaxNumLoopLevels) return; @@ -399,7 +410,7 @@ void ShortestPathAnalysis::analyzeLoopsRecursively(SILLoop *Loop, int LoopDepth) solveDataFlow(Loop->getBlocks(), LoopDepth); int LoopLength = getExitDistFromSuccs(Loop->getHeader(), LoopDepth) + - HeaderInfo->getLength(LoopDepth); + HeaderInfo->getLength(LoopDepth); HeaderDists.DistToExit = LoopLength; // If there is a loop bypass edge, add the loop length to the loop pre-pre- @@ -411,16 +422,18 @@ void ShortestPathAnalysis::analyzeLoopsRecursively(SILLoop *Loop, int LoopDepth) // Add the full loop length (= assumed-iteration-count * length) to the loop // header so that it is considered in the parent scope. HeaderInfo->getDistances(LoopDepth - 1).LoopHeaderLength = - LoopCount * LoopLength; + LoopCount * LoopLength; } -ShortestPathAnalysis::Weight ShortestPathAnalysis:: -getWeight(SILBasicBlock *BB, Weight CallerWeight) { +ShortestPathAnalysis::Weight +ShortestPathAnalysis::getWeight(SILBasicBlock *BB, Weight CallerWeight) { assert(BB->getParent() == F); - // Return a conservative default if the analysis was not done due to a high number of blocks. + // Return a conservative default if the analysis was not done due to a high + // number of blocks. if (BlockInfos.empty()) - return Weight(CallerWeight.ScopeLength + ColdBlockLength, CallerWeight.LoopWeight); + return Weight(CallerWeight.ScopeLength + ColdBlockLength, + CallerWeight.LoopWeight); SILLoop *Loop = LI->getLoopFor(BB); if (!Loop) { @@ -441,8 +454,8 @@ getWeight(SILBasicBlock *BB, Weight CallerWeight) { while (Loop) { assert(LoopDepth > 0); BlockInfo *HeaderInfo = getBlockInfo(Loop->getHeader()); - int InnerLoopLength = HeaderInfo->getScopeLength(LoopDepth) * - ShortestPathAnalysis::LoopCount; + int InnerLoopLength = + HeaderInfo->getScopeLength(LoopDepth) * ShortestPathAnalysis::LoopCount; int OuterLoopWeight = SingleLoopWeight; int OuterScopeLength = HeaderInfo->getScopeLength(LoopDepth - 1); @@ -484,9 +497,7 @@ getWeight(SILBasicBlock *BB, Weight CallerWeight) { return W; } -void ShortestPathAnalysis::dump() { - printFunction(llvm::errs()); -} +void ShortestPathAnalysis::dump() { printFunction(llvm::errs()); } void ShortestPathAnalysis::printFunction(llvm::raw_ostream &OS) { OS << "SPA @" << F->getName() << "\n"; @@ -599,33 +610,64 @@ static bool calleeHasPartialApplyWithOpenedExistentials(FullApplySite AI) { return false; } -// Returns true if a given apply site should be skipped during the -// early inlining pass. +// Returns true if a given apply site with @_semantics should be skipped during +// the early inlining pass. // -// NOTE: Add here the checks for any specific @_semantics/@_effects -// attributes causing a given callee to be excluded from the inlining -// during the early inlining pass. -static bool shouldSkipApplyDuringEarlyInlining(FullApplySite AI) { - // Add here the checks for any specific @_semantics attributes that need - // to be skipped during the early inlining pass. +static bool shouldInlineSemanticApplyDuringEarlyInlining(FullApplySite AI) { + + // Skip all array semantic calls for various array optimizations. ArraySemanticsCall ASC(AI.getInstruction()); - if (ASC && !ASC.canInlineEarly()) - return true; + if (ASC) + return false; SILFunction *Callee = AI.getReferencedFunctionOrNull(); - if (!Callee) - return false; + assert(Callee && "The caller checks for a known callee."); if (Callee->hasSemanticsAttr("self_no_escaping_closure") || Callee->hasSemanticsAttr("pair_no_escaping_closure")) - return true; + return false; - // Add here the checks for any specific @_effects attributes that need - // to be skipped during the early inlining pass. - if (Callee->hasEffectsKind()) - return true; + if (Callee->hasSemanticsAttr("inline_late")) + return false; - return false; + return true; +} +bool shouldInlineSemanticCall(FullApplySite AI, InlineSelection WhatToInline) { + SILFunction *Callee = AI.getReferencedFunction(); + assert(Callee && "The caller checks for a known callee."); + + switch (WhatToInline) { + case InlineSelection::NoSemanticsAndGlobalInit: + if (!shouldInlineSemanticApplyDuringEarlyInlining(AI)) + return false; + break; + case InlineSelection::NoGlobalInit: + // Don't inline availability checks before serialization. + if (Callee->hasSemanticsAttrThatStartsWith("availability") || + (Callee->hasSemanticsAttrThatStartsWith("inline_late"))) + return false; + + // Handle nested semantics. Never inline a semantic call into + // another. First, the outer semantic call must be inlined. Then a full + // round of mid-level optimizations must rerun (all array + // optimizations). Finally the inner semantic call can be + // inlined. Afterward, all mid-level optimizations will be rerun yet + // again. This relies on no unannotated functions on the call stack between + // the outer and inner semantic functions. + if (Callee->hasSemanticsAttrs() && AI.getFunction()->hasSemanticsAttrs()) + return false; + + break; + case InlineSelection::Everything: { + ModuleDecl *SwiftModule = Callee->getModule().getSwiftModule(); + bool IsInStdlib = + (SwiftModule->isStdlibModule() || SwiftModule->isOnoneSupportModule()); + if (Callee->hasSemanticsAttrThatStartsWith("inline_late") && IsInStdlib) { + return false; + } + } + } + return true; } /// Checks if a generic callee and caller have compatible layout constraints. @@ -679,34 +721,18 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, if (!SILInliner::canInlineApplySite(AI)) return nullptr; - ModuleDecl *SwiftModule = Callee->getModule().getSwiftModule(); - bool IsInStdlib = (SwiftModule->isStdlibModule() || - SwiftModule->isOnoneSupportModule()); + // Don't inline functions marked with @_effects in the first inlining pass. + if (WhatToInline == InlineSelection::NoSemanticsAndGlobalInit && + Callee->hasEffectsKind()) + return nullptr; - // Don't inline functions that are marked with the @_semantics or @_effects - // attribute if the inliner is asked not to inline them. - if (Callee->hasSemanticsAttrs() || Callee->hasEffectsKind()) { - if (WhatToInline == InlineSelection::NoSemanticsAndGlobalInit) { - if (shouldSkipApplyDuringEarlyInlining(AI)) - return nullptr; - if (Callee->hasSemanticsAttr("inline_late")) - return nullptr; - } - // The "availability" semantics attribute is treated like global-init. - if (Callee->hasSemanticsAttrs() && - WhatToInline != InlineSelection::Everything && - (Callee->hasSemanticsAttrThatStartsWith("availability") || - (Callee->hasSemanticsAttrThatStartsWith("inline_late")))) { + // Check if an @_semantics attribute inhibits inlining. + if (Callee->hasSemanticsAttrs()) { + if (!shouldInlineSemanticCall(AI, WhatToInline)) return nullptr; - } - if (Callee->hasSemanticsAttrs() && - WhatToInline == InlineSelection::Everything) { - if (Callee->hasSemanticsAttrThatStartsWith("inline_late") && IsInStdlib) { - return nullptr; - } - } + } - } else if (Callee->isGlobalInit()) { + if (Callee->isGlobalInit()) { if (WhatToInline != InlineSelection::Everything) { return nullptr; } @@ -721,6 +747,9 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, if (Callee->getInlineStrategy() == NoInline) { return nullptr; } + if (!SILInlineNeverFuns.empty() && + Callee->getName().find(SILInlineNeverFuns, 0) != StringRef::npos) + return nullptr; if (!Callee->shouldOptimize()) { return nullptr; @@ -749,8 +778,7 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, } // A non-fragile function may not be inlined into a fragile function. - if (Caller->isSerialized() && - !Callee->hasValidLinkageForFragileInline()) { + if (Caller->isSerialized() && !Callee->hasValidLinkageForFragileInline()) { if (!Callee->hasValidLinkageForFragileRef()) { llvm::errs() << "caller: " << Caller->getName() << "\n"; llvm::errs() << "callee: " << Callee->getName() << "\n"; @@ -793,21 +821,22 @@ SILFunction *swift::getEligibleFunction(FullApplySite AI, return Callee; } + /// Returns true if the instruction \I has any interesting side effects which /// might prevent inlining a pure function. static bool hasInterestingSideEffect(SILInstruction *I) { switch (I->getKind()) { - // Those instructions turn into no-ops after inlining, redundante load - // elimination, constant folding and dead-object elimination. - case swift::SILInstructionKind::StrongRetainInst: - case swift::SILInstructionKind::StrongReleaseInst: - case swift::SILInstructionKind::RetainValueInst: - case swift::SILInstructionKind::ReleaseValueInst: - case swift::SILInstructionKind::StoreInst: - case swift::SILInstructionKind::DeallocRefInst: - return false; - default: - return I->getMemoryBehavior() != SILInstruction::MemoryBehavior::None; + // Those instructions turn into no-ops after inlining, redundante load + // elimination, constant folding and dead-object elimination. + case swift::SILInstructionKind::StrongRetainInst: + case swift::SILInstructionKind::StrongReleaseInst: + case swift::SILInstructionKind::RetainValueInst: + case swift::SILInstructionKind::ReleaseValueInst: + case swift::SILInstructionKind::StoreInst: + case swift::SILInstructionKind::DeallocRefInst: + return false; + default: + return I->getMemoryBehavior() != SILInstruction::MemoryBehavior::None; } } @@ -856,7 +885,6 @@ static bool isConstantArg(Operand *Arg) { return true; } - bool swift::isPureCall(FullApplySite AI, SideEffectAnalysis *SEA) { // If a call has only constant arguments and the call is pure, i.e. has // no side effects, then we should always inline it. diff --git a/stdlib/public/core/ContiguousArray.swift b/stdlib/public/core/ContiguousArray.swift index b44ff845d0bc1..3c3dc0090c268 100644 --- a/stdlib/public/core/ContiguousArray.swift +++ b/stdlib/public/core/ContiguousArray.swift @@ -657,18 +657,31 @@ extension ContiguousArray: RangeReplaceableCollection { public mutating func reserveCapacity(_ minimumCapacity: Int) { if _buffer.requestUniqueMutableBackingBuffer( minimumCapacity: minimumCapacity) == nil { - - let newBuffer = _ContiguousArrayBuffer( - _uninitializedCount: count, minimumCapacity: minimumCapacity) - - _buffer._copyContents( - subRange: _buffer.indices, - initializing: newBuffer.firstElementAddress) - _buffer = _Buffer( - _buffer: newBuffer, shiftedToStartIndex: _buffer.startIndex) + _copyToNewBuffer(minimumCapacity: minimumCapacity) + } _internalInvariant(capacity >= minimumCapacity) } + /// Copy the contents of the current buffer to a new buffer with + /// minimumCapacity. + /// + /// This is defined out-of-line to prevent inlining. The inliner + /// would otherwise agressively inline code that makes array + /// semantic calls (_getCount). Inlining this "slow" code both hurts + /// code size and prevents inlining important Sequence/Collection + /// methods that have grown too large. + @inline(never) + @inlinable // @specializable + internal mutating func _copyToNewBuffer(minimumCapacity: Int) { + let newBuffer = _ContiguousArrayBuffer( + _uninitializedCount: count, minimumCapacity: minimumCapacity) + + _buffer._copyContents( + subRange: _buffer.indices, + initializing: newBuffer.firstElementAddress) + _buffer = _Buffer( + _buffer: newBuffer, shiftedToStartIndex: _buffer.startIndex) + } /// Copy the contents of the current buffer to a new unique mutable buffer. /// The count of the new buffer is set to `oldCount`, the capacity of the diff --git a/test/SILOptimizer/array_contentof_opt.swift b/test/SILOptimizer/array_contentof_opt.swift index 7a0dfad4330ea..6125fcbb00589 100644 --- a/test/SILOptimizer/array_contentof_opt.swift +++ b/test/SILOptimizer/array_contentof_opt.swift @@ -1,59 +1,147 @@ -// RUN: %target-swift-frontend -O -sil-verify-all -emit-sil -enforce-exclusivity=unchecked %s | %FileCheck %s -// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib - -// This is an end-to-end test of the array(contentsOf) -> array(Element) optimization - -// CHECK-LABEL: sil @{{.*}}testInt -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NEXT: tuple -// CHECK-NEXT: return -public func testInt(_ a: inout [Int]) { - a += [1] -} - -// CHECK-LABEL: sil @{{.*}}testThreeInt -// CHECK-NOT: apply -// CHECK: [[FR:%[0-9]+]] = function_ref @$sSa15reserveCapacityyySiFSi_Tg5 -// CHECK-NEXT: apply [[FR]] -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSi_Tg5 -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: apply [[F]] -// CHECK-NEXT: tuple -// CHECK-NEXT: return -public func testThreeInts(_ a: inout [Int]) { - a += [1, 2, 3] -} - -// CHECK-LABEL: sil @{{.*}}testTooManyInts -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lFSi_SaySiGTg5Tf4gn_n -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NOT: apply -// CHECK: return -public func testTooManyInts(_ a: inout [Int]) { - a += [1, 2, 3, 4, 5, 6, 7] -} - -// CHECK-LABEL: sil @{{.*}}testString -// CHECK-NOT: apply -// CHECK: [[F:%[0-9]+]] = function_ref @$sSa6appendyyxnFSS_Tg5 -// CHECK-NOT: apply -// CHECK: apply [[F]] -// CHECK-NOT: apply -// CHECK: tuple -// CHECK-NEXT: return -public func testString(_ a: inout [String], s: String) { - a += [s] -} - -// This is not supported yet. Just check that we don't crash on this.` -public func dontPropagateContiguousArray(_ a: inout ContiguousArray) { - a += [4] -} +// RUN: %target-swift-frontend -O -emit-sil %s -Xllvm -debug-only=sil-inliner -Xllvm -debug-only=array-element-propagation -Xllvm -debug-only=cowarray-opts 2>&1 | %FileCheck %s + + // Test nested array semantic calls. + // + // The relevant sequence of passes is: + // + // - Early inlining does *not* inlinine Array.append(contentsOf:). + // + // - Early inlining inlines testInlineElts -> testInlineAppend. + // + // - ArrayElementPropagation of literal '[1, 2]' replaces + // append(contentsOf:) with two calls to Array.append and removes the + // temporary array literal. + // + // - Performance inlining does *not* initially inline any nested Array semantic + // calls, like "_makeUniqueAndReserveCapacityIfNotUnique". + // + // - Performance inlining inlines Array.append(contentsOf:) and resets + // the function pipeline. + // + // - COWArrayOpts hoists the call to _makeUniqueAndReserveCapacityIfNotUnique". + + // Here is the same sequence with interleaved CHECKs: + + // - Performance inlining does not initially any nested Array semantic + // CHECK-NOT: inline [{{.*}}]] $sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lF + // CHECK-NOT: inline [{{.*}}] $sSa034_makeUniqueAndReserveCapacityIfNotB0yyFSi_Tg5 + + // - Early inlining inlines testInlineElts -> testInlineAppend. + // CHECK-LABEL: Inline into caller: $s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF + // CHECK: inline [{{.*}}] $s22array_semantics_nested14testInlineElts_4eltsySaySiGz_ADtF + + // CHECK-NOT: inline [{{.*}}]] $sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lF + // CHECK-NOT: inline [{{.*}}] $sSa034_makeUniqueAndReserveCapacityIfNotB0yyFSi_Tg5 + + // - ArrayElementPropagation of literal '[1, 2]' replaces + // append(contentsOf:) with two calls to Array.append and removes the + // temporary array literal. + // CHECK: Array append contentsOf calls replaced in $s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF (1) + + // - Performance inlining does *not* initially inline any nested Array semantic + // calls, like "_makeUniqueAndReserveCapacityIfNotUnique". + + // CHECK-NOT: inline [{{.*}}]] $sSa6append10contentsOfyqd__n_t7ElementQyd__RszSTRd__lF + // CHECK-NOT: inline [{{.*}}] $sSa034_makeUniqueAndReserveCapacityIfNotB0yyFSi_Tg5 + + // - Performance inlining inlines Array.append(Element) and resets + // the function pipeline. + + // CHECK: Inline into caller: $s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF + // CHECK: inline [{{.*}}] $sSa6appendyyxnFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa6appendyyxnFSi_Tg5 + + // CHECK: COW Array Opts in Func $s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF + // CHECK: Array Opts in Loop Loop at depth 1 containing: + // CHECK: Checking mutable array: %{{.*}} = alloc_stack $Array, var, name "result" + // CHECK: Hoisting make_mutable: + // CHECK: Removing make_mutable call: + + // CHECK-NOT: Inline into caller + + // - The next round of inlinling in the same function pipeline. + // CHECK: Inline into caller: $s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF + // CHECK: inline [{{.*}}] $sSa034_makeUniqueAndReserveCapacityIfNotB0yyFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa9_getCountSiyFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa12_getCapacitySiyFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa15reserveCapacityyySiFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa9_getCountSiyFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa36_reserveCapacityAssumingUniqueBuffer8oldCountySi_tFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa37_appendElementAssumeUniqueAndCapacity_03newB0ySi_xntFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa9_getCountSiyFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa36_reserveCapacityAssumingUniqueBuffer8oldCountySi_tFSi_Tg5 + // CHECK: inline [{{.*}}] $sSa37_appendElementAssumeUniqueAndCapacity_03newB0ySi_xntFSi_Tg5 + + // This helper ensures that at least one round of inlining is needed + // *before* inlining Array.append. + func testInlineElts(_ a: inout [Int], elts: [Int]) -> () { + a.append(contentsOf: elts) + } + + // CHECK-LABEL: sil @$s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF : $@convention(thin) (Int) -> @owned Array { + // CHECK: bb0(%0 : $Int): + // Initialize the array... + // CHECK: [[RESULTARRAY:%[0-9]+]] = alloc_stack $Array, var, name "result" + // CHECK: store %{{.*}} to [[RESULTARRAY]] : $*Array + + // Perform the uniqueness check... (FIXME: should be able to optimize this away since it hasn't been escaped) + // CHECK: [[BUFADR:%[0-9]+]] = struct_element_addr [[RESULTARRAY]] : $*Array, #Array._buffer + // CHECK: [[STORADR:%[0-9]+]] = struct_element_addr [[BUFADR]] : $*_ArrayBuffer, #_ArrayBuffer._storage + // CHECK: [[BRIDGE:%[0-9]+]] = struct_element_addr [[STORADR]] : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue + // CHECK: [[NATIVE:%[0-9]+]] = unchecked_addr_cast [[BRIDGE]] : $*Builtin.BridgeObject to $*Builtin.NativeObject + // CHECK: [[UNIQ:%[0-9]+]] = is_unique [[NATIVE]] : $*Builtin.NativeObject + // CHECK: [[EXPECT:%[0-9]+]] = builtin "int_expect_Int1"([[UNIQ]] : $Builtin.Int1 + // CHECK: cond_br [[EXPECT]] + + // Enter the loop... + // CHECK: [[LOOPBB:bb[0-9]+]](%{{.*}} : $Builtin.Int64): // Preds: [[TAILBB:bb[0-9]+]] bb + + // CHECK-NOT: apply + + // Reserve capacity... FIXME: There is still an is_uniq inside the + // loop because of reserveCapacity. Either reserveCapacity should be + // split into a hoistable uniqueness check, or we should be able to + // prove that the uniqueness check is dominated with no escape. + // CHECK: %59 = load [[BRIDGE]] : $*Builtin.BridgeObject + // CHECK: %63 = struct_element_addr %62 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count + // CHECK: %65 = load %64 : $*Builtin.Int64 + // CHECK: %67 = struct_element_addr %62 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage._capacityAndFlags + // CHECK: %69 = load %68 : $*Builtin.Int64 + // CHECK: cond_br %76, bb8, bb9 + // CHECK-NOT: apply + // CHECK: bb + // CHECK: is_unique %26 : $*Builtin.NativeObject + // CHECK: cond_br %90, bb14, bb13 + + // CHECK-NOT: apply + + // CHECK: [[SZF:%[0-9]+]] = function_ref @_swift_stdlib_malloc_size : $@convention(c) (UnsafeRawPointer) -> Int + // CHECK: apply [[SZF]](%{{.*}}) : $@convention(c) (UnsafeRawPointer) -> Int + + // CHECK-NOT: apply + + // CHECK: builtin "copyArray"(%153 : $@thick Int.Type, %150 : $Builtin.RawPointer, %152 : $Builtin.RawPointer, %154 : $Builtin.Word) : $() + // CHECK: store %{{.*}} to [[RESULTARRAY]] : $*Array + + // CHECK-NOT: apply + + // CHECK: %198 = function_ref @$sSa16_copyToNewBuffer8oldCountySi_tFSi_Tg5 : $@convention(method) (Int, @inout Array) -> () + // CHECK: %199 = apply %198(%197, %2) : $@convention(method) (Int, @inout Array) -> () + + // CHECK-NOT: apply + + // CHECK: %233 = function_ref @$sSa16_copyToNewBuffer8oldCountySi_tFSi_Tg5 : $@convention(method) (Int, @inout Array) -> () + // CHECK: %234 = apply %233(%232, %2) : $@convention(method) (Int, @inout Array) -> () + + // CHECK-NOT: apply + + // CHECK: br [[LOOPBB]](%237 : $Builtin.Int64) + // CHECK-LABEL: } // end sil function '$s22array_semantics_nested16testInlineAppend5countSaySiGSi_tF' + + public func testInlineAppend(count: Int) -> [Int] { + var result = Array() + for _ in 0.. BufferIDOpt; if (Loc.isValid()) { - BufferIDOpt = SM.findBufferContainingLoc(Loc); + BufferIDOpt = SM.findBufferContainingLoc(Loc); } if (BufferIDOpt && !isInputBufferID(*BufferIDOpt)) { if (Info.ID == diag::error_from_clang.ID || Info.ID == diag::warning_from_clang.ID || - Info.ID == diag::note_from_clang.ID || - !IsNote) { + Info.ID == diag::note_from_clang.ID || !IsNote) { // Handle it as other diagnostics. } else { // FIXME: This is a note pointing to a synthesized declaration buffer for @@ -178,15 +177,15 @@ void EditorDiagConsumer::handleDiagnostic( } switch (Kind) { - case DiagnosticKind::Error: - SKInfo.Severity = DiagnosticSeverityKind::Error; - break; - case DiagnosticKind::Warning: - SKInfo.Severity = DiagnosticSeverityKind::Warning; - break; - case DiagnosticKind::Note: - case DiagnosticKind::Remark: - llvm_unreachable("already covered"); + case DiagnosticKind::Error: + SKInfo.Severity = DiagnosticSeverityKind::Error; + break; + case DiagnosticKind::Warning: + SKInfo.Severity = DiagnosticSeverityKind::Warning; + break; + case DiagnosticKind::Note: + case DiagnosticKind::Remark: + llvm_unreachable("already covered"); } if (!BufferIDOpt) { @@ -206,10 +205,11 @@ void EditorDiagConsumer::handleDiagnostic( } // Keep the diagnostics array in source order. - auto Pos = std::lower_bound(Diagnostics.begin(), Diagnostics.end(), SKInfo.Offset, - [&](const DiagnosticEntryInfo &LHS, unsigned Offset) -> bool { - return LHS.Offset < Offset; - }); + auto Pos = std::lower_bound( + Diagnostics.begin(), Diagnostics.end(), SKInfo.Offset, + [&](const DiagnosticEntryInfo &LHS, unsigned Offset) -> bool { + return LHS.Offset < Offset; + }); LastDiagBufferID = BufferID; LastDiagIndex = Pos - Diagnostics.begin(); Diagnostics.insert(Pos, std::move(SKInfo)); @@ -219,11 +219,11 @@ SwiftEditorDocumentRef SwiftEditorDocumentFileMap::getByUnresolvedName(StringRef FilePath) { SwiftEditorDocumentRef EditorDoc; - Queue.dispatchSync([&]{ + Queue.dispatchSync([&] { auto It = Docs.find(FilePath); if (It != Docs.end()) EditorDoc = It->second.DocRef; - }); + }); return EditorDoc; } @@ -233,7 +233,7 @@ SwiftEditorDocumentFileMap::findByPath(StringRef FilePath) { SwiftEditorDocumentRef EditorDoc; std::string ResolvedPath = SwiftLangSupport::resolvePathSymlinks(FilePath); - Queue.dispatchSync([&]{ + Queue.dispatchSync([&] { for (auto &Entry : Docs) { if (Entry.getKey() == FilePath || Entry.getValue().ResolvedPath == ResolvedPath) { @@ -253,7 +253,7 @@ bool SwiftEditorDocumentFileMap::getOrUpdate( bool found = false; std::string ResolvedPath = SwiftLangSupport::resolvePathSymlinks(FilePath); - Queue.dispatchBarrierSync([&]{ + Queue.dispatchBarrierSync([&] { DocInfo &Doc = Docs[FilePath]; if (!Doc.DocRef) { Doc.DocRef = EditorDoc; @@ -269,7 +269,7 @@ bool SwiftEditorDocumentFileMap::getOrUpdate( SwiftEditorDocumentRef SwiftEditorDocumentFileMap::remove(StringRef FilePath) { SwiftEditorDocumentRef Removed; - Queue.dispatchBarrierSync([&]{ + Queue.dispatchBarrierSync([&] { auto I = Docs.find(FilePath); if (I != Docs.end()) { Removed = I->second.DocRef; @@ -283,10 +283,11 @@ namespace { /// Merges two overlapping ranges and splits the first range into two /// ranges before and after the overlapping range. -void mergeSplitRanges(unsigned Off1, unsigned Len1, unsigned Off2, unsigned Len2, +void mergeSplitRanges(unsigned Off1, unsigned Len1, unsigned Off2, + unsigned Len2, std::function applier) { + unsigned AfterOff, unsigned AfterLen)> + applier) { unsigned End1 = Off1 + Len1; unsigned End2 = Off2 + Len2; if (End1 > Off2) { @@ -296,8 +297,7 @@ void mergeSplitRanges(unsigned Off1, unsigned Len1, unsigned Off2, unsigned Len2 unsigned AfterOff = End2; unsigned AfterLen = End1 > End2 ? End1 - End2 : 0; applier(BeforeOff, BeforeLen, AfterOff, AfterLen); - } - else { + } else { // Not overlapping. applier(Off1, Len1, 0, 0); } @@ -305,15 +305,15 @@ void mergeSplitRanges(unsigned Off1, unsigned Len1, unsigned Off2, unsigned Len2 struct SwiftSyntaxToken { unsigned Offset; - unsigned Length:24; - SyntaxNodeKind Kind:8; + unsigned Length : 24; + SyntaxNodeKind Kind : 8; static SwiftSyntaxToken createInvalid() { return {0, 0, SyntaxNodeKind::AttributeBuiltin}; } SwiftSyntaxToken(unsigned Offset, unsigned Length, SyntaxNodeKind Kind) - : Offset(Offset), Length(Length), Kind(Kind) {} + : Offset(Offset), Length(Length), Kind(Kind) {} unsigned endOffset() const { return Offset + Length; } @@ -321,12 +321,12 @@ struct SwiftSyntaxToken { bool operator==(const SwiftSyntaxToken &Other) const { return Offset == Other.Offset && Length == Other.Length && - Kind == Other.Kind; + Kind == Other.Kind; } bool operator!=(const SwiftSyntaxToken &Other) const { return Offset != Other.Offset || Length != Other.Length || - Kind != Other.Kind; + Kind != Other.Kind; } }; @@ -334,11 +334,11 @@ struct SwiftEditorCharRange { unsigned Offset; unsigned EndOffset; - SwiftEditorCharRange(unsigned Offset, unsigned EndOffset) : - Offset(Offset), EndOffset(EndOffset) {} + SwiftEditorCharRange(unsigned Offset, unsigned EndOffset) + : Offset(Offset), EndOffset(EndOffset) {} - SwiftEditorCharRange(SwiftSyntaxToken Token) : - Offset(Token.Offset), EndOffset(Token.endOffset()) {} + SwiftEditorCharRange(SwiftSyntaxToken Token) + : Offset(Token.Offset), EndOffset(Token.endOffset()) {} size_t length() const { return EndOffset - Offset; } bool isEmpty() const { return Offset == EndOffset; } @@ -358,17 +358,17 @@ struct SwiftEditorCharRange { /// Finds and represents the first mismatching tokens in two syntax maps, /// ignoring invalidated tokens. -template -struct TokenMismatch { +template struct TokenMismatch { /// The begin and end iterators of the previous syntax map Iter PrevTok, PrevEnd; /// The begin and end iterators of the current syntax map Iter CurrTok, CurrEnd; - TokenMismatch(Iter CurrTok, Iter CurrEnd, Iter PrevTok, Iter PrevEnd) : - PrevTok(PrevTok), PrevEnd(PrevEnd), CurrTok(CurrTok), CurrEnd(CurrEnd) { + TokenMismatch(Iter CurrTok, Iter CurrEnd, Iter PrevTok, Iter PrevEnd) + : PrevTok(PrevTok), PrevEnd(PrevEnd), CurrTok(CurrTok), CurrEnd(CurrEnd) { skipInvalid(); - while(advance()); + while (advance()) + ; } /// Returns true if a mismatch was found @@ -462,8 +462,8 @@ struct SwiftSyntaxMap { unsigned ReplacedStart = Offset; unsigned ReplacedEnd = Offset + Len; bool TokenIntersected = false; - SwiftEditorCharRange Affected = { /*Offset=*/ReplacedStart, - /*EndOffset=*/ReplacedEnd}; + SwiftEditorCharRange Affected = {/*Offset=*/ReplacedStart, + /*EndOffset=*/ReplacedEnd}; // Adjust the tokens auto Token = Tokens.begin(); while (Token != Tokens.end() && Token->endOffset() <= ReplacedStart) { @@ -505,7 +505,7 @@ struct SwiftSyntaxMap { /// Passes each token in this SwiftSyntaxMap to the given \p Consumer void forEach(EditorConsumer &Consumer) { - for (auto &Token: Tokens) { + for (auto &Token : Tokens) { auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Token.Kind); Consumer.handleSyntaxMap(Token.Offset, Token.Length, Kind); } @@ -524,21 +524,21 @@ struct SwiftSyntaxMap { typedef std::vector::const_reverse_iterator ReverseIt; // Find the first pair of tokens that don't match - TokenMismatch - Forward(Tokens.begin(), Tokens.end(), Prev.Tokens.begin(), Prev.Tokens.end()); + TokenMismatch Forward(Tokens.begin(), Tokens.end(), + Prev.Tokens.begin(), Prev.Tokens.end()); // Exit early if there was no mismatch if (!Forward.foundMismatch()) return Affected && !Affected->isEmpty(); // Find the last pair of tokens that don't match - TokenMismatch - Backward(Tokens.rbegin(), Tokens.rend(), Prev.Tokens.rbegin(), Prev.Tokens.rend()); + TokenMismatch Backward(Tokens.rbegin(), Tokens.rend(), + Prev.Tokens.rbegin(), Prev.Tokens.rend()); assert(Backward.foundMismatch()); // Set or extend the affected range to include the mismatched range - SwiftEditorCharRange - MismatchRange = {Forward.mismatchStart(),Backward.mismatchEnd()}; + SwiftEditorCharRange MismatchRange = {Forward.mismatchStart(), + Backward.mismatchEnd()}; if (!Affected) { Affected = MismatchRange; } else { @@ -548,9 +548,10 @@ struct SwiftSyntaxMap { // Report all tokens in the affected range to the EditorConsumer auto From = Forward.CurrTok; auto To = Backward.CurrTok; - while (From != Tokens.begin() && (From-1)->Offset >= Affected->Offset) + while (From != Tokens.begin() && (From - 1)->Offset >= Affected->Offset) --From; - while (To != Tokens.rbegin() && (To-1)->endOffset() <= Affected->EndOffset) + while (To != Tokens.rbegin() && + (To - 1)->endOffset() <= Affected->EndOffset) --To; for (; From < To.base(); ++From) { auto Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(From->Kind); @@ -566,7 +567,7 @@ struct EditorConsumerSyntaxMapEntry { unsigned Length; UIdent Kind; EditorConsumerSyntaxMapEntry(unsigned Offset, unsigned Length, UIdent Kind) - :Offset(Offset), Length(Length), Kind(Kind) { } + : Offset(Offset), Length(Length), Kind(Kind) {} }; struct SwiftSemanticToken { @@ -578,11 +579,10 @@ struct SwiftSemanticToken { unsigned IsRef : 1; unsigned IsSystem : 1; - SwiftSemanticToken(CodeCompletionDeclKind Kind, - unsigned ByteOffset, unsigned Length, - bool IsRef, bool IsSystem) - : ByteOffset(ByteOffset), Length(Length), Kind(Kind), - IsRef(IsRef), IsSystem(IsSystem) { } + SwiftSemanticToken(CodeCompletionDeclKind Kind, unsigned ByteOffset, + unsigned Length, bool IsRef, bool IsSystem) + : ByteOffset(ByteOffset), Length(Length), Kind(Kind), IsRef(IsRef), + IsSystem(IsSystem) {} bool getIsRef() const { return static_cast(IsRef); } @@ -594,8 +594,8 @@ struct SwiftSemanticToken { }; static_assert(sizeof(SwiftSemanticToken) == 8, "Too big"); -class SwiftDocumentSemanticInfo : - public ThreadSafeRefCountedBase { +class SwiftDocumentSemanticInfo + : public ThreadSafeRefCountedBase { const std::string Filename; std::weak_ptr ASTMgr; @@ -621,9 +621,7 @@ class SwiftDocumentSemanticInfo : : Filename(Filename), ASTMgr(ASTMgr), NotificationCtr(NotificationCtr), fileSystem(fileSystem) {} - SwiftInvocationRef getInvocation() const { - return InvokRef; - } + SwiftInvocationRef getInvocation() const { return InvokRef; } llvm::IntrusiveRefCntPtr getFileSystem() const { return fileSystem; @@ -633,8 +631,7 @@ class SwiftDocumentSemanticInfo : void setCompilerArgs(ArrayRef Args) { if (auto ASTMgr = this->ASTMgr.lock()) { - InvokRef = - ASTMgr->getInvocation(Args, Filename, CompilerArgsError); + InvokRef = ASTMgr->getInvocation(Args, Filename, CompilerArgsError); } } @@ -658,12 +655,12 @@ class SwiftDocumentSemanticInfo : } private: - std::vector takeSemanticTokens( - ImmutableTextSnapshotRef NewSnapshot); + std::vector + takeSemanticTokens(ImmutableTextSnapshotRef NewSnapshot); - Optional> getSemanticDiagnostics( - ImmutableTextSnapshotRef NewSnapshot, - ArrayRef ParserDiags); + Optional> + getSemanticDiagnostics(ImmutableTextSnapshotRef NewSnapshot, + ArrayRef ParserDiags); }; class SwiftDocumentSyntaxInfo { @@ -681,13 +678,12 @@ class SwiftDocumentSyntaxInfo { public: SwiftDocumentSyntaxInfo(const CompilerInvocation &CompInv, ImmutableTextSnapshotRef Snapshot, - std::vector &Args, - StringRef FilePath) - : Args(Args), PrimaryFile(FilePath) { + std::vector &Args, StringRef FilePath) + : Args(Args), PrimaryFile(FilePath) { std::unique_ptr BufCopy = - llvm::MemoryBuffer::getMemBufferCopy( - Snapshot->getBuffer()->getText(), FilePath); + llvm::MemoryBuffer::getMemBufferCopy(Snapshot->getBuffer()->getText(), + FilePath); BufferID = SM.addNewSourceBuffer(std::move(BufCopy)); DiagConsumer.setInputBufferIDs(BufferID); @@ -698,13 +694,10 @@ class SwiftDocumentSyntaxInfo { SM, BufferID, CompInv.getMainFileSyntaxParsingCache(), syntaxArena); } - Parser.reset( - new ParserUnit(SM, SourceFileKind::Main, BufferID, - CompInv.getLangOptions(), - CompInv.getModuleName(), - SynTreeCreator, - CompInv.getMainFileSyntaxParsingCache()) - ); + Parser.reset(new ParserUnit(SM, SourceFileKind::Main, BufferID, + CompInv.getLangOptions(), + CompInv.getModuleName(), SynTreeCreator, + CompInv.getMainFileSyntaxParsingCache())); Parser->getDiagnosticEngine().addConsumer(DiagConsumer); @@ -722,21 +715,13 @@ class SwiftDocumentSyntaxInfo { SynTreeCreator->acceptSyntaxRoot(root, Parser->getSourceFile()); } - SourceFile &getSourceFile() { - return Parser->getSourceFile(); - } + SourceFile &getSourceFile() { return Parser->getSourceFile(); } - unsigned getBufferID() { - return BufferID; - } + unsigned getBufferID() { return BufferID; } - const LangOptions &getLangOptions() { - return Parser->getLangOptions(); - } + const LangOptions &getLangOptions() { return Parser->getLangOptions(); } - SourceManager &getSourceManager() { - return SM; - } + SourceManager &getSourceManager() { return SM; } bool hasUpToDateAST() { return HasUpToDateAST; } @@ -764,8 +749,7 @@ void SwiftDocumentSemanticInfo::readSemanticInfo( Diags = getSemanticDiagnostics(NewSnapshot, ParserDiags); } -std::vector -SwiftDocumentSemanticInfo::takeSemanticTokens( +std::vector SwiftDocumentSemanticInfo::takeSemanticTokens( ImmutableTextSnapshotRef NewSnapshot) { llvm::sys::ScopedLock L(Mtx); @@ -774,38 +758,40 @@ SwiftDocumentSemanticInfo::takeSemanticTokens( return {}; // Adjust the position of the tokens. - TokSnapshot->foreachReplaceUntil(NewSnapshot, - [&](ReplaceImmutableTextUpdateRef Upd) -> bool { - if (SemaToks.empty()) - return false; - - auto ReplaceBegin = std::lower_bound(SemaToks.begin(), SemaToks.end(), - Upd->getByteOffset(), - [&](const SwiftSemanticToken &Tok, unsigned StartOffset) -> bool { - return Tok.ByteOffset+Tok.Length < StartOffset; - }); + TokSnapshot->foreachReplaceUntil( + NewSnapshot, [&](ReplaceImmutableTextUpdateRef Upd) -> bool { + if (SemaToks.empty()) + return false; - std::vector::iterator ReplaceEnd; - if (Upd->getLength() == 0) { - ReplaceEnd = ReplaceBegin; - } else { - ReplaceEnd = std::upper_bound(ReplaceBegin, SemaToks.end(), - Upd->getByteOffset() + Upd->getLength(), - [&](unsigned EndOffset, const SwiftSemanticToken &Tok) -> bool { - return EndOffset < Tok.ByteOffset; + auto ReplaceBegin = std::lower_bound( + SemaToks.begin(), SemaToks.end(), Upd->getByteOffset(), + [&](const SwiftSemanticToken &Tok, unsigned StartOffset) -> bool { + return Tok.ByteOffset + Tok.Length < StartOffset; }); - } - unsigned InsertLen = Upd->getText().size(); - int Delta = InsertLen - Upd->getLength(); - if (Delta != 0) { - for (std::vector::iterator - I = ReplaceEnd, E = SemaToks.end(); I != E; ++I) - I->ByteOffset += Delta; - } - SemaToks.erase(ReplaceBegin, ReplaceEnd); - return true; - }); + std::vector::iterator ReplaceEnd; + if (Upd->getLength() == 0) { + ReplaceEnd = ReplaceBegin; + } else { + ReplaceEnd = std::upper_bound( + ReplaceBegin, SemaToks.end(), + Upd->getByteOffset() + Upd->getLength(), + [&](unsigned EndOffset, const SwiftSemanticToken &Tok) -> bool { + return EndOffset < Tok.ByteOffset; + }); + } + + unsigned InsertLen = Upd->getText().size(); + int Delta = InsertLen - Upd->getLength(); + if (Delta != 0) { + for (std::vector::iterator I = ReplaceEnd, + E = SemaToks.end(); + I != E; ++I) + I->ByteOffset += Delta; + } + SemaToks.erase(ReplaceBegin, ReplaceEnd); + return true; + }); return std::move(SemaToks); } @@ -853,7 +839,7 @@ SwiftDocumentSemanticInfo::getSemanticDiagnostics( orderDiagnosticEntryInfos); std::vector finalDiags; - finalDiags.reserve(sortedParserDiags.size()+curSemaDiags.size()); + finalDiags.reserve(sortedParserDiags.size() + curSemaDiags.size()); // Add sema diagnostics unless it is an existing parser diagnostic. // Note that we want to merge and eliminate diagnostics from the 'sema' set @@ -863,16 +849,16 @@ SwiftDocumentSemanticInfo::getSemanticDiagnostics( // them, even if there's some duplicate one. This is why we don't just do a // simple append/sort/keep-uniques step. for (const auto &curDE : curSemaDiags) { - bool existsAsParserDiag = std::binary_search(sortedParserDiags.begin(), - sortedParserDiags.end(), - curDE, orderDiagnosticEntryInfos); + bool existsAsParserDiag = + std::binary_search(sortedParserDiags.begin(), sortedParserDiags.end(), + curDE, orderDiagnosticEntryInfos); if (!existsAsParserDiag) { finalDiags.push_back(curDE); } } - finalDiags.insert(finalDiags.end(), - sortedParserDiags.begin(), sortedParserDiags.end()); + finalDiags.insert(finalDiags.end(), sortedParserDiags.begin(), + sortedParserDiags.end()); std::stable_sort(finalDiags.begin(), finalDiags.end(), orderDiagnosticEntryInfos); @@ -881,8 +867,7 @@ SwiftDocumentSemanticInfo::getSemanticDiagnostics( void SwiftDocumentSemanticInfo::updateSemanticInfo( std::vector Toks, - std::vector Diags, - ImmutableTextSnapshotRef Snapshot, + std::vector Diags, ImmutableTextSnapshotRef Snapshot, uint64_t ASTGeneration) { { @@ -904,12 +889,12 @@ namespace { class SemanticAnnotator : public SourceEntityWalker { SourceManager &SM; unsigned BufferID; -public: +public: std::vector SemaToks; SemanticAnnotator(SourceManager &SM, unsigned BufferID) - : SM(SM), BufferID(BufferID) {} + : SM(SM), BufferID(BufferID) {} bool visitDeclReference(ValueDecl *D, CharSourceRange Range, TypeDecl *CtorTyRef, ExtensionDecl *ExtTyRef, Type T, @@ -960,8 +945,8 @@ class AnnotAndDiagASTConsumer : public SwiftASTConsumer { AnnotAndDiagASTConsumer(EditableTextBufferRef EditableBuffer, RefPtr SemaInfoRef) - : EditableBuffer(std::move(EditableBuffer)), - SemaInfoRef(std::move(SemaInfoRef)) { } + : EditableBuffer(std::move(EditableBuffer)), + SemaInfoRef(std::move(SemaInfoRef)) {} void failed(StringRef Error) override { LOG_WARN_FUNC("sema annotations failed: " << Error); @@ -995,7 +980,8 @@ class AnnotAndDiagASTConsumer : public SwiftASTConsumer { if (Generation == SemaInfoRef->getASTGeneration()) { // Save time if we already know we processed this AST version. - if (DocSnapshot->getStamp() != EditableBuffer->getSnapshot()->getStamp()){ + if (DocSnapshot->getStamp() != + EditableBuffer->getSnapshot()->getStamp()) { // Handle edits that occurred after we processed the AST. SemaInfoRef->processLatestSnapshotAsync(EditableBuffer); } @@ -1006,17 +992,17 @@ class AnnotAndDiagASTConsumer : public SwiftASTConsumer { LOG_WARN_FUNC("Primary SourceFile is expected to have a BufferID"); return; } - unsigned BufferID = AstUnit->getPrimarySourceFile().getBufferID().getValue(); + unsigned BufferID = + AstUnit->getPrimarySourceFile().getBufferID().getValue(); SemanticAnnotator Annotator(CompIns.getSourceMgr(), BufferID); Annotator.walk(AstUnit->getPrimarySourceFile()); SemaToks = std::move(Annotator.SemaToks); - SemaInfoRef-> - updateSemanticInfo(std::move(SemaToks), - std::move(Consumer.getDiagnosticsForBuffer(BufferID)), - DocSnapshot, - Generation); + SemaInfoRef->updateSemanticInfo( + std::move(SemaToks), + std::move(Consumer.getDiagnosticsForBuffer(BufferID)), DocSnapshot, + Generation); if (DocSnapshot->getStamp() != EditableBuffer->getSnapshot()->getStamp()) { // Handle edits that occurred after we processed the AST. @@ -1031,1416 +1017,1406 @@ void SwiftDocumentSemanticInfo::processLatestSnapshotAsync( EditableTextBufferRef EditableBuffer) { SwiftInvocationRef Invok = InvokRef; - if (!Invok) - return; + if (Invok) { - RefPtr SemaInfoRef = this; - auto Consumer = std::make_shared(EditableBuffer, - SemaInfoRef); + RefPtr SemaInfoRef = this; + auto Consumer = + std::make_shared(EditableBuffer, SemaInfoRef); - // Semantic annotation queries for a particular document should cancel - // previously queued queries for the same document. Each document has a - // SwiftDocumentSemanticInfo pointer so use that for the token. - const void *OncePerASTToken = SemaInfoRef.get(); - if (auto ASTMgr = this->ASTMgr.lock()) { - ASTMgr->processASTAsync(Invok, std::move(Consumer), OncePerASTToken, - fileSystem); + // Semantic annotation queries for a particular document should cancel + // previously queued queries for the same document. Each document has a + // SwiftDocumentSemanticInfo pointer so use that for the token. + const void *OncePerASTToken = SemaInfoRef.get(); + if (auto ASTMgr = this->ASTMgr.lock()) { + ASTMgr->processASTAsync(Invok, std::move(Consumer), OncePerASTToken, + fileSystem); + } } -} -struct SwiftEditorDocument::Implementation { - std::weak_ptr ASTMgr; - std::shared_ptr NotificationCtr; + struct SwiftEditorDocument::Implementation { + std::weak_ptr ASTMgr; + std::shared_ptr NotificationCtr; - const std::string FilePath; - EditableTextBufferRef EditableBuffer; + const std::string FilePath; + EditableTextBufferRef EditableBuffer; - /// The list of syntax highlighted token offsets and ranges in the document - SwiftSyntaxMap SyntaxMap; - /// The minimal range of syntax highlighted tokens affected by the last edit - llvm::Optional AffectedRange; - /// Whether the last operation was an edit rather than a document open - bool Edited; - /// The syntax tree of the document - llvm::Optional SyntaxTree; - - std::vector ParserDiagnostics; - RefPtr SemanticInfo; - CodeFormatOptions FormatOptions; - - std::shared_ptr SyntaxInfo; - - std::shared_ptr getSyntaxInfo() { - llvm::sys::ScopedLock L(AccessMtx); - return SyntaxInfo; - } - - llvm::sys::Mutex AccessMtx; - - Implementation(StringRef FilePath, SwiftLangSupport &LangSupport, - CodeFormatOptions options, - llvm::IntrusiveRefCntPtr fileSystem) - : ASTMgr(LangSupport.getASTManager()), - NotificationCtr(LangSupport.getNotificationCenter()), - FilePath(FilePath), FormatOptions(options) { - assert(fileSystem); - // This instance of semantic info is used if a document is opened with - // `key.syntactic_only: 1`, but subsequently a semantic request such as - // cursor_info is made. - SemanticInfo = new SwiftDocumentSemanticInfo( - FilePath, ASTMgr, NotificationCtr, fileSystem); - } -}; + /// The list of syntax highlighted token offsets and ranges in the document + SwiftSyntaxMap SyntaxMap; + /// The minimal range of syntax highlighted tokens affected by the last edit + llvm::Optional AffectedRange; + /// Whether the last operation was an edit rather than a document open + bool Edited; + /// The syntax tree of the document + llvm::Optional SyntaxTree; -namespace { - -static UIdent getAccessLevelUID(AccessLevel Access) { - static UIdent AccessOpen("source.lang.swift.accessibility.open"); - static UIdent AccessPublic("source.lang.swift.accessibility.public"); - static UIdent AccessInternal("source.lang.swift.accessibility.internal"); - static UIdent AccessFilePrivate("source.lang.swift.accessibility.fileprivate"); - static UIdent AccessPrivate("source.lang.swift.accessibility.private"); - - switch (Access) { - case AccessLevel::Private: - return AccessPrivate; - case AccessLevel::FilePrivate: - return AccessFilePrivate; - case AccessLevel::Internal: - return AccessInternal; - case AccessLevel::Public: - return AccessPublic; - case AccessLevel::Open: - return AccessOpen; - } - - llvm_unreachable("Unhandled access level in switch."); -} + std::vector ParserDiagnostics; + RefPtr SemanticInfo; + CodeFormatOptions FormatOptions; -class SwiftDocumentStructureWalker: public ide::SyntaxModelWalker { - SourceManager &SrcManager; - EditorConsumer &Consumer; - unsigned BufferID; + std::shared_ptr SyntaxInfo; -public: - SwiftDocumentStructureWalker(SourceManager &SrcManager, - unsigned BufferID, - EditorConsumer &Consumer) - : SrcManager(SrcManager), Consumer(Consumer), BufferID(BufferID) { } - - bool walkToSubStructurePre(SyntaxStructureNode Node) override { - unsigned StartOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), - BufferID); - unsigned EndOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), - BufferID); - unsigned NameStart; - unsigned NameEnd; - if (Node.NameRange.isValid()) { - NameStart = SrcManager.getLocOffsetInBuffer(Node.NameRange.getStart(), - BufferID); - NameEnd = SrcManager.getLocOffsetInBuffer(Node.NameRange.getEnd(), - BufferID); - } - else { - NameStart = NameEnd = 0; - } - - unsigned BodyOffset; - unsigned BodyEnd; - if (Node.BodyRange.isValid()) { - BodyOffset = SrcManager.getLocOffsetInBuffer(Node.BodyRange.getStart(), - BufferID); - BodyEnd = SrcManager.getLocOffsetInBuffer(Node.BodyRange.getEnd(), - BufferID); - } - else { - BodyOffset = BodyEnd = 0; - } - - unsigned DocOffset = 0; - unsigned DocEnd = 0; - if (Node.DocRange.isValid()) { - DocOffset = SrcManager.getLocOffsetInBuffer(Node.DocRange.getStart(), - BufferID); - DocEnd = SrcManager.getLocOffsetInBuffer(Node.DocRange.getEnd(), - BufferID); - } - - UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureKind(Node.Kind); - UIdent AccessLevel; - UIdent SetterAccessLevel; - if (Node.Kind != SyntaxStructureKind::Parameter && - Node.Kind != SyntaxStructureKind::LocalVariable && - Node.Kind != SyntaxStructureKind::GenericTypeParam) { - if (auto *VD = dyn_cast_or_null(Node.Dcl)) { - AccessLevel = getAccessLevelUID(VD->getFormalAccess()); - } else if (auto *ED = dyn_cast_or_null(Node.Dcl)) { - auto StrictAccess = ED->getDefaultAccessLevel(); - AccessLevel = getAccessLevelUID(StrictAccess); - } - if (auto *ASD = dyn_cast_or_null(Node.Dcl)) { - if (ASD->isSettable(/*UseDC=*/nullptr)) { - swift::AccessLevel SetAccess = ASD->getSetterFormalAccess(); - SetterAccessLevel = getAccessLevelUID(SetAccess); - } - } + std::shared_ptr getSyntaxInfo() { + llvm::sys::ScopedLock L(AccessMtx); + return SyntaxInfo; } - SmallVector InheritedNames; - if (!Node.InheritedTypeRanges.empty()) { - for (auto &TR : Node.InheritedTypeRanges) { - InheritedNames.push_back(SrcManager.extractText(TR)); - } - } + llvm::sys::Mutex AccessMtx; - StringRef TypeName; - if (Node.TypeRange.isValid()) { - TypeName = SrcManager.extractText(Node.TypeRange); + Implementation(StringRef FilePath, SwiftLangSupport &LangSupport, + CodeFormatOptions options, + llvm::IntrusiveRefCntPtr fileSystem) + : ASTMgr(LangSupport.getASTManager()), + NotificationCtr(LangSupport.getNotificationCenter()), + FilePath(FilePath), FormatOptions(options) { + assert(fileSystem); + // This instance of semantic info is used if a document is opened with + // `key.syntactic_only: 1`, but subsequently a semantic request such as + // cursor_info is made. + SemanticInfo = new SwiftDocumentSemanticInfo(FilePath, ASTMgr, + NotificationCtr, fileSystem); } + }; - SmallString<64> DisplayNameBuf; - StringRef DisplayName; - if (auto ValueD = dyn_cast_or_null(Node.Dcl)) { - llvm::raw_svector_ostream OS(DisplayNameBuf); - if (!SwiftLangSupport::printDisplayName(ValueD, OS)) - DisplayName = OS.str(); - } - else if (Node.NameRange.isValid()) { - DisplayName = SrcManager.extractText(Node.NameRange); - } + namespace { + + static UIdent getAccessLevelUID(AccessLevel Access) { + static UIdent AccessOpen("source.lang.swift.accessibility.open"); + static UIdent AccessPublic("source.lang.swift.accessibility.public"); + static UIdent AccessInternal("source.lang.swift.accessibility.internal"); + static UIdent AccessFilePrivate( + "source.lang.swift.accessibility.fileprivate"); + static UIdent AccessPrivate("source.lang.swift.accessibility.private"); - SmallString<64> RuntimeNameBuf; - StringRef RuntimeName = getObjCRuntimeName(Node.Dcl, RuntimeNameBuf); + switch (Access) { + case AccessLevel::Private: + return AccessPrivate; + case AccessLevel::FilePrivate: + return AccessFilePrivate; + case AccessLevel::Internal: + return AccessInternal; + case AccessLevel::Public: + return AccessPublic; + case AccessLevel::Open: + return AccessOpen; + } - SmallString<64> SelectorNameBuf; - StringRef SelectorName = getObjCSelectorName(Node.Dcl, SelectorNameBuf); + llvm_unreachable("Unhandled access level in switch."); + } - std::vector> Attrs; + class SwiftDocumentStructureWalker : public ide::SyntaxModelWalker { + SourceManager &SrcManager; + EditorConsumer &Consumer; + unsigned BufferID; - for (auto Attr : Node.Attrs) { - if (auto AttrUID = SwiftLangSupport::getUIDForDeclAttribute(Attr)) { - unsigned AttrOffset = 0; - unsigned AttrEnd = 0; - auto AttrRange = Attr->getRangeWithAt(); - if (AttrRange.isValid()) { - auto CharRange = Lexer::getCharSourceRangeFromSourceRange(SrcManager, - AttrRange); - AttrOffset = SrcManager.getLocOffsetInBuffer(CharRange.getStart(), - BufferID); - AttrEnd = SrcManager.getLocOffsetInBuffer(CharRange.getEnd(), + public: + SwiftDocumentStructureWalker(SourceManager &SrcManager, unsigned BufferID, + EditorConsumer &Consumer) + : SrcManager(SrcManager), Consumer(Consumer), BufferID(BufferID) {} + + bool walkToSubStructurePre(SyntaxStructureNode Node) override { + unsigned StartOffset = + SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID); + unsigned EndOffset = + SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID); + unsigned NameStart; + unsigned NameEnd; + if (Node.NameRange.isValid()) { + NameStart = SrcManager.getLocOffsetInBuffer(Node.NameRange.getStart(), BufferID); + NameEnd = + SrcManager.getLocOffsetInBuffer(Node.NameRange.getEnd(), BufferID); + } else { + NameStart = NameEnd = 0; + } + + unsigned BodyOffset; + unsigned BodyEnd; + if (Node.BodyRange.isValid()) { + BodyOffset = SrcManager.getLocOffsetInBuffer(Node.BodyRange.getStart(), + BufferID); + BodyEnd = + SrcManager.getLocOffsetInBuffer(Node.BodyRange.getEnd(), BufferID); + } else { + BodyOffset = BodyEnd = 0; + } + + unsigned DocOffset = 0; + unsigned DocEnd = 0; + if (Node.DocRange.isValid()) { + DocOffset = + SrcManager.getLocOffsetInBuffer(Node.DocRange.getStart(), BufferID); + DocEnd = + SrcManager.getLocOffsetInBuffer(Node.DocRange.getEnd(), BufferID); + } + + UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureKind(Node.Kind); + UIdent AccessLevel; + UIdent SetterAccessLevel; + if (Node.Kind != SyntaxStructureKind::Parameter && + Node.Kind != SyntaxStructureKind::LocalVariable && + Node.Kind != SyntaxStructureKind::GenericTypeParam) { + if (auto *VD = dyn_cast_or_null(Node.Dcl)) { + AccessLevel = getAccessLevelUID(VD->getFormalAccess()); + } else if (auto *ED = dyn_cast_or_null(Node.Dcl)) { + auto StrictAccess = ED->getDefaultAccessLevel(); + AccessLevel = getAccessLevelUID(StrictAccess); } + if (auto *ASD = dyn_cast_or_null(Node.Dcl)) { + if (ASD->isSettable(/*UseDC=*/nullptr)) { + swift::AccessLevel SetAccess = ASD->getSetterFormalAccess(); + SetterAccessLevel = getAccessLevelUID(SetAccess); + } + } + } - auto AttrTuple = std::make_tuple(AttrUID.getValue(), AttrOffset, - AttrEnd - AttrOffset); - Attrs.push_back(AttrTuple); + SmallVector InheritedNames; + if (!Node.InheritedTypeRanges.empty()) { + for (auto &TR : Node.InheritedTypeRanges) { + InheritedNames.push_back(SrcManager.extractText(TR)); + } } - } - Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset, - Kind, AccessLevel, SetterAccessLevel, - NameStart, NameEnd - NameStart, - BodyOffset, BodyEnd - BodyOffset, - DocOffset, DocEnd - DocOffset, - DisplayName, - TypeName, RuntimeName, - SelectorName, - InheritedNames, Attrs); + StringRef TypeName; + if (Node.TypeRange.isValid()) { + TypeName = SrcManager.extractText(Node.TypeRange); + } - for (const auto &Elem : Node.Elements) { - if (Elem.Range.isInvalid()) - continue; + SmallString<64> DisplayNameBuf; + StringRef DisplayName; + if (auto ValueD = dyn_cast_or_null(Node.Dcl)) { + llvm::raw_svector_ostream OS(DisplayNameBuf); + if (!SwiftLangSupport::printDisplayName(ValueD, OS)) + DisplayName = OS.str(); + } else if (Node.NameRange.isValid()) { + DisplayName = SrcManager.extractText(Node.NameRange); + } - UIdent Kind = SwiftLangSupport::getUIDForSyntaxStructureElementKind(Elem.Kind); - unsigned Offset = SrcManager.getLocOffsetInBuffer(Elem.Range.getStart(), - BufferID); - unsigned Length = Elem.Range.getByteLength(); - Consumer.handleDocumentSubStructureElement(Kind, Offset, Length); - } + SmallString<64> RuntimeNameBuf; + StringRef RuntimeName = getObjCRuntimeName(Node.Dcl, RuntimeNameBuf); + + SmallString<64> SelectorNameBuf; + StringRef SelectorName = getObjCSelectorName(Node.Dcl, SelectorNameBuf); + + std::vector> Attrs; + + for (auto Attr : Node.Attrs) { + if (auto AttrUID = SwiftLangSupport::getUIDForDeclAttribute(Attr)) { + unsigned AttrOffset = 0; + unsigned AttrEnd = 0; + auto AttrRange = Attr->getRangeWithAt(); + if (AttrRange.isValid()) { + auto CharRange = + Lexer::getCharSourceRangeFromSourceRange(SrcManager, AttrRange); + AttrOffset = + SrcManager.getLocOffsetInBuffer(CharRange.getStart(), BufferID); + AttrEnd = + SrcManager.getLocOffsetInBuffer(CharRange.getEnd(), BufferID); + } - return true; - } + auto AttrTuple = std::make_tuple(AttrUID.getValue(), AttrOffset, + AttrEnd - AttrOffset); + Attrs.push_back(AttrTuple); + } + } - StringRef getObjCRuntimeName(const Decl *D, SmallString<64> &Buf) { - if (!D || D->isInvalid()) - return StringRef(); - if (!isa(D) && !isa(D)) - return StringRef(); - auto *VD = cast(D); - if (!VD->hasName()) - return StringRef(); - auto ident = VD->getBaseName().getIdentifier().str(); - if (ident.empty() || Mangle::isDigit(ident.front())) - return StringRef(); + Consumer.beginDocumentSubStructure( + StartOffset, EndOffset - StartOffset, Kind, AccessLevel, + SetterAccessLevel, NameStart, NameEnd - NameStart, BodyOffset, + BodyEnd - BodyOffset, DocOffset, DocEnd - DocOffset, DisplayName, + TypeName, RuntimeName, SelectorName, InheritedNames, Attrs); + + for (const auto &Elem : Node.Elements) { + if (Elem.Range.isInvalid()) + continue; + + UIdent Kind = + SwiftLangSupport::getUIDForSyntaxStructureElementKind(Elem.Kind); + unsigned Offset = + SrcManager.getLocOffsetInBuffer(Elem.Range.getStart(), BufferID); + unsigned Length = Elem.Range.getByteLength(); + Consumer.handleDocumentSubStructureElement(Kind, Offset, Length); + } - // We don't support getting the runtime name for nested classes. - // This would require typechecking or at least name lookup, if the nested - // class is in an extension. - if (!D->getDeclContext()->isModuleScopeContext()) - return StringRef(); + return true; + } - if (auto ClassD = dyn_cast(D)) { - // We don't vend the runtime name for generic classes for now. - if (ClassD->getGenericParams()) + StringRef getObjCRuntimeName(const Decl *D, SmallString<64> &Buf) { + if (!D || D->isInvalid()) + return StringRef(); + if (!isa(D) && !isa(D)) + return StringRef(); + auto *VD = cast(D); + if (!VD->hasName()) + return StringRef(); + auto ident = VD->getBaseName().getIdentifier().str(); + if (ident.empty() || Mangle::isDigit(ident.front())) + return StringRef(); + + // We don't support getting the runtime name for nested classes. + // This would require typechecking or at least name lookup, if the nested + // class is in an extension. + if (!D->getDeclContext()->isModuleScopeContext()) return StringRef(); - return ClassD->getObjCRuntimeName(Buf); - } - return cast(D)->getObjCRuntimeName(Buf); - } - StringRef getObjCSelectorName(const Decl *D, SmallString<64> &Buf) { - // We only vend the selector name for @IBAction and @IBSegueAction methods. - if (auto FuncD = dyn_cast_or_null(D)) { - if (FuncD->getAttrs().hasAttribute() || - FuncD->getAttrs().hasAttribute()) - return FuncD->getObjCSelector().getString(Buf); + if (auto ClassD = dyn_cast(D)) { + // We don't vend the runtime name for generic classes for now. + if (ClassD->getGenericParams()) + return StringRef(); + return ClassD->getObjCRuntimeName(Buf); + } + return cast(D)->getObjCRuntimeName(Buf); } - return StringRef(); - } - bool walkToSubStructurePost(SyntaxStructureNode Node) override { - Consumer.endDocumentSubStructure(); - return true; - } + StringRef getObjCSelectorName(const Decl *D, SmallString<64> &Buf) { + // We only vend the selector name for @IBAction and @IBSegueAction + // methods. + if (auto FuncD = dyn_cast_or_null(D)) { + if (FuncD->getAttrs().hasAttribute() || + FuncD->getAttrs().hasAttribute()) + return FuncD->getObjCSelector().getString(Buf); + } + return StringRef(); + } - bool walkToNodePre(SyntaxNode Node) override { - if (Node.Kind != SyntaxNodeKind::CommentMarker) - return false; + bool walkToSubStructurePost(SyntaxStructureNode Node) override { + Consumer.endDocumentSubStructure(); + return true; + } - unsigned StartOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), - BufferID); - unsigned EndOffset = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), - BufferID); - UIdent Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind); - Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset, - Kind, UIdent(), UIdent(), 0, 0, - 0, 0, 0, 0, - StringRef(), - StringRef(), StringRef(), - StringRef(), - {}, {}); - return true; - } + bool walkToNodePre(SyntaxNode Node) override { + if (Node.Kind != SyntaxNodeKind::CommentMarker) + return false; - bool walkToNodePost(SyntaxNode Node) override { - if (Node.Kind != SyntaxNodeKind::CommentMarker) + unsigned StartOffset = + SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID); + unsigned EndOffset = + SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID); + UIdent Kind = SwiftLangSupport::getUIDForSyntaxNodeKind(Node.Kind); + Consumer.beginDocumentSubStructure(StartOffset, EndOffset - StartOffset, + Kind, UIdent(), UIdent(), 0, 0, 0, 0, + 0, 0, StringRef(), StringRef(), + StringRef(), StringRef(), {}, {}); return true; + } - Consumer.endDocumentSubStructure(); - return true; - } -}; + bool walkToNodePost(SyntaxNode Node) override { + if (Node.Kind != SyntaxNodeKind::CommentMarker) + return true; -/// Walks the syntax model to populate a given SwiftSyntaxMap with the token -/// ranges to highlight and pass document structure information to the given -/// EditorConsumer. -class SwiftEditorSyntaxWalker: public ide::SyntaxModelWalker { - /// The syntax map to populate - SwiftSyntaxMap &SyntaxMap; - SourceManager &SrcManager; - unsigned BufferID; - SwiftDocumentStructureWalker DocStructureWalker; - /// The current token nesting level (e.g. for a field in a doc comment) - unsigned NestingLevel = 0; -public: - SwiftEditorSyntaxWalker(SwiftSyntaxMap &SyntaxMap, - SourceManager &SrcManager, EditorConsumer &Consumer, - unsigned BufferID) - : SyntaxMap(SyntaxMap), SrcManager(SrcManager), BufferID(BufferID), - DocStructureWalker(SrcManager, BufferID, Consumer) { } - - bool walkToNodePre(SyntaxNode Node) override { - if (Node.Kind == SyntaxNodeKind::CommentMarker) - return DocStructureWalker.walkToNodePre(Node); - ++NestingLevel; - - auto End = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID), - Start = SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID); - - if (NestingLevel > 1) { - // We're nested inside the previously reported token - merge - SyntaxMap.mergeToken({Start, End - Start, Node.Kind}); - } else { - // We're a top-level token, add it after the previous one - SyntaxMap.addToken({Start, End - Start, Node.Kind}); + Consumer.endDocumentSubStructure(); + return true; } + }; - return true; - } - - bool walkToNodePost(SyntaxNode Node) override { - if (Node.Kind == SyntaxNodeKind::CommentMarker) - return DocStructureWalker.walkToNodePost(Node); - --NestingLevel; + /// Walks the syntax model to populate a given SwiftSyntaxMap with the token + /// ranges to highlight and pass document structure information to the given + /// EditorConsumer. + class SwiftEditorSyntaxWalker : public ide::SyntaxModelWalker { + /// The syntax map to populate + SwiftSyntaxMap &SyntaxMap; + SourceManager &SrcManager; + unsigned BufferID; + SwiftDocumentStructureWalker DocStructureWalker; + /// The current token nesting level (e.g. for a field in a doc comment) + unsigned NestingLevel = 0; - return true; - } + public: + SwiftEditorSyntaxWalker(SwiftSyntaxMap &SyntaxMap, + SourceManager &SrcManager, EditorConsumer &Consumer, + unsigned BufferID) + : SyntaxMap(SyntaxMap), SrcManager(SrcManager), BufferID(BufferID), + DocStructureWalker(SrcManager, BufferID, Consumer) {} + + bool walkToNodePre(SyntaxNode Node) override { + if (Node.Kind == SyntaxNodeKind::CommentMarker) + return DocStructureWalker.walkToNodePre(Node); + ++NestingLevel; + + auto End = SrcManager.getLocOffsetInBuffer(Node.Range.getEnd(), BufferID), + Start = + SrcManager.getLocOffsetInBuffer(Node.Range.getStart(), BufferID); + + if (NestingLevel > 1) { + // We're nested inside the previously reported token - merge + SyntaxMap.mergeToken({Start, End - Start, Node.Kind}); + } else { + // We're a top-level token, add it after the previous one + SyntaxMap.addToken({Start, End - Start, Node.Kind}); + } - bool walkToSubStructurePre(SyntaxStructureNode Node) override { - return DocStructureWalker.walkToSubStructurePre(Node); - } + return true; + } - bool walkToSubStructurePost(SyntaxStructureNode Node) override { - return DocStructureWalker.walkToSubStructurePost(Node); - } + bool walkToNodePost(SyntaxNode Node) override { + if (Node.Kind == SyntaxNodeKind::CommentMarker) + return DocStructureWalker.walkToNodePost(Node); + --NestingLevel; -}; + return true; + } -class PlaceholderExpansionScanner { + bool walkToSubStructurePre(SyntaxStructureNode Node) override { + return DocStructureWalker.walkToSubStructurePre(Node); + } -public: - struct Param { - CharSourceRange NameRange; - CharSourceRange TypeRange; - Param(CharSourceRange NameRange, CharSourceRange TypeRange) - :NameRange(NameRange), TypeRange(TypeRange) { } + bool walkToSubStructurePost(SyntaxStructureNode Node) override { + return DocStructureWalker.walkToSubStructurePost(Node); + } }; -private: + class PlaceholderExpansionScanner { - struct ClosureInfo { - std::vector Params; - CharSourceRange ReturnTypeRange; - }; + public: + struct Param { + CharSourceRange NameRange; + CharSourceRange TypeRange; + Param(CharSourceRange NameRange, CharSourceRange TypeRange) + : NameRange(NameRange), TypeRange(TypeRange) {} + }; - SourceManager &SM; - ClosureInfo TargetClosureInfo; - EditorPlaceholderExpr *PHE = nullptr; + private: + struct ClosureInfo { + std::vector Params; + CharSourceRange ReturnTypeRange; + }; - class PlaceholderFinder: public ASTWalker { - SourceLoc PlaceholderLoc; - EditorPlaceholderExpr *&Found; + SourceManager &SM; + ClosureInfo TargetClosureInfo; + EditorPlaceholderExpr *PHE = nullptr; - public: - PlaceholderFinder(SourceLoc PlaceholderLoc, - EditorPlaceholderExpr *&Found) - : PlaceholderLoc(PlaceholderLoc), Found(Found) { - } + class PlaceholderFinder : public ASTWalker { + SourceLoc PlaceholderLoc; + EditorPlaceholderExpr *&Found; - std::pair walkToExprPre(Expr *E) override { - if (isa(E) && E->getStartLoc() == PlaceholderLoc) { - Found = cast(E); - return { false, nullptr }; + public: + PlaceholderFinder(SourceLoc PlaceholderLoc, EditorPlaceholderExpr *&Found) + : PlaceholderLoc(PlaceholderLoc), Found(Found) {} + + std::pair walkToExprPre(Expr *E) override { + if (isa(E) && + E->getStartLoc() == PlaceholderLoc) { + Found = cast(E); + return {false, nullptr}; + } + return {true, E}; } - return { true, E }; - } - - bool walkToDeclPre(Decl *D) override { - if (auto *ICD = dyn_cast(D)) { - // The base walker assumes the content of active IfConfigDecl clauses - // has been injected into the parent context and will be walked there. - // This doesn't hold for pre-typechecked ASTs and we need to find - // placeholders in inactive clauses anyway, so walk them here. - for (auto Clause: ICD->getClauses()) { - for (auto Elem: Clause.Elements) { - Elem.walk(*this); + + bool walkToDeclPre(Decl *D) override { + if (auto *ICD = dyn_cast(D)) { + // The base walker assumes the content of active IfConfigDecl clauses + // has been injected into the parent context and will be walked there. + // This doesn't hold for pre-typechecked ASTs and we need to find + // placeholders in inactive clauses anyway, so walk them here. + for (auto Clause : ICD->getClauses()) { + for (auto Elem : Clause.Elements) { + Elem.walk(*this); + } } + return false; } - return false; + return true; } - return true; - } - }; + }; - class ClosureTypeWalker: public ASTWalker { - SourceManager &SM; - ClosureInfo &Info; - public: - bool FoundFunctionTypeRepr = false; - explicit ClosureTypeWalker(SourceManager &SM, ClosureInfo &Info) : SM(SM), - Info(Info) { } - - bool walkToTypeReprPre(TypeRepr *T) override { - if (auto *FTR = dyn_cast(T)) { - FoundFunctionTypeRepr = true; - for (auto &ArgElt : FTR->getArgsTypeRepr()->getElements()) { - CharSourceRange NR; - CharSourceRange TR; - auto name = ArgElt.Name; - if (!name.empty()) { - NR = CharSourceRange(ArgElt.NameLoc, - name.getLength()); + class ClosureTypeWalker : public ASTWalker { + SourceManager &SM; + ClosureInfo &Info; + + public: + bool FoundFunctionTypeRepr = false; + explicit ClosureTypeWalker(SourceManager &SM, ClosureInfo &Info) + : SM(SM), Info(Info) {} + + bool walkToTypeReprPre(TypeRepr *T) override { + if (auto *FTR = dyn_cast(T)) { + FoundFunctionTypeRepr = true; + for (auto &ArgElt : FTR->getArgsTypeRepr()->getElements()) { + CharSourceRange NR; + CharSourceRange TR; + auto name = ArgElt.Name; + if (!name.empty()) { + NR = CharSourceRange(ArgElt.NameLoc, name.getLength()); + } + SourceLoc SRE = + Lexer::getLocForEndOfToken(SM, ArgElt.Type->getEndLoc()); + TR = CharSourceRange(SM, ArgElt.Type->getStartLoc(), SRE); + Info.Params.emplace_back(NR, TR); + } + if (auto *RTR = FTR->getResultTypeRepr()) { + SourceLoc SRE = Lexer::getLocForEndOfToken(SM, RTR->getEndLoc()); + Info.ReturnTypeRange = CharSourceRange(SM, RTR->getStartLoc(), SRE); } - SourceLoc SRE = Lexer::getLocForEndOfToken(SM, - ArgElt.Type->getEndLoc()); - TR = CharSourceRange(SM, ArgElt.Type->getStartLoc(), SRE); - Info.Params.emplace_back(NR, TR); - } - if (auto *RTR = FTR->getResultTypeRepr()) { - SourceLoc SRE = Lexer::getLocForEndOfToken(SM, RTR->getEndLoc()); - Info.ReturnTypeRange = CharSourceRange(SM, RTR->getStartLoc(), SRE); } + return !FoundFunctionTypeRepr; } - return !FoundFunctionTypeRepr; - } - - bool walkToTypeReprPost(TypeRepr *T) override { - // If we just visited the FunctionTypeRepr, end traversal. - return !FoundFunctionTypeRepr; - } - }; + bool walkToTypeReprPost(TypeRepr *T) override { + // If we just visited the FunctionTypeRepr, end traversal. + return !FoundFunctionTypeRepr; + } + }; - bool containClosure(Expr *E) { - if (E->getStartLoc().isInvalid()) - return false; - EditorPlaceholderExpr *Found = nullptr; - ClosureInfo Info; - ClosureTypeWalker ClosureWalker(SM, Info); - PlaceholderFinder Finder(E->getStartLoc(), Found); - E->walk(Finder); - if (Found) { - if (auto TR = Found->getTypeLoc().getTypeRepr()) { - TR->walk(ClosureWalker); - return ClosureWalker.FoundFunctionTypeRepr; + bool containClosure(Expr *E) { + if (E->getStartLoc().isInvalid()) + return false; + EditorPlaceholderExpr *Found = nullptr; + ClosureInfo Info; + ClosureTypeWalker ClosureWalker(SM, Info); + PlaceholderFinder Finder(E->getStartLoc(), Found); + E->walk(Finder); + if (Found) { + if (auto TR = Found->getTypeLoc().getTypeRepr()) { + TR->walk(ClosureWalker); + return ClosureWalker.FoundFunctionTypeRepr; + } } + E->walk(ClosureWalker); + return ClosureWalker.FoundFunctionTypeRepr; } - E->walk(ClosureWalker); - return ClosureWalker.FoundFunctionTypeRepr; - } - bool scanClosureType(SourceFile &SF, SourceLoc PlaceholderLoc) { - TargetClosureInfo.Params.clear(); - TargetClosureInfo.ReturnTypeRange = CharSourceRange(); - PlaceholderFinder Finder(PlaceholderLoc, PHE); - SF.walk(Finder); - if (!PHE || !PHE->getTypeForExpansion()) - return false; - ClosureTypeWalker PW(SM, TargetClosureInfo); - PHE->getTypeForExpansion()->walk(PW); - return PW.FoundFunctionTypeRepr; - } - - /// Finds the enclosing CallExpr, and indicates whether it should be further - /// considered a candidate for application of trailing closure. - /// For example, if the CallExpr is enclosed in another expression or statement - /// such as "outer(inner(<#closure#>))", or "if inner(<#closure#>)", then trailing - /// closure should not be applied to the inner call. - std::pair enclosingCallExprArg(SourceFile &SF, SourceLoc SL) { + bool scanClosureType(SourceFile &SF, SourceLoc PlaceholderLoc) { + TargetClosureInfo.Params.clear(); + TargetClosureInfo.ReturnTypeRange = CharSourceRange(); + PlaceholderFinder Finder(PlaceholderLoc, PHE); + SF.walk(Finder); + if (!PHE || !PHE->getTypeForExpansion()) + return false; + ClosureTypeWalker PW(SM, TargetClosureInfo); + PHE->getTypeForExpansion()->walk(PW); + return PW.FoundFunctionTypeRepr; + } + + /// Finds the enclosing CallExpr, and indicates whether it should be further + /// considered a candidate for application of trailing closure. + /// For example, if the CallExpr is enclosed in another expression or + /// statement such as "outer(inner(<#closure#>))", or "if + /// inner(<#closure#>)", then trailing closure should not be applied to the + /// inner call. + std::pair enclosingCallExprArg(SourceFile &SF, SourceLoc SL) { + + class CallExprFinder : public SourceEntityWalker { + public: + const SourceManager &SM; + SourceLoc TargetLoc; + std::pair EnclosingCallAndArg; + Expr *OuterExpr; + Stmt *OuterStmt; + explicit CallExprFinder(const SourceManager &SM) : SM(SM) {} + + bool checkCallExpr(Expr *E) { + Expr *Arg = nullptr; + if (auto *CE = dyn_cast(E)) { + // Call expression can have argument. + Arg = CE->getArg(); + } else if (auto UME = dyn_cast(E)) { + // Unresolved member can have argument too. + Arg = UME->getArgument(); + } + if (Arg) { - class CallExprFinder : public SourceEntityWalker { - public: - const SourceManager &SM; - SourceLoc TargetLoc; - std::pair EnclosingCallAndArg; - Expr *OuterExpr; - Stmt *OuterStmt; - explicit CallExprFinder(const SourceManager &SM) - :SM(SM) { } - - bool checkCallExpr(Expr *E) { - Expr* Arg = nullptr; - if (auto *CE = dyn_cast(E)) { - // Call expression can have argument. - Arg = CE->getArg(); - } else if (auto UME = dyn_cast(E)) { - // Unresolved member can have argument too. - Arg = UME->getArgument(); - } - if (!Arg) + if (EnclosingCallAndArg.first) + OuterExpr = EnclosingCallAndArg.first; + EnclosingCallAndArg = {E, Arg}; + return true; + } return false; - if (EnclosingCallAndArg.first) - OuterExpr = EnclosingCallAndArg.first; - EnclosingCallAndArg = {E, Arg}; - return true; - } + } - bool walkToExprPre(Expr *E) override { - auto SR = E->getSourceRange(); - if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) { - if (auto closure = dyn_cast(E)) { - if (closure->hasSingleExpressionBody()) { - // Treat a single-expression body like a brace statement and reset - // the enclosing context. Note: when the placeholder is the whole - // body it is handled specially as wrapped in braces by - // shouldUseTrailingClosureInTuple(). - auto SR = closure->getSingleExpressionBody()->getSourceRange(); - if (SR.isValid() && SR.Start != TargetLoc && - SM.rangeContainsTokenLoc(SR, TargetLoc)) { - OuterStmt = nullptr; - OuterExpr = nullptr; - EnclosingCallAndArg = {nullptr, nullptr}; - return true; + bool walkToExprPre(Expr *E) override { + auto SR = E->getSourceRange(); + if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) { + if (auto closure = dyn_cast(E)) { + if (closure->hasSingleExpressionBody()) { + // Treat a single-expression body like a brace statement and + // reset the enclosing context. Note: when the placeholder is + // the whole body it is handled specially as wrapped in braces + // by shouldUseTrailingClosureInTuple(). + auto SR = closure->getSingleExpressionBody()->getSourceRange(); + if (SR.isValid() && SR.Start != TargetLoc && + SM.rangeContainsTokenLoc(SR, TargetLoc)) { + OuterStmt = nullptr; + OuterExpr = nullptr; + EnclosingCallAndArg = {nullptr, nullptr}; + return true; + } } } - } - if (!checkCallExpr(E) && !EnclosingCallAndArg.first) { - OuterExpr = E; + if (!checkCallExpr(E) && !EnclosingCallAndArg.first) { + OuterExpr = E; + } } + return true; } - return true; - } - bool walkToExprPost(Expr *E) override { - if (E->getStartLoc() == TargetLoc) - return false; // found what we needed to find, stop walking. - return true; - } - - bool walkToStmtPre(Stmt *S) override { - auto SR = S->getSourceRange(); - if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) { - // A statement inside an expression - e.g. `foo({ if ... })` - resets - // the enclosing context. - OuterExpr = nullptr; - EnclosingCallAndArg = {nullptr, nullptr}; + bool walkToExprPost(Expr *E) override { + if (E->getStartLoc() == TargetLoc) + return false; // found what we needed to find, stop walking. + return true; + } - switch (S->getKind()) { - case StmtKind::Brace: - case StmtKind::Return: - case StmtKind::Yield: - case StmtKind::Throw: - // A trailing closure is allowed in these statements. - OuterStmt = nullptr; - break; - default: - OuterStmt = S; - break; + bool walkToStmtPre(Stmt *S) override { + auto SR = S->getSourceRange(); + if (SR.isValid() && SM.rangeContainsTokenLoc(SR, TargetLoc)) { + // A statement inside an expression - e.g. `foo({ if ... })` - + // resets the enclosing context. + OuterExpr = nullptr; + EnclosingCallAndArg = {nullptr, nullptr}; + + switch (S->getKind()) { + case StmtKind::Brace: + case StmtKind::Return: + case StmtKind::Yield: + case StmtKind::Throw: + // A trailing closure is allowed in these statements. + OuterStmt = nullptr; + break; + default: + OuterStmt = S; + break; + } } + return true; } - return true; - } - bool shouldWalkInactiveConfigRegion() override { return true; } + bool shouldWalkInactiveConfigRegion() override { return true; } - Expr *findEnclosingCallArg(SourceFile &SF, SourceLoc SL) { - EnclosingCallAndArg = {nullptr, nullptr}; - OuterExpr = nullptr; - OuterStmt = nullptr; - TargetLoc = SL; - walk(SF); - return EnclosingCallAndArg.second; - } - }; + Expr *findEnclosingCallArg(SourceFile &SF, SourceLoc SL) { + EnclosingCallAndArg = {nullptr, nullptr}; + OuterExpr = nullptr; + OuterStmt = nullptr; + TargetLoc = SL; + walk(SF); + return EnclosingCallAndArg.second; + } + }; - CallExprFinder CEFinder(SM); - auto *CE = CEFinder.findEnclosingCallArg(SF, SL); + CallExprFinder CEFinder(SM); + auto *CE = CEFinder.findEnclosingCallArg(SF, SL); - if (!CE) - return std::make_pair(CE, false); - if (CEFinder.OuterExpr) - return std::make_pair(CE, false); - if (CEFinder.OuterStmt) - return std::make_pair(CE, false); + if (!CE) + return std::make_pair(CE, false); + if (CEFinder.OuterExpr) + return std::make_pair(CE, false); + if (CEFinder.OuterStmt) + return std::make_pair(CE, false); - return std::make_pair(CE, true); - } + return std::make_pair(CE, true); + } - bool shouldUseTrailingClosureInTuple(TupleExpr *TE, - SourceLoc PlaceHolderStartLoc, - bool &isWrappedWithBraces) { - if (TE->getElements().empty()) - return false; + bool shouldUseTrailingClosureInTuple(TupleExpr *TE, + SourceLoc PlaceHolderStartLoc, + bool &isWrappedWithBraces) { + if (TE->getElements().empty()) + return false; - for (unsigned I = 0, N = TE->getNumElements(); I < N; ++ I) { - bool IsLast = I == N - 1; - Expr *E = TE->getElement(I); - - // Placeholders wrapped in braces {<#T##() -> Int#>} can also - // be valid for trailing syntax. - if (auto CE = dyn_cast(E)) { - if (CE->hasSingleExpressionBody() && - CE->getSingleExpressionBody()->getStartLoc() - == PlaceHolderStartLoc) { - // We found the placeholder. - isWrappedWithBraces = true; - return IsLast; + for (unsigned I = 0, N = TE->getNumElements(); I < N; ++I) { + bool IsLast = I == N - 1; + Expr *E = TE->getElement(I); + + // Placeholders wrapped in braces {<#T##() -> Int#>} can also + // be valid for trailing syntax. + if (auto CE = dyn_cast(E)) { + if (CE->hasSingleExpressionBody() && + CE->getSingleExpressionBody()->getStartLoc() == + PlaceHolderStartLoc) { + // We found the placeholder. + isWrappedWithBraces = true; + return IsLast; + } + } else if (IsLast) { + return E->getStartLoc() == PlaceHolderStartLoc; + } else if (containClosure(E)) { + return false; } - } else if (IsLast) { - return E->getStartLoc() == PlaceHolderStartLoc; - } else if (containClosure(E)) { - return false; } + return false; } - return false; - } -public: - explicit PlaceholderExpansionScanner(SourceManager &SM) : SM(SM) { } - - /// Retrieves the parameter list, return type and context info for - /// a typed completion placeholder in a function call. - /// For example: foo.bar(aaa, <#T##(Int, Int) -> Bool#>). - bool scan(SourceFile &SF, unsigned BufID, unsigned Offset, - unsigned Length, std::function, - CharSourceRange)> Callback, - std::function NonClosureCallback) { - - SourceLoc PlaceholderStartLoc = SM.getLocForOffset(BufID, Offset); - - // See if the placeholder is encapsulated with an EditorPlaceholderExpr - // and retrieve parameter and return type ranges. - if (!scanClosureType(SF, PlaceholderStartLoc)) { - return NonClosureCallback(PHE); - } - - // Now we need to see if we can suggest trailing closure expansion, - // and if the call parens can be removed in that case. - // We'll first find the enclosing CallExpr, and then do further analysis. - bool UseTrailingClosure = false; - bool isWrappedWithBraces = false; - auto ECE = enclosingCallExprArg(SF, PlaceholderStartLoc); - Expr *Args = ECE.first; - if (Args && ECE.second) { - if (isa(Args)) { - UseTrailingClosure = true; - } else if (auto *TE = dyn_cast(Args)) { - UseTrailingClosure = shouldUseTrailingClosureInTuple( - TE, PlaceholderStartLoc, - isWrappedWithBraces); + public: + explicit PlaceholderExpansionScanner(SourceManager &SM) : SM(SM) {} + + /// Retrieves the parameter list, return type and context info for + /// a typed completion placeholder in a function call. + /// For example: foo.bar(aaa, <#T##(Int, Int) -> Bool#>). + bool scan(SourceFile &SF, unsigned BufID, unsigned Offset, unsigned Length, + std::function, + CharSourceRange)> + Callback, + std::function NonClosureCallback) { + + SourceLoc PlaceholderStartLoc = SM.getLocForOffset(BufID, Offset); + + // See if the placeholder is encapsulated with an EditorPlaceholderExpr + // and retrieve parameter and return type ranges. + if (!scanClosureType(SF, PlaceholderStartLoc)) { + return NonClosureCallback(PHE); } - } - Callback(Args, UseTrailingClosure, isWrappedWithBraces, - TargetClosureInfo.Params, - TargetClosureInfo.ReturnTypeRange); - return true; - } + // Now we need to see if we can suggest trailing closure expansion, + // and if the call parens can be removed in that case. + // We'll first find the enclosing CallExpr, and then do further analysis. + bool UseTrailingClosure = false; + bool isWrappedWithBraces = false; + auto ECE = enclosingCallExprArg(SF, PlaceholderStartLoc); + Expr *Args = ECE.first; + if (Args && ECE.second) { + if (isa(Args)) { + UseTrailingClosure = true; + } else if (auto *TE = dyn_cast(Args)) { + UseTrailingClosure = shouldUseTrailingClosureInTuple( + TE, PlaceholderStartLoc, isWrappedWithBraces); + } + } -}; + Callback(Args, UseTrailingClosure, isWrappedWithBraces, + TargetClosureInfo.Params, TargetClosureInfo.ReturnTypeRange); + return true; + } + }; -} // anonymous namespace + } // anonymous namespace -SwiftEditorDocument::SwiftEditorDocument( - StringRef FilePath, SwiftLangSupport &LangSupport, - llvm::IntrusiveRefCntPtr fileSystem, - CodeFormatOptions Options) - :Impl(*new Implementation(FilePath, LangSupport, Options, fileSystem)) { } + SwiftEditorDocument::SwiftEditorDocument( + StringRef FilePath, SwiftLangSupport & LangSupport, + llvm::IntrusiveRefCntPtr fileSystem, + CodeFormatOptions Options) + : Impl(*new Implementation(FilePath, LangSupport, Options, fileSystem)) {} -SwiftEditorDocument::~SwiftEditorDocument() -{ - delete &Impl; -} + SwiftEditorDocument::~SwiftEditorDocument() { delete &Impl; } -ImmutableTextSnapshotRef SwiftEditorDocument::initializeText( - llvm::MemoryBuffer *Buf, ArrayRef Args, - bool ProvideSemanticInfo, - llvm::IntrusiveRefCntPtr fileSystem) { - llvm::sys::ScopedLock L(Impl.AccessMtx); + ImmutableTextSnapshotRef SwiftEditorDocument::initializeText( + llvm::MemoryBuffer * Buf, ArrayRef Args, + bool ProvideSemanticInfo, + llvm::IntrusiveRefCntPtr fileSystem) { + llvm::sys::ScopedLock L(Impl.AccessMtx); - Impl.Edited = false; - Impl.EditableBuffer = - new EditableTextBuffer(Impl.FilePath, Buf->getBuffer()); + Impl.Edited = false; + Impl.EditableBuffer = + new EditableTextBuffer(Impl.FilePath, Buf->getBuffer()); - // Reset the syntax map data and affected range - Impl.SyntaxMap.Tokens.clear(); - Impl.AffectedRange = {0, static_cast(Buf->getBufferSize())}; + // Reset the syntax map data and affected range + Impl.SyntaxMap.Tokens.clear(); + Impl.AffectedRange = {0, static_cast(Buf->getBufferSize())}; - // Try to create a compiler invocation object if needing semantic info - // or it's syntactic-only but with passed-in compiler arguments. - if (ProvideSemanticInfo || !Args.empty()) { - Impl.SemanticInfo = new SwiftDocumentSemanticInfo( - Impl.FilePath, Impl.ASTMgr, Impl.NotificationCtr, fileSystem); - Impl.SemanticInfo->setCompilerArgs(Args); + // Try to create a compiler invocation object if needing semantic info + // or it's syntactic-only but with passed-in compiler arguments. + if (ProvideSemanticInfo || !Args.empty()) { + Impl.SemanticInfo = new SwiftDocumentSemanticInfo( + Impl.FilePath, Impl.ASTMgr, Impl.NotificationCtr, fileSystem); + Impl.SemanticInfo->setCompilerArgs(Args); + } + return Impl.EditableBuffer->getSnapshot(); } - return Impl.EditableBuffer->getSnapshot(); -} -static void updateSemaInfo(RefPtr SemanticInfo, - EditableTextBufferRef EditableBuffer) { - if (SemanticInfo) { - SemanticInfo->processLatestSnapshotAsync(EditableBuffer); + static void updateSemaInfo(RefPtr SemanticInfo, + EditableTextBufferRef EditableBuffer) { + if (SemanticInfo) { + SemanticInfo->processLatestSnapshotAsync(EditableBuffer); + } } -} -ImmutableTextSnapshotRef SwiftEditorDocument::replaceText( - unsigned Offset, unsigned Length, llvm::MemoryBuffer *Buf, - bool ProvideSemanticInfo, std::string &error) { + ImmutableTextSnapshotRef SwiftEditorDocument::replaceText( + unsigned Offset, unsigned Length, llvm::MemoryBuffer *Buf, + bool ProvideSemanticInfo, std::string &error) { - ImmutableTextSnapshotRef Snapshot; - EditableTextBufferRef EditableBuffer; - RefPtr SemanticInfo; - { - llvm::sys::ScopedLock L(Impl.AccessMtx); + ImmutableTextSnapshotRef Snapshot; + EditableTextBufferRef EditableBuffer; + RefPtr SemanticInfo; + { + llvm::sys::ScopedLock L(Impl.AccessMtx); - EditableBuffer = Impl.EditableBuffer; - SemanticInfo = Impl.SemanticInfo; + EditableBuffer = Impl.EditableBuffer; + SemanticInfo = Impl.SemanticInfo; - // Validate offset and length. - if ((Offset + Length) > EditableBuffer->getSize()) { - error = "'offset' + 'length' is out of range"; - return nullptr; - } + // Validate offset and length. + if ((Offset + Length) > EditableBuffer->getSize()) { + error = "'offset' + 'length' is out of range"; + return nullptr; + } - Impl.Edited = true; - llvm::StringRef Str = Buf->getBuffer(); + Impl.Edited = true; + llvm::StringRef Str = Buf->getBuffer(); - // Update the buffer itself - Snapshot = EditableBuffer->replace(Offset, Length, Str); + // Update the buffer itself + Snapshot = EditableBuffer->replace(Offset, Length, Str); - // Update the old syntax map offsets to account for the replaced range. - // Also set the initial AffectedRange to cover any tokens that - // the replaced range intersected. This allows for clients that split - // multi-line tokens at line boundaries, and ensure all parts of these tokens - // will be cleared. - Impl.AffectedRange = - Impl.SyntaxMap.adjustForReplacement(Offset, Length, Str.size()); + // Update the old syntax map offsets to account for the replaced range. + // Also set the initial AffectedRange to cover any tokens that + // the replaced range intersected. This allows for clients that split + // multi-line tokens at line boundaries, and ensure all parts of these + // tokens will be cleared. + Impl.AffectedRange = + Impl.SyntaxMap.adjustForReplacement(Offset, Length, Str.size()); - // We need to release `AccessMtx` before calling into the ASTManager, since - // it may call back to the editor for document state. - } + // We need to release `AccessMtx` before calling into the ASTManager, + // since it may call back to the editor for document state. + } - if (ProvideSemanticInfo) { - // If this is not a no-op, update semantic info. - if (Length != 0 || Buf->getBufferSize() != 0) { - ::updateSemaInfo(SemanticInfo, EditableBuffer); + if (ProvideSemanticInfo) { + // If this is not a no-op, update semantic info. + if (Length != 0 || Buf->getBufferSize() != 0) { + ::updateSemaInfo(SemanticInfo, EditableBuffer); - // FIXME: we should also update any "interesting" ASTs that depend on this - // document here, e.g. any ASTs for files visible in an editor. However, - // because our API conflates this with any file with unsaved changes we do - // not update all open documents, since there could be too many of them. + // FIXME: we should also update any "interesting" ASTs that depend on + // this document here, e.g. any ASTs for files visible in an editor. + // However, because our API conflates this with any file with unsaved + // changes we do not update all open documents, since there could be too + // many of them. + } } - } - return Snapshot; -} - -void SwiftEditorDocument::updateSemaInfo() { - Impl.AccessMtx.lock(); - auto EditableBuffer = Impl.EditableBuffer; - auto SemanticInfo = Impl.SemanticInfo; - // We need to release `AccessMtx` before calling into the ASTManager, since it - // may call back to the editor for document state. - Impl.AccessMtx.unlock(); + return Snapshot; + } - ::updateSemaInfo(SemanticInfo, EditableBuffer); -} + void SwiftEditorDocument::updateSemaInfo() { + Impl.AccessMtx.lock(); + auto EditableBuffer = Impl.EditableBuffer; + auto SemanticInfo = Impl.SemanticInfo; + // We need to release `AccessMtx` before calling into the ASTManager, since + // it may call back to the editor for document state. + Impl.AccessMtx.unlock(); -void SwiftEditorDocument::parse(ImmutableTextSnapshotRef Snapshot, - SwiftLangSupport &Lang, bool BuildSyntaxTree, - SyntaxParsingCache *SyntaxCache) { - llvm::sys::ScopedLock L(Impl.AccessMtx); + ::updateSemaInfo(SemanticInfo, EditableBuffer); + } - assert(Impl.SemanticInfo && "Impl.SemanticInfo must be set"); + void SwiftEditorDocument::parse(ImmutableTextSnapshotRef Snapshot, + SwiftLangSupport & Lang, bool BuildSyntaxTree, + SyntaxParsingCache *SyntaxCache) { + llvm::sys::ScopedLock L(Impl.AccessMtx); - std::vector Args; - std::string PrimaryFile; // Ignored, Impl.FilePath will be used + assert(Impl.SemanticInfo && "Impl.SemanticInfo must be set"); - CompilerInvocation CompInv; - if (Impl.SemanticInfo->getInvocation()) { - Impl.SemanticInfo->getInvocation()->applyTo(CompInv); - Impl.SemanticInfo->getInvocation()->raw(Args, PrimaryFile); - } else { - // Use stdin as a .swift input to satisfy the driver. Note that we don't - // use Impl.FilePath here because it may be invalid filename for driver - // like "" or "-foobar". - SmallVector Args; - Args.push_back("-"); - std::string Error; - // Ignore possible error(s) - Lang.getASTManager()-> - initCompilerInvocation(CompInv, Args, StringRef(), Error); - } - CompInv.getLangOptions().BuildSyntaxTree = BuildSyntaxTree; - CompInv.setMainFileSyntaxParsingCache(SyntaxCache); - // When reuse parts of the syntax tree from a SyntaxParsingCache, not - // all tokens are visited and thus token collection is invalid - CompInv.getLangOptions().CollectParsedToken = (SyntaxCache == nullptr); - // Access to Impl.SyntaxInfo is guarded by Impl.AccessMtx - Impl.SyntaxInfo.reset( - new SwiftDocumentSyntaxInfo(CompInv, Snapshot, Args, Impl.FilePath)); - - Impl.SyntaxInfo->parse(); -} + std::vector Args; + std::string PrimaryFile; // Ignored, Impl.FilePath will be used -void SwiftEditorDocument::readSyntaxInfo(EditorConsumer &Consumer) { - llvm::sys::ScopedLock L(Impl.AccessMtx); + CompilerInvocation CompInv; + if (Impl.SemanticInfo->getInvocation()) { + Impl.SemanticInfo->getInvocation()->applyTo(CompInv); + Impl.SemanticInfo->getInvocation()->raw(Args, PrimaryFile); + } else { + // Use stdin as a .swift input to satisfy the driver. Note that we don't + // use Impl.FilePath here because it may be invalid filename for driver + // like "" or "-foobar". + SmallVector Args; + Args.push_back("-"); + std::string Error; + // Ignore possible error(s) + Lang.getASTManager()->initCompilerInvocation(CompInv, Args, StringRef(), + Error); + } + CompInv.getLangOptions().BuildSyntaxTree = BuildSyntaxTree; + CompInv.setMainFileSyntaxParsingCache(SyntaxCache); + // When reuse parts of the syntax tree from a SyntaxParsingCache, not + // all tokens are visited and thus token collection is invalid + CompInv.getLangOptions().CollectParsedToken = (SyntaxCache == nullptr); + // Access to Impl.SyntaxInfo is guarded by Impl.AccessMtx + Impl.SyntaxInfo.reset( + new SwiftDocumentSyntaxInfo(CompInv, Snapshot, Args, Impl.FilePath)); + + Impl.SyntaxInfo->parse(); + } + + void SwiftEditorDocument::readSyntaxInfo(EditorConsumer & Consumer) { + llvm::sys::ScopedLock L(Impl.AccessMtx); - Impl.ParserDiagnostics = Impl.SyntaxInfo->getDiagnostics(); + Impl.ParserDiagnostics = Impl.SyntaxInfo->getDiagnostics(); - SwiftSyntaxMap NewMap = SwiftSyntaxMap(Impl.SyntaxMap.Tokens.size() + 16); + SwiftSyntaxMap NewMap = SwiftSyntaxMap(Impl.SyntaxMap.Tokens.size() + 16); - if (Consumer.syntaxTreeEnabled()) { - auto SyntaxTree = Impl.SyntaxInfo->getSourceFile().getSyntaxRoot(); - Impl.SyntaxTree.emplace(SyntaxTree); - if (Consumer.syntaxMapEnabled()) { - Consumer.handleRequestError( - "Retrieving both a syntax map and a syntax tree at the same time is " - "not supported. Use the SyntaxClassifier in swiftSyntax to generate " - "the syntax map on the Swift side."); - } - if (Consumer.documentStructureEnabled()) { - Consumer.handleRequestError( - "Retrieving both the document structure and a syntax tree at the " - "same time is not supported. Use the syntax tree to compute the " - "document structure."); - } - } else { - ide::SyntaxModelContext ModelContext(Impl.SyntaxInfo->getSourceFile()); - - SwiftEditorSyntaxWalker SyntaxWalker( - NewMap, Impl.SyntaxInfo->getSourceManager(), Consumer, - Impl.SyntaxInfo->getBufferID()); - ModelContext.walk(SyntaxWalker); - - bool SawChanges = true; - if (Impl.Edited) { - // We're ansering an edit request. Report all highlighted token ranges not - // in the previous syntax map to the Consumer and extend the AffectedRange - // to contain all added/removed token ranges. - SawChanges = - NewMap.forEachChanged(Impl.SyntaxMap, Impl.AffectedRange, Consumer); + if (Consumer.syntaxTreeEnabled()) { + auto SyntaxTree = Impl.SyntaxInfo->getSourceFile().getSyntaxRoot(); + Impl.SyntaxTree.emplace(SyntaxTree); + if (Consumer.syntaxMapEnabled()) { + Consumer.handleRequestError("Retrieving both a syntax map and a syntax " + "tree at the same time is " + "not supported. Use the SyntaxClassifier " + "in swiftSyntax to generate " + "the syntax map on the Swift side."); + } + if (Consumer.documentStructureEnabled()) { + Consumer.handleRequestError( + "Retrieving both the document structure and a syntax tree at the " + "same time is not supported. Use the syntax tree to compute the " + "document structure."); + } + } else { + ide::SyntaxModelContext ModelContext(Impl.SyntaxInfo->getSourceFile()); + + SwiftEditorSyntaxWalker SyntaxWalker( + NewMap, Impl.SyntaxInfo->getSourceManager(), Consumer, + Impl.SyntaxInfo->getBufferID()); + ModelContext.walk(SyntaxWalker); + + bool SawChanges = true; + if (Impl.Edited) { + // We're ansering an edit request. Report all highlighted token ranges + // not in the previous syntax map to the Consumer and extend the + // AffectedRange to contain all added/removed token ranges. + SawChanges = + NewMap.forEachChanged(Impl.SyntaxMap, Impl.AffectedRange, Consumer); + } else { + // The is an open/initialise. Report all highlighted token ranges to the + // Consumer. + NewMap.forEach(Consumer); + } + Impl.SyntaxMap = std::move(NewMap); + + // Recording an affected length of 0 still results in the client updating + // its copy of the syntax map (by clearning all tokens on the line of the + // affected offset). We need to not record it at all to signal a no-op. + if (SawChanges) + Consumer.recordAffectedRange(Impl.AffectedRange->Offset, + Impl.AffectedRange->length()); + } + } + + void SwiftEditorDocument::readSemanticInfo(ImmutableTextSnapshotRef Snapshot, + EditorConsumer & Consumer) { + std::vector SemaToks; + Optional> SemaDiags; + Impl.SemanticInfo->readSemanticInfo(Snapshot, SemaToks, SemaDiags, + Impl.ParserDiagnostics); + + for (auto SemaTok : SemaToks) { + unsigned Offset = SemaTok.ByteOffset; + unsigned Length = SemaTok.Length; + UIdent Kind = SemaTok.getUIdentForKind(); + bool IsSystem = SemaTok.getIsSystem(); + if (Kind.isValid()) + Consumer.handleSemanticAnnotation(Offset, Length, Kind, IsSystem); + } + + static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema"); + static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse"); + + // If there's no value returned for diagnostics it means they are + // out-of-date (based on a different snapshot). + if (SemaDiags.hasValue()) { + Consumer.setDiagnosticStage(SemaDiagStage); + for (auto &Diag : SemaDiags.getValue()) + Consumer.handleDiagnostic(Diag, SemaDiagStage); } else { - // The is an open/initialise. Report all highlighted token ranges to the - // Consumer. - NewMap.forEach(Consumer); + Consumer.setDiagnosticStage(ParseDiagStage); + for (auto &Diag : Impl.ParserDiagnostics) + Consumer.handleDiagnostic(Diag, ParseDiagStage); } - Impl.SyntaxMap = std::move(NewMap); - - // Recording an affected length of 0 still results in the client updating - // its copy of the syntax map (by clearning all tokens on the line of the - // affected offset). We need to not record it at all to signal a no-op. - if (SawChanges) - Consumer.recordAffectedRange(Impl.AffectedRange->Offset, - Impl.AffectedRange->length()); } -} -void SwiftEditorDocument::readSemanticInfo(ImmutableTextSnapshotRef Snapshot, - EditorConsumer& Consumer) { - std::vector SemaToks; - Optional> SemaDiags; - Impl.SemanticInfo->readSemanticInfo(Snapshot, SemaToks, SemaDiags, - Impl.ParserDiagnostics); - - for (auto SemaTok : SemaToks) { - unsigned Offset = SemaTok.ByteOffset; - unsigned Length = SemaTok.Length; - UIdent Kind = SemaTok.getUIdentForKind(); - bool IsSystem = SemaTok.getIsSystem(); - if (Kind.isValid()) - Consumer.handleSemanticAnnotation(Offset, Length, Kind, IsSystem); - } - - static UIdent SemaDiagStage("source.diagnostic.stage.swift.sema"); - static UIdent ParseDiagStage("source.diagnostic.stage.swift.parse"); - - // If there's no value returned for diagnostics it means they are out-of-date - // (based on a different snapshot). - if (SemaDiags.hasValue()) { - Consumer.setDiagnosticStage(SemaDiagStage); - for (auto &Diag : SemaDiags.getValue()) - Consumer.handleDiagnostic(Diag, SemaDiagStage); - } else { - Consumer.setDiagnosticStage(ParseDiagStage); - for (auto &Diag : Impl.ParserDiagnostics) - Consumer.handleDiagnostic(Diag, ParseDiagStage); + void SwiftEditorDocument::removeCachedAST() { + Impl.SemanticInfo->removeCachedAST(); } -} -void SwiftEditorDocument::removeCachedAST() { - Impl.SemanticInfo->removeCachedAST(); -} + void SwiftEditorDocument::applyFormatOptions(OptionsDictionary & FmtOptions) { + static UIdent KeyUseTabs("key.editor.format.usetabs"); + static UIdent KeyIndentWidth("key.editor.format.indentwidth"); + static UIdent KeyTabWidth("key.editor.format.tabwidth"); + static UIdent KeyIndentSwitchCase("key.editor.format.indent_switch_case"); -void SwiftEditorDocument::applyFormatOptions(OptionsDictionary &FmtOptions) { - static UIdent KeyUseTabs("key.editor.format.usetabs"); - static UIdent KeyIndentWidth("key.editor.format.indentwidth"); - static UIdent KeyTabWidth("key.editor.format.tabwidth"); - static UIdent KeyIndentSwitchCase("key.editor.format.indent_switch_case"); + FmtOptions.valueForOption(KeyUseTabs, Impl.FormatOptions.UseTabs); + FmtOptions.valueForOption(KeyIndentWidth, Impl.FormatOptions.IndentWidth); + FmtOptions.valueForOption(KeyTabWidth, Impl.FormatOptions.TabWidth); + FmtOptions.valueForOption(KeyIndentSwitchCase, + Impl.FormatOptions.IndentSwitchCase); + } - FmtOptions.valueForOption(KeyUseTabs, Impl.FormatOptions.UseTabs); - FmtOptions.valueForOption(KeyIndentWidth, Impl.FormatOptions.IndentWidth); - FmtOptions.valueForOption(KeyTabWidth, Impl.FormatOptions.TabWidth); - FmtOptions.valueForOption(KeyIndentSwitchCase, Impl.FormatOptions.IndentSwitchCase); -} + const CodeFormatOptions &SwiftEditorDocument::getFormatOptions() { + return Impl.FormatOptions; + } -const CodeFormatOptions &SwiftEditorDocument::getFormatOptions() { - return Impl.FormatOptions; -} + const llvm::Optional + &SwiftEditorDocument::getSyntaxTree() const { + return Impl.SyntaxTree; + } -const llvm::Optional & -SwiftEditorDocument::getSyntaxTree() const { - return Impl.SyntaxTree; -} + std::string SwiftEditorDocument::getFilePath() const { return Impl.FilePath; } -std::string SwiftEditorDocument::getFilePath() const { return Impl.FilePath; } + bool SwiftEditorDocument::hasUpToDateAST() const { + return Impl.SyntaxInfo->hasUpToDateAST(); + } -bool SwiftEditorDocument::hasUpToDateAST() const { - return Impl.SyntaxInfo->hasUpToDateAST(); -} + llvm::IntrusiveRefCntPtr + SwiftEditorDocument::getFileSystem() const { + llvm::sys::ScopedLock L(Impl.AccessMtx); + return Impl.SemanticInfo ? Impl.SemanticInfo->getFileSystem() + : llvm::vfs::getRealFileSystem(); + } -llvm::IntrusiveRefCntPtr -SwiftEditorDocument::getFileSystem() const { - llvm::sys::ScopedLock L(Impl.AccessMtx); - return Impl.SemanticInfo ? Impl.SemanticInfo->getFileSystem() - : llvm::vfs::getRealFileSystem(); -} + void SwiftEditorDocument::formatText(unsigned Line, unsigned Length, + EditorConsumer &Consumer) { + auto SyntaxInfo = Impl.getSyntaxInfo(); + SourceFile &SF = SyntaxInfo->getSourceFile(); + SourceManager &SM = SyntaxInfo->getSourceManager(); -void SwiftEditorDocument::formatText(unsigned Line, unsigned Length, - EditorConsumer &Consumer) { - auto SyntaxInfo = Impl.getSyntaxInfo(); - SourceFile &SF = SyntaxInfo->getSourceFile(); - SourceManager &SM = SyntaxInfo->getSourceManager(); + LineRange inputRange = LineRange(Line, Length); + CodeFormatOptions Options = getFormatOptions(); + auto indented = reformat(inputRange, Options, SM, SF); - LineRange inputRange = LineRange(Line, Length); - CodeFormatOptions Options = getFormatOptions(); - auto indented = reformat(inputRange, Options, SM, SF); + LineRange LineRange = indented.first; + StringRef ModifiedText = indented.second; + Consumer.recordFormattedText(ModifiedText); + Consumer.recordAffectedLineRange(LineRange.startLine(), + LineRange.lineCount()); + } - LineRange LineRange = indented.first; - StringRef ModifiedText = indented.second; - Consumer.recordFormattedText(ModifiedText); - Consumer.recordAffectedLineRange(LineRange.startLine(), LineRange.lineCount()); -} + bool isReturningVoid(SourceManager & SM, CharSourceRange Range) { + if (Range.isInvalid()) + return false; + StringRef Text = SM.extractText(Range); + return "()" == Text || "Void" == Text; + } -bool isReturningVoid(SourceManager &SM, CharSourceRange Range) { - if (Range.isInvalid()) - return false; - StringRef Text = SM.extractText(Range); - return "()" == Text || "Void" == Text; -} + void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length, + EditorConsumer &Consumer) { + auto SyntaxInfo = Impl.getSyntaxInfo(); + SourceManager &SM = SyntaxInfo->getSourceManager(); + unsigned BufID = SyntaxInfo->getBufferID(); -void SwiftEditorDocument::expandPlaceholder(unsigned Offset, unsigned Length, - EditorConsumer &Consumer) { - auto SyntaxInfo = Impl.getSyntaxInfo(); - SourceManager &SM = SyntaxInfo->getSourceManager(); - unsigned BufID = SyntaxInfo->getBufferID(); + const unsigned PlaceholderStartLen = 2; + const unsigned PlaceholderEndLen = 2; - const unsigned PlaceholderStartLen = 2; - const unsigned PlaceholderEndLen = 2; + if (Length < (PlaceholderStartLen + PlaceholderEndLen)) { + Consumer.handleRequestError("Invalid Length parameter"); + return; + } - if (Length < (PlaceholderStartLen + PlaceholderEndLen)) { - Consumer.handleRequestError("Invalid Length parameter"); - return; - } + PlaceholderExpansionScanner Scanner(SM); + SourceFile &SF = SyntaxInfo->getSourceFile(); + + Scanner.scan( + SF, BufID, Offset, Length, + [&](Expr *Args, bool UseTrailingClosure, bool isWrappedWithBraces, + ArrayRef ClosureParams, + CharSourceRange ClosureReturnTypeRange) { + unsigned EffectiveOffset = Offset; + unsigned EffectiveLength = Length; + llvm::SmallString<128> ExpansionStr; + { + llvm::raw_svector_ostream OS(ExpansionStr); + if (UseTrailingClosure) { + assert(Args); + + if (isa(Args)) { + // There appears to be no other parameters in this call, so + // we'll expand replacement for trailing closure and cover call + // parens. For example: foo.bar(<#closure#>) turns into foo.bar + // <#closure#>. + EffectiveOffset = + SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID); + OS << " "; + } else { + auto *TupleE = cast(Args); + auto Elems = TupleE->getElements(); + assert(!Elems.empty()); + if (Elems.size() == 1) { + EffectiveOffset = + SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID); + OS << " "; + } else { + // Expand replacement range for trailing closure. + // For example: + // foo.bar(a, <#closure#>) turns into foo.bar(a) <#closure#>. + + // If the preceding token in the call is the leading parameter + // separator, we'll expand replacement to cover that. + assert(Elems.size() > 1); + SourceLoc BeforeLoc = Lexer::getLocForEndOfToken( + SM, Elems[Elems.size() - 2]->getEndLoc()); + EffectiveOffset = SM.getLocOffsetInBuffer(BeforeLoc, BufID); + OS << ") "; + } + } - PlaceholderExpansionScanner Scanner(SM); - SourceFile &SF = SyntaxInfo->getSourceFile(); - - Scanner.scan(SF, BufID, Offset, Length, - [&](Expr *Args, - bool UseTrailingClosure, bool isWrappedWithBraces, - ArrayRef ClosureParams, - CharSourceRange ClosureReturnTypeRange) { - - unsigned EffectiveOffset = Offset; - unsigned EffectiveLength = Length; - llvm::SmallString<128> ExpansionStr; - { - llvm::raw_svector_ostream OS(ExpansionStr); - if (UseTrailingClosure) { - assert(Args); - - if (isa(Args)) { - // There appears to be no other parameters in this call, so we'll - // expand replacement for trailing closure and cover call parens. - // For example: - // foo.bar(<#closure#>) turns into foo.bar <#closure#>. - EffectiveOffset = SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID); - OS << " "; - } else { - auto *TupleE = cast(Args); - auto Elems = TupleE->getElements(); - assert(!Elems.empty()); - if (Elems.size() == 1) { - EffectiveOffset = SM.getLocOffsetInBuffer(Args->getStartLoc(), BufID); - OS << " "; - } else { - // Expand replacement range for trailing closure. - // For example: - // foo.bar(a, <#closure#>) turns into foo.bar(a) <#closure#>. - - // If the preceding token in the call is the leading parameter - // separator, we'll expand replacement to cover that. - assert(Elems.size() > 1); - SourceLoc BeforeLoc = Lexer::getLocForEndOfToken(SM, - Elems[Elems.size()-2]->getEndLoc()); - EffectiveOffset = SM.getLocOffsetInBuffer(BeforeLoc, BufID); - OS << ") "; + unsigned End = SM.getLocOffsetInBuffer(Args->getEndLoc(), BufID); + EffectiveLength = (End + 1) - EffectiveOffset; } - } + // Trailing closure syntax handling will replace braces anyway. + bool printBraces = !isWrappedWithBraces || UseTrailingClosure; + if (printBraces) + OS << "{ "; + + bool ReturningVoid = isReturningVoid(SM, ClosureReturnTypeRange); + + bool HasSignature = + !ClosureParams.empty() || + (ClosureReturnTypeRange.isValid() && !ReturningVoid); + bool FirstParam = true; + if (HasSignature) + OS << "("; + for (auto &Param : ClosureParams) { + if (!FirstParam) + OS << ", "; + FirstParam = false; + if (Param.NameRange.isValid()) { + // If we have a parameter name, just output the name as is and + // skip the type. For example: + // <#(arg1: Int, arg2: Int)#> turns into (arg1, arg2). + OS << SM.extractText(Param.NameRange); + } else { + // If we only have the parameter type, output the type as a + // placeholder. For example: + // <#(Int, Int)#> turns into (<#Int#>, <#Int#>). + OS << "<#"; + OS << SM.extractText(Param.TypeRange); + OS << "#>"; + } + } + if (HasSignature) + OS << ") "; + if (ClosureReturnTypeRange.isValid()) { + auto ReturnTypeText = SM.extractText(ClosureReturnTypeRange); - unsigned End = SM.getLocOffsetInBuffer(Args->getEndLoc(), BufID); - EffectiveLength = (End + 1) - EffectiveOffset; - } - // Trailing closure syntax handling will replace braces anyway. - bool printBraces = !isWrappedWithBraces || UseTrailingClosure; - if (printBraces) - OS << "{ "; - - bool ReturningVoid = isReturningVoid(SM, ClosureReturnTypeRange); - - bool HasSignature = !ClosureParams.empty() || - (ClosureReturnTypeRange.isValid() && !ReturningVoid); - bool FirstParam = true; - if (HasSignature) - OS << "("; - for (auto &Param: ClosureParams) { - if (!FirstParam) - OS << ", "; - FirstParam = false; - if (Param.NameRange.isValid()) { - // If we have a parameter name, just output the name as is and skip - // the type. For example: - // <#(arg1: Int, arg2: Int)#> turns into (arg1, arg2). - OS << SM.extractText(Param.NameRange); - } - else { - // If we only have the parameter type, output the type as a - // placeholder. For example: - // <#(Int, Int)#> turns into (<#Int#>, <#Int#>). - OS << "<#"; - OS << SM.extractText(Param.TypeRange); - OS << "#>"; + // We need return type if it is not Void. + if (!ReturningVoid) { + OS << "-> "; + OS << ReturnTypeText << " "; + } + } + if (HasSignature) + OS << "in"; + OS << "\n" << getCodePlaceholder() << "\n"; + if (printBraces) + OS << "}"; } - } - if (HasSignature) - OS << ") "; - if (ClosureReturnTypeRange.isValid()) { - auto ReturnTypeText = SM.extractText(ClosureReturnTypeRange); - - // We need return type if it is not Void. - if (!ReturningVoid) { - OS << "-> "; - OS << ReturnTypeText << " "; + Consumer.handleSourceText(ExpansionStr); + Consumer.recordAffectedRange(EffectiveOffset, EffectiveLength); + }, + [&](EditorPlaceholderExpr *PHE) { + if (!PHE) + return false; + if (auto Ty = PHE->getTypeForExpansion()) { + std::string S; + llvm::raw_string_ostream OS(S); + Ty->print(OS); + Consumer.handleSourceText(OS.str()); + Consumer.recordAffectedRange(Offset, Length); + return true; } - } - if (HasSignature) - OS << "in"; - OS << "\n" << getCodePlaceholder() << "\n"; - if (printBraces) - OS << "}"; - } - Consumer.handleSourceText(ExpansionStr); - Consumer.recordAffectedRange(EffectiveOffset, EffectiveLength); - }, [&](EditorPlaceholderExpr *PHE) { - if (!PHE) - return false; - if (auto Ty = PHE->getTypeForExpansion()) { - std::string S; - llvm::raw_string_ostream OS(S); - Ty->print(OS); - Consumer.handleSourceText(OS.str()); - Consumer.recordAffectedRange(Offset, Length); - return true; - } - return false; - }); -} - -ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const { - llvm::sys::ScopedLock L(Impl.AccessMtx); - return Impl.EditableBuffer->getSnapshot(); -} + return false; + }); + } -void SwiftEditorDocument::reportDocumentStructure(SourceFile &SrcFile, - EditorConsumer &Consumer) { - ide::SyntaxModelContext ModelContext(SrcFile); - SwiftDocumentStructureWalker Walker(SrcFile.getASTContext().SourceMgr, - *SrcFile.getBufferID(), - Consumer); - ModelContext.walk(Walker); -} + ImmutableTextSnapshotRef SwiftEditorDocument::getLatestSnapshot() const { + llvm::sys::ScopedLock L(Impl.AccessMtx); + return Impl.EditableBuffer->getSnapshot(); + } -//===----------------------------------------------------------------------===// -// EditorOpen -//===----------------------------------------------------------------------===// + void SwiftEditorDocument::reportDocumentStructure(SourceFile & SrcFile, + EditorConsumer & Consumer) { + ide::SyntaxModelContext ModelContext(SrcFile); + SwiftDocumentStructureWalker Walker(SrcFile.getASTContext().SourceMgr, + *SrcFile.getBufferID(), Consumer); + ModelContext.walk(Walker); + } -void SwiftLangSupport::editorOpen( - StringRef Name, llvm::MemoryBuffer *Buf, EditorConsumer &Consumer, - ArrayRef Args, Optional vfsOptions) { + //===----------------------------------------------------------------------===// + // EditorOpen + //===----------------------------------------------------------------------===// - std::string error; - // Do not provide primaryFile so that opening an existing document will - // reinitialize the filesystem instead of keeping the old one. - auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); - if (!fileSystem) - return Consumer.handleRequestError(error.c_str()); + void SwiftLangSupport::editorOpen( + StringRef Name, llvm::MemoryBuffer * Buf, EditorConsumer & Consumer, + ArrayRef Args, Optional vfsOptions) { - ImmutableTextSnapshotRef Snapshot = nullptr; - auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); - if (!EditorDoc) { - EditorDoc = new SwiftEditorDocument(Name, *this, fileSystem); - Snapshot = EditorDoc->initializeText( - Buf, Args, Consumer.needsSemanticInfo(), fileSystem); - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); - if (EditorDocuments->getOrUpdate(Name, *this, EditorDoc)) { - // Document already exists, re-initialize it. This should only happen - // if we get OPEN request while the previous document is not closed. - LOG_WARN_FUNC("Document already exists in editorOpen(..): " << Name); - Snapshot = nullptr; + std::string error; + // Do not provide primaryFile so that opening an existing document will + // reinitialize the filesystem instead of keeping the old one. + auto fileSystem = getFileSystem(vfsOptions, /*primaryFile=*/None, error); + if (!fileSystem) + return Consumer.handleRequestError(error.c_str()); + + ImmutableTextSnapshotRef Snapshot = nullptr; + auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); + if (!EditorDoc) { + EditorDoc = new SwiftEditorDocument(Name, *this, fileSystem); + Snapshot = EditorDoc->initializeText( + Buf, Args, Consumer.needsSemanticInfo(), fileSystem); + EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); + if (EditorDocuments->getOrUpdate(Name, *this, EditorDoc)) { + // Document already exists, re-initialize it. This should only happen + // if we get OPEN request while the previous document is not closed. + LOG_WARN_FUNC("Document already exists in editorOpen(..): " << Name); + Snapshot = nullptr; + } + auto numOpen = ++Stats->numOpenDocs; + Stats->maxOpenDocs.updateMax(numOpen); } - auto numOpen = ++Stats->numOpenDocs; - Stats->maxOpenDocs.updateMax(numOpen); - } - - if (!Snapshot) { - Snapshot = EditorDoc->initializeText( - Buf, Args, Consumer.needsSemanticInfo(), fileSystem); - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); - } - if (Consumer.needsSemanticInfo()) { - EditorDoc->updateSemaInfo(); - } - - EditorDoc->readSyntaxInfo(Consumer); - EditorDoc->readSemanticInfo(Snapshot, Consumer); + if (!Snapshot) { + Snapshot = EditorDoc->initializeText( + Buf, Args, Consumer.needsSemanticInfo(), fileSystem); + EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled()); + } - if (Consumer.syntaxTreeEnabled()) { - assert(EditorDoc->getSyntaxTree().hasValue()); - std::unordered_set ReusedNodeIds; - Consumer.handleSyntaxTree(EditorDoc->getSyntaxTree().getValue(), - ReusedNodeIds); - } -} + if (Consumer.needsSemanticInfo()) { + EditorDoc->updateSemaInfo(); + } -//===----------------------------------------------------------------------===// -// EditorClose -//===----------------------------------------------------------------------===// + EditorDoc->readSyntaxInfo(Consumer); + EditorDoc->readSemanticInfo(Snapshot, Consumer); -void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) { - auto Removed = EditorDocuments->remove(Name); - if (Removed) { - --Stats->numOpenDocs; - } else { - IFaceGenContexts.remove(Name); + if (Consumer.syntaxTreeEnabled()) { + assert(EditorDoc->getSyntaxTree().hasValue()); + std::unordered_set ReusedNodeIds; + Consumer.handleSyntaxTree(EditorDoc->getSyntaxTree().getValue(), + ReusedNodeIds); + } } - if (Removed && RemoveCache) - Removed->removeCachedAST(); - // FIXME: Report error if Name did not apply to anything ? -} + //===----------------------------------------------------------------------===// + // EditorClose + //===----------------------------------------------------------------------===// + void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) { + auto Removed = EditorDocuments->remove(Name); + if (Removed) { + --Stats->numOpenDocs; + } else { + IFaceGenContexts.remove(Name); + } + + if (Removed && RemoveCache) + Removed->removeCachedAST(); + // FIXME: Report error if Name did not apply to anything ? + } + + //===----------------------------------------------------------------------===// + // EditorReplaceText + //===----------------------------------------------------------------------===// + + void verifyIncrementalParse(SwiftEditorDocumentRef EditorDoc, + unsigned EditOffset, unsigned EditLength, + StringRef PreEditText, StringRef ReplaceText) { + swift::json::Output::UserInfoMap JsonUserInfo; + JsonUserInfo[swift::json::DontSerializeNodeIdsUserInfoKey] = + reinterpret_cast(true); + + // Dump the incremental syntax tree + std::string IncrTreeString; + llvm::raw_string_ostream IncrTreeStream(IncrTreeString); + swift::json::Output IncrTreeOutput(IncrTreeStream, JsonUserInfo); + IncrTreeOutput << *EditorDoc->getSyntaxTree()->getRaw(); + + // Reparse the file from scratch + CompilerInvocation Invocation; + Invocation.getLangOptions().BuildSyntaxTree = true; + std::vector Args; + SwiftDocumentSyntaxInfo ScratchSyntaxInfo(Invocation, + EditorDoc->getLatestSnapshot(), + Args, EditorDoc->getFilePath()); + ScratchSyntaxInfo.parse(); + + // Dump the from-scratch syntax tree + std::string FromScratchTreeString; + llvm::raw_string_ostream ScratchTreeStream(FromScratchTreeString); + swift::json::Output ScratchTreeOutput(ScratchTreeStream, JsonUserInfo); + auto SyntaxRoot = ScratchSyntaxInfo.getSourceFile().getSyntaxRoot(); + ScratchTreeOutput << *SyntaxRoot.getRaw(); + + // If the serialized format of the two trees doesn't match incremental + // parsing we have found an error. + if (IncrTreeStream.str().compare(ScratchTreeStream.str())) { + LOG_SECTION("Incremental Parsing", Warning) { + Log->getOS() + << "Incremental parsing different to from scratch parsing\n"; + Log->getOS() << "Edit was " << EditOffset << "-" + << (EditOffset + EditLength) << "='" << ReplaceText << "'" + << " pre-edit-text: '" << PreEditText << "'\n"; + + SmallString<32> DirectoryName; + if (llvm::sys::fs::createUniqueDirectory( + "SourceKit-IncrementalParsing-Inconsistency", DirectoryName)) { + Log->getOS() << "Failed to create log directory\n"; + } -//===----------------------------------------------------------------------===// -// EditorReplaceText -//===----------------------------------------------------------------------===// - -void verifyIncrementalParse(SwiftEditorDocumentRef EditorDoc, - unsigned EditOffset, unsigned EditLength, - StringRef PreEditText, StringRef ReplaceText) { - swift::json::Output::UserInfoMap JsonUserInfo; - JsonUserInfo[swift::json::DontSerializeNodeIdsUserInfoKey] = - reinterpret_cast(true); - - // Dump the incremental syntax tree - std::string IncrTreeString; - llvm::raw_string_ostream IncrTreeStream(IncrTreeString); - swift::json::Output IncrTreeOutput(IncrTreeStream, JsonUserInfo); - IncrTreeOutput << *EditorDoc->getSyntaxTree()->getRaw(); - - // Reparse the file from scratch - CompilerInvocation Invocation; - Invocation.getLangOptions().BuildSyntaxTree = true; - std::vector Args; - SwiftDocumentSyntaxInfo ScratchSyntaxInfo(Invocation, - EditorDoc->getLatestSnapshot(), - Args, EditorDoc->getFilePath()); - ScratchSyntaxInfo.parse(); - - // Dump the from-scratch syntax tree - std::string FromScratchTreeString; - llvm::raw_string_ostream ScratchTreeStream(FromScratchTreeString); - swift::json::Output ScratchTreeOutput(ScratchTreeStream, JsonUserInfo); - auto SyntaxRoot = ScratchSyntaxInfo.getSourceFile().getSyntaxRoot(); - ScratchTreeOutput << *SyntaxRoot.getRaw(); - - // If the serialized format of the two trees doesn't match incremental parsing - // we have found an error. - if (IncrTreeStream.str().compare(ScratchTreeStream.str())) { - LOG_SECTION("Incremental Parsing", Warning) { - Log->getOS() << "Incremental parsing different to from scratch parsing\n"; - Log->getOS() << "Edit was " << EditOffset << "-" - << (EditOffset + EditLength) << "='" << ReplaceText << "'" - << " pre-edit-text: '" << PreEditText << "'\n"; - - SmallString<32> DirectoryName; - if (llvm::sys::fs::createUniqueDirectory( - "SourceKit-IncrementalParsing-Inconsistency", DirectoryName)) { - Log->getOS() << "Failed to create log directory\n"; - } + std::error_code ErrorCode; + + // Write the incremental syntax tree + auto IncrTreeFilename = DirectoryName + "/incrementalTree.json"; + llvm::raw_fd_ostream IncrementalFilestream( + IncrTreeFilename.str(), ErrorCode, + llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); + IncrementalFilestream << IncrTreeStream.str(); + if (ErrorCode) { + Log->getOS() << "Failed to write incremental syntax tree to " + << IncrTreeFilename << "(error code " + << ErrorCode.value() << ": " << ErrorCode.message() + << ")\n"; + } else { + Log->getOS() << "Incremental syntax tree written to " + << IncrTreeFilename << '\n'; + } - std::error_code ErrorCode; - - // Write the incremental syntax tree - auto IncrTreeFilename = DirectoryName + "/incrementalTree.json"; - llvm::raw_fd_ostream IncrementalFilestream( - IncrTreeFilename.str(), ErrorCode, - llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); - IncrementalFilestream << IncrTreeStream.str(); - if (ErrorCode) { - Log->getOS() << "Failed to write incremental syntax tree to " - << IncrTreeFilename << "(error code " << ErrorCode.value() - << ": " << ErrorCode.message() << ")\n"; - } else { - Log->getOS() << "Incremental syntax tree written to " - << IncrTreeFilename << '\n'; - } + // Write from-scratch syntax tree + auto ScratchTreeFilename = DirectoryName + "/fromScratchTree.json"; + llvm::raw_fd_ostream ScratchTreeFilestream( + ScratchTreeFilename.str(), ErrorCode, + llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); + ScratchTreeFilestream << ScratchTreeStream.str(); + if (ErrorCode) { + Log->getOS() << "Failed to write from-scratch syntax tree to " + << ScratchTreeFilename << "(error code " + << ErrorCode.value() << ": " << ErrorCode.message() + << ")\n"; + } else { + Log->getOS() << "From-scratch syntax tree written to " + << ScratchTreeFilename << '\n'; + } - // Write from-scratch syntax tree - auto ScratchTreeFilename = DirectoryName + "/fromScratchTree.json"; - llvm::raw_fd_ostream ScratchTreeFilestream( - ScratchTreeFilename.str(), ErrorCode, - llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); - ScratchTreeFilestream << ScratchTreeStream.str(); - if (ErrorCode) { - Log->getOS() << "Failed to write from-scratch syntax tree to " - << ScratchTreeFilename << "(error code " - << ErrorCode.value() << ": " << ErrorCode.message() - << ")\n"; - } else { - Log->getOS() << "From-scratch syntax tree written to " - << ScratchTreeFilename << '\n'; + // Write source file + auto SourceFilename = DirectoryName + "/postEditSource.swift"; + llvm::raw_fd_ostream SourceFilestream(SourceFilename.str(), ErrorCode, + llvm::sys::fs::FA_Read | + llvm::sys::fs::FA_Write); + auto FileBuffer = EditorDoc->getLatestSnapshot()->getBuffer(); + SourceFilestream << FileBuffer->getText(); } - - // Write source file - auto SourceFilename = DirectoryName + "/postEditSource.swift"; - llvm::raw_fd_ostream SourceFilestream(SourceFilename.str(), ErrorCode, - llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); - auto FileBuffer = EditorDoc->getLatestSnapshot()->getBuffer(); - SourceFilestream << FileBuffer->getText(); } } -} - -void SwiftLangSupport::editorReplaceText(StringRef Name, - llvm::MemoryBuffer *Buf, - unsigned Offset, unsigned Length, - EditorConsumer &Consumer) { - bool LogReuseRegions = ::getenv("SOURCEKIT_LOG_INCREMENTAL_REUSE_REGIONS"); - bool ValidateSyntaxTree = ::getenv("SOURCEKIT_INCREMENTAL_PARSE_VALIDATION"); - auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); - if (!EditorDoc) { - Consumer.handleRequestError("No associated Editor Document"); - return; - } + void SwiftLangSupport::editorReplaceText( + StringRef Name, llvm::MemoryBuffer * Buf, unsigned Offset, + unsigned Length, EditorConsumer &Consumer) { + bool LogReuseRegions = ::getenv("SOURCEKIT_LOG_INCREMENTAL_REUSE_REGIONS"); + bool ValidateSyntaxTree = + ::getenv("SOURCEKIT_INCREMENTAL_PARSE_VALIDATION"); - ImmutableTextSnapshotRef Snapshot; - if (Length != 0 || Buf->getBufferSize() != 0) { - std::string PreEditText; - if (ValidateSyntaxTree) { - auto CurBuffer = EditorDoc->getLatestSnapshot()->getBuffer(); - auto BufferStart = CurBuffer->getInternalBuffer()->getBufferStart(); - StringRef PreEditTextRef(BufferStart + Offset, Length); - PreEditText = PreEditTextRef.str(); - } - std::string error; - Snapshot = EditorDoc->replaceText(Offset, Length, Buf, - Consumer.needsSemanticInfo(), error); - if (!Snapshot) { - assert(error.size()); - Consumer.handleRequestError(error.c_str()); + auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); + if (!EditorDoc) { + Consumer.handleRequestError("No associated Editor Document"); return; } - llvm::Optional SyntaxCache = llvm::None; - if (EditorDoc->getSyntaxTree().hasValue()) { - SyntaxCache.emplace(EditorDoc->getSyntaxTree().getValue()); - SyntaxCache->addEdit(Offset, Offset + Length, Buf->getBufferSize()); - } + ImmutableTextSnapshotRef Snapshot; + if (Length != 0 || Buf->getBufferSize() != 0) { + std::string PreEditText; + if (ValidateSyntaxTree) { + auto CurBuffer = EditorDoc->getLatestSnapshot()->getBuffer(); + auto BufferStart = CurBuffer->getInternalBuffer()->getBufferStart(); + StringRef PreEditTextRef(BufferStart + Offset, Length); + PreEditText = PreEditTextRef.str(); + } + std::string error; + Snapshot = EditorDoc->replaceText(Offset, Length, Buf, + Consumer.needsSemanticInfo(), error); + if (!Snapshot) { + assert(error.size()); + Consumer.handleRequestError(error.c_str()); + return; + } - SyntaxParsingCache *SyntaxCachePtr = nullptr; - if (SyntaxCache.hasValue()) { - SyntaxCachePtr = SyntaxCache.getPointer(); - } - EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled(), - SyntaxCachePtr); - EditorDoc->readSyntaxInfo(Consumer); + llvm::Optional SyntaxCache = llvm::None; + if (EditorDoc->getSyntaxTree().hasValue()) { + SyntaxCache.emplace(EditorDoc->getSyntaxTree().getValue()); + SyntaxCache->addEdit(Offset, Offset + Length, Buf->getBufferSize()); + } - // Log reuse information - if (SyntaxCache.hasValue() && LogReuseRegions) { - auto &SyntaxTree = EditorDoc->getSyntaxTree(); - auto ReuseRegions = SyntaxCache->getReusedRegions(*SyntaxTree); - LOG_SECTION("SyntaxCache", InfoHighPrio) { - Log->getOS() << "Reused "; - - bool FirstIteration = true; - for (auto ReuseRegion : ReuseRegions) { - if (!FirstIteration) { - Log->getOS() << ", "; - } else { - FirstIteration = false; + SyntaxParsingCache *SyntaxCachePtr = nullptr; + if (SyntaxCache.hasValue()) { + SyntaxCachePtr = SyntaxCache.getPointer(); + } + EditorDoc->parse(Snapshot, *this, Consumer.syntaxTreeEnabled(), + SyntaxCachePtr); + EditorDoc->readSyntaxInfo(Consumer); + + // Log reuse information + if (SyntaxCache.hasValue() && LogReuseRegions) { + auto &SyntaxTree = EditorDoc->getSyntaxTree(); + auto ReuseRegions = SyntaxCache->getReusedRegions(*SyntaxTree); + LOG_SECTION("SyntaxCache", InfoHighPrio) { + Log->getOS() << "Reused "; + + bool FirstIteration = true; + for (auto ReuseRegion : ReuseRegions) { + if (!FirstIteration) { + Log->getOS() << ", "; + } else { + FirstIteration = false; + } + + Log->getOS() << ReuseRegion.Start << " - " << ReuseRegion.End; } + } + } - Log->getOS() << ReuseRegion.Start << " - " << ReuseRegion.End; + if (Consumer.syntaxTreeEnabled()) { + std::unordered_set ReusedNodeIds; + if (SyntaxCache.hasValue()) { + auto &ReusedVector = SyntaxCache->getReusedNodeIds(); + ReusedNodeIds = std::unordered_set(ReusedVector.begin(), + ReusedVector.end()); } + Consumer.handleSyntaxTree(EditorDoc->getSyntaxTree().getValue(), + ReusedNodeIds); } - } - if (Consumer.syntaxTreeEnabled()) { - std::unordered_set ReusedNodeIds; - if (SyntaxCache.hasValue()) { - auto &ReusedVector = SyntaxCache->getReusedNodeIds(); - ReusedNodeIds = std::unordered_set(ReusedVector.begin(), - ReusedVector.end()); + if (ValidateSyntaxTree) { + verifyIncrementalParse(EditorDoc, Offset, Length, PreEditText, + Buf->getBuffer()); } - Consumer.handleSyntaxTree(EditorDoc->getSyntaxTree().getValue(), - ReusedNodeIds); + } else { + Snapshot = EditorDoc->getLatestSnapshot(); } - if (ValidateSyntaxTree) { - verifyIncrementalParse(EditorDoc, Offset, Length, PreEditText, - Buf->getBuffer()); - } - } else { - Snapshot = EditorDoc->getLatestSnapshot(); + EditorDoc->readSemanticInfo(Snapshot, Consumer); } - EditorDoc->readSemanticInfo(Snapshot, Consumer); -} + //===----------------------------------------------------------------------===// + // EditorFormatText + //===----------------------------------------------------------------------===// + void SwiftLangSupport::editorApplyFormatOptions( + StringRef Name, OptionsDictionary & FmtOptions) { + auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); + if (EditorDoc) + EditorDoc->applyFormatOptions(FmtOptions); + } + void SwiftLangSupport::editorFormatText(StringRef Name, unsigned Line, + unsigned Length, + EditorConsumer &Consumer) { + auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); + if (!EditorDoc) { + Consumer.handleRequestError("No associated Editor Document"); + return; + } -//===----------------------------------------------------------------------===// -// EditorFormatText -//===----------------------------------------------------------------------===// -void SwiftLangSupport::editorApplyFormatOptions(StringRef Name, - OptionsDictionary &FmtOptions) { - auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); - if (EditorDoc) - EditorDoc->applyFormatOptions(FmtOptions); -} + if (!EditorDoc->hasUpToDateAST()) { + // An up-to-date AST is needed for formatting. If it does not exist, fall + // back to a full reparse of the file + EditorDoc->parse(EditorDoc->getLatestSnapshot(), *this, + /*BuildSyntaxTree=*/true); + } -void SwiftLangSupport::editorFormatText(StringRef Name, unsigned Line, - unsigned Length, - EditorConsumer &Consumer) { - auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); - if (!EditorDoc) { - Consumer.handleRequestError("No associated Editor Document"); - return; + EditorDoc->formatText(Line, Length, Consumer); } - if (!EditorDoc->hasUpToDateAST()) { - // An up-to-date AST is needed for formatting. If it does not exist, fall - // back to a full reparse of the file - EditorDoc->parse(EditorDoc->getLatestSnapshot(), *this, - /*BuildSyntaxTree=*/true); + void SwiftLangSupport::editorExtractTextFromComment( + StringRef Source, EditorConsumer & Consumer) { + Consumer.handleSourceText(extractPlainTextFromComment(Source)); } - EditorDoc->formatText(Line, Length, Consumer); -} - -void SwiftLangSupport::editorExtractTextFromComment(StringRef Source, - EditorConsumer &Consumer) { - Consumer.handleSourceText(extractPlainTextFromComment(Source)); -} - -void SwiftLangSupport::editorConvertMarkupToXML(StringRef Source, - EditorConsumer &Consumer) { - std::string Result; - llvm::raw_string_ostream OS(Result); - if (convertMarkupToXML(Source, OS)) { - Consumer.handleRequestError("Conversion failed."); - return; + void SwiftLangSupport::editorConvertMarkupToXML(StringRef Source, + EditorConsumer & Consumer) { + std::string Result; + llvm::raw_string_ostream OS(Result); + if (convertMarkupToXML(Source, OS)) { + Consumer.handleRequestError("Conversion failed."); + return; + } + Consumer.handleSourceText(Result); } - Consumer.handleSourceText(Result); -} -//===----------------------------------------------------------------------===// -// EditorExpandPlaceholder -//===----------------------------------------------------------------------===// -void SwiftLangSupport::editorExpandPlaceholder(StringRef Name, unsigned Offset, - unsigned Length, - EditorConsumer &Consumer) { - auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); - if (!EditorDoc) { - Consumer.handleRequestError("No associated Editor Document"); - return; - } + //===----------------------------------------------------------------------===// + // EditorExpandPlaceholder + //===----------------------------------------------------------------------===// + void SwiftLangSupport::editorExpandPlaceholder( + StringRef Name, unsigned Offset, unsigned Length, + EditorConsumer &Consumer) { + auto EditorDoc = EditorDocuments->getByUnresolvedName(Name); + if (!EditorDoc) { + Consumer.handleRequestError("No associated Editor Document"); + return; + } - EditorDoc->expandPlaceholder(Offset, Length, Consumer); -} + EditorDoc->expandPlaceholder(Offset, Length, Consumer); + } diff --git a/utils/swift-autocomplete.bash b/utils/swift-autocomplete.bash index 7e48fd7d5fa85..d79094d0c64c4 100644 --- a/utils/swift-autocomplete.bash +++ b/utils/swift-autocomplete.bash @@ -68,6 +68,7 @@ _swift_complete() -sil-verify-without-invalidation \ -sil-inline-test-threshold \ -sil-inline-test \ + -sil-inline-never-functions \ -sroa-args-remove-dead-args-after \ -ml \ -sil-print-escapes \