From 0b5157db53a3bd1988d27820491bbf02cd1a1278 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Wed, 15 Jan 2020 13:42:06 +0100 Subject: [PATCH 001/374] First commit on the branch --- llvm/docs/ReleaseNotes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index 96331a76f266..be9b704d9bf3 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -111,6 +111,8 @@ Changes to the LLVM IR Changes to building LLVM ------------------------ +... + Changes to the ARM Backend -------------------------- From c4a134a5107c1926262ee887057036ca53aa7265 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 15 Jan 2020 10:45:02 -0500 Subject: [PATCH 002/374] Replace CLANG_SPAWN_CC1 env var with a driver mode flag Flags are clang's default UI is flags. We can have an env var in addition to that, but in D69825 nobody has yet mentioned why this needs an env var, so omit it for now. If someone needs to set the flag via env var, the existing CCC_OVERRIDE_OPTIONS mechanism works for it (set CCC_OVERRIDE_OPTIONS=+-fno-integrated-cc1 for example). Also mention the cc1-in-process change in the release notes. Also spruce up the test a bit so it actually tests something :) Differential Revision: https://reviews.llvm.org/D72769 (cherry picked from commit 8e5018e990b701391e6c33ba85b012343df67272) --- clang/docs/ReleaseNotes.rst | 5 +++ clang/include/clang/Driver/Options.td | 8 +++++ clang/lib/Driver/Driver.cpp | 4 +++ clang/test/Driver/cc1-spawnprocess.c | 26 ++++++++++++--- clang/tools/driver/driver.cpp | 47 +++++++++++++-------------- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e131e4f12780..881dcb78b663 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -46,6 +46,11 @@ sections with improvements to Clang's support for those languages. Major New Features ------------------ +- clang used to run the actual compilation in a subprocess ("clang -cc1"). + Now compilations are done in-process by default. ``-fno-integrated-cc1`` + restores the former behavior. The ``-v`` and ``-###`` flags will print + "(in-process)" when compilations are done in-process. + - ... Improvements to Clang's diagnostics diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 1218172fd5b6..abfa767afea8 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2855,6 +2855,14 @@ def fintegrated_as : Flag<["-"], "fintegrated-as">, Flags<[DriverOption]>, def fno_integrated_as : Flag<["-"], "fno-integrated-as">, Flags<[CC1Option, DriverOption]>, Group, HelpText<"Disable the integrated assembler">; + +def fintegrated_cc1 : Flag<["-"], "fintegrated-cc1">, + Flags<[CoreOption, DriverOption]>, Group, + HelpText<"Run cc1 in-process">; +def fno_integrated_cc1 : Flag<["-"], "fno-integrated-cc1">, + Flags<[CoreOption, DriverOption]>, Group, + HelpText<"Spawn a separate process for each cc1">; + def : Flag<["-"], "integrated-as">, Alias, Flags<[DriverOption]>; def : Flag<["-"], "no-integrated-as">, Alias, Flags<[CC1Option, DriverOption]>; diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index e718b8366df0..7ee3caaa0bce 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -1035,6 +1035,10 @@ Compilation *Driver::BuildCompilation(ArrayRef ArgList) { // -no-canonical-prefixes is used very early in main. Args.ClaimAllArgs(options::OPT_no_canonical_prefixes); + // f(no-)integated-cc1 is also used very early in main. + Args.ClaimAllArgs(options::OPT_fintegrated_cc1); + Args.ClaimAllArgs(options::OPT_fno_integrated_cc1); + // Ignore -pipe. Args.ClaimAllArgs(options::OPT_pipe); diff --git a/clang/test/Driver/cc1-spawnprocess.c b/clang/test/Driver/cc1-spawnprocess.c index c5c16ce8513f..624593895961 100644 --- a/clang/test/Driver/cc1-spawnprocess.c +++ b/clang/test/Driver/cc1-spawnprocess.c @@ -1,4 +1,22 @@ -// RUN: env CLANG_SPAWN_CC1= %clang -S %s -o /dev/null -// RUN: env CLANG_SPAWN_CC1=0 %clang -S %s -o /dev/null -// RUN: env CLANG_SPAWN_CC1=1 %clang -S %s -o /dev/null -// RUN: env CLANG_SPAWN_CC1=test not %clang -S %s -o /dev/null +// RUN: %clang -fintegrated-cc1 -### %s 2>&1 | FileCheck %s --check-prefix=YES +// RUN: %clang -fno-integrated-cc1 -### %s 2>&1 | FileCheck %s --check-prefix=NO + +// RUN: %clang -fintegrated-cc1 -fno-integrated-cc1 -### %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NO +// RUN: %clang -fno-integrated-cc1 -fintegrated-cc1 -### %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=YES + +// RUN: %clang_cl -fintegrated-cc1 -### -- %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=YES +// RUN: %clang_cl -fno-integrated-cc1 -### -- %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NO + +// RUN: env CCC_OVERRIDE_OPTIONS=+-fintegrated-cc1 \ +// RUN: %clang -fintegrated-cc1 -### %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=YES +// RUN: env CCC_OVERRIDE_OPTIONS=+-fno-integrated-cc1 \ +// RUN: %clang -fintegrated-cc1 -### %s 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NO + +// YES: (in-process) +// NO-NOT: (in-process) diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index 4cdf8015b1bf..39f6c7f62be4 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -258,27 +258,6 @@ static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); if (TheDriver.CCLogDiagnostics) TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); - - // Whether the cc1 tool should be called inside the current process, or if we - // should spawn a new clang process (old behavior). - // Not having an additional process saves some execution time of Windows, - // and makes debugging easier. - bool UseNewCC1Process = CLANG_SPAWN_CC1; - - StringRef SpawnCC1Str = ::getenv("CLANG_SPAWN_CC1"); - if (!SpawnCC1Str.empty()) { - if (SpawnCC1Str != "0" && SpawnCC1Str != "1") { - llvm::errs() << "error: the value of the environment variable " - "CLANG_SPAWN_CC1 must be either 0 or 1.\n"; - ::exit(1); - } - UseNewCC1Process = SpawnCC1Str[0] - '0'; - } - if (!UseNewCC1Process) { - TheDriver.CC1Main = &ExecuteCC1Tool; - // Ensure the CC1Command actually catches cc1 crashes - llvm::CrashRecoveryContext::Enable(); - } } static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, @@ -294,7 +273,7 @@ static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, // This lets us create the DiagnosticsEngine with a properly-filled-out // DiagnosticOptions instance. static DiagnosticOptions * -CreateAndPopulateDiagOpts(ArrayRef argv) { +CreateAndPopulateDiagOpts(ArrayRef argv, bool &UseNewCC1Process) { auto *DiagOpts = new DiagnosticOptions; unsigned MissingArgIndex, MissingArgCount; InputArgList Args = getDriverOptTable().ParseArgs( @@ -303,6 +282,12 @@ CreateAndPopulateDiagOpts(ArrayRef argv) { // Any errors that would be diagnosed here will also be diagnosed later, // when the DiagnosticsEngine actually exists. (void)ParseDiagnosticArgs(*DiagOpts, Args); + + UseNewCC1Process = + Args.hasFlag(clang::driver::options::OPT_fno_integrated_cc1, + clang::driver::options::OPT_fintegrated_cc1, + /*Default=*/CLANG_SPAWN_CC1); + return DiagOpts; } @@ -330,7 +315,7 @@ static void SetInstallDir(SmallVectorImpl &argv, static int ExecuteCC1Tool(ArrayRef argv) { // If we call the cc1 tool from the clangDriver library (through - // Driver::CC1Main), we need to cleanup the options usage count. The options + // Driver::CC1Main), we need to clean up the options usage count. The options // are currently global, and they might have been used previously by the // driver. llvm::cl::ResetAllOptionOccurrences(); @@ -413,6 +398,8 @@ int main(int argc_, const char **argv_) { return ExecuteCC1Tool(argv); } + // Handle options that need handling before the real command line parsing in + // Driver::BuildCompilation() bool CanonicalPrefixes = true; for (int i = 1, size = argv.size(); i < size; ++i) { // Skip end-of-line response file markers @@ -457,8 +444,14 @@ int main(int argc_, const char **argv_) { std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes); + // Whether the cc1 tool should be called inside the current process, or if we + // should spawn a new clang subprocess (old behavior). + // Not having an additional process saves some execution time of Windows, + // and makes debugging and profiling easier. + bool UseNewCC1Process; + IntrusiveRefCntPtr DiagOpts = - CreateAndPopulateDiagOpts(argv); + CreateAndPopulateDiagOpts(argv, UseNewCC1Process); TextDiagnosticPrinter *DiagClient = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); @@ -486,6 +479,12 @@ int main(int argc_, const char **argv_) { SetBackdoorDriverOutputsFromEnvVars(TheDriver); + if (!UseNewCC1Process) { + TheDriver.CC1Main = &ExecuteCC1Tool; + // Ensure the CC1Command actually catches cc1 crashes + llvm::CrashRecoveryContext::Enable(); + } + std::unique_ptr C(TheDriver.BuildCompilation(argv)); int Res = 1; if (C && !C->containsError()) { From ac446302ca4145cdc89f377c0c364c29ee303be5 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Wed, 15 Jan 2020 17:51:51 +0100 Subject: [PATCH 003/374] Revert "[mlir] Create a gpu.module operation for the GPU Dialect." This reverts commit 4624a1e8ac8a3f69cc887403b976f538f587744a. Causing problems downstream. (cherry picked from commit 0133cc60e4e230ee2c176c23eff5aa2f4ee17a75) --- .../mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h | 11 +--- .../mlir/Conversion/GPUToNVVM/GPUToNVVMPass.h | 10 +--- mlir/include/mlir/Dialect/GPU/GPUOps.td | 52 ------------------ .../GPUToCUDA/ConvertKernelFuncToCubin.cpp | 21 ++++--- .../ConvertLaunchFuncToCudaCalls.cpp | 13 +++-- .../GPUToNVVM/LowerGpuOpsToNVVMOps.cpp | 21 +++---- mlir/lib/Conversion/GPUToSPIRV/CMakeLists.txt | 7 --- .../GPUToSPIRV/ConvertGPUToSPIRV.cpp | 55 ++++++++++++++----- .../GPUToSPIRV/ConvertGPUToSPIRVPass.cpp | 15 +++-- mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.td | 22 -------- mlir/lib/Dialect/GPU/IR/GPUDialect.cpp | 55 ++++--------------- .../GPU/Transforms/KernelOutlining.cpp | 22 +++----- .../GPUToCUDA/lower-launch-func-to-cuda.mlir | 2 +- .../GPUToCUDA/lower-nvvm-kernel-to-cubin.mlir | 6 +- .../Conversion/GPUToNVVM/gpu-to-nvvm.mlir | 20 +++---- .../GPUToNVVM/memory-attrbution.mlir | 8 +-- mlir/test/Conversion/GPUToSPIRV/builtins.mlir | 12 ++-- .../Conversion/GPUToSPIRV/load-store.mlir | 2 +- mlir/test/Conversion/GPUToSPIRV/loop.mlir | 2 +- mlir/test/Conversion/GPUToSPIRV/simple.mlir | 2 +- mlir/test/Dialect/GPU/invalid.mlir | 11 ++-- mlir/test/Dialect/GPU/ops.mlir | 2 +- mlir/test/Dialect/GPU/outlining.mlir | 2 +- .../mlir-cuda-runner/mlir-cuda-runner.cpp | 2 +- 24 files changed, 140 insertions(+), 235 deletions(-) delete mode 100644 mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.td diff --git a/mlir/include/mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h b/mlir/include/mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h index f61e40ef5f96..4eb6379adf6e 100644 --- a/mlir/include/mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h +++ b/mlir/include/mlir/Conversion/GPUToCUDA/GPUToCUDAPass.h @@ -19,17 +19,12 @@ namespace mlir { class Location; class ModuleOp; -template -class OpPassBase; - -namespace gpu { -class GPUModuleOp; -} // namespace gpu - namespace LLVM { class LLVMDialect; } // namespace LLVM +template class OpPassBase; + using OwnedCubin = std::unique_ptr>; using CubinGenerator = std::function; @@ -43,7 +38,7 @@ using CubinGenerator = /// attached as a string attribute named 'nvvm.cubin' to the kernel function. /// After the transformation, the body of the kernel function is removed (i.e., /// it is turned into a declaration). -std::unique_ptr> +std::unique_ptr> createConvertGPUKernelToCubinPass(CubinGenerator cubinGenerator); /// Creates a pass to convert a gpu.launch_func operation into a sequence of diff --git a/mlir/include/mlir/Conversion/GPUToNVVM/GPUToNVVMPass.h b/mlir/include/mlir/Conversion/GPUToNVVM/GPUToNVVMPass.h index b3212279fab9..75e4f7e374c6 100644 --- a/mlir/include/mlir/Conversion/GPUToNVVM/GPUToNVVMPass.h +++ b/mlir/include/mlir/Conversion/GPUToNVVM/GPUToNVVMPass.h @@ -14,19 +14,15 @@ namespace mlir { class LLVMTypeConverter; class OwningRewritePatternList; -template -class OpPassBase; - -namespace gpu { -class GPUModuleOp; -} +class ModuleOp; +template class OpPassBase; /// Collect a set of patterns to convert from the GPU dialect to NVVM. void populateGpuToNVVMConversionPatterns(LLVMTypeConverter &converter, OwningRewritePatternList &patterns); /// Creates a pass that lowers GPU dialect operations to NVVM counterparts. -std::unique_ptr> createLowerGpuOpsToNVVMOpsPass(); +std::unique_ptr> createLowerGpuOpsToNVVMOpsPass(); } // namespace mlir diff --git a/mlir/include/mlir/Dialect/GPU/GPUOps.td b/mlir/include/mlir/Dialect/GPU/GPUOps.td index 3df6ff4be0c1..766ddbf202c2 100644 --- a/mlir/include/mlir/Dialect/GPU/GPUOps.td +++ b/mlir/include/mlir/Dialect/GPU/GPUOps.td @@ -588,56 +588,4 @@ def GPU_BarrierOp : GPU_Op<"barrier"> { let printer = [{ p << getOperationName(); }]; } -def GPU_GPUModuleOp : GPU_Op<"module", [ - IsolatedFromAbove, SymbolTable, Symbol, - SingleBlockImplicitTerminator<"ModuleEndOp"> -]> { - let summary = "A top level compilation unit containing code to be run on a GPU."; - let description = [{ - GPU module contains code that is intended to be run on a GPU. A host device - can launch this code through a gpu.launc_func that creates a fully - qualified symbol through the gpu.module's symbol and a gpu.func symbol - contained in the gpu.module. - - The module's top-level scope is modeled by a single region with a single - block. GPU modules are required to have a name that is used for symbol - resolution by the gpu.launch_func operation. - - Using an op with a region to define a GPU module enables "embedding" GPU - modules with SIMT execution models in other dialects in a clean manner and - allows filtering of code regions to execute passes on only code intended to - or not intended to be run on the separate device. - - ``` - gpu.module @symbol_name { - gpu.func {} - ... - gpu.module_end - } - - ``` - }]; - let builders = [OpBuilder<"Builder *builder, OperationState &result, " - "StringRef name">]; - let parser = [{ return ::parseGPUModuleOp(parser, result); }]; - let printer = [{ return ::print(p, *this); }]; - let regions = (region SizedRegion<1>:$body); - - // We need to ensure the block inside the region is properly terminated; - // the auto-generated builders do not guarantee that. - let skipDefaultBuilders = 1; -} - -def GPU_ModuleEndOp : GPU_Op<"module_end", [ - Terminator, HasParent<"GPUModuleOp"> -]> { - let summary = "A pseudo op that marks the end of a gpu.module."; - let description = [{ - This op terminates the only block inside the only region of a `gpu.module`. - }]; - - let parser = [{ return success(); }]; - let printer = [{ p << getOperationName(); }]; -} - #endif // GPU_OPS diff --git a/mlir/lib/Conversion/GPUToCUDA/ConvertKernelFuncToCubin.cpp b/mlir/lib/Conversion/GPUToCUDA/ConvertKernelFuncToCubin.cpp index b111c96313c2..66a2e66f99a4 100644 --- a/mlir/lib/Conversion/GPUToCUDA/ConvertKernelFuncToCubin.cpp +++ b/mlir/lib/Conversion/GPUToCUDA/ConvertKernelFuncToCubin.cpp @@ -46,15 +46,18 @@ static constexpr const char *kCubinAnnotation = "nvvm.cubin"; /// IR and further to PTX. A user provided CubinGenerator compiles the PTX to /// GPU binary code, which is then attached as an attribute to the function. The /// function body is erased. -class GpuKernelToCubinPass - : public OperationPass { +class GpuKernelToCubinPass : public ModulePass { public: GpuKernelToCubinPass( CubinGenerator cubinGenerator = compilePtxToCubinForTesting) : cubinGenerator(cubinGenerator) {} - void runOnOperation() override { - gpu::GPUModuleOp module = getOperation(); + void runOnModule() override { + ModuleOp module = getModule(); + if (!module.getAttrOfType( + gpu::GPUDialect::getKernelModuleAttrName()) || + !module.getName()) + return; // Make sure the NVPTX target is initialized. LLVMInitializeNVPTXTarget(); @@ -68,8 +71,8 @@ class GpuKernelToCubinPass // Translate the module to CUBIN and attach the result as attribute to the // module. - if (auto cubinAttr = translateGPUModuleToCubinAnnotation( - *llvmModule, module.getLoc(), module.getName())) + if (auto cubinAttr = translateGpuModuleToCubinAnnotation( + *llvmModule, module.getLoc(), *module.getName())) module.setAttr(kCubinAnnotation, cubinAttr); else signalPassFailure(); @@ -89,7 +92,7 @@ class GpuKernelToCubinPass StringRef name); /// Translates llvmModule to cubin and returns the result as attribute. - StringAttr translateGPUModuleToCubinAnnotation(llvm::Module &llvmModule, + StringAttr translateGpuModuleToCubinAnnotation(llvm::Module &llvmModule, Location loc, StringRef name); CubinGenerator cubinGenerator; @@ -146,7 +149,7 @@ OwnedCubin GpuKernelToCubinPass::convertModuleToCubin(llvm::Module &llvmModule, return cubinGenerator(ptx, loc, name); } -StringAttr GpuKernelToCubinPass::translateGPUModuleToCubinAnnotation( +StringAttr GpuKernelToCubinPass::translateGpuModuleToCubinAnnotation( llvm::Module &llvmModule, Location loc, StringRef name) { auto cubin = convertModuleToCubin(llvmModule, loc, name); if (!cubin) @@ -154,7 +157,7 @@ StringAttr GpuKernelToCubinPass::translateGPUModuleToCubinAnnotation( return StringAttr::get({cubin->data(), cubin->size()}, loc->getContext()); } -std::unique_ptr> +std::unique_ptr> mlir::createConvertGPUKernelToCubinPass(CubinGenerator cubinGenerator) { return std::make_unique(cubinGenerator); } diff --git a/mlir/lib/Conversion/GPUToCUDA/ConvertLaunchFuncToCudaCalls.cpp b/mlir/lib/Conversion/GPUToCUDA/ConvertLaunchFuncToCudaCalls.cpp index 31024d2881b5..41f69d6e21d1 100644 --- a/mlir/lib/Conversion/GPUToCUDA/ConvertLaunchFuncToCudaCalls.cpp +++ b/mlir/lib/Conversion/GPUToCUDA/ConvertLaunchFuncToCudaCalls.cpp @@ -132,9 +132,9 @@ class GpuLaunchFuncToCudaCallsPass // GPU kernel modules are no longer necessary since we have a global // constant with the CUBIN data. - for (auto m : - llvm::make_early_inc_range(getModule().getOps())) - m.erase(); + for (auto m : llvm::make_early_inc_range(getModule().getOps())) + if (m.getAttrOfType(gpu::GPUDialect::getKernelModuleAttrName())) + m.erase(); } private: @@ -343,8 +343,8 @@ void GpuLaunchFuncToCudaCallsPass::translateGpuLaunchCalls( builder.getI32IntegerAttr(0)); // Create an LLVM global with CUBIN extracted from the kernel annotation and // obtain a pointer to the first byte in it. - auto kernelModule = getModule().lookupSymbol( - launchOp.getKernelModuleName()); + auto kernelModule = + getModule().lookupSymbol(launchOp.getKernelModuleName()); assert(kernelModule && "expected a kernel module"); auto cubinAttr = kernelModule.getAttrOfType(kCubinAnnotation); @@ -354,7 +354,8 @@ void GpuLaunchFuncToCudaCallsPass::translateGpuLaunchCalls( return signalPassFailure(); } - SmallString<128> nameBuffer(kernelModule.getName()); + assert(kernelModule.getName() && "expected a named module"); + SmallString<128> nameBuffer(*kernelModule.getName()); nameBuffer.append(kCubinStorageSuffix); Value data = LLVM::createGlobalString( loc, builder, nameBuffer.str(), cubinAttr.getValue(), diff --git a/mlir/lib/Conversion/GPUToNVVM/LowerGpuOpsToNVVMOps.cpp b/mlir/lib/Conversion/GPUToNVVM/LowerGpuOpsToNVVMOps.cpp index 84bc7ff1d5f5..e2b1e0e533c5 100644 --- a/mlir/lib/Conversion/GPUToNVVM/LowerGpuOpsToNVVMOps.cpp +++ b/mlir/lib/Conversion/GPUToNVVM/LowerGpuOpsToNVVMOps.cpp @@ -200,7 +200,7 @@ struct GPUAllReduceOpLowering : public LLVMOpLowering { auto type = operand.getType().cast(); // Create shared memory array to store the warp reduction. - auto module = operand.getDefiningOp()->getParentOfType(); + auto module = operand.getDefiningOp()->getParentOfType(); assert(module && "op must belong to a module"); Value sharedMemPtr = createSharedMemoryArray(loc, module, type, kWarpSize, rewriter); @@ -391,10 +391,10 @@ struct GPUAllReduceOpLowering : public LLVMOpLowering { } /// Creates a global array stored in shared memory. - Value createSharedMemoryArray(Location loc, gpu::GPUModuleOp module, + Value createSharedMemoryArray(Location loc, ModuleOp module, LLVM::LLVMType elementType, int numElements, ConversionPatternRewriter &rewriter) const { - OpBuilder builder(module.body()); + OpBuilder builder(module.getBodyRegion()); auto arrayType = LLVM::LLVMType::getArrayTy(elementType, numElements); StringRef name = "reduce_buffer"; @@ -699,11 +699,13 @@ struct GPUReturnOpLowering : public LLVMOpLowering { /// /// This pass only handles device code and is not meant to be run on GPU host /// code. -class LowerGpuOpsToNVVMOpsPass - : public OperationPass { +class LowerGpuOpsToNVVMOpsPass : public ModulePass { public: - void runOnOperation() override { - gpu::GPUModuleOp m = getOperation(); + void runOnModule() override { + ModuleOp m = getModule(); + if (!m.getAttrOfType(gpu::GPUDialect::getKernelModuleAttrName())) + return; + OwningRewritePatternList patterns; NVVMTypeConverter converter(m.getContext()); populateStdToLLVMConversionPatterns(converter, patterns); @@ -716,7 +718,7 @@ class LowerGpuOpsToNVVMOpsPass target.addLegalDialect(); target.addLegalDialect(); // TODO(csigg): Remove once we support replacing non-root ops. - target.addLegalOp(); + target.addLegalOp(); if (failed(applyPartialConversion(m, target, patterns, &converter))) signalPassFailure(); } @@ -748,8 +750,7 @@ void mlir::populateGpuToNVVMConversionPatterns( "__nv_exp"); } -std::unique_ptr> -mlir::createLowerGpuOpsToNVVMOpsPass() { +std::unique_ptr> mlir::createLowerGpuOpsToNVVMOpsPass() { return std::make_unique(); } diff --git a/mlir/lib/Conversion/GPUToSPIRV/CMakeLists.txt b/mlir/lib/Conversion/GPUToSPIRV/CMakeLists.txt index adeb4e099ab9..be82894461d6 100644 --- a/mlir/lib/Conversion/GPUToSPIRV/CMakeLists.txt +++ b/mlir/lib/Conversion/GPUToSPIRV/CMakeLists.txt @@ -1,15 +1,8 @@ -set(LLVM_TARGET_DEFINITIONS GPUToSPIRV.td) -mlir_tablegen(GPUToSPIRV.cpp.inc -gen-rewriters) -add_public_tablegen_target(MLIRGPUToSPIRVIncGen) - add_llvm_library(MLIRGPUtoSPIRVTransforms ConvertGPUToSPIRV.cpp ConvertGPUToSPIRVPass.cpp ) -add_dependencies(MLIRGPUtoSPIRVTransforms - MLIRGPUToSPIRVIncGen) - target_link_libraries(MLIRGPUtoSPIRVTransforms MLIRGPU MLIRIR diff --git a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp index a90cea99be49..2fd8cedfd63b 100644 --- a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp +++ b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp @@ -63,13 +63,27 @@ class KernelFnConversion final : public SPIRVOpLowering { SmallVector workGroupSizeAsInt32; }; -/// Pattern to convert a gpu.module to a spv.module. -class GPUModuleConversion final : public SPIRVOpLowering { +/// Pattern to convert a module with gpu.kernel_module attribute to a +/// spv.module. +class KernelModuleConversion final : public SPIRVOpLowering { public: - using SPIRVOpLowering::SPIRVOpLowering; + using SPIRVOpLowering::SPIRVOpLowering; PatternMatchResult - matchAndRewrite(gpu::GPUModuleOp moduleOp, ArrayRef operands, + matchAndRewrite(ModuleOp moduleOp, ArrayRef operands, + ConversionPatternRewriter &rewriter) const override; +}; + +/// Pattern to convert a module terminator op to a terminator of spv.module op. +// TODO: Move this into DRR, but that requires ModuleTerminatorOp to be defined +// in ODS. +class KernelModuleTerminatorConversion final + : public SPIRVOpLowering { +public: + using SPIRVOpLowering::SPIRVOpLowering; + + PatternMatchResult + matchAndRewrite(ModuleTerminatorOp terminatorOp, ArrayRef operands, ConversionPatternRewriter &rewriter) const override; }; @@ -270,12 +284,16 @@ KernelFnConversion::matchAndRewrite(gpu::GPUFuncOp funcOp, } //===----------------------------------------------------------------------===// -// ModuleOp with gpu.module. +// ModuleOp with gpu.kernel_module. //===----------------------------------------------------------------------===// -PatternMatchResult GPUModuleConversion::matchAndRewrite( - gpu::GPUModuleOp moduleOp, ArrayRef operands, +PatternMatchResult KernelModuleConversion::matchAndRewrite( + ModuleOp moduleOp, ArrayRef operands, ConversionPatternRewriter &rewriter) const { + if (!moduleOp.getAttrOfType( + gpu::GPUDialect::getKernelModuleAttrName())) { + return matchFailure(); + } // TODO : Generalize this to account for different extensions, // capabilities, extended_instruction_sets, other addressing models // and memory models. @@ -284,8 +302,8 @@ PatternMatchResult GPUModuleConversion::matchAndRewrite( spirv::MemoryModel::GLSL450, spirv::Capability::Shader, spirv::Extension::SPV_KHR_storage_buffer_storage_class); // Move the region from the module op into the SPIR-V module. - Region &spvModuleRegion = spvModule.body(); - rewriter.inlineRegionBefore(moduleOp.body(), spvModuleRegion, + Region &spvModuleRegion = spvModule.getOperation()->getRegion(0); + rewriter.inlineRegionBefore(moduleOp.getBodyRegion(), spvModuleRegion, spvModuleRegion.begin()); // The spv.module build method adds a block with a terminator. Remove that // block. The terminator of the module op in the remaining block will be @@ -295,6 +313,17 @@ PatternMatchResult GPUModuleConversion::matchAndRewrite( return matchSuccess(); } +//===----------------------------------------------------------------------===// +// ModuleTerminatorOp for gpu.kernel_module. +//===----------------------------------------------------------------------===// + +PatternMatchResult KernelModuleTerminatorConversion::matchAndRewrite( + ModuleTerminatorOp terminatorOp, ArrayRef operands, + ConversionPatternRewriter &rewriter) const { + rewriter.replaceOpWithNewOp(terminatorOp); + return matchSuccess(); +} + //===----------------------------------------------------------------------===// // GPU return inside kernel functions to SPIR-V return. //===----------------------------------------------------------------------===// @@ -313,18 +342,14 @@ PatternMatchResult GPUReturnOpConversion::matchAndRewrite( // GPU To SPIRV Patterns. //===----------------------------------------------------------------------===// -namespace { -#include "GPUToSPIRV.cpp.inc" -} - void mlir::populateGPUToSPIRVPatterns(MLIRContext *context, SPIRVTypeConverter &typeConverter, OwningRewritePatternList &patterns, ArrayRef workGroupSize) { - populateWithGenerated(context, &patterns); patterns.insert(context, typeConverter, workGroupSize); patterns.insert< - GPUReturnOpConversion, ForOpConversion, GPUModuleConversion, + GPUReturnOpConversion, ForOpConversion, KernelModuleConversion, + KernelModuleTerminatorConversion, LaunchConfigConversion, LaunchConfigConversion, LaunchConfigConversion, diff --git a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRVPass.cpp b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRVPass.cpp index 4dda8bdc2b39..9b758d1052c7 100644 --- a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRVPass.cpp +++ b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRVPass.cpp @@ -60,12 +60,15 @@ void GPUToSPIRVPass::runOnModule() { SmallVector kernelModules; OpBuilder builder(context); - module.walk([&builder, &kernelModules](gpu::GPUModuleOp moduleOp) { - // For each kernel module (should be only 1 for now, but that is not a - // requirement here), clone the module for conversion because the - // gpu.launch function still needs the kernel module. - builder.setInsertionPoint(moduleOp.getOperation()); - kernelModules.push_back(builder.clone(*moduleOp.getOperation())); + module.walk([&builder, &kernelModules](ModuleOp moduleOp) { + if (moduleOp.getAttrOfType( + gpu::GPUDialect::getKernelModuleAttrName())) { + // For each kernel module (should be only 1 for now, but that is not a + // requirement here), clone the module for conversion because the + // gpu.launch function still needs the kernel module. + builder.setInsertionPoint(moduleOp.getOperation()); + kernelModules.push_back(builder.clone(*moduleOp.getOperation())); + } }); SPIRVTypeConverter typeConverter; diff --git a/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.td b/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.td deleted file mode 100644 index cfe9d26273cc..000000000000 --- a/mlir/lib/Conversion/GPUToSPIRV/GPUToSPIRV.td +++ /dev/null @@ -1,22 +0,0 @@ -//===-- GPUToSPIRV.td - GPU to SPIR-V Dialect Lowerings ----*- tablegen -*-===// -// -// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file contains patterns to lower GPU dialect ops to to SPIR-V ops. -// -//===----------------------------------------------------------------------===// - - -#ifndef CONVERT_GPU_TO_SPIRV -#define CONVERT_GPU_TO_SPIRV - -include "mlir/Dialect/GPU/GPUOps.td" -include "mlir/Dialect/SPIRV/SPIRVStructureOps.td" - -def : Pat<(GPU_ModuleEndOp), (SPV_ModuleEndOp)>; - -#endif // CONVERT_GPU_TO_SPIRV diff --git a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp index 083e28260201..e5bcc6973447 100644 --- a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp +++ b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp @@ -72,10 +72,15 @@ LogicalResult GPUDialect::verifyOperationAttribute(Operation *op, // Check that `launch_func` refers to a well-formed GPU kernel module. StringRef kernelModuleName = launchOp.getKernelModuleName(); - auto kernelModule = module.lookupSymbol(kernelModuleName); + auto kernelModule = module.lookupSymbol(kernelModuleName); if (!kernelModule) return launchOp.emitOpError() << "kernel module '" << kernelModuleName << "' is undefined"; + if (!kernelModule.getAttrOfType( + GPUDialect::getKernelModuleAttrName())) + return launchOp.emitOpError("module '") + << kernelModuleName << "' is missing the '" + << GPUDialect::getKernelModuleAttrName() << "' attribute"; // Check that `launch_func` refers to a well-formed kernel function. StringRef kernelName = launchOp.kernel(); @@ -512,9 +517,10 @@ void LaunchFuncOp::build(Builder *builder, OperationState &result, result.addOperands(kernelOperands); result.addAttribute(getKernelAttrName(), builder->getStringAttr(kernelFunc.getName())); - auto kernelModule = kernelFunc.getParentOfType(); - result.addAttribute(getKernelModuleAttrName(), - builder->getSymbolRefAttr(kernelModule.getName())); + auto kernelModule = kernelFunc.getParentOfType(); + if (Optional kernelModuleName = kernelModule.getName()) + result.addAttribute(getKernelModuleAttrName(), + builder->getSymbolRefAttr(*kernelModuleName)); } void LaunchFuncOp::build(Builder *builder, OperationState &result, @@ -814,47 +820,6 @@ LogicalResult GPUFuncOp::verifyBody() { return success(); } -//===----------------------------------------------------------------------===// -// GPUModuleOp -//===----------------------------------------------------------------------===// - -void GPUModuleOp::build(Builder *builder, OperationState &result, - StringRef name) { - ensureTerminator(*result.addRegion(), *builder, result.location); - result.attributes.push_back(builder->getNamedAttr( - ::mlir::SymbolTable::getSymbolAttrName(), builder->getStringAttr(name))); -} - -static ParseResult parseGPUModuleOp(OpAsmParser &parser, - OperationState &result) { - StringAttr nameAttr; - if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), - result.attributes)) - return failure(); - - // If module attributes are present, parse them. - if (parser.parseOptionalAttrDictWithKeyword(result.attributes)) - return failure(); - - // Parse the module body. - auto *body = result.addRegion(); - if (parser.parseRegion(*body, None, None)) - return failure(); - - // Ensure that this module has a valid terminator. - GPUModuleOp::ensureTerminator(*body, parser.getBuilder(), result.location); - return success(); -} - -static void print(OpAsmPrinter &p, GPUModuleOp op) { - p << op.getOperationName() << ' '; - p.printSymbolName(op.getName()); - p.printOptionalAttrDictWithKeyword(op.getAttrs(), - {SymbolTable::getSymbolAttrName()}); - p.printRegion(op.getOperation()->getRegion(0), /*printEntryBlockArgs=*/false, - /*printBlockTerminators=*/false); -} - // Namespace avoids ambiguous ReturnOpOperandAdaptor. namespace mlir { namespace gpu { diff --git a/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp b/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp index 0f8e2253980c..37f9c2e7b843 100644 --- a/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp +++ b/mlir/lib/Dialect/GPU/Transforms/KernelOutlining.cpp @@ -140,8 +140,8 @@ namespace { /// inside a nested module. It also creates an external function of the same /// name in the parent module. /// -/// The gpu.modules are intended to be compiled to a cubin blob independently in -/// a separate pass. The external functions can then be annotated with the +/// The kernel modules are intended to be compiled to a cubin blob independently +/// in a separate pass. The external functions can then be annotated with the /// symbol of the cubin accessor function. class GpuKernelOutliningPass : public ModulePass { public: @@ -174,19 +174,15 @@ class GpuKernelOutliningPass : public ModulePass { } private: - // Returns a gpu.module containing kernelFunc and all callees (recursive). - gpu::GPUModuleOp createKernelModule(gpu::GPUFuncOp kernelFunc, - const SymbolTable &parentSymbolTable) { - // TODO: This code cannot use an OpBuilder because it must be inserted into - // a SymbolTable by the caller. SymbolTable needs to be refactored to - // prevent manual building of Ops with symbols in code using SymbolTables - // and then this needs to use the OpBuilder. + // Returns a module containing kernelFunc and all callees (recursive). + ModuleOp createKernelModule(gpu::GPUFuncOp kernelFunc, + const SymbolTable &parentSymbolTable) { auto context = getModule().getContext(); Builder builder(context); - OperationState state(kernelFunc.getLoc(), - gpu::GPUModuleOp::getOperationName()); - gpu::GPUModuleOp::build(&builder, state, kernelFunc.getName()); - auto kernelModule = cast(Operation::create(state)); + auto kernelModule = + ModuleOp::create(builder.getUnknownLoc(), kernelFunc.getName()); + kernelModule.setAttr(gpu::GPUDialect::getKernelModuleAttrName(), + builder.getUnitAttr()); SymbolTable symbolTable(kernelModule); symbolTable.insert(kernelFunc); diff --git a/mlir/test/Conversion/GPUToCUDA/lower-launch-func-to-cuda.mlir b/mlir/test/Conversion/GPUToCUDA/lower-launch-func-to-cuda.mlir index 707f4a063958..6865462595f7 100644 --- a/mlir/test/Conversion/GPUToCUDA/lower-launch-func-to-cuda.mlir +++ b/mlir/test/Conversion/GPUToCUDA/lower-launch-func-to-cuda.mlir @@ -5,7 +5,7 @@ module attributes {gpu.container_module} { // CHECK: llvm.mlir.global internal constant @[[kernel_name:.*]]("kernel\00") // CHECK: llvm.mlir.global internal constant @[[global:.*]]("CUBIN") - gpu.module @kernel_module attributes {nvvm.cubin = "CUBIN"} { + module @kernel_module attributes {gpu.kernel_module, nvvm.cubin = "CUBIN"} { gpu.func @kernel(%arg0: !llvm.float, %arg1: !llvm<"float*">) attributes {gpu.kernel} { gpu.return } diff --git a/mlir/test/Conversion/GPUToCUDA/lower-nvvm-kernel-to-cubin.mlir b/mlir/test/Conversion/GPUToCUDA/lower-nvvm-kernel-to-cubin.mlir index 78b9f56b6202..62fe2b993388 100644 --- a/mlir/test/Conversion/GPUToCUDA/lower-nvvm-kernel-to-cubin.mlir +++ b/mlir/test/Conversion/GPUToCUDA/lower-nvvm-kernel-to-cubin.mlir @@ -1,7 +1,7 @@ // RUN: mlir-opt %s --test-kernel-to-cubin -split-input-file | FileCheck %s -// CHECK: attributes {nvvm.cubin = "CUBIN"} -gpu.module @foo { +// CHECK: attributes {gpu.kernel_module, nvvm.cubin = "CUBIN"} +module @foo attributes {gpu.kernel_module} { llvm.func @kernel(%arg0 : !llvm.float, %arg1 : !llvm<"float*">) // CHECK: attributes {gpu.kernel} attributes { gpu.kernel } { @@ -11,7 +11,7 @@ gpu.module @foo { // ----- -gpu.module @bar { +module @bar attributes {gpu.kernel_module} { // CHECK: func @kernel_a llvm.func @kernel_a() attributes { gpu.kernel } { diff --git a/mlir/test/Conversion/GPUToNVVM/gpu-to-nvvm.mlir b/mlir/test/Conversion/GPUToNVVM/gpu-to-nvvm.mlir index 7f69cb7482c5..24bf56557c3e 100644 --- a/mlir/test/Conversion/GPUToNVVM/gpu-to-nvvm.mlir +++ b/mlir/test/Conversion/GPUToNVVM/gpu-to-nvvm.mlir @@ -1,6 +1,6 @@ // RUN: mlir-opt %s -convert-gpu-to-nvvm -split-input-file | FileCheck %s -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK-LABEL: func @gpu_index_ops() func @gpu_index_ops() attributes { gpu.kernel } { @@ -38,7 +38,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK-LABEL: func @gpu_all_reduce_op() func @gpu_all_reduce_op() attributes { gpu.kernel } { @@ -55,7 +55,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK-LABEL: func @gpu_all_reduce_region() func @gpu_all_reduce_region() attributes { gpu.kernel } { @@ -74,7 +74,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK-LABEL: func @gpu_shuffle() func @gpu_shuffle() attributes { gpu.kernel } { @@ -99,7 +99,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK-LABEL: func @gpu_sync() func @gpu_sync() attributes { gpu.kernel } { @@ -111,7 +111,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK: llvm.func @__nv_fabsf(!llvm.float) -> !llvm.float // CHECK: llvm.func @__nv_fabs(!llvm.double) -> !llvm.double // CHECK-LABEL: func @gpu_fabs @@ -126,7 +126,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK: llvm.func @__nv_ceilf(!llvm.float) -> !llvm.float // CHECK: llvm.func @__nv_ceil(!llvm.double) -> !llvm.double // CHECK-LABEL: func @gpu_ceil @@ -141,7 +141,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK: llvm.func @__nv_cosf(!llvm.float) -> !llvm.float // CHECK: llvm.func @__nv_cos(!llvm.double) -> !llvm.double // CHECK-LABEL: func @gpu_cos @@ -156,7 +156,7 @@ gpu.module @test_module { // ----- -gpu.module @test_module { +module attributes {gpu.kernel_module} { // CHECK: llvm.func @__nv_expf(!llvm.float) -> !llvm.float // CHECK: llvm.func @__nv_exp(!llvm.double) -> !llvm.double // CHECK-LABEL: func @gpu_exp @@ -174,7 +174,7 @@ gpu.module @test_module { // ----- // Test that we handled properly operation with SymbolTable other than module op -gpu.module @test_module { +module attributes {gpu.kernel_module} { "test.symbol_scope"() ({ // CHECK: test.symbol_scope // CHECK: llvm.func @__nv_expf(!llvm.float) -> !llvm.float diff --git a/mlir/test/Conversion/GPUToNVVM/memory-attrbution.mlir b/mlir/test/Conversion/GPUToNVVM/memory-attrbution.mlir index 115c71d12800..69a16b25139e 100644 --- a/mlir/test/Conversion/GPUToNVVM/memory-attrbution.mlir +++ b/mlir/test/Conversion/GPUToNVVM/memory-attrbution.mlir @@ -1,6 +1,6 @@ // RUN: mlir-opt --convert-gpu-to-nvvm --split-input-file %s | FileCheck %s -gpu.module @kernel { +module attributes {gpu.kernel_module} { // CHECK-LABEL: llvm.func @private gpu.func @private(%arg0: f32) private(%arg1: memref<4xf32, 5>) { // Allocate private memory inside the function. @@ -32,7 +32,7 @@ gpu.module @kernel { // ----- -gpu.module @kernel { +module attributes {gpu.kernel_module} { // Workgroup buffers are allocated as globals. // CHECK: llvm.mlir.global internal @[[buffer:.*]]() // CHECK-SAME: addr_space = 3 @@ -72,7 +72,7 @@ gpu.module @kernel { // ----- -gpu.module @kernel { +module attributes {gpu.kernel_module} { // Check that the total size was computed correctly. // CHECK: llvm.mlir.global internal @[[buffer:.*]]() // CHECK-SAME: addr_space = 3 @@ -113,7 +113,7 @@ gpu.module @kernel { // ----- -gpu.module @kernel { +module attributes {gpu.kernel_module} { // Check that several buffers are defined. // CHECK: llvm.mlir.global internal @[[buffer1:.*]]() // CHECK-SAME: !llvm<"[1 x float]"> diff --git a/mlir/test/Conversion/GPUToSPIRV/builtins.mlir b/mlir/test/Conversion/GPUToSPIRV/builtins.mlir index 7f4081e4eda0..c0a68a9db2af 100644 --- a/mlir/test/Conversion/GPUToSPIRV/builtins.mlir +++ b/mlir/test/Conversion/GPUToSPIRV/builtins.mlir @@ -9,7 +9,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[WORKGROUPID:@.*]] built_in("WorkgroupId") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_workgroup_id_x() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[WORKGROUPID]] @@ -32,7 +32,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[WORKGROUPID:@.*]] built_in("WorkgroupId") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_workgroup_id_y() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[WORKGROUPID]] @@ -55,7 +55,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[WORKGROUPID:@.*]] built_in("WorkgroupId") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_workgroup_id_z() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[WORKGROUPID]] @@ -78,7 +78,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[WORKGROUPSIZE:@.*]] built_in("WorkgroupSize") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_workgroup_size_x() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[WORKGROUPSIZE]] @@ -101,7 +101,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[LOCALINVOCATIONID:@.*]] built_in("LocalInvocationId") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_local_id_x() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[LOCALINVOCATIONID]] @@ -124,7 +124,7 @@ module attributes {gpu.container_module} { // CHECK-LABEL: spv.module "Logical" "GLSL450" // CHECK: spv.globalVariable [[NUMWORKGROUPS:@.*]] built_in("NumWorkgroups") - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @builtin_num_workgroups_x() attributes {gpu.kernel} { // CHECK: [[ADDRESS:%.*]] = spv._address_of [[NUMWORKGROUPS]] diff --git a/mlir/test/Conversion/GPUToSPIRV/load-store.mlir b/mlir/test/Conversion/GPUToSPIRV/load-store.mlir index 446c0d602ed3..d104c96cfa9d 100644 --- a/mlir/test/Conversion/GPUToSPIRV/load-store.mlir +++ b/mlir/test/Conversion/GPUToSPIRV/load-store.mlir @@ -16,7 +16,7 @@ module attributes {gpu.container_module} { } // CHECK-LABEL: spv.module "Logical" "GLSL450" - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { // CHECK-DAG: spv.globalVariable [[WORKGROUPSIZEVAR:@.*]] built_in("WorkgroupSize") : !spv.ptr, Input> // CHECK-DAG: spv.globalVariable [[NUMWORKGROUPSVAR:@.*]] built_in("NumWorkgroups") : !spv.ptr, Input> // CHECK-DAG: spv.globalVariable [[LOCALINVOCATIONIDVAR:@.*]] built_in("LocalInvocationId") : !spv.ptr, Input> diff --git a/mlir/test/Conversion/GPUToSPIRV/loop.mlir b/mlir/test/Conversion/GPUToSPIRV/loop.mlir index bd97315a2ea4..6d38360b7e84 100644 --- a/mlir/test/Conversion/GPUToSPIRV/loop.mlir +++ b/mlir/test/Conversion/GPUToSPIRV/loop.mlir @@ -7,7 +7,7 @@ module attributes {gpu.container_module} { return } - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @loop_kernel(%arg2 : memref<10xf32>, %arg3 : memref<10xf32>) attributes {gpu.kernel} { // CHECK: [[LB:%.*]] = spv.constant 4 : i32 diff --git a/mlir/test/Conversion/GPUToSPIRV/simple.mlir b/mlir/test/Conversion/GPUToSPIRV/simple.mlir index cca5eb9d0b49..e1b687c1a0b0 100644 --- a/mlir/test/Conversion/GPUToSPIRV/simple.mlir +++ b/mlir/test/Conversion/GPUToSPIRV/simple.mlir @@ -2,7 +2,7 @@ module attributes {gpu.container_module} { - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { // CHECK: spv.module "Logical" "GLSL450" { // CHECK-LABEL: func @kernel_1 // CHECK-SAME: {{%.*}}: f32 {spv.interface_var_abi = {binding = 0 : i32, descriptor_set = 0 : i32, storage_class = 12 : i32{{[}][}]}} diff --git a/mlir/test/Dialect/GPU/invalid.mlir b/mlir/test/Dialect/GPU/invalid.mlir index 8f900bf6b5ce..8323fdf8709e 100644 --- a/mlir/test/Dialect/GPU/invalid.mlir +++ b/mlir/test/Dialect/GPU/invalid.mlir @@ -167,7 +167,7 @@ module attributes {gpu.container_module} { } func @launch_func_missing_module_attribute(%sz : index) { - // expected-error@+1 {{kernel module 'kernels' is undefined}} + // expected-error@+1 {{module 'kernels' is missing the 'gpu.kernel_module' attribute}} "gpu.launch_func"(%sz, %sz, %sz, %sz, %sz, %sz) { kernel = "kernel_1", kernel_module = @kernels } : (index, index, index, index, index, index) -> () @@ -178,7 +178,8 @@ module attributes {gpu.container_module} { // ----- module attributes {gpu.container_module} { - gpu.module @kernels { } + module @kernels attributes {gpu.kernel_module} { + } func @launch_func_undefined_function(%sz : index) { // expected-error@+1 {{kernel function 'kernel_1' is undefined}} @@ -192,7 +193,7 @@ module attributes {gpu.container_module} { // ----- module attributes {gpu.container_module} { - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @kernel_1(%arg1 : !llvm<"float*">) kernel { gpu.return } @@ -210,7 +211,7 @@ module attributes {gpu.container_module} { // ----- module attributes {gpu.container_module} { - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @kernel_1(%arg1 : !llvm<"float*">) attributes { gpu.kernel } { gpu.return } @@ -228,7 +229,7 @@ module attributes {gpu.container_module} { // ----- -gpu.module @kernels { +module @kernels attributes {gpu.kernel_module} { gpu.func @kernel_1(%arg1 : !llvm<"float*">) attributes { gpu.kernel } { gpu.return } diff --git a/mlir/test/Dialect/GPU/ops.mlir b/mlir/test/Dialect/GPU/ops.mlir index 033e7cbcb7e1..1dd08cea492a 100644 --- a/mlir/test/Dialect/GPU/ops.mlir +++ b/mlir/test/Dialect/GPU/ops.mlir @@ -60,7 +60,7 @@ module attributes {gpu.container_module} { return } - gpu.module @kernels { + module @kernels attributes {gpu.kernel_module} { gpu.func @kernel_1(%arg0 : f32, %arg1 : memref) attributes {gpu.kernel} { %tIdX = "gpu.thread_id"() {dimension = "x"} : () -> (index) %tIdY = "gpu.thread_id"() {dimension = "y"} : () -> (index) diff --git a/mlir/test/Dialect/GPU/outlining.mlir b/mlir/test/Dialect/GPU/outlining.mlir index 425b4b3090c5..5adb881a1dc6 100644 --- a/mlir/test/Dialect/GPU/outlining.mlir +++ b/mlir/test/Dialect/GPU/outlining.mlir @@ -136,7 +136,7 @@ func @recursive_device_function() { gpu.return } -// CHECK: gpu.module @function_call_kernel { +// CHECK: module @function_call_kernel attributes {gpu.kernel_module} { // CHECK: gpu.func @function_call_kernel() // CHECK: call @device_function() : () -> () // CHECK: call @device_function() : () -> () diff --git a/mlir/tools/mlir-cuda-runner/mlir-cuda-runner.cpp b/mlir/tools/mlir-cuda-runner/mlir-cuda-runner.cpp index a05016f48e86..d6160d6d6e0c 100644 --- a/mlir/tools/mlir-cuda-runner/mlir-cuda-runner.cpp +++ b/mlir/tools/mlir-cuda-runner/mlir-cuda-runner.cpp @@ -105,7 +105,7 @@ static LogicalResult runMLIRPasses(ModuleOp m) { applyPassManagerCLOptions(pm); pm.addPass(createGpuKernelOutliningPass()); - auto &kernelPm = pm.nest(); + auto &kernelPm = pm.nest(); kernelPm.addPass(createLowerGpuOpsToNVVMOpsPass()); kernelPm.addPass(createConvertGPUKernelToCubinPass(&compilePtxToCubin)); pm.addPass(createLowerToLLVMPass()); From fac11406197ed993f1965ed1edc0369a12a2f8e2 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 16 Jan 2020 12:00:24 -0800 Subject: [PATCH 004/374] [ELF] -r: don't create .interp `{clang,gcc} -nostdlib -r a.c` passes --dynamic-linker to the linker, and the expected behavior is to ignore it. If .interp is kept in the relocatable object file, a final link will get PT_INTERP even if --dynamic-linker is not specified. glibc ld.so expects to see PT_DYNAMIC and the executable will likely fail to run. Ignore --dynamic-linker in -r mode as well as -shared. (cherry picked from commit 2d7a8cf90478cd845ffb39763b0e95b7715322d2) --- lld/ELF/Writer.cpp | 4 ++-- lld/test/ELF/dynamic-linker.s | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 6373044d8804..ac332de2a057 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -135,8 +135,8 @@ StringRef getOutputSectionName(const InputSectionBase *s) { } static bool needsInterpSection() { - return !config->shared && !config->dynamicLinker.empty() && - script->needsInterpSection(); + return !config->relocatable && !config->shared && + !config->dynamicLinker.empty() && script->needsInterpSection(); } template void writeResult() { Writer().run(); } diff --git a/lld/test/ELF/dynamic-linker.s b/lld/test/ELF/dynamic-linker.s index 4d1dab48aec6..7330b52e155e 100644 --- a/lld/test/ELF/dynamic-linker.s +++ b/lld/test/ELF/dynamic-linker.s @@ -10,11 +10,16 @@ # CHECK: [Requesting program interpreter: foo] # RUN: ld.lld %t.o -o %t -# RUN: llvm-readelf -program-headers %t | FileCheck --check-prefix=NO %s +# RUN: llvm-readelf -S -l %t | FileCheck --check-prefix=NO %s # RUN: ld.lld --dynamic-linker foo --no-dynamic-linker %t.o -o %t -# RUN: llvm-readelf --program-headers %t | FileCheck --check-prefix=NO %s +# RUN: llvm-readelf -S -l %t | FileCheck --check-prefix=NO %s +## {clang,gcc} -nostdlib -r passes --dynamic-linker, and the expected behavior is to ignore it. +# RUN: ld.lld -r --dynamic-linker foo %t.o -o %t +# RUN: llvm-readelf -S -l %t | FileCheck --check-prefix=NO %s + +# NO-NOT: .interp # NO-NOT: PT_INTERP .globl _start From ac2c2db1674f200f87b05bee528c761600d87615 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Thu, 16 Jan 2020 20:04:42 -0800 Subject: [PATCH 005/374] [Transforms][RISCV] Remove a "using namespace llvm" from an include file. Fix a place that became dependent on it. This include file was created in October and has a "using namespace llvm". This seems to get exposed to other include files and finally onto cpp files. While this somewhat okay for llvm itself, its bad for other projects that use llvm as a library and includes a header file that picks this up. This was found by ISPC which has some class names at gloal scope with the same names as LLVM. It looks like RISCV accidentally became dependent on this. I fixed it by reordering some includes in the RISCV code, but maybe we want to change the TableGenEmitter to put "namespace llvm {" in the generated file instead? But we probably want to do the simplest thing first so we can merge it to 10.0. Differential Revision: https://reviews.llvm.org/D72895 (cherry picked from commit caee96031d3be9f951e4a17c8d3fb1c8b748fb31) --- llvm/include/llvm/Transforms/Utils/SizeOpts.h | 16 +++++++--------- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/SizeOpts.h b/llvm/include/llvm/Transforms/Utils/SizeOpts.h index ba0f86c45263..1137c89195a6 100644 --- a/llvm/include/llvm/Transforms/Utils/SizeOpts.h +++ b/llvm/include/llvm/Transforms/Utils/SizeOpts.h @@ -17,15 +17,13 @@ #include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Support/CommandLine.h" -using namespace llvm; - -extern cl::opt EnablePGSO; -extern cl::opt PGSOLargeWorkingSetSizeOnly; -extern cl::opt PGSOIRPassOrTestOnly; -extern cl::opt PGSOColdCodeOnly; -extern cl::opt ForcePGSO; -extern cl::opt PgsoCutoffInstrProf; -extern cl::opt PgsoCutoffSampleProf; +extern llvm::cl::opt EnablePGSO; +extern llvm::cl::opt PGSOLargeWorkingSetSizeOnly; +extern llvm::cl::opt PGSOIRPassOrTestOnly; +extern llvm::cl::opt PGSOColdCodeOnly; +extern llvm::cl::opt ForcePGSO; +extern llvm::cl::opt PgsoCutoffInstrProf; +extern llvm::cl::opt PgsoCutoffSampleProf; namespace llvm { diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 3b416ce3d3f4..f6bc52968a33 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -24,14 +24,14 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TargetRegistry.h" +using namespace llvm; + #define GEN_CHECK_COMPRESS_INSTR #include "RISCVGenCompressInstEmitter.inc" #define GET_INSTRINFO_CTOR_DTOR #include "RISCVGenInstrInfo.inc" -using namespace llvm; - RISCVInstrInfo::RISCVInstrInfo(RISCVSubtarget &STI) : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP), STI(STI) {} From fdb501e59f25e59b9dff7d51db468d590909cfef Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 16 Sep 2019 20:36:55 -0700 Subject: [PATCH 006/374] Work around PR43337: don't try to use the vec_sel overloads for vector long long, since clang's doesn't provide it yet! (cherry picked from commit 388eaa1270c2762d61b756759b6db8cf15bd3a83) --- clang/lib/Headers/ppc_wrappers/emmintrin.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Headers/ppc_wrappers/emmintrin.h b/clang/lib/Headers/ppc_wrappers/emmintrin.h index 293276cc9be0..4dcb8485e2e9 100644 --- a/clang/lib/Headers/ppc_wrappers/emmintrin.h +++ b/clang/lib/Headers/ppc_wrappers/emmintrin.h @@ -1749,7 +1749,7 @@ _mm_sll_epi64 (__m128i __A, __m128i __B) lshift = vec_splat ((__v2du) __B, 0); shmask = vec_cmplt (lshift, shmax); result = vec_sl ((__v2du) __A, lshift); - result = vec_sel ((__v2du) shmask, result, shmask); + result = (__v2du)vec_sel ((__v2df) shmask, (__v2df)result, shmask); return (__m128i) result; } @@ -1843,7 +1843,7 @@ _mm_srl_epi64 (__m128i __A, __m128i __B) rshift = vec_splat ((__v2du) __B, 0); shmask = vec_cmplt (rshift, shmax); result = vec_sr ((__v2du) __A, rshift); - result = vec_sel ((__v2du) shmask, result, shmask); + result = (__v2du)vec_sel ((__v2df) shmask, (__v2df)result, shmask); return (__m128i) result; } From 7a8b8f09daa1d4f8b82275006678ce3b5daa03b2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 8 May 2019 23:30:37 -0700 Subject: [PATCH 007/374] PR17164: Change clang's default behavior from -flax-vector-conversions=all to -flax-vector-conversions=integer. Summary: See proposal on cfe-dev: http://lists.llvm.org/pipermail/cfe-dev/2019-April/062030.html Reviewers: SjoerdMeijer, eli.friedman Subscribers: kristof.beyls, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D67678 (cherry picked from commit b72a8c65e4e34779b6bc9e466203f553f5294486) --- clang/docs/CommandGuide/clang.rst | 11 +++++++++- clang/docs/ReleaseNotes.rst | 24 +++++++++++++++++++++ clang/include/clang/Basic/LangOptions.def | 2 +- clang/test/Headers/altivec-header.c | 2 +- clang/test/Headers/arm-neon-header.c | 2 +- clang/test/Headers/x86-intrinsics-headers.c | 2 +- clang/test/Headers/x86intrin-2.c | 4 ++-- clang/test/Headers/x86intrin.c | 2 +- clang/test/Sema/vector-assign.c | 12 +++++------ clang/test/Sema/vector-cast.c | 23 +++++++++++++------- clang/test/Sema/vector-ops.c | 3 ++- 11 files changed, 64 insertions(+), 23 deletions(-) diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 7b0873600fc3..6947450beb43 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -278,9 +278,18 @@ Language Selection and Mode Options Make all string literals default to writable. This disables uniquing of strings and other optimizations. -.. option:: -flax-vector-conversions +.. option:: -flax-vector-conversions, -flax-vector-conversions=, -fno-lax-vector-conversions Allow loose type checking rules for implicit vector conversions. + Possible values of : + + - ``none``: allow no implicit conversions between vectors + - ``integer``: allow implicit bitcasts between integer vectors of the same + overall bit-width + - ``all``: allow implicit bitcasts between any vectors of the same + overall bit-width + + defaults to ``integer`` if unspecified. .. option:: -fblocks diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 881dcb78b663..8c48724e7c66 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -102,6 +102,15 @@ Non-comprehensive list of changes in this release the found gcc installation is older than 4.7.0. Add ``-fno-use-init-array`` to get the old behavior (``.ctors``). +* Lax vector conversions involving floating-point vectors have been disabled + by default, and can no longer be enabled with ``-flax-vector-conversions``. + This matches the behavior of these flags in GCC, but code relying on implicit + vector bitcasts between integer and floating-point types that used to compile + with older versions of Clang is no longer accepted by default in Clang 10. + The old behavior can be restored with ``-flax-vector-conversions=all``. + In a future release of Clang, we intend to change the default to + ``-fno-lax-vector-conversions``. + New Compiler Flags ------------------ @@ -132,6 +141,21 @@ Modified Compiler Flags - RISC-V now sets the architecture (riscv32/riscv64) based on the value provided to the ``-march`` flag, overriding the target provided by ``-triple``. +- ``-flax-vector-conversions`` has been split into three different levels of + laxness: + + - ``-flax-vector-conversions=all``: This is Clang's historical default, and + permits implicit vector conversions (performed as bitcasts) between any + two vector types of the same overall bit-width. + + - ``-flax-vector-conversions=integer``: This is Clang's current default, + and permits implicit vector conversions (performed as bitcasts) between + any two integer vector types of the same overall bit-width. + Synonym: ``-flax-vector-conversions``. + + - ``-flax-vector-conversions=none``: Do not perform any implicit bitcasts + between vector types. Synonym: ``-fno-lax-vector-conversions``. + New Pragmas in Clang -------------------- diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 068f206f4484..4bbe6ea26fba 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -121,7 +121,7 @@ BENIGN_LANGOPT(PascalStrings, 1, 0, "Pascal string support") LANGOPT(WritableStrings , 1, 0, "writable string support") LANGOPT(ConstStrings , 1, 0, "const-qualified string support") ENUM_LANGOPT(LaxVectorConversions, LaxVectorConversionKind, 2, - LaxVectorConversionKind::All, "lax vector conversions") + LaxVectorConversionKind::Integer, "lax vector conversions") LANGOPT(ConvergentFunctions, 1, 1, "Assume convergent functions") LANGOPT(AltiVec , 1, 0, "AltiVec-style vector initializers") LANGOPT(ZVector , 1, 0, "System z vector extensions") diff --git a/clang/test/Headers/altivec-header.c b/clang/test/Headers/altivec-header.c index 00e5f444de7c..aa85a33d26da 100644 --- a/clang/test/Headers/altivec-header.c +++ b/clang/test/Headers/altivec-header.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -flax-vector-conversions=none -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -flax-vector-conversions=all -o - %s | FileCheck %s // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -x c++ -o - %s | FileCheck %s #include diff --git a/clang/test/Headers/arm-neon-header.c b/clang/test/Headers/arm-neon-header.c index f6362886010a..8626a883fdf3 100644 --- a/clang/test/Headers/arm-neon-header.c +++ b/clang/test/Headers/arm-neon-header.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -Wvector-conversions -ffreestanding %s +// RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -flax-vector-conversions=all -Wvector-conversions -ffreestanding %s // RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -flax-vector-conversions=none -ffreestanding %s // RUN: %clang_cc1 -x c++ -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -Wvector-conversions -ffreestanding %s diff --git a/clang/test/Headers/x86-intrinsics-headers.c b/clang/test/Headers/x86-intrinsics-headers.c index 59ca354e1160..2efd3505bca6 100644 --- a/clang/test/Headers/x86-intrinsics-headers.c +++ b/clang/test/Headers/x86-intrinsics-headers.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding %s +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=all %s // RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none %s // RUN: %clang_cc1 -fsyntax-only -ffreestanding -x c++ %s diff --git a/clang/test/Headers/x86intrin-2.c b/clang/test/Headers/x86intrin-2.c index 90475c658fce..bd6ed565d0de 100644 --- a/clang/test/Headers/x86intrin-2.c +++ b/clang/test/Headers/x86intrin-2.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual %s -verify -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none -Wcast-qual %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -flax-vector-conversions=all %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -flax-vector-conversions=none %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -x c++ %s -verify // expected-no-diagnostics diff --git a/clang/test/Headers/x86intrin.c b/clang/test/Headers/x86intrin.c index 53e369559f40..e904e9ed5462 100644 --- a/clang/test/Headers/x86intrin.c +++ b/clang/test/Headers/x86intrin.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=all %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -x c++ %s -verify // expected-no-diagnostics diff --git a/clang/test/Sema/vector-assign.c b/clang/test/Sema/vector-assign.c index ad3406e304a7..88be03e2cb6d 100644 --- a/clang/test/Sema/vector-assign.c +++ b/clang/test/Sema/vector-assign.c @@ -14,12 +14,12 @@ void test1() { v1 = v2; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v2u' (vector of 2 'unsigned int' values)}} v1 = v3; // expected-error {{assigning to 'v2s' (vector of 2 'int' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v1 = v4; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v2f' (vector of 2 'float' values)}} + v1 = v4; // expected-error {{assigning to 'v2s' (vector of 2 'int' values) from incompatible type 'v2f' (vector of 2 'float' values)}} v1 = v5; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v4ss' (vector of 4 'short' values)}} v2 = v1; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v2s' (vector of 2 'int' values)}} v2 = v3; // expected-error {{assigning to 'v2u' (vector of 2 'unsigned int' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v2 = v4; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v2f' (vector of 2 'float' values)}} + v2 = v4; // expected-error {{assigning to 'v2u' (vector of 2 'unsigned int' values) from incompatible type 'v2f' (vector of 2 'float' values)}} v2 = v5; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v4ss' (vector of 4 'short' values)}} v3 = v1; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v2s' (vector of 2 'int' values)}} @@ -27,15 +27,15 @@ void test1() { v3 = v4; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v2f' (vector of 2 'float' values)}} v3 = v5; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v4ss'}} - v4 = v1; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v2s' (vector of 2 'int' values)}} - v4 = v2; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v2u' (vector of 2 'unsigned int' values)}} + v4 = v1; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v2s' (vector of 2 'int' values)}} + v4 = v2; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v2u' (vector of 2 'unsigned int' values)}} v4 = v3; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v4 = v5; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v4ss' (vector of 4 'short' values)}} + v4 = v5; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v4ss' (vector of 4 'short' values)}} v5 = v1; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2s' (vector of 2 'int' values)}} v5 = v2; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2u' (vector of 2 'unsigned int' values)}} v5 = v3; // expected-error {{assigning to 'v4ss' (vector of 4 'short' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v5 = v4; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2f'}} + v5 = v4; // expected-error {{assigning to 'v4ss' (vector of 4 'short' values) from incompatible type 'v2f'}} } // PR2263 diff --git a/clang/test/Sema/vector-cast.c b/clang/test/Sema/vector-cast.c index 2bdc00707d4c..01b5c3d252ab 100644 --- a/clang/test/Sema/vector-cast.c +++ b/clang/test/Sema/vector-cast.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only %s -verify -Wvector-conversion +// RUN: %clang_cc1 -fsyntax-only %s -verify=expected,no-lax -Wvector-conversion -flax-vector-conversions=none +// RUN: %clang_cc1 -fsyntax-only %s -verify=expected,lax -Wvector-conversion -flax-vector-conversions=all typedef long long t1 __attribute__ ((vector_size (8))); typedef char t2 __attribute__ ((vector_size (16))); @@ -41,7 +42,9 @@ type 't1' (vector of 1 'long long' value) and integer type 'short' of different void f2(t2 X); // expected-note{{passing argument to parameter 'X' here}} void f3(t3 Y) { - f2(Y); // expected-warning {{incompatible vector types passing 't3' (vector of 4 'float' values) to parameter of type 't2' (vector of 16 'char' values)}} + f2(Y); + // lax-warning@-1 {{incompatible vector types passing 't3' (vector of 4 'float' values) to parameter of type 't2' (vector of 16 'char' values)}} + // no-lax-error@-2 {{passing 't3' (vector of 4 'float' values) to parameter of incompatible type 't2' (vector of 16 'char' values)}} } typedef float float2 __attribute__ ((vector_size (8))); @@ -58,13 +61,15 @@ void f4() { float64x2_t v = {0.0, 1.0}; f2 += d; // expected-error {{cannot convert between scalar type 'double' and vector type 'float2' (vector of 2 'float' values) as implicit conversion would cause truncation}} d += f2; // expected-error {{assigning to 'double' from incompatible type 'float2' (vector of 2 'float' values)}} - a = 3.0 + vget_low_f64(v); - b = vget_low_f64(v) + 3.0; - c = vget_low_f64(v); - c -= vget_low_f64(v); + a = 3.0 + vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} + b = vget_low_f64(v) + 3.0; // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} + c = vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} + c -= vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} // LAX conversions between scalar and vector types require same size and one element sized vectors. d = f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}} - d = d + f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}} + d = d + f2; + // lax-error@-1 {{assigning to 'double' from incompatible type 'float2' (vector of 2 'float' values)}} + // no-lax-error@-2 {{cannot convert between scalar type 'double' and vector type 'float2' (vector of 2 'float' values) as implicit conversion would cause truncation}} } // rdar://15931426 @@ -78,6 +83,8 @@ void f5() { } void f6(vSInt32 a0) { - vUInt32 counter = (float16){0.0f, 0.0f, 0.0f, 0.0f}; // expected-warning {{incompatible vector types initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of type 'float16' (vector of 4 'float' values)}} + vUInt32 counter = (float16){0.0f, 0.0f, 0.0f, 0.0f}; + // lax-warning@-1 {{incompatible vector types initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of type 'float16' (vector of 4 'float' values)}} + // no-lax-error@-2 {{initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of incompatible type 'float16' (vector of 4 'float' values)}} counter -= a0; } diff --git a/clang/test/Sema/vector-ops.c b/clang/test/Sema/vector-ops.c index 575f38b972f5..d8031f0d2f4a 100644 --- a/clang/test/Sema/vector-ops.c +++ b/clang/test/Sema/vector-ops.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only -Wvector-conversion -triple x86_64-apple-darwin10 +// RUN: %clang_cc1 %s -verify -fsyntax-only -Wvector-conversion -triple x86_64-apple-darwin10 -flax-vector-conversions=all +// FIXME: We get worse diagnostics here with -flax-vector-conversions disabled. typedef unsigned int v2u __attribute__ ((vector_size (8))); typedef int v2s __attribute__ ((vector_size (8))); typedef float v2f __attribute__ ((vector_size(8))); From e241c8fe6d2e6d83e9fb32bd34da8ffcdc0dd83d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 15 Jan 2020 13:14:13 -0800 Subject: [PATCH 008/374] Fix pack deduction to only deduce the arity of packs that are actually expanded by the deduced pack. We recently started also deducing the arity of separately-expanded packs that are merely mentioned within the pack in question, which is incorrect. (cherry picked from commit e8f198dd9e9dabed8d50276465906e7c8827cada) --- clang/lib/Sema/SemaTemplateDeduction.cpp | 50 ++++++++++++++---------- clang/test/SemaTemplate/deduction.cpp | 18 +++++++++ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 1b9f1b2144d1..048a50a741e4 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -724,38 +724,48 @@ class PackDeductionScope { // Compute the set of template parameter indices that correspond to // parameter packs expanded by the pack expansion. llvm::SmallBitVector SawIndices(TemplateParams->size()); + llvm::SmallVector ExtraDeductions; auto AddPack = [&](unsigned Index) { if (SawIndices[Index]) return; SawIndices[Index] = true; addPack(Index); + + // Deducing a parameter pack that is a pack expansion also constrains the + // packs appearing in that parameter to have the same deduced arity. Also, + // in C++17 onwards, deducing a non-type template parameter deduces its + // type, so we need to collect the pending deduced values for those packs. + if (auto *NTTP = dyn_cast( + TemplateParams->getParam(Index))) { + if (auto *Expansion = dyn_cast(NTTP->getType())) + ExtraDeductions.push_back(Expansion->getPattern()); + } + // FIXME: Also collect the unexpanded packs in any type and template + // parameter packs that are pack expansions. }; - // First look for unexpanded packs in the pattern. - SmallVector Unexpanded; - S.collectUnexpandedParameterPacks(Pattern, Unexpanded); - for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { - unsigned Depth, Index; - std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]); - if (Depth == Info.getDeducedDepth()) - AddPack(Index); - } + auto Collect = [&](TemplateArgument Pattern) { + SmallVector Unexpanded; + S.collectUnexpandedParameterPacks(Pattern, Unexpanded); + for (unsigned I = 0, N = Unexpanded.size(); I != N; ++I) { + unsigned Depth, Index; + std::tie(Depth, Index) = getDepthAndIndex(Unexpanded[I]); + if (Depth == Info.getDeducedDepth()) + AddPack(Index); + } + }; + + // Look for unexpanded packs in the pattern. + Collect(Pattern); assert(!Packs.empty() && "Pack expansion without unexpanded packs?"); unsigned NumNamedPacks = Packs.size(); - // We can also have deduced template parameters that do not actually - // appear in the pattern, but can be deduced by it (the type of a non-type - // template parameter pack, in particular). These won't have prevented us - // from partially expanding the pack. - llvm::SmallBitVector Used(TemplateParams->size()); - MarkUsedTemplateParameters(S.Context, Pattern, /*OnlyDeduced*/true, - Info.getDeducedDepth(), Used); - for (int Index = Used.find_first(); Index != -1; - Index = Used.find_next(Index)) - if (TemplateParams->getParam(Index)->isParameterPack()) - AddPack(Index); + // Also look for unexpanded packs that are indirectly deduced by deducing + // the sizes of the packs in this pattern. + while (!ExtraDeductions.empty()) + Collect(ExtraDeductions.pop_back_val()); return NumNamedPacks; } diff --git a/clang/test/SemaTemplate/deduction.cpp b/clang/test/SemaTemplate/deduction.cpp index 1f1c30a8b4ab..7268912dd6c5 100644 --- a/clang/test/SemaTemplate/deduction.cpp +++ b/clang/test/SemaTemplate/deduction.cpp @@ -546,3 +546,21 @@ namespace designators { static_assert(f({.a = 1, .b = 2}) == 3, ""); // expected-error {{no matching function}} } + +namespace nested_packs { + template void f(T (*...f)(U...)); // expected-note {{deduced packs of different lengths for parameter 'U' (<> vs. )}} + void g() { f(g); f(g, g); f(g, g, g); } + void h(int) { f(h); f(h, h); f(h, h, h); } + void i() { f(g, h); } // expected-error {{no matching function}} + +#if __cplusplus >= 201703L + template struct Q {}; + template void q(Q, Q); // #q + void qt(Q<> q0, Q<1, 2> qii, Q<1, 2, 3> qiii) { + q(q0, q0); + q(qii, qii); + q(qii, qiii); // expected-error {{no match}} expected-note@#q {{deduced packs of different lengths for parameter 'T' ( vs. )}} + q(q0, qiii); // expected-error {{no match}} expected-note@#q {{deduced packs of different lengths for parameter 'T' (<> vs. )}} + } +#endif +} From 0a08d2c4e7830a1b2428c2c77f205ac74fa29899 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 15 Jan 2020 18:37:32 -0800 Subject: [PATCH 009/374] PR42694 Support explicit(bool) in older language modes as an extension. This needs somewhat careful disambiguation, as C++2a explicit(bool) is a breaking change. We only enable it in cases where the source construct could not possibly be anything else. (cherry picked from commit 45d70806f4386adfb62b0d75949a8aad58e0576f) --- .../clang/Basic/DiagnosticParseKinds.td | 13 ++-- clang/include/clang/Parse/Parser.h | 15 +++++ clang/lib/Parse/ParseDecl.cpp | 9 ++- clang/lib/Parse/ParseTentative.cpp | 64 +++++++++++++++++-- clang/lib/Parse/Parser.cpp | 5 +- clang/test/SemaCXX/cxx2a-explicit-bool.cpp | 1 + 6 files changed, 91 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index cc6a74ac3e6d..41f788e7d9bd 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -33,10 +33,6 @@ def err_asm_goto_cannot_have_output : Error< let CategoryName = "Parse Issue" in { -def warn_cxx2a_compat_explicit_bool : Warning< - "this expression will be parsed as explicit(bool) in C++2a">, - InGroup, DefaultIgnore; - def ext_empty_translation_unit : Extension< "ISO C requires a translation unit to contain at least one declaration">, InGroup>; @@ -684,6 +680,15 @@ def err_ms_property_expected_comma_or_rparen : Error< def err_ms_property_initializer : Error< "property declaration cannot have an in-class initializer">; +def warn_cxx2a_compat_explicit_bool : Warning< + "this expression will be parsed as explicit(bool) in C++2a">, + InGroup, DefaultIgnore; +def warn_cxx17_compat_explicit_bool : Warning< + "explicit(bool) is incompatible with C++ standards before C++2a">, + InGroup, DefaultIgnore; +def ext_explicit_bool : ExtWarn<"explicit(bool) is a C++2a extension">, + InGroup; + /// C++ Templates def err_expected_template : Error<"expected template">; def err_unknown_template_name : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index e320c9647818..b7bed4713992 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -806,6 +806,16 @@ class Parser : public CodeCompletionHandler { bool IsNewScope); bool TryAnnotateCXXScopeToken(bool EnteringContext = false); + bool MightBeCXXScopeToken() { + return Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || + (Tok.is(tok::annot_template_id) && + NextToken().is(tok::coloncolon)) || + Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super); + } + bool TryAnnotateOptionalCXXScopeToken(bool EnteringContext = false) { + return MightBeCXXScopeToken() && TryAnnotateCXXScopeToken(EnteringContext); + } + private: enum AnnotatedNameKind { /// Annotation has failed and emitted an error. @@ -2395,6 +2405,11 @@ class Parser : public CodeCompletionHandler { /// rather than a less-than expression. TPResult isTemplateArgumentList(unsigned TokensToSkip); + /// Determine whether an '(' after an 'explicit' keyword is part of a C++20 + /// 'explicit(bool)' declaration, in earlier language modes where that is an + /// extension. + TPResult isExplicitBool(); + /// Determine whether an identifier has been tentatively declared as a /// non-type. Such tentative declarations should not be found to name a type /// during a tentative parse, but also should not be annotated as a non-type. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 69a3ed9cbad7..d8c5a0ab02d3 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3617,7 +3617,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumedEnd = ExplicitLoc; ConsumeToken(); // kw_explicit if (Tok.is(tok::l_paren)) { - if (getLangOpts().CPlusPlus2a) { + if (getLangOpts().CPlusPlus2a || isExplicitBool() == TPResult::True) { + Diag(Tok.getLocation(), getLangOpts().CPlusPlus2a + ? diag::warn_cxx17_compat_explicit_bool + : diag::ext_explicit_bool); + ExprResult ExplicitExpr(static_cast(nullptr)); BalancedDelimiterTracker Tracker(*this, tok::l_paren); Tracker.consumeOpen(); @@ -3630,8 +3634,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Actions.ActOnExplicitBoolSpecifier(ExplicitExpr.get()); } else Tracker.skipToEnd(); - } else + } else { Diag(Tok.getLocation(), diag::warn_cxx2a_compat_explicit_bool); + } } isInvalid = DS.setFunctionSpecExplicit(ExplicitLoc, PrevSpec, DiagID, ExplicitSpec, CloseParenLoc); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 4d69fb4693fb..d5068fb11b86 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -202,9 +202,7 @@ Parser::TPResult Parser::TryConsumeDeclarationSpecifier() { } } - if (Tok.isOneOf(tok::identifier, tok::coloncolon, tok::kw_decltype, - tok::annot_template_id) && - TryAnnotateCXXScopeToken()) + if (TryAnnotateOptionalCXXScopeToken()) return TPResult::Error; if (Tok.is(tok::annot_cxxscope)) ConsumeAnnotationToken(); @@ -785,9 +783,8 @@ Parser::isCXX11AttributeSpecifier(bool Disambiguate, Parser::TPResult Parser::TryParsePtrOperatorSeq() { while (true) { - if (Tok.isOneOf(tok::coloncolon, tok::identifier)) - if (TryAnnotateCXXScopeToken(true)) - return TPResult::Error; + if (TryAnnotateOptionalCXXScopeToken(true)) + return TPResult::Error; if (Tok.isOneOf(tok::star, tok::amp, tok::caret, tok::ampamp) || (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) { @@ -2137,3 +2134,58 @@ Parser::TPResult Parser::isTemplateArgumentList(unsigned TokensToSkip) { return TPResult::Ambiguous; return TPResult::False; } + +/// Determine whether we might be looking at the '(' of a C++20 explicit(bool) +/// in an earlier language mode. +Parser::TPResult Parser::isExplicitBool() { + assert(Tok.is(tok::l_paren) && "expected to be looking at a '(' token"); + + RevertingTentativeParsingAction PA(*this); + ConsumeParen(); + + // We can only have 'explicit' on a constructor, conversion function, or + // deduction guide. The declarator of a deduction guide cannot be + // parenthesized, so we know this isn't a deduction guide. So the only + // thing we need to check for is some number of parens followed by either + // the current class name or 'operator'. + while (Tok.is(tok::l_paren)) + ConsumeParen(); + + if (TryAnnotateOptionalCXXScopeToken()) + return TPResult::Error; + + // Class-scope constructor and conversion function names can't really be + // qualified, but we get better diagnostics if we assume they can be. + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), + Tok.getAnnotationRange(), + SS); + ConsumeAnnotationToken(); + } + + // 'explicit(operator' might be explicit(bool) or the declaration of a + // conversion function, but it's probably a conversion function. + if (Tok.is(tok::kw_operator)) + return TPResult::Ambiguous; + + // If this can't be a constructor name, it can only be explicit(bool). + if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) + return TPResult::True; + if (!Actions.isCurrentClassName(Tok.is(tok::identifier) + ? *Tok.getIdentifierInfo() + : *takeTemplateIdAnnotation(Tok)->Name, + getCurScope(), &SS)) + return TPResult::True; + // Formally, we must have a right-paren after the constructor name to match + // the grammar for a constructor. But clang permits a parenthesized + // constructor declarator, so also allow a constructor declarator to follow + // with no ')' token after the constructor name. + if (!NextToken().is(tok::r_paren) && + !isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(), + /*DeductionGuide=*/false)) + return TPResult::True; + + // Might be explicit(bool) or a parenthesized constructor name. + return TPResult::Ambiguous; +} diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 4249de361b89..0fb0a5217d54 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2005,10 +2005,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { assert(getLangOpts().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); - assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) || - (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) || - Tok.is(tok::kw_decltype) || Tok.is(tok::kw___super)) && - "Cannot be a type or scope token!"); + assert(MightBeCXXScopeToken() && "Cannot be a type or scope token!"); CXXScopeSpec SS; if (ParseOptionalCXXScopeSpecifier(SS, nullptr, EnteringContext)) diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp index df776b390548..45385972cab8 100644 --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp @@ -1,3 +1,4 @@ +// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify -Wno-c++2a-extensions // RUN: %clang_cc1 -std=c++2a -fsyntax-only %s -verify template struct enable_ifv {}; From 2d2d057ae23036baecb5a2a4a7f929626f46921a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 16 Jan 2020 18:12:45 -0800 Subject: [PATCH 010/374] Add extra test file forgotten in 45d7080. (cherry picked from commit b78e8e0d79c47a6698a0abc10a37b8a253cb6064) --- clang/test/Parser/explicit-bool.cpp | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 clang/test/Parser/explicit-bool.cpp diff --git a/clang/test/Parser/explicit-bool.cpp b/clang/test/Parser/explicit-bool.cpp new file mode 100644 index 000000000000..bdd91dbbafc9 --- /dev/null +++ b/clang/test/Parser/explicit-bool.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -std=c++17 -verify=cxx17 -Wc++2a-compat %s +// RUN: %clang_cc1 -std=c++2a -verify=cxx2a -Wc++17-compat %s + +namespace disambig { + +// Cases that are valid in C++17 and before, ill-formed in C++20, and that we +// should not treat as explicit(bool) as an extension. +struct A { // cxx2a-note +{{}} + constexpr A() {} + constexpr operator bool() { return true; } + + constexpr explicit (A)(int); // #1 + // cxx17-warning@#1 {{will be parsed as explicit(bool)}} + // cxx2a-error@#1 +{{}} cxx2a-note@#1 +{{}} + // cxx2a-warning@#1 {{incompatible with C++ standards before C++2a}} + + // This is ill-formed (via a DR change), and shouldn't be recognized as a + // constructor (the function declarator cannot be parenthesized in a + // constructor declaration). But accepting it as an extension seems + // reasonable. + // FIXME: Produce an ExtWarn for this. + constexpr explicit (A(float)); // #1b + // cxx17-warning@#1b {{will be parsed as explicit(bool)}} + // cxx2a-error@#1b +{{}} + // cxx2a-warning@#1b {{incompatible with C++ standards before C++2a}} + + explicit (operator int)(); // #2 + // cxx17-warning@#2 {{will be parsed as explicit(bool)}} + // cxx2a-error@#2 +{{}} + // cxx2a-warning@#2 {{incompatible with C++ standards before C++2a}} + + explicit (A::operator float)(); // #2b + // cxx17-warning@#2b {{will be parsed as explicit(bool)}} + // cxx17-error@#2b {{extra qualification on member}} + // cxx2a-error@#2b +{{}} + // cxx2a-warning@#2b {{incompatible with C++ standards before C++2a}} +}; + +constexpr bool operator+(A) { return true; } + +constexpr bool C = false; + +// Cases that should (ideally) be disambiguated as explicit(bool) in earlier +// language modes as an extension. +struct B { + // Looks like a constructor, but not the constructor of B. + explicit (A()) B(); // #3 + // cxx17-warning@#3 {{C++2a extension}} + // cxx2a-warning@#3 {{incompatible with C++ standards before C++2a}} + + // Looks like a 'constructor' of C. Actually a constructor of B. + explicit (C)(B)(A); // #4 + // cxx17-warning@#4 {{C++2a extension}} + // cxx2a-warning@#4 {{incompatible with C++ standards before C++2a}} + + explicit (operator+(A())) operator int(); // #5 + // cxx17-error@#5 {{requires a type specifier}} cxx17-error@#5 {{expected ';'}} + // cxx17-warning@#5 {{will be parsed as explicit(bool)}} + // cxx2a-warning@#5 {{incompatible with C++ standards before C++2a}} +}; + +} From cd4c65f91d5a77de28239979b772a8022dc6627a Mon Sep 17 00:00:00 2001 From: serge-sans-paille Date: Thu, 16 Jan 2020 21:53:32 +0100 Subject: [PATCH 011/374] Add __warn_memset_zero_len builtin as a workaround for glibc issue Glibc issue: https://sourceware.org/bugzilla/show_bug.cgi?id=25399 The fix consist in considering the missing function as a builtin lowered to a nop. Differential Revision: https://reviews.llvm.org/D72869 (cherry picked from commit d293417931d3a9d46799b42795988ca3b5cfd766) --- clang/include/clang/Basic/Builtins.def | 3 +++ clang/lib/CodeGen/CGBuiltin.cpp | 2 ++ clang/test/CodeGen/builtins.c | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index d388afe7fae6..1a6c85ce2dd3 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -788,6 +788,9 @@ BUILTIN(__builtin_abort, "v", "Fnr") BUILTIN(__builtin_index, "c*cC*i", "Fn") BUILTIN(__builtin_rindex, "c*cC*i", "Fn") +// ignored glibc builtin, see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 +BUILTIN(__warn_memset_zero_len, "v", "nU") + // Microsoft builtins. These are only active with -fms-extensions. LANGBUILTIN(_alloca, "v*z", "n", ALL_MS_LANGUAGES) LANGBUILTIN(__annotation, "wC*.","n", ALL_MS_LANGUAGES) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 09fd3087b494..2d20f92fbb3d 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3222,6 +3222,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, Builder.CreateZExt(EmitSignBit(*this, EmitScalarExpr(E->getArg(0))), ConvertType(E->getType()))); } + case Builtin::BI__warn_memset_zero_len: + return RValue::getIgnored(); case Builtin::BI__annotation: { // Re-encode each wide string to UTF8 and make an MDString. SmallVector Strings; diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c index 591416d00cc7..9f2a74f6c873 100644 --- a/clang/test/CodeGen/builtins.c +++ b/clang/test/CodeGen/builtins.c @@ -453,6 +453,13 @@ void test_builtin_launder(int *p) { int *d = __builtin_launder(p); } +// __warn_memset_zero_len should be NOP, see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 +// CHECK-LABEL: define void @test___warn_memset_zero_len +void test___warn_memset_zero_len() { + // CHECK-NOT: @__warn_memset_zero_len + __warn_memset_zero_len(); +} + // Behavior of __builtin_os_log differs between platforms, so only test on X86 #ifdef __x86_64__ From f06cd8c8c8e2adc5dad66a851bc1df1ecdd1b58e Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Wed, 15 Jan 2020 13:58:29 -0800 Subject: [PATCH 012/374] [libcxx] Use mtx_plain | mtx_recursive following C11 API The C11 API specifies that to initialize a recursive mutex, mtx_plain | mtx_recursive should be used with mtx_init. Differential Revision: https://reviews.llvm.org/D72809 (cherry picked from commit 3481e5d7ed08d068a4e3427cb1afcd8bf2acafdc) --- libcxx/include/__threading_support | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/include/__threading_support b/libcxx/include/__threading_support index 026429f6a8ec..dbf313a1bf22 100644 --- a/libcxx/include/__threading_support +++ b/libcxx/include/__threading_support @@ -445,7 +445,7 @@ int __libcpp_tls_set(__libcpp_tls_key __key, void *__p) int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t *__m) { - return mtx_init(__m, mtx_recursive) == thrd_success ? 0 : EINVAL; + return mtx_init(__m, mtx_plain | mtx_recursive) == thrd_success ? 0 : EINVAL; } int __libcpp_recursive_mutex_lock(__libcpp_recursive_mutex_t *__m) From afbebff6cd7be7329bda4500dbfacfc94ff8edba Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Wed, 15 Jan 2020 14:32:14 +0000 Subject: [PATCH 013/374] [ELF] Avoid false-positive assert in getErrPlace() This assertion was added as part of D70659 but did not account for .bss input sections. I noticed that this assert was incorrectly triggering while building FreeBSD for MIPS64. Fixed by relaxing the assert to also account for SHT_NOBITS input sections and adjust the test mips-jalr-non-function.s to link a file with a .bss section first. Reviewed By: MaskRay, grimar Differential Revision: https://reviews.llvm.org/D72567 (cherry picked from commit 441410be471d5d0a5d1d47cf363de155e397a0c2) --- lld/ELF/Target.cpp | 2 +- lld/test/ELF/mips-jalr-non-functions.s | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index 70a68fd8db9e..d3899d0f18f1 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -95,7 +95,7 @@ template static ErrorPlace getErrPlace(const uint8_t *loc) { assert(loc != nullptr); for (InputSectionBase *d : inputSections) { auto *isec = cast(d); - if (!isec->getParent()) + if (!isec->getParent() || (isec->type & SHT_NOBITS)) continue; const uint8_t *isecLoc = diff --git a/lld/test/ELF/mips-jalr-non-functions.s b/lld/test/ELF/mips-jalr-non-functions.s index cdb60cd39e83..70f899c48f6c 100644 --- a/lld/test/ELF/mips-jalr-non-functions.s +++ b/lld/test/ELF/mips-jalr-non-functions.s @@ -6,7 +6,12 @@ ## relocations to avoid generating binaries that crash when executed. # RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o -# RUN: ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s -check-prefix WARNING-MESSAGE +## Link in another object file with a .bss as a regression test: +## Previously LLD asserted when skipping over .bss sections when determining the +## location for a warning/error message. By adding another file with a .bss +## section before the actual %t.o we can reproduce this case. +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %S/Inputs/common.s -o %t-common.o +# RUN: ld.lld -shared %t-common.o %t.o -o %t.so 2>&1 | FileCheck %s -check-prefix WARNING-MESSAGE # RUN: llvm-objdump --no-show-raw-insn --no-leading-addr -d %t.so | FileCheck %s .set noreorder From a10f87d5695bdd4f1e366c82fd2869f0fe1d4cfe Mon Sep 17 00:00:00 2001 From: Joachim Protze Date: Wed, 15 Jan 2020 07:07:47 -1000 Subject: [PATCH 014/374] [OpenMP][Tool] Fix memory leak and double-allocation Fix the memory leak pointed out in https://reviews.llvm.org/D70412. And a second one due to double-allocation. Reviewed by: Hahnfeld Differential revision: https://reviews.llvm.org/D72779 (cherry picked from commit 39f746d8def66ef8f5c4d3f1eb4c4cee4baac988) --- openmp/tools/archer/ompt-tsan.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/openmp/tools/archer/ompt-tsan.cpp b/openmp/tools/archer/ompt-tsan.cpp index 50b98e2a4851..39512d38e3c2 100644 --- a/openmp/tools/archer/ompt-tsan.cpp +++ b/openmp/tools/archer/ompt-tsan.cpp @@ -114,7 +114,6 @@ void __attribute__((weak)) __tsan_flush_memory() {} } #endif ArcherFlags *archer_flags; -TsanFlags *tsan_flags; // The following definitions are pasted from "llvm/Support/Compiler.h" to allow // the code @@ -862,10 +861,8 @@ static void ompt_tsan_mutex_released(ompt_mutex_t kind, static int ompt_tsan_initialize(ompt_function_lookup_t lookup, int device_num, ompt_data_t *tool_data) { - const char *options = getenv("ARCHER_OPTIONS"); - archer_flags = new ArcherFlags(options); - options = getenv("TSAN_OPTIONS"); - tsan_flags = new TsanFlags(options); + const char *options = getenv("TSAN_OPTIONS"); + TsanFlags tsan_flags(options); ompt_set_callback_t ompt_set_callback = (ompt_set_callback_t)lookup("ompt_set_callback"); @@ -898,7 +895,7 @@ static int ompt_tsan_initialize(ompt_function_lookup_t lookup, SET_CALLBACK_T(mutex_released, mutex); SET_OPTIONAL_CALLBACK_T(reduction, sync_region, hasReductionCallback, ompt_set_never); - if (!tsan_flags->ignore_noninstrumented_modules) + if (!tsan_flags.ignore_noninstrumented_modules) fprintf( stderr, "Warning: please export TSAN_OPTIONS='ignore_noninstrumented_modules=1' " From 572c7f6fe7b55ce5f7591fb7a445dea27329074a Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Wed, 15 Jan 2020 17:41:15 -0500 Subject: [PATCH 015/374] Revert "[OPENMP]Do not use RTTI by default for NVPTX devices." This reverts commit 23058f9dd4d7e18239fd63b6da52549514b45fda. It breaks builds of cuda code somehow in some cases. (cherry picked from commit 6b29aa21180cf14bfb619d38fc4826913cabfb66) --- clang/lib/Driver/ToolChain.cpp | 3 +-- clang/test/Driver/openmp-offload-gpu.cpp | 20 -------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 clang/test/Driver/openmp-offload-gpu.cpp diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 3ebbd30195b3..cab97b1a601a 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -68,8 +68,7 @@ static ToolChain::RTTIMode CalculateRTTIMode(const ArgList &Args, } // -frtti is default, except for the PS4 CPU. - return (Triple.isPS4CPU() || Triple.isNVPTX()) ? ToolChain::RM_Disabled - : ToolChain::RM_Enabled; + return (Triple.isPS4CPU()) ? ToolChain::RM_Disabled : ToolChain::RM_Enabled; } ToolChain::ToolChain(const Driver &D, const llvm::Triple &T, diff --git a/clang/test/Driver/openmp-offload-gpu.cpp b/clang/test/Driver/openmp-offload-gpu.cpp deleted file mode 100644 index 9da7308506ae..000000000000 --- a/clang/test/Driver/openmp-offload-gpu.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/// -/// Perform several driver tests for OpenMP offloading -/// - -// REQUIRES: clang-driver -// REQUIRES: x86-registered-target -// REQUIRES: powerpc-registered-target -// REQUIRES: nvptx-registered-target - -/// ########################################################################### - -/// PTXAS is passed -c flag by default when offloading to an NVIDIA device using OpenMP -/// Check that the flag is passed when -fopenmp-relocatable-target is used. -// RUN: %clangxx -### -fopenmp=libomp -fopenmp-targets=nvptx64-nvidia-cuda \ -// RUN: -save-temps -no-canonical-prefixes %s -x c++ -c 2>&1 \ -// RUN: | FileCheck -check-prefix=CHK-RTTI %s - -// CHK-RTTI: clang{{.*}}" "-triple" "nvptx64-nvidia-cuda" -// CHK-RTTI-SAME: "-fno-rtti" - From 9007f06af0e009f41b876ae30e6b1ca96feee02e Mon Sep 17 00:00:00 2001 From: Amy Huang Date: Wed, 15 Jan 2020 14:34:19 -0800 Subject: [PATCH 016/374] Revert "Allow system header to provide their own implementation of some builtin" This reverts commit 921f871ac438175ca8fcfcafdfcfac4d7ddf3905 because it causes libc++ code to trigger __warn_memset_zero_len. See https://reviews.llvm.org/D71082. (cherry picked from commit 3d210ed3d1880c615776b07d1916edb400c245a6) --- clang/include/clang/AST/Decl.h | 3 --- clang/lib/AST/Decl.cpp | 8 -------- clang/lib/CodeGen/CGExpr.cpp | 9 +-------- clang/lib/CodeGen/CodeGenModule.cpp | 5 ----- clang/test/CodeGen/memcpy-nobuiltin.c | 15 --------------- clang/test/CodeGen/memcpy-nobuiltin.inc | 19 ------------------- 6 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 clang/test/CodeGen/memcpy-nobuiltin.c delete mode 100644 clang/test/CodeGen/memcpy-nobuiltin.inc diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 43c6c7b85db4..620ab4b089db 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2309,9 +2309,6 @@ class FunctionDecl : public DeclaratorDecl, /// true through IsAligned. bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const; - /// Determine if this function provides an inline implementation of a builtin. - bool isInlineBuiltinDeclaration() const; - /// Determine whether this is a destroying operator delete. bool isDestroyingOperatorDelete() const; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 0d30f64b992e..be59d88b73f1 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3046,14 +3046,6 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const return Params == FPT->getNumParams(); } -bool FunctionDecl::isInlineBuiltinDeclaration() const { - if (!getBuiltinID()) - return false; - - const FunctionDecl *Definition; - return hasBody(Definition) && Definition->isInlineSpecified(); -} - bool FunctionDecl::isDestroyingOperatorDelete() const { // C++ P0722: // Within a class C, a single object deallocation function with signature diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 8e0604181fb1..b23d9df5f4ba 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4621,15 +4621,8 @@ RValue CodeGenFunction::EmitSimpleCallExpr(const CallExpr *E, } static CGCallee EmitDirectCallee(CodeGenFunction &CGF, const FunctionDecl *FD) { - if (auto builtinID = FD->getBuiltinID()) { - // Replaceable builtin provide their own implementation of a builtin. Unless - // we are in the builtin implementation itself, don't call the actual - // builtin. If we are in the builtin implementation, avoid trivial infinite - // recursion. - if (!FD->isInlineBuiltinDeclaration() || - CGF.CurFn->getName() == FD->getName()) - return CGCallee::forBuiltin(builtinID, FD); + return CGCallee::forBuiltin(builtinID, FD); } llvm::Constant *calleePtr = EmitFunctionDeclPointer(CGF.CGM, FD); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 57beda26677c..038078bbe88d 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1840,11 +1840,6 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, else if (const auto *SA = FD->getAttr()) F->setSection(SA->getName()); - if (FD->isInlineBuiltinDeclaration()) { - F->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoBuiltin); - } - if (FD->isReplaceableGlobalAllocationFunction()) { // A replaceable global allocation function does not act like a builtin by // default, only if it is invoked by a new-expression or delete-expression. diff --git a/clang/test/CodeGen/memcpy-nobuiltin.c b/clang/test/CodeGen/memcpy-nobuiltin.c deleted file mode 100644 index fb51d87413a1..000000000000 --- a/clang/test/CodeGen/memcpy-nobuiltin.c +++ /dev/null @@ -1,15 +0,0 @@ -// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_DECL | FileCheck --check-prefix=CHECK-WITH-DECL %s -// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -UWITH_DECL | FileCheck --check-prefix=CHECK-NO-DECL %s -// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_SELF_REFERENCE_DECL | FileCheck --check-prefix=CHECK-SELF-REF-DECL %s -// -// CHECK-WITH-DECL-NOT: @llvm.memcpy -// CHECK-NO-DECL: @llvm.memcpy -// CHECK-SELF-REF-DECL: @llvm.memcpy -// -#include -void test(void *dest, void const *from, size_t n) { - memcpy(dest, from, n); - - static char buffer[1]; - memcpy(buffer, from, 2); // expected-warning {{'memcpy' will always overflow; destination buffer has size 1, but size argument is 2}} -} diff --git a/clang/test/CodeGen/memcpy-nobuiltin.inc b/clang/test/CodeGen/memcpy-nobuiltin.inc deleted file mode 100644 index 25eab0a9ffd0..000000000000 --- a/clang/test/CodeGen/memcpy-nobuiltin.inc +++ /dev/null @@ -1,19 +0,0 @@ -#include -extern void *memcpy(void *dest, void const *from, size_t n); - -#ifdef WITH_DECL -inline void *memcpy(void *dest, void const *from, size_t n) { - char const *ifrom = from; - char *idest = dest; - while (n--) - *idest++ = *ifrom++; - return dest; -} -#endif -#ifdef WITH_SELF_REFERENCE_DECL -inline void *memcpy(void *dest, void const *from, size_t n) { - if (n != 0) - memcpy(dest, from, n); - return dest; -} -#endif From b28326516ca3ad9d51688532e944f491ce8b7908 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Fri, 17 Jan 2020 12:51:06 +0100 Subject: [PATCH 017/374] Revert 9007f06af0e "Revert "Allow system header to provide their own implementation of some builtin"" This should no longer be necessary after cd4c65f91d5 "Add __warn_memset_zero_len builtin as a workaround for glibc issue" --- clang/include/clang/AST/Decl.h | 3 +++ clang/lib/AST/Decl.cpp | 8 ++++++++ clang/lib/CodeGen/CGExpr.cpp | 9 ++++++++- clang/lib/CodeGen/CodeGenModule.cpp | 5 +++++ clang/test/CodeGen/memcpy-nobuiltin.c | 15 +++++++++++++++ clang/test/CodeGen/memcpy-nobuiltin.inc | 19 +++++++++++++++++++ 6 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGen/memcpy-nobuiltin.c create mode 100644 clang/test/CodeGen/memcpy-nobuiltin.inc diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 620ab4b089db..43c6c7b85db4 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2309,6 +2309,9 @@ class FunctionDecl : public DeclaratorDecl, /// true through IsAligned. bool isReplaceableGlobalAllocationFunction(bool *IsAligned = nullptr) const; + /// Determine if this function provides an inline implementation of a builtin. + bool isInlineBuiltinDeclaration() const; + /// Determine whether this is a destroying operator delete. bool isDestroyingOperatorDelete() const; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index be59d88b73f1..0d30f64b992e 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -3046,6 +3046,14 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const return Params == FPT->getNumParams(); } +bool FunctionDecl::isInlineBuiltinDeclaration() const { + if (!getBuiltinID()) + return false; + + const FunctionDecl *Definition; + return hasBody(Definition) && Definition->isInlineSpecified(); +} + bool FunctionDecl::isDestroyingOperatorDelete() const { // C++ P0722: // Within a class C, a single object deallocation function with signature diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index b23d9df5f4ba..8e0604181fb1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -4621,8 +4621,15 @@ RValue CodeGenFunction::EmitSimpleCallExpr(const CallExpr *E, } static CGCallee EmitDirectCallee(CodeGenFunction &CGF, const FunctionDecl *FD) { + if (auto builtinID = FD->getBuiltinID()) { - return CGCallee::forBuiltin(builtinID, FD); + // Replaceable builtin provide their own implementation of a builtin. Unless + // we are in the builtin implementation itself, don't call the actual + // builtin. If we are in the builtin implementation, avoid trivial infinite + // recursion. + if (!FD->isInlineBuiltinDeclaration() || + CGF.CurFn->getName() == FD->getName()) + return CGCallee::forBuiltin(builtinID, FD); } llvm::Constant *calleePtr = EmitFunctionDeclPointer(CGF.CGM, FD); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 038078bbe88d..57beda26677c 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1840,6 +1840,11 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, else if (const auto *SA = FD->getAttr()) F->setSection(SA->getName()); + if (FD->isInlineBuiltinDeclaration()) { + F->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoBuiltin); + } + if (FD->isReplaceableGlobalAllocationFunction()) { // A replaceable global allocation function does not act like a builtin by // default, only if it is invoked by a new-expression or delete-expression. diff --git a/clang/test/CodeGen/memcpy-nobuiltin.c b/clang/test/CodeGen/memcpy-nobuiltin.c new file mode 100644 index 000000000000..fb51d87413a1 --- /dev/null +++ b/clang/test/CodeGen/memcpy-nobuiltin.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_DECL | FileCheck --check-prefix=CHECK-WITH-DECL %s +// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -UWITH_DECL | FileCheck --check-prefix=CHECK-NO-DECL %s +// RUN: %clang_cc1 -verify -S -emit-llvm -o- %s -isystem %S -DWITH_SELF_REFERENCE_DECL | FileCheck --check-prefix=CHECK-SELF-REF-DECL %s +// +// CHECK-WITH-DECL-NOT: @llvm.memcpy +// CHECK-NO-DECL: @llvm.memcpy +// CHECK-SELF-REF-DECL: @llvm.memcpy +// +#include +void test(void *dest, void const *from, size_t n) { + memcpy(dest, from, n); + + static char buffer[1]; + memcpy(buffer, from, 2); // expected-warning {{'memcpy' will always overflow; destination buffer has size 1, but size argument is 2}} +} diff --git a/clang/test/CodeGen/memcpy-nobuiltin.inc b/clang/test/CodeGen/memcpy-nobuiltin.inc new file mode 100644 index 000000000000..25eab0a9ffd0 --- /dev/null +++ b/clang/test/CodeGen/memcpy-nobuiltin.inc @@ -0,0 +1,19 @@ +#include +extern void *memcpy(void *dest, void const *from, size_t n); + +#ifdef WITH_DECL +inline void *memcpy(void *dest, void const *from, size_t n) { + char const *ifrom = from; + char *idest = dest; + while (n--) + *idest++ = *ifrom++; + return dest; +} +#endif +#ifdef WITH_SELF_REFERENCE_DECL +inline void *memcpy(void *dest, void const *from, size_t n) { + if (n != 0) + memcpy(dest, from, n); + return dest; +} +#endif From 9aacec8331d9bc1f80046e5954be1f5e0e0acd42 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Fri, 17 Jan 2020 11:50:00 -0800 Subject: [PATCH 018/374] [ELF] Allow R_PLT_PC (R_PC) to a hidden undefined weak symbol This essentially reverts b841e119d77ed0502e3a2e710f26a899bef28b3c. Such code construct can be used in the following way: // glibc/stdlib/exit.c // clang -fuse-ld=lld => succeeded // clang -fuse-ld=lld -fpie -pie => relocation R_PLT_PC cannot refer to absolute symbol __attribute__((weak, visibility("hidden"))) extern void __call_tls_dtors(); void __run_exit_handlers() { if (__call_tls_dtors) __call_tls_dtors(); } Since we allow R_PLT_PC in -no-pie mode, it makes sense to allow it in -pie mode as well. Reviewed By: pcc Differential Revision: https://reviews.llvm.org/D72943 (cherry picked from commit 6ab89c3c5df8b679e6ee240a13356309c048fc71) --- lld/ELF/Relocations.cpp | 8 ++++++++ lld/test/ELF/weak-undef-hidden.s | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 4731554e0c0d..ced9991f2003 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -408,6 +408,14 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym, assert(absVal && relE); + // Allow R_PLT_PC (optimized to R_PC here) to a hidden undefined weak symbol + // in PIC mode. This is a little strange, but it allows us to link function + // calls to such symbols (e.g. glibc/stdlib/exit.c:__run_exit_handlers). + // Normally such a call will be guarded with a comparison, which will load a + // zero from the GOT. + if (sym.isUndefWeak()) + return true; + // We set the final symbols values for linker script defined symbols later. // They always can be computed as a link time constant. if (sym.scriptDefined) diff --git a/lld/test/ELF/weak-undef-hidden.s b/lld/test/ELF/weak-undef-hidden.s index 632e051ffdc1..2baad5738c36 100644 --- a/lld/test/ELF/weak-undef-hidden.s +++ b/lld/test/ELF/weak-undef-hidden.s @@ -2,6 +2,11 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o // RUN: ld.lld %t.o -o %t.so -shared // RUN: llvm-readobj -r -S --section-data %t.so | FileCheck %s +// RUN: ld.lld %t.o -o %t -pie +// RUN: llvm-readobj -r -S --section-data %t | FileCheck %s + +/// This is usually guarded with a comparison. Don't report an error. +call g .data .weak g From de4b2a7fad6571d8227df42f73d373574c4b5fe1 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 20 Jan 2020 11:05:32 +0100 Subject: [PATCH 019/374] nop change to test the buildkite trigger --- llvm/docs/ReleaseNotes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst index be9b704d9bf3..f48fae25a086 100644 --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -98,6 +98,8 @@ Non-comprehensive list of changes in this release which has 3 values: ``none``, ``non-leaf``, and ``all``. The values mean what functions should retain frame pointers. +* ... + Changes to the LLVM IR ---------------------- From 587b3b2a13d27c50006607a6b7e7458e6d7cc3fa Mon Sep 17 00:00:00 2001 From: mydeveloperday Date: Sun, 19 Jan 2020 15:56:04 +0000 Subject: [PATCH 020/374] [clang-format] Expand the SpacesAroundConditions option to include catch statements Summary: This diff expands the SpacesAroundConditions option added in D68346 to include adding spaces to catch statements. Reviewed By: MyDeveloperDay Patch by: timwoj Differential Revision: https://reviews.llvm.org/D72793 (cherry picked from commit ea2be452542c81b04621e26c0d5e83be565f07e2) --- clang/lib/Format/TokenAnnotator.cpp | 2 +- clang/unittests/Format/FormatTest.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d5d394e61926..88564e02f23e 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2596,7 +2596,7 @@ bool TokenAnnotator::spaceRequiredBeforeParens(const FormatToken &Right) const { /// otherwise. static bool isKeywordWithCondition(const FormatToken &Tok) { return Tok.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, - tok::kw_constexpr); + tok::kw_constexpr, tok::kw_catch); } bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 2d67b9759d7f..c47e2e9a116e 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -14927,6 +14927,7 @@ TEST_F(FormatTest, SpacesInConditionalStatement) { verifyFormat("while ( a )\n return;", Spaces); verifyFormat("while ( (a && b) )\n return;", Spaces); verifyFormat("do {\n} while ( 1 != 0 );", Spaces); + verifyFormat("try {\n} catch ( const std::exception & ) {\n}", Spaces); // Check that space on the left of "::" is inserted as expected at beginning // of condition. verifyFormat("while ( ::func() )\n return;", Spaces); From 138451c771abe013b7b99650faeb7ae6010f7a8d Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 19 Jan 2020 21:53:10 -0800 Subject: [PATCH 021/374] [StackColoring] Remap FixedStackPseudoSourceValue frame index referenced by MachineMemOperand StackColoring::remapInstructions() remaps MachineOperand frame index (e.g. %stack.1 -> %stack.0) but does not remap FixedStackPseudoSourceValue frame index (e.g. store 4 into %stack.1.ap2.i.i) referenced by MachineMemoryOperand. This can cause an assertion failure when LiveDebugValues references a dead stack object. It is difficult to craft a test case. -g, va_copy and stack-coloring are required. I can only reproduce it on ppc32. (cherry picked from commit eaab1bf21e1d6803fd217fe6052537fc33b06837) (cherry picked from commit 854f7be20a0cb1a95671a16d6cc8200107ee25f4) (cherry picked from commit 7a8b0b1595e7dc878b48cf9bbaa652087a6895db) --- llvm/lib/CodeGen/StackColoring.cpp | 19 ++ .../CodeGen/PowerPC/stack-coloring-vararg.mir | 171 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 llvm/test/CodeGen/PowerPC/stack-coloring-vararg.mir diff --git a/llvm/lib/CodeGen/StackColoring.cpp b/llvm/lib/CodeGen/StackColoring.cpp index b6e81116286f..40bc36c3030b 100644 --- a/llvm/lib/CodeGen/StackColoring.cpp +++ b/llvm/lib/CodeGen/StackColoring.cpp @@ -960,6 +960,7 @@ void StackColoring::remapInstructions(DenseMap &SlotRemap) { } // Remap all instructions to the new stack slots. + std::vector> SSRefs(MFI->getObjectIndexEnd()); for (MachineBasicBlock &BB : *MF) for (MachineInstr &I : BB) { // Skip lifetime markers. We'll remove them soon. @@ -1025,6 +1026,16 @@ void StackColoring::remapInstructions(DenseMap &SlotRemap) { SmallVector NewMMOs; bool ReplaceMemOps = false; for (MachineMemOperand *MMO : I.memoperands()) { + // Collect MachineMemOperands which reference + // FixedStackPseudoSourceValues with old frame indices. + if (const auto *FSV = dyn_cast_or_null( + MMO->getPseudoValue())) { + int FI = FSV->getFrameIndex(); + auto To = SlotRemap.find(FI); + if (To != SlotRemap.end()) + SSRefs[FI].push_back(MMO); + } + // If this memory location can be a slot remapped here, // we remove AA information. bool MayHaveConflictingAAMD = false; @@ -1062,6 +1073,14 @@ void StackColoring::remapInstructions(DenseMap &SlotRemap) { I.setMemRefs(*MF, NewMMOs); } + // Rewrite MachineMemOperands that reference old frame indices. + for (auto E : enumerate(SSRefs)) { + const PseudoSourceValue *NewSV = + MF->getPSVManager().getFixedStack(SlotRemap[E.index()]); + for (MachineMemOperand *Ref : E.value()) + Ref->setValue(NewSV); + } + // Update the location of C++ catch objects for the MSVC personality routine. if (WinEHFuncInfo *EHInfo = MF->getWinEHFuncInfo()) for (WinEHTryBlockMapEntry &TBME : EHInfo->TryBlockMap) diff --git a/llvm/test/CodeGen/PowerPC/stack-coloring-vararg.mir b/llvm/test/CodeGen/PowerPC/stack-coloring-vararg.mir new file mode 100644 index 000000000000..0085369e1978 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/stack-coloring-vararg.mir @@ -0,0 +1,171 @@ +# RUN: llc -run-pass=stack-coloring %s -o - | FileCheck %s + +## Test %stack.1 is merged into %stack.0 and there is no MemoryMemOperand +## referencing %stack.1. This regression test is sensitive to the StackColoring +## algorithm. Please adjust or delete this test if the merging strategy +## changes. + +# CHECK: {{^}}stack: +# CHECK-NEXT: - { id: 0, +# CHECK-NOT: - { id: 1, +# CHECK: - { id: 2, +# CHECK-NOT: %stack.1 + +--- | + ; ModuleID = '' + source_filename = "" + target datalayout = "E-m:e-p:32:32-i64:64-n32" + target triple = "powerpc-unknown-freebsd13.0" + + %struct.__va_list_tag = type { i8, i8, i16, i8*, i8* } + ; Function Attrs: argmemonly nounwind willreturn + declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #0 + define dso_local void @atf_tc_fail_nonfatal(i8* %fmt, ...) !dbg !3 { + entry: + %buf.i.i = alloca [1024 x i8], align 1 + %ap2.i.i = alloca [1 x %struct.__va_list_tag], align 4 + br i1 undef, label %format_reason_ap.exit.i, label %if.then6.i.i + + if.then6.i.i: ; preds = %entry + %0 = bitcast [1 x %struct.__va_list_tag]* %ap2.i.i to i8* + call void @llvm.lifetime.start.p0i8(i64 12, i8* nonnull %0) + call void @llvm.va_copy(i8* nonnull %0, i8* nonnull null) + ret void + + format_reason_ap.exit.i: ; preds = %entry + %1 = bitcast [1024 x i8]* %buf.i.i to i8* + call void @llvm.lifetime.start.p0i8(i64 1024, i8* nonnull %1) + call void @fprintf(i8* nonnull %1) + ret void + } + declare void @fprintf(i8*) + ; Function Attrs: nounwind + declare void @llvm.va_copy(i8*, i8*) #1 + + attributes #0 = { argmemonly nounwind willreturn } + attributes #1 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "tc.c", directory: "") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = distinct !DISubprogram(name: "atf_tc_fail_nonfatal", scope: !1, file: !1, line: 1067, type: !4, scopeLine: 1068, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) + !4 = !DISubroutineType(types: !5) + !5 = !{} + +... +--- +name: atf_tc_fail_nonfatal +alignment: 4 +tracksRegLiveness: true +registers: + - { id: 0, class: gprc } + - { id: 1, class: gprc } + - { id: 2, class: gprc } + - { id: 3, class: gprc } + - { id: 4, class: gprc } + - { id: 5, class: gprc } + - { id: 6, class: gprc } + - { id: 7, class: gprc } + - { id: 8, class: f8rc } + - { id: 9, class: f8rc } + - { id: 10, class: f8rc } + - { id: 11, class: f8rc } + - { id: 12, class: f8rc } + - { id: 13, class: f8rc } + - { id: 14, class: f8rc } + - { id: 15, class: f8rc } + - { id: 16, class: crbitrc } + - { id: 17, class: gprc } + - { id: 18, class: gprc } + - { id: 19, class: gprc } + - { id: 20, class: gprc } +liveins: + - { reg: '$r3', virtual-reg: '%0' } + - { reg: '$r4', virtual-reg: '%1' } + - { reg: '$r5', virtual-reg: '%2' } + - { reg: '$r6', virtual-reg: '%3' } + - { reg: '$r7', virtual-reg: '%4' } + - { reg: '$r8', virtual-reg: '%5' } + - { reg: '$r9', virtual-reg: '%6' } + - { reg: '$r10', virtual-reg: '%7' } + - { reg: '$f1', virtual-reg: '%8' } + - { reg: '$f2', virtual-reg: '%9' } + - { reg: '$f3', virtual-reg: '%10' } + - { reg: '$f4', virtual-reg: '%11' } + - { reg: '$f5', virtual-reg: '%12' } + - { reg: '$f6', virtual-reg: '%13' } + - { reg: '$f7', virtual-reg: '%14' } + - { reg: '$f8', virtual-reg: '%15' } +frameInfo: + maxAlignment: 8 + hasCalls: true +fixedStack: + - { id: 0, offset: 8, size: 4, alignment: 8, isImmutable: true } +stack: + - { id: 0, name: buf.i.i, size: 1024, alignment: 1 } + - { id: 1, name: ap2.i.i, size: 12, alignment: 8 } + - { id: 2, size: 96, alignment: 8 } +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $f1, $f2, $f3, $f4, $f5, $f6, $f7, $f8 + + %15:f8rc = COPY $f8 + %14:f8rc = COPY $f7 + %13:f8rc = COPY $f6 + %12:f8rc = COPY $f5 + %11:f8rc = COPY $f4 + %10:f8rc = COPY $f3 + %9:f8rc = COPY $f2 + %8:f8rc = COPY $f1 + %7:gprc = COPY $r10 + %6:gprc = COPY $r9 + %5:gprc = COPY $r8 + %4:gprc = COPY $r7 + %3:gprc = COPY $r6 + %2:gprc = COPY $r5 + %1:gprc = COPY $r4 + %0:gprc = COPY $r3 + STW %0, 0, %stack.2 :: (store 4 into %stack.2, align 8) + STW %1, 4, %stack.2 :: (store 4 into %stack.2 + 4) + STW %2, 8, %stack.2 :: (store 4 into %stack.2 + 8, align 8) + STW %3, 12, %stack.2 :: (store 4) + STW %4, 16, %stack.2 :: (store 4 into %stack.2 + 16, align 8) + STW %5, 20, %stack.2 :: (store 4) + STW %6, 24, %stack.2 :: (store 4 into %stack.2 + 24, align 8) + STW %7, 28, %stack.2 :: (store 4) + STFD %8, 32, %stack.2 :: (store 8) + STFD %9, 40, %stack.2 :: (store 8) + STFD %10, 48, %stack.2 :: (store 8) + STFD %11, 56, %stack.2 :: (store 8) + STFD %12, 64, %stack.2 :: (store 8) + STFD %13, 72, %stack.2 :: (store 8) + STFD %14, 80, %stack.2 :: (store 8) + STFD %15, 88, %stack.2 :: (store 8) + %16:crbitrc = IMPLICIT_DEF + BC killed %16, %bb.2 + B %bb.1 + + bb.1.if.then6.i.i: + LIFETIME_START %stack.1.ap2.i.i + %17:gprc = LWZ 8, $zero :: (load 4, align 8) + STW killed %17, 8, %stack.1.ap2.i.i :: (store 4 into %stack.1.ap2.i.i + 8, align 8) + %18:gprc = LWZ 4, $zero :: (load 4) + STW killed %18, 4, %stack.1.ap2.i.i :: (store 4 into %stack.1.ap2.i.i + 4, align 8) + %19:gprc = LWZ 0, $zero :: (load 4, align 8) + STW killed %19, 0, %stack.1.ap2.i.i :: (store 4 into %stack.1.ap2.i.i, align 8) + BLR implicit $lr, implicit $rm + + bb.2.format_reason_ap.exit.i: + LIFETIME_START %stack.0.buf.i.i + ADJCALLSTACKDOWN 8, 0, implicit-def dead $r1, implicit $r1 + %20:gprc = ADDI %stack.0.buf.i.i, 0 + $r3 = COPY %20 + BL @fprintf, csr_svr432, implicit-def dead $lr, implicit $rm, implicit $r3, implicit-def $r1 + ADJCALLSTACKUP 8, 0, implicit-def dead $r1, implicit $r1 + BLR implicit $lr, implicit $rm + +... From 3cce3790072249cbe51b96cea26bc78019c11fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Holger=20W=C3=BCnsche?= Date: Tue, 21 Jan 2020 09:27:56 -0800 Subject: [PATCH 022/374] [HIP] use GetProgramPath for executable discovery This change replaces the manual building of executable paths using llvm::sys::path::append with GetProgramPath. This enables adding other paths in case executables reside in different directories and makes the code easier to read. Differential Revision: https://reviews.llvm.org/D72903 (cherry picked from commit 24d7a0935bea390fc444f6ac8cf02447fe8d6917) --- clang/lib/Driver/ToolChains/HIP.cpp | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/clang/lib/Driver/ToolChains/HIP.cpp b/clang/lib/Driver/ToolChains/HIP.cpp index f89e648948ab..da7004cf283f 100644 --- a/clang/lib/Driver/ToolChains/HIP.cpp +++ b/clang/lib/Driver/ToolChains/HIP.cpp @@ -105,9 +105,8 @@ const char *AMDGCN::Linker::constructLLVMLinkCommand( CmdArgs.push_back("-o"); auto OutputFileName = getOutputFileName(C, OutputFilePrefix, "-linked", "bc"); CmdArgs.push_back(OutputFileName); - SmallString<128> ExecPath(C.getDriver().Dir); - llvm::sys::path::append(ExecPath, "llvm-link"); - const char *Exec = Args.MakeArgString(ExecPath); + const char *Exec = + Args.MakeArgString(getToolChain().GetProgramPath("llvm-link")); C.addCommand(std::make_unique(JA, *this, Exec, CmdArgs, Inputs)); return OutputFileName; } @@ -133,9 +132,8 @@ const char *AMDGCN::Linker::constructOptCommand( auto OutputFileName = getOutputFileName(C, OutputFilePrefix, "-optimized", "bc"); OptArgs.push_back(OutputFileName); - SmallString<128> OptPath(C.getDriver().Dir); - llvm::sys::path::append(OptPath, "opt"); - const char *OptExec = Args.MakeArgString(OptPath); + const char *OptExec = + Args.MakeArgString(getToolChain().GetProgramPath("opt")); C.addCommand(std::make_unique(JA, *this, OptExec, OptArgs, Inputs)); return OutputFileName; } @@ -180,9 +178,7 @@ const char *AMDGCN::Linker::constructLlcCommand( auto LlcOutputFile = getOutputFileName(C, OutputFilePrefix, "", OutputIsAsm ? "s" : "o"); LlcArgs.push_back(LlcOutputFile); - SmallString<128> LlcPath(C.getDriver().Dir); - llvm::sys::path::append(LlcPath, "llc"); - const char *Llc = Args.MakeArgString(LlcPath); + const char *Llc = Args.MakeArgString(getToolChain().GetProgramPath("llc")); C.addCommand(std::make_unique(JA, *this, Llc, LlcArgs, Inputs)); return LlcOutputFile; } @@ -196,9 +192,7 @@ void AMDGCN::Linker::constructLldCommand(Compilation &C, const JobAction &JA, // The output from ld.lld is an HSA code object file. ArgStringList LldArgs{ "-flavor", "gnu", "-shared", "-o", Output.getFilename(), InputFileName}; - SmallString<128> LldPath(C.getDriver().Dir); - llvm::sys::path::append(LldPath, "lld"); - const char *Lld = Args.MakeArgString(LldPath); + const char *Lld = Args.MakeArgString(getToolChain().GetProgramPath("lld")); C.addCommand(std::make_unique(JA, *this, Lld, LldArgs, Inputs)); } @@ -230,9 +224,8 @@ void AMDGCN::constructHIPFatbinCommand(Compilation &C, const JobAction &JA, Args.MakeArgString(std::string("-outputs=").append(OutputFileName)); BundlerArgs.push_back(BundlerOutputArg); - SmallString<128> BundlerPath(C.getDriver().Dir); - llvm::sys::path::append(BundlerPath, "clang-offload-bundler"); - const char *Bundler = Args.MakeArgString(BundlerPath); + const char *Bundler = Args.MakeArgString( + T.getToolChain().GetProgramPath("clang-offload-bundler")); C.addCommand(std::make_unique(JA, T, Bundler, BundlerArgs, Inputs)); } From 54acc20e6da529f8ab6183d912a75a94b25d2bd9 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Tue, 21 Jan 2020 17:03:00 -0500 Subject: [PATCH 023/374] [PATCH] Reland [Clang] Un-break scan-build after integrated-cc1 change The issue was reported by @xazax.hun here: https://reviews.llvm.org/D69825#1827826 "This patch (D69825) breaks scan-build-py which parses the output of "-###" to get -cc1 command. There might be other tools with the same problems. Could we either remove (in-process) from CC1Command::Print or add a line break? Having the last line as a valid invocation is valuable and there might be tools relying on that." Differential Revision: https://reviews.llvm.org/D72982 (cherry picked from commit 133a7e631cee97965e310f0d110739217427fd3d) --- clang/lib/Driver/Compilation.cpp | 2 +- clang/lib/Driver/Job.cpp | 2 +- clang/test/Driver/cc-print-options.c | 3 ++- clang/test/Driver/cuda-simple.cu | 4 ++-- clang/test/Driver/offloading-interoperability.c | 4 ++-- clang/test/Driver/option-aliases.c | 4 ++-- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/clang/lib/Driver/Compilation.cpp b/clang/lib/Driver/Compilation.cpp index ba188f5c4083..25aec3690f21 100644 --- a/clang/lib/Driver/Compilation.cpp +++ b/clang/lib/Driver/Compilation.cpp @@ -172,7 +172,7 @@ int Compilation::ExecuteCommand(const Command &C, } if (getDriver().CCPrintOptions) - *OS << "[Logging clang options]"; + *OS << "[Logging clang options]\n"; C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions); } diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp index d57c3a1cdbb8..7dab2a022d92 100644 --- a/clang/lib/Driver/Job.cpp +++ b/clang/lib/Driver/Job.cpp @@ -373,7 +373,7 @@ int Command::Execute(ArrayRef> Redirects, void CC1Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, CrashReportInfo *CrashInfo) const { - OS << " (in-process)"; + OS << " (in-process)\n"; Command::Print(OS, Terminator, Quote, CrashInfo); } diff --git a/clang/test/Driver/cc-print-options.c b/clang/test/Driver/cc-print-options.c index 77dd0fef5f98..dc7f4a3ac35c 100644 --- a/clang/test/Driver/cc-print-options.c +++ b/clang/test/Driver/cc-print-options.c @@ -3,5 +3,6 @@ // RUN: %clang -no-canonical-prefixes -S -o %t.s %s // RUN: FileCheck %s < %t.log -// CHECK: [Logging clang options]{{.*}}clang{{.*}}"-S" +// CHECK: [Logging clang options] +// CHECK: {{.*}}clang{{.*}}"-S" diff --git a/clang/test/Driver/cuda-simple.cu b/clang/test/Driver/cuda-simple.cu index b6840be4e2e5..54e18403108b 100644 --- a/clang/test/Driver/cuda-simple.cu +++ b/clang/test/Driver/cuda-simple.cu @@ -5,10 +5,10 @@ // Verify that we pass -x cuda-cpp-output to compiler after // preprocessing a CUDA file // RUN: %clang -Werror -### -save-temps -c %s 2>&1 | FileCheck %s -// CHECK: "-cc1" +// CHECK-LABEL: "-cc1" // CHECK: "-E" // CHECK: "-x" "cuda" -// CHECK-NEXT: "-cc1" +// CHECK-LABEL: "-cc1" // CHECK: "-x" "cuda-cpp-output" // // Verify that compiler accepts CUDA syntax with "-x cuda-cpp-output". diff --git a/clang/test/Driver/offloading-interoperability.c b/clang/test/Driver/offloading-interoperability.c index 9c80d91d1d78..c3a72f0dfe2a 100644 --- a/clang/test/Driver/offloading-interoperability.c +++ b/clang/test/Driver/offloading-interoperability.c @@ -8,10 +8,10 @@ // RUN: %clang -no-canonical-prefixes -### -x cuda -target powerpc64le-linux-gnu -std=c++11 --cuda-gpu-arch=sm_35 -fopenmp=libomp %s 2>&1 \ // RUN: | FileCheck %s --check-prefix NO-OPENMP-FLAGS-FOR-CUDA-DEVICE // -// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE: clang{{.*}}" "-cc1" "-triple" "nvptx64-nvidia-cuda" +// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-LABEL:clang{{.*}}" "-cc1" "-triple" "nvptx64-nvidia-cuda" // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NOT: -fopenmp // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: ptxas" "-m64" // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: fatbinary"{{( "--cuda")?}} "-64" -// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: clang{{.*}}" "-cc1" "-triple" "powerpc64le-unknown-linux-gnu" +// NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-LABEL:clang{{.*}}" "-cc1" "-triple" "powerpc64le-unknown-linux-gnu" // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE: -fopenmp // NO-OPENMP-FLAGS-FOR-CUDA-DEVICE-NEXT: {{ld(.exe)?"}} {{.*}}"-m" "elf64lppc" diff --git a/clang/test/Driver/option-aliases.c b/clang/test/Driver/option-aliases.c index 9cd9252a4832..e50289cc3bdf 100644 --- a/clang/test/Driver/option-aliases.c +++ b/clang/test/Driver/option-aliases.c @@ -3,12 +3,12 @@ // RUN: --param=FOO --output=FOO %s 2>&1 | \ // RUN: FileCheck %s -// CHECK: "-cc1" +// CHECK-LABEL: "-cc1" // CHECK: "-E" // CHECK: "-U" "FOO" // CHECK: "-U" "BAR" // CHECK: "-o" "option-aliases.i" -// CHECK-NEXT: "-cc1" +// CHECK-LABEL: "-cc1" // CHECK: "-S" // CHECK: "-o" "FOO" From 050e1a3c2688f2daf03456cf94dd3ed79e8ebe7f Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Wed, 22 Jan 2020 09:16:40 -0800 Subject: [PATCH 024/374] [AArch64] Don't rename registers with pseudo defs in Ld/St opt. If the root def of for renaming is a noop-pseudo instruction like kill, we would end up without a correct def for the renamed register, causing miscompiles. This patch conservatively bails out on any pseudo instruction. This fixes https://bugs.chromium.org/p/chromium/issues/detail?id=1037912#c70 (cherry picked from commit 300997c41a00b705ca10264c15910dd8d691ab75) --- .../AArch64/AArch64LoadStoreOptimizer.cpp | 13 ++++++++ .../CodeGen/AArch64/stp-opt-with-renaming.mir | 33 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/llvm/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp b/llvm/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp index 3156bb446963..bc91d628f0b4 100644 --- a/llvm/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp +++ b/llvm/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp @@ -1325,6 +1325,19 @@ canRenameUpToDef(MachineInstr &FirstMI, LiveRegUnits &UsedInBetween, // For defs, check if we can rename the first def of RegToRename. if (FoundDef) { + // For some pseudo instructions, we might not generate code in the end + // (e.g. KILL) and we would end up without a correct def for the rename + // register. + // TODO: This might be overly conservative and we could handle those cases + // in multiple ways: + // 1. Insert an extra copy, to materialize the def. + // 2. Skip pseudo-defs until we find an non-pseudo def. + if (MI.isPseudo()) { + LLVM_DEBUG(dbgs() << " Cannot rename pseudo instruction " << MI + << "\n"); + return false; + } + for (auto &MOP : MI.operands()) { if (!MOP.isReg() || !MOP.isDef() || MOP.isDebug() || !MOP.getReg() || !TRI->regsOverlap(MOP.getReg(), RegToRename)) diff --git a/llvm/test/CodeGen/AArch64/stp-opt-with-renaming.mir b/llvm/test/CodeGen/AArch64/stp-opt-with-renaming.mir index 018827772da5..b57e32338f07 100644 --- a/llvm/test/CodeGen/AArch64/stp-opt-with-renaming.mir +++ b/llvm/test/CodeGen/AArch64/stp-opt-with-renaming.mir @@ -469,3 +469,36 @@ body: | RET undef $lr ... +# Make sure we do not rename if pseudo-defs. Noop pseudo instructions like KILL +# may lead to a missing definition of the rename register. +# +# CHECK-LABEL: name: test14_pseudo +# CHECK: bb.0: +# CHECK-NEXT: liveins: $w8, $fp, $w25 +# CHECK: renamable $w8 = KILL killed renamable $w8, implicit-def $x8 +# CHECK-NEXT: STURXi killed renamable $x8, $fp, -40 :: (store 8) +# CHECK-NEXT: $w8 = ORRWrs $wzr, killed $w25, 0, implicit-def $x8 +# CHECK-NEXT: STURXi killed renamable $x8, $fp, -32 :: (store 8) +# CHECK-NEXT: RET undef $lr +# +name: test14_pseudo +alignment: 4 +tracksRegLiveness: true +liveins: + - { reg: '$x0' } + - { reg: '$x1' } + - { reg: '$x8' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0: + liveins: $w8, $fp, $w25 + + renamable $w8 = KILL killed renamable $w8, implicit-def $x8 + STURXi killed renamable $x8, $fp, -40 :: (store 8) + $w8 = ORRWrs $wzr, killed $w25, 0, implicit-def $x8 + STURXi killed renamable $x8, $fp, -32 :: (store 8) + RET undef $lr +... From 7adf83beece381d153f8b4a67fa341d13f9c4ba2 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 15 Jan 2020 22:24:15 +0100 Subject: [PATCH 025/374] [InstCombine] Fix worklist management in DSE (PR44552) Fixes https://bugs.llvm.org/show_bug.cgi?id=44552. We need to make sure that the store is reprocessed, because performing DSE may expose more DSE opportunities. There is a slight caveat here though: We need to make sure that we add back the store the worklist first, because that means it will be processed after the operands of the removed store have been processed. This is a general bug in InstCombine worklist management that I hope to address at some point, but for now it means we need to do this manually rather than just returning the instruction as changed. Differential Revision: https://reviews.llvm.org/D72807 (cherry picked from commit 522c030aa9b1dd1881feb5a0d0fa2639b4a5feb7) --- .../InstCombineLoadStoreAlloca.cpp | 7 ++- llvm/test/Transforms/InstCombine/pr44552.ll | 59 +++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 llvm/test/Transforms/InstCombine/pr44552.ll diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp index ebf9d24eecc4..c288a7d8d403 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -1439,9 +1439,12 @@ Instruction *InstCombiner::visitStoreInst(StoreInst &SI) { if (PrevSI->isUnordered() && equivalentAddressValues(PrevSI->getOperand(1), SI.getOperand(1))) { ++NumDeadStore; - ++BBI; + // Manually add back the original store to the worklist now, so it will + // be processed after the operands of the removed store, as this may + // expose additional DSE opportunities. + Worklist.Add(&SI); eraseInstFromFunction(*PrevSI); - continue; + return nullptr; } break; } diff --git a/llvm/test/Transforms/InstCombine/pr44552.ll b/llvm/test/Transforms/InstCombine/pr44552.ll new file mode 100644 index 000000000000..adefe829df2f --- /dev/null +++ b/llvm/test/Transforms/InstCombine/pr44552.ll @@ -0,0 +1,59 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instcombine -instcombine-infinite-loop-threshold=2 < %s | FileCheck %s + +; This used to require 10 instcombine iterations to fully optimize. +; The number of iterations grew linearly with the number of DSEd stores, +; resulting in overall quadratic runtime. + +%struct.S3 = type { i64 } + +@csmith_sink_ = dso_local global i64 0, align 1 +@g_302_7 = internal constant i32 0, align 1 +@g_313_0 = internal global i16 0, align 1 +@g_313_1 = internal global i32 0, align 1 +@g_313_2 = internal global i32 0, align 1 +@g_313_3 = internal global i32 0, align 1 +@g_313_4 = internal global i16 0, align 1 +@g_313_5 = internal global i16 0, align 1 +@g_313_6 = internal global i16 0, align 1 +@g_316 = internal global %struct.S3 zeroinitializer, align 1 +@g_316_1_0 = internal global i16 0, align 1 + +define i16 @main() { +; CHECK-LABEL: @main( +; CHECK-NEXT: entry: +; CHECK-NEXT: store i64 0, i64* @csmith_sink_, align 8 +; CHECK-NEXT: ret i16 0 +; +entry: + store i64 0, i64* @csmith_sink_, align 1 + %0 = load i16, i16* @g_313_0, align 1 + %conv2 = sext i16 %0 to i64 + store i64 %conv2, i64* @csmith_sink_, align 1 + %1 = load i32, i32* @g_313_1, align 1 + %conv3 = zext i32 %1 to i64 + store i64 %conv3, i64* @csmith_sink_, align 1 + %2 = load i32, i32* @g_313_2, align 1 + %conv4 = sext i32 %2 to i64 + store i64 %conv4, i64* @csmith_sink_, align 1 + %3 = load i32, i32* @g_313_3, align 1 + %conv5 = zext i32 %3 to i64 + store i64 %conv5, i64* @csmith_sink_, align 1 + %4 = load i16, i16* @g_313_4, align 1 + %conv6 = sext i16 %4 to i64 + store i64 %conv6, i64* @csmith_sink_, align 1 + %5 = load i16, i16* @g_313_5, align 1 + %conv7 = sext i16 %5 to i64 + store i64 %conv7, i64* @csmith_sink_, align 1 + %6 = load i16, i16* @g_313_6, align 1 + %conv8 = sext i16 %6 to i64 + store i64 %conv8, i64* @csmith_sink_, align 1 + %7 = load i64, i64* getelementptr inbounds (%struct.S3, %struct.S3* @g_316, i32 0, i32 0), align 1 + store i64 %7, i64* @csmith_sink_, align 1 + %8 = load i16, i16* @g_316_1_0, align 1 + %conv9 = sext i16 %8 to i64 + store i64 %conv9, i64* @csmith_sink_, align 1 + store i64 0, i64* @csmith_sink_, align 1 + ret i16 0 +} + From b5cdee9bdce49b31c74b0cf4f8231f66bbc2708e Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Tue, 21 Jan 2020 15:49:04 -0800 Subject: [PATCH 026/374] [PGO][PGSO] Update BFI in CodeGenPrepare::optimizeSelectInst. Summary: Without the BFI update, some hot blocks are incorrectly treated as cold code. This fixes a FDO perf regression in the TSVC benchmark from D71288. Reviewers: davidxl Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D73146 (cherry picked from commit ddbc728828c70728473b47c9f7427aa9514f3d17) --- llvm/lib/CodeGen/CodeGenPrepare.cpp | 1 + llvm/test/CodeGen/X86/cmov-into-branch.ll | 25 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp index f05afd058746..003db39fe5f9 100644 --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -6122,6 +6122,7 @@ bool CodeGenPrepare::optimizeSelectInst(SelectInst *SI) { BasicBlock *StartBlock = SI->getParent(); BasicBlock::iterator SplitPt = ++(BasicBlock::iterator(LastSI)); BasicBlock *EndBlock = StartBlock->splitBasicBlock(SplitPt, "select.end"); + BFI->setBlockFreq(EndBlock, BFI->getBlockFreq(StartBlock).getFrequency()); // Delete the unconditional branch that was just created by the split. StartBlock->getTerminator()->eraseFromParent(); diff --git a/llvm/test/CodeGen/X86/cmov-into-branch.ll b/llvm/test/CodeGen/X86/cmov-into-branch.ll index 6cc81ac0833b..d9ab2550f791 100644 --- a/llvm/test/CodeGen/X86/cmov-into-branch.ll +++ b/llvm/test/CodeGen/X86/cmov-into-branch.ll @@ -165,6 +165,30 @@ define i32 @weighted_select_pgso(i32 %a, i32 %b) !prof !14 { ret i32 %sel } +; If two selects in a row are predictable, turn them into branches. +define i32 @weighted_selects(i32 %a, i32 %b) !prof !19 { +; CHECK-LABEL: weighted_selects: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %esi, %eax +; CHECK-NEXT: testl %edi, %edi +; CHECK-NEXT: movl %edi, %ecx +; CHECK-NEXT: jne .LBB11_2 +; CHECK-NEXT: # %bb.1: # %select.false +; CHECK-NEXT: movl %eax, %ecx +; CHECK-NEXT: .LBB11_2: # %select.end +; CHECK-NEXT: testl %ecx, %ecx +; CHECK-NEXT: jne .LBB11_4 +; CHECK-NEXT: # %bb.3: # %select.false2 +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: .LBB11_4: # %select.end1 +; CHECK-NEXT: retq + %cmp = icmp ne i32 %a, 0 + %sel = select i1 %cmp, i32 %a, i32 %b, !prof !16 + %cmp1 = icmp ne i32 %sel, 0 + %sel1 = select i1 %cmp1, i32 %b, i32 %a, !prof !16 + ret i32 %sel1 +} + !llvm.module.flags = !{!0} !0 = !{i32 1, !"ProfileSummary", !1} !1 = !{!2, !3, !4, !5, !6, !7, !8, !9} @@ -185,3 +209,4 @@ define i32 @weighted_select_pgso(i32 %a, i32 %b) !prof !14 { !16 = !{!"branch_weights", i32 1, i32 100} !17 = !{!"branch_weights", i32 100, i32 1} !18 = !{!"branch_weights", i32 0, i32 0} +!19 = !{!"function_entry_count", i64 100} From 1f98c2b586e4ebce68d75856699059663a052cb7 Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 31 Dec 2019 09:57:16 +0000 Subject: [PATCH 027/374] [clang-tidy] Disable Checks on If constexpr statements in template Instantiations for BugproneBranchClone and ReadabilityBracesAroundStatements Summary: fixes [[ https://bugs.llvm.org/show_bug.cgi?id=32203 | readability-braces-around-statements broken for if constexpr]] and [[ https://bugs.llvm.org/show_bug.cgi?id=44229 | bugprone-branch-clone false positive with template functions and constexpr ]] by disabling the relevant checks on if constexpr statements while inside an instantiated template. This is due to how the else branch of an if constexpr statement is folded away to a null statement if the condition evaluates to false Reviewers: alexfh, hokein, aaron.ballman, xazax.hun Reviewed By: aaron.ballman, xazax.hun Subscribers: rnkovacs, JonasToth, Jim, lebedev.ri, xazax.hun, cfe-commits Tags: #clang-tools-extra, #clang Differential Revision: https://reviews.llvm.org/D71980 (cherry picked from commit f9c46229e4ac29053747c96e08c574c6c48d544b) --- .../clang-tidy/bugprone/BranchCloneCheck.cpp | 3 +- .../BracesAroundStatementsCheck.cpp | 5 +- ...one-branch-clone-if-constexpr-template.cpp | 58 +++++++++++++++++++ ...ound-statements-constexpr-if-templates.cpp | 48 +++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone-branch-clone-if-constexpr-template.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements-constexpr-if-templates.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp index eb54aaa99445..e40b27585d3d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp @@ -59,7 +59,8 @@ namespace bugprone { void BranchCloneCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( - ifStmt(stmt().bind("if"), + ifStmt(unless(allOf(isConstexpr(), isInTemplateInstantiation())), + stmt().bind("if"), hasParent(stmt(unless(ifStmt(hasElse(equalsBoundNode("if")))))), hasElse(stmt().bind("else"))), this); diff --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp index 117ef36d78fe..da0bef32c091 100644 --- a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp @@ -123,7 +123,10 @@ void BracesAroundStatementsCheck::storeOptions( } void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(ifStmt().bind("if"), this); + Finder->addMatcher( + ifStmt(unless(allOf(isConstexpr(), isInTemplateInstantiation()))) + .bind("if"), + this); Finder->addMatcher(whileStmt().bind("while"), this); Finder->addMatcher(doStmt().bind("do"), this); Finder->addMatcher(forStmt().bind("for"), this); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-branch-clone-if-constexpr-template.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-branch-clone-if-constexpr-template.cpp new file mode 100644 index 000000000000..382330139c8c --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-branch-clone-if-constexpr-template.cpp @@ -0,0 +1,58 @@ +// RUN: %check_clang_tidy %s bugprone-branch-clone %t -- -- -std=c++17 + +void handle(int); + +template +void shouldFail() { + if constexpr (Index == 0) { + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: repeated branch in conditional chain [bugprone-branch-clone] + handle(0); + } else if constexpr (Index == 1) { + handle(1); + } else { + handle(0); + } +} + +template +void shouldPass() { + if constexpr (Index == 0) { + handle(0); + } else if constexpr (Index == 1) { + handle(1); + } else { + handle(2); + } +} + +void shouldFailNonTemplate() { + constexpr unsigned Index = 1; + if constexpr (Index == 0) { + // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: repeated branch in conditional chain [bugprone-branch-clone] + handle(0); + } else if constexpr (Index == 1) { + handle(1); + } else { + handle(0); + } +} + +void shouldPassNonTemplate() { + constexpr unsigned Index = 1; + if constexpr (Index == 0) { + handle(0); + } else if constexpr (Index == 1) { + handle(1); + } else { + handle(2); + } +} + +void run() { + shouldFail<0>(); + shouldFail<1>(); + shouldFail<2>(); + shouldPass<0>(); + shouldPass<1>(); + shouldPass<2>(); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements-constexpr-if-templates.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements-constexpr-if-templates.cpp new file mode 100644 index 000000000000..988125f9ce2d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements-constexpr-if-templates.cpp @@ -0,0 +1,48 @@ +// RUN: %check_clang_tidy %s readability-braces-around-statements %t -- -- -std=c++17 + +void handle(bool); + +template +void shouldFail() { + if constexpr (branch) + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: statement should be inside braces [readability-braces-around-statements] + handle(true); + else + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: statement should be inside braces [readability-braces-around-statements] + handle(false); +} + +template +void shouldPass() { + if constexpr (branch) { + handle(true); + } else { + handle(false); + } +} + +void shouldFailNonTemplate() { + constexpr bool branch = false; + if constexpr (branch) + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: statement should be inside braces [readability-braces-around-statements] + handle(true); + else + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: statement should be inside braces [readability-braces-around-statements] + handle(false); +} + +void shouldPass() { + constexpr bool branch = false; + if constexpr (branch) { + handle(true); + } else { + handle(false); + } +} + +void run() { + shouldFail(); + shouldFail(); + shouldPass(); + shouldPass(); +} From d64ca7abe191e5813ab171df325f2cc2693dae21 Mon Sep 17 00:00:00 2001 From: Andrei Elovikov Date: Wed, 15 Jan 2020 09:24:12 -0800 Subject: [PATCH 028/374] [SLP] Add a test showing miscompilation in AltOpcode support Reviewers: Vasilis, RKSimon, ABataev Reviewed By: RKSimon, ABataev Subscribers: ABataev, inglorion, dexonsmith, llvm-commits, vdmitrie Tags: #llvm Differential Revision: https://reviews.llvm.org/D72739 (cherry picked from commit 757fe53994c1792cbdc84526696a0e256345911f) --- .../SLPVectorizer/X86/no_alternate_divrem.ll | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll diff --git a/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll b/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll new file mode 100644 index 000000000000..b16e67564d6f --- /dev/null +++ b/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll @@ -0,0 +1,131 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -slp-vectorizer -slp-threshold=-200 -mtriple=x86_64-unknown-linux -mcpu=core-avx2 -S | FileCheck %s + +define void @test_add_sdiv(i32 *%arr1, i32 *%arr2, i32 %a0, i32 %a1, i32 %a2, i32 %a3) { +; CHECK-LABEL: @test_add_sdiv( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP1_0:%.*]] = getelementptr i32, i32* [[ARR1:%.*]], i32 0 +; CHECK-NEXT: [[GEP1_1:%.*]] = getelementptr i32, i32* [[ARR1]], i32 1 +; CHECK-NEXT: [[GEP1_2:%.*]] = getelementptr i32, i32* [[ARR1]], i32 2 +; CHECK-NEXT: [[GEP1_3:%.*]] = getelementptr i32, i32* [[ARR1]], i32 3 +; CHECK-NEXT: [[GEP2_0:%.*]] = getelementptr i32, i32* [[ARR2:%.*]], i32 0 +; CHECK-NEXT: [[GEP2_1:%.*]] = getelementptr i32, i32* [[ARR2]], i32 1 +; CHECK-NEXT: [[GEP2_2:%.*]] = getelementptr i32, i32* [[ARR2]], i32 2 +; CHECK-NEXT: [[GEP2_3:%.*]] = getelementptr i32, i32* [[ARR2]], i32 3 +; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[GEP1_0]] to <4 x i32>* +; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, <4 x i32>* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i32> undef, i32 [[A0:%.*]], i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> [[TMP2]], i32 [[A1:%.*]], i32 1 +; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i32> [[TMP3]], i32 [[A2:%.*]], i32 2 +; CHECK-NEXT: [[TMP5:%.*]] = insertelement <4 x i32> [[TMP4]], i32 [[A3:%.*]], i32 3 +; CHECK-NEXT: [[TMP6:%.*]] = add nsw <4 x i32> [[TMP5]], +; CHECK-NEXT: [[TMP7:%.*]] = add nsw <4 x i32> [[TMP1]], [[TMP6]] + +;; FIXME: Last lane of TMP6 may contain zero (if %a3 is zero). In such case, the +;; next instruction would cause division by zero resulting in SIGFPE during +;; execution. +; CHECK-NEXT: [[TMP8:%.*]] = sdiv <4 x i32> [[TMP1]], [[TMP6]] + +; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x i32> [[TMP7]], <4 x i32> [[TMP8]], <4 x i32> +; CHECK-NEXT: [[TMP10:%.*]] = bitcast i32* [[GEP2_0]] to <4 x i32>* +; CHECK-NEXT: store <4 x i32> [[TMP9]], <4 x i32>* [[TMP10]], align 4 +; CHECK-NEXT: ret void +; +entry: + %gep1.0 = getelementptr i32, i32* %arr1, i32 0 + %gep1.1 = getelementptr i32, i32* %arr1, i32 1 + %gep1.2 = getelementptr i32, i32* %arr1, i32 2 + %gep1.3 = getelementptr i32, i32* %arr1, i32 3 + %gep2.0 = getelementptr i32, i32* %arr2, i32 0 + %gep2.1 = getelementptr i32, i32* %arr2, i32 1 + %gep2.2 = getelementptr i32, i32* %arr2, i32 2 + %gep2.3 = getelementptr i32, i32* %arr2, i32 3 + %v0 = load i32, i32* %gep1.0 + %v1 = load i32, i32* %gep1.1 + %v2 = load i32, i32* %gep1.2 + %v3 = load i32, i32* %gep1.3 + %y0 = add nsw i32 %a0, 1146 + %y1 = add nsw i32 %a1, 146 + %y2 = add nsw i32 %a2, 42 + ;; %y3 is zero if %a3 is zero + %y3 = add nsw i32 %a3, 0 + %res0 = add nsw i32 %v0, %y0 + %res1 = add nsw i32 %v1, %y1 + ;; As such, doing alternate shuffling would be incorrect: + ;; %vadd = add nsw %v[0-3], %y[0-3] + ;; %vsdiv = sdiv %v[0-3], %y[0-3] + ;; %result = shuffle %vadd, %vsdiv, + ;; would be illegal. + %res2 = sdiv i32 %v2, %y2 + %res3 = add nsw i32 %v3, %y3 + store i32 %res0, i32* %gep2.0 + store i32 %res1, i32* %gep2.1 + store i32 %res2, i32* %gep2.2 + store i32 %res3, i32* %gep2.3 + ret void +} + +;; Similar test, but now div/rem is main opcode and not the alternate one. Same issue. +define void @test_urem_add(i32 *%arr1, i32 *%arr2, i32 %a0, i32 %a1, i32 %a2, i32 %a3) { +; CHECK-LABEL: @test_urem_add( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[GEP1_0:%.*]] = getelementptr i32, i32* [[ARR1:%.*]], i32 0 +; CHECK-NEXT: [[GEP1_1:%.*]] = getelementptr i32, i32* [[ARR1]], i32 1 +; CHECK-NEXT: [[GEP1_2:%.*]] = getelementptr i32, i32* [[ARR1]], i32 2 +; CHECK-NEXT: [[GEP1_3:%.*]] = getelementptr i32, i32* [[ARR1]], i32 3 +; CHECK-NEXT: [[GEP2_0:%.*]] = getelementptr i32, i32* [[ARR2:%.*]], i32 0 +; CHECK-NEXT: [[GEP2_1:%.*]] = getelementptr i32, i32* [[ARR2]], i32 1 +; CHECK-NEXT: [[GEP2_2:%.*]] = getelementptr i32, i32* [[ARR2]], i32 2 +; CHECK-NEXT: [[GEP2_3:%.*]] = getelementptr i32, i32* [[ARR2]], i32 3 +; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[GEP1_0]] to <4 x i32>* +; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, <4 x i32>* [[TMP0]], align 4 +; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i32> undef, i32 [[A0:%.*]], i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> [[TMP2]], i32 [[A1:%.*]], i32 1 +; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i32> [[TMP3]], i32 [[A2:%.*]], i32 2 +; CHECK-NEXT: [[TMP5:%.*]] = insertelement <4 x i32> [[TMP4]], i32 [[A3:%.*]], i32 3 +; CHECK-NEXT: [[TMP6:%.*]] = add nsw <4 x i32> [[TMP5]], + +;; FIXME: Last lane of TMP6 may contain zero (if %a3 is zero). In such case, the +;; next instruction would cause division by zero resulting in SIGFPE during +;; execution. +; CHECK-NEXT: [[TMP7:%.*]] = urem <4 x i32> [[TMP1]], [[TMP6]] + +; CHECK-NEXT: [[TMP8:%.*]] = add nsw <4 x i32> [[TMP1]], [[TMP6]] +; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x i32> [[TMP7]], <4 x i32> [[TMP8]], <4 x i32> +; CHECK-NEXT: [[TMP10:%.*]] = bitcast i32* [[GEP2_0]] to <4 x i32>* +; CHECK-NEXT: store <4 x i32> [[TMP9]], <4 x i32>* [[TMP10]], align 4 +; CHECK-NEXT: ret void +; +entry: + %gep1.0 = getelementptr i32, i32* %arr1, i32 0 + %gep1.1 = getelementptr i32, i32* %arr1, i32 1 + %gep1.2 = getelementptr i32, i32* %arr1, i32 2 + %gep1.3 = getelementptr i32, i32* %arr1, i32 3 + %gep2.0 = getelementptr i32, i32* %arr2, i32 0 + %gep2.1 = getelementptr i32, i32* %arr2, i32 1 + %gep2.2 = getelementptr i32, i32* %arr2, i32 2 + %gep2.3 = getelementptr i32, i32* %arr2, i32 3 + %v0 = load i32, i32* %gep1.0 + %v1 = load i32, i32* %gep1.1 + %v2 = load i32, i32* %gep1.2 + %v3 = load i32, i32* %gep1.3 + %y0 = add nsw i32 %a0, 1146 + %y1 = add nsw i32 %a1, 146 + %y2 = add nsw i32 %a2, 42 + ;; %y3 is zero if %a3 is zero + %y3 = add nsw i32 %a3, 0 + %res0 = urem i32 %v0, %y0 + %res1 = urem i32 %v1, %y1 + %res2 = urem i32 %v2, %y2 + ;; As such, doing alternate shuffling would be incorrect: + ;; %vurem = urem %v[0-3], %y[0-3] + ;; %vadd = add nsw %v[0-3], %y[0-3] + ;; %result = shuffle %vurem, %vadd, + ;; would be illegal. + %res3 = add nsw i32 %v3, %y3 + store i32 %res0, i32* %gep2.0 + store i32 %res1, i32* %gep2.1 + store i32 %res2, i32* %gep2.2 + store i32 %res3, i32* %gep2.3 + ret void +} From 029140ee1ca99e23558c774bb23257a4ea796069 Mon Sep 17 00:00:00 2001 From: Andrei Elovikov Date: Tue, 21 Jan 2020 14:25:55 -0800 Subject: [PATCH 029/374] [SLP] Don't allow Div/Rem as alternate opcodes Summary: We don't have control/verify what will be the RHS of the division, so it might happen to be zero, causing UB. Reviewers: Vasilis, RKSimon, ABataev Reviewed By: ABataev Subscribers: vporpo, ABataev, hiraditya, llvm-commits, vdmitrie Tags: #llvm Differential Revision: https://reviews.llvm.org/D72740 (cherry picked from commit e1d6d368529322edc658c893c01eaadaf8053ea6) --- .../Transforms/Vectorize/SLPVectorizer.cpp | 18 ++++- .../SLPVectorizer/X86/no_alternate_divrem.ll | 66 +++++++++---------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp index aabd974cd73e..479bca83b51e 100644 --- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -377,6 +377,18 @@ static Value *isOneOf(const InstructionsState &S, Value *Op) { return S.OpValue; } +/// \returns true if \p Opcode is allowed as part of of the main/alternate +/// instruction for SLP vectorization. +/// +/// Example of unsupported opcode is SDIV that can potentially cause UB if the +/// "shuffled out" lane would result in division by zero. +static bool isValidForAlternation(unsigned Opcode) { + if (Instruction::isIntDivRem(Opcode)) + return false; + + return true; +} + /// \returns analysis of the Instructions in \p VL described in /// InstructionsState, the Opcode that we suppose the whole list /// could be vectorized even if its structure is diverse. @@ -399,7 +411,8 @@ static InstructionsState getSameOpcode(ArrayRef VL, if (IsBinOp && isa(VL[Cnt])) { if (InstOpcode == Opcode || InstOpcode == AltOpcode) continue; - if (Opcode == AltOpcode) { + if (Opcode == AltOpcode && isValidForAlternation(InstOpcode) && + isValidForAlternation(Opcode)) { AltOpcode = InstOpcode; AltIndex = Cnt; continue; @@ -411,6 +424,9 @@ static InstructionsState getSameOpcode(ArrayRef VL, if (InstOpcode == Opcode || InstOpcode == AltOpcode) continue; if (Opcode == AltOpcode) { + assert(isValidForAlternation(Opcode) && + isValidForAlternation(InstOpcode) && + "Cast isn't safe for alternation, logic needs to be updated!"); AltOpcode = InstOpcode; AltIndex = Cnt; continue; diff --git a/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll b/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll index b16e67564d6f..468cabc598f1 100644 --- a/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll +++ b/llvm/test/Transforms/SLPVectorizer/X86/no_alternate_divrem.ll @@ -12,23 +12,22 @@ define void @test_add_sdiv(i32 *%arr1, i32 *%arr2, i32 %a0, i32 %a1, i32 %a2, i3 ; CHECK-NEXT: [[GEP2_1:%.*]] = getelementptr i32, i32* [[ARR2]], i32 1 ; CHECK-NEXT: [[GEP2_2:%.*]] = getelementptr i32, i32* [[ARR2]], i32 2 ; CHECK-NEXT: [[GEP2_3:%.*]] = getelementptr i32, i32* [[ARR2]], i32 3 -; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[GEP1_0]] to <4 x i32>* -; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, <4 x i32>* [[TMP0]], align 4 -; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i32> undef, i32 [[A0:%.*]], i32 0 -; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> [[TMP2]], i32 [[A1:%.*]], i32 1 -; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i32> [[TMP3]], i32 [[A2:%.*]], i32 2 -; CHECK-NEXT: [[TMP5:%.*]] = insertelement <4 x i32> [[TMP4]], i32 [[A3:%.*]], i32 3 -; CHECK-NEXT: [[TMP6:%.*]] = add nsw <4 x i32> [[TMP5]], -; CHECK-NEXT: [[TMP7:%.*]] = add nsw <4 x i32> [[TMP1]], [[TMP6]] - -;; FIXME: Last lane of TMP6 may contain zero (if %a3 is zero). In such case, the -;; next instruction would cause division by zero resulting in SIGFPE during -;; execution. -; CHECK-NEXT: [[TMP8:%.*]] = sdiv <4 x i32> [[TMP1]], [[TMP6]] - -; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x i32> [[TMP7]], <4 x i32> [[TMP8]], <4 x i32> -; CHECK-NEXT: [[TMP10:%.*]] = bitcast i32* [[GEP2_0]] to <4 x i32>* -; CHECK-NEXT: store <4 x i32> [[TMP9]], <4 x i32>* [[TMP10]], align 4 +; CHECK-NEXT: [[V0:%.*]] = load i32, i32* [[GEP1_0]] +; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[GEP1_1]] +; CHECK-NEXT: [[V2:%.*]] = load i32, i32* [[GEP1_2]] +; CHECK-NEXT: [[V3:%.*]] = load i32, i32* [[GEP1_3]] +; CHECK-NEXT: [[Y0:%.*]] = add nsw i32 [[A0:%.*]], 1146 +; CHECK-NEXT: [[Y1:%.*]] = add nsw i32 [[A1:%.*]], 146 +; CHECK-NEXT: [[Y2:%.*]] = add nsw i32 [[A2:%.*]], 42 +; CHECK-NEXT: [[Y3:%.*]] = add nsw i32 [[A3:%.*]], 0 +; CHECK-NEXT: [[RES0:%.*]] = add nsw i32 [[V0]], [[Y0]] +; CHECK-NEXT: [[RES1:%.*]] = add nsw i32 [[V1]], [[Y1]] +; CHECK-NEXT: [[RES2:%.*]] = sdiv i32 [[V2]], [[Y2]] +; CHECK-NEXT: [[RES3:%.*]] = add nsw i32 [[V3]], [[Y3]] +; CHECK-NEXT: store i32 [[RES0]], i32* [[GEP2_0]] +; CHECK-NEXT: store i32 [[RES1]], i32* [[GEP2_1]] +; CHECK-NEXT: store i32 [[RES2]], i32* [[GEP2_2]] +; CHECK-NEXT: store i32 [[RES3]], i32* [[GEP2_3]] ; CHECK-NEXT: ret void ; entry: @@ -77,23 +76,22 @@ define void @test_urem_add(i32 *%arr1, i32 *%arr2, i32 %a0, i32 %a1, i32 %a2, i3 ; CHECK-NEXT: [[GEP2_1:%.*]] = getelementptr i32, i32* [[ARR2]], i32 1 ; CHECK-NEXT: [[GEP2_2:%.*]] = getelementptr i32, i32* [[ARR2]], i32 2 ; CHECK-NEXT: [[GEP2_3:%.*]] = getelementptr i32, i32* [[ARR2]], i32 3 -; CHECK-NEXT: [[TMP0:%.*]] = bitcast i32* [[GEP1_0]] to <4 x i32>* -; CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, <4 x i32>* [[TMP0]], align 4 -; CHECK-NEXT: [[TMP2:%.*]] = insertelement <4 x i32> undef, i32 [[A0:%.*]], i32 0 -; CHECK-NEXT: [[TMP3:%.*]] = insertelement <4 x i32> [[TMP2]], i32 [[A1:%.*]], i32 1 -; CHECK-NEXT: [[TMP4:%.*]] = insertelement <4 x i32> [[TMP3]], i32 [[A2:%.*]], i32 2 -; CHECK-NEXT: [[TMP5:%.*]] = insertelement <4 x i32> [[TMP4]], i32 [[A3:%.*]], i32 3 -; CHECK-NEXT: [[TMP6:%.*]] = add nsw <4 x i32> [[TMP5]], - -;; FIXME: Last lane of TMP6 may contain zero (if %a3 is zero). In such case, the -;; next instruction would cause division by zero resulting in SIGFPE during -;; execution. -; CHECK-NEXT: [[TMP7:%.*]] = urem <4 x i32> [[TMP1]], [[TMP6]] - -; CHECK-NEXT: [[TMP8:%.*]] = add nsw <4 x i32> [[TMP1]], [[TMP6]] -; CHECK-NEXT: [[TMP9:%.*]] = shufflevector <4 x i32> [[TMP7]], <4 x i32> [[TMP8]], <4 x i32> -; CHECK-NEXT: [[TMP10:%.*]] = bitcast i32* [[GEP2_0]] to <4 x i32>* -; CHECK-NEXT: store <4 x i32> [[TMP9]], <4 x i32>* [[TMP10]], align 4 +; CHECK-NEXT: [[V0:%.*]] = load i32, i32* [[GEP1_0]] +; CHECK-NEXT: [[V1:%.*]] = load i32, i32* [[GEP1_1]] +; CHECK-NEXT: [[V2:%.*]] = load i32, i32* [[GEP1_2]] +; CHECK-NEXT: [[V3:%.*]] = load i32, i32* [[GEP1_3]] +; CHECK-NEXT: [[Y0:%.*]] = add nsw i32 [[A0:%.*]], 1146 +; CHECK-NEXT: [[Y1:%.*]] = add nsw i32 [[A1:%.*]], 146 +; CHECK-NEXT: [[Y2:%.*]] = add nsw i32 [[A2:%.*]], 42 +; CHECK-NEXT: [[Y3:%.*]] = add nsw i32 [[A3:%.*]], 0 +; CHECK-NEXT: [[RES0:%.*]] = urem i32 [[V0]], [[Y0]] +; CHECK-NEXT: [[RES1:%.*]] = urem i32 [[V1]], [[Y1]] +; CHECK-NEXT: [[RES2:%.*]] = urem i32 [[V2]], [[Y2]] +; CHECK-NEXT: [[RES3:%.*]] = add nsw i32 [[V3]], [[Y3]] +; CHECK-NEXT: store i32 [[RES0]], i32* [[GEP2_0]] +; CHECK-NEXT: store i32 [[RES1]], i32* [[GEP2_1]] +; CHECK-NEXT: store i32 [[RES2]], i32* [[GEP2_2]] +; CHECK-NEXT: store i32 [[RES3]], i32* [[GEP2_3]] ; CHECK-NEXT: ret void ; entry: From 6c9da109c9cee54b494b03edbfd1648b685331c3 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Sat, 18 Jan 2020 12:57:16 -0500 Subject: [PATCH 030/374] [mlir] Fix compilation with VS2019. (cherry picked from commit e3d92b7442eaf3319f84bc060492df5b7ac3e9a1) --- mlir/include/mlir/IR/Attributes.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mlir/include/mlir/IR/Attributes.h b/mlir/include/mlir/IR/Attributes.h index 49f42affb566..ed82ef111756 100644 --- a/mlir/include/mlir/IR/Attributes.h +++ b/mlir/include/mlir/IR/Attributes.h @@ -220,10 +220,11 @@ class ArrayAttr : public Attribute::AttrBase class attr_value_iterator final - : public llvm::mapped_iterator { + : public llvm::mapped_iterator { public: - explicit attr_value_iterator(iterator it) - : llvm::mapped_iterator( + explicit attr_value_iterator(ArrayAttr::iterator it) + : llvm::mapped_iterator( it, [](Attribute attr) { return attr.cast(); }) {} AttrTy operator*() { return (*this->I).template cast(); } }; From 3e429b691ec89de09324d6af33e35f1491f45b7d Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Wed, 22 Jan 2020 22:06:56 +0000 Subject: [PATCH 031/374] [cmake] Fix clang builds with BUILD_SHARED=ON and CLANG_LINK_CLANG_DYLIB=ON Summary: We were linking all the clang objects and shared libraries into libclang-cpp.so, which was causing the command line options to be registered twice. Reviewers: beanz, mgorny Reviewed By: beanz, mgorny Subscribers: mgorny, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D68520 (cherry picked from commit df839cfda09dbadc26b8be635f27da75f1f27190) --- clang/tools/clang-shlib/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/clang/tools/clang-shlib/CMakeLists.txt b/clang/tools/clang-shlib/CMakeLists.txt index a0fc8f6bfbde..16fc8a0ca29b 100644 --- a/clang/tools/clang-shlib/CMakeLists.txt +++ b/clang/tools/clang-shlib/CMakeLists.txt @@ -14,9 +14,17 @@ foreach (lib ${clang_libs}) list(APPEND _OBJECTS $) endif() list(APPEND _DEPS $) - list(APPEND _DEPS $) + get_target_property(interface ${lib} LINK_LIBRARIES) + if (interface) + list(APPEND _DEPS ${interface}) + endif() endforeach () +# clang libraries are redundant since we are linking all the individual +# object files into libclang-cpp.so, so filter them out from _DEPS. +# This avoids problems with LLVM global data when building with +# BUILD_SHARED_LIBS=ON +list(FILTER _DEPS EXCLUDE REGEX "^clang") if (CLANG_LINK_CLANG_DYLIB) set(INSTALL_WITH_TOOLCHAIN INSTALL_WITH_TOOLCHAIN) endif() From ba92233ce227753221e15bfab0326a35ef05caa1 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Wed, 22 Jan 2020 16:12:45 -0800 Subject: [PATCH 032/374] Revert "[cmake] Fix clang builds with BUILD_SHARED=ON and CLANG_LINK_CLANG_DYLIB=ON" This reverts commit df839cfda09dbadc26b8be635f27da75f1f27190. This change used cmake's list filter operation which was not added until cmake 3.6. (cherry picked from commit 4751e4f8c24bc07fdb668dc49ee559b97c1e3c22) --- clang/tools/clang-shlib/CMakeLists.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/clang/tools/clang-shlib/CMakeLists.txt b/clang/tools/clang-shlib/CMakeLists.txt index 16fc8a0ca29b..a0fc8f6bfbde 100644 --- a/clang/tools/clang-shlib/CMakeLists.txt +++ b/clang/tools/clang-shlib/CMakeLists.txt @@ -14,17 +14,9 @@ foreach (lib ${clang_libs}) list(APPEND _OBJECTS $) endif() list(APPEND _DEPS $) - get_target_property(interface ${lib} LINK_LIBRARIES) - if (interface) - list(APPEND _DEPS ${interface}) - endif() + list(APPEND _DEPS $) endforeach () -# clang libraries are redundant since we are linking all the individual -# object files into libclang-cpp.so, so filter them out from _DEPS. -# This avoids problems with LLVM global data when building with -# BUILD_SHARED_LIBS=ON -list(FILTER _DEPS EXCLUDE REGEX "^clang") if (CLANG_LINK_CLANG_DYLIB) set(INSTALL_WITH_TOOLCHAIN INSTALL_WITH_TOOLCHAIN) endif() From 85ee70e86456e3bcb3c706c404db497c5a448602 Mon Sep 17 00:00:00 2001 From: Alexandre Ganea Date: Wed, 22 Jan 2020 16:53:38 -0500 Subject: [PATCH 033/374] Clang] Fix expansion of response files in -Wp after integrated-cc1 change After rGb4a99a061f517e60985667e39519f60186cbb469, passing a response file such as -Wp,@a.rsp wasn't working anymore because .rsp expansion happens inside clang's main() function. This patch adds response file expansion in the -cc1 tool. Differential Revision: https://reviews.llvm.org/D73120 (cherry picked from commit 68d7f06092e56b17eb0cddf560a9d9fe8afb7dd8) --- clang/include/clang/Driver/Driver.h | 2 +- clang/test/Driver/Wp-args.c | 10 ++++++++++ clang/tools/driver/driver.cpp | 22 +++++++++++++--------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Driver/Driver.h b/clang/include/clang/Driver/Driver.h index fd25663bd358..6c3feaba0568 100644 --- a/clang/include/clang/Driver/Driver.h +++ b/clang/include/clang/Driver/Driver.h @@ -208,7 +208,7 @@ class Driver { /// When the clangDriver lib is used through clang.exe, this provides a /// shortcut for executing the -cc1 command-line directly, in the same /// process. - typedef int (*CC1ToolFunc)(ArrayRef argv); + typedef int (*CC1ToolFunc)(SmallVectorImpl &ArgV); CC1ToolFunc CC1Main = nullptr; private: diff --git a/clang/test/Driver/Wp-args.c b/clang/test/Driver/Wp-args.c index e01e2a2651f0..587b7b83e4ca 100644 --- a/clang/test/Driver/Wp-args.c +++ b/clang/test/Driver/Wp-args.c @@ -19,3 +19,13 @@ // MMD: "-cc1" // MMD-NOT: -MMD // MMD: "-dependency-file" "Wp-args.d" + +// Ensure response files are properly expanded with -Wp +// RUN: echo -DTEST > %t.rsp +// RUN: %clang -Wp,@%t.rsp -E %s | FileCheck -check-prefix RSP %s + +#ifdef TEST +void foo(); +#endif + +// RSP: foo() diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp index 39f6c7f62be4..4457e40ff04b 100644 --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -241,8 +241,6 @@ static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver, *NumberSignPtr = '='; } -static int ExecuteCC1Tool(ArrayRef argv); - static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); @@ -313,21 +311,27 @@ static void SetInstallDir(SmallVectorImpl &argv, TheDriver.setInstalledDir(InstalledPathParent); } -static int ExecuteCC1Tool(ArrayRef argv) { +static int ExecuteCC1Tool(SmallVectorImpl &ArgV) { // If we call the cc1 tool from the clangDriver library (through // Driver::CC1Main), we need to clean up the options usage count. The options // are currently global, and they might have been used previously by the // driver. llvm::cl::ResetAllOptionOccurrences(); - StringRef Tool = argv[1]; - void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath; + + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + llvm::cl::ExpandResponseFiles(Saver, &llvm::cl::TokenizeGNUCommandLine, ArgV, + /*MarkEOLs=*/false); + StringRef Tool = ArgV[1]; + void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; if (Tool == "-cc1") - return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP); + return cc1_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); if (Tool == "-cc1as") - return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP); + return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], + GetExecutablePathVP); if (Tool == "-cc1gen-reproducer") - return cc1gen_reproducer_main(argv.slice(2), argv[0], GetExecutablePathVP); - + return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0], + GetExecutablePathVP); // Reject unknown tools. llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " << "Valid tools include '-cc1' and '-cc1as'.\n"; From 5d37ce7e19c9961b45bef2f2f66805f4a8f02daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20B=C3=B6ck?= Date: Thu, 23 Jan 2020 10:43:56 +0200 Subject: [PATCH 034/374] [LLD][COFF] Enable linking of __declspec(selectany) symbols from Clang and GCC When annotating a symbol with __declspec(selectany), Clang assigns it comdat 2 while GCC assigns it comdat 3. This patch enables two object files that contain a __declspec(selectany) symbol, one created by gcc and the other by clang, to be linked together instead of issuing a duplicate symbol error. Differential Revision: https://reviews.llvm.org/D73139 (cherry picked from commit 9dbc1ab23268abce5db98ad9a1e3aef89c371524) --- lld/COFF/InputFiles.cpp | 11 +++++++++++ lld/test/COFF/comdat-gcc-compatibility.s | 13 +++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 lld/test/COFF/comdat-gcc-compatibility.s diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index d884201ba31b..272f9c0717c0 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -500,6 +500,17 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection, leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST; } + // GCCs __declspec(selectany) doesn't actually pick "any" but "same size as". + // Clang on the other hand picks "any". To be able to link two object files + // with a __declspec(selectany) declaration, one compiled with gcc and the + // other with clang, we merge them as proper "same size as" + if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY && + leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) || + (selection == IMAGE_COMDAT_SELECT_SAME_SIZE && + leaderSelection == IMAGE_COMDAT_SELECT_ANY))) { + leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE; + } + // Other than that, comdat selections must match. This is a bit more // strict than link.exe which allows merging "any" and "largest" if "any" // is the first symbol the linker sees, and it allows merging "largest" diff --git a/lld/test/COFF/comdat-gcc-compatibility.s b/lld/test/COFF/comdat-gcc-compatibility.s new file mode 100644 index 000000000000..cd35955ff0f5 --- /dev/null +++ b/lld/test/COFF/comdat-gcc-compatibility.s @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc %s -triple x86_64-pc-win32 -defsym obj=0 -filetype=obj -o %t1.obj +# RUN: llvm-mc %s -triple x86_64-pc-win32 -defsym obj=1 -filetype=obj -o %t2.obj +# RUN: lld-link /lldmingw /noentry /dll %t1.obj %t2.obj /out:%t3.dll +# RUN: not lld-link /noentry /dll %t1.obj %t2.obj /out:%t3.dll +.if obj==0 + .section .text$nm, "", discard, symbol +.else + .section .text$nm, "", same_size, symbol +.endif + .globl symbol +symbol: + .long 1 From 8634a82910eba78279a69fcba0925d3a602a0563 Mon Sep 17 00:00:00 2001 From: James Clarke Date: Thu, 23 Jan 2020 02:05:46 +0000 Subject: [PATCH 035/374] [RISCV] Fix evaluating %pcrel_lo against global and weak symbols Summary: Previously, we would erroneously turn %pcrel_lo(label), where label has a %pcrel_hi against a weak symbol, into %pcrel_lo(label + offset), as evaluatePCRelLo would believe the target independent logic was going to fold it. Moreover, even if that were fixed, shouldForceRelocation lacks an MCAsmLayout and thus cannot evaluate the %pcrel_hi fixup to a value and check the symbol, so we would then erroneously constant-fold the %pcrel_lo whilst leaving the %pcrel_hi intact. After D72197, this same sequence also occurs for symbols with global binding, which is triggered in real-world code. Instead, as discussed in D71978, we introduce a new FKF_IsTarget flag to avoid these kinds of issues. All the resolution logic happens in one place, with no coordination required between RISCAsmBackend and RISCVMCExpr to ensure they implement the same logic twice. Although the implementation of %pcrel_hi can be left as target independent, we make it target dependent to ensure that they are handled identically to %pcrel_lo, otherwise we risk one of them being constant folded but the other being preserved. This also allows us to properly support fixup pairs where the instructions are in different fragments. Reviewers: asb, lenary, efriedma Reviewed By: efriedma Subscribers: arichardson, hiraditya, rbar, johnrusso, simoncook, sabuasal, niosHD, kito-cheng, shiva0217, MaskRay, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, rkruppe, PkmX, jocewei, psnobl, benna, Jim, s.egerton, pzheng, sameer.abuasal, apazos, luismarques, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D73211 (cherry picked from commit 3f5976c97dbfefb4669abcf968bd79a9a64c18e0) --- llvm/include/llvm/MC/MCAsmBackend.h | 8 ++ llvm/include/llvm/MC/MCFixupKindInfo.h | 5 +- llvm/lib/MC/MCAssembler.cpp | 7 ++ .../RISCV/MCTargetDesc/RISCVAsmBackend.cpp | 97 ++++++++++++------- .../RISCV/MCTargetDesc/RISCVAsmBackend.h | 14 ++- .../Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp | 69 +------------ .../Target/RISCV/MCTargetDesc/RISCVMCExpr.h | 7 +- llvm/test/MC/RISCV/pcrel-fixups.s | 74 ++++++++++++-- llvm/test/MC/RISCV/pcrel-lo12-invalid.s | 2 + llvm/test/MC/RISCV/rv32i-aliases-valid.s | 7 +- llvm/test/MC/RISCV/rv32i-valid.s | 7 +- llvm/test/MC/RISCV/rv64i-aliases-valid.s | 8 +- 12 files changed, 179 insertions(+), 126 deletions(-) diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h index ed7d5c7f01f4..bf41420f2a5a 100644 --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -108,6 +108,14 @@ class MCAsmBackend { return false; } + virtual bool evaluateTargetFixup(const MCAssembler &Asm, + const MCAsmLayout &Layout, + const MCFixup &Fixup, const MCFragment *DF, + const MCValue &Target, uint64_t &Value, + bool &WasForced) { + llvm_unreachable("Need to implement hook if target has custom fixups"); + } + /// Apply the \p Value for given \p Fixup into the provided data fragment, at /// the offset specified by the fixup and following the fixup kind as /// appropriate. Errors (such as an out of range fixup value) should be diff --git a/llvm/include/llvm/MC/MCFixupKindInfo.h b/llvm/include/llvm/MC/MCFixupKindInfo.h index 0ea34866db6a..0d57441ce0dc 100644 --- a/llvm/include/llvm/MC/MCFixupKindInfo.h +++ b/llvm/include/llvm/MC/MCFixupKindInfo.h @@ -19,7 +19,10 @@ struct MCFixupKindInfo { FKF_IsPCRel = (1 << 0), /// Should this fixup kind force a 4-byte aligned effective PC value? - FKF_IsAlignedDownTo32Bits = (1 << 1) + FKF_IsAlignedDownTo32Bits = (1 << 1), + + /// Should this fixup be evaluated in a target dependent manner? + FKF_IsTarget = (1 << 2) }; /// A target specific name for the fixup kind. The names will be unique for diff --git a/llvm/lib/MC/MCAssembler.cpp b/llvm/lib/MC/MCAssembler.cpp index b30137aafb8d..75ec27975564 100644 --- a/llvm/lib/MC/MCAssembler.cpp +++ b/llvm/lib/MC/MCAssembler.cpp @@ -217,6 +217,13 @@ bool MCAssembler::evaluateFixup(const MCAsmLayout &Layout, } assert(getBackendPtr() && "Expected assembler backend"); + bool IsTarget = getBackendPtr()->getFixupKindInfo(Fixup.getKind()).Flags & + MCFixupKindInfo::FKF_IsTarget; + + if (IsTarget) + return getBackend().evaluateTargetFixup(*this, Layout, Fixup, DF, Target, + Value, WasForced); + bool IsPCRel = getBackendPtr()->getFixupKindInfo(Fixup.getKind()).Flags & MCFixupKindInfo::FKF_IsPCRel; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 5881a0a86ef7..373d0ccb1857 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -9,6 +9,7 @@ #include "RISCVAsmBackend.h" #include "RISCVMCExpr.h" #include "llvm/ADT/APInt.h" +#include "llvm/MC/MCAsmLayout.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" @@ -28,8 +29,6 @@ using namespace llvm; bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target) { - bool ShouldForce = false; - switch (Fixup.getTargetKind()) { default: break; @@ -44,40 +43,9 @@ bool RISCVAsmBackend::shouldForceRelocation(const MCAssembler &Asm, case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: return true; - case RISCV::fixup_riscv_pcrel_lo12_i: - case RISCV::fixup_riscv_pcrel_lo12_s: - // For pcrel_lo12, force a relocation if the target of the corresponding - // pcrel_hi20 is not in the same fragment. - const MCFixup *T = cast(Fixup.getValue())->getPCRelHiFixup(); - if (!T) { - Asm.getContext().reportError(Fixup.getLoc(), - "could not find corresponding %pcrel_hi"); - return false; - } - - switch (T->getTargetKind()) { - default: - llvm_unreachable("Unexpected fixup kind for pcrel_lo12"); - break; - case RISCV::fixup_riscv_got_hi20: - case RISCV::fixup_riscv_tls_got_hi20: - case RISCV::fixup_riscv_tls_gd_hi20: - ShouldForce = true; - break; - case RISCV::fixup_riscv_pcrel_hi20: { - MCFragment *TFragment = T->getValue()->findAssociatedFragment(); - MCFragment *FixupFragment = Fixup.getValue()->findAssociatedFragment(); - assert(FixupFragment && "We should have a fragment for this fixup"); - ShouldForce = - !TFragment || TFragment->getParent() != FixupFragment->getParent(); - break; - } - } - break; } - return ShouldForce || STI.getFeatureBits()[RISCV::FeatureRelax] || - ForceRelocs; + return STI.getFeatureBits()[RISCV::FeatureRelax] || ForceRelocs; } bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, @@ -284,6 +252,67 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, } } +bool RISCVAsmBackend::evaluateTargetFixup( + const MCAssembler &Asm, const MCAsmLayout &Layout, const MCFixup &Fixup, + const MCFragment *DF, const MCValue &Target, uint64_t &Value, + bool &WasForced) { + const MCFixup *AUIPCFixup; + const MCFragment *AUIPCDF; + MCValue AUIPCTarget; + switch (Fixup.getTargetKind()) { + default: + llvm_unreachable("Unexpected fixup kind!"); + case RISCV::fixup_riscv_pcrel_hi20: + AUIPCFixup = &Fixup; + AUIPCDF = DF; + AUIPCTarget = Target; + break; + case RISCV::fixup_riscv_pcrel_lo12_i: + case RISCV::fixup_riscv_pcrel_lo12_s: { + AUIPCFixup = cast(Fixup.getValue())->getPCRelHiFixup(&AUIPCDF); + if (!AUIPCFixup) { + Asm.getContext().reportError(Fixup.getLoc(), + "could not find corresponding %pcrel_hi"); + return true; + } + + // MCAssembler::evaluateFixup will emit an error for this case when it sees + // the %pcrel_hi, so don't duplicate it when also seeing the %pcrel_lo. + const MCExpr *AUIPCExpr = AUIPCFixup->getValue(); + if (!AUIPCExpr->evaluateAsRelocatable(AUIPCTarget, &Layout, AUIPCFixup)) + return true; + break; + } + } + + if (!AUIPCTarget.getSymA() || AUIPCTarget.getSymB()) + return false; + + const MCSymbolRefExpr *A = AUIPCTarget.getSymA(); + const MCSymbol &SA = A->getSymbol(); + if (A->getKind() != MCSymbolRefExpr::VK_None || SA.isUndefined()) + return false; + + auto *Writer = Asm.getWriterPtr(); + if (!Writer) + return false; + + bool IsResolved = Writer->isSymbolRefDifferenceFullyResolvedImpl( + Asm, SA, *AUIPCDF, false, true); + if (!IsResolved) + return false; + + Value = Layout.getSymbolOffset(SA) + AUIPCTarget.getConstant(); + Value -= Layout.getFragmentOffset(AUIPCDF) + AUIPCFixup->getOffset(); + + if (shouldForceRelocation(Asm, *AUIPCFixup, AUIPCTarget)) { + WasForced = true; + return false; + } + + return true; +} + void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h index 254249c87dc8..1c3c587355a2 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -65,6 +65,11 @@ class RISCVAsmBackend : public MCAsmBackend { const MCAsmLayout &Layout, MCAlignFragment &AF) override; + bool evaluateTargetFixup(const MCAssembler &Asm, const MCAsmLayout &Layout, + const MCFixup &Fixup, const MCFragment *DF, + const MCValue &Target, uint64_t &Value, + bool &WasForced) override; + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved, @@ -101,9 +106,12 @@ class RISCVAsmBackend : public MCAsmBackend { { "fixup_riscv_hi20", 12, 20, 0 }, { "fixup_riscv_lo12_i", 20, 12, 0 }, { "fixup_riscv_lo12_s", 0, 32, 0 }, - { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_i", 20, 12, MCFixupKindInfo::FKF_IsPCRel }, - { "fixup_riscv_pcrel_lo12_s", 0, 32, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_pcrel_hi20", 12, 20, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget }, + { "fixup_riscv_pcrel_lo12_i", 20, 12, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget }, + { "fixup_riscv_pcrel_lo12_s", 0, 32, + MCFixupKindInfo::FKF_IsPCRel | MCFixupKindInfo::FKF_IsTarget }, { "fixup_riscv_got_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, { "fixup_riscv_tprel_hi20", 12, 20, 0 }, { "fixup_riscv_tprel_lo12_i", 20, 12, 0 }, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp index 7aa9b5e7d683..2a6f372e50be 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -47,7 +47,7 @@ void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { OS << ')'; } -const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { +const MCFixup *RISCVMCExpr::getPCRelHiFixup(const MCFragment **DFOut) const { MCValue AUIPCLoc; if (!getSubExpr()->evaluateAsRelocatable(AUIPCLoc, nullptr, nullptr)) return nullptr; @@ -81,6 +81,8 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { case RISCV::fixup_riscv_tls_got_hi20: case RISCV::fixup_riscv_tls_gd_hi20: case RISCV::fixup_riscv_pcrel_hi20: + if (DFOut) + *DFOut = DF; return &F; } } @@ -88,74 +90,9 @@ const MCFixup *RISCVMCExpr::getPCRelHiFixup() const { return nullptr; } -bool RISCVMCExpr::evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout, - const MCFixup *Fixup) const { - // VK_RISCV_PCREL_LO has to be handled specially. The MCExpr inside is - // actually the location of a auipc instruction with a VK_RISCV_PCREL_HI fixup - // pointing to the real target. We need to generate an MCValue in the form of - // ( + ). The Fixup - // is pcrel relative to the VK_RISCV_PCREL_LO fixup, so we need to add the - // offset to the VK_RISCV_PCREL_HI Fixup from VK_RISCV_PCREL_LO to correct. - - // Don't try to evaluate if the fixup will be forced as a relocation (e.g. - // as linker relaxation is enabled). If we evaluated pcrel_lo in this case, - // the modified fixup will be converted into a relocation that no longer - // points to the pcrel_hi as the linker requires. - auto &RAB = - static_cast(Layout->getAssembler().getBackend()); - if (RAB.willForceRelocations()) - return false; - - MCValue AUIPCLoc; - if (!getSubExpr()->evaluateAsValue(AUIPCLoc, *Layout)) - return false; - - const MCSymbolRefExpr *AUIPCSRE = AUIPCLoc.getSymA(); - // Don't try to evaluate %pcrel_hi/%pcrel_lo pairs that cross fragment - // boundries. - if (!AUIPCSRE || - findAssociatedFragment() != AUIPCSRE->findAssociatedFragment()) - return false; - - const MCSymbol *AUIPCSymbol = &AUIPCSRE->getSymbol(); - if (!AUIPCSymbol) - return false; - - const MCFixup *TargetFixup = getPCRelHiFixup(); - if (!TargetFixup) - return false; - - if ((unsigned)TargetFixup->getKind() != RISCV::fixup_riscv_pcrel_hi20) - return false; - - MCValue Target; - if (!TargetFixup->getValue()->evaluateAsValue(Target, *Layout)) - return false; - - if (!Target.getSymA() || !Target.getSymA()->getSymbol().isInSection()) - return false; - - if (&Target.getSymA()->getSymbol().getSection() != - findAssociatedFragment()->getParent()) - return false; - - // We must use TargetFixup rather than AUIPCSymbol here. They will almost - // always have the same offset, except for the case when AUIPCSymbol is at - // the end of a fragment and the fixup comes from offset 0 in the next - // fragment. - uint64_t AUIPCOffset = TargetFixup->getOffset(); - - Res = MCValue::get(Target.getSymA(), nullptr, - Target.getConstant() + (Fixup->getOffset() - AUIPCOffset)); - return true; -} - bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, const MCFixup *Fixup) const { - if (Kind == VK_RISCV_PCREL_LO && evaluatePCRelLo(Res, Layout, Fixup)) - return true; - if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) return false; diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h index 921df376f3df..167e7d553e7d 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -46,9 +46,6 @@ class RISCVMCExpr : public MCTargetExpr { int64_t evaluateAsInt64(int64_t Value) const; - bool evaluatePCRelLo(MCValue &Res, const MCAsmLayout *Layout, - const MCFixup *Fixup) const; - explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind) : Expr(Expr), Kind(Kind) {} @@ -61,11 +58,11 @@ class RISCVMCExpr : public MCTargetExpr { const MCExpr *getSubExpr() const { return Expr; } /// Get the corresponding PC-relative HI fixup that a VK_RISCV_PCREL_LO - /// points to. + /// points to, and optionally the fragment containing it. /// /// \returns nullptr if this isn't a VK_RISCV_PCREL_LO pointing to a /// known PC-relative HI fixup. - const MCFixup *getPCRelHiFixup() const; + const MCFixup *getPCRelHiFixup(const MCFragment **DFOut) const; void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, diff --git a/llvm/test/MC/RISCV/pcrel-fixups.s b/llvm/test/MC/RISCV/pcrel-fixups.s index 1025988967a0..f4f9e2c57a0e 100644 --- a/llvm/test/MC/RISCV/pcrel-fixups.s +++ b/llvm/test/MC/RISCV/pcrel-fixups.s @@ -12,11 +12,12 @@ # RUN: | FileCheck --check-prefix RELAX %s # Fixups for %pcrel_hi / %pcrel_lo can be evaluated within a section, -# regardless of the fragment containing the target address. +# regardless of the fragment containing the target address, provided symbol +# binding allows it. function: .Lpcrel_label1: - auipc a0, %pcrel_hi(other_function) + auipc a0, %pcrel_hi(local_function) addi a1, a0, %pcrel_lo(.Lpcrel_label1) # NORELAX: auipc a0, 0 # NORELAX-NOT: R_RISCV @@ -24,7 +25,7 @@ function: # NORELAX-NOT: R_RISCV # RELAX: auipc a0, 0 -# RELAX: R_RISCV_PCREL_HI20 other_function +# RELAX: R_RISCV_PCREL_HI20 local_function # RELAX: R_RISCV_RELAX *ABS* # RELAX: addi a1, a0, 0 # RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label1 @@ -32,7 +33,7 @@ function: .p2align 2 # Cause a new fragment be emitted here .Lpcrel_label2: - auipc a0, %pcrel_hi(other_function) + auipc a0, %pcrel_hi(local_function) addi a1, a0, %pcrel_lo(.Lpcrel_label2) # NORELAX: auipc a0, 0 # NORELAX-NOT: R_RISCV @@ -40,13 +41,72 @@ function: # NORELAX-NOT: R_RISCV # RELAX: auipc a0, 0 -# RELAX: R_RISCV_PCREL_HI20 other_function +# RELAX: R_RISCV_PCREL_HI20 local_function # RELAX: R_RISCV_RELAX *ABS* # RELAX: addi a1, a0, 0 # RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label2 # RELAX: R_RISCV_RELAX *ABS* - .type other_function,@function -other_function: + .type local_function,@function +local_function: ret +# Check we correctly evaluate when fixups are in different fragments + +.Lpcrel_label3: + auipc a0, %pcrel_hi(local_function) + .p2align 2 # Cause a new fragment be emitted here + addi a1, a0, %pcrel_lo(.Lpcrel_label3) +# NORELAX: auipc a0, 0 +# NORELAX-NOT: R_RISCV +# NORELAX: addi a1, a0, -4 +# NORELAX-NOT: R_RISCV + +# RELAX: auipc a0, 0 +# RELAX: R_RISCV_PCREL_HI20 local_function +# RELAX: R_RISCV_RELAX *ABS* +# RELAX: addi a1, a0, 0 +# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label3 +# RELAX: R_RISCV_RELAX *ABS* + +# Check handling of symbol binding. + +.Lpcrel_label4: + auipc a0, %pcrel_hi(global_function) + addi a1, a0, %pcrel_lo(.Lpcrel_label4) +# NORELAX: auipc a0, 0 +# NORELAX: R_RISCV_PCREL_HI20 global_function +# NORELAX: addi a1, a0, 0 +# NORELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label4 + +# RELAX: auipc a0, 0 +# RELAX: R_RISCV_PCREL_HI20 global_function +# RELAX: R_RISCV_RELAX *ABS* +# RELAX: addi a1, a0, 0 +# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label4 +# RELAX: R_RISCV_RELAX *ABS* + +.Lpcrel_label5: + auipc a0, %pcrel_hi(weak_function) + addi a1, a0, %pcrel_lo(.Lpcrel_label5) +# NORELAX: auipc a0, 0 +# NORELAX: R_RISCV_PCREL_HI20 weak_function +# NORELAX: addi a1, a0, 0 +# NORELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label5 + +# RELAX: auipc a0, 0 +# RELAX: R_RISCV_PCREL_HI20 weak_function +# RELAX: R_RISCV_RELAX *ABS* +# RELAX: addi a1, a0, 0 +# RELAX: R_RISCV_PCREL_LO12_I .Lpcrel_label5 +# RELAX: R_RISCV_RELAX *ABS* + + .global global_function + .type global_function,@function +global_function: + ret + + .weak weak_function + .type weak_function,@function +weak_function: + ret diff --git a/llvm/test/MC/RISCV/pcrel-lo12-invalid.s b/llvm/test/MC/RISCV/pcrel-lo12-invalid.s index 7cf2494ad8e9..74a1f2fac923 100644 --- a/llvm/test/MC/RISCV/pcrel-lo12-invalid.s +++ b/llvm/test/MC/RISCV/pcrel-lo12-invalid.s @@ -3,3 +3,5 @@ 1: addi a0, a0, %pcrel_lo(1b) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi + addi a0, a0, %pcrel_lo(0x123456) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi + addi a0, a0, %pcrel_lo(foo) # CHECK: :[[@LINE]]:3: error: could not find corresponding %pcrel_hi diff --git a/llvm/test/MC/RISCV/rv32i-aliases-valid.s b/llvm/test/MC/RISCV/rv32i-aliases-valid.s index d2e142efcfc5..5140fb2adfef 100644 --- a/llvm/test/MC/RISCV/rv32i-aliases-valid.s +++ b/llvm/test/MC/RISCV/rv32i-aliases-valid.s @@ -14,6 +14,8 @@ # CHECK-ALIAS....Match the alias (tests instr. to alias mapping) # CHECK-EXPAND...Match canonical instr. unconditionally (tests alias expansion) +# Needed for testing valid %pcrel_lo expressions +.Lpcrel_hi0: auipc a0, %pcrel_hi(foo) # CHECK-INST: addi a0, zero, 0 # CHECK-ALIAS: mv a0, zero @@ -71,16 +73,13 @@ li x12, 0xFFFFFFFF # CHECK-EXPAND: addi a0, zero, 1110 li a0, %lo(0x123456) -# CHECK-OBJ-NOALIAS: addi a0, zero, 0 -# CHECK-OBJ: R_RISCV_PCREL_LO12 -li a0, %pcrel_lo(0x123456) # CHECK-OBJ-NOALIAS: addi a0, zero, 0 # CHECK-OBJ: R_RISCV_LO12 li a0, %lo(foo) # CHECK-OBJ-NOALIAS: addi a0, zero, 0 # CHECK-OBJ: R_RISCV_PCREL_LO12 -li a0, %pcrel_lo(foo) +li a0, %pcrel_lo(.Lpcrel_hi0) .equ CONST, 0x123456 # CHECK-EXPAND: lui a0, 291 diff --git a/llvm/test/MC/RISCV/rv32i-valid.s b/llvm/test/MC/RISCV/rv32i-valid.s index bbcfa5feac41..580f0f548c3f 100644 --- a/llvm/test/MC/RISCV/rv32i-valid.s +++ b/llvm/test/MC/RISCV/rv32i-valid.s @@ -11,6 +11,9 @@ .equ CONST, 30 +# Needed for testing valid %pcrel_lo expressions +.Lpcrel_hi0: auipc a0, %pcrel_hi(foo) + # CHECK-ASM-AND-OBJ: lui a0, 2 # CHECK-ASM: encoding: [0x37,0x25,0x00,0x00] lui a0, 2 @@ -161,11 +164,11 @@ lw a0, 97(a2) # CHECK-OBJ: lbu s5, 0(s6) # CHECK-OBJ: R_RISCV_LO12 lbu s5, %lo(foo)(s6) -# CHECK-ASM: lhu t3, %pcrel_lo(foo)(t3) +# CHECK-ASM: lhu t3, %pcrel_lo(.Lpcrel_hi0)(t3) # CHECK-ASM: encoding: [0x03,0x5e,0bAAAA1110,A] # CHECK-OBJ: lhu t3, 0(t3) # CHECK-OBJ: R_RISCV_PCREL_LO12 -lhu t3, %pcrel_lo(foo)(t3) +lhu t3, %pcrel_lo(.Lpcrel_hi0)(t3) # CHECK-ASM-AND-OBJ: lb t0, 30(t1) # CHECK-ASM: encoding: [0x83,0x02,0xe3,0x01] lb t0, CONST(t1) diff --git a/llvm/test/MC/RISCV/rv64i-aliases-valid.s b/llvm/test/MC/RISCV/rv64i-aliases-valid.s index 551e46f85302..9f0ad246cc92 100644 --- a/llvm/test/MC/RISCV/rv64i-aliases-valid.s +++ b/llvm/test/MC/RISCV/rv64i-aliases-valid.s @@ -17,6 +17,9 @@ # TODO ld # TODO sd +# Needed for testing valid %pcrel_lo expressions +.Lpcrel_hi0: auipc a0, %pcrel_hi(foo) + # CHECK-INST: addi a0, zero, 0 # CHECK-ALIAS: mv a0, zero li x10, 0 @@ -107,16 +110,13 @@ li t5, 0xFFFFFFFFFFFFFFFF # CHECK-EXPAND: addi a0, zero, 1110 li a0, %lo(0x123456) -# CHECK-OBJ-NOALIAS: addi a0, zero, 0 -# CHECK-OBJ: R_RISCV_PCREL_LO12 -li a0, %pcrel_lo(0x123456) # CHECK-OBJ-NOALIAS: addi a0, zero, 0 # CHECK-OBJ: R_RISCV_LO12 li a0, %lo(foo) # CHECK-OBJ-NOALIAS: addi a0, zero, 0 # CHECK-OBJ: R_RISCV_PCREL_LO12 -li a0, %pcrel_lo(foo) +li a0, %pcrel_lo(.Lpcrel_hi0) .equ CONST, 0x123456 # CHECK-EXPAND: lui a0, 291 From a3982a59ce34039f63fff35c6c0562cf6fd5c771 Mon Sep 17 00:00:00 2001 From: James Clarke Date: Thu, 23 Jan 2020 02:30:10 +0000 Subject: [PATCH 036/374] [test] Fix lld/test/ELF/riscv-pcrel-hilo-error.s after D73211 (cherry picked from commit ddfe8751b16a1d57b0586fb48d1109c98234bc3f) --- lld/test/ELF/riscv-pcrel-hilo-error.s | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lld/test/ELF/riscv-pcrel-hilo-error.s b/lld/test/ELF/riscv-pcrel-hilo-error.s index bfc205520bd8..1557ac77bb7e 100644 --- a/lld/test/ELF/riscv-pcrel-hilo-error.s +++ b/lld/test/ELF/riscv-pcrel-hilo-error.s @@ -4,4 +4,10 @@ # CHECK: error: R_RISCV_PCREL_LO12 relocation points to an absolute symbol: external +# We provide a dummy %pcrel_hi referred to by external to appease the +# assembler, but make external weak so --defsym can still override it at link +# time. +.weak external +external: +auipc sp,%pcrel_hi(external) addi sp,sp,%pcrel_lo(external) From 318677e78def0023d210a29f4b3cf648e02f9fdc Mon Sep 17 00:00:00 2001 From: Mark de Wever Date: Sun, 19 Jan 2020 17:01:12 +0100 Subject: [PATCH 037/374] [Sema] Avoid Wrange-loop-analysis false positives When Wrange-loop-analysis issues a diagnostic on a dependent type in a template the diagnostic may not be valid for all instantiations. Therefore the diagnostic is suppressed during the instantiation. Non dependent types still issue a diagnostic. The same can happen when using macros. Therefore the diagnostic is disabled for macros. Fixes https://bugs.llvm.org/show_bug.cgi?id=44556 Differential Revision: https://reviews.llvm.org/D73007 (cherry picked from commit 41fcd17250fa0526e4b7fd2c7df7721b0f79b683) --- clang/lib/Sema/SemaStmt.cpp | 6 ++ .../test/SemaCXX/warn-range-loop-analysis.cpp | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index d6c3af9e84c8..ff6481006280 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2838,6 +2838,9 @@ static void DiagnoseForRangeConstVariableCopies(Sema &SemaRef, /// Suggest "const foo &x" to prevent the copy. static void DiagnoseForRangeVariableCopies(Sema &SemaRef, const CXXForRangeStmt *ForStmt) { + if (SemaRef.inTemplateInstantiation()) + return; + if (SemaRef.Diags.isIgnored(diag::warn_for_range_const_reference_copy, ForStmt->getBeginLoc()) && SemaRef.Diags.isIgnored(diag::warn_for_range_variable_always_copy, @@ -2860,6 +2863,9 @@ static void DiagnoseForRangeVariableCopies(Sema &SemaRef, if (!InitExpr) return; + if (InitExpr->getExprLoc().isMacroID()) + return; + if (VariableType->isReferenceType()) { DiagnoseForRangeReferenceVariableCopies(SemaRef, VD, ForStmt->getRangeInit()->getType()); diff --git a/clang/test/SemaCXX/warn-range-loop-analysis.cpp b/clang/test/SemaCXX/warn-range-loop-analysis.cpp index 53b0ca288194..951844c953ef 100644 --- a/clang/test/SemaCXX/warn-range-loop-analysis.cpp +++ b/clang/test/SemaCXX/warn-range-loop-analysis.cpp @@ -454,3 +454,75 @@ void test10() { // expected-note@-2 {{'Bar'}} // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:17-[[@LINE-3]]:18}:" " } + +template +void test_template_function() { + // In a template instantiation the diagnostics should not be emitted for + // loops with dependent types. + Container C; + for (const Bar &x : C) {} + // expected-warning@-1 {{always a copy}} + // expected-note@-2 {{'Bar'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:18-[[@LINE-3]]:19}:"" + + Container Dependent; + for (const T &x : Dependent) {} +} +template void test_template_function(); + +template +struct test_template_struct { + static void static_member() { + Container C; + for (const Bar &x : C) {} + // expected-warning@-1 {{always a copy}} + // expected-note@-2 {{'Bar'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:21}:"" + + Container Dependent; + for (const T &x : Dependent) {} + } + + void member() { + Container C; + for (const Bar &x : C) {} + // expected-warning@-1 {{always a copy}} + // expected-note@-2 {{'Bar'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:21}:"" + + Container Dependent; + for (const T &x : Dependent) {} + } +}; +template struct test_template_struct; + +struct test_struct_with_templated_member { + void member() { + Container C; + for (const Bar &x : C) {} + // expected-warning@-1 {{always a copy}} + // expected-note@-2 {{'Bar'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:21}:"" + } + + template + void template_member() { + Container C; + for (const Bar &x : C) {} + // expected-warning@-1 {{always a copy}} + // expected-note@-2 {{'Bar'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:20-[[@LINE-3]]:21}:"" + + Container Dependent; + for (const T &x : Dependent) {} + } +}; +template void test_struct_with_templated_member::template_member(); + +#define TEST_MACRO \ + void test_macro() { \ + Container C; \ + for (const Bar &x : C) {} \ + } + +TEST_MACRO From b079266dcb6d1ee6446d074ebd1d212a13ce0665 Mon Sep 17 00:00:00 2001 From: Mitch Phillips <31459023+hctim@users.noreply.github.com> Date: Mon, 20 Jan 2020 16:34:09 -0800 Subject: [PATCH 038/374] Revert "PR17164: Change clang's default behavior from -flax-vector-conversions=all to -flax-vector-conversions=integer." This patch broke the Sanitizer buildbots. Please see the commit's differential revision for more information (https://reviews.llvm.org/D67678). This reverts commit b72a8c65e4e34779b6bc9e466203f553f5294486. (cherry picked from commit edd4398f4cd33a305afbca76ac4e6590e9337f4d) --- clang/docs/CommandGuide/clang.rst | 11 +--------- clang/include/clang/Basic/LangOptions.def | 2 +- clang/test/Headers/altivec-header.c | 2 +- clang/test/Headers/arm-neon-header.c | 2 +- clang/test/Headers/x86-intrinsics-headers.c | 2 +- clang/test/Headers/x86intrin-2.c | 4 ++-- clang/test/Headers/x86intrin.c | 2 +- clang/test/Sema/vector-assign.c | 12 +++++------ clang/test/Sema/vector-cast.c | 23 +++++++-------------- clang/test/Sema/vector-ops.c | 3 +-- 10 files changed, 23 insertions(+), 40 deletions(-) diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 6947450beb43..7b0873600fc3 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -278,18 +278,9 @@ Language Selection and Mode Options Make all string literals default to writable. This disables uniquing of strings and other optimizations. -.. option:: -flax-vector-conversions, -flax-vector-conversions=, -fno-lax-vector-conversions +.. option:: -flax-vector-conversions Allow loose type checking rules for implicit vector conversions. - Possible values of : - - - ``none``: allow no implicit conversions between vectors - - ``integer``: allow implicit bitcasts between integer vectors of the same - overall bit-width - - ``all``: allow implicit bitcasts between any vectors of the same - overall bit-width - - defaults to ``integer`` if unspecified. .. option:: -fblocks diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 4bbe6ea26fba..068f206f4484 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -121,7 +121,7 @@ BENIGN_LANGOPT(PascalStrings, 1, 0, "Pascal string support") LANGOPT(WritableStrings , 1, 0, "writable string support") LANGOPT(ConstStrings , 1, 0, "const-qualified string support") ENUM_LANGOPT(LaxVectorConversions, LaxVectorConversionKind, 2, - LaxVectorConversionKind::Integer, "lax vector conversions") + LaxVectorConversionKind::All, "lax vector conversions") LANGOPT(ConvergentFunctions, 1, 1, "Assume convergent functions") LANGOPT(AltiVec , 1, 0, "AltiVec-style vector initializers") LANGOPT(ZVector , 1, 0, "System z vector extensions") diff --git a/clang/test/Headers/altivec-header.c b/clang/test/Headers/altivec-header.c index aa85a33d26da..00e5f444de7c 100644 --- a/clang/test/Headers/altivec-header.c +++ b/clang/test/Headers/altivec-header.c @@ -1,5 +1,5 @@ +// RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -flax-vector-conversions=none -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -flax-vector-conversions=all -o - %s | FileCheck %s // RUN: %clang_cc1 -triple powerpc64-unknown-unknown -target-feature +altivec -ffreestanding -emit-llvm -x c++ -o - %s | FileCheck %s #include diff --git a/clang/test/Headers/arm-neon-header.c b/clang/test/Headers/arm-neon-header.c index 8626a883fdf3..f6362886010a 100644 --- a/clang/test/Headers/arm-neon-header.c +++ b/clang/test/Headers/arm-neon-header.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -flax-vector-conversions=all -Wvector-conversions -ffreestanding %s +// RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -Wvector-conversions -ffreestanding %s // RUN: %clang_cc1 -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -flax-vector-conversions=none -ffreestanding %s // RUN: %clang_cc1 -x c++ -triple thumbv7-apple-darwin10 -target-cpu cortex-a8 -fsyntax-only -Wvector-conversions -ffreestanding %s diff --git a/clang/test/Headers/x86-intrinsics-headers.c b/clang/test/Headers/x86-intrinsics-headers.c index 2efd3505bca6..59ca354e1160 100644 --- a/clang/test/Headers/x86-intrinsics-headers.c +++ b/clang/test/Headers/x86-intrinsics-headers.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=all %s +// RUN: %clang_cc1 -fsyntax-only -ffreestanding %s // RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none %s // RUN: %clang_cc1 -fsyntax-only -ffreestanding -x c++ %s diff --git a/clang/test/Headers/x86intrin-2.c b/clang/test/Headers/x86intrin-2.c index bd6ed565d0de..90475c658fce 100644 --- a/clang/test/Headers/x86intrin-2.c +++ b/clang/test/Headers/x86intrin-2.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -flax-vector-conversions=all %s -verify -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -flax-vector-conversions=none %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none -Wcast-qual %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -Wcast-qual -x c++ %s -verify // expected-no-diagnostics diff --git a/clang/test/Headers/x86intrin.c b/clang/test/Headers/x86intrin.c index e904e9ed5462..53e369559f40 100644 --- a/clang/test/Headers/x86intrin.c +++ b/clang/test/Headers/x86intrin.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=all %s -verify +// RUN: %clang_cc1 -fsyntax-only -ffreestanding %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -flax-vector-conversions=none %s -verify // RUN: %clang_cc1 -fsyntax-only -ffreestanding -x c++ %s -verify // expected-no-diagnostics diff --git a/clang/test/Sema/vector-assign.c b/clang/test/Sema/vector-assign.c index 88be03e2cb6d..ad3406e304a7 100644 --- a/clang/test/Sema/vector-assign.c +++ b/clang/test/Sema/vector-assign.c @@ -14,12 +14,12 @@ void test1() { v1 = v2; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v2u' (vector of 2 'unsigned int' values)}} v1 = v3; // expected-error {{assigning to 'v2s' (vector of 2 'int' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v1 = v4; // expected-error {{assigning to 'v2s' (vector of 2 'int' values) from incompatible type 'v2f' (vector of 2 'float' values)}} + v1 = v4; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v2f' (vector of 2 'float' values)}} v1 = v5; // expected-warning {{incompatible vector types assigning to 'v2s' (vector of 2 'int' values) from 'v4ss' (vector of 4 'short' values)}} v2 = v1; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v2s' (vector of 2 'int' values)}} v2 = v3; // expected-error {{assigning to 'v2u' (vector of 2 'unsigned int' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v2 = v4; // expected-error {{assigning to 'v2u' (vector of 2 'unsigned int' values) from incompatible type 'v2f' (vector of 2 'float' values)}} + v2 = v4; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v2f' (vector of 2 'float' values)}} v2 = v5; // expected-warning {{incompatible vector types assigning to 'v2u' (vector of 2 'unsigned int' values) from 'v4ss' (vector of 4 'short' values)}} v3 = v1; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v2s' (vector of 2 'int' values)}} @@ -27,15 +27,15 @@ void test1() { v3 = v4; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v2f' (vector of 2 'float' values)}} v3 = v5; // expected-error {{assigning to 'v1s' (vector of 1 'int' value) from incompatible type 'v4ss'}} - v4 = v1; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v2s' (vector of 2 'int' values)}} - v4 = v2; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v2u' (vector of 2 'unsigned int' values)}} + v4 = v1; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v2s' (vector of 2 'int' values)}} + v4 = v2; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v2u' (vector of 2 'unsigned int' values)}} v4 = v3; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v4 = v5; // expected-error {{assigning to 'v2f' (vector of 2 'float' values) from incompatible type 'v4ss' (vector of 4 'short' values)}} + v4 = v5; // expected-warning {{incompatible vector types assigning to 'v2f' (vector of 2 'float' values) from 'v4ss' (vector of 4 'short' values)}} v5 = v1; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2s' (vector of 2 'int' values)}} v5 = v2; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2u' (vector of 2 'unsigned int' values)}} v5 = v3; // expected-error {{assigning to 'v4ss' (vector of 4 'short' values) from incompatible type 'v1s' (vector of 1 'int' value)}} - v5 = v4; // expected-error {{assigning to 'v4ss' (vector of 4 'short' values) from incompatible type 'v2f'}} + v5 = v4; // expected-warning {{incompatible vector types assigning to 'v4ss' (vector of 4 'short' values) from 'v2f'}} } // PR2263 diff --git a/clang/test/Sema/vector-cast.c b/clang/test/Sema/vector-cast.c index 01b5c3d252ab..2bdc00707d4c 100644 --- a/clang/test/Sema/vector-cast.c +++ b/clang/test/Sema/vector-cast.c @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only %s -verify=expected,no-lax -Wvector-conversion -flax-vector-conversions=none -// RUN: %clang_cc1 -fsyntax-only %s -verify=expected,lax -Wvector-conversion -flax-vector-conversions=all +// RUN: %clang_cc1 -fsyntax-only %s -verify -Wvector-conversion typedef long long t1 __attribute__ ((vector_size (8))); typedef char t2 __attribute__ ((vector_size (16))); @@ -42,9 +41,7 @@ type 't1' (vector of 1 'long long' value) and integer type 'short' of different void f2(t2 X); // expected-note{{passing argument to parameter 'X' here}} void f3(t3 Y) { - f2(Y); - // lax-warning@-1 {{incompatible vector types passing 't3' (vector of 4 'float' values) to parameter of type 't2' (vector of 16 'char' values)}} - // no-lax-error@-2 {{passing 't3' (vector of 4 'float' values) to parameter of incompatible type 't2' (vector of 16 'char' values)}} + f2(Y); // expected-warning {{incompatible vector types passing 't3' (vector of 4 'float' values) to parameter of type 't2' (vector of 16 'char' values)}} } typedef float float2 __attribute__ ((vector_size (8))); @@ -61,15 +58,13 @@ void f4() { float64x2_t v = {0.0, 1.0}; f2 += d; // expected-error {{cannot convert between scalar type 'double' and vector type 'float2' (vector of 2 'float' values) as implicit conversion would cause truncation}} d += f2; // expected-error {{assigning to 'double' from incompatible type 'float2' (vector of 2 'float' values)}} - a = 3.0 + vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} - b = vget_low_f64(v) + 3.0; // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} - c = vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} - c -= vget_low_f64(v); // no-lax-error {{assigning to 'double' from incompatible type 'float64x1_t' (vector of 1 'double' value)}} + a = 3.0 + vget_low_f64(v); + b = vget_low_f64(v) + 3.0; + c = vget_low_f64(v); + c -= vget_low_f64(v); // LAX conversions between scalar and vector types require same size and one element sized vectors. d = f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}} - d = d + f2; - // lax-error@-1 {{assigning to 'double' from incompatible type 'float2' (vector of 2 'float' values)}} - // no-lax-error@-2 {{cannot convert between scalar type 'double' and vector type 'float2' (vector of 2 'float' values) as implicit conversion would cause truncation}} + d = d + f2; // expected-error {{assigning to 'double' from incompatible type 'float2'}} } // rdar://15931426 @@ -83,8 +78,6 @@ void f5() { } void f6(vSInt32 a0) { - vUInt32 counter = (float16){0.0f, 0.0f, 0.0f, 0.0f}; - // lax-warning@-1 {{incompatible vector types initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of type 'float16' (vector of 4 'float' values)}} - // no-lax-error@-2 {{initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of incompatible type 'float16' (vector of 4 'float' values)}} + vUInt32 counter = (float16){0.0f, 0.0f, 0.0f, 0.0f}; // expected-warning {{incompatible vector types initializing 'vUInt32' (vector of 4 'unsigned int' values) with an expression of type 'float16' (vector of 4 'float' values)}} counter -= a0; } diff --git a/clang/test/Sema/vector-ops.c b/clang/test/Sema/vector-ops.c index d8031f0d2f4a..575f38b972f5 100644 --- a/clang/test/Sema/vector-ops.c +++ b/clang/test/Sema/vector-ops.c @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only -Wvector-conversion -triple x86_64-apple-darwin10 -flax-vector-conversions=all -// FIXME: We get worse diagnostics here with -flax-vector-conversions disabled. +// RUN: %clang_cc1 %s -verify -fsyntax-only -Wvector-conversion -triple x86_64-apple-darwin10 typedef unsigned int v2u __attribute__ ((vector_size (8))); typedef int v2s __attribute__ ((vector_size (8))); typedef float v2f __attribute__ ((vector_size(8))); From ed63454d984f2262ce332b9b15d49917be3eac98 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 23 Jan 2020 12:22:07 -0800 Subject: [PATCH 039/374] Update documentation and release notes to match the state of -flax-vector-conversions on the Clang 10 release branch. --- clang/docs/CommandGuide/clang.rst | 11 ++++++++++- clang/docs/ReleaseNotes.rst | 31 ++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/clang/docs/CommandGuide/clang.rst b/clang/docs/CommandGuide/clang.rst index 7b0873600fc3..6947450beb43 100644 --- a/clang/docs/CommandGuide/clang.rst +++ b/clang/docs/CommandGuide/clang.rst @@ -278,9 +278,18 @@ Language Selection and Mode Options Make all string literals default to writable. This disables uniquing of strings and other optimizations. -.. option:: -flax-vector-conversions +.. option:: -flax-vector-conversions, -flax-vector-conversions=, -fno-lax-vector-conversions Allow loose type checking rules for implicit vector conversions. + Possible values of : + + - ``none``: allow no implicit conversions between vectors + - ``integer``: allow implicit bitcasts between integer vectors of the same + overall bit-width + - ``all``: allow implicit bitcasts between any vectors of the same + overall bit-width + + defaults to ``integer`` if unspecified. .. option:: -fblocks diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8c48724e7c66..caa6abd4b791 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -102,14 +102,13 @@ Non-comprehensive list of changes in this release the found gcc installation is older than 4.7.0. Add ``-fno-use-init-array`` to get the old behavior (``.ctors``). -* Lax vector conversions involving floating-point vectors have been disabled - by default, and can no longer be enabled with ``-flax-vector-conversions``. - This matches the behavior of these flags in GCC, but code relying on implicit - vector bitcasts between integer and floating-point types that used to compile - with older versions of Clang is no longer accepted by default in Clang 10. - The old behavior can be restored with ``-flax-vector-conversions=all``. - In a future release of Clang, we intend to change the default to - ``-fno-lax-vector-conversions``. +* The behavior of the flag ``-flax-vector-conversions`` has been modified to + more closely match GCC, as described below. In Clang 10 onwards, command lines + specifying this flag do not permit implicit vector bitcasts between integer + vectors and floating-point vectors. Such conversions are still permitted by + default, however, and the default can be explicitly requested with the + Clang-specific flag ``-flax-vector-conversions=all``. In a future release of + Clang, we intend to change the default to ``-fno-lax-vector-conversions``. New Compiler Flags ------------------ @@ -142,19 +141,21 @@ Modified Compiler Flags to the ``-march`` flag, overriding the target provided by ``-triple``. - ``-flax-vector-conversions`` has been split into three different levels of - laxness: + laxness, and has been updated to match the GCC semantics: - - ``-flax-vector-conversions=all``: This is Clang's historical default, and + - ``-flax-vector-conversions=all``: This is Clang's current default, and permits implicit vector conversions (performed as bitcasts) between any two vector types of the same overall bit-width. + Former synonym: ``-flax-vector-conversions`` (Clang <= 9). - - ``-flax-vector-conversions=integer``: This is Clang's current default, - and permits implicit vector conversions (performed as bitcasts) between - any two integer vector types of the same overall bit-width. - Synonym: ``-flax-vector-conversions``. + - ``-flax-vector-conversions=integer``: This permits implicit vector + conversions (performed as bitcasts) between any two integer vector types of + the same overall bit-width. + Synonym: ``-flax-vector-conversions`` (Clang >= 10). - ``-flax-vector-conversions=none``: Do not perform any implicit bitcasts - between vector types. Synonym: ``-fno-lax-vector-conversions``. + between vector types. + Synonym: ``-fno-lax-vector-conversions``. New Pragmas in Clang -------------------- From 26fd69afd9f3c6bb48b8a4e60578ea8ae919593f Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 23 Jan 2020 11:52:03 -0800 Subject: [PATCH 040/374] [ELF] --no-dynamic-linker: don't emit undefined weak symbols to .dynsym I felt really sad to push this commit for my selfish purpose to make glibc -static-pie build with lld. Some code constructs in glibc require R_X86_64_GOTPCREL/R_X86_64_REX_GOTPCRELX referencing undefined weak to be resolved to a GOT entry not relocated by R_X86_64_GLOB_DAT (GNU ld behavior), e.g. csu/libc-start.c if (__pthread_initialize_minimal != NULL) __pthread_initialize_minimal (); elf/dl-object.c void _dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid) { /* We modify the list of loaded objects. */ __rtld_lock_lock_recursive (GL(dl_load_write_lock)); Emitting a GLOB_DAT will make the address equal &__ehdr_start (true value) and cause elf/ldconfig to segfault. glibc really should move away from weak references, which do not have defined semantics. Temporarily special case --no-dynamic-linker. (cherry picked from commit 0fbf28f7aae0ceb70071cac56de345e3ff04439c) --- lld/ELF/Config.h | 1 + lld/ELF/Driver.cpp | 7 ++++++- lld/ELF/Symbols.cpp | 6 +++++- lld/test/ELF/weak-undef-no-dynamic-linker.s | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 lld/test/ELF/weak-undef-no-dynamic-linker.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 06ba88a83dd4..ef1edbcd1992 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -169,6 +169,7 @@ struct Configuration { bool mipsN32Abi = false; bool mmapOutputFile; bool nmagic; + bool noDynamicLinker = false; bool noinhibitExec; bool nostdlib; bool oFormatBinary; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 23da749d3078..25330832339c 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -602,8 +602,13 @@ static DiscardPolicy getDiscard(opt::InputArgList &args) { static StringRef getDynamicLinker(opt::InputArgList &args) { auto *arg = args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); - if (!arg || arg->getOption().getID() == OPT_no_dynamic_linker) + if (!arg) + return ""; + if (arg->getOption().getID() == OPT_no_dynamic_linker) { + // --no-dynamic-linker suppresses undefined weak symbols in .dynsym + config->noDynamicLinker = true; return ""; + } return arg->getValue(); } diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index f0f6121009a5..0dcf34722d33 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -278,7 +278,11 @@ bool Symbol::includeInDynsym() const { if (computeBinding() == STB_LOCAL) return false; if (!isDefined() && !isCommon()) - return true; + // This should unconditionally return true, unfortunately glibc -static-pie + // expects undefined weak symbols not to exist in .dynsym, e.g. + // __pthread_mutex_lock reference in _dl_add_to_namespace_list, + // __pthread_initialize_minimal reference in csu/libc-start.c. + return !(config->noDynamicLinker && isUndefWeak()); return exportDynamic || inDynamicList; } diff --git a/lld/test/ELF/weak-undef-no-dynamic-linker.s b/lld/test/ELF/weak-undef-no-dynamic-linker.s new file mode 100644 index 000000000000..fa6936e1ef39 --- /dev/null +++ b/lld/test/ELF/weak-undef-no-dynamic-linker.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: ld.lld -pie %t.o -o %t +# RUN: llvm-readobj --dyn-syms %t | FileCheck %s +# RUN: ld.lld -pie --no-dynamic-linker %t.o -o %t +# RUN: llvm-readobj --dyn-syms %t | FileCheck --check-prefix=NO %s + +## With --no-dynamic-linker, don't emit undefined weak symbols to .dynsym . +## This will suppress a relocation. +# CHECK: Name: foo +# NO-NOT: Name: foo + +.weak foo +cmpq $0, foo@GOTPCREL(%rip) +callq foo From 2dd6b91f35edb967f329f0437b53ea14395aa770 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi Date: Tue, 21 Jan 2020 22:45:23 -0500 Subject: [PATCH 041/374] [clang][IFS][test] Temporary work around for in-process cc1 ASAN issues. When using in-process cc1, the Clang Interface Stubs pipeline setup exposes an ASAN bug. I am still investigating this issue but want to green the bots for now. I don't think this is a huge issue since the Clang Interface Stubs Driver setup code is the only code path that sets up such a pipeline (ie N cc1's for N c files followed by another N cc1's for to generate stub files for the same N c files). This issue is being discussed in https://reviews.llvm.org/D69825. If a resolution is not found soon, a bugzilla filling will be in order. (cherry picked from commit c38e42527b21acee8d01a016d5bfa2fb83202e29) --- clang/test/InterfaceStubs/driver-test.c | 11 ++++++++++- clang/test/InterfaceStubs/driver-test2.c | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/clang/test/InterfaceStubs/driver-test.c b/clang/test/InterfaceStubs/driver-test.c index 9ca5577a5c20..894d896bb219 100644 --- a/clang/test/InterfaceStubs/driver-test.c +++ b/clang/test/InterfaceStubs/driver-test.c @@ -1,8 +1,17 @@ // REQUIRES: x86-registered-target // REQUIRES: shell +// NOTE: -fno-integrated-cc1 has been added to work around an ASAN failure +// caused by in-process cc1 invocation. Clang InterfaceStubs is not the +// culprit, but Clang Interface Stubs' Driver pipeline setup uncovers an +// existing ASAN issue when invoking multiple normal cc1 jobs along with +// multiple Clang Interface Stubs cc1 jobs together. +// There is currently a discussion of this going on at: +// https://reviews.llvm.org/D69825 // RUN: mkdir -p %t; cd %t -// RUN: %clang -target x86_64-unknown-linux-gnu -x c -S -emit-interface-stubs %s %S/object.c %S/weak.cpp && \ +// RUN: %clang -target x86_64-unknown-linux-gnu -x c -S \ +// RUN: -fno-integrated-cc1 \ +// RUN: -emit-interface-stubs %s %S/object.c %S/weak.cpp && \ // RUN: llvm-nm %t/a.out.ifso 2>&1 | FileCheck --check-prefix=CHECK-IFS %s // CHECK-IFS-DAG: data diff --git a/clang/test/InterfaceStubs/driver-test2.c b/clang/test/InterfaceStubs/driver-test2.c index c3a3b31b212d..905b27922264 100644 --- a/clang/test/InterfaceStubs/driver-test2.c +++ b/clang/test/InterfaceStubs/driver-test2.c @@ -1,10 +1,19 @@ // REQUIRES: x86-registered-target // REQUIRES: shell +// NOTE: -fno-integrated-cc1 has been added to work around an ASAN failure +// caused by in-process cc1 invocation. Clang InterfaceStubs is not the +// culprit, but Clang Interface Stubs' Driver pipeline setup uncovers an +// existing ASAN issue when invoking multiple normal cc1 jobs along with +// multiple Clang Interface Stubs cc1 jobs together. +// There is currently a discussion of this going on at: +// https://reviews.llvm.org/D69825 // RUN: mkdir -p %t; cd %t // RUN: %clang -target x86_64-unknown-linux-gnu -c -emit-interface-stubs \ +// RUN: -fno-integrated-cc1 \ // RUN: %s %S/object.c %S/weak.cpp // RUN: %clang -emit-interface-stubs -emit-merged-ifs \ +// RUN: -fno-integrated-cc1 \ // RUN: %t/driver-test2.o %t/object.o %t/weak.o -S -o - 2>&1 | FileCheck %s // CHECK-DAG: data From bfaba51f07d1b79ff6e71da106c2b7e153874b1d Mon Sep 17 00:00:00 2001 From: Mitch Phillips <31459023+hctim@users.noreply.github.com> Date: Thu, 23 Jan 2020 14:23:38 -0800 Subject: [PATCH 042/374] [Clang][IFS][Test] Work around in-process cc1 ASAN issues #2. Using the same strategy as c38e42527b21. D69825 revealed (introduced?) a problem when building with ASan, and some memory leaks somewhere. More details are available in the original patch. Looks like we missed one failing tests, this patch adds the workaround to this test as well. (cherry picked from commit e174da447c180b586719cb28f7bd556e30625762) --- clang/test/Driver/cl-showfilenames.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/clang/test/Driver/cl-showfilenames.c b/clang/test/Driver/cl-showfilenames.c index b2932f1a01ac..73205978c44c 100644 --- a/clang/test/Driver/cl-showfilenames.c +++ b/clang/test/Driver/cl-showfilenames.c @@ -2,11 +2,19 @@ // target Windows. // REQUIRES: x86-registered-target -// RUN: %clang_cl --target=i686-pc-win32 /c /Fo%T/ /showFilenames -- %s 2>&1 | FileCheck -check-prefix=show %s -// RUN: %clang_cl --target=i686-pc-win32 /c /Fo%T/ /showFilenames -- %s %S/Inputs/wildcard*.c 2>&1 | FileCheck -check-prefix=multiple %s +// NOTE: -fno-integrated-cc1 has been added to work around an ASAN failure +// caused by in-process cc1 invocation. Clang InterfaceStubs is not the +// culprit, but Clang Interface Stubs' Driver pipeline setup uncovers an +// existing ASAN issue when invoking multiple normal cc1 jobs along with +// multiple Clang Interface Stubs cc1 jobs together. +// There is currently a discussion of this going on at: +// https://reviews.llvm.org/D69825 -// RUN: %clang_cl --target=i686-pc-win32 /c /Fo%T/ -- %s 2>&1 | FileCheck -check-prefix=noshow %s -// RUN: %clang_cl --target=i686-pc-win32 /c /Fo%T/ /showFilenames /showFilenames- -- %s 2>&1 | FileCheck -check-prefix=noshow %s +// RUN: %clang_cl -fno-integrated-cc1 --target=i686-pc-win32 /c /Fo%T/ /showFilenames -- %s 2>&1 | FileCheck -check-prefix=show %s +// RUN: %clang_cl -fno-integrated-cc1 --target=i686-pc-win32 /c /Fo%T/ /showFilenames -- %s %S/Inputs/wildcard*.c 2>&1 | FileCheck -check-prefix=multiple %s + +// RUN: %clang_cl -fno-integrated-cc1 --target=i686-pc-win32 /c /Fo%T/ -- %s 2>&1 | FileCheck -check-prefix=noshow %s +// RUN: %clang_cl -fno-integrated-cc1 --target=i686-pc-win32 /c /Fo%T/ /showFilenames /showFilenames- -- %s 2>&1 | FileCheck -check-prefix=noshow %s #pragma message "Hello" From 57f70e387e362d988937b6627461d781ecf09e50 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Thu, 16 Jan 2020 13:35:20 +0200 Subject: [PATCH 043/374] [Concepts] Fix ConceptSpecializationExpr profiling crash ConceptSpecializationExprs (CSEs) were being created with nullptr TemplateArgsAsWritten during TemplateTemplateParmDecl canonicalization, and we were relying on them during profiling which caused sporadic crashes in test/CXX/.../temp.arg.template/p3-2a.cpp introduced in D44352. Change profiling of CSEs to instead rely on the actual converted template arguments and concept named. (cherry picked from commit 8a3446746098ba29348bb8f85357dd0b466a6d6e) --- clang/lib/AST/StmtProfile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 2aa5106e90fa..c0b0f3b0b064 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1335,9 +1335,9 @@ void StmtProfiler::VisitAtomicExpr(const AtomicExpr *S) { void StmtProfiler::VisitConceptSpecializationExpr( const ConceptSpecializationExpr *S) { VisitExpr(S); - VisitDecl(S->getFoundDecl()); - VisitTemplateArguments(S->getTemplateArgsAsWritten()->getTemplateArgs(), - S->getTemplateArgsAsWritten()->NumTemplateArgs); + VisitDecl(S->getNamedConcept()); + for (const TemplateArgument &Arg : S->getTemplateArguments()) + VisitTemplateArgument(Arg); } static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, From ab514b91196345ba4c61026e4997871e85ddd97d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 17 Jan 2020 15:42:11 -0800 Subject: [PATCH 044/374] Remove redundant CXXScopeSpec from TemplateIdAnnotation. A TemplateIdAnnotation represents only a template-id, not a nested-name-specifier plus a template-id. Don't make a redundant copy of the CXXScopeSpec and store it on the template-id annotation. This slightly improves error recovery by more properly handling the case where we would form an invalid CXXScopeSpec while parsing a typename specifier, instead of accidentally putting the token stream into a broken "annot_template_id with a scope specifier, but with no preceding annot_cxxscope token" state. (cherry picked from commit a42fd84cff265b7e9faa3fe42885ee171393e4db) --- clang/include/clang/Parse/Parser.h | 7 +- clang/include/clang/Sema/ParsedTemplate.h | 24 +-- clang/include/clang/Sema/Sema.h | 7 +- clang/lib/Parse/ParseDecl.cpp | 9 +- clang/lib/Parse/ParseDeclCXX.cpp | 10 +- clang/lib/Parse/ParseExpr.cpp | 5 +- clang/lib/Parse/ParseExprCXX.cpp | 9 +- clang/lib/Parse/ParseTemplate.cpp | 194 ++++++++++-------- clang/lib/Parse/ParseTentative.cpp | 2 +- clang/lib/Parse/Parser.cpp | 4 +- clang/lib/Sema/SemaExprCXX.cpp | 4 +- clang/lib/Sema/SemaTemplate.cpp | 12 +- .../temp.dep/temp.dep.constexpr/p2.cpp | 2 +- clang/test/Parser/cxx-decl.cpp | 5 +- .../ms-delayed-default-template-args.cpp | 2 +- clang/test/SemaTemplate/rdar9173693.cpp | 9 +- 16 files changed, 153 insertions(+), 152 deletions(-) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index b7bed4713992..182024ea5108 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3079,13 +3079,13 @@ class Parser : public CodeCompletionHandler { SourceLocation &RAngleLoc); bool ParseTemplateParameterList(unsigned Depth, SmallVectorImpl &TemplateParams); - bool isStartOfTemplateTypeParameter(bool &ScopeError); + TPResult isStartOfTemplateTypeParameter(); NamedDecl *ParseTemplateParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTypeParameter(unsigned Depth, unsigned Position); NamedDecl *ParseTemplateTemplateParameter(unsigned Depth, unsigned Position); NamedDecl *ParseNonTypeTemplateParameter(unsigned Depth, unsigned Position); bool isTypeConstraintAnnotation(); - bool TryAnnotateTypeConstraint(CXXScopeSpec &SS); + bool TryAnnotateTypeConstraint(); NamedDecl * ParseConstrainedTemplateTypeParameter(unsigned Depth, unsigned Position); void DiagnoseMisplacedEllipsis(SourceLocation EllipsisLoc, @@ -3111,7 +3111,8 @@ class Parser : public CodeCompletionHandler { UnqualifiedId &TemplateName, bool AllowTypeAnnotation = true, bool TypeConstraint = false); - void AnnotateTemplateIdTokenAsType(bool IsClassName = false); + void AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, + bool IsClassName = false); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); ParsedTemplateArgument ParseTemplateTemplateArgument(); ParsedTemplateArgument ParseTemplateArgument(); diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h index 0874905b38a5..82d00494b0d6 100644 --- a/clang/include/clang/Sema/ParsedTemplate.h +++ b/clang/include/clang/Sema/ParsedTemplate.h @@ -139,9 +139,8 @@ namespace clang { /// Information about a template-id annotation /// token. /// - /// A template-id annotation token contains the template declaration, - /// template arguments, whether those template arguments were types, - /// expressions, or template names, and the source locations for important + /// A template-id annotation token contains the template name, + /// template arguments, and the source locations for important /// tokens. All of the information about template arguments is allocated /// directly after this structure. /// A template-id annotation token can also be generated by a type-constraint @@ -152,9 +151,6 @@ namespace clang { : private llvm::TrailingObjects { friend TrailingObjects; - /// The nested-name-specifier that precedes the template name. - CXXScopeSpec SS; - /// TemplateKWLoc - The location of the template keyword. /// For e.g. typename T::template Y SourceLocation TemplateKWLoc; @@ -195,16 +191,15 @@ namespace clang { /// Creates a new TemplateIdAnnotation with NumArgs arguments and /// appends it to List. static TemplateIdAnnotation * - Create(CXXScopeSpec SS, SourceLocation TemplateKWLoc, - SourceLocation TemplateNameLoc, IdentifierInfo *Name, - OverloadedOperatorKind OperatorKind, + Create(SourceLocation TemplateKWLoc, SourceLocation TemplateNameLoc, + IdentifierInfo *Name, OverloadedOperatorKind OperatorKind, ParsedTemplateTy OpaqueTemplateName, TemplateNameKind TemplateKind, SourceLocation LAngleLoc, SourceLocation RAngleLoc, ArrayRef TemplateArgs, SmallVectorImpl &CleanupList) { TemplateIdAnnotation *TemplateId = new (llvm::safe_malloc( totalSizeToAlloc(TemplateArgs.size()))) - TemplateIdAnnotation(SS, TemplateKWLoc, TemplateNameLoc, Name, + TemplateIdAnnotation(TemplateKWLoc, TemplateNameLoc, Name, OperatorKind, OpaqueTemplateName, TemplateKind, LAngleLoc, RAngleLoc, TemplateArgs); CleanupList.push_back(TemplateId); @@ -221,17 +216,16 @@ namespace clang { private: TemplateIdAnnotation(const TemplateIdAnnotation &) = delete; - TemplateIdAnnotation(CXXScopeSpec SS, SourceLocation TemplateKWLoc, + TemplateIdAnnotation(SourceLocation TemplateKWLoc, SourceLocation TemplateNameLoc, IdentifierInfo *Name, OverloadedOperatorKind OperatorKind, ParsedTemplateTy OpaqueTemplateName, TemplateNameKind TemplateKind, SourceLocation LAngleLoc, SourceLocation RAngleLoc, ArrayRef TemplateArgs) noexcept - : SS(SS), TemplateKWLoc(TemplateKWLoc), - TemplateNameLoc(TemplateNameLoc), Name(Name), Operator(OperatorKind), - Template(OpaqueTemplateName), Kind(TemplateKind), - LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), + : TemplateKWLoc(TemplateKWLoc), TemplateNameLoc(TemplateNameLoc), + Name(Name), Operator(OperatorKind), Template(OpaqueTemplateName), + Kind(TemplateKind), LAngleLoc(LAngleLoc), RAngleLoc(RAngleLoc), NumArgs(TemplateArgs.size()) { std::uninitialized_copy(TemplateArgs.begin(), TemplateArgs.end(), diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 47a055f696b1..4789fad89560 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6873,7 +6873,8 @@ class Sema final { SourceLocation EqualLoc, ParsedType DefaultArg, bool HasTypeConstraint); - bool ActOnTypeConstraint(TemplateIdAnnotation *TypeConstraint, + bool ActOnTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc); @@ -7028,8 +7029,8 @@ class Sema final { DeclResult ActOnClassTemplateSpecialization( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, - SourceLocation ModulePrivateLoc, TemplateIdAnnotation &TemplateId, - const ParsedAttributesView &Attr, + SourceLocation ModulePrivateLoc, CXXScopeSpec &SS, + TemplateIdAnnotation &TemplateId, const ParsedAttributesView &Attr, MultiTemplateParamsArg TemplateParameterLists, SkipBodyInfo *SkipBody = nullptr); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index d8c5a0ab02d3..192c0e99e5a5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3177,7 +3177,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DSContext == DeclSpecContext::DSC_class) && TemplateId->Name && Actions.isCurrentClassName(*TemplateId->Name, getCurScope(), &SS) && - isConstructorDeclarator(/*Unqualified*/ false)) { + isConstructorDeclarator(/*Unqualified=*/false)) { // The user meant this to be an out-of-line constructor // definition, but template arguments are not allowed // there. Just allow this as a constructor; we'll @@ -3189,7 +3189,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumeAnnotationToken(); // The C++ scope. assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); continue; } @@ -3448,12 +3448,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // constructor declaration. if (getLangOpts().CPlusPlus && DSContext == DeclSpecContext::DSC_class && Actions.isCurrentClassName(*TemplateId->Name, getCurScope()) && - isConstructorDeclarator(TemplateId->SS.isEmpty())) + isConstructorDeclarator(/*Unqualified=*/true)) goto DoneWithDeclSpec; // Turn the template-id annotation token into a type annotation // token, then try again to parse it as a type-specifier. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); continue; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 081d4d8b1209..9c7d3c566554 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1142,7 +1142,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name || TemplateId->Kind == TNK_Undeclared_template) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); ParsedType Type = getTypeAnnotation(Tok); @@ -1193,7 +1193,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc, TemplateName)) return true; if (TNK == TNK_Type_template || TNK == TNK_Dependent_template_name) - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); // If we didn't end up with a typename token, there's nothing more we // can do. @@ -1826,7 +1826,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate)) { ProhibitAttributes(attrs); TypeResult = Actions.ActOnTagTemplateIdType(TUK, TagType, StartLoc, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->TemplateNameLoc, @@ -1876,7 +1876,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Build the class template specialization. TagOrTempResult = Actions.ActOnClassTemplateSpecialization( getCurScope(), TagType, TUK, StartLoc, DS.getModulePrivateSpecLoc(), - *TemplateId, attrs, + SS, *TemplateId, attrs, MultiTemplateParamsArg(TemplateParams ? &(*TemplateParams)[0] : nullptr, TemplateParams ? TemplateParams->size() : 0), @@ -3520,7 +3520,7 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { if (TemplateId && (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name || TemplateId->Kind == TNK_Undeclared_template)) { - AnnotateTemplateIdTokenAsType(/*IsClassName*/true); + AnnotateTemplateIdTokenAsType(SS, /*IsClassName*/true); assert(Tok.is(tok::annot_typename) && "template-id -> type failed"); TemplateTypeTy = getTypeAnnotation(Tok); ConsumeAnnotationToken(); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 1442df046bb9..32dacbcc9646 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1530,7 +1530,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, CXXScopeSpec SS; ParseOptionalCXXScopeSpecifier(SS, nullptr, /*EnteringContext=*/false); - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); @@ -1548,7 +1548,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, // We have a template-id that we know refers to a type, // translate it into a type and continue parsing as a cast // expression. - AnnotateTemplateIdTokenAsType(); + CXXScopeSpec SS; + AnnotateTemplateIdTokenAsType(SS); return ParseCastExpression(ParseKind, isAddressOfOperand, NotCastExpr, isTypeCast, isVectorLiteral, NotPrimaryExpression); diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index e685d5ea8a9c..7d477d271dc2 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -165,13 +165,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, return false; } - if (Tok.is(tok::annot_template_id)) { - // If the current token is an annotated template id, it may already have - // a scope specifier. Restore it. - TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); - SS = TemplateId->SS; - } - // Has to happen before any "return false"s in this function. bool CheckForDestructor = false; if (MayBePseudoDestructor && *MayBePseudoDestructor) { @@ -2405,7 +2398,7 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS, : Id.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, + TemplateKWLoc, Id.StartLocation, TemplateII, OpKind, Template, TNK, LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); Id.setTemplateId(TemplateId); diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 1b9301b6591d..0f7aefaa147a 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -499,10 +499,7 @@ Parser::ParseTemplateParameterList(const unsigned Depth, /// Determine whether the parser is at the start of a template /// type parameter. -/// \param ScopeError will receive true if there was an error parsing a -/// scope specifier at the current location. -bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { - ScopeError = false; +Parser::TPResult Parser::isStartOfTemplateTypeParameter() { if (Tok.is(tok::kw_class)) { // "class" may be the start of an elaborated-type-specifier or a // type-parameter. Per C++ [temp.param]p3, we prefer the type-parameter. @@ -512,7 +509,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::identifier: // This may be either a type-parameter or an elaborated-type-specifier. @@ -520,7 +517,7 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { break; default: - return false; + return TPResult::False; } switch (GetLookAheadToken(2).getKind()) { @@ -528,51 +525,28 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::comma: case tok::greater: case tok::greatergreater: - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } - bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); - CXXScopeSpec SS; - ScopeError = - ParseOptionalCXXScopeSpecifier(SS, ParsedType(), - /*EnteringContext=*/false, - /*MayBePseudoDestructor=*/nullptr, - // If this is not a type-constraint, then - // this scope-spec is part of the typename - // of a non-type template parameter - /*IsTypename=*/true, /*LastII=*/nullptr, - // We won't find concepts in - // non-namespaces anyway, so might as well - // parse this correctly for possible type - // names. - /*OnlyNamespace=*/false); - if (ScopeError) - return false; - if (TryAnnotateTypeConstraint(SS)) - return false; - bool IsTypeConstraint = isTypeConstraintAnnotation(); - if (!IsTypeConstraint && SS.isNotEmpty()) { - // This isn't a type-constraint but we've already parsed this scope - // specifier - annotate it. - AnnotateScopeToken(SS, /*isNewAnnotation=*/!WasScopeAnnotation); - return false; - } + if (TryAnnotateTypeConstraint()) + return TPResult::Error; - if (IsTypeConstraint && + if (isTypeConstraintAnnotation() && // Next token might be 'auto' or 'decltype', indicating that this // type-constraint is in fact part of a placeholder-type-specifier of a // non-type template parameter. - !NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) - return true; + !GetLookAheadToken(Tok.is(tok::annot_cxxscope) ? 2 : 1) + .isOneOf(tok::kw_auto, tok::kw_decltype)) + return TPResult::True; // 'typedef' is a reasonably-common typo/thinko for 'typename', and is // ill-formed otherwise. if (Tok.isNot(tok::kw_typename) && Tok.isNot(tok::kw_typedef)) - return false; + return TPResult::False; // C++ [temp.param]p2: // There is no semantic difference between class and typename in a @@ -592,17 +566,17 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { case tok::greater: case tok::greatergreater: case tok::ellipsis: - return true; + return TPResult::True; case tok::kw_typename: case tok::kw_typedef: case tok::kw_class: // These indicate that a comma was missed after a type parameter, not that // we have found a non-type parameter. - return true; + return TPResult::True; default: - return false; + return TPResult::False; } } @@ -627,13 +601,8 @@ bool Parser::isStartOfTemplateTypeParameter(bool &ScopeError) { /// typename /// NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { - // We could be facing a type-constraint, which (could) start a type parameter. - // Annotate it now (we might end up not using it if we determine this - // type-constraint is in fact part of a placeholder-type-specifier of a - // non-type template parameter. - - bool ScopeError; - if (isStartOfTemplateTypeParameter(ScopeError)) { + switch (isStartOfTemplateTypeParameter()) { + case TPResult::True: // Is there just a typo in the input code? ('typedef' instead of // 'typename') if (Tok.is(tok::kw_typedef)) { @@ -649,8 +618,11 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { } return ParseTypeParameter(Depth, Position); - } - if (ScopeError) { + + case TPResult::False: + break; + + case TPResult::Error: { // We return an invalid parameter as opposed to null to avoid having bogus // diagnostics about an empty template parameter list. // FIXME: Fix ParseTemplateParameterList to better handle nullptr results @@ -670,6 +642,11 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { StopAtSemi | StopBeforeMatch); return ErrorParam; } + + case TPResult::Ambiguous: + llvm_unreachable("template param classification can't be ambiguous"); + } + if (Tok.is(tok::kw_template)) return ParseTemplateTemplateParameter(Depth, Position); @@ -682,15 +659,15 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { /// Check whether the current token is a template-id annotation denoting a /// type-constraint. bool Parser::isTypeConstraintAnnotation() { - if (Tok.isNot(tok::annot_template_id)) + const Token &T = Tok.is(tok::annot_cxxscope) ? NextToken() : Tok; + if (T.isNot(tok::annot_template_id)) return false; const auto *ExistingAnnot = - static_cast(Tok.getAnnotationValue()); + static_cast(T.getAnnotationValue()); return ExistingAnnot->Kind == TNK_Concept_template; } -/// Try parsing a type-constraint construct at the current location, after the -/// optional scope specifier. +/// Try parsing a type-constraint at the current location. /// /// type-constraint: /// nested-name-specifier[opt] concept-name @@ -698,35 +675,60 @@ bool Parser::isTypeConstraintAnnotation() { /// '<' template-argument-list[opt] '>'[opt] /// /// \returns true if an error occurred, and false otherwise. -bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { - if (!getLangOpts().ConceptsTS || Tok.isNot(tok::identifier)) +bool Parser::TryAnnotateTypeConstraint() { + if (!getLangOpts().ConceptsTS) return false; - UnqualifiedId PossibleConceptName; - PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), - Tok.getLocation()); - - TemplateTy PossibleConcept; - bool MemberOfUnknownSpecialization = false; - auto TNK = Actions.isTemplateName(getCurScope(), SS, - /*hasTemplateKeyword=*/false, - PossibleConceptName, - /*ObjectType=*/ParsedType(), - /*EnteringContext=*/false, - PossibleConcept, - MemberOfUnknownSpecialization); - assert(!MemberOfUnknownSpecialization - && "Member when we only allowed namespace scope qualifiers??"); - if (!PossibleConcept || TNK != TNK_Concept_template) - return false; + CXXScopeSpec SS; + bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); + if (ParseOptionalCXXScopeSpecifier( + SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, then + // this scope-spec is part of the typename + // of a non-type template parameter + /*IsTypename=*/true, /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as well + // parse this correctly for possible type + // names. + /*OnlyNamespace=*/false)) + return true; - // At this point we're sure we're dealing with a constrained parameter. It - // may or may not have a template parameter list following the concept name. - return AnnotateTemplateIdToken(PossibleConcept, TNK, SS, - /*TemplateKWLoc=*/SourceLocation(), - PossibleConceptName, - /*AllowTypeAnnotation=*/false, - /*TypeConstraint=*/true); + if (Tok.is(tok::identifier)) { + UnqualifiedId PossibleConceptName; + PossibleConceptName.setIdentifier(Tok.getIdentifierInfo(), + Tok.getLocation()); + + TemplateTy PossibleConcept; + bool MemberOfUnknownSpecialization = false; + auto TNK = Actions.isTemplateName(getCurScope(), SS, + /*hasTemplateKeyword=*/false, + PossibleConceptName, + /*ObjectType=*/ParsedType(), + /*EnteringContext=*/false, + PossibleConcept, + MemberOfUnknownSpecialization); + assert(!MemberOfUnknownSpecialization + && "Member when we only allowed namespace scope qualifiers??"); + if (!PossibleConcept || TNK != TNK_Concept_template) + return false; + + // At this point we're sure we're dealing with a constrained parameter. It + // may or may not have a template parameter list following the concept + // name. + if (AnnotateTemplateIdToken(PossibleConcept, TNK, SS, + /*TemplateKWLoc=*/SourceLocation(), + PossibleConceptName, + /*AllowTypeAnnotation=*/false, + /*TypeConstraint=*/true)) + return true; + } + + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return false; } /// ParseTypeParameter - Parse a template type parameter (C++ [temp.param]). @@ -739,13 +741,17 @@ bool Parser::TryAnnotateTypeConstraint(CXXScopeSpec &SS) { /// 'typename' ...[opt][C++0x] identifier[opt] /// 'typename' identifier[opt] '=' type-id NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { - assert(Tok.isOneOf(tok::kw_class, tok::kw_typename, tok::annot_template_id) && + assert((Tok.isOneOf(tok::kw_class, tok::kw_typename) || + isTypeConstraintAnnotation()) && "A type-parameter starts with 'class', 'typename' or a " "type-constraint"); + CXXScopeSpec TypeConstraintSS; TemplateIdAnnotation *TypeConstraint = nullptr; bool TypenameKeyword = false; SourceLocation KeyLoc; + ParseOptionalCXXScopeSpecifier(TypeConstraintSS, nullptr, + /*EnteringContext*/ false); if (Tok.is(tok::annot_template_id)) { // Consume the 'type-constraint'. TypeConstraint = @@ -754,6 +760,9 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { "stray non-concept template-id annotation"); KeyLoc = ConsumeAnnotationToken(); } else { + assert(TypeConstraintSS.isEmpty() && + "expected type constraint after scope specifier"); + // Consume the 'class' or 'typename' keyword. TypenameKeyword = Tok.is(tok::kw_typename); KeyLoc = ConsumeToken(); @@ -795,7 +804,8 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { ParsedType DefaultArg; if (TryConsumeToken(tok::equal, EqualLoc)) DefaultArg = ParseTypeName(/*Range=*/nullptr, - DeclaratorContext::TemplateTypeArgContext).get(); + DeclaratorContext::TemplateTypeArgContext) + .get(); NamedDecl *NewDecl = Actions.ActOnTypeParameter(getCurScope(), TypenameKeyword, EllipsisLoc, @@ -804,10 +814,11 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) { DefaultArg, TypeConstraint != nullptr); - if (TypeConstraint) - Actions.ActOnTypeConstraint(TypeConstraint, + if (TypeConstraint) { + Actions.ActOnTypeConstraint(TypeConstraintSS, TypeConstraint, cast(NewDecl), EllipsisLoc); + } return NewDecl; } @@ -1331,8 +1342,8 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, : TemplateName.OperatorFunctionId.Operator; TemplateIdAnnotation *TemplateId = TemplateIdAnnotation::Create( - SS, TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, - LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); + TemplateKWLoc, TemplateNameLoc, TemplateII, OpKind, Template, TNK, + LAngleLoc, RAngleLoc, TemplateArgs, TemplateIds); Tok.setAnnotationValue(TemplateId); if (TemplateKWLoc.isValid()) @@ -1357,11 +1368,14 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, /// a type annotation token will still be created, but will have a /// NULL type pointer to signify an error. /// +/// \param SS The scope specifier appearing before the template-id, if any. +/// /// \param IsClassName Is this template-id appearing in a context where we /// know it names a class, such as in an elaborated-type-specifier or /// base-specifier? ('typename' and 'template' are unneeded and disallowed /// in those contexts.) -void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { +void Parser::AnnotateTemplateIdTokenAsType(CXXScopeSpec &SS, + bool IsClassName) { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); @@ -1375,7 +1389,7 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { TypeResult Type = Actions.ActOnTemplateIdType(getCurScope(), - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -1388,8 +1402,8 @@ void Parser::AnnotateTemplateIdTokenAsType(bool IsClassName) { // Create the new "type" annotation token. Tok.setKind(tok::annot_typename); setTypeAnnotation(Tok, Type.isInvalid() ? nullptr : Type.get()); - if (TemplateId->SS.isNotEmpty()) // it was a C++ qualified type name. - Tok.setLocation(TemplateId->SS.getBeginLoc()); + if (SS.isNotEmpty()) // it was a C++ qualified type name. + Tok.setLocation(SS.getBeginLoc()); // End location stays the same // Replace the template-id annotation token, and possible the scope-specifier diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index d5068fb11b86..79e96816f864 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1519,7 +1519,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); assert(Tok.is(tok::annot_typename)); goto case_typename; } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0fb0a5217d54..0194c243f93d 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1810,7 +1810,7 @@ bool Parser::TryAnnotateTypeOrScopeToken() { /*EnteringContext=*/false, nullptr, /*IsTypename*/ true)) return true; - if (!SS.isSet()) { + if (SS.isEmpty()) { if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) || Tok.is(tok::annot_decltype)) { // Attempt to recover by skipping the invalid 'typename' @@ -1983,7 +1983,7 @@ bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(CXXScopeSpec &SS, // template-id annotation in a context where we weren't allowed // to produce a type annotation token. Update the template-id // annotation token to a type annotation token now. - AnnotateTemplateIdTokenAsType(); + AnnotateTemplateIdTokenAsType(SS); return false; } } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index a73e6906fceb..938420d85c65 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -7317,7 +7317,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); TypeResult T = ActOnTemplateIdType(S, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, @@ -7370,7 +7370,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base, ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), TemplateId->NumArgs); TypeResult T = ActOnTemplateIdType(S, - TemplateId->SS, + SS, TemplateId->TemplateKWLoc, TemplateId->Template, TemplateId->Name, diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1184446796eb..8a50a9e1538f 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1050,7 +1050,8 @@ makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) { return TemplateArgs; } -bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr, +bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstr, TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc) { ConceptDecl *CD = @@ -1080,8 +1081,7 @@ bool Sema::ActOnTypeConstraint(TemplateIdAnnotation *TypeConstr, makeTemplateArgumentListInfo(*this, *TypeConstr); } return AttachTypeConstraint( - TypeConstr->SS.isSet() ? TypeConstr->SS.getWithLocInContext(Context) : - NestedNameSpecifierLoc(), + SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(), DeclarationNameInfo(DeclarationName(TypeConstr->Name), TypeConstr->TemplateNameLoc), CD, TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr, @@ -7872,13 +7872,11 @@ bool Sema::CheckTemplatePartialSpecializationArgs( DeclResult Sema::ActOnClassTemplateSpecialization( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, - SourceLocation ModulePrivateLoc, TemplateIdAnnotation &TemplateId, - const ParsedAttributesView &Attr, + SourceLocation ModulePrivateLoc, CXXScopeSpec &SS, + TemplateIdAnnotation &TemplateId, const ParsedAttributesView &Attr, MultiTemplateParamsArg TemplateParameterLists, SkipBodyInfo *SkipBody) { assert(TUK != TUK_Reference && "References are not specializations"); - CXXScopeSpec &SS = TemplateId.SS; - // NOTE: KWLoc is the location of the tag keyword. This will instead // store the location of the outermost template keyword in the declaration. SourceLocation TemplateKWLoc = TemplateParameterLists.size() > 0 diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp index 68a41c7184c0..6c0df1aff231 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.constexpr/p2.cpp @@ -16,6 +16,6 @@ template struct T { typename S::T check3; // ok, u is value-dependent const int &i = k; - typename S::T check4; // expected-error {{not an integral constant expression}} expected-error {{qualified name}} + typename S::T check4; // expected-error {{not an integral constant expression}} } }; diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp index a868904bb36d..1914f347d9dd 100644 --- a/clang/test/Parser/cxx-decl.cpp +++ b/clang/test/Parser/cxx-decl.cpp @@ -238,12 +238,11 @@ namespace PR5066 { namespace PR17255 { void foo() { - typename A::template B<>; // expected-error {{use of undeclared identifier 'A'}} - // expected-error@-1 {{'template' keyword not permitted here}} + typename A::template B<> c; // expected-error {{use of undeclared identifier 'A'}} #if __cplusplus <= 199711L + // expected-error@-2 {{'typename' occurs outside of a template}} // expected-error@-3 {{'template' keyword outside of a template}} #endif - // expected-error@-5 {{expected a qualified name after 'typename'}} } } diff --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp index beb64f8273da..276464875342 100644 --- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp +++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp @@ -93,7 +93,7 @@ namespace test_undeclared_nontype_parm_arg { // template parameter. template struct Bar { T x; }; -template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} +template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} expected-note {{declared here}} struct Foo { }; typedef int Xylophone; diff --git a/clang/test/SemaTemplate/rdar9173693.cpp b/clang/test/SemaTemplate/rdar9173693.cpp index ed09a64a0ddd..76919e265fd3 100644 --- a/clang/test/SemaTemplate/rdar9173693.cpp +++ b/clang/test/SemaTemplate/rdar9173693.cpp @@ -2,8 +2,7 @@ // template< bool C > struct assert { }; -// FIXME: We diagnose the same problem multiple times here because we have no -// way to indicate in the token stream that we already tried to annotate a -// template-id and we failed. -template< bool > struct assert_arg_pred_impl { }; // expected-note 4 {{declared here}} -template< typename Pred > assert assert_not_arg( void (*)(Pred), typename assert_arg_pred::type ); // expected-error 6 {{}} +template< bool > struct assert_arg_pred_impl { }; // expected-note 2 {{declared here}} +template< typename Pred > assert assert_not_arg( void (*)(Pred), typename assert_arg_pred::type ); +// expected-error@-1 {{did you mean 'assert_arg_pred_impl'}} +// expected-error@-2 {{template argument for non-type template parameter must be an expression}} From c96ef5118857ff938aa6d304ccf7c0f9b81bc5ba Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Sat, 18 Jan 2020 09:11:43 +0200 Subject: [PATCH 045/374] [Concepts] Requires Expressions Implement support for C++2a requires-expressions. Re-commit after compilation failure on some platforms due to alignment issues with PointerIntPair. Differential Revision: https://reviews.llvm.org/D50360 (cherry picked from commit a0f50d731639350c7a79f140f026c27a18215531) --- clang/include/clang/AST/ASTConcept.h | 1 + clang/include/clang/AST/DeclCXX.h | 31 + clang/include/clang/AST/ExprCXX.h | 94 --- clang/include/clang/AST/ExprConcepts.h | 540 ++++++++++++++++++ clang/include/clang/AST/RecursiveASTVisitor.h | 25 + clang/include/clang/AST/Stmt.h | 12 + clang/include/clang/AST/StmtVisitor.h | 1 + clang/include/clang/Basic/DeclNodes.td | 1 + .../clang/Basic/DiagnosticParseKinds.td | 27 + .../clang/Basic/DiagnosticSemaKinds.td | 66 ++- clang/include/clang/Basic/StmtNodes.td | 1 + clang/include/clang/Parse/Parser.h | 3 +- clang/include/clang/Sema/DeclSpec.h | 9 +- clang/include/clang/Sema/Sema.h | 84 ++- clang/include/clang/Sema/SemaConcept.h | 7 + .../include/clang/Serialization/ASTBitCodes.h | 4 + clang/lib/AST/CMakeLists.txt | 1 + clang/lib/AST/DeclBase.cpp | 2 + clang/lib/AST/DeclCXX.cpp | 10 + clang/lib/AST/Expr.cpp | 1 + clang/lib/AST/ExprCXX.cpp | 79 +-- clang/lib/AST/ExprClassification.cpp | 1 + clang/lib/AST/ExprConcepts.cpp | 185 ++++++ clang/lib/AST/ExprConstant.cpp | 5 + clang/lib/AST/ItaniumMangle.cpp | 2 + clang/lib/AST/Stmt.cpp | 1 + clang/lib/AST/StmtPrinter.cpp | 54 ++ clang/lib/AST/StmtProfile.cpp | 43 ++ clang/lib/CodeGen/CGDecl.cpp | 1 + clang/lib/CodeGen/CGExprScalar.cpp | 4 + clang/lib/Frontend/FrontendActions.cpp | 4 + clang/lib/Frontend/InitPreprocessor.cpp | 3 + clang/lib/Parse/ParseDecl.cpp | 20 +- clang/lib/Parse/ParseExpr.cpp | 4 + clang/lib/Parse/ParseExprCXX.cpp | 326 ++++++++++- clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaConcept.cpp | 204 ++++++- clang/lib/Sema/SemaDecl.cpp | 2 + clang/lib/Sema/SemaExceptionSpec.cpp | 1 + clang/lib/Sema/SemaExpr.cpp | 13 +- clang/lib/Sema/SemaExprCXX.cpp | 213 +++++++ clang/lib/Sema/SemaLookup.cpp | 4 +- clang/lib/Sema/SemaTemplate.cpp | 127 ++-- clang/lib/Sema/SemaTemplateInstantiate.cpp | 250 +++++++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 + clang/lib/Sema/SemaType.cpp | 6 + clang/lib/Sema/TreeTransform.h | 220 ++++++- clang/lib/Serialization/ASTCommon.cpp | 1 + clang/lib/Serialization/ASTReaderDecl.cpp | 7 + clang/lib/Serialization/ASTReaderStmt.cpp | 162 +++++- clang/lib/Serialization/ASTWriter.cpp | 1 + clang/lib/Serialization/ASTWriterDecl.cpp | 5 + clang/lib/Serialization/ASTWriterStmt.cpp | 105 +++- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 + .../expr.prim.req/compound-requirement.cpp | 175 ++++++ .../expr.prim/expr.prim.req/equivalence.cpp | 125 ++++ .../expr.prim.req/nested-requirement.cpp | 46 ++ .../CXX/expr/expr.prim/expr.prim.req/p3.cpp | 37 ++ .../expr.prim/expr.prim.req/requires-expr.cpp | 68 +++ .../expr.prim.req/simple-requirement.cpp | 106 ++++ .../expr.prim.req/type-requirement.cpp | 194 +++++++ clang/test/PCH/cxx2a-requires-expr.cpp | 20 + .../Parser/cxx2a-concepts-requires-expr.cpp | 141 +++++ .../instantiate-requires-expr.cpp | 216 +++++++ clang/tools/libclang/CIndex.cpp | 1 + clang/tools/libclang/CXCursor.cpp | 1 + 66 files changed, 3811 insertions(+), 302 deletions(-) create mode 100644 clang/include/clang/AST/ExprConcepts.h create mode 100644 clang/lib/AST/ExprConcepts.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp create mode 100644 clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp create mode 100644 clang/test/PCH/cxx2a-requires-expr.cpp create mode 100644 clang/test/Parser/cxx2a-concepts-requires-expr.cpp create mode 100644 clang/test/SemaTemplate/instantiate-requires-expr.cpp diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 896d857d8c96..84a611c14e0b 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -22,6 +22,7 @@ #include namespace clang { class ConceptDecl; +class ConceptSpecializationExpr; /// \brief The result of a constraint satisfaction check, containing the /// necessary information to diagnose an unsatisfied constraint. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index b716ea453a5a..2e8e31dbf4c7 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1893,6 +1893,37 @@ class CXXDeductionGuideDecl : public FunctionDecl { static bool classofKind(Kind K) { return K == CXXDeductionGuide; } }; +/// \brief Represents the body of a requires-expression. +/// +/// This decl exists merely to serve as the DeclContext for the local +/// parameters of the requires expression as well as other declarations inside +/// it. +/// +/// \code +/// template requires requires (T t) { {t++} -> regular; } +/// \endcode +/// +/// In this example, a RequiresExpr object will be generated for the expression, +/// and a RequiresExprBodyDecl will be created to hold the parameter t and the +/// template argument list imposed by the compound requirement. +class RequiresExprBodyDecl : public Decl, public DeclContext { + RequiresExprBodyDecl(ASTContext &C, DeclContext *DC, SourceLocation StartLoc) + : Decl(RequiresExprBody, DC, StartLoc), DeclContext(RequiresExprBody) {} + +public: + friend class ASTDeclReader; + friend class ASTDeclWriter; + + static RequiresExprBodyDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation StartLoc); + + static RequiresExprBodyDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == RequiresExprBody; } +}; + /// Represents a static or instance method of a struct/union/class. /// /// In the terminology of the C++ Standard, these are the (static and diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index 2c29409e0ca5..cea360d12e91 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -14,7 +14,6 @@ #ifndef LLVM_CLANG_AST_EXPRCXX_H #define LLVM_CLANG_AST_EXPRCXX_H -#include "clang/AST/ASTConcept.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -4836,99 +4835,6 @@ class BuiltinBitCastExpr final } }; -/// \brief Represents the specialization of a concept - evaluates to a prvalue -/// of type bool. -/// -/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the -/// specialization of a concept results in a prvalue of type bool. -class ConceptSpecializationExpr final : public Expr, public ConceptReference, - private llvm::TrailingObjects { - friend class ASTStmtReader; - friend TrailingObjects; -public: - using SubstitutionDiagnostic = std::pair; - -protected: - /// \brief The number of template arguments in the tail-allocated list of - /// converted template arguments. - unsigned NumTemplateArgs; - - /// \brief Information about the satisfaction of the named concept with the - /// given arguments. If this expression is value dependent, this is to be - /// ignored. - ASTConstraintSatisfaction *Satisfaction; - - ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, - const ConstraintSatisfaction *Satisfaction); - - ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs); - -public: - - static ConceptSpecializationExpr * - Create(const ASTContext &C, NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, - const ConstraintSatisfaction *Satisfaction); - - static ConceptSpecializationExpr * - Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs); - - ArrayRef getTemplateArguments() const { - return ArrayRef(getTrailingObjects(), - NumTemplateArgs); - } - - /// \brief Set new template arguments for this concept specialization. - void setTemplateArguments(ArrayRef Converted); - - /// \brief Whether or not the concept with the given arguments was satisfied - /// when the expression was created. - /// The expression must not be dependent. - bool isSatisfied() const { - assert(!isValueDependent() - && "isSatisfied called on a dependent ConceptSpecializationExpr"); - return Satisfaction->IsSatisfied; - } - - /// \brief Get elaborated satisfaction info about the template arguments' - /// satisfaction of the named concept. - /// The expression must not be dependent. - const ASTConstraintSatisfaction &getSatisfaction() const { - assert(!isValueDependent() - && "getSatisfaction called on dependent ConceptSpecializationExpr"); - return *Satisfaction; - } - - static bool classof(const Stmt *T) { - return T->getStmtClass() == ConceptSpecializationExprClass; - } - - SourceLocation getBeginLoc() const LLVM_READONLY { - return ConceptName.getBeginLoc(); - } - - SourceLocation getEndLoc() const LLVM_READONLY { - return ArgsAsWritten->RAngleLoc; - } - - // Iterators - child_range children() { - return child_range(child_iterator(), child_iterator()); - } - const_child_range children() const { - return const_child_range(const_child_iterator(), const_child_iterator()); - } -}; - } // namespace clang #endif // LLVM_CLANG_AST_EXPRCXX_H diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h new file mode 100644 index 000000000000..2a64326e8604 --- /dev/null +++ b/clang/include/clang/AST/ExprConcepts.h @@ -0,0 +1,540 @@ +//===- ExprConcepts.h - C++2a Concepts expressions --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines Expressions and AST nodes for C++2a concepts. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_EXPRCONCEPTS_H +#define LLVM_CLANG_AST_EXPRCONCEPTS_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConcept.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/TrailingObjects.h" +#include +#include + +namespace clang { +class ASTStmtReader; +class ASTStmtWriter; + +/// \brief Represents the specialization of a concept - evaluates to a prvalue +/// of type bool. +/// +/// According to C++2a [expr.prim.id]p3 an id-expression that denotes the +/// specialization of a concept results in a prvalue of type bool. +class ConceptSpecializationExpr final : public Expr, public ConceptReference, + private llvm::TrailingObjects { + friend class ASTStmtReader; + friend TrailingObjects; +public: + using SubstitutionDiagnostic = std::pair; + +protected: + /// \brief The number of template arguments in the tail-allocated list of + /// converted template arguments. + unsigned NumTemplateArgs; + + /// \brief Information about the satisfaction of the named concept with the + /// given arguments. If this expression is value dependent, this is to be + /// ignored. + ASTConstraintSatisfaction *Satisfaction; + + ConceptSpecializationExpr(const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + const ConstraintSatisfaction *Satisfaction); + + ConceptSpecializationExpr(EmptyShell Empty, unsigned NumTemplateArgs); + +public: + + static ConceptSpecializationExpr * + Create(const ASTContext &C, NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + const ConstraintSatisfaction *Satisfaction); + + static ConceptSpecializationExpr * + Create(ASTContext &C, EmptyShell Empty, unsigned NumTemplateArgs); + + ArrayRef getTemplateArguments() const { + return ArrayRef(getTrailingObjects(), + NumTemplateArgs); + } + + /// \brief Set new template arguments for this concept specialization. + void setTemplateArguments(ArrayRef Converted); + + /// \brief Whether or not the concept with the given arguments was satisfied + /// when the expression was created. + /// The expression must not be dependent. + bool isSatisfied() const { + assert(!isValueDependent() + && "isSatisfied called on a dependent ConceptSpecializationExpr"); + return Satisfaction->IsSatisfied; + } + + /// \brief Get elaborated satisfaction info about the template arguments' + /// satisfaction of the named concept. + /// The expression must not be dependent. + const ASTConstraintSatisfaction &getSatisfaction() const { + assert(!isValueDependent() + && "getSatisfaction called on dependent ConceptSpecializationExpr"); + return *Satisfaction; + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ConceptSpecializationExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return ConceptName.getBeginLoc(); + } + + SourceLocation getEndLoc() const LLVM_READONLY { + return ArgsAsWritten->RAngleLoc; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + +namespace concepts { + +/// \brief A static requirement that can be used in a requires-expression to +/// check properties of types and expression. +class Requirement { +public: + // Note - simple and compound requirements are both represented by the same + // class (ExprRequirement). + enum RequirementKind { RK_Type, RK_Simple, RK_Compound, RK_Nested }; +private: + const RequirementKind Kind; + bool Dependent : 1; + bool ContainsUnexpandedParameterPack : 1; + bool Satisfied : 1; +public: + struct SubstitutionDiagnostic { + StringRef SubstitutedEntity; + // FIXME: Store diagnostics semantically and not as prerendered strings. + // Fixing this probably requires serialization of PartialDiagnostic + // objects. + SourceLocation DiagLoc; + StringRef DiagMessage; + }; + + Requirement(RequirementKind Kind, bool IsDependent, + bool ContainsUnexpandedParameterPack, bool IsSatisfied = true) : + Kind(Kind), Dependent(IsDependent), + ContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack), + Satisfied(IsSatisfied) {} + + RequirementKind getKind() const { return Kind; } + + bool isSatisfied() const { + assert(!Dependent && + "isSatisfied can only be called on non-dependent requirements."); + return Satisfied; + } + + void setSatisfied(bool IsSatisfied) { + assert(!Dependent && + "setSatisfied can only be called on non-dependent requirements."); + Satisfied = IsSatisfied; + } + + void setDependent(bool IsDependent) { Dependent = IsDependent; } + bool isDependent() const { return Dependent; } + + void setContainsUnexpandedParameterPack(bool Contains) { + ContainsUnexpandedParameterPack = Contains; + } + bool containsUnexpandedParameterPack() const { + return ContainsUnexpandedParameterPack; + } +}; + +/// \brief A requires-expression requirement which queries the existence of a +/// type name or type template specialization ('type' requirements). +class TypeRequirement : public Requirement { +public: + enum SatisfactionStatus { + SS_Dependent, + SS_SubstitutionFailure, + SS_Satisfied + }; +private: + llvm::PointerUnion Value; + SatisfactionStatus Status; +public: + friend ASTStmtReader; + friend ASTStmtWriter; + + /// \brief Construct a type requirement from a type. If the given type is not + /// dependent, this indicates that the type exists and the requirement will be + /// satisfied. Otherwise, the SubstitutionDiagnostic constructor is to be + /// used. + TypeRequirement(TypeSourceInfo *T); + + /// \brief Construct a type requirement when the nested name specifier is + /// invalid due to a bad substitution. The requirement is unsatisfied. + TypeRequirement(SubstitutionDiagnostic *Diagnostic) : + Requirement(RK_Type, false, false, false), Value(Diagnostic), + Status(SS_SubstitutionFailure) {} + + SatisfactionStatus getSatisfactionStatus() const { return Status; } + void setSatisfactionStatus(SatisfactionStatus Status) { + this->Status = Status; + } + + bool isSubstitutionFailure() const { + return Status == SS_SubstitutionFailure; + } + + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(Status == SS_SubstitutionFailure && + "Attempted to get substitution diagnostic when there has been no " + "substitution failure."); + return Value.get(); + } + + TypeSourceInfo *getType() const { + assert(!isSubstitutionFailure() && + "Attempted to get type when there has been a substitution failure."); + return Value.get(); + } + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Type; + } +}; + +/// \brief A requires-expression requirement which queries the validity and +/// properties of an expression ('simple' and 'compound' requirements). +class ExprRequirement : public Requirement { +public: + enum SatisfactionStatus { + SS_Dependent, + SS_ExprSubstitutionFailure, + SS_NoexceptNotMet, + SS_TypeRequirementSubstitutionFailure, + SS_ConstraintsNotSatisfied, + SS_Satisfied + }; + class ReturnTypeRequirement { + llvm::PointerIntPair< + llvm::PointerUnion, + 1, bool> + TypeConstraintInfo; + public: + friend ASTStmtReader; + friend ASTStmtWriter; + + /// \brief No return type requirement was specified. + ReturnTypeRequirement() : TypeConstraintInfo(nullptr, 0) {} + + /// \brief A return type requirement was specified but it was a + /// substitution failure. + ReturnTypeRequirement(SubstitutionDiagnostic *SubstDiag) : + TypeConstraintInfo(SubstDiag, 0) {} + + /// \brief A 'type constraint' style return type requirement. + /// \param TPL an invented template parameter list containing a single + /// type parameter with a type-constraint. + // TODO: Can we maybe not save the whole template parameter list and just + // the type constraint? Saving the whole TPL makes it easier to handle in + // serialization but is less elegant. + ReturnTypeRequirement(TemplateParameterList *TPL); + + bool isDependent() const { + return TypeConstraintInfo.getInt(); + } + + bool containsUnexpandedParameterPack() const { + if (!isTypeConstraint()) + return false; + return getTypeConstraintTemplateParameterList() + ->containsUnexpandedParameterPack(); + } + + bool isEmpty() const { + return TypeConstraintInfo.getPointer().isNull(); + } + + bool isSubstitutionFailure() const { + return !isEmpty() && + TypeConstraintInfo.getPointer().is(); + } + + bool isTypeConstraint() const { + return !isEmpty() && + TypeConstraintInfo.getPointer().is(); + } + + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(isSubstitutionFailure()); + return TypeConstraintInfo.getPointer().get(); + } + + const TypeConstraint *getTypeConstraint() const; + + TemplateParameterList *getTypeConstraintTemplateParameterList() const { + assert(isTypeConstraint()); + return TypeConstraintInfo.getPointer().get(); + } + }; +private: + llvm::PointerUnion Value; + SourceLocation NoexceptLoc; // May be empty if noexcept wasn't specified. + ReturnTypeRequirement TypeReq; + ConceptSpecializationExpr *SubstitutedConstraintExpr; + SatisfactionStatus Status; +public: + friend ASTStmtReader; + friend ASTStmtWriter; + + /// \brief Construct a compound requirement. + /// \param E the expression which is checked by this requirement. + /// \param IsSimple whether this was a simple requirement in source. + /// \param NoexceptLoc the location of the noexcept keyword, if it was + /// specified, otherwise an empty location. + /// \param Req the requirement for the type of the checked expression. + /// \param Status the satisfaction status of this requirement. + ExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, SatisfactionStatus Status, + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr); + + /// \brief Construct a compound requirement whose expression was a + /// substitution failure. The requirement is not satisfied. + /// \param E the diagnostic emitted while instantiating the original + /// expression. + /// \param IsSimple whether this was a simple requirement in source. + /// \param NoexceptLoc the location of the noexcept keyword, if it was + /// specified, otherwise an empty location. + /// \param Req the requirement for the type of the checked expression (omit + /// if no requirement was specified). + ExprRequirement(SubstitutionDiagnostic *E, bool IsSimple, + SourceLocation NoexceptLoc, ReturnTypeRequirement Req = {}); + + bool isSimple() const { return getKind() == RK_Simple; } + bool isCompound() const { return getKind() == RK_Compound; } + + bool hasNoexceptRequirement() const { return NoexceptLoc.isValid(); } + SourceLocation getNoexceptLoc() const { return NoexceptLoc; } + + SatisfactionStatus getSatisfactionStatus() const { return Status; } + + bool isExprSubstitutionFailure() const { + return Status == SS_ExprSubstitutionFailure; + } + + const ReturnTypeRequirement &getReturnTypeRequirement() const { + return TypeReq; + } + + ConceptSpecializationExpr * + getReturnTypeRequirementSubstitutedConstraintExpr() const { + assert(Status >= SS_TypeRequirementSubstitutionFailure); + return SubstitutedConstraintExpr; + } + + SubstitutionDiagnostic *getExprSubstitutionDiagnostic() const { + assert(isExprSubstitutionFailure() && + "Attempted to get expression substitution diagnostic when there has " + "been no expression substitution failure"); + return Value.get(); + } + + Expr *getExpr() const { + assert(!isExprSubstitutionFailure() && + "ExprRequirement has no expression because there has been a " + "substitution failure."); + return Value.get(); + } + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Compound || R->getKind() == RK_Simple; + } +}; + +/// \brief A requires-expression requirement which is satisfied when a general +/// constraint expression is satisfied ('nested' requirements). +class NestedRequirement : public Requirement { + llvm::PointerUnion Value; + const ASTConstraintSatisfaction *Satisfaction = nullptr; + +public: + friend ASTStmtReader; + friend ASTStmtWriter; + + NestedRequirement(SubstitutionDiagnostic *SubstDiag) : + Requirement(RK_Nested, /*Dependent=*/false, + /*ContainsUnexpandedParameterPack*/false, + /*Satisfied=*/false), Value(SubstDiag) {} + + NestedRequirement(Expr *Constraint) : + Requirement(RK_Nested, /*Dependent=*/true, + Constraint->containsUnexpandedParameterPack()), + Value(Constraint) { + assert(Constraint->isInstantiationDependent() && + "Nested requirement with non-dependent constraint must be " + "constructed with a ConstraintSatisfaction object"); + } + + NestedRequirement(ASTContext &C, Expr *Constraint, + const ConstraintSatisfaction &Satisfaction) : + Requirement(RK_Nested, Constraint->isInstantiationDependent(), + Constraint->containsUnexpandedParameterPack(), + Satisfaction.IsSatisfied), + Value(Constraint), + Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {} + + bool isSubstitutionFailure() const { + return Value.is(); + } + + SubstitutionDiagnostic *getSubstitutionDiagnostic() const { + assert(isSubstitutionFailure() && + "getSubstitutionDiagnostic() may not be called when there was no " + "substitution failure."); + return Value.get(); + } + + Expr *getConstraintExpr() const { + assert(!isSubstitutionFailure() && "getConstraintExpr() may not be called " + "on nested requirements with " + "substitution failures."); + return Value.get(); + } + + const ASTConstraintSatisfaction &getConstraintSatisfaction() const { + assert(!isSubstitutionFailure() && "getConstraintSatisfaction() may not be " + "called on nested requirements with " + "substitution failures."); + return *Satisfaction; + } + + static bool classof(const Requirement *R) { + return R->getKind() == RK_Nested; + } +}; + +} // namespace concepts + +/// C++2a [expr.prim.req]: +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// [...] +/// A requires-expression is a prvalue of type bool [...] +class RequiresExpr final : public Expr, + llvm::TrailingObjects { + friend TrailingObjects; + friend class ASTStmtReader; + + unsigned NumLocalParameters; + unsigned NumRequirements; + RequiresExprBodyDecl *Body; + SourceLocation RBraceLoc; + + unsigned numTrailingObjects(OverloadToken) const { + return NumLocalParameters; + } + + unsigned numTrailingObjects(OverloadToken) const { + return NumRequirements; + } + + RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc); + RequiresExpr(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, + unsigned NumRequirements); + +public: + static RequiresExpr * + Create(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc); + static RequiresExpr * + Create(ASTContext &C, EmptyShell Empty, unsigned NumLocalParameters, + unsigned NumRequirements); + + ArrayRef getLocalParameters() const { + return {getTrailingObjects(), NumLocalParameters}; + } + + RequiresExprBodyDecl *getBody() const { return Body; } + + ArrayRef getRequirements() const { + return {getTrailingObjects(), NumRequirements}; + } + + /// \brief Whether or not the requires clause is satisfied. + /// The expression must not be dependent. + bool isSatisfied() const { + assert(!isValueDependent() + && "isSatisfied called on a dependent RequiresExpr"); + return RequiresExprBits.IsSatisfied; + } + + SourceLocation getRequiresKWLoc() const { + return RequiresExprBits.RequiresKWLoc; + } + + SourceLocation getRBraceLoc() const { return RBraceLoc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == RequiresExprClass; + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return RequiresExprBits.RequiresKWLoc; + } + SourceLocation getEndLoc() const LLVM_READONLY { + return RBraceLoc; + } + + // Iterators + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } +}; + +} // namespace clang + +#endif // LLVM_CLANG_AST_EXPRCONCEPTS_H \ No newline at end of file diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index f8ab8e451d8c..4633122aba48 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -23,6 +23,7 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -2138,6 +2139,8 @@ DEF_TRAVERSE_DECL(ParmVarDecl, { TRY_TO(TraverseStmt(D->getDefaultArg())); }) +DEF_TRAVERSE_DECL(RequiresExprBodyDecl, {}) + #undef DEF_TRAVERSE_DECL // ----------------- Stmt traversal ----------------- @@ -2709,6 +2712,28 @@ DEF_TRAVERSE_STMT(ConceptSpecializationExpr, { TRY_TO(TraverseConceptReference(*S)); }) +DEF_TRAVERSE_STMT(RequiresExpr, { + TRY_TO(TraverseDecl(S->getBody())); + for (ParmVarDecl *Parm : S->getLocalParameters()) + TRY_TO(TraverseDecl(Parm)); + for (concepts::Requirement *Req : S->getRequirements()) + if (auto *TypeReq = dyn_cast(Req)) { + if (!TypeReq->isSubstitutionFailure()) + TRY_TO(TraverseTypeLoc(TypeReq->getType()->getTypeLoc())); + } else if (auto *ExprReq = dyn_cast(Req)) { + if (!ExprReq->isExprSubstitutionFailure()) + TRY_TO(TraverseStmt(ExprReq->getExpr())); + auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isTypeConstraint()) + TRY_TO(TraverseTemplateParameterListHelper( + RetReq.getTypeConstraintTemplateParameterList())); + } else { + auto *NestedReq = cast(Req); + if (!NestedReq->isSubstitutionFailure()) + TRY_TO(TraverseStmt(NestedReq->getConstraintExpr())); + } +}) + // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, {}) DEF_TRAVERSE_STMT(FixedPointLiteral, {}) diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index eaacb1a5b252..253b76941991 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -910,6 +910,17 @@ class alignas(void *) Stmt { SourceLocation NameLoc; }; + class RequiresExprBitfields { + friend class ASTStmtReader; + friend class ASTStmtWriter; + friend class RequiresExpr; + + unsigned : NumExprBits; + + unsigned IsSatisfied : 1; + SourceLocation RequiresKWLoc; + }; + //===--- C++ Coroutines TS bitfields classes ---===// class CoawaitExprBitfields { @@ -1008,6 +1019,7 @@ class alignas(void *) Stmt { UnresolvedMemberExprBitfields UnresolvedMemberExprBits; CXXNoexceptExprBitfields CXXNoexceptExprBits; SubstNonTypeTemplateParmExprBitfields SubstNonTypeTemplateParmExprBits; + RequiresExprBitfields RequiresExprBits; // C++ Coroutines TS expressions CoawaitExprBitfields CoawaitBits; diff --git a/clang/include/clang/AST/StmtVisitor.h b/clang/include/clang/AST/StmtVisitor.h index d3be93d228cc..3e5155199eac 100644 --- a/clang/include/clang/AST/StmtVisitor.h +++ b/clang/include/clang/AST/StmtVisitor.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_STMTVISITOR_H #define LLVM_CLANG_AST_STMTVISITOR_H +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index c2c23237285b..d5bbc604819f 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -100,5 +100,6 @@ def OMPThreadPrivate : DeclNode; def OMPAllocate : DeclNode; def OMPRequires : DeclNode; def Empty : DeclNode; +def RequiresExprBody : DeclNode, DeclContext; def LifetimeExtendedTemporary : DeclNode; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 41f788e7d9bd..105ed7bf6c84 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -744,6 +744,33 @@ def err_friend_explicit_instantiation : Error< def err_explicit_instantiation_enum : Error< "enumerations cannot be explicitly instantiated">; def err_expected_template_parameter : Error<"expected template parameter">; +def note_ill_formed_requires_expression_outside_template : Note< + "requires expression outside a template declaration may not contain invalid " + "types or expressions">; +def err_empty_requires_expr : Error< + "a requires expression must contain at least one requirement">; +def err_requires_expr_parameter_list_ellipsis : Error< + "varargs not allowed in requires expression">; +def err_requires_expr_type_req_illegal_identifier : Error< + "expected identifier or template-id in type requirement">; +def err_requires_expr_type_req_template_args_on_non_template : Error< + "template arguments provided for non-template '%0'">; +def err_expected_semi_requirement : Error< + "expected ';' at end of requirement">; +def err_requires_expr_missing_arrow : Error< + "expected '->' before expression type requirement">; +def err_requires_expr_expected_type_constraint : Error< + "expected concept name with optional arguments">; +def err_requires_expr_simple_requirement_noexcept : Error< + "'noexcept' can only be used in a compound requirement (with '{' '}' around " + "the expression)">; +def err_requires_expr_simple_requirement_unexpected_tok : Error< + "unexpected %0 after expression; did you intend to use a compound " + "requirement (with '{' '}' around the expression)?">; +def warn_requires_expr_in_simple_requirement : Warning< + "this requires expression will only be checked for syntactic validity; did " + "you intend to place it in a nested requirement? (add another 'requires' " + "before the expression)">, InGroup>; def err_missing_dependent_template_keyword : Error< "use 'template' keyword to treat '%0' as a dependent template name">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7d8231d140e4..82649ddf8ab4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2102,12 +2102,21 @@ def err_auto_not_allowed : Error< "|in template argument|in typedef|in type alias|in function return type" "|in conversion function type|here|in lambda parameter" "|in type allocated by 'new'|in K&R-style function parameter" - "|in template parameter|in friend declaration}1">; + "|in template parameter|in friend declaration" + "|in requires expression parameter}1">; +def err_auto_not_allowed_in_return_type_requirement : Error< + "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression " + "type requirement">; def err_dependent_deduced_tst : Error< "typename specifier refers to " "%select{class template|function template|variable template|alias template|" "template template parameter|template}0 member in %1; " "argument deduction not allowed here">; +def err_deduced_tst : Error< + "typename specifier refers to " + "%select{class template|function template|variable template|alias template|" + "template template parameter|template}0; argument deduction not allowed " + "here">; def err_auto_not_allowed_var_inst : Error< "'auto' variable template instantiation is not allowed">; def err_auto_var_requires_init : Error< @@ -2590,19 +2599,56 @@ def note_constraints_not_satisfied : Note< def note_substituted_constraint_expr_is_ill_formed : Note< "because substituted constraint expression is ill-formed%0">; def note_atomic_constraint_evaluated_to_false : Note< - "%select{and |because }0'%1' evaluated to false">; + "%select{and|because}0 '%1' evaluated to false">; def note_concept_specialization_constraint_evaluated_to_false : Note< - "%select{and |because }0'%1' evaluated to false">; + "%select{and|because}0 '%1' evaluated to false">; def note_single_arg_concept_specialization_constraint_evaluated_to_false : Note< - "%select{and |because }0%1 does not satisfy %2">; + "%select{and|because}0 %1 does not satisfy %2">; def note_atomic_constraint_evaluated_to_false_elaborated : Note< - "%select{and |because }0'%1' (%2 %3 %4) evaluated to false">; + "%select{and|because}0 '%1' (%2 %3 %4) evaluated to false">; def err_constrained_virtual_method : Error< "virtual function cannot have a requires clause">; def err_trailing_requires_clause_on_deduction_guide : Error< "deduction guide cannot have a requires clause">; def err_reference_to_function_with_unsatisfied_constraints : Error< "invalid reference to function %0: constraints not satisfied">; +def note_requires_expr_ill_formed_expr : Note< + "expression is invalid: %0">; +def note_requires_expr_no_implicit_conversion : Note< + "no implicit conversion exists between expression type %0 and expected type " + "%1">; +def err_requires_expr_local_parameter_default_argument : Error< + "default arguments not allowed for parameters of a requires expression">; +def err_requires_expr_parameter_referenced_in_evaluated_context : Error< + "constraint variable %0 cannot be used in an evaluated context">; +def note_expr_requirement_expr_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_expr_requirement_expr_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def note_expr_requirement_noexcept_not_met : Note< + "%select{and|because}0 '%1' may throw an exception">; +def note_expr_requirement_type_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_expr_requirement_type_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def note_expr_requirement_constraints_not_satisfied : Note< + "%select{and|because}0 type constraint '%1' was not satisfied:">; +def note_expr_requirement_constraints_not_satisfied_simple : Note< + "%select{and|because}0 %1 does not satisfy %2:">; +def note_type_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_type_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; +def err_type_requirement_non_type_template : Error< + "'%0' refers to a %select{class template|function template|" + "variable template|alias template|template template parameter|template}1, " + "not a type template">; +def err_type_requirement_no_such_type : Error< + "'%0' does not name a type">; +def note_nested_requirement_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid: %2">; +def note_nested_requirement_unknown_substitution_error : Note< + "%select{and|because}0 '%1' would be invalid">; def note_ambiguous_atomic_constraints : Note< "similar constraint expressions not considered equivalent; constraint " "expressions cannot be considered equivalent unless they originate from the " @@ -4588,6 +4634,8 @@ def note_template_type_alias_instantiation_here : Note< "in instantiation of template type alias %0 requested here">; def note_template_exception_spec_instantiation_here : Note< "in instantiation of exception specification for %0 requested here">; +def note_template_requirement_instantiation_here : Note< + "in instantiation of requirement here">; def warn_var_template_missing : Warning<"instantiation of variable %q0 " "required here, but no definition is available">, InGroup; @@ -4623,6 +4671,8 @@ def note_template_default_arg_checking : Note< "while checking a default template argument used here">; def note_concept_specialization_here : Note< "while checking the satisfaction of concept '%0' requested here">; +def note_nested_requirement_here : Note< + "while checking the satisfaction of nested requirement requested here">; def note_checking_constraints_for_template_id_here : Note< "while checking constraint satisfaction for template '%0' required here">; def note_checking_constraints_for_var_spec_id_here : Note< @@ -4756,8 +4806,12 @@ def err_typename_nested_not_found_requirement : Error< "declaration">; def err_typename_nested_not_type : Error< "typename specifier refers to non-type member %0 in %1">; -def note_typename_refers_here : Note< +def err_typename_not_type : Error< + "typename specifier refers to non-type %0">; +def note_typename_member_refers_here : Note< "referenced member %0 is declared here">; +def note_typename_refers_here : Note< + "referenced %0 is declared here">; def err_typename_missing : Error< "missing 'typename' prior to dependent type name '%0%1'">; def err_typename_missing_template : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 294993298a18..41923cddc493 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -164,6 +164,7 @@ def CoyieldExpr : StmtNode; // C++2a Concepts expressions def ConceptSpecializationExpr : StmtNode; +def RequiresExpr : StmtNode; // Obj-C Expressions. def ObjCStringLiteral : StmtNode; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 182024ea5108..41f46861d089 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1933,6 +1933,7 @@ class Parser : public CodeCompletionHandler { //===--------------------------------------------------------------------===// // C++ Concepts + ExprResult ParseRequiresExpression(); void ParseTrailingRequiresClause(Declarator &D); //===--------------------------------------------------------------------===// @@ -2771,7 +2772,7 @@ class Parser : public CodeCompletionHandler { Declarator &D, SmallVectorImpl &ParamInfo); void ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &attrs, SmallVectorImpl &ParamInfo, SourceLocation &EllipsisLoc); diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index aceec9cbe1c9..1222549161e4 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -1757,7 +1757,8 @@ enum class DeclaratorContext { TemplateArgContext, // Any template argument (in template argument list). TemplateTypeArgContext, // Template type argument (in default argument). AliasDeclContext, // C++11 alias-declaration. - AliasTemplateContext // C++11 alias-declaration template. + AliasTemplateContext, // C++11 alias-declaration template. + RequiresExprContext // C++2a requires-expression. }; @@ -1981,6 +1982,7 @@ class Declarator { case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::RequiresExprContext: return true; } llvm_unreachable("unknown context kind!"); @@ -2003,6 +2005,7 @@ class Declarator { case DeclaratorContext::TemplateParamContext: case DeclaratorContext::CXXCatchContext: case DeclaratorContext::ObjCCatchContext: + case DeclaratorContext::RequiresExprContext: return true; case DeclaratorContext::TypeNameContext: @@ -2039,6 +2042,7 @@ class Declarator { case DeclaratorContext::MemberContext: case DeclaratorContext::PrototypeContext: case DeclaratorContext::TemplateParamContext: + case DeclaratorContext::RequiresExprContext: // Maybe one day... return false; @@ -2116,6 +2120,7 @@ class Declarator { case DeclaratorContext::TemplateArgContext: case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: + case DeclaratorContext::RequiresExprContext: return false; } llvm_unreachable("unknown context kind!"); @@ -2337,6 +2342,7 @@ class Declarator { case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: + case DeclaratorContext::RequiresExprContext: return false; } llvm_unreachable("unknown context kind!"); @@ -2370,6 +2376,7 @@ class Declarator { case DeclaratorContext::TrailingReturnContext: case DeclaratorContext::TrailingReturnVarContext: case DeclaratorContext::TemplateTypeArgContext: + case DeclaratorContext::RequiresExprContext: return false; case DeclaratorContext::BlockContext: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4789fad89560..0f02c0f9d2b1 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -21,6 +21,7 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExternalASTSource.h" @@ -6282,13 +6283,17 @@ class Sema final { /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. + /// \param First whether this is the first time an unsatisfied constraint is + /// diagnosed for this error. void - DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction); + DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction, + bool First = true); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied. void - DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction& Satisfaction); + DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction, + bool First = true); /// \brief Emit diagnostics explaining why a constraint expression was deemed /// unsatisfied because it was ill-formed. @@ -7262,7 +7267,17 @@ class Sema final { SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo &II, - SourceLocation IILoc); + SourceLocation IILoc, + TypeSourceInfo **TSI, + bool DeducedTSTContext); + + QualType CheckTypenameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo &II, + SourceLocation IILoc, + bool DeducedTSTContext = true); + TypeSourceInfo *RebuildTypeInCurrentInstantiation(TypeSourceInfo *T, SourceLocation Loc, @@ -7282,11 +7297,52 @@ class Sema final { const TemplateArgument *Args, unsigned NumArgs); - // Concepts + //===--------------------------------------------------------------------===// + // C++ Concepts + //===--------------------------------------------------------------------===// Decl *ActOnConceptDefinition( Scope *S, MultiTemplateParamsArg TemplateParameterLists, IdentifierInfo *Name, SourceLocation NameLoc, Expr *ConstraintExpr); + RequiresExprBodyDecl * + ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope); + void ActOnFinishRequiresExpr(); + concepts::Requirement *ActOnSimpleRequirement(Expr *E); + concepts::Requirement *ActOnTypeRequirement( + SourceLocation TypenameKWLoc, CXXScopeSpec &SS, SourceLocation NameLoc, + IdentifierInfo *TypeName, TemplateIdAnnotation *TemplateId); + concepts::Requirement *ActOnCompoundRequirement(Expr *E, + SourceLocation NoexceptLoc); + concepts::Requirement * + ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth); + concepts::Requirement *ActOnNestedRequirement(Expr *Constraint); + concepts::ExprRequirement * + BuildExprRequirement( + Expr *E, bool IsSatisfied, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); + concepts::ExprRequirement * + BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstDiag, + bool IsSatisfied, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement); + concepts::TypeRequirement *BuildTypeRequirement(TypeSourceInfo *Type); + concepts::TypeRequirement * + BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag); + concepts::NestedRequirement *BuildNestedRequirement(Expr *E); + concepts::NestedRequirement * + BuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag); + ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc); + //===--------------------------------------------------------------------===// // C++ Variadic Templates (C++0x [temp.variadic]) //===--------------------------------------------------------------------===// @@ -7933,6 +7989,13 @@ class Sema final { /// template which was deferred until it was needed. ExceptionSpecInstantiation, + /// We are instantiating a requirement of a requires expression. + RequirementInstantiation, + + /// We are checking the satisfaction of a nested requirement of a requires + /// expression. + NestedRequirementConstraintsCheck, + /// We are declaring an implicit special member function. DeclaringSpecialMember, @@ -8254,6 +8317,19 @@ class Sema final { ParameterMappingSubstitution, NamedDecl *Template, SourceRange InstantiationRange); + /// \brief Note that we are substituting template arguments into a part of + /// a requirement of a requires expression. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::Requirement *Req, + sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange = SourceRange()); + + /// \brief Note that we are checking the satisfaction of the constraint + /// expression inside of a nested requirement. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::NestedRequirement *Req, ConstraintsCheck, + SourceRange InstantiationRange = SourceRange()); + /// Note that we have finished instantiating this template. void Clear(); diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h index acd1e604211a..7fc42a4816ec 100644 --- a/clang/include/clang/Sema/SemaConcept.h +++ b/clang/include/clang/Sema/SemaConcept.h @@ -13,10 +13,17 @@ #ifndef LLVM_CLANG_SEMA_SEMACONCEPT_H #define LLVM_CLANG_SEMA_SEMACONCEPT_H +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Expr.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" +#include +#include + namespace clang { class Sema; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 1bfcbda8c9f1..44a12c875da7 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1403,6 +1403,9 @@ namespace serialization { /// An LifetimeExtendedTemporaryDecl record. DECL_LIFETIME_EXTENDED_TEMPORARY, + /// A RequiresExprBodyDecl record. + DECL_REQUIRES_EXPR_BODY, + /// An ObjCTypeParamDecl record. DECL_OBJC_TYPE_PARAM, @@ -1785,6 +1788,7 @@ namespace serialization { EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr EXPR_CXX_FOLD, // CXXFoldExpr EXPR_CONCEPT_SPECIALIZATION,// ConceptSpecializationExpr + EXPR_REQUIRES, // RequiresExpr // CUDA EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index bd9b0934591c..e79c245d2f8c 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -46,6 +46,7 @@ add_clang_library(clangAST DeclTemplate.cpp Expr.cpp ExprClassification.cpp + ExprConcepts.cpp ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 6ee767ccecf7..cb4d61cac2c7 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -804,6 +804,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case OMPCapturedExpr: case Empty: case LifetimeExtendedTemporary: + case RequiresExprBody: // Never looked up by name. return 0; } @@ -1177,6 +1178,7 @@ DeclContext *DeclContext::getPrimaryContext() { case Decl::Captured: case Decl::OMPDeclareReduction: case Decl::OMPDeclareMapper: + case Decl::RequiresExprBody: // There is only one DeclContext for these entities. return this; diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 2ead1e70ea0d..48e310e858b2 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1968,6 +1968,16 @@ CXXDeductionGuideDecl *CXXDeductionGuideDecl::CreateDeserialized(ASTContext &C, QualType(), nullptr, SourceLocation()); } +RequiresExprBodyDecl *RequiresExprBodyDecl::Create( + ASTContext &C, DeclContext *DC, SourceLocation StartLoc) { + return new (C, DC) RequiresExprBodyDecl(C, DC, StartLoc); +} + +RequiresExprBodyDecl *RequiresExprBodyDecl::CreateDeserialized(ASTContext &C, + unsigned ID) { + return new (C, ID) RequiresExprBodyDecl(C, nullptr, SourceLocation()); +} + void CXXMethodDecl::anchor() {} bool CXXMethodDecl::isStatic() const { diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 73ddbc62482d..835198958766 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3457,6 +3457,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case OpaqueValueExprClass: case SourceLocExprClass: case ConceptSpecializationExprClass: + case RequiresExprClass: // These never have a side-effect. return false; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 422227d787b1..e4bd218ae2d3 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Expr.h" #include "clang/AST/LambdaCapture.h" @@ -1764,81 +1765,3 @@ CUDAKernelCallExpr *CUDAKernelCallExpr::CreateEmpty(const ASTContext &Ctx, alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, Empty); } - -ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, - NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) - : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, - /*TypeDependent=*/false, - // All the flags below are set in setTemplateArguments. - /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, - /*ContainsUnexpandedParameterPacks=*/false), - ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten), - NumTemplateArgs(ConvertedArgs.size()), - Satisfaction(Satisfaction ? - ASTConstraintSatisfaction::Create(C, *Satisfaction) : - nullptr) { - setTemplateArguments(ConvertedArgs); -} - -ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, - unsigned NumTemplateArgs) - : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), - NumTemplateArgs(NumTemplateArgs) { } - -void ConceptSpecializationExpr::setTemplateArguments( - ArrayRef Converted) { - assert(Converted.size() == NumTemplateArgs); - std::uninitialized_copy(Converted.begin(), Converted.end(), - getTrailingObjects()); - bool IsInstantiationDependent = false; - bool ContainsUnexpandedParameterPack = false; - for (const TemplateArgument& Arg : Converted) { - if (Arg.isInstantiationDependent()) - IsInstantiationDependent = true; - if (Arg.containsUnexpandedParameterPack()) - ContainsUnexpandedParameterPack = true; - if (ContainsUnexpandedParameterPack && IsInstantiationDependent) - break; - } - - // Currently guaranteed by the fact concepts can only be at namespace-scope. - assert(!NestedNameSpec || - (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && - !NestedNameSpec.getNestedNameSpecifier() - ->containsUnexpandedParameterPack())); - setInstantiationDependent(IsInstantiationDependent); - setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); - assert((!isValueDependent() || isInstantiationDependent()) && - "should not be value-dependent"); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(const ASTContext &C, - NestedNameSpecifierLoc NNS, - SourceLocation TemplateKWLoc, - DeclarationNameInfo ConceptNameInfo, - NamedDecl *FoundDecl, - ConceptDecl *NamedConcept, - const ASTTemplateArgumentListInfo *ArgsAsWritten, - ArrayRef ConvertedArgs, - const ConstraintSatisfaction *Satisfaction) { - void *Buffer = C.Allocate(totalSizeToAlloc( - ConvertedArgs.size())); - return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, - ConceptNameInfo, FoundDecl, - NamedConcept, ArgsAsWritten, - ConvertedArgs, Satisfaction); -} - -ConceptSpecializationExpr * -ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, - unsigned NumTemplateArgs) { - void *Buffer = C.Allocate(totalSizeToAlloc( - NumTemplateArgs)); - return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); -} diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 9dbf6fe9e0f0..d201af31f521 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -193,6 +193,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::DesignatedInitUpdateExprClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: return Cl::CL_PRValue; case Expr::ConstantExprClass: diff --git a/clang/lib/AST/ExprConcepts.cpp b/clang/lib/AST/ExprConcepts.cpp new file mode 100644 index 000000000000..76d57ed5d5b1 --- /dev/null +++ b/clang/lib/AST/ExprConcepts.cpp @@ -0,0 +1,185 @@ +//===- ExprCXX.cpp - (C++) Expression AST Node Implementation -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the subclesses of Expr class declared in ExprCXX.h +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprConcepts.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConcept.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/NestedNameSpecifier.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/Support/TrailingObjects.h" +#include +#include +#include + +using namespace clang; + +ConceptSpecializationExpr::ConceptSpecializationExpr(const ASTContext &C, + NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) + : Expr(ConceptSpecializationExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TypeDependent=*/false, + // All the flags below are set in setTemplateArguments. + /*ValueDependent=*/!Satisfaction, /*InstantiationDependent=*/false, + /*ContainsUnexpandedParameterPacks=*/false), + ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten), + NumTemplateArgs(ConvertedArgs.size()), + Satisfaction(Satisfaction ? + ASTConstraintSatisfaction::Create(C, *Satisfaction) : + nullptr) { + setTemplateArguments(ConvertedArgs); +} + +ConceptSpecializationExpr::ConceptSpecializationExpr(EmptyShell Empty, + unsigned NumTemplateArgs) + : Expr(ConceptSpecializationExprClass, Empty), ConceptReference(), + NumTemplateArgs(NumTemplateArgs) { } + +void ConceptSpecializationExpr::setTemplateArguments( + ArrayRef Converted) { + assert(Converted.size() == NumTemplateArgs); + std::uninitialized_copy(Converted.begin(), Converted.end(), + getTrailingObjects()); + bool IsInstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; + for (const TemplateArgument& Arg : Converted) { + if (Arg.isInstantiationDependent()) + IsInstantiationDependent = true; + if (Arg.containsUnexpandedParameterPack()) + ContainsUnexpandedParameterPack = true; + if (ContainsUnexpandedParameterPack && IsInstantiationDependent) + break; + } + + // Currently guaranteed by the fact concepts can only be at namespace-scope. + assert(!NestedNameSpec || + (!NestedNameSpec.getNestedNameSpecifier()->isInstantiationDependent() && + !NestedNameSpec.getNestedNameSpecifier() + ->containsUnexpandedParameterPack())); + setInstantiationDependent(IsInstantiationDependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); + assert((!isValueDependent() || isInstantiationDependent()) && + "should not be value-dependent"); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(const ASTContext &C, + NestedNameSpecifierLoc NNS, + SourceLocation TemplateKWLoc, + DeclarationNameInfo ConceptNameInfo, + NamedDecl *FoundDecl, + ConceptDecl *NamedConcept, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + ArrayRef ConvertedArgs, + const ConstraintSatisfaction *Satisfaction) { + void *Buffer = C.Allocate(totalSizeToAlloc( + ConvertedArgs.size())); + return new (Buffer) ConceptSpecializationExpr(C, NNS, TemplateKWLoc, + ConceptNameInfo, FoundDecl, + NamedConcept, ArgsAsWritten, + ConvertedArgs, Satisfaction); +} + +ConceptSpecializationExpr * +ConceptSpecializationExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumTemplateArgs) { + void *Buffer = C.Allocate(totalSizeToAlloc( + NumTemplateArgs)); + return new (Buffer) ConceptSpecializationExpr(Empty, NumTemplateArgs); +} + +const TypeConstraint * +concepts::ExprRequirement::ReturnTypeRequirement::getTypeConstraint() const { + assert(isTypeConstraint()); + auto TPL = + TypeConstraintInfo.getPointer().get(); + return cast(TPL->getParam(0)) + ->getTypeConstraint(); +} + +RequiresExpr::RequiresExpr(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc) + : Expr(RequiresExprClass, C.BoolTy, VK_RValue, OK_Ordinary, + /*TD=*/false, /*VD=*/false, /*ID=*/false, + /*ContainsUnexpandedParameterPack=*/false), + NumLocalParameters(LocalParameters.size()), + NumRequirements(Requirements.size()), Body(Body), RBraceLoc(RBraceLoc) { + RequiresExprBits.IsSatisfied = false; + RequiresExprBits.RequiresKWLoc = RequiresKWLoc; + bool Dependent = false; + bool ContainsUnexpandedParameterPack = false; + for (ParmVarDecl *P : LocalParameters) { + Dependent |= P->getType()->isInstantiationDependentType(); + ContainsUnexpandedParameterPack |= + P->getType()->containsUnexpandedParameterPack(); + } + RequiresExprBits.IsSatisfied = true; + for (concepts::Requirement *R : Requirements) { + Dependent |= R->isDependent(); + ContainsUnexpandedParameterPack |= R->containsUnexpandedParameterPack(); + if (!Dependent) { + RequiresExprBits.IsSatisfied = R->isSatisfied(); + if (!RequiresExprBits.IsSatisfied) + break; + } + } + std::copy(LocalParameters.begin(), LocalParameters.end(), + getTrailingObjects()); + std::copy(Requirements.begin(), Requirements.end(), + getTrailingObjects()); + RequiresExprBits.IsSatisfied |= Dependent; + setValueDependent(Dependent); + setInstantiationDependent(Dependent); + setContainsUnexpandedParameterPack(ContainsUnexpandedParameterPack); +} + +RequiresExpr::RequiresExpr(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, + unsigned NumRequirements) + : Expr(RequiresExprClass, Empty), NumLocalParameters(NumLocalParameters), + NumRequirements(NumRequirements) { } + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation RBraceLoc) { + void *Mem = + C.Allocate(totalSizeToAlloc( + LocalParameters.size(), Requirements.size()), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, RequiresKWLoc, Body, LocalParameters, + Requirements, RBraceLoc); +} + +RequiresExpr * +RequiresExpr::Create(ASTContext &C, EmptyShell Empty, + unsigned NumLocalParameters, unsigned NumRequirements) { + void *Mem = + C.Allocate(totalSizeToAlloc( + NumLocalParameters, NumRequirements), + alignof(RequiresExpr)); + return new (Mem) RequiresExpr(C, Empty, NumLocalParameters, NumRequirements); +} diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index c4b27b5d1daa..c79973507323 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -9912,6 +9912,7 @@ class IntExprEvaluator bool VisitSizeOfPackExpr(const SizeOfPackExpr *E); bool VisitSourceLocExpr(const SourceLocExpr *E); bool VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E); + bool VisitRequiresExpr(const RequiresExpr *E); // FIXME: Missing: array subscript of vector, member of vector }; @@ -12524,6 +12525,9 @@ bool IntExprEvaluator::VisitConceptSpecializationExpr( return Success(E->isSatisfied(), E); } +bool IntExprEvaluator::VisitRequiresExpr(const RequiresExpr *E) { + return Success(E->isSatisfied(), E); +} bool FixedPointExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { @@ -14182,6 +14186,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::CXXScalarValueInitExprClass: case Expr::TypeTraitExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::CXXNoexceptExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 0d567edac521..5d485e000750 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" @@ -3668,6 +3669,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::ConvertVectorExprClass: case Expr::StmtExprClass: case Expr::TypeTraitExprClass: + case Expr::RequiresExprClass: case Expr::ArrayTypeTraitExprClass: case Expr::ExpressionTraitExprClass: case Expr::VAArgExprClass: diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index b6e4d8aff21e..7409ae7ddc9e 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -16,6 +16,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index c14bb886bb11..45fd8ceae8d3 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -2269,6 +2269,60 @@ void StmtPrinter::VisitConceptSpecializationExpr(ConceptSpecializationExpr *E) { Policy); } +void StmtPrinter::VisitRequiresExpr(RequiresExpr *E) { + OS << "requires "; + auto LocalParameters = E->getLocalParameters(); + if (!LocalParameters.empty()) { + OS << "("; + for (ParmVarDecl *LocalParam : LocalParameters) { + PrintRawDecl(LocalParam); + if (LocalParam != LocalParameters.back()) + OS << ", "; + } + + OS << ") "; + } + OS << "{ "; + auto Requirements = E->getRequirements(); + for (concepts::Requirement *Req : Requirements) { + if (auto *TypeReq = dyn_cast(Req)) { + if (TypeReq->isSubstitutionFailure()) + OS << "<>"; + else + TypeReq->getType()->getType().print(OS, Policy); + } else if (auto *ExprReq = dyn_cast(Req)) { + if (ExprReq->isCompound()) + OS << "{ "; + if (ExprReq->isExprSubstitutionFailure()) + OS << "<>"; + else + PrintExpr(ExprReq->getExpr()); + if (ExprReq->isCompound()) { + OS << " }"; + if (ExprReq->getNoexceptLoc().isValid()) + OS << " noexcept"; + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (!RetReq.isEmpty()) { + OS << " -> "; + if (RetReq.isSubstitutionFailure()) + OS << "<>"; + else if (RetReq.isTypeConstraint()) + RetReq.getTypeConstraint()->print(OS, Policy); + } + } + } else { + auto *NestedReq = cast(Req); + OS << "requires "; + if (NestedReq->isSubstitutionFailure()) + OS << "<>"; + else + PrintExpr(NestedReq->getConstraintExpr()); + } + OS << "; "; + } + OS << "}"; +} + // C++ Coroutines TS void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index c0b0f3b0b064..382ea5c8d7ef 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1340,6 +1340,49 @@ void StmtProfiler::VisitConceptSpecializationExpr( VisitTemplateArgument(Arg); } +void StmtProfiler::VisitRequiresExpr(const RequiresExpr *S) { + VisitExpr(S); + ID.AddInteger(S->getLocalParameters().size()); + for (ParmVarDecl *LocalParam : S->getLocalParameters()) + VisitDecl(LocalParam); + ID.AddInteger(S->getRequirements().size()); + for (concepts::Requirement *Req : S->getRequirements()) { + if (auto *TypeReq = dyn_cast(Req)) { + ID.AddInteger(concepts::Requirement::RK_Type); + ID.AddBoolean(TypeReq->isSubstitutionFailure()); + if (!TypeReq->isSubstitutionFailure()) + VisitType(TypeReq->getType()->getType()); + } else if (auto *ExprReq = dyn_cast(Req)) { + ID.AddInteger(concepts::Requirement::RK_Compound); + ID.AddBoolean(ExprReq->isExprSubstitutionFailure()); + if (!ExprReq->isExprSubstitutionFailure()) + Visit(ExprReq->getExpr()); + // C++2a [expr.prim.req.compound]p1 Example: + // [...] The compound-requirement in C1 requires that x++ is a valid + // expression. It is equivalent to the simple-requirement x++; [...] + // We therefore do not profile isSimple() here. + ID.AddBoolean(ExprReq->getNoexceptLoc().isValid()); + const concepts::ExprRequirement::ReturnTypeRequirement &RetReq = + ExprReq->getReturnTypeRequirement(); + if (RetReq.isEmpty()) { + ID.AddInteger(0); + } else if (RetReq.isTypeConstraint()) { + ID.AddInteger(1); + Visit(RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()); + } else { + assert(RetReq.isSubstitutionFailure()); + ID.AddInteger(2); + } + } else { + ID.AddInteger(concepts::Requirement::RK_Nested); + auto *NestedReq = cast(Req); + ID.AddBoolean(NestedReq->isSubstitutionFailure()); + if (!NestedReq->isSubstitutionFailure()) + Visit(NestedReq->getConstraintExpr()); + } + } +} + static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 5aac7a8d54c7..60f1dba7c768 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -111,6 +111,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Empty: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: // None of these decls require codegen support. return; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 3f23fe11e4f5..de5c3a03fb68 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -680,6 +680,10 @@ class ScalarExprEmitter return Builder.getInt1(E->isSatisfied()); } + Value *VisitRequiresExpr(const RequiresExpr *E) { + return Builder.getInt1(E->isSatisfied()); + } + Value *VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { return llvm::ConstantInt::get(Builder.getInt32Ty(), E->getValue()); } diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 8574d0a7e813..935c64a0fa13 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -429,6 +429,10 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback { return "ConstraintNormalization"; case CodeSynthesisContext::ParameterMappingSubstitution: return "ParameterMappingSubstitution"; + case CodeSynthesisContext::RequirementInstantiation: + return "RequirementInstantiation"; + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + return "NestedRequirementConstraintsCheck"; } return ""; } diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 2c7e3a56c043..42e6fd88515a 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -385,6 +385,9 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, else Builder.defineMacro("__cplusplus", "199711L"); + if (LangOpts.ConceptsTS) + Builder.defineMacro("__cpp_concepts", "201707L"); + // C++1z [cpp.predefined]p1: // An integer literal of type std::size_t whose value is the alignment // guaranteed by a call to operator new(std::size_t) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 192c0e99e5a5..178cb1b661c7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6366,7 +6366,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D, ProhibitAttributes(FnAttrs); } else { if (Tok.isNot(tok::r_paren)) - ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo, + ParseParameterDeclarationClause(D.getContext(), FirstArgAttrs, ParamInfo, EllipsisLoc); else if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); @@ -6584,9 +6584,9 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// after the opening parenthesis. This function will not parse a K&R-style /// identifier list. /// -/// D is the declarator being parsed. If FirstArgAttrs is non-null, then the -/// caller parsed those arguments immediately after the open paren - they should -/// be considered to be part of the first parameter. +/// DeclContext is the context of the declarator being parsed. If FirstArgAttrs +/// is non-null, then the caller parsed those attributes immediately after the +/// open paren - they should be considered to be part of the first parameter. /// /// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will /// be the location of the ellipsis, if any was parsed. @@ -6612,7 +6612,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - Declarator &D, + DeclaratorContext DeclaratorContext, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6661,9 +6661,11 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, D.getContext() == DeclaratorContext::LambdaExprContext - ? DeclaratorContext::LambdaExprParameterContext - : DeclaratorContext::PrototypeContext); + DS, DeclaratorContext == DeclaratorContext::RequiresExprContext + ? DeclaratorContext::RequiresExprContext + : DeclaratorContext == DeclaratorContext::LambdaExprContext + ? DeclaratorContext::LambdaExprParameterContext + : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); // Parse GNU attributes, if present. @@ -6717,7 +6719,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (D.getContext() == DeclaratorContext::MemberContext) { + if (DeclaratorContext == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 32dacbcc9646..df8388926db1 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -756,6 +756,7 @@ class CastExpressionIdValidator final : public CorrectionCandidateCallback { /// [C++11] user-defined-literal /// '(' expression ')' /// [C11] generic-selection +/// [C++2a] requires-expression /// '__func__' [C99 6.4.2.2] /// [GNU] '__FUNCTION__' /// [MS] '__FUNCDNAME__' @@ -1601,6 +1602,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, *NotPrimaryExpression = true; return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw_requires: // [C++2a] requires-expression + return ParseRequiresExpression(); + case tok::kw_noexcept: { // [C++0x] 'noexcept' '(' expression ')' if (NotPrimaryExpression) *NotPrimaryExpression = true; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 7d477d271dc2..036eabb94dd7 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" @@ -1299,9 +1301,9 @@ ExprResult Parser::ParseLambdaExpressionAfterIntroducer( Actions.RecordParsingTemplateParameterDepth( CurTemplateDepthTracker.getOriginalDepth()); - ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc); - - // For a generic lambda, each 'auto' within the parameter declaration + ParseParameterDeclarationClause(D.getContext(), Attr, ParamInfo, + EllipsisLoc); + // For a generic lambda, each 'auto' within the parameter declaration // clause creates a template type parameter, so increment the depth. // If we've parsed any explicit template parameters, then the depth will // have already been incremented. So we make sure that at most a single @@ -3255,6 +3257,324 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.get()); } +/// ParseRequiresExpression - Parse a C++2a requires-expression. +/// C++2a [expr.prim.req]p1 +/// A requires-expression provides a concise way to express requirements on +/// template arguments. A requirement is one that can be checked by name +/// lookup (6.4) or by checking properties of types and expressions. +/// +/// requires-expression: +/// 'requires' requirement-parameter-list[opt] requirement-body +/// +/// requirement-parameter-list: +/// '(' parameter-declaration-clause[opt] ')' +/// +/// requirement-body: +/// '{' requirement-seq '}' +/// +/// requirement-seq: +/// requirement +/// requirement-seq requirement +/// +/// requirement: +/// simple-requirement +/// type-requirement +/// compound-requirement +/// nested-requirement +ExprResult Parser::ParseRequiresExpression() { + assert(Tok.is(tok::kw_requires) && "Expected 'requires' keyword"); + SourceLocation RequiresKWLoc = ConsumeToken(); // Consume 'requires' + + llvm::SmallVector LocalParameterDecls; + if (Tok.is(tok::l_paren)) { + // requirement parameter list is present. + ParseScope LocalParametersScope(this, Scope::FunctionPrototypeScope | + Scope::DeclScope); + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + if (!Tok.is(tok::r_paren)) { + ParsedAttributes FirstArgAttrs(getAttrFactory()); + SourceLocation EllipsisLoc; + llvm::SmallVector LocalParameters; + DiagnosticErrorTrap Trap(Diags); + ParseParameterDeclarationClause(DeclaratorContext::RequiresExprContext, + FirstArgAttrs, LocalParameters, + EllipsisLoc); + if (EllipsisLoc.isValid()) + Diag(EllipsisLoc, diag::err_requires_expr_parameter_list_ellipsis); + for (auto &ParamInfo : LocalParameters) + LocalParameterDecls.push_back(cast(ParamInfo.Param)); + if (Trap.hasErrorOccurred()) + SkipUntil(tok::r_paren, StopBeforeMatch); + } + Parens.consumeClose(); + } + + BalancedDelimiterTracker Braces(*this, tok::l_brace); + if (Braces.expectAndConsume()) + return ExprError(); + + // Start of requirement list + llvm::SmallVector Requirements; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + Actions, Sema::ExpressionEvaluationContext::Unevaluated); + + ParseScope BodyScope(this, Scope::DeclScope); + RequiresExprBodyDecl *Body = Actions.ActOnStartRequiresExpr( + RequiresKWLoc, LocalParameterDecls, getCurScope()); + + if (Tok.is(tok::r_brace)) { + // Grammar does not allow an empty body. + // requirement-body: + // { requirement-seq } + // requirement-seq: + // requirement + // requirement-seq requirement + Diag(Tok, diag::err_empty_requires_expr); + // Continue anyway and produce a requires expr with no requirements. + } else { + while (!Tok.is(tok::r_brace)) { + switch (Tok.getKind()) { + case tok::l_brace: { + // Compound requirement + // C++ [expr.prim.req.compound] + // compound-requirement: + // '{' expression '}' 'noexcept'[opt] + // return-type-requirement[opt] ';' + // return-type-requirement: + // trailing-return-type + // '->' cv-qualifier-seq[opt] constrained-parameter + // cv-qualifier-seq[opt] abstract-declarator[opt] + BalancedDelimiterTracker ExprBraces(*this, tok::l_brace); + ExprBraces.consumeOpen(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + ExprBraces.skipToEnd(); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (ExprBraces.consumeClose()) + ExprBraces.skipToEnd(); + + concepts::Requirement *Req = nullptr; + SourceLocation NoexceptLoc; + TryConsumeToken(tok::kw_noexcept, NoexceptLoc); + if (Tok.is(tok::semi)) { + Req = Actions.ActOnCompoundRequirement(Expression.get(), NoexceptLoc); + if (Req) + Requirements.push_back(Req); + break; + } + if (!TryConsumeToken(tok::arrow)) + // User probably forgot the arrow, remind them and try to continue. + Diag(Tok, diag::err_requires_expr_missing_arrow) + << FixItHint::CreateInsertion(Tok.getLocation(), "->"); + // Try to parse a 'type-constraint' + CXXScopeSpec SS; + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), + /*EnteringContext=*/false, + /*MayBePseudoDestructor=*/nullptr, + // If this is not a type-constraint, + // then this scope-spec is part of + // the typename of a non-type + // template parameter + /*IsTypename=*/true, + /*LastII=*/nullptr, + // We won't find concepts in + // non-namespaces anyway, so might as + // well parse this correctly for + // possible type names. + /*OnlyNamespace=*/false, + /*SuppressDiagnostic=*/true)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (TryAnnotateTypeConstraint()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!isTypeConstraintAnnotation()) { + Diag(Tok, diag::err_requires_expr_expected_type_constraint); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (Tok.is(tok::annot_cxxscope)) + ConsumeAnnotationToken(); + + Req = Actions.ActOnCompoundRequirement( + Expression.get(), NoexceptLoc, SS, takeTemplateIdAnnotation(Tok), + TemplateParameterDepth); + ConsumeAnnotationToken(); + if (Req) + Requirements.push_back(Req); + break; + } + default: { + bool PossibleRequiresExprInSimpleRequirement = false; + if (Tok.is(tok::kw_requires)) { + auto IsNestedRequirement = [&] { + RevertingTentativeParsingAction TPA(*this); + ConsumeToken(); // 'requires' + if (Tok.is(tok::l_brace)) + // This is a requires expression + // requires (T t) { + // requires { t++; }; + // ... ^ + // } + return false; + if (Tok.is(tok::l_paren)) { + // This might be the parameter list of a requires expression + ConsumeParen(); + auto Res = TryParseParameterDeclarationClause(); + if (Res != TPResult::False) { + // Skip to the closing parenthesis + // FIXME: Don't traverse these tokens twice (here and in + // TryParseParameterDeclarationClause). + unsigned Depth = 1; + while (Depth != 0) { + if (Tok.is(tok::l_paren)) + Depth++; + else if (Tok.is(tok::r_paren)) + Depth--; + ConsumeAnyToken(); + } + // requires (T t) { + // requires () ? + // ... ^ + // - OR - + // requires (int x) ? + // ... ^ + // } + if (Tok.is(tok::l_brace)) + // requires (...) { + // ^ - a requires expression as a + // simple-requirement. + return false; + } + } + return true; + }; + if (IsNestedRequirement()) { + ConsumeToken(); + // Nested requirement + // C++ [expr.prim.req.nested] + // nested-requirement: + // 'requires' constraint-expression ';' + ExprResult ConstraintExpr = + Actions.CorrectDelayedTyposInExpr(ParseConstraintExpression()); + if (ConstraintExpr.isInvalid() || !ConstraintExpr.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + if (auto *Req = + Actions.ActOnNestedRequirement(ConstraintExpr.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, + SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } else + PossibleRequiresExprInSimpleRequirement = true; + } else if (Tok.is(tok::kw_typename)) { + // This might be 'typename T::value_type;' (a type requirement) or + // 'typename T::value_type{};' (a simple requirement). + TentativeParsingAction TPA(*this); + + // We need to consume the typename to allow 'requires { typename a; }' + SourceLocation TypenameKWLoc = ConsumeToken(); + if (TryAnnotateCXXScopeToken()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + CXXScopeSpec SS; + if (Tok.is(tok::annot_cxxscope)) { + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + ConsumeAnnotationToken(); + } + + if (Tok.isOneOf(tok::identifier, tok::annot_template_id) && + !NextToken().isOneOf(tok::l_brace, tok::l_paren)) { + TPA.Commit(); + SourceLocation NameLoc = Tok.getLocation(); + IdentifierInfo *II = nullptr; + TemplateIdAnnotation *TemplateId = nullptr; + if (Tok.is(tok::identifier)) { + II = Tok.getIdentifierInfo(); + ConsumeToken(); + } else { + TemplateId = takeTemplateIdAnnotation(Tok); + ConsumeAnnotationToken(); + } + + if (auto *Req = Actions.ActOnTypeRequirement(TypenameKWLoc, SS, + NameLoc, II, + TemplateId)) { + Requirements.push_back(Req); + } + break; + } + TPA.Revert(); + } + // Simple requirement + // C++ [expr.prim.req.simple] + // simple-requirement: + // expression ';' + SourceLocation StartLoc = Tok.getLocation(); + ExprResult Expression = + Actions.CorrectDelayedTyposInExpr(ParseExpression()); + if (!Expression.isUsable()) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + if (!Expression.isInvalid() && PossibleRequiresExprInSimpleRequirement) + Diag(StartLoc, diag::warn_requires_expr_in_simple_requirement) + << FixItHint::CreateInsertion(StartLoc, "requires"); + if (auto *Req = Actions.ActOnSimpleRequirement(Expression.get())) + Requirements.push_back(Req); + else { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + // User may have tried to put some compound requirement stuff here + if (Tok.is(tok::kw_noexcept)) { + Diag(Tok, diag::err_requires_expr_simple_requirement_noexcept) + << FixItHint::CreateInsertion(StartLoc, "{") + << FixItHint::CreateInsertion(Tok.getLocation(), "}"); + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + break; + } + break; + } + } + if (ExpectAndConsumeSemi(diag::err_expected_semi_requirement)) { + SkipUntil(tok::semi, tok::r_brace, SkipUntilFlags::StopBeforeMatch); + TryConsumeToken(tok::semi); + break; + } + } + if (Requirements.empty()) { + // Don't emit an empty requires expr here to avoid confusing the user with + // other diagnostics quoting an empty requires expression they never + // wrote. + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return ExprError(); + } + } + Braces.consumeClose(); + Actions.ActOnFinishRequiresExpr(); + return Actions.ActOnRequiresExpr(RequiresKWLoc, Body, LocalParameterDecls, + Requirements, Braces.getCloseLocation()); +} + static TypeTrait TypeTraitFromTokKind(tok::TokenKind kind) { switch (kind) { default: llvm_unreachable("Not a known type trait"); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 2cd158a8b43c..f8da1cb89b9d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1261,7 +1261,8 @@ DeclContext *Sema::getFunctionLevelDeclContext() { DeclContext *DC = CurContext; while (true) { - if (isa(DC) || isa(DC) || isa(DC)) { + if (isa(DC) || isa(DC) || isa(DC) || + isa(DC)) { DC = DC->getParent(); } else if (isa(DC) && cast(DC)->getOverloadedOperator() == OO_Call && diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 018ac2d7dc9d..93e5b4511da9 100755 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -17,7 +17,10 @@ #include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/Template.h" -#include "clang/AST/ExprCXX.h" +#include "clang/Sema/Overload.h" +#include "clang/Sema/Initialization.h" +#include "clang/Sema/SemaInternal.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/OperatorPrecedence.h" #include "llvm/ADT/DenseMap.h" @@ -336,6 +339,118 @@ bool Sema::EnsureTemplateArgumentListConstraints( return false; } +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::ExprRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::ExprRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + break; + case concepts::ExprRequirement::SS_ExprSubstitutionFailure: { + auto *SubstDiag = Req->getExprSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_expr_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_NoexceptNotMet: + S.Diag(Req->getNoexceptLoc(), + diag::note_expr_requirement_noexcept_not_met) + << (int)First << Req->getExpr(); + break; + case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: { + auto *SubstDiag = + Req->getReturnTypeRequirement().getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_expr_requirement_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + break; + } + case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: { + ConceptSpecializationExpr *ConstraintExpr = + Req->getReturnTypeRequirementSubstitutedConstraintExpr(); + if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) + // A simple case - expr type is the type being constrained and the concept + // was not provided arguments. + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied_simple) + << (int)First << S.BuildDecltypeType(Req->getExpr(), + Req->getExpr()->getBeginLoc()) + << ConstraintExpr->getNamedConcept(); + else + S.Diag(ConstraintExpr->getBeginLoc(), + diag::note_expr_requirement_constraints_not_satisfied) + << (int)First << ConstraintExpr; + S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction()); + break; + } + case concepts::ExprRequirement::SS_Satisfied: + llvm_unreachable("We checked this above"); + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::TypeRequirement *Req, + bool First) { + assert(!Req->isSatisfied() + && "Diagnose() can only be used on an unsatisfied requirement"); + switch (Req->getSatisfactionStatus()) { + case concepts::TypeRequirement::SS_Dependent: + llvm_unreachable("Diagnosing a dependent requirement"); + return; + case concepts::TypeRequirement::SS_SubstitutionFailure: { + auto *SubstDiag = Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_substitution_error) << (int)First + << SubstDiag->SubstitutedEntity << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_type_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + default: + llvm_unreachable("Unknown satisfaction status"); + return; + } +} + +static void diagnoseUnsatisfiedRequirement(Sema &S, + concepts::NestedRequirement *Req, + bool First) { + if (Req->isSubstitutionFailure()) { + concepts::Requirement::SubstitutionDiagnostic *SubstDiag = + Req->getSubstitutionDiagnostic(); + if (!SubstDiag->DiagMessage.empty()) + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity + << SubstDiag->DiagMessage; + else + S.Diag(SubstDiag->DiagLoc, + diag::note_nested_requirement_unknown_substitution_error) + << (int)First << SubstDiag->SubstitutedEntity; + return; + } + S.DiagnoseUnsatisfiedConstraint(Req->getConstraintSatisfaction(), First); +} + + static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, Expr *SubstExpr, bool First = true) { @@ -412,6 +527,19 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S, } S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction()); return; + } else if (auto *RE = dyn_cast(SubstExpr)) { + for (concepts::Requirement *Req : RE->getRequirements()) + if (!Req->isDependent() && !Req->isSatisfied()) { + if (auto *E = dyn_cast(Req)) + diagnoseUnsatisfiedRequirement(S, E, First); + else if (auto *T = dyn_cast(Req)) + diagnoseUnsatisfiedRequirement(S, T, First); + else + diagnoseUnsatisfiedRequirement( + S, cast(Req), First); + break; + } + return; } S.Diag(SubstExpr->getSourceRange().getBegin(), @@ -434,11 +562,11 @@ static void diagnoseUnsatisfiedConstraintExpr( Record.template get(), First); } -void Sema::DiagnoseUnsatisfiedConstraint( - const ConstraintSatisfaction& Satisfaction) { +void +Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction.Details) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -446,10 +574,10 @@ void Sema::DiagnoseUnsatisfiedConstraint( } void Sema::DiagnoseUnsatisfiedConstraint( - const ASTConstraintSatisfaction &Satisfaction) { + const ASTConstraintSatisfaction &Satisfaction, + bool First) { assert(!Satisfaction.IsSatisfied && "Attempted to diagnose a satisfied constraint"); - bool First = true; for (auto &Pair : Satisfaction) { diagnoseUnsatisfiedConstraintExpr(*this, Pair.first, Pair.second, First); First = false; @@ -826,3 +954,67 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(NamedDecl *D1, << AmbiguousAtomic2->getSourceRange(); return true; } + +concepts::ExprRequirement::ExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + ReturnTypeRequirement Req, SatisfactionStatus Status, + ConceptSpecializationExpr *SubstitutedConstraintExpr) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Status == SS_Dependent, + Status == SS_Dependent && + (E->containsUnexpandedParameterPack() || + Req.containsUnexpandedParameterPack()), + Status == SS_Satisfied), Value(E), NoexceptLoc(NoexceptLoc), + TypeReq(Req), SubstitutedConstraintExpr(SubstitutedConstraintExpr), + Status(Status) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); + assert((Status > SS_TypeRequirementSubstitutionFailure && Req.isTypeConstraint()) == + (SubstitutedConstraintExpr != nullptr)); +} + +concepts::ExprRequirement::ExprRequirement( + SubstitutionDiagnostic *ExprSubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, ReturnTypeRequirement Req) : + Requirement(IsSimple ? RK_Simple : RK_Compound, Req.isDependent(), + Req.containsUnexpandedParameterPack(), /*IsSatisfied=*/false), + Value(ExprSubstDiag), NoexceptLoc(NoexceptLoc), TypeReq(Req), + Status(SS_ExprSubstitutionFailure) { + assert((!IsSimple || (Req.isEmpty() && NoexceptLoc.isInvalid())) && + "Simple requirement must not have a return type requirement or a " + "noexcept specification"); +} + +concepts::ExprRequirement::ReturnTypeRequirement:: +ReturnTypeRequirement(TemplateParameterList *TPL) : + TypeConstraintInfo(TPL, 0) { + assert(TPL->size() == 1); + const TypeConstraint *TC = + cast(TPL->getParam(0))->getTypeConstraint(); + assert(TC && + "TPL must have a template type parameter with a type constraint"); + auto *Constraint = + cast_or_null( + TC->getImmediatelyDeclaredConstraint()); + bool Dependent = false; + if (Constraint->getTemplateArgsAsWritten()) { + for (auto &ArgLoc : + Constraint->getTemplateArgsAsWritten()->arguments().drop_front(1)) { + if (ArgLoc.getArgument().isDependent()) { + Dependent = true; + break; + } + } + } + TypeConstraintInfo.setInt(Dependent ? 1 : 0); +} + +concepts::TypeRequirement::TypeRequirement(TypeSourceInfo *T) : + Requirement(RK_Type, T->getType()->isDependentType(), + T->getType()->containsUnexpandedParameterPack(), + // We reach this ctor with either dependent types (in which + // IsSatisfied doesn't matter) or with non-dependent type in + // which the existence of the type indicates satisfaction. + /*IsSatisfied=*/true + ), Value(T), + Status(T->getType()->isDependentType() ? SS_Dependent : SS_Satisfied) {} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 507e4a6cd436..372f3d158597 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6468,6 +6468,8 @@ static bool shouldConsiderLinkage(const VarDecl *VD) { return true; if (DC->isRecord()) return false; + if (isa(DC)) + return false; llvm_unreachable("Unexpected context"); } diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 5aedbe7644e4..193eaa3e01f9 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1386,6 +1386,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { case Expr::StringLiteralClass: case Expr::SourceLocExprClass: case Expr::ConceptSpecializationExprClass: + case Expr::RequiresExprClass: // These expressions can never throw. return CT_Cannot; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5f4071924d3f..ea4b93ee6a5a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -350,6 +350,17 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, } } + if (isa(D) && isa(D->getDeclContext()) && + !isUnevaluatedContext()) { + // C++ [expr.prim.req.nested] p3 + // A local parameter shall only appear as an unevaluated operand + // (Clause 8) within the constraint-expression. + Diag(Loc, diag::err_requires_expr_parameter_referenced_in_evaluated_context) + << D; + Diag(D->getLocation(), diag::note_entity_declared_at) << D; + return true; + } + return false; } @@ -1904,7 +1915,7 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, bool RefersToCapturedVariable = isa(D) && NeedToCaptureVariable(cast(D), NameInfo.getLoc()); - + DeclRefExpr *E = DeclRefExpr::Create( Context, NNS, TemplateKWLoc, D, RefersToCapturedVariable, NameInfo, Ty, VK, FoundD, TemplateArgs, getNonOdrUseReasonInCurrentContext(D)); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 938420d85c65..192c237b6c1c 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "clang/Sema/Template.h" #include "clang/Sema/SemaInternal.h" #include "TreeTransform.h" #include "TypeLocBuilder.h" @@ -8331,3 +8332,215 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, SourceLocation KeywordLoc, return CheckMicrosoftIfExistsSymbol(S, SS, TargetNameInfo); } + +concepts::Requirement *Sema::ActOnSimpleRequirement(Expr *E) { + return BuildExprRequirement(E, /*IsSimple=*/true, + /*NoexceptLoc=*/SourceLocation(), + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnTypeRequirement(SourceLocation TypenameKWLoc, CXXScopeSpec &SS, + SourceLocation NameLoc, IdentifierInfo *TypeName, + TemplateIdAnnotation *TemplateId) { + assert(((!TypeName && TemplateId) || (TypeName && !TemplateId)) && + "Exactly one of TypeName and TemplateId must be specified."); + TypeSourceInfo *TSI = nullptr; + if (TypeName) { + QualType T = CheckTypenameType(ETK_Typename, TypenameKWLoc, + SS.getWithLocInContext(Context), *TypeName, + NameLoc, &TSI, /*DeducedTypeContext=*/false); + if (T.isNull()) + return nullptr; + } else { + ASTTemplateArgsPtr ArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + TypeResult T = ActOnTypenameType(CurScope, TypenameKWLoc, SS, + TemplateId->TemplateKWLoc, + TemplateId->Template, TemplateId->Name, + TemplateId->TemplateNameLoc, + TemplateId->LAngleLoc, ArgsPtr, + TemplateId->RAngleLoc); + if (T.isInvalid()) + return nullptr; + if (GetTypeFromParser(T.get(), &TSI).isNull()) + return nullptr; + } + return BuildTypeRequirement(TSI); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement(Expr *E, SourceLocation NoexceptLoc) { + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, + /*ReturnTypeRequirement=*/{}); +} + +concepts::Requirement * +Sema::ActOnCompoundRequirement( + Expr *E, SourceLocation NoexceptLoc, CXXScopeSpec &SS, + TemplateIdAnnotation *TypeConstraint, unsigned Depth) { + // C++2a [expr.prim.req.compound] p1.3.3 + // [..] the expression is deduced against an invented function template + // F [...] F is a void function template with a single type template + // parameter T declared with the constrained-parameter. Form a new + // cv-qualifier-seq cv by taking the union of const and volatile specifiers + // around the constrained-parameter. F has a single parameter whose + // type-specifier is cv T followed by the abstract-declarator. [...] + // + // The cv part is done in the calling function - we get the concept with + // arguments and the abstract declarator with the correct CV qualification and + // have to synthesize T and the single parameter of F. + auto &II = Context.Idents.get("expr-type"); + auto *TParam = TemplateTypeParmDecl::Create(Context, CurContext, + SourceLocation(), + SourceLocation(), Depth, + /*Index=*/0, &II, + /*Typename=*/true, + /*ParameterPack=*/false, + /*HasTypeConstraint=*/true); + + if (ActOnTypeConstraint(SS, TypeConstraint, TParam, + /*EllpsisLoc=*/SourceLocation())) + // Just produce a requirement with no type requirements. + return BuildExprRequirement(E, /*IsSimple=*/false, NoexceptLoc, {}); + + auto *TPL = TemplateParameterList::Create(Context, SourceLocation(), + SourceLocation(), + ArrayRef(TParam), + SourceLocation(), + /*RequiresClause=*/nullptr); + return BuildExprRequirement( + E, /*IsSimple=*/false, NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement(TPL)); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + auto Status = concepts::ExprRequirement::SS_Satisfied; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + if (E->isInstantiationDependent() || ReturnTypeRequirement.isDependent()) + Status = concepts::ExprRequirement::SS_Dependent; + else if (NoexceptLoc.isValid() && canThrow(E) == CanThrowResult::CT_Can) + Status = concepts::ExprRequirement::SS_NoexceptNotMet; + else if (ReturnTypeRequirement.isSubstitutionFailure()) + Status = concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure; + else if (ReturnTypeRequirement.isTypeConstraint()) { + // C++2a [expr.prim.req]p1.3.3 + // The immediately-declared constraint ([temp]) of decltype((E)) shall + // be satisfied. + TemplateParameterList *TPL = + ReturnTypeRequirement.getTypeConstraintTemplateParameterList(); + QualType MatchedType = + BuildDecltypeType(E, E->getBeginLoc()).getCanonicalType(); + llvm::SmallVector Args; + Args.push_back(TemplateArgument(MatchedType)); + TemplateArgumentList TAL(TemplateArgumentList::OnStack, Args); + MultiLevelTemplateArgumentList MLTAL(TAL); + for (unsigned I = 0; I < TPL->getDepth(); ++I) + MLTAL.addOuterRetainedLevel(); + Expr *IDC = + cast(TPL->getParam(0))->getTypeConstraint() + ->getImmediatelyDeclaredConstraint(); + ExprResult Constraint = SubstExpr(IDC, MLTAL); + assert(!Constraint.isInvalid() && + "Substitution cannot fail as it is simply putting a type template " + "argument into a concept specialization expression's parameter."); + + SubstitutedConstraintExpr = + cast(Constraint.get()); + if (!SubstitutedConstraintExpr->isSatisfied()) + Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied; + } + return new (Context) concepts::ExprRequirement(E, IsSimple, NoexceptLoc, + ReturnTypeRequirement, Status, + SubstitutedConstraintExpr); +} + +concepts::ExprRequirement * +Sema::BuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *ExprSubstitutionDiagnostic, + bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement ReturnTypeRequirement) { + return new (Context) concepts::ExprRequirement(ExprSubstitutionDiagnostic, + IsSimple, NoexceptLoc, + ReturnTypeRequirement); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement(TypeSourceInfo *Type) { + return new (Context) concepts::TypeRequirement(Type); +} + +concepts::TypeRequirement * +Sema::BuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::TypeRequirement(SubstDiag); +} + +concepts::Requirement *Sema::ActOnNestedRequirement(Expr *Constraint) { + return BuildNestedRequirement(Constraint); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement(Expr *Constraint) { + ConstraintSatisfaction Satisfaction; + if (!Constraint->isInstantiationDependent() && + CheckConstraintSatisfaction(Constraint, Satisfaction)) + return nullptr; + return new (Context) concepts::NestedRequirement(Context, Constraint, + Satisfaction); +} + +concepts::NestedRequirement * +Sema::BuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return new (Context) concepts::NestedRequirement(SubstDiag); +} + +RequiresExprBodyDecl * +Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, + ArrayRef LocalParameters, + Scope *BodyScope) { + assert(BodyScope); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create(Context, CurContext, + RequiresKWLoc); + + PushDeclContext(BodyScope, Body); + + for (ParmVarDecl *Param : LocalParameters) { + if (Param->hasDefaultArg()) + // C++2a [expr.prim.req] p4 + // [...] A local parameter of a requires-expression shall not have a + // default argument. [...] + Diag(Param->getDefaultArgRange().getBegin(), + diag::err_requires_expr_local_parameter_default_argument); + // Ignore default argument and move on + + Param->setDeclContext(Body); + // If this has an identifier, add it to the scope stack. + if (Param->getIdentifier()) { + CheckShadow(BodyScope, Param); + PushOnScopeChains(Param, BodyScope); + } + } + return Body; +} + +void Sema::ActOnFinishRequiresExpr() { + assert(CurContext && "DeclContext imbalance!"); + CurContext = CurContext->getLexicalParent(); + assert(CurContext && "Popped translation unit!"); +} + +ExprResult +Sema::ActOnRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(Context, RequiresKWLoc, Body, LocalParameters, + Requirements, ClosingBraceLoc); +} diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 0ed51de0cc13..8d96404a5c27 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1575,7 +1575,9 @@ llvm::DenseSet &Sema::getLookupModules() { unsigned N = CodeSynthesisContexts.size(); for (unsigned I = CodeSynthesisContextLookupModules.size(); I != N; ++I) { - Module *M = getDefiningModule(*this, CodeSynthesisContexts[I].Entity); + Module *M = CodeSynthesisContexts[I].Entity ? + getDefiningModule(*this, CodeSynthesisContexts[I].Entity) : + nullptr; if (M && !LookupModulesCache.insert(M).second) M = nullptr; CodeSynthesisContextLookupModules.push_back(M); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 8a50a9e1538f..661a66246a53 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10010,24 +10010,12 @@ Sema::ActOnTypenameType(Scope *S, SourceLocation TypenameLoc, << FixItHint::CreateRemoval(TypenameLoc); NestedNameSpecifierLoc QualifierLoc = SS.getWithLocInContext(Context); + TypeSourceInfo *TSI = nullptr; QualType T = CheckTypenameType(TypenameLoc.isValid()? ETK_Typename : ETK_None, - TypenameLoc, QualifierLoc, II, IdLoc); + TypenameLoc, QualifierLoc, II, IdLoc, &TSI, + /*DeducedTSTContext=*/true); if (T.isNull()) return true; - - TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(T); - if (isa(T)) { - DependentNameTypeLoc TL = TSI->getTypeLoc().castAs(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.setNameLoc(IdLoc); - } else { - ElaboratedTypeLoc TL = TSI->getTypeLoc().castAs(); - TL.setElaboratedKeywordLoc(TypenameLoc); - TL.setQualifierLoc(QualifierLoc); - TL.getNamedTypeLoc().castAs().setNameLoc(IdLoc); - } - return CreateParsedType(T, TSI); } @@ -10164,6 +10152,35 @@ static bool isEnableIf(NestedNameSpecifierLoc NNS, const IdentifierInfo &II, return true; } +QualType +Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, + SourceLocation KeywordLoc, + NestedNameSpecifierLoc QualifierLoc, + const IdentifierInfo &II, + SourceLocation IILoc, + TypeSourceInfo **TSI, + bool DeducedTSTContext) { + QualType T = CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, II, IILoc, + DeducedTSTContext); + if (T.isNull()) + return QualType(); + + *TSI = Context.CreateTypeSourceInfo(T); + if (isa(T)) { + DependentNameTypeLoc TL = + (*TSI)->getTypeLoc().castAs(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.setNameLoc(IILoc); + } else { + ElaboratedTypeLoc TL = (*TSI)->getTypeLoc().castAs(); + TL.setElaboratedKeywordLoc(KeywordLoc); + TL.setQualifierLoc(QualifierLoc); + TL.getNamedTypeLoc().castAs().setNameLoc(IILoc); + } + return T; +} + /// Build the type that describes a C++ typename specifier, /// e.g., "typename T::type". QualType @@ -10171,32 +10188,38 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, SourceLocation KeywordLoc, NestedNameSpecifierLoc QualifierLoc, const IdentifierInfo &II, - SourceLocation IILoc) { + SourceLocation IILoc, bool DeducedTSTContext) { CXXScopeSpec SS; SS.Adopt(QualifierLoc); - DeclContext *Ctx = computeDeclContext(SS); - if (!Ctx) { - // If the nested-name-specifier is dependent and couldn't be - // resolved to a type, build a typename type. - assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); - return Context.getDependentNameType(Keyword, - QualifierLoc.getNestedNameSpecifier(), - &II); + DeclContext *Ctx = nullptr; + if (QualifierLoc) { + Ctx = computeDeclContext(SS); + if (!Ctx) { + // If the nested-name-specifier is dependent and couldn't be + // resolved to a type, build a typename type. + assert(QualifierLoc.getNestedNameSpecifier()->isDependent()); + return Context.getDependentNameType(Keyword, + QualifierLoc.getNestedNameSpecifier(), + &II); + } + + // If the nested-name-specifier refers to the current instantiation, + // the "typename" keyword itself is superfluous. In C++03, the + // program is actually ill-formed. However, DR 382 (in C++0x CD1) + // allows such extraneous "typename" keywords, and we retroactively + // apply this DR to C++03 code with only a warning. In any case we continue. + + if (RequireCompleteDeclContext(SS, Ctx)) + return QualType(); } - // If the nested-name-specifier refers to the current instantiation, - // the "typename" keyword itself is superfluous. In C++03, the - // program is actually ill-formed. However, DR 382 (in C++0x CD1) - // allows such extraneous "typename" keywords, and we retroactively - // apply this DR to C++03 code with only a warning. In any case we continue. - - if (RequireCompleteDeclContext(SS, Ctx)) - return QualType(); - DeclarationName Name(&II); LookupResult Result(*this, Name, IILoc, LookupOrdinaryName); - LookupQualifiedName(Result, Ctx, SS); + if (Ctx) + LookupQualifiedName(Result, Ctx, SS); + else + LookupName(Result, CurScope); unsigned DiagID = 0; Decl *Referenced = nullptr; switch (Result.getResultKind()) { @@ -10205,7 +10228,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // a more specific diagnostic. SourceRange CondRange; Expr *Cond = nullptr; - if (isEnableIf(QualifierLoc, II, CondRange, Cond)) { + if (Ctx && isEnableIf(QualifierLoc, II, CondRange, Cond)) { // If we have a condition, narrow it down to the specific failed // condition. if (Cond) { @@ -10221,12 +10244,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, return QualType(); } - Diag(CondRange.getBegin(), diag::err_typename_nested_not_found_enable_if) + Diag(CondRange.getBegin(), + diag::err_typename_nested_not_found_enable_if) << Ctx << CondRange; return QualType(); } - DiagID = diag::err_typename_nested_not_found; + DiagID = Ctx ? diag::err_typename_nested_not_found + : diag::err_unknown_typename; break; } @@ -10292,6 +10317,19 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // is a placeholder for a deduced class type [...]. if (getLangOpts().CPlusPlus17) { if (auto *TD = getAsTypeTemplateDecl(Result.getFoundDecl())) { + if (!DeducedTSTContext) { + QualType T(QualifierLoc + ? QualifierLoc.getNestedNameSpecifier()->getAsType() + : nullptr, 0); + if (!T.isNull()) + Diag(IILoc, diag::err_dependent_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)) << T; + else + Diag(IILoc, diag::err_deduced_tst) + << (int)getTemplateNameKindForDiagnostics(TemplateName(TD)); + Diag(TD->getLocation(), diag::note_template_decl_here); + return QualType(); + } return Context.getElaboratedType( Keyword, QualifierLoc.getNestedNameSpecifier(), Context.getDeducedTemplateSpecializationType(TemplateName(TD), @@ -10299,12 +10337,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, } } - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = Result.getFoundDecl(); break; case LookupResult::FoundOverloaded: - DiagID = diag::err_typename_nested_not_type; + DiagID = Ctx ? diag::err_typename_nested_not_type + : diag::err_typename_not_type; Referenced = *Result.begin(); break; @@ -10316,9 +10356,14 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword, // type. Emit an appropriate diagnostic and return an error. SourceRange FullRange(KeywordLoc.isValid() ? KeywordLoc : SS.getBeginLoc(), IILoc); - Diag(IILoc, DiagID) << FullRange << Name << Ctx; + if (Ctx) + Diag(IILoc, DiagID) << FullRange << Name << Ctx; + else + Diag(IILoc, DiagID) << FullRange << Name; if (Referenced) - Diag(Referenced->getLocation(), diag::note_typename_refers_here) + Diag(Referenced->getLocation(), + Ctx ? diag::note_typename_member_refers_here + : diag::note_typename_refers_here) << Name; return QualType(); } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index af41e231134d..39bc28d62305 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -26,6 +26,7 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/TemplateInstCallback.h" +#include "clang/Sema/SemaConcept.h" #include "llvm/Support/TimeProfiler.h" using namespace clang; @@ -199,8 +200,10 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case DeducedTemplateArgumentSubstitution: case PriorTemplateArgumentSubstitution: case ConstraintsCheck: + case NestedRequirementConstraintsCheck: return true; + case RequirementInstantiation: case DefaultTemplateArgumentChecking: case DeclaringSpecialMember: case DeclaringImplicitEqualityComparison: @@ -247,7 +250,7 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( Inst.InstantiationRange = InstantiationRange; SemaRef.pushCodeSynthesisContext(Inst); - AlreadyInstantiating = + AlreadyInstantiating = !Inst.Entity ? false : !SemaRef.InstantiatingSpecializations .insert(std::make_pair(Inst.Entity->getCanonicalDecl(), Inst.Kind)) .second; @@ -364,6 +367,26 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( PointOfInstantiation, InstantiationRange, Param, Template, TemplateArgs) {} +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::Requirement *Req, sema::TemplateDeductionInfo &DeductionInfo, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::RequirementInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None, &DeductionInfo) {} + + +Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + concepts::NestedRequirement *Req, ConstraintsCheck, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, CodeSynthesisContext::NestedRequirementConstraintsCheck, + PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr, + /*Template=*/nullptr, /*TemplateArgs=*/None) {} + + Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, ConstraintsCheck, NamedDecl *Template, @@ -446,8 +469,9 @@ void Sema::InstantiatingTemplate::Clear() { if (!Invalid) { if (!AlreadyInstantiating) { auto &Active = SemaRef.CodeSynthesisContexts.back(); - SemaRef.InstantiatingSpecializations.erase( - std::make_pair(Active.Entity, Active.Kind)); + if (Active.Entity) + SemaRef.InstantiatingSpecializations.erase( + std::make_pair(Active.Entity, Active.Kind)); } atTemplateEnd(SemaRef.TemplateInstCallbacks, SemaRef, @@ -684,6 +708,18 @@ void Sema::PrintInstantiationStack() { << Active->InstantiationRange; break; + case CodeSynthesisContext::RequirementInstantiation: + Diags.Report(Active->PointOfInstantiation, + diag::note_template_requirement_instantiation_here) + << Active->InstantiationRange; + break; + + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + Diags.Report(Active->PointOfInstantiation, + diag::note_nested_requirement_here) + << Active->InstantiationRange; + break; + case CodeSynthesisContext::DeclaringSpecialMember: Diags.Report(Active->PointOfInstantiation, diag::note_in_declaration_of_implicit_special_member) @@ -788,6 +824,7 @@ Optional Sema::isSFINAEContext() const { case CodeSynthesisContext::ConstraintsCheck: case CodeSynthesisContext::ParameterMappingSubstitution: case CodeSynthesisContext::ConstraintNormalization: + case CodeSynthesisContext::NestedRequirementConstraintsCheck: // This is a template instantiation, so there is no SFINAE. return None; @@ -802,9 +839,10 @@ Optional Sema::isSFINAEContext() const { case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: case CodeSynthesisContext::ConstraintSubstitution: - // We're either substituting explicitly-specified template arguments - // or deduced template arguments or a constraint expression, so SFINAE - // applies. + case CodeSynthesisContext::RequirementInstantiation: + // We're either substituting explicitly-specified template arguments, + // deduced template arguments, a constraint expression or a requirement + // in a requires expression, so SFINAE applies. assert(Active->DeductionInfo && "Missing deduction info pointer"); return Active->DeductionInfo; @@ -1056,6 +1094,41 @@ namespace { return TreeTransform::TransformLambdaExpr(E); } + ExprResult TransformRequiresExpr(RequiresExpr *E) { + LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); + return TreeTransform::TransformRequiresExpr(E); + } + + bool TransformRequiresExprRequirements( + ArrayRef Reqs, + SmallVectorImpl &Transformed) { + bool SatisfactionDetermined = false; + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (!SatisfactionDetermined) { + if (auto *TypeReq = dyn_cast(Req)) + TransReq = TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast(Req)) + TransReq = TransformExprRequirement(ExprReq); + else + TransReq = TransformNestedRequirement( + cast(Req)); + if (!TransReq) + return true; + if (!TransReq->isDependent() && !TransReq->isSatisfied()) + // [expr.prim.req]p6 + // [...] The substitution and semantic constraint checking + // proceeds in lexical order and stops when a condition that + // determines the result of the requires-expression is + // encountered. [..] + SatisfactionDetermined = true; + } else + TransReq = Req; + Transformed.push_back(TransReq); + } + return false; + } + TemplateParameterList *TransformTemplateParameterList( TemplateParameterList *OrigTPL) { if (!OrigTPL || !OrigTPL->size()) return OrigTPL; @@ -1065,6 +1138,14 @@ namespace { /* DeclContext *Owner */ Owner, TemplateArgs); return DeclInstantiator.SubstTemplateParams(OrigTPL); } + + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + private: ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, SourceLocation loc, @@ -1669,6 +1750,163 @@ TemplateInstantiator::TransformSubstTemplateTypeParmPackType( return Result; } +template +static concepts::Requirement::SubstitutionDiagnostic * +createSubstDiag(Sema &S, TemplateDeductionInfo &Info, EntityPrinter Printer) { + SmallString<128> Message; + SourceLocation ErrorLoc; + if (Info.hasSFINAEDiagnostic()) { + PartialDiagnosticAt PDA(SourceLocation(), + PartialDiagnostic::NullDiagnostic{}); + Info.takeSFINAEDiagnostic(PDA); + PDA.second.EmitToString(S.getDiagnostics(), Message); + ErrorLoc = PDA.first; + } else { + ErrorLoc = Info.getLocation(); + } + char *MessageBuf = new (S.Context) char[Message.size()]; + std::copy(Message.begin(), Message.end(), MessageBuf); + SmallString<128> Entity; + llvm::raw_svector_ostream OS(Entity); + Printer(OS); + char *EntityBuf = new (S.Context) char[Entity.size()]; + std::copy(Entity.begin(), Entity.end(), EntityBuf); + return new (S.Context) concepts::Requirement::SubstitutionDiagnostic{ + StringRef(EntityBuf, Entity.size()), ErrorLoc, + StringRef(MessageBuf, Message.size())}; +} + +concepts::TypeRequirement * +TemplateInstantiator::TransformTypeRequirement(concepts::TypeRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getType()->getTypeLoc().getBeginLoc()); + Sema::InstantiatingTemplate TypeInst(SemaRef, + Req->getType()->getTypeLoc().getBeginLoc(), Req, Info, + Req->getType()->getTypeLoc().getSourceRange()); + if (TypeInst.isInvalid()) + return nullptr; + TypeSourceInfo *TransType = TransformType(Req->getType()); + if (!TransType || Trap.hasErrorOccurred()) + return RebuildTypeRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy()); + })); + return RebuildTypeRequirement(TransType); +} + +concepts::ExprRequirement * +TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + + Sema::SFINAETrap Trap(SemaRef); + TemplateDeductionInfo Info(Req->getExpr()->getBeginLoc()); + + llvm::PointerUnion + TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + Sema::InstantiatingTemplate ExprInst(SemaRef, Req->getExpr()->getBeginLoc(), + Req, Info, + Req->getExpr()->getSourceRange()); + if (ExprInst.isInvalid()) + return nullptr; + ExprResult TransExprRes = TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid() || Trap.hasErrorOccurred()) + TransExpr = createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + }); + else + TransExpr = TransExprRes.get(); + } + + llvm::Optional TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + Sema::InstantiatingTemplate TPLInst(SemaRef, OrigTPL->getTemplateLoc(), + Req, Info, OrigTPL->getSourceRange()); + if (TPLInst.isInvalid()) + return nullptr; + TemplateParameterList *TPL = + TransformTemplateParameterList(OrigTPL); + if (!TPL) + TransRetReq.emplace(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint() + ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy()); + })); + else { + TPLInst.Clear(); + TransRetReq.emplace(TPL); + } + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast()) + return RebuildExprRequirement(E, Req->isSimple(), Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return RebuildExprRequirement( + TransExpr.get(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +concepts::NestedRequirement * +TemplateInstantiator::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (!Req->isDependent() && !AlwaysRebuild()) + return Req; + if (Req->isSubstitutionFailure()) { + if (AlwaysRebuild()) + return RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + Sema::InstantiatingTemplate ReqInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, + Sema::InstantiatingTemplate::ConstraintsCheck{}, + Req->getConstraintExpr()->getSourceRange()); + + ExprResult TransConstraint; + TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc()); + { + EnterExpressionEvaluationContext ContextRAII( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); + Sema::SFINAETrap Trap(SemaRef); + Sema::InstantiatingTemplate ConstrInst(SemaRef, + Req->getConstraintExpr()->getBeginLoc(), Req, Info, + Req->getConstraintExpr()->getSourceRange()); + if (ConstrInst.isInvalid()) + return nullptr; + TransConstraint = TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid() || Trap.hasErrorOccurred()) + return RebuildNestedRequirement(createSubstDiag(SemaRef, Info, + [&] (llvm::raw_ostream& OS) { + Req->getConstraintExpr()->printPretty(OS, nullptr, + SemaRef.getPrintingPolicy()); + })); + } + return RebuildNestedRequirement(TransConstraint.get()); +} + + /// Perform substitution on the type T with a given set of template /// arguments. /// diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 64500d0a26d5..a470cfc87440 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3600,6 +3600,12 @@ Decl *TemplateDeclInstantiator::VisitConceptDecl(ConceptDecl *D) { llvm_unreachable("Concept definitions cannot reside inside a template"); } +Decl * +TemplateDeclInstantiator::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + return RequiresExprBodyDecl::Create(SemaRef.Context, D->getDeclContext(), + D->getBeginLoc()); +} + Decl *TemplateDeclInstantiator::VisitDecl(Decl *D) { llvm_unreachable("Unexpected decl"); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3884fdae8fe7..bafeed6e8447 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2994,6 +2994,9 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::PrototypeContext: Error = 0; break; + case DeclaratorContext::RequiresExprContext: + Error = 21; + break; case DeclaratorContext::LambdaExprParameterContext: // In C++14, generic lambdas allow 'auto' in their parameters. if (!SemaRef.getLangOpts().CPlusPlus14 || @@ -3221,6 +3224,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: case DeclaratorContext::KNRTypeListContext: + case DeclaratorContext::RequiresExprContext: // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. DiagID = diag::err_type_defined_in_param_type; @@ -4279,6 +4283,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, case DeclaratorContext::TemplateTypeArgContext: case DeclaratorContext::TypeNameContext: case DeclaratorContext::FunctionalCastContext: + case DeclaratorContext::RequiresExprContext: // Don't infer in these contexts. break; } @@ -5227,6 +5232,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, switch (D.getContext()) { case DeclaratorContext::PrototypeContext: case DeclaratorContext::LambdaExprParameterContext: + case DeclaratorContext::RequiresExprContext: // C++0x [dcl.fct]p13: // [...] When it is part of a parameter-declaration-clause, the // parameter pack is a function parameter pack (14.5.3). The type T diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 3b827fbc950b..1f7251173838 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprConcepts.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" @@ -509,6 +510,15 @@ class TreeTransform { DeclarationNameInfo TransformDeclarationNameInfo(const DeclarationNameInfo &NameInfo); + bool TransformRequiresExprRequirements(ArrayRef Reqs, + llvm::SmallVectorImpl &Transformed); + concepts::TypeRequirement * + TransformTypeRequirement(concepts::TypeRequirement *Req); + concepts::ExprRequirement * + TransformExprRequirement(concepts::ExprRequirement *Req); + concepts::NestedRequirement * + TransformNestedRequirement(concepts::NestedRequirement *Req); + /// Transform the given template name. /// /// \param SS The nested-name-specifier that qualifies the template @@ -1056,23 +1066,8 @@ class TreeTransform { } if (Keyword == ETK_None || Keyword == ETK_Typename) { - QualType T = SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, - *Id, IdLoc); - // If a dependent name resolves to a deduced template specialization type, - // check that we're in one of the syntactic contexts permitting it. - if (!DeducedTSTContext) { - if (auto *Deduced = dyn_cast_or_null( - T.isNull() ? nullptr : T->getContainedDeducedType())) { - SemaRef.Diag(IdLoc, diag::err_dependent_deduced_tst) - << (int)SemaRef.getTemplateNameKindForDiagnostics( - Deduced->getTemplateName()) - << QualType(QualifierLoc.getNestedNameSpecifier()->getAsType(), 0); - if (auto *TD = Deduced->getTemplateName().getAsTemplateDecl()) - SemaRef.Diag(TD->getLocation(), diag::note_template_decl_here); - return QualType(); - } - } - return T; + return SemaRef.CheckTypenameType(Keyword, KeywordLoc, QualifierLoc, + *Id, IdLoc, DeducedTSTContext); } TagTypeKind Kind = TypeWithKeyword::getTagTypeKindForKeyword(Keyword); @@ -3078,7 +3073,56 @@ class TreeTransform { return Result; } - /// \brief Build a new Objective-C boxed expression. + /// \brief Build a new requires expression. + /// + /// By default, performs semantic analysis to build the new expression. + /// Subclasses may override this routine to provide different behavior. + ExprResult RebuildRequiresExpr(SourceLocation RequiresKWLoc, + RequiresExprBodyDecl *Body, + ArrayRef LocalParameters, + ArrayRef Requirements, + SourceLocation ClosingBraceLoc) { + return RequiresExpr::Create(SemaRef.Context, RequiresKWLoc, Body, + LocalParameters, Requirements, ClosingBraceLoc); + } + + concepts::TypeRequirement * + RebuildTypeRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildTypeRequirement(SubstDiag); + } + + concepts::TypeRequirement *RebuildTypeRequirement(TypeSourceInfo *T) { + return SemaRef.BuildTypeRequirement(T); + } + + concepts::ExprRequirement * + RebuildExprRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag, bool IsSimple, + SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(SubstDiag, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::ExprRequirement * + RebuildExprRequirement(Expr *E, bool IsSimple, SourceLocation NoexceptLoc, + concepts::ExprRequirement::ReturnTypeRequirement Ret) { + return SemaRef.BuildExprRequirement(E, IsSimple, NoexceptLoc, + std::move(Ret)); + } + + concepts::NestedRequirement * + RebuildNestedRequirement( + concepts::Requirement::SubstitutionDiagnostic *SubstDiag) { + return SemaRef.BuildNestedRequirement(SubstDiag); + } + + concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { + return SemaRef.BuildNestedRequirement(Constraint); + } + + /// \brief Build a new Objective-C boxed expression. /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. @@ -11179,6 +11223,146 @@ TreeTransform::TransformConceptSpecializationExpr( &TransArgs); } +template +ExprResult +TreeTransform::TransformRequiresExpr(RequiresExpr *E) { + SmallVector TransParams; + SmallVector TransParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; + + // C++2a [expr.prim.req]p2 + // Expressions appearing within a requirement-body are unevaluated operands. + EnterExpressionEvaluationContext Ctx( + SemaRef, Sema::ExpressionEvaluationContext::Unevaluated); + + RequiresExprBodyDecl *Body = RequiresExprBodyDecl::Create( + getSema().Context, E->getBody()->getDeclContext(), + E->getBody()->getBeginLoc()); + + Sema::ContextRAII SavedContext(getSema(), Body, /*NewThisContext*/false); + + if (getDerived().TransformFunctionTypeParams(E->getRequiresKWLoc(), + E->getLocalParameters(), + /*ParamTypes=*/nullptr, + /*ParamInfos=*/nullptr, + TransParamTypes, &TransParams, + ExtParamInfos)) + return ExprError(); + + for (ParmVarDecl *Param : TransParams) + Param->setDeclContext(Body); + + SmallVector TransReqs; + if (getDerived().TransformRequiresExprRequirements(E->getRequirements(), + TransReqs)) + return ExprError(); + + for (concepts::Requirement *Req : TransReqs) { + if (auto *ER = dyn_cast(Req)) { + if (ER->getReturnTypeRequirement().isTypeConstraint()) { + ER->getReturnTypeRequirement() + .getTypeConstraintTemplateParameterList()->getParam(0) + ->setDeclContext(Body); + } + } + } + + return getDerived().RebuildRequiresExpr(E->getRequiresKWLoc(), Body, + TransParams, TransReqs, + E->getRBraceLoc()); +} + +template +bool TreeTransform::TransformRequiresExprRequirements( + ArrayRef Reqs, + SmallVectorImpl &Transformed) { + for (concepts::Requirement *Req : Reqs) { + concepts::Requirement *TransReq = nullptr; + if (auto *TypeReq = dyn_cast(Req)) + TransReq = getDerived().TransformTypeRequirement(TypeReq); + else if (auto *ExprReq = dyn_cast(Req)) + TransReq = getDerived().TransformExprRequirement(ExprReq); + else + TransReq = getDerived().TransformNestedRequirement( + cast(Req)); + if (!TransReq) + return true; + Transformed.push_back(TransReq); + } + return false; +} + +template +concepts::TypeRequirement * +TreeTransform::TransformTypeRequirement( + concepts::TypeRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildTypeRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + TypeSourceInfo *TransType = getDerived().TransformType(Req->getType()); + if (!TransType) + return nullptr; + return getDerived().RebuildTypeRequirement(TransType); +} + +template +concepts::ExprRequirement * +TreeTransform::TransformExprRequirement(concepts::ExprRequirement *Req) { + llvm::PointerUnion TransExpr; + if (Req->isExprSubstitutionFailure()) + TransExpr = Req->getExprSubstitutionDiagnostic(); + else { + ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr()); + if (TransExprRes.isInvalid()) + return nullptr; + TransExpr = TransExprRes.get(); + } + + llvm::Optional TransRetReq; + const auto &RetReq = Req->getReturnTypeRequirement(); + if (RetReq.isEmpty()) + TransRetReq.emplace(); + else if (RetReq.isSubstitutionFailure()) + TransRetReq.emplace(RetReq.getSubstitutionDiagnostic()); + else if (RetReq.isTypeConstraint()) { + TemplateParameterList *OrigTPL = + RetReq.getTypeConstraintTemplateParameterList(); + TemplateParameterList *TPL = + getDerived().TransformTemplateParameterList(OrigTPL); + if (!TPL) + return nullptr; + TransRetReq.emplace(TPL); + } + assert(TransRetReq.hasValue() && + "All code paths leading here must set TransRetReq"); + if (Expr *E = TransExpr.dyn_cast()) + return getDerived().RebuildExprRequirement(E, Req->isSimple(), + Req->getNoexceptLoc(), + std::move(*TransRetReq)); + return getDerived().RebuildExprRequirement( + TransExpr.get(), + Req->isSimple(), Req->getNoexceptLoc(), std::move(*TransRetReq)); +} + +template +concepts::NestedRequirement * +TreeTransform::TransformNestedRequirement( + concepts::NestedRequirement *Req) { + if (Req->isSubstitutionFailure()) { + if (getDerived().AlwaysRebuild()) + return getDerived().RebuildNestedRequirement( + Req->getSubstitutionDiagnostic()); + return Req; + } + ExprResult TransConstraint = + getDerived().TransformExpr(Req->getConstraintExpr()); + if (TransConstraint.isInvalid()) + return nullptr; + return getDerived().RebuildNestedRequirement(TransConstraint.get()); +} template ExprResult diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index cdb5b17022c2..f93f1f77405d 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -402,6 +402,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Binding: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: return false; // These indirectly derive from Redeclarable but are not actually diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 96a7d5ae0a31..4fd079e9d8e1 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -375,6 +375,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); DeclID VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D); @@ -2037,6 +2038,9 @@ void ASTDeclReader::VisitConceptDecl(ConceptDecl *D) { mergeMergeable(D); } +void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { +} + ASTDeclReader::RedeclarableResult ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableResult Redecl = VisitRedeclarable(D); @@ -3839,6 +3843,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { case DECL_CONCEPT: D = ConceptDecl::CreateDeserialized(Context, ID); break; + case DECL_REQUIRES_EXPR_BODY: + D = RequiresExprBodyDecl::CreateDeserialized(Context, ID); + break; case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index f558c26b5f1e..5dd0ef9d43c3 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -724,27 +724,15 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { E->setRParenLoc(readSourceLocation()); } -void ASTStmtReader::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - unsigned NumTemplateArgs = Record.readInt(); - E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); - E->TemplateKWLoc = Record.readSourceLocation(); - E->ConceptName = Record.readDeclarationNameInfo(); - E->NamedConcept = readDeclAs(); - E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); - llvm::SmallVector Args; - for (unsigned I = 0; I < NumTemplateArgs; ++I) - Args.push_back(Record.readTemplateArgument()); - E->setTemplateArguments(Args); +static ConstraintSatisfaction +readConstraintSatisfaction(ASTRecordReader &Record) { ConstraintSatisfaction Satisfaction; Satisfaction.IsSatisfied = Record.readInt(); if (!Satisfaction.IsSatisfied) { unsigned NumDetailRecords = Record.readInt(); for (unsigned i = 0; i != NumDetailRecords; ++i) { Expr *ConstraintExpr = Record.readExpr(); - bool IsDiagnostic = Record.readInt(); - if (IsDiagnostic) { + if (bool IsDiagnostic = Record.readInt()) { SourceLocation DiagLocation = Record.readSourceLocation(); std::string DiagMessage = Record.readString(); Satisfaction.Details.emplace_back( @@ -755,8 +743,137 @@ void ASTStmtReader::VisitConceptSpecializationExpr( Satisfaction.Details.emplace_back(ConstraintExpr, Record.readExpr()); } } - E->Satisfaction = ASTConstraintSatisfaction::Create(Record.getContext(), - Satisfaction); + return Satisfaction; +} + +void ASTStmtReader::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + unsigned NumTemplateArgs = Record.readInt(); + E->NestedNameSpec = Record.readNestedNameSpecifierLoc(); + E->TemplateKWLoc = Record.readSourceLocation(); + E->ConceptName = Record.readDeclarationNameInfo(); + E->NamedConcept = readDeclAs(); + E->ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + llvm::SmallVector Args; + for (unsigned I = 0; I < NumTemplateArgs; ++I) + Args.push_back(Record.readTemplateArgument()); + E->setTemplateArguments(Args); + E->Satisfaction = E->isValueDependent() ? nullptr : + ASTConstraintSatisfaction::Create(Record.getContext(), + readConstraintSatisfaction(Record)); +} + +static concepts::Requirement::SubstitutionDiagnostic * +readSubstitutionDiagnostic(ASTRecordReader &Record) { + std::string SubstitutedEntity = Record.readString(); + SourceLocation DiagLoc = Record.readSourceLocation(); + std::string DiagMessage = Record.readString(); + return new (Record.getContext()) + concepts::Requirement::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc, + DiagMessage}; +} + +void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + unsigned NumLocalParameters = Record.readInt(); + unsigned NumRequirements = Record.readInt(); + E->RequiresExprBits.RequiresKWLoc = Record.readSourceLocation(); + E->RequiresExprBits.IsSatisfied = Record.readInt(); + E->Body = Record.readDeclAs(); + llvm::SmallVector LocalParameters; + for (unsigned i = 0; i < NumLocalParameters; ++i) + LocalParameters.push_back(cast(Record.readDecl())); + std::copy(LocalParameters.begin(), LocalParameters.end(), + E->getTrailingObjects()); + llvm::SmallVector Requirements; + for (unsigned i = 0; i < NumRequirements; ++i) { + auto RK = + static_cast(Record.readInt()); + concepts::Requirement *R = nullptr; + switch (RK) { + case concepts::Requirement::RK_Type: { + auto Status = + static_cast( + Record.readInt()); + if (Status == concepts::TypeRequirement::SS_SubstitutionFailure) + R = new (Record.getContext()) + concepts::TypeRequirement(readSubstitutionDiagnostic(Record)); + else + R = new (Record.getContext()) + concepts::TypeRequirement(Record.readTypeSourceInfo()); + } break; + case concepts::Requirement::RK_Simple: + case concepts::Requirement::RK_Compound: { + auto Status = + static_cast( + Record.readInt()); + llvm::PointerUnion E; + if (Status == concepts::ExprRequirement::SS_ExprSubstitutionFailure) { + E = readSubstitutionDiagnostic(Record); + } else + E = Record.readExpr(); + + llvm::Optional Req; + ConceptSpecializationExpr *SubstitutedConstraintExpr = nullptr; + SourceLocation NoexceptLoc; + if (RK == concepts::Requirement::RK_Simple) { + Req.emplace(); + } else { + NoexceptLoc = Record.readSourceLocation(); + switch (auto returnTypeRequirementKind = Record.readInt()) { + case 0: + // No return type requirement. + Req.emplace(); + break; + case 1: { + // type-constraint + TemplateParameterList *TPL = Record.readTemplateParameterList(); + if (Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + SubstitutedConstraintExpr = + cast(Record.readExpr()); + Req.emplace(TPL); + } break; + case 2: + // Substitution failure + Req.emplace(readSubstitutionDiagnostic(Record)); + break; + } + } + if (Expr *Ex = E.dyn_cast()) + R = new (Record.getContext()) concepts::ExprRequirement( + Ex, RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req), Status, SubstitutedConstraintExpr); + else + R = new (Record.getContext()) concepts::ExprRequirement( + E.get(), + RK == concepts::Requirement::RK_Simple, NoexceptLoc, + std::move(*Req)); + } break; + case concepts::Requirement::RK_Nested: { + if (bool IsSubstitutionDiagnostic = Record.readInt()) { + R = new (Record.getContext()) concepts::NestedRequirement( + readSubstitutionDiagnostic(Record)); + break; + } + Expr *E = Record.readExpr(); + if (E->isInstantiationDependent()) + R = new (Record.getContext()) concepts::NestedRequirement(E); + else + R = new (Record.getContext()) + concepts::NestedRequirement(Record.getContext(), E, + readConstraintSatisfaction(Record)); + } break; + } + if (!R) + continue; + Requirements.push_back(R); + } + std::copy(Requirements.begin(), Requirements.end(), + E->getTrailingObjects()); + E->RBraceLoc = Record.readSourceLocation(); } void ASTStmtReader::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { @@ -3566,11 +3683,18 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) DependentCoawaitExpr(Empty); break; - case EXPR_CONCEPT_SPECIALIZATION: + case EXPR_CONCEPT_SPECIALIZATION: { unsigned numTemplateArgs = Record[ASTStmtReader::NumExprFields]; S = ConceptSpecializationExpr::Create(Context, Empty, numTemplateArgs); break; - + } + + case EXPR_REQUIRES: + unsigned numLocalParameters = Record[ASTStmtReader::NumExprFields]; + unsigned numRequirement = Record[ASTStmtReader::NumExprFields + 1]; + S = RequiresExpr::Create(Context, Empty, numLocalParameters, + numRequirement); + break; } // We hit a STMT_STOP, so we're done with this expression. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 6eba48a1abe9..2dfdcc1f4fb3 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -885,6 +885,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(DECL_NON_TYPE_TEMPLATE_PARM); RECORD(DECL_TEMPLATE_TEMPLATE_PARM); RECORD(DECL_CONCEPT); + RECORD(DECL_REQUIRES_EXPR_BODY); RECORD(DECL_TYPE_ALIAS_TEMPLATE); RECORD(DECL_STATIC_ASSERT); RECORD(DECL_CXX_BASE_SPECIFIERS); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index b2a8c118d401..459e61713ed1 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -104,6 +104,7 @@ namespace clang { void VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); void VisitTemplateDecl(TemplateDecl *D); void VisitConceptDecl(ConceptDecl *D); + void VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D); void VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); void VisitVarTemplateDecl(VarTemplateDecl *D); @@ -1481,6 +1482,10 @@ void ASTDeclWriter::VisitConceptDecl(ConceptDecl *D) { Code = serialization::DECL_CONCEPT; } +void ASTDeclWriter::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { + Code = serialization::DECL_REQUIRES_EXPR_BODY; +} + void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { VisitRedeclarable(D); diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 9231f3b2b9ba..1b118c257a4c 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTRecordWriter.h" +#include "clang/Sema/DeclSpec.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -388,19 +389,9 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { Code = serialization::EXPR_DEPENDENT_COAWAIT; } -void ASTStmtWriter::VisitConceptSpecializationExpr( - ConceptSpecializationExpr *E) { - VisitExpr(E); - ArrayRef TemplateArgs = E->getTemplateArguments(); - Record.push_back(TemplateArgs.size()); - Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); - Record.AddSourceLocation(E->getTemplateKWLoc()); - Record.AddDeclarationNameInfo(E->getConceptNameInfo()); - Record.AddDeclRef(E->getNamedConcept()); - Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); - for (const TemplateArgument &Arg : TemplateArgs) - Record.AddTemplateArgument(Arg); - const ASTConstraintSatisfaction &Satisfaction = E->getSatisfaction(); +static void +addConstraintSatisfaction(ASTRecordWriter &Record, + const ASTConstraintSatisfaction &Satisfaction) { Record.push_back(Satisfaction.IsSatisfied); if (!Satisfaction.IsSatisfied) { Record.push_back(Satisfaction.NumRecords); @@ -418,10 +409,98 @@ void ASTStmtWriter::VisitConceptSpecializationExpr( } } } +} + +static void +addSubstitutionDiagnostic( + ASTRecordWriter &Record, + const concepts::Requirement::SubstitutionDiagnostic *D) { + Record.AddString(D->SubstitutedEntity); + Record.AddSourceLocation(D->DiagLoc); + Record.AddString(D->DiagMessage); +} + +void ASTStmtWriter::VisitConceptSpecializationExpr( + ConceptSpecializationExpr *E) { + VisitExpr(E); + ArrayRef TemplateArgs = E->getTemplateArguments(); + Record.push_back(TemplateArgs.size()); + Record.AddNestedNameSpecifierLoc(E->getNestedNameSpecifierLoc()); + Record.AddSourceLocation(E->getTemplateKWLoc()); + Record.AddDeclarationNameInfo(E->getConceptNameInfo()); + Record.AddDeclRef(E->getNamedConcept()); + Record.AddASTTemplateArgumentListInfo(E->getTemplateArgsAsWritten()); + for (const TemplateArgument &Arg : TemplateArgs) + Record.AddTemplateArgument(Arg); + if (!E->isValueDependent()) + addConstraintSatisfaction(Record, E->getSatisfaction()); Code = serialization::EXPR_CONCEPT_SPECIALIZATION; } +void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) { + VisitExpr(E); + Record.push_back(E->getLocalParameters().size()); + Record.push_back(E->getRequirements().size()); + Record.AddSourceLocation(E->RequiresExprBits.RequiresKWLoc); + Record.push_back(E->RequiresExprBits.IsSatisfied); + Record.AddDeclRef(E->getBody()); + for (ParmVarDecl *P : E->getLocalParameters()) + Record.AddDeclRef(P); + for (concepts::Requirement *R : E->getRequirements()) { + if (auto *TypeReq = dyn_cast(R)) { + Record.push_back(concepts::Requirement::RK_Type); + Record.push_back(TypeReq->Status); + if (TypeReq->Status == concepts::TypeRequirement::SS_SubstitutionFailure) + addSubstitutionDiagnostic(Record, TypeReq->getSubstitutionDiagnostic()); + else + Record.AddTypeSourceInfo(TypeReq->getType()); + } else if (auto *ExprReq = dyn_cast(R)) { + Record.push_back(ExprReq->getKind()); + Record.push_back(ExprReq->Status); + if (ExprReq->isExprSubstitutionFailure()) { + addSubstitutionDiagnostic(Record, + ExprReq->Value.get()); + } else + Record.AddStmt(ExprReq->Value.get()); + if (ExprReq->getKind() == concepts::Requirement::RK_Compound) { + Record.AddSourceLocation(ExprReq->NoexceptLoc); + const auto &RetReq = ExprReq->getReturnTypeRequirement(); + if (RetReq.isSubstitutionFailure()) { + Record.push_back(2); + addSubstitutionDiagnostic(Record, RetReq.getSubstitutionDiagnostic()); + } else if (RetReq.isTypeConstraint()) { + Record.push_back(1); + Record.AddTemplateParameterList( + RetReq.getTypeConstraintTemplateParameterList()); + if (ExprReq->Status >= + concepts::ExprRequirement::SS_ConstraintsNotSatisfied) + Record.AddStmt( + ExprReq->getReturnTypeRequirementSubstitutedConstraintExpr()); + } else { + assert(RetReq.isEmpty()); + Record.push_back(0); + } + } + } else { + auto *NestedReq = cast(R); + Record.push_back(concepts::Requirement::RK_Nested); + Record.push_back(NestedReq->isSubstitutionFailure()); + if (NestedReq->isSubstitutionFailure()){ + addSubstitutionDiagnostic(Record, + NestedReq->getSubstitutionDiagnostic()); + } else { + Record.AddStmt(NestedReq->Value.get()); + if (!NestedReq->isDependent()) + addConstraintSatisfaction(Record, *NestedReq->Satisfaction); + } + } + } + Record.AddSourceLocation(E->getEndLoc()); + + Code = serialization::EXPR_REQUIRES; +} + void ASTStmtWriter::VisitCapturedStmt(CapturedStmt *S) { VisitStmt(S); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f917a4c8637b..b542cf2c0303 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1386,6 +1386,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::AsTypeExprClass: case Stmt::ConceptSpecializationExprClass: case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::RequiresExprClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp new file mode 100644 index 000000000000..578efb5b24ea --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +static_assert(requires { { 0 }; }); +static_assert(requires { { "aaaa" }; }); +static_assert(requires { { (0).da }; }); // expected-error{{member reference base type 'int' is not a structure or union}} + +void foo() {} +static_assert(requires { { foo() }; }); + +// Substitution failure in expression + +struct A {}; +struct B { + B operator+(const B &other) const { return other; } +}; +struct C { + C operator+(C &other) const { return other; } +}; + +template requires requires (T a, const T& b) { { a + b }; } // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}} +struct r1 {}; + +using r1i1 = r1; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}} +using r1i3 = r1; +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} + +struct D { void foo() {} }; + +template requires requires (T a) { { a.foo() }; } // expected-note{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} expected-note{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} expected-note{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}} +struct r2 {}; + +using r2i1 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} +using r2i2 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}} +using r2i3 = r2; +using r2i4 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} + +template requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +struct r3 {}; + +using r3i1 = r3; +using r3i2 = r3; +using r3i3 = r3; +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} + +// Non-dependent expressions + +template requires requires (T t) { { 0 }; { "a" }; { (void)'a' }; } +struct r4 {}; + +using r4i1 = r4; +using r4i2 = r4; +using r4i3 = r4; + +// Noexcept requirement +void maythrow() { } +static_assert(!requires { { maythrow() } noexcept; }); +static_assert(requires { { 1 } noexcept; }); + +struct E { void operator++(int) noexcept; }; +struct F { void operator++(int); }; + +template requires requires (T t) { { t++ } noexcept; } // expected-note{{because 't ++' may throw an exception}} +struct r5 {}; + +using r5i1 = r5; +using r5i2 = r5; +using r5i2 = r5; // expected-error{{constraints not satisfied for class template 'r5' [with T = F]}} + +template requires requires (T t) { { t.foo() } noexcept; } // expected-note{{because 't.foo()' would be invalid: no member named 'foo' in 'E'}} +struct r6 {}; + +using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with T = E]}} + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +template +concept Same = is_same_v; + +template +concept Large = sizeof(T) >= 4; // expected-note{{because 'sizeof(short) >= 4' (2 >= 4) evaluated to false}} + +template requires requires (T t) { { t } -> Large; } // expected-note{{because 'decltype(t)' (aka 'short') does not satisfy 'Large':}} +struct r7 {}; + +using r7i1 = r7; +using r7i2 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = short]}} + +template requires requires (T t) { { t } -> Same; } +struct r8 {}; + +using r8i1 = r8; +using r8i2 = r8; + +// Substitution failure in type constraint + +template requires requires (T t) { { t } -> Same; } // expected-note{{because 'Same' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +struct r9 {}; + +struct M { using type = M; }; + +using r9i1 = r9; +using r9i2 = r9; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}} + +// Substitution failure in both expression and return type requirement + +template requires requires (T t) { { t.foo() } -> Same; } // expected-note{{because 't.foo()' would be invalid: member reference base type 'int' is not a structure or union}} +struct r10 {}; + +using r10i = r10; // expected-error{{constraints not satisfied for class template 'r10' [with T = int]}} + +// Non-type concept in type constraint + +template +concept IsEven = (T % 2) == 0; + +template requires requires (T t) { { t } -> IsEven; } // expected-error{{concept named in type constraint is not a type concept}} +struct r11 {}; + +// C++ [expr.prim.req.compound] Example +namespace std_example { + template concept C1 = + requires(T x) { + {x++}; + }; + + template constexpr bool is_same_v = false; + template constexpr bool is_same_v = true; + + template concept same_as = is_same_v; + // expected-note@-1 {{because 'is_same_v' evaluated to false}} + + static_assert(C1); + static_assert(C1); + template struct C1_check {}; + using c1c1 = C1_check; + using c1c2 = C1_check; + + template concept C2 = + requires(T x) { + {*x} -> same_as; + // expected-note@-1{{because type constraint 'same_as' was not satisfied:}} + // expected-note@-2{{because '*x' would be invalid: indirection requires pointer operand ('int' invalid)}} + }; + + struct T1 { + using inner = int; + inner operator *() { return 0; } + }; + struct T2 { + using inner = int *; + int operator *() { return 0; } + }; + static_assert(C2); + template struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}} + using c2c1 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}} + using c2c2 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}} + + template + void g(T t) noexcept(sizeof(T) == 1) {} + + template concept C5 = + requires(T x) { + {g(x)} noexcept; // expected-note{{because 'g(x)' may throw an exception}} + }; + + static_assert(C5); + template struct C5_check {}; // expected-note{{because 'short' does not satisfy 'C5'}} + using c5 = C5_check; // expected-error{{constraints not satisfied for class template 'C5_check' [with T = short]}} +} \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp new file mode 100644 index 000000000000..ea3453201902 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/equivalence.cpp @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template constexpr bool is_same_v = false; +template constexpr bool is_same_v = true; + +template struct identity { using type = T; }; +template using identity_t = T; + +// Type requirements +template requires requires { typename identity_t; } +struct r1; +template requires requires { typename identity_t; } // expected-note{{previous template declaration is here}} +struct r1; +template requires requires { typename identity_t; } // expected-error{{requires clause differs in template redeclaration}} +struct r1; +template requires requires { typename ::identity_t; } +struct r1; + +template requires requires { typename identity::type; } +struct r2; +template requires requires { typename identity::type; } +struct r2; +template requires requires { typename ::identity::type; } // expected-note 2{{previous template declaration is here}} +struct r2; +template requires requires { typename identity::typr; } // expected-error{{requires clause differs in template redeclaration}} +struct r2; +namespace ns { + template struct identity { using type = T; }; +} +template requires requires { typename ns::identity::type; } // expected-error{{requires clause differs in template redeclaration}} +struct r2; + +template requires requires { typename T::template identity::type; } +struct r3; +template requires requires { typename U::template identity::type; } // expected-note{{previous template declaration is here}} +struct r3; +template requires requires { typename T::template identitr::type; } // expected-error{{requires clause differs in template redeclaration}} +struct r3; + +template requires requires { typename T::template temp<>; } +struct r4; +template requires requires { typename U::template temp<>; } +struct r4; + +// Expr requirements +template requires requires { 0; } // expected-note{{previous template declaration is here}} +struct r5; +template requires requires { 1; } // expected-error{{requires clause differs in template redeclaration}} +struct r5; + +template +concept C1 = true; + +template requires requires { sizeof(T); } +struct r6; +template requires requires { sizeof(U); } // expected-note{{previous template declaration is here}} +struct r6; +template requires requires { sizeof(U) - 1; } // expected-error{{requires clause differs in template redeclaration}} +struct r6; +template requires requires { { sizeof(T) }; } // expected-note 2{{previous template declaration is here}} +struct r6; +template requires requires { { sizeof(T) } noexcept; } // expected-error{{requires clause differs in template redeclaration}} +struct r6; +template requires requires { { sizeof(T) } -> C1; } // expected-error{{requires clause differs in template redeclaration}} +struct r6; + +template requires requires { { sizeof(T) } -> C1; } +struct r7; +template requires requires { { sizeof(U) } -> C1; } +struct r7; +template requires requires { { sizeof(T) } -> C1<>; } // expected-note {{previous template declaration is here}} +struct r7; +template requires requires { { sizeof(U) }; } // expected-error{{requires clause differs in template redeclaration}} +struct r7; + +template +concept C2 = true; + +template requires requires { { sizeof(T) } -> C2; } +struct r8; +template requires requires { { sizeof(U) } -> C2; } // expected-note{{previous template declaration is here}} +struct r8; +template requires requires { { sizeof(T) } -> C2; } // expected-error{{requires clause differs in template redeclaration}} +struct r8; + +// Nested requirements +template requires requires { requires sizeof(T) == 0; } +struct r9; +template requires requires { requires sizeof(U) == 0; } // expected-note{{previous template declaration is here}} +struct r9; +template requires requires { requires sizeof(T) == 1; } // expected-error{{requires clause differs in template redeclaration}} +struct r9; + +// Parameter list +template requires requires { requires true; } +struct r10; +template requires requires() { requires true; } // expected-note{{previous template declaration is here}} +struct r10; +template requires requires(T i) { requires true; } // expected-error{{requires clause differs in template redeclaration}} +struct r10; + +template requires requires(T i, T *j) { requires true; } // expected-note 2{{previous template declaration is here}} +struct r11; +template requires requires(T i) { requires true; } // expected-error{{requires clause differs in template redeclaration}} +struct r11; +template requires requires(T i, T *j, T &k) { requires true; } // expected-error{{requires clause differs in template redeclaration}} +struct r11; + +// Parameter names +template requires requires(int i) { requires sizeof(i) == 1; } +struct r12; +template requires requires(int j) { requires sizeof(j) == 1; } // expected-note 2{{previous template declaration is here}} +struct r12; +template requires requires(int k) { requires sizeof(k) == 2; } // expected-error{{requires clause differs in template redeclaration}} +struct r12; +template requires requires(const int k) { requires sizeof(k) == 1; } // expected-error{{requires clause differs in template redeclaration}} +struct r12; + +// Order of requirements +template requires requires { requires true; 0; } +struct r13; +template requires requires { requires true; 0; } // expected-note{{previous template declaration is here}} +struct r13; +template requires requires { 0; requires true; } // expected-error{{requires clause differs in template redeclaration}} +struct r13; diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp new file mode 100644 index 000000000000..f8776832d33f --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +static_assert(requires { requires true; }); + +template requires requires { requires false; } // expected-note{{because 'false' evaluated to false}} +struct r1 {}; + +using r1i = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + +template requires requires { requires sizeof(T) == 0; } // expected-note{{because 'sizeof(int) == 0' (4 == 0) evaluated to false}} +struct r2 {}; + +using r2i = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} + +template requires requires (T t) { requires sizeof(t) == 0; } // expected-note{{because 'sizeof (t) == 0' (4 == 0) evaluated to false}} +struct r3 {}; + +using r3i = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = int]}} + +template +struct X { + template requires requires (U u) { requires sizeof(u) == sizeof(T); } // expected-note{{because 'sizeof (u) == sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} + struct r4 {}; +}; + +using r4i = X::r4; // expected-error{{constraints not satisfied for class template 'r4' [with U = int]}} + +// C++ [expr.prim.req.nested] Examples +namespace std_example { + template concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}} + template concept D = + requires (T t) { + requires C1; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}} + }; + + struct T1 { char operator+() { return 'a'; } }; + static_assert(D); + template struct D_check {}; // expected-note{{because 'short' does not satisfy 'D'}} + using dc1 = D_check; // expected-error{{constraints not satisfied for class template 'D_check' [with T = short]}} + + template + concept C2 = requires (T a) { // expected-note{{'a' declared here}} + requires sizeof(a) == 4; // OK + requires a == 0; // expected-error{{constraint variable 'a' cannot be used in an evaluated context}} + }; +} \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp new file mode 100644 index 000000000000..a1a745253dd4 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/p3.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +// Examples from standard + +template +concept convertible_to = requires(T t) { U(t); }; + +template +concept R = requires (T i) { + typename T::type; + {*i} -> convertible_to; +}; + +template requires R struct S {}; + +struct T { + using type = int; + type i; + const type &operator*() { return i; } +}; + +using si = S; + +template +requires requires (T x) { x + x; } // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}} +T add(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}} + +int x = add(1, 2); +int y = add(T{}, T{}); // expected-error{{no matching function for call to 'add'}} + +template +concept C = requires (T x) { x + x; }; // expected-note{{because 'x + x' would be invalid: invalid operands to binary expression ('T' and 'T')}} +template requires C // expected-note{{because 'T' does not satisfy 'C'}} +T add2(T a, T b) { return a + b; } // expected-note{{candidate template ignored: constraints not satisfied [with T = T]}} + +int z = add2(1, 2); +int w = add2(T{}, T{}); // expected-error{{no matching function for call to 'add2'}} \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp new file mode 100644 index 000000000000..f9d8c3eed111 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/requires-expr.cpp @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +using A = int; + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +template +concept same_as = is_same_v; + +static_assert(requires { requires true; 0; typename A; + { 0 } -> same_as; }); +static_assert(is_same_v); + +// Check that requires expr is an unevaluated context. +struct Y { + int i; + static constexpr bool r = requires { i; }; +}; + +template requires requires (T t) { + requires false; // expected-note{{because 'false' evaluated to false}} + requires false; + requires requires { + requires false; + }; +} +struct r1 { }; + +using r1i = r1; +// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = int]}} + +template requires requires (T t) { + requires requires { + requires false; // expected-note{{because 'false' evaluated to false}} + }; +} +struct r2 { }; + +using r2i = r2; +// expected-error@-1 {{constraints not satisfied for class template 'r2' [with T = int]}} + +template requires requires (T t) { + requires requires { + requires true; + }; + requires true; + requires requires { + requires false; // expected-note{{because 'false' evaluated to false}} + }; +} +struct r3 { }; + +using r3i = r3; +// expected-error@-1 {{constraints not satisfied for class template 'r3' [with T = int]}} + +template +struct S { static const int s = T::value; }; + +template requires requires { T::value; S::s; } +// expected-note@-1 {{because 'T::value' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +struct r4 { }; + +using r4i = r4; +// expected-error@-1 {{constraints not satisfied for class template 'r4' [with T = int]}} \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp new file mode 100644 index 000000000000..a5e6c3057fe6 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp @@ -0,0 +1,106 @@ +// RUN: %clang_cc1 %s -I%S -std=c++2a -fconcepts-ts -verify + +namespace std { struct type_info; } + +static_assert(requires { 0; }); +static_assert(requires { "aaaa"; }); +static_assert(requires { (0).da; }); // expected-error{{member reference base type 'int' is not a structure or union}} + +struct A {}; +struct B { + B operator+(const B &other) const { return other; } +}; +struct C { + C operator+(C &other) const { return other; } +}; + +template requires requires (T a, const T& b) { a + b; } +// expected-note@-1{{because 'a + b' would be invalid: invalid operands to binary expression ('A' and 'const A')}} +// expected-note@-2{{because 'a + b' would be invalid: invalid operands to binary expression ('C' and 'const C')}} +struct r1 {}; + +using r1i1 = r1; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = A]}} +using r1i3 = r1; +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} + +struct D { void foo() {} }; + +template requires requires (T a) { a.foo(); } +// expected-note@-1{{because 'a.foo()' would be invalid: no member named 'foo' in 'A'}} +// expected-note@-2{{because 'a.foo()' would be invalid: member reference base type 'int' is not a structure or union}} +// expected-note@-3{{because 'a.foo()' would be invalid: 'this' argument to member function 'foo' has type 'const D', but function is not marked const}} +struct r2 {}; + +using r2i1 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} +using r2i2 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = A]}} +using r2i3 = r2; +using r2i4 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}} + +template requires requires { sizeof(T); } +// expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} +// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}} +struct r3 {}; + +using r3i1 = r3; +using r3i2 = r3; +using r3i3 = r3; +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}} +using r3i4 = r3; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}} + +template requires requires (T t) { 0; "a"; (void)'a'; } +struct r4 {}; + +using r4i1 = r4; +using r4i2 = r4; +using r4i3 = r4; + +template void f(T) = delete; +template requires (sizeof(T) == 1) void f(T) { } + +template requires requires(T t) { f(t); } +// expected-note@-1{{because 'f(t)' would be invalid: call to deleted function 'f'}} +struct r5 {}; + +using r5i1 = r5; +// expected-error@-1 {{constraints not satisfied for class template 'r5' [with T = int]}} +using r5i2 = r5; + +template +struct E { + struct non_default_constructible { non_default_constructible(T t) { } }; +}; + +template requires requires(T t) { typename E::non_default_constructible{}; } +// expected-note@-1 {{because 'typename E::non_default_constructible({})' would be invalid: no matching constructor for initialization of 'typename E::non_default_constructible'}} +struct r6 {}; + +using r6i1 = r6; +// expected-error@-1 {{constraints not satisfied for class template 'r6' [with T = int]}} + +template requires requires(T t) { typename E::non_default_constructible(); } +// expected-note@-1 {{because 'typename E::non_default_constructible()' would be invalid: no matching constructor for initialization of 'typename E::non_default_constructible'}} +struct r7 {}; + +using r7i1 = r7; +// expected-error@-1 {{constraints not satisfied for class template 'r7' [with T = int]}} + +// C++ [expr.prim.req.simple] Example +namespace std_example { + template concept C = + requires (T a, T b) { // expected-note{{because substituted constraint expression is ill-formed: argument may not have 'void' type}} + a + b; // expected-note{{because 'a + b' would be invalid: invalid operands to binary expression ('int *' and 'int *')}} + }; + + static_assert(C); + template struct C_check {}; // expected-note{{because 'void' does not satisfy 'C'}} expected-note{{because 'int *' does not satisfy 'C'}} + using c1c1 = C_check; // expected-error{{constraints not satisfied for class template 'C_check' [with T = void]}} + using c1c2 = C_check; // expected-error{{constraints not satisfied for class template 'C_check' [with T = int *]}} +} + +// typeid() of an expression becomes potentially evaluated if the expression is +// of a polymorphic type. +class X { virtual ~X(); }; +constexpr bool b = requires (X &x) { static_cast(nullptr); }; +// expected-error@-1{{constraint variable 'x' cannot be used in an evaluated context}} +// expected-note@-2{{'x' declared here}} diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp new file mode 100644 index 000000000000..8e402a88c403 --- /dev/null +++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp @@ -0,0 +1,194 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +using A = int; + +template using identity_t = T; // expected-note 4{{template is declared here}}} + +template struct identity { using type = T; }; +// expected-note@-1 2{{template is declared here}} + +struct C {}; + +struct D { static int type; }; // expected-note{{referenced member 'type' is declared here}} + +// Basic unqualified and global-qualified lookups + +static_assert(requires { typename A; typename ::A; }); +static_assert(requires { typename identity_t; typename ::identity_t; }); +static_assert(!requires { typename identity_t; }); // expected-error{{too many template arguments for alias template 'identity_t'}} +static_assert(!requires { typename ::identity_t; }); // expected-error{{too many template arguments for alias template 'identity_t'}} +static_assert(requires { typename identity; }); +static_assert(!requires { typename identity; }); +// expected-error@-1 {{typename specifier refers to class template; argument deduction not allowed here}} +static_assert(!requires { typename ::identity; }); +// expected-error@-1 {{typename specifier refers to class template; argument deduction not allowed here}} +static_assert(!requires { typename identity_t; }); +// expected-error@-1 {{typename specifier refers to alias template; argument deduction not allowed here}} +static_assert(!requires { typename ::identity_t; }); +// expected-error@-1 {{typename specifier refers to alias template; argument deduction not allowed here}} + +namespace ns { + using B = int; + int C = 0; + // expected-note@-1 {{referenced 'C' is declared here}} + static_assert(requires { typename A; typename B; typename ::A; }); + static_assert(!requires { typename ns::A; }); // expected-error{{no type named 'A' in namespace 'ns'}} + static_assert(!requires { typename ::B; }); // expected-error{{no type named 'B' in the global namespace}} + static_assert(requires { typename C; }); + // expected-error@-1 {{typename specifier refers to non-type 'C'}} +} + +// member type lookups + +static_assert(requires { typename identity::type; typename ::identity::type; }); +static_assert(!requires { typename identity::typr; }); // expected-error{{no type named 'typr' in 'identity'}} +static_assert(!requires { typename ::identity::typr; }); // expected-error{{no type named 'typr' in 'identity'}} + +template requires requires { typename T::type; } +// expected-note@-1 {{because 'typename T::type' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +// expected-note@-2 {{because 'typename T::type' would be invalid: no type named 'type' in 'C'}} +// expected-note@-3 {{because 'typename T::type' would be invalid: typename specifier refers to non-type member 'type' in 'D'}} +// expected-note@-4 {{in instantiation of template class 'invalid' requested here}} +// expected-note@-5 {{in instantiation of requirement here}} +// expected-note@-6 {{while substituting template arguments into constraint expression here}} +// expected-note@-7 {{because 'typename T::type' would be invalid}} +struct r1 {}; + +using r1i1 = r1>; +using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} +using r1i3 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = C]}} +using r1i4 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = D]}} + +template struct invalid { typename T::type x; }; +// expected-error@-1 {{typename specifier refers to non-type member 'type' in 'D'}} +using r1i5 = r1>; +// expected-error@-1 {{constraints not satisfied for class template 'r1' [with T = invalid]}} +// expected-note@-2 {{while checking constraint satisfaction for template 'r1 >' required here}} + +// mismatching template arguments + +template requires requires { typename identity; } // expected-note{{because 'typename identity' would be invalid: too many template arguments for class template 'identity'}} +struct r2 {}; + +using r2i1 = r2; +using r2i2 = r2; +using r2i3 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = ]}} + +namespace ns2 { + template struct identity {}; + + template requires requires { typename identity; } // expected-note 2{{because 'typename identity' would be invalid: too few template arguments for class template 'identity'}} + struct r4 {}; + + using r4i1 = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} +} + +using r4i2 = ns2::r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} + +using E = int; +template requires requires { typename E; } // expected-error{{expected ';' at end of requirement}} +struct r5v1 {}; +template requires requires { typename ::E; } // expected-error{{expected ';' at end of requirement}} +struct r5v2 {}; + +template requires (sizeof(T) == 1) +struct chars_only {}; + +template requires requires { typename chars_only; } // expected-note{{because 'typename chars_only' would be invalid: constraints not satisfied for class template 'chars_only' [with T = int]}} +struct r6 {}; + +using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with T = int]}} + +template int F = 0; // expected-note 2{{variable template 'F' declared here}} + +static_assert(!requires { typename F; }); +// expected-error@-1{{template name refers to non-type template 'F'}} +static_assert(!requires { typename ::F; }); +// expected-error@-1{{template name refers to non-type template '::F'}} + +struct G { template static T temp; }; + +template requires requires { typename T::template temp; } +// expected-note@-1{{because 'typename T::temp' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} +// expected-note@-2{{because 'typename T::temp' would be invalid: no member named 'temp' in 'D'}} +// expected-note@-3{{because 'typename T::temp' would be invalid: template name refers to non-type template 'G::template temp'}} +struct r7 {}; + +using r7i1 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = int]}} +using r7i2 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = D]}} +using r7i3 = r7; // expected-error{{constraints not satisfied for class template 'r7' [with T = G]}} + +template struct H; + +template requires requires { typename H; } +struct r8 {}; + +using r8i = r8; + +template struct I { struct incomplete; }; // expected-note{{member is declared here}} + +static_assert(!requires { I::incomplete::inner; }); // expected-error{{implicit instantiation of undefined member 'I::incomplete'}} + +template requires requires { typename I::incomplete::inner; } // expected-note{{because 'typename I::incomplete::inner' would be invalid: implicit instantiation of undefined member 'I::incomplete'}} +struct r9 {}; + +using r9i = r9; // expected-error{{constraints not satisfied for class template 'r9' [with T = int]}} + +namespace ns3 { + struct X { }; // expected-note 2{{candidate found by name lookup is 'ns3::X'}} +} + +struct X { using inner = int; }; // expected-note 2{{candidate found by name lookup is 'X'}} + +using namespace ns3; +static_assert(requires { typename X; }); // expected-error{{reference to 'X' is ambiguous}} +static_assert(requires { typename X::inner; }); // expected-error{{reference to 'X' is ambiguous}} +// expected-error@-1{{unknown type name 'inner'}} + +// naming a type template specialization in a type requirement does not require +// it to be complete and should not care about partial specializations. + +template +struct Z; + +template requires (sizeof(T) >= 1) +struct Z {}; // expected-note{{partial specialization matches [with T = int]}} + +template requires (sizeof(T) <= 4) +struct Z {}; // expected-note{{partial specialization matches [with T = int]}} + +Z x; // expected-error{{ambiguous partial specializations of 'Z'}} + +static_assert(requires { typename Z; }); + +// C++ [expr.prim.req.type] Example +namespace std_example { + template struct S; + // expected-note@-1 {{because 'typename S' would be invalid: no type named 'type' in 'std_example::has_inner}} + template using Ref = T&; // expected-note{{because 'typename Ref' would be invalid: cannot form a reference to 'void'}} + template concept C1 = + requires { + typename T::inner; + // expected-note@-1 {{because 'typename T::inner' would be invalid: type 'int' cannot be used prior to '::' because it has no members}} + // expected-note@-2 {{because 'typename T::inner' would be invalid: no type named 'inner' in 'std_example::has_type'}} + }; + template concept C2 = requires { typename S; }; + template concept C3 = requires { typename Ref; }; + + struct has_inner { using inner = int;}; + struct has_type { using type = int; }; + struct has_inner_and_type { using inner = int; using type = int; }; + + static_assert(C1 && C2 && C3); + template struct C1_check {}; + // expected-note@-1 {{because 'int' does not satisfy 'C1'}} + // expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}} + template struct C2_check {}; + // expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}} + template struct C3_check {}; + // expected-note@-1 {{because 'void' does not satisfy 'C3'}} + using c1 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}} + using c2 = C1_check; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}} + using c3 = C2_check; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}} + using c4 = C3_check; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}} +} \ No newline at end of file diff --git a/clang/test/PCH/cxx2a-requires-expr.cpp b/clang/test/PCH/cxx2a-requires-expr.cpp new file mode 100644 index 000000000000..d52d451c2d68 --- /dev/null +++ b/clang/test/PCH/cxx2a-requires-expr.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -emit-pch -std=c++2a -fconcepts-ts -o %t %s +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x ast -ast-print %t | FileCheck %s + +template +concept C = true; + +template +concept C2 = true; + +template +bool f() { + // CHECK: requires (T t) { t++; { t++ } noexcept -> C; { t++ } -> C2; typename T::a; requires T::val; }; + return requires (T t) { + t++; + { t++ } noexcept -> C; + { t++ } -> C2; + typename T::a; + requires T::val; + }; +} diff --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp new file mode 100644 index 000000000000..a53189caa5f7 --- /dev/null +++ b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp @@ -0,0 +1,141 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +bool r1 = requires () {}; +// expected-error@-1 {{a requires expression must contain at least one requirement}} + +bool r2 = requires { requires true; }; + +bool r3 = requires (int a, ...) { requires true; }; +// expected-error@-1 {{varargs not allowed in requires expression}} + +template +bool r4 = requires (T... ts) { requires true; }; + +bool r5 = requires (bool c, int d) { c; d; }; + +bool r6 = requires (bool c, int d) { c; d; } && decltype(d){}; +// expected-error@-1 {{use of undeclared identifier 'd'}} + +bool r7 = requires (bool c) { c; (requires (int d) { c; d; }); d; } && decltype(c){} && decltype(d){}; +// expected-error@-1 2{{use of undeclared identifier 'd'}} +// expected-error@-2 {{use of undeclared identifier 'c'}} + +bool r8 = requires (bool, int) { requires true; }; + +bool r9 = requires (bool a, int a) { requires true; }; +// expected-error@-1 {{redefinition of parameter 'a'}} +// expected-note@-2 {{previous declaration is here}} + +bool r10 = requires (struct new_struct { int x; } s) { requires true; }; +// expected-error@-1 {{'new_struct' cannot be defined in a parameter type}} + +bool r11 = requires (int x(1)) { requires true; }; +// expected-error@-1 {{expected parameter declarator}} +// expected-error@-2 {{expected ')'}} +// expected-note@-3 {{to match this '('}} + +bool r12 = requires (int x = 10) { requires true; }; +// expected-error@-1 {{default arguments not allowed for parameters of a requires expression}} + +bool r13 = requires (int f(int)) { requires true; }; + +bool r14 = requires (int (*f)(int)) { requires true; }; + +bool r15 = requires (10) { requires true; }; +// expected-error@-1 {{expected parameter declarator}} + +bool r16 = requires (auto x) { requires true; }; +// expected-error@-1 {{'auto' not allowed in requires expression parameter}} + +bool r17 = requires (auto [x, y]) { requires true; }; +// expected-error@-1 {{'auto' not allowed in requires expression parameter}} +// expected-error@-2 {{use of undeclared identifier 'x'}} + +using a = int; + +bool r18 = requires { typename a; }; + +bool r19 = requires { typename ::a; }; + +template struct identity { using type = T; }; + +template using identity_t = T; + +bool r20 = requires { + typename identity::type; + typename identity; + typename ::identity_t; +}; + +struct s { bool operator==(const s&); ~s(); }; + +bool r21 = requires { typename s::operator==; }; +// expected-error@-1 {{expected an identifier or template-id after '::'}} + +bool r22 = requires { typename s::~s; }; +// expected-error@-1 {{expected an identifier or template-id after '::'}} + +template +bool r23 = requires { typename identity::temp; }; +// expected-error@-1 {{use 'template' keyword to treat 'temp' as a dependent template name}} + +template +bool r24 = requires { + typename identity::template temp; + typename identity::template temp; // expected-error{{expected an identifier or template-id after '::'}} +}; + +bool r25 = requires { ; }; +// expected-error@-1 {{expected expression}} + +bool r26 = requires { {}; }; +// expected-error@-1 {{expected expression}} + +bool r27 = requires { { 0 } noexcept; }; + +bool r28 = requires { { 0 } noexcept noexcept; }; +// expected-error@-1 {{expected '->' before expression type requirement}} +// expected-error@-2 {{expected concept name with optional arguments}} + +template +concept C1 = true; + +template +concept C2 = true; + +bool r29 = requires { { 0 } noexcept C1; }; +// expected-error@-1 {{expected '->' before expression type requirement}} + +bool r30 = requires { { 0 } noexcept -> C2; }; + +template +T i1 = 0; + +bool r31 = requires { requires false, 1; }; +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r32 = requires { 0 noexcept; }; +// expected-error@-1 {{'noexcept' can only be used in a compound requirement (with '{' '}' around the expression)}} + +bool r33 = requires { 0 int; }; +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r34 = requires { requires true }; +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r35 = requires (bool b) { requires sizeof(b) == 1; }; + +void r36(bool b) requires requires { 1 } {} +// expected-error@-1 {{expected ';' at end of requirement}} + +bool r37 = requires { requires { 1; }; }; +// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}} + +bool r38 = requires { requires () { 1; }; }; +// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}} + +bool r39 = requires { requires (int i) { i; }; }; +// expected-warning@-1 {{this requires expression will only be checked for syntactic validity; did you intend to place it in a nested requirement? (add another 'requires' before the expression)}} + +bool r40 = requires { requires (); }; +// expected-error@-1 {{expected expression}} diff --git a/clang/test/SemaTemplate/instantiate-requires-expr.cpp b/clang/test/SemaTemplate/instantiate-requires-expr.cpp new file mode 100644 index 000000000000..3304fd25cdc9 --- /dev/null +++ b/clang/test/SemaTemplate/instantiate-requires-expr.cpp @@ -0,0 +1,216 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify -Wno-unused-value + +template +constexpr bool is_same_v = false; + +template +constexpr bool is_same_v = true; + +// We use a hack in this file to make the compiler print out the requires +// expression after it has been instantiated - we put false_v as +// the requires clause of a class template, then instantiate the template. +// The requirement will not be satisfied, and the explaining diagnostic will +// print out false_v in its raw form (the false_v serves to +// prevent the diagnostic from elaborating on why the requires expr wasn't +// satisfied). + +template +constexpr bool false_v = false; + +template +using void_t = void; + +// Check that requires parameters are instantiated correctly. + +template requires +false_v; }> +// expected-note@-1 {{because 'false_v; }>' evaluated to false}} +// expected-note@-2 {{because 'false_v; }>' evaluated to false}} +struct r1 {}; + +using r1i1 = r1; // expected-error {{constraints not satisfied for class template 'r1' [with T = int]}} +using r1i2 = r1; // expected-error {{constraints not satisfied for class template 'r1' [with T = char]}} + +// Check that parameter packs work. + +template requires +false_v +// expected-note@-1 {{because 'false_v'}} +// expected-note@-2 {{because 'false_v' evaluated to false}} +struct r2 {}; + +using r2i1 = r2; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = ]}} +using r2i2 = r2; // expected-error {{constraints not satisfied for class template 'r2' [with Ts = ]}} + +template requires +false_v<(requires (Ts ts) {requires sizeof(ts) != 0;} && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +// expected-note@-2 {{because 'false_v' evaluated to false}} +struct r3 {}; + +using r3i1 = r3; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = ]}} +using r3i2 = r3; // expected-error {{constraints not satisfied for class template 'r3' [with Ts = ]}} + +template +struct identity { using type = T; }; + +namespace type_requirement { + struct A {}; + + // check that nested name specifier is instantiated correctly. + template requires false_v // expected-note{{because 'false_v::type; }>' evaluated to false}} + struct r1 {}; + + using r1i = r1>; // expected-error{{constraints not satisfied for class template 'r1' [with T = identity]}} + + // check that template argument list is instantiated correctly. + template + struct contains_template { + template requires is_same_v, U> + using temp = int; + }; + + template requires + false_v; }> + // expected-note@-1 {{because 'false_v::temp >; }>' evaluated to false}} + // expected-note@-2 {{because 'false_v::temp >; }>' evaluated to false}} + struct r2 {}; + + using r2i1 = r2>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template]}} + using r2i2 = r2>; // expected-error{{constraints not satisfied for class template 'r2' [with T = type_requirement::contains_template]}} + + // substitution error occurs, then requires expr is instantiated again + + template + struct a { + template requires (requires { typename T::a::a; }, false) + // expected-note@-1{{because 'requires { <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // Parameter pack inside expr + template requires + false_v<(requires { typename Ts::type; } && ...)> + // expected-note@-1 {{because 'false_v::type; } && requires { typename identity::type; } && requires { <>; }>' evaluated to false}} + struct r5 {}; + + using r5i = r5, identity, short>; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = , identity, short>]}} + template requires + false_v<(requires { typename void_t; } && ...)> // expected-note{{because 'false_v; } && requires { typename void_t; }>' evaluated to false}} + struct r6 {}; + + using r6i = r6; // expected-error{{constraints not satisfied for class template 'r6' [with Ts = ]}} + + template requires + false_v<(requires { typename Ts::template aaa; } && ...)> + // expected-note@-1 {{because 'false_v>; } && requires { <>; }>' evaluated to false}} + struct r7 {}; + + using r7i = r7; // expected-error{{constraints not satisfied for class template 'r7' [with Ts = ]}} +} + +namespace expr_requirement { + // check that compound/simple requirements are instantiated correctly. + + template requires false_v + // expected-note@-1 {{because 'false_v' evaluated to false}} + // expected-note@-2 {{because 'false_v>; { sizeof(T) }; }>' evaluated to false}} + struct r1 {}; + + using r1i1 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + using r1i2 = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = void]}} + + // substitution error occurs in expr, then expr is instantiated again. + + template + struct a { + template requires (requires { sizeof(T::a); }, false) // expected-note{{because 'requires { <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // check that the return-type-requirement is instantiated correctly. + + template + concept C1 = is_same_v; + + template requires false_v C1; }> + // expected-note@-1 {{because 'false_v C1; }>' evaluated to false}} + // expected-note@-2 {{because 'false_v C1; }>' evaluated to false}} + struct r2 {}; + + using r2i1 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = int]}} + using r2i2 = r2; // expected-error{{constraints not satisfied for class template 'r2' [with T = double]}} + + + // substitution error occurs in return type requirement, then requires expr is + // instantiated again. + + template + struct b { + template requires (requires { { 0 } -> C1; }, false) // expected-note{{because 'requires { { 0 } -> <>; } , false' evaluated to false}} + struct r {}; + }; + + using bri = b::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + + template requires + false_v<(requires { { 0 } noexcept -> C1; } && ...)> + // expected-note@-1 {{because 'false_v C1; } && requires { { 0 } noexcept -> C1; }>' evaluated to false}} + struct r3 {}; + + using r3i = r3; // expected-error{{constraints not satisfied for class template 'r3' [with Ts = ]}} +} + +namespace nested_requirement { + // check that constraint expression is instantiated correctly + template requires false_v // expected-note{{because 'false_v' evaluated to false}} + struct r1 {}; + + using r1i = r1; // expected-error{{constraints not satisfied for class template 'r1' [with T = int]}} + + // substitution error occurs in expr, then expr is instantiated again. + template + struct a { + template requires + (requires { requires sizeof(T::a) == 0; }, false) // expected-note{{because 'requires { requires <>; } , false' evaluated to false}} + struct r {}; + }; + + using ari = a::r; // expected-error{{constraints not satisfied for class template 'r' [with U = short]}} + + // Parameter pack inside expr + template requires + false_v<(requires { requires sizeof(Ts) == 0; } && ...)> + // expected-note@-1 {{because 'false_v' evaluated to false}} + struct r2 {}; + + using r2i = r2; // expected-error{{constraints not satisfied for class template 'r2' [with Ts = ]}} +} + +// Parameter pack inside multiple requirements +template requires +false_v<(requires { requires sizeof(Ts) == 0; sizeof(Ts); } && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +struct r4 {}; + +using r4i = r4; // expected-error{{constraints not satisfied for class template 'r4' [with Ts = ]}} + +template requires +false_v<(requires(Ts t) { requires sizeof(t) == 0; t++; } && ...)> +// expected-note@-1 {{because 'false_v' evaluated to false}} +struct r5 {}; + +using r5i = r5; // expected-error{{constraints not satisfied for class template 'r5' [with Ts = ]}} + +template requires +false_v<(requires(T t) { T{t}; })> // T{t} creates an "UnevaluatedList" context. +// expected-note@-1 {{because 'false_v<(requires (int t) { int{t}; })>' evaluated to false}} +struct r6 {}; + +using r6i = r6; +// expected-error@-1 {{constraints not satisfied for class template 'r6' [with T = int]}} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 6f3490471de5..9a09fca5e269 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -6340,6 +6340,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::UsingPack: case Decl::Concept: case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: return C; // Declaration kinds that don't make any sense here, but are diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 3632e0f85d10..04b713c68b80 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -257,6 +257,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::TypeTraitExprClass: case Stmt::CoawaitExprClass: case Stmt::ConceptSpecializationExprClass: + case Stmt::RequiresExprClass: case Stmt::DependentCoawaitExprClass: case Stmt::CoyieldExprClass: case Stmt::CXXBindTemporaryExprClass: From fe039ecc2d7467ee02a30a9b32d99256e468a671 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Sun, 19 Jan 2020 00:45:25 +0200 Subject: [PATCH 046/374] [Concepts] Fix name-type conflict compilation issues D50360 caused some platforms to not compile due to a parameter with the name of a type. Rename the parameter. (cherry picked from commit e68c1e00eba4ae64d38e62eebebd581e3d3d6bd4) --- clang/lib/Parse/ParseDecl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 178cb1b661c7..065a82b9298a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -6612,7 +6612,7 @@ void Parser::ParseFunctionDeclaratorIdentifierList( /// [C++11] attribute-specifier-seq parameter-declaration /// void Parser::ParseParameterDeclarationClause( - DeclaratorContext DeclaratorContext, + DeclaratorContext DeclaratorCtx, ParsedAttributes &FirstArgAttrs, SmallVectorImpl &ParamInfo, SourceLocation &EllipsisLoc) { @@ -6661,9 +6661,9 @@ void Parser::ParseParameterDeclarationClause( // "LambdaExprParameterContext", because we must accept either // 'declarator' or 'abstract-declarator' here. Declarator ParmDeclarator( - DS, DeclaratorContext == DeclaratorContext::RequiresExprContext + DS, DeclaratorCtx == DeclaratorContext::RequiresExprContext ? DeclaratorContext::RequiresExprContext - : DeclaratorContext == DeclaratorContext::LambdaExprContext + : DeclaratorCtx == DeclaratorContext::LambdaExprContext ? DeclaratorContext::LambdaExprParameterContext : DeclaratorContext::PrototypeContext); ParseDeclarator(ParmDeclarator); @@ -6719,7 +6719,7 @@ void Parser::ParseParameterDeclarationClause( SourceLocation EqualLoc = Tok.getLocation(); // Parse the default argument - if (DeclaratorContext == DeclaratorContext::MemberContext) { + if (DeclaratorCtx == DeclaratorContext::MemberContext) { // If we're inside a class definition, cache the tokens // corresponding to the default argument. We'll actually parse // them when we see the end of the class definition. From b597c9e46cb5993edbb872586022dd9ca36cfd8d Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 02:41:37 +0200 Subject: [PATCH 047/374] [clang-tidy] Fix check for generic lambda invented template parameters clang-tidy previously relied on there being no identifier for a TemplateTypeParmDecl for checking whether 'decltype(x)' should be inserted, instead of checking whether or not it is implicit. D65042 added new names for invented generic lambda template parameters, rendering that check incorrect. (cherry picked from commit 5fdad8e3f803adce501ca25118f325184e54018d) --- .../clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp index bf6f2f6ed035..8953f95159a9 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MoveForwardingReferenceCheck.cpp @@ -33,7 +33,7 @@ static void replaceMoveWithForward(const UnresolvedLookupExpr *Callee, if (CallRange.isValid()) { const std::string TypeName = - TypeParmDecl->getIdentifier() + (TypeParmDecl->getIdentifier() && !TypeParmDecl->isImplicit()) ? TypeParmDecl->getName().str() : (llvm::Twine("decltype(") + ParmVar->getName() + ")").str(); From 62709e7e49aacfc591c5f4e05be8216a0111c159 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 02:50:12 +0200 Subject: [PATCH 048/374] [Concepts] Constraint Satisfaction Caching Add a simple cache for constraint satisfaction results. Whether or not this simple caching would be permitted in final C++2a is currently being discussed but it is required for acceptable performance so we use it in the meantime, with the possibility of adding some cache invalidation mechanisms later. Differential Revision: https://reviews.llvm.org/D72552 (cherry picked from commit b933d37cd3774e5431b35e82187eebb59b1ff59e) --- clang/include/clang/AST/ASTConcept.h | 30 ++++++-- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/CC1Options.td | 3 + clang/include/clang/Sema/Sema.h | 26 +++---- clang/include/clang/Sema/TemplateDeduction.h | 1 + clang/lib/AST/ASTConcept.cpp | 10 +++ clang/lib/Frontend/CompilerInvocation.cpp | 2 + clang/lib/Sema/Sema.cpp | 16 +++- clang/lib/Sema/SemaConcept.cpp | 76 ++++++++++++------- .../SemaTemplate/cxx2a-constraint-caching.cpp | 34 +++++++++ 10 files changed, 144 insertions(+), 55 deletions(-) create mode 100644 clang/test/SemaTemplate/cxx2a-constraint-caching.cpp diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 84a611c14e0b..30c4706d2a15 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -24,9 +24,23 @@ namespace clang { class ConceptDecl; class ConceptSpecializationExpr; -/// \brief The result of a constraint satisfaction check, containing the -/// necessary information to diagnose an unsatisfied constraint. -struct ConstraintSatisfaction { +/// The result of a constraint satisfaction check, containing the necessary +/// information to diagnose an unsatisfied constraint. +class ConstraintSatisfaction : public llvm::FoldingSetNode { + // The template-like entity that 'owns' the constraint checked here (can be a + // constrained entity or a concept). + NamedDecl *ConstraintOwner = nullptr; + llvm::SmallVector TemplateArgs; + +public: + + ConstraintSatisfaction() = default; + + ConstraintSatisfaction(NamedDecl *ConstraintOwner, + ArrayRef TemplateArgs) : + ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs.begin(), + TemplateArgs.end()) { } + using SubstitutionDiagnostic = std::pair; using Detail = llvm::PointerUnion; @@ -38,9 +52,13 @@ struct ConstraintSatisfaction { /// invalid expression. llvm::SmallVector, 4> Details; - // This can leak if used in an AST node, use ASTConstraintSatisfaction - // instead. - void *operator new(size_t bytes, ASTContext &C) = delete; + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { + Profile(ID, C, ConstraintOwner, TemplateArgs); + } + + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, + NamedDecl *ConstraintOwner, + ArrayRef TemplateArgs); }; /// Pairs of unsatisfied atomic constraint expressions along with the diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 068f206f4484..b8112eb26913 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -238,6 +238,7 @@ LANGOPT(AlignedAllocation , 1, 0, "aligned allocation") LANGOPT(AlignedAllocationUnavailable, 1, 0, "aligned allocation functions are unavailable") LANGOPT(NewAlignOverride , 32, 0, "maximum alignment guaranteed by '::operator new(size_t)'") LANGOPT(ConceptsTS , 1, 0, "enable C++ Extensions for Concepts") +LANGOPT(ConceptSatisfactionCaching , 1, 1, "enable satisfaction caching for C++2a Concepts") BENIGN_LANGOPT(ModulesCodegen , 1, 0, "Modules code generation") BENIGN_LANGOPT(ModulesDebugInfo , 1, 0, "Modules debug info") BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision") diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 1d550eb15ea8..ac86cda81ee4 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -554,6 +554,9 @@ def ftest_module_file_extension_EQ : "The argument is parsed as blockname:major:minor:hashed:user info">; def fconcepts_ts : Flag<["-"], "fconcepts-ts">, HelpText<"Enable C++ Extensions for Concepts.">; +def fno_concept_satisfaction_caching : Flag<["-"], + "fno-concept-satisfaction-caching">, + HelpText<"Disable satisfaction caching for C++2a Concepts.">; let Group = Action_Group in { diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0f02c0f9d2b1..c94027cf56d7 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6200,6 +6200,9 @@ class Sema final { llvm::DenseMap NormalizationCache; + llvm::ContextualFoldingSet + SatisfactionCache; + public: const NormalizedConstraint * getNormalizedAssociatedConstraints( @@ -6226,6 +6229,8 @@ class Sema final { /// \brief Check whether the given list of constraint expressions are /// satisfied (as if in a 'conjunction') given template arguments. + /// \param Template the template-like entity that triggered the constraints + /// check (either a concept or a constrained entity). /// \param ConstraintExprs a list of constraint expressions, treated as if /// they were 'AND'ed together. /// \param TemplateArgs the list of template arguments to substitute into the @@ -6237,23 +6242,10 @@ class Sema final { /// expression. /// \returns true if an error occurred and satisfaction could not be checked, /// false otherwise. - bool CheckConstraintSatisfaction(TemplateDecl *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); - - bool CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl *TD, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); - - bool CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl *TD, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction); + bool CheckConstraintSatisfaction( + NamedDecl *Template, ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, + SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction); /// \brief Check whether the given non-dependent constraint expression is /// satisfied. Returns false and updates Satisfaction with the satisfaction diff --git a/clang/include/clang/Sema/TemplateDeduction.h b/clang/include/clang/Sema/TemplateDeduction.h index b60939c97872..f787c2689d85 100644 --- a/clang/include/clang/Sema/TemplateDeduction.h +++ b/clang/include/clang/Sema/TemplateDeduction.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_SEMA_TEMPLATEDEDUCTION_H #include "clang/Sema/Ownership.h" +#include "clang/Sema/SemaConcept.h" #include "clang/AST/ASTConcept.h" #include "clang/AST/DeclAccessPair.h" #include "clang/AST/DeclTemplate.h" diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index fc32e768d92f..66d272da7049 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" +#include "clang/Sema/SemaConcept.h" using namespace clang; ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, @@ -53,3 +54,12 @@ ASTConstraintSatisfaction::Create(const ASTContext &C, void *Mem = C.Allocate(size, alignof(ASTConstraintSatisfaction)); return new (Mem) ASTConstraintSatisfaction(C, Satisfaction); } + +void ConstraintSatisfaction::Profile( + llvm::FoldingSetNodeID &ID, const ASTContext &C, NamedDecl *ConstraintOwner, + ArrayRef TemplateArgs) { + ID.AddPointer(ConstraintOwner); + ID.AddInteger(TemplateArgs.size()); + for (auto &Arg : TemplateArgs) + Arg.Profile(ID, C); +} diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index e1e59565083b..af5113f0abae 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2853,6 +2853,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.NewAlignOverride = 0; } Opts.ConceptsTS = Args.hasArg(OPT_fconcepts_ts); + Opts.ConceptSatisfactionCaching = + !Args.hasArg(OPT_fno_concept_satisfaction_caching); Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index f8da1cb89b9d..7eb8c8d2f760 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -153,10 +153,10 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, TUKind(TUKind), NumSFINAEErrors(0), FullyCheckedComparisonCategories( static_cast(ComparisonCategoryType::Last) + 1), - AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false), - NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1), - CurrentInstantiationScope(nullptr), DisableTypoCorrection(false), - TyposCorrected(0), AnalysisWarnings(*this), + SatisfactionCache(Context), AccessCheckingSFINAE(false), + InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0), + ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr), + DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this), ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr), CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) { TUScope = nullptr; @@ -379,6 +379,14 @@ Sema::~Sema() { if (isMultiplexExternalSource) delete ExternalSource; + // Delete cached satisfactions. + std::vector Satisfactions; + Satisfactions.reserve(Satisfactions.size()); + for (auto &Node : SatisfactionCache) + Satisfactions.push_back(&Node); + for (auto *Node : Satisfactions) + delete Node; + threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); // Destroys data sharing attributes stack for OpenMP diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 93e5b4511da9..81601b09ce0d 100755 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -272,36 +272,56 @@ static bool CheckConstraintSatisfaction(Sema &S, TemplateDeclT *Template, return false; } -bool Sema::CheckConstraintSatisfaction(TemplateDecl *Template, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Template, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); -} +bool Sema::CheckConstraintSatisfaction( + NamedDecl *Template, ArrayRef ConstraintExprs, + ArrayRef TemplateArgs, SourceRange TemplateIDRange, + ConstraintSatisfaction &OutSatisfaction) { + if (ConstraintExprs.empty()) { + OutSatisfaction.IsSatisfied = true; + return false; + } -bool -Sema::CheckConstraintSatisfaction(ClassTemplatePartialSpecializationDecl* Part, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Part, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); -} + llvm::FoldingSetNodeID ID; + void *InsertPos; + ConstraintSatisfaction *Satisfaction = nullptr; + if (LangOpts.ConceptSatisfactionCaching) { + ConstraintSatisfaction::Profile(ID, Context, Template, TemplateArgs); + Satisfaction = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos); + if (Satisfaction) { + OutSatisfaction = *Satisfaction; + return false; + } + Satisfaction = new ConstraintSatisfaction(Template, TemplateArgs); + } else { + Satisfaction = &OutSatisfaction; + } + bool Failed; + if (auto *T = dyn_cast(Template)) + Failed = ::CheckConstraintSatisfaction(*this, T, ConstraintExprs, + TemplateArgs, TemplateIDRange, + *Satisfaction); + else if (auto *P = + dyn_cast(Template)) + Failed = ::CheckConstraintSatisfaction(*this, P, ConstraintExprs, + TemplateArgs, TemplateIDRange, + *Satisfaction); + else + Failed = ::CheckConstraintSatisfaction( + *this, cast(Template), + ConstraintExprs, TemplateArgs, TemplateIDRange, *Satisfaction); + if (Failed) { + if (LangOpts.ConceptSatisfactionCaching) + delete Satisfaction; + return true; + } -bool -Sema::CheckConstraintSatisfaction(VarTemplatePartialSpecializationDecl* Partial, - ArrayRef ConstraintExprs, - ArrayRef TemplateArgs, - SourceRange TemplateIDRange, - ConstraintSatisfaction &Satisfaction) { - return ::CheckConstraintSatisfaction(*this, Partial, ConstraintExprs, - TemplateArgs, TemplateIDRange, - Satisfaction); + if (LangOpts.ConceptSatisfactionCaching) { + // We cannot use InsertNode here because CheckConstraintSatisfaction might + // have invalidated it. + SatisfactionCache.InsertNode(Satisfaction); + OutSatisfaction = *Satisfaction; + } + return false; } bool Sema::CheckConstraintSatisfaction(const Expr *ConstraintExpr, diff --git a/clang/test/SemaTemplate/cxx2a-constraint-caching.cpp b/clang/test/SemaTemplate/cxx2a-constraint-caching.cpp new file mode 100644 index 000000000000..fd95c80ff61e --- /dev/null +++ b/clang/test/SemaTemplate/cxx2a-constraint-caching.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s -fno-concept-satisfaction-caching -DNO_CACHE +// expected-no-diagnostics + +template +concept C = (f(T()), true); + +template +constexpr bool foo() { return false; } + +template + requires (f(T()), true) +constexpr bool foo() requires (f(T()), true) { return true; } + +namespace a { + struct A {}; + void f(A a); +} + +static_assert(C); +static_assert(foo()); + +namespace a { + // This makes calls to f ambiguous, but the second check will still succeed + // because the constraint satisfaction results are cached. + void f(A a, int = 2); +} +#ifdef NO_CACHE +static_assert(!C); +static_assert(!foo()); +#else +static_assert(C); +static_assert(foo()); +#endif \ No newline at end of file From 897b83842c0c53b1a11d3c3dd81c299fcb106139 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 04:06:51 +0200 Subject: [PATCH 049/374] [Concepts] Fix circular AST->Sema dependency in ASTConcept.cpp Remove inappropriate Sema include in ASTConcept.cpp introduced by D72552 for the finer-grained includes actually needed. (cherry picked from commit bb9b964072eb42a09e76fe148b36eefcfff077b2) --- clang/lib/AST/ASTConcept.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index 66d272da7049..c28a06bdf0b2 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -14,7 +14,10 @@ #include "clang/AST/ASTConcept.h" #include "clang/AST/ASTContext.h" -#include "clang/Sema/SemaConcept.h" +#include "clang/AST/Decl.h" +#include "clang/AST/TemplateBase.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" using namespace clang; ASTConstraintSatisfaction::ASTConstraintSatisfaction(const ASTContext &C, From 8ca4a61753985b65e2f76c75416ea47126b2b011 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 10:46:05 +0200 Subject: [PATCH 050/374] [Concepts] Fix incorrect recovery in TryAnnotateTypeConstraint TryAnnotateTypeConstraint would not put the scope specifier back into the token stream when faced with a non-concept name after a scope specifier. (cherry picked from commit de51559fa68049da73b696a4e89468154b12852a) --- clang/lib/Parse/ParseTemplate.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 0f7aefaa147a..2ac8be430c31 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -712,8 +712,11 @@ bool Parser::TryAnnotateTypeConstraint() { MemberOfUnknownSpecialization); assert(!MemberOfUnknownSpecialization && "Member when we only allowed namespace scope qualifiers??"); - if (!PossibleConcept || TNK != TNK_Concept_template) + if (!PossibleConcept || TNK != TNK_Concept_template) { + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); return false; + } // At this point we're sure we're dealing with a constrained parameter. It // may or may not have a template parameter list following the concept From 51a0e9fd6ae58c1b61fbbea1adf7d97371b7fe1c Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 20:43:38 +0200 Subject: [PATCH 051/374] [Concepts] Fix bug when referencing function parameters in instantiated function template requires clause Fixes bug #44613 - incorrect instantiated parameters were being added when checking instantiated function constraints (cherry picked from commit c2a250e1c43c05925fe040dc9624403af7879453) --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 +++--- .../test/SemaTemplate/instantiate-requires-clause.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index a470cfc87440..7d60298be2be 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4242,9 +4242,9 @@ bool Sema::CheckInstantiatedFunctionTemplateConstraints( MLTAL.getInnermost(), SourceRange()); if (Inst.isInvalid()) return true; - if (addInstantiatedParametersToScope(*this, Decl, - Decl->getTemplateInstantiationPattern(), - Scope, MLTAL)) + if (addInstantiatedParametersToScope( + *this, Decl, Decl->getPrimaryTemplate()->getTemplatedDecl(), + Scope, MLTAL)) return true; } diff --git a/clang/test/SemaTemplate/instantiate-requires-clause.cpp b/clang/test/SemaTemplate/instantiate-requires-clause.cpp index f36396b98db7..04b595717e6d 100644 --- a/clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ b/clang/test/SemaTemplate/instantiate-requires-clause.cpp @@ -29,3 +29,13 @@ using f31 = decltype(f3('a')); using f32 = decltype(f3(1, 'b')); using f33 = decltype(f3(1, 'b', 2)); // expected-error@-1 {{no matching function for call to 'f3'}} + +template +struct S { + template + static constexpr auto f(U const index) requires(index, true) { + return true; + } +}; + +static_assert(S::f(1)); From dd5820699b75849332c930d77beeb24417316b43 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Thu, 23 Jan 2020 09:47:55 +0200 Subject: [PATCH 052/374] [Concepts] Profile TypeConstraints in ProfileTemplateParameterList Profile TypeConstraints in ProfileTemplateParameterList so we can distinguish between partial specializations which differ in their TemplateParameterList type constraints. Recommit, now profiling the IDC so that we can deal with situations where the TemplateArgsAsWritten are nullptr (happens when canonicalizing type constraints). (cherry picked from commit 62c221b5090c2e1d3ca408bcab6f69c4d9e175b7) --- clang/lib/AST/DeclTemplate.cpp | 5 ++++- .../partial-specializations.cpp | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 95a2e26e0df8..58ce49aab400 100755 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -483,7 +483,10 @@ static void ProfileTemplateParameterList(ASTContext &C, if (const auto *TTP = dyn_cast(D)) { ID.AddInteger(1); ID.AddBoolean(TTP->isParameterPack()); - // TODO: Concepts: profile type-constraints. + ID.AddBoolean(TTP->hasTypeConstraint()); + if (const TypeConstraint *TC = TTP->getTypeConstraint()) + TC->getImmediatelyDeclaredConstraint()->Profile(ID, C, + /*Canonical=*/true); continue; } const auto *TTP = cast(D); diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp index 1ea4da29ee9f..9f3c21f99174 100644 --- a/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp +++ b/clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp @@ -31,6 +31,23 @@ namespace class_templates // expected-note@-2{{during template argument deduction for class template partial specialization 'B' [with T = int *]}} // expected-note@-3{{during template argument deduction for class template partial specialization 'B' [with T = int]}} // expected-note@-4 2{{in instantiation of template class 'class_templates::B' requested here}} + + template + concept same_as = is_same::value; + + template T> requires A::type + struct B {}; + // expected-note@-1{{previous}} + + template T> requires A::type + struct B {}; + // expected-error@-1{{redefinition}} + + template requires A::type + struct B {}; + + template T> requires A::type + struct B {}; } namespace variable_templates From 6a8cd9fc81e86916c1d8d55f52e3c7d5ccdb598a Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 02:03:05 +0200 Subject: [PATCH 053/374] [Concepts] Placeholder constraints and abbreviated templates This patch implements P1141R2 "Yet another approach for constrained declarations". General strategy for this patch was: - Expand AutoType to include optional type-constraint, reflecting the wording and easing the integration of constraints. - Replace autos in parameter type specifiers with invented parameters in GetTypeSpecTypeForDeclarator, using the same logic previously used for generic lambdas, now unified with abbreviated templates, by: - Tracking the template parameter lists in the Declarator object - Tracking the template parameter depth before parsing function declarators (at which point we can match template parameters against scope specifiers to know if we have an explicit template parameter list to append invented parameters to or not). - When encountering an AutoType in a parameter context we check a stack of InventedTemplateParameterInfo structures that contain the info required to create and accumulate invented template parameters (fields that were already present in LambdaScopeInfo, which now inherits from this class and is looked up when an auto is encountered in a lambda context). Resubmit after fixing MSAN failures caused by incomplete initialization of AutoTypeLocs in TypeSpecLocFiller. Differential Revision: https://reviews.llvm.org/D65042 (cherry picked from commit b481f028144ca91c15d1db3649ce14f174259e7e) --- clang/include/clang/AST/ASTContext.h | 7 +- clang/include/clang/AST/ASTNodeTraverser.h | 4 +- clang/include/clang/AST/DeclTemplate.h | 43 ++- clang/include/clang/AST/PropertiesBase.td | 2 + clang/include/clang/AST/RecursiveASTVisitor.h | 14 +- clang/include/clang/AST/TemplateBase.h | 7 +- clang/include/clang/AST/Type.h | 74 +++-- clang/include/clang/AST/TypeLoc.h | 138 +++++++++- clang/include/clang/AST/TypeProperties.td | 11 +- .../clang/Basic/DiagnosticParseKinds.td | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 12 +- clang/include/clang/Sema/DeclSpec.h | 72 ++++- clang/include/clang/Sema/Scope.h | 6 + clang/include/clang/Sema/ScopeInfo.h | 20 +- clang/include/clang/Sema/Sema.h | 47 +++- clang/lib/AST/ASTContext.cpp | 152 +++++----- clang/lib/AST/ASTImporter.cpp | 18 +- clang/lib/AST/ASTStructuralEquivalence.cpp | 26 +- clang/lib/AST/DeclTemplate.cpp | 45 ++- clang/lib/AST/ODRHash.cpp | 7 + clang/lib/AST/TemplateBase.cpp | 2 +- clang/lib/AST/TextNodeDumper.cpp | 5 + clang/lib/AST/Type.cpp | 36 ++- clang/lib/AST/TypeLoc.cpp | 95 +++++++ clang/lib/AST/TypePrinter.cpp | 29 +- clang/lib/Parse/ParseCXXInlineMethods.cpp | 4 +- clang/lib/Parse/ParseDecl.cpp | 73 ++++- clang/lib/Parse/ParseDeclCXX.cpp | 2 + clang/lib/Parse/ParseTemplate.cpp | 5 +- clang/lib/Parse/ParseTentative.cpp | 21 ++ clang/lib/Parse/Parser.cpp | 24 ++ clang/lib/Sema/DeclSpec.cpp | 9 + clang/lib/Sema/Sema.cpp | 15 + clang/lib/Sema/SemaDecl.cpp | 39 ++- clang/lib/Sema/SemaDeclCXX.cpp | 47 ++++ clang/lib/Sema/SemaLambda.cpp | 3 +- clang/lib/Sema/SemaTemplate.cpp | 190 ++++++++----- clang/lib/Sema/SemaTemplateDeduction.cpp | 77 +++++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 + clang/lib/Sema/SemaType.cpp | 231 +++++++++++++--- clang/lib/Sema/TreeTransform.h | 137 ++++++--- clang/lib/Serialization/ASTReader.cpp | 11 + clang/lib/Serialization/ASTReaderDecl.cpp | 22 +- clang/lib/Serialization/ASTWriter.cpp | 12 + clang/lib/Serialization/ASTWriterDecl.cpp | 4 + .../ast-dump-record-definition-data-json.cpp | 57 +++- clang/test/CXX/dcl/dcl.fct/p17.cpp | 260 ++++++++++++++++++ .../dcl.spec/dcl.type/dcl.spec.auto/p6.cpp | 44 +++ .../expr.prim.lambda.closure/p3.cpp | 6 +- clang/test/CXX/temp/temp.param/p10-2a.cpp | 25 +- .../cxx2a-placeholder-type-constraint.cpp | 26 ++ clang/test/SemaCXX/cxx1y-generic-lambdas.cpp | 2 +- .../ms-delayed-default-template-args.cpp | 3 +- 53 files changed, 1871 insertions(+), 362 deletions(-) create mode 100644 clang/test/CXX/dcl/dcl.fct/p17.cpp create mode 100644 clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp create mode 100644 clang/test/Parser/cxx2a-placeholder-type-constraint.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index fb269cef1ce8..f8403cf13c4a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -88,6 +88,7 @@ class AtomicExpr; class BlockExpr; class BuiltinTemplateDecl; class CharUnits; +class ConceptDecl; class CXXABI; class CXXConstructorDecl; class CXXMethodDecl; @@ -211,7 +212,7 @@ class ASTContext : public RefCountedBase { mutable llvm::FoldingSet ObjCObjectPointerTypes; mutable llvm::FoldingSet DependentUnaryTransformTypes; - mutable llvm::FoldingSet AutoTypes; + mutable llvm::ContextualFoldingSet AutoTypes; mutable llvm::FoldingSet DeducedTemplateSpecializationTypes; mutable llvm::FoldingSet AtomicTypes; @@ -1542,7 +1543,9 @@ class ASTContext : public RefCountedBase { /// C++11 deduced auto type. QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent, bool IsPack = false) const; + bool IsDependent, bool IsPack = false, + ConceptDecl *TypeConstraintConcept = nullptr, + ArrayRef TypeConstraintArgs ={}) const; /// C++11 deduction pattern for 'auto' type. QualType getAutoDeductType() const; diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index e0ebb020e697..9ebf64a12af5 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -548,8 +548,8 @@ class ASTNodeTraverser } void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) { - if (const auto *TC = D->getPlaceholderTypeConstraint()) - Visit(TC->getImmediatelyDeclaredConstraint()); + if (const auto *E = D->getPlaceholderTypeConstraint()) + Visit(E); if (D->hasDefaultArgument()) Visit(D->getDefaultArgument(), SourceRange(), D->getDefaultArgStorage().getInheritedFrom(), diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 7a55d04a0f35..7a9f623d8152 100755 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -1102,6 +1102,17 @@ class FunctionTemplateDecl : public RedeclarableTemplateDecl { /// template. ArrayRef getInjectedTemplateArgs(); + /// Return whether this function template is an abbreviated function template, + /// e.g. `void foo(auto x)` or `template void foo(auto x)` + bool isAbbreviated() const { + // Since the invented template parameters generated from 'auto' parameters + // are either appended to the end of the explicit template parameter list or + // form a new template paramter list, we can simply observe the last + // parameter to determine if such a thing happened. + const TemplateParameterList *TPL = getTemplateParameters(); + return TPL->getParam(TPL->size() - 1)->isImplicit(); + } + /// Merge \p Prev with our RedeclarableTemplateDecl::Common. void mergePrevDecl(FunctionTemplateDecl *Prev); @@ -1215,7 +1226,6 @@ class TemplateTypeParmDecl final : public TypeDecl, bool ParameterPack, bool HasTypeConstraint = false, Optional NumExpanded = None); - static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, unsigned ID); static TemplateTypeParmDecl *CreateDeserialized(const ASTContext &C, @@ -1374,7 +1384,8 @@ class NonTypeTemplateParmDecl final : public DeclaratorDecl, protected TemplateParmPosition, private llvm::TrailingObjects> { + std::pair, + Expr *> { friend class ASTDeclReader; friend TrailingObjects; @@ -1429,10 +1440,12 @@ class NonTypeTemplateParmDecl final ArrayRef ExpandedTInfos); static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C, - unsigned ID); + unsigned ID, + bool HasTypeConstraint); static NonTypeTemplateParmDecl *CreateDeserialized(ASTContext &C, unsigned ID, - unsigned NumExpandedTypes); + unsigned NumExpandedTypes, + bool HasTypeConstraint); using TemplateParmPosition::getDepth; using TemplateParmPosition::setDepth; @@ -1543,20 +1556,22 @@ class NonTypeTemplateParmDecl final return TypesAndInfos[I].second; } - /// Return the type-constraint in the placeholder type of this non-type + /// Return the constraint introduced by the placeholder type of this non-type /// template parameter (if any). - TypeConstraint *getPlaceholderTypeConstraint() const { - // TODO: Concepts: Implement once we have actual placeholders with type - // constraints. - return nullptr; + Expr *getPlaceholderTypeConstraint() const { + return hasPlaceholderTypeConstraint() ? *getTrailingObjects() : + nullptr; + } + + void setPlaceholderTypeConstraint(Expr *E) { + *getTrailingObjects() = E; } /// Determine whether this non-type template parameter's type has a /// placeholder with a type-constraint. bool hasPlaceholderTypeConstraint() const { - // TODO: Concepts: Implement once we have actual placeholders with type - // constraints. - return false; + auto *AT = getType()->getContainedAutoType(); + return AT && AT->isConstrained(); } /// \brief Get the associated-constraints of this template parameter. @@ -1566,8 +1581,8 @@ class NonTypeTemplateParmDecl final /// Use this instead of getPlaceholderImmediatelyDeclaredConstraint for /// concepts APIs that accept an ArrayRef of constraint expressions. void getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { - if (TypeConstraint *TC = getPlaceholderTypeConstraint()) - AC.push_back(TC->getImmediatelyDeclaredConstraint()); + if (Expr *E = getPlaceholderTypeConstraint()) + AC.push_back(E); } // Implement isa/cast/dyncast/etc. diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td index 9aacdb9fee36..ba0f237a3bc3 100644 --- a/clang/include/clang/AST/PropertiesBase.td +++ b/clang/include/clang/AST/PropertiesBase.td @@ -99,6 +99,8 @@ def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; } SubclassPropertyType<"TagDecl", DeclRef>; def TemplateDeclRef : SubclassPropertyType<"TemplateDecl", DeclRef>; + def ConceptDeclRef : + SubclassPropertyType<"ConceptDecl", DeclRef>; def TemplateTypeParmDeclRef : SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>; def TemplateTemplateParmDeclRef : diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 4633122aba48..86521d82c6ff 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1040,7 +1040,13 @@ DEF_TRAVERSE_TYPE(UnaryTransformType, { TRY_TO(TraverseType(T->getUnderlyingType())); }) -DEF_TRAVERSE_TYPE(AutoType, { TRY_TO(TraverseType(T->getDeducedType())); }) +DEF_TRAVERSE_TYPE(AutoType, { + TRY_TO(TraverseType(T->getDeducedType())); + if (T->isConstrained()) { + TRY_TO(TraverseDecl(T->getTypeConstraintConcept())); + TRY_TO(TraverseTemplateArguments(T->getArgs(), T->getNumArgs())); + } +}) DEF_TRAVERSE_TYPE(DeducedTemplateSpecializationType, { TRY_TO(TraverseTemplateName(T->getTemplateName())); TRY_TO(TraverseType(T->getDeducedType())); @@ -1287,6 +1293,12 @@ DEF_TRAVERSE_TYPELOC(UnaryTransformType, { DEF_TRAVERSE_TYPELOC(AutoType, { TRY_TO(TraverseType(TL.getTypePtr()->getDeducedType())); + if (TL.isConstrained()) { + TRY_TO(TraverseNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc())); + TRY_TO(TraverseDeclarationNameInfo(TL.getConceptNameInfo())); + for (unsigned I = 0, E = TL.getNumArgs(); I != E; ++I) + TRY_TO(TraverseTemplateArgumentLoc(TL.getArgLoc(I))); + } }) DEF_TRAVERSE_TYPELOC(DeducedTemplateSpecializationType, { diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h index 058a5bc0a067..93f7b62b8aea 100644 --- a/clang/include/clang/AST/TemplateBase.h +++ b/clang/include/clang/AST/TemplateBase.h @@ -637,7 +637,7 @@ struct ASTTemplateArgumentListInfo final } static const ASTTemplateArgumentListInfo * - Create(ASTContext &C, const TemplateArgumentListInfo &List); + Create(const ASTContext &C, const TemplateArgumentListInfo &List); }; /// Represents an explicit template argument list in C++, e.g., @@ -702,6 +702,11 @@ inline const TemplateArgument & return getArgs()[Idx]; } +inline const TemplateArgument &AutoType::getArg(unsigned Idx) const { + assert(Idx < getNumArgs() && "Template argument out of range"); + return getArgs()[Idx]; +} + } // namespace clang #endif // LLVM_CLANG_AST_TEMPLATEBASE_H diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index f5955c45fafc..abc8136653fa 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -58,6 +58,7 @@ namespace clang { class ExtQuals; class QualType; +class ConceptDecl; class TagDecl; class Type; @@ -1683,6 +1684,15 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { /// Was this placeholder type spelled as 'auto', 'decltype(auto)', /// or '__auto_type'? AutoTypeKeyword value. unsigned Keyword : 2; + + /// The number of template arguments in the type-constraints, which is + /// expected to be able to hold at least 1024 according to [implimits]. + /// However as this limit is somewhat easy to hit with template + /// metaprogramming we'd prefer to keep it as large as possible. + /// At the moment it has been left as a non-bitfield since this type + /// safely fits in 64 bits as an unsigned, so there is no reason to + /// introduce the performance impact of a bitfield. + unsigned NumArgs; }; class SubstTemplateTypeParmPackTypeBitfields { @@ -4814,8 +4824,7 @@ class SubstTemplateTypeParmPackType : public Type, public llvm::FoldingSetNode { /// Common base class for placeholders for types that get replaced by /// placeholder type deduction: C++11 auto, C++14 decltype(auto), C++17 deduced -/// class template types, and (eventually) constrained type names from the C++ -/// Concepts TS. +/// class template types, and constrained type names. /// /// These types are usually a placeholder for a deduced type. However, before /// the initializer is attached, or (usually) if the initializer is @@ -4860,18 +4869,50 @@ class DeducedType : public Type { } }; -/// Represents a C++11 auto or C++14 decltype(auto) type. -class AutoType : public DeducedType, public llvm::FoldingSetNode { +/// Represents a C++11 auto or C++14 decltype(auto) type, possibly constrained +/// by a type-constraint. +class alignas(8) AutoType : public DeducedType, public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these + ConceptDecl *TypeConstraintConcept; + AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, - bool IsDeducedAsDependent, bool IsDeducedAsPack) - : DeducedType(Auto, DeducedAsType, IsDeducedAsDependent, - IsDeducedAsDependent, IsDeducedAsPack) { - AutoTypeBits.Keyword = (unsigned)Keyword; + bool IsDeducedAsDependent, bool IsDeducedAsPack, ConceptDecl *CD, + ArrayRef TypeConstraintArgs); + + const TemplateArgument *getArgBuffer() const { + return reinterpret_cast(this+1); + } + + TemplateArgument *getArgBuffer() { + return reinterpret_cast(this+1); } public: + /// Retrieve the template arguments. + const TemplateArgument *getArgs() const { + return getArgBuffer(); + } + + /// Retrieve the number of template arguments. + unsigned getNumArgs() const { + return AutoTypeBits.NumArgs; + } + + const TemplateArgument &getArg(unsigned Idx) const; // in TemplateBase.h + + ArrayRef getTypeConstraintArguments() const { + return {getArgs(), getNumArgs()}; + } + + ConceptDecl *getTypeConstraintConcept() const { + return TypeConstraintConcept; + } + + bool isConstrained() const { + return TypeConstraintConcept != nullptr; + } + bool isDecltypeAuto() const { return getKeyword() == AutoTypeKeyword::DecltypeAuto; } @@ -4880,18 +4921,15 @@ class AutoType : public DeducedType, public llvm::FoldingSetNode { return (AutoTypeKeyword)AutoTypeBits.Keyword; } - void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getDeducedType(), getKeyword(), isDependentType(), - containsUnexpandedParameterPack()); + void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) { + Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), + getTypeConstraintConcept(), getTypeConstraintArguments()); } - static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced, - AutoTypeKeyword Keyword, bool IsDependent, bool IsPack) { - ID.AddPointer(Deduced.getAsOpaquePtr()); - ID.AddInteger((unsigned)Keyword); - ID.AddBoolean(IsDependent); - ID.AddBoolean(IsPack); - } + static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + QualType Deduced, AutoTypeKeyword Keyword, + bool IsDependent, ConceptDecl *CD, + ArrayRef Arguments); static bool classof(const Type *T) { return T->getTypeClass() == Auto; diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h index c3baaa3e4174..3fc53d823c37 100644 --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_TYPELOC_H #define LLVM_CLANG_AST_TYPELOC_H +#include "clang/AST/DeclarationName.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" @@ -34,6 +35,7 @@ namespace clang { class Attr; class ASTContext; class CXXRecordDecl; +class ConceptDecl; class Expr; class ObjCInterfaceDecl; class ObjCProtocolDecl; @@ -181,6 +183,11 @@ class TypeLoc { /// AttributedTypeLoc, for those type attributes that behave as qualifiers TypeLoc findExplicitQualifierLoc() const; + /// Get the typeloc of an AutoType whose type will be deduced for a variable + /// with an initializer of this type. This looks through declarators like + /// pointer types, but not through decltype or typedefs. + AutoTypeLoc getContainedAutoTypeLoc() const; + /// Initializes this to state that every location in this /// type is the given location. /// @@ -1923,8 +1930,137 @@ class DeducedTypeLoc : public InheritingConcreteTypeLoc {}; +struct AutoTypeLocInfo : TypeSpecLocInfo { + NestedNameSpecifierLoc NestedNameSpec; + SourceLocation TemplateKWLoc; + SourceLocation ConceptNameLoc; + NamedDecl *FoundDecl; + SourceLocation LAngleLoc; + SourceLocation RAngleLoc; +}; + class AutoTypeLoc - : public InheritingConcreteTypeLoc { + : public ConcreteTypeLoc { +public: + AutoTypeKeyword getAutoKeyword() const { + return getTypePtr()->getKeyword(); + } + + bool isConstrained() const { + return getTypePtr()->isConstrained(); + } + + const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const { + return getLocalData()->NestedNameSpec; + } + + void setNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + getLocalData()->NestedNameSpec = NNS; + } + + SourceLocation getTemplateKWLoc() const { + return getLocalData()->TemplateKWLoc; + } + + void setTemplateKWLoc(SourceLocation Loc) { + getLocalData()->TemplateKWLoc = Loc; + } + + SourceLocation getConceptNameLoc() const { + return getLocalData()->ConceptNameLoc; + } + + void setConceptNameLoc(SourceLocation Loc) { + getLocalData()->ConceptNameLoc = Loc; + } + + NamedDecl *getFoundDecl() const { + return getLocalData()->FoundDecl; + } + + void setFoundDecl(NamedDecl *D) { + getLocalData()->FoundDecl = D; + } + + ConceptDecl *getNamedConcept() const { + return getTypePtr()->getTypeConstraintConcept(); + } + + DeclarationNameInfo getConceptNameInfo() const; + + bool hasExplicitTemplateArgs() const { + return getLocalData()->LAngleLoc.isValid(); + } + + SourceLocation getLAngleLoc() const { + return this->getLocalData()->LAngleLoc; + } + + void setLAngleLoc(SourceLocation Loc) { + this->getLocalData()->LAngleLoc = Loc; + } + + SourceLocation getRAngleLoc() const { + return this->getLocalData()->RAngleLoc; + } + + void setRAngleLoc(SourceLocation Loc) { + this->getLocalData()->RAngleLoc = Loc; + } + + unsigned getNumArgs() const { + return getTypePtr()->getNumArgs(); + } + + void setArgLocInfo(unsigned i, TemplateArgumentLocInfo AI) { + getArgInfos()[i] = AI; + } + + TemplateArgumentLocInfo getArgLocInfo(unsigned i) const { + return getArgInfos()[i]; + } + + TemplateArgumentLoc getArgLoc(unsigned i) const { + return TemplateArgumentLoc(getTypePtr()->getTypeConstraintArguments()[i], + getArgLocInfo(i)); + } + + SourceRange getLocalSourceRange() const { + return{ + isConstrained() + ? (getNestedNameSpecifierLoc() + ? getNestedNameSpecifierLoc().getBeginLoc() + : (getTemplateKWLoc().isValid() + ? getTemplateKWLoc() + : getConceptNameLoc())) + : getNameLoc(), + getNameLoc() + }; + } + + void copy(AutoTypeLoc Loc) { + unsigned size = getFullDataSize(); + assert(size == Loc.getFullDataSize()); + memcpy(Data, Loc.Data, size); + } + + void initializeLocal(ASTContext &Context, SourceLocation Loc); + + unsigned getExtraLocalDataSize() const { + return getNumArgs() * sizeof(TemplateArgumentLocInfo); + } + + unsigned getExtraLocalDataAlignment() const { + return alignof(TemplateArgumentLocInfo); + } + +private: + TemplateArgumentLocInfo *getArgInfos() const { + return static_cast(getExtraLocalData()); + } }; class DeducedTemplateSpecializationTypeLoc diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 4df2e2f77e2b..3cf56e5a5629 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -395,6 +395,13 @@ let Class = AutoType in { def : Property<"keyword", AutoTypeKeyword> { let Read = [{ node->getKeyword() }]; } + def : Property<"typeConstraintConcept", Optional> { + let Read = [{ makeOptionalFromPointer( + const_cast(node->getTypeConstraintConcept())) }]; + } + def : Property<"typeConstraintArguments", Array> { + let Read = [{ node->getTypeConstraintArguments() }]; + } // FIXME: better enumerated value // Only really required when the deduced type is null def : Property<"dependence", UInt32> { @@ -406,7 +413,9 @@ let Class = AutoType in { def : Creator<[{ return ctx.getAutoType(makeNullableFromOptional(deducedType), keyword, /*isDependentWithoutDeducedType*/ dependence > 0, - /*isPackWithoutDeducedType*/ dependence > 1); + /*isPackWithoutDeducedType*/ dependence > 1, + makePointerFromOptional(typeConstraintConcept), + typeConstraintArguments); }]>; } diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index 105ed7bf6c84..04b103e3087a 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1371,6 +1371,8 @@ def err_concept_definition_not_identifier : Error< def ext_concept_legacy_bool_keyword : ExtWarn< "ISO C++2a does not permit the 'bool' keyword after 'concept'">, InGroup>; +def err_placeholder_expected_auto_or_decltype_auto : Error< + "expected 'auto' or 'decltype(auto)' after concept name">; } } // end of Parser diagnostics diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 82649ddf8ab4..7636d04a34c3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2102,11 +2102,8 @@ def err_auto_not_allowed : Error< "|in template argument|in typedef|in type alias|in function return type" "|in conversion function type|here|in lambda parameter" "|in type allocated by 'new'|in K&R-style function parameter" - "|in template parameter|in friend declaration" - "|in requires expression parameter}1">; -def err_auto_not_allowed_in_return_type_requirement : Error< - "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed in expression " - "type requirement">; + "|in template parameter|in friend declaration|in function prototype that is " + "not a function declaration|in requires expression parameter}1">; def err_dependent_deduced_tst : Error< "typename specifier refers to " "%select{class template|function template|variable template|alias template|" @@ -2655,6 +2652,9 @@ def note_ambiguous_atomic_constraints : Note< "same concept">; def note_ambiguous_atomic_constraints_similar_expression : Note< "similar constraint expression here">; +def err_unsupported_placeholder_constraint : Error< + "constrained placeholder types other than simple 'auto' on non-type template " + "parameters not supported yet">; def err_template_different_requires_clause : Error< "requires clause differs in template redeclaration">; @@ -2669,6 +2669,8 @@ def err_type_constraint_non_type_concept : Error< def err_type_constraint_missing_arguments : Error< "%0 requires more than 1 template argument; provide the remaining arguments " "explicitly to use it here">; +def err_placeholder_constraints_not_satisfied : Error< + "deduced type %0 does not satisfy %1">; // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 1222549161e4..1559b51ea77f 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -349,6 +349,7 @@ class DeclSpec { unsigned TypeSpecOwned : 1; unsigned TypeSpecPipe : 1; unsigned TypeSpecSat : 1; + unsigned ConstrainedAuto : 1; // type-qualifiers unsigned TypeQualifiers : 5; // Bitwise OR of TQ. @@ -369,6 +370,7 @@ class DeclSpec { UnionParsedType TypeRep; Decl *DeclRep; Expr *ExprRep; + TemplateIdAnnotation *TemplateIdRep; }; /// ExplicitSpecifier - Store information about explicit spicifer. @@ -413,6 +415,9 @@ class DeclSpec { static bool isExprRep(TST T) { return (T == TST_typeofExpr || T == TST_decltype); } + static bool isTemplateIdRep(TST T) { + return (T == TST_auto || T == TST_decltype_auto); + } DeclSpec(const DeclSpec &) = delete; void operator=(const DeclSpec &) = delete; @@ -430,7 +435,8 @@ class DeclSpec { TypeSpecComplex(TSC_unspecified), TypeSpecSign(TSS_unspecified), TypeSpecType(TST_unspecified), TypeAltiVecVector(false), TypeAltiVecPixel(false), TypeAltiVecBool(false), TypeSpecOwned(false), - TypeSpecPipe(false), TypeSpecSat(false), TypeQualifiers(TQ_unspecified), + TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false), + TypeQualifiers(TQ_unspecified), FS_inline_specified(false), FS_forceinline_specified(false), FS_virtual_specified(false), FS_noreturn_specified(false), Friend_specified(false), ConstexprSpecifier(CSK_unspecified), @@ -478,6 +484,7 @@ class DeclSpec { bool isTypeRep() const { return isTypeRep((TST) TypeSpecType); } bool isTypeSpecPipe() const { return TypeSpecPipe; } bool isTypeSpecSat() const { return TypeSpecSat; } + bool isConstrainedAuto() const { return ConstrainedAuto; } ParsedType getRepAsType() const { assert(isTypeRep((TST) TypeSpecType) && "DeclSpec does not store a type"); @@ -491,6 +498,11 @@ class DeclSpec { assert(isExprRep((TST) TypeSpecType) && "DeclSpec does not store an expr"); return ExprRep; } + TemplateIdAnnotation *getRepAsTemplateId() const { + assert(isTemplateIdRep((TST) TypeSpecType) && + "DeclSpec does not store a template id"); + return TemplateIdRep; + } CXXScopeSpec &getTypeSpecScope() { return TypeScope; } const CXXScopeSpec &getTypeSpecScope() const { return TypeScope; } @@ -666,6 +678,9 @@ class DeclSpec { SourceLocation TagNameLoc, const char *&PrevSpec, unsigned &DiagID, Decl *Rep, bool Owned, const PrintingPolicy &Policy); + bool SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, TemplateIdAnnotation *Rep, + const PrintingPolicy &Policy); bool SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, Expr *Rep, @@ -1831,6 +1846,14 @@ class Declarator { /// requires-clause, or null if no such clause was specified. Expr *TrailingRequiresClause; + /// If this declarator declares a template, its template parameter lists. + ArrayRef TemplateParameterLists; + + /// If the declarator declares an abbreviated function template, the innermost + /// template parameter list containing the invented and explicit template + /// parameters (if any). + TemplateParameterList *InventedTemplateParameterList; + #ifndef _MSC_VER union { #endif @@ -1861,7 +1884,8 @@ class Declarator { Redeclaration(false), Extension(false), ObjCIvar(false), ObjCWeakProperty(false), InlineStorageUsed(false), Attrs(ds.getAttributePool().getFactory()), AsmLabel(nullptr), - TrailingRequiresClause(nullptr) {} + TrailingRequiresClause(nullptr), + InventedTemplateParameterList(nullptr) {} ~Declarator() { clear(); @@ -2429,6 +2453,30 @@ class Declarator { return TrailingRequiresClause != nullptr; } + /// Sets the template parameter lists that preceded the declarator. + void setTemplateParameterLists(ArrayRef TPLs) { + TemplateParameterLists = TPLs; + } + + /// The template parameter lists that preceded the declarator. + ArrayRef getTemplateParameterLists() const { + return TemplateParameterLists; + } + + /// Sets the template parameter list generated from the explicit template + /// parameters along with any invented template parameters from + /// placeholder-typed parameters. + void setInventedTemplateParameterList(TemplateParameterList *Invented) { + InventedTemplateParameterList = Invented; + } + + /// The template parameter list generated from the explicit template + /// parameters along with any invented template parameters from + /// placeholder-typed parameters, if there were any such parameters. + TemplateParameterList * getInventedTemplateParameterList() const { + return InventedTemplateParameterList; + } + /// takeAttributes - Takes attributes from the given parsed-attributes /// set and add them to this declarator. /// @@ -2629,6 +2677,26 @@ struct LambdaIntroducer { } }; +struct InventedTemplateParameterInfo { + /// The number of parameters in the template parameter list that were + /// explicitly specified by the user, as opposed to being invented by use + /// of an auto parameter. + unsigned NumExplicitTemplateParams = 0; + + /// If this is a generic lambda or abbreviated function template, use this + /// as the depth of each 'auto' parameter, during initial AST construction. + unsigned AutoTemplateParameterDepth = 0; + + /// Store the list of the template parameters for a generic lambda or an + /// abbreviated function template. + /// If this is a generic lambda or abbreviated function template, this holds + /// the explicit template parameters followed by the auto parameters + /// converted into TemplateTypeParmDecls. + /// It can be used to construct the generic lambda or abbreviated template's + /// template parameter list during initial AST construction. + SmallVector TemplateParams; +}; + } // end namespace clang #endif // LLVM_CLANG_SEMA_DECLSPEC_H diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h index 7848df8f70d9..6133425a42a6 100644 --- a/clang/include/clang/Sema/Scope.h +++ b/clang/include/clang/Sema/Scope.h @@ -385,6 +385,12 @@ class Scope { return getFlags() & Scope::FunctionPrototypeScope; } + /// isFunctionDeclarationScope - Return true if this scope is a + /// function prototype scope. + bool isFunctionDeclarationScope() const { + return getFlags() & Scope::FunctionDeclarationScope; + } + /// isAtCatchScope - Return true if this scope is \@catch. bool isAtCatchScope() const { return getFlags() & Scope::AtCatchScope; diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 4f7534f9ef1a..3c4847a2932c 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -22,6 +22,7 @@ #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/CleanupInfo.h" +#include "clang/Sema/DeclSpec.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/MapVector.h" @@ -789,7 +790,8 @@ class CapturedRegionScopeInfo final : public CapturingScopeInfo { } }; -class LambdaScopeInfo final : public CapturingScopeInfo { +class LambdaScopeInfo final : + public CapturingScopeInfo, public InventedTemplateParameterInfo { public: /// The class that describes the lambda. CXXRecordDecl *Lambda = nullptr; @@ -823,25 +825,9 @@ class LambdaScopeInfo final : public CapturingScopeInfo { /// Packs introduced by this lambda, if any. SmallVector LocalPacks; - /// If this is a generic lambda, use this as the depth of - /// each 'auto' parameter, during initial AST construction. - unsigned AutoTemplateParameterDepth = 0; - - /// The number of parameters in the template parameter list that were - /// explicitly specified by the user, as opposed to being invented by use - /// of an auto parameter. - unsigned NumExplicitTemplateParams = 0; - /// Source range covering the explicit template parameter list (if it exists). SourceRange ExplicitTemplateParamsRange; - /// Store the list of the template parameters for a generic lambda. - /// If this is a generic lambda, this holds the explicit template parameters - /// followed by the auto parameters converted into TemplateTypeParmDecls. - /// It can be used to construct the generic lambda's template parameter list - /// during initial AST construction. - SmallVector TemplateParams; - /// If this is a generic lambda, and the template parameter /// list has been created (from the TemplateParams) then store /// a reference to it (cache it to avoid reconstructing it). diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index c94027cf56d7..a88dd2814487 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -620,6 +620,13 @@ class Sema final { /// function, block, and method scopes that are currently active. SmallVector FunctionScopes; + /// Stack containing information needed when in C++2a an 'auto' is encountered + /// in a function declaration parameter type specifier in order to invent a + /// corresponding template parameter in the enclosing abbreviated function + /// template. This information is also present in LambdaScopeInfo, stored in + /// the FunctionScopes stack. + SmallVector InventedParameterInfos; + typedef LazyVector ExtVectorDeclsType; @@ -1425,6 +1432,11 @@ class Sema final { /// Retrieve the module loader associated with the preprocessor. ModuleLoader &getModuleLoader() const; + /// Invent a new identifier for parameters of abbreviated templates. + IdentifierInfo * + InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned Index); + void emitAndClearUnusedLocalTypedefWarnings(); enum TUFragmentKind { @@ -1519,6 +1531,15 @@ class Sema final { /// WeakTopLevelDeclDecls - access to \#pragma weak-generated Decls SmallVectorImpl &WeakTopLevelDecls() { return WeakTopLevelDecl; } + /// Called before parsing a function declarator belonging to a function + /// declaration. + void ActOnStartFunctionDeclarationDeclarator(Declarator &D, + unsigned TemplateParameterDepth); + + /// Called after parsing a function declarator belonging to a function + /// declaration. + void ActOnFinishFunctionDeclarationDeclarator(Declarator &D); + void ActOnComment(SourceRange Comment); //===--------------------------------------------------------------------===// @@ -1922,6 +1943,8 @@ class Sema final { NC_FunctionTemplate, /// The name was classified as an ADL-only function template name. NC_UndeclaredTemplate, + /// The name was classified as a concept name. + NC_Concept, }; class NameClassification { @@ -1986,6 +2009,12 @@ class Sema final { return Result; } + static NameClassification Concept(TemplateName Name) { + NameClassification Result(NC_Concept); + Result.Template = Name; + return Result; + } + static NameClassification UndeclaredTemplate(TemplateName Name) { NameClassification Result(NC_UndeclaredTemplate); Result.Template = Name; @@ -2011,7 +2040,8 @@ class Sema final { TemplateName getTemplateName() const { assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate || - Kind == NC_VarTemplate || Kind == NC_UndeclaredTemplate); + Kind == NC_VarTemplate || Kind == NC_Concept || + Kind == NC_UndeclaredTemplate); return Template; } @@ -2023,6 +2053,8 @@ class Sema final { return TNK_Function_template; case NC_VarTemplate: return TNK_Var_template; + case NC_Concept: + return TNK_Concept_template; case NC_UndeclaredTemplate: return TNK_Undeclared_template; default: @@ -6882,6 +6914,10 @@ class Sema final { TemplateTypeParmDecl *ConstrainedParameter, SourceLocation EllipsisLoc); + bool AttachTypeConstraint(AutoTypeLoc TL, + NonTypeTemplateParmDecl *ConstrainedParameter, + SourceLocation EllipsisLoc); + QualType CheckNonTypeTemplateParameterType(TypeSourceInfo *&TSI, SourceLocation Loc); QualType CheckNonTypeTemplateParameterType(QualType T, SourceLocation Loc); @@ -6931,7 +6967,8 @@ class Sema final { SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, ArrayRef ParamLists, - bool IsFriend, bool &IsMemberSpecialization, bool &Invalid); + bool IsFriend, bool &IsMemberSpecialization, bool &Invalid, + bool SuppressDiagnostic = false); DeclResult CheckClassTemplate( Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, @@ -7843,10 +7880,12 @@ class Sema final { DeduceAutoResult DeduceAutoType(TypeSourceInfo *AutoType, Expr *&Initializer, QualType &Result, - Optional DependentDeductionDepth = None); + Optional DependentDeductionDepth = None, + bool IgnoreConstraints = false); DeduceAutoResult DeduceAutoType(TypeLoc AutoTypeLoc, Expr *&Initializer, QualType &Result, - Optional DependentDeductionDepth = None); + Optional DependentDeductionDepth = None, + bool IgnoreConstraints = false); void DiagnoseAutoDeductionFailure(VarDecl *VDecl, Expr *Init); bool DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose = true); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index a51429264dbe..6d1db38e36cc 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -716,6 +716,61 @@ ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID, RequiresClause->Profile(ID, C, /*Canonical=*/true); } +static Expr * +canonicalizeImmediatelyDeclaredConstraint(const ASTContext &C, Expr *IDC, + QualType ConstrainedType) { + // This is a bit ugly - we need to form a new immediately-declared + // constraint that references the new parameter; this would ideally + // require semantic analysis (e.g. template struct S {}; - the + // converted arguments of C could be an argument pack if C is + // declared as template concept C = ...). + // We don't have semantic analysis here so we dig deep into the + // ready-made constraint expr and change the thing manually. + ConceptSpecializationExpr *CSE; + if (const auto *Fold = dyn_cast(IDC)) + CSE = cast(Fold->getLHS()); + else + CSE = cast(IDC); + ArrayRef OldConverted = CSE->getTemplateArguments(); + SmallVector NewConverted; + NewConverted.reserve(OldConverted.size()); + if (OldConverted.front().getKind() == TemplateArgument::Pack) { + // The case: + // template concept C = true; + // template T> struct S; -> constraint is C<{T, int}> + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) + NewConverted.push_back(Arg); + TemplateArgument NewPack(NewConverted); + + NewConverted.clear(); + NewConverted.push_back(NewPack); + assert(OldConverted.size() == 1 && + "Template parameter pack should be the last parameter"); + } else { + assert(OldConverted.front().getKind() == TemplateArgument::Type && + "Unexpected first argument kind for immediately-declared " + "constraint"); + NewConverted.push_back(ConstrainedType); + for (auto &Arg : OldConverted.drop_front(1)) + NewConverted.push_back(Arg); + } + Expr *NewIDC = ConceptSpecializationExpr::Create( + C, NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), + CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), + CSE->getNamedConcept(), + // Actually canonicalizing a TemplateArgumentLoc is difficult so we + // simply omit the ArgsAsWritten + /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); + + if (auto *OrigFold = dyn_cast(IDC)) + NewIDC = new (C) CXXFoldExpr(OrigFold->getType(), SourceLocation(), NewIDC, + BinaryOperatorKind::BO_LAnd, + SourceLocation(), /*RHS=*/nullptr, + SourceLocation(), /*NumExpansions=*/None); + return NewIDC; +} + TemplateTemplateParmDecl * ASTContext::getCanonicalTemplateTemplateParmDecl( TemplateTemplateParmDecl *TTP) const { @@ -743,68 +798,23 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( TTP->isExpandedParameterPack() ? llvm::Optional(TTP->getNumExpansionParameters()) : None); if (const auto *TC = TTP->getTypeConstraint()) { - // This is a bit ugly - we need to form a new immediately-declared - // constraint that references the new parameter; this would ideally - // require semantic analysis (e.g. template struct S {}; - the - // converted arguments of C could be an argument pack if C is - // declared as template concept C = ...). - // We don't have semantic analysis here so we dig deep into the - // ready-made constraint expr and change the thing manually. - Expr *IDC = TC->getImmediatelyDeclaredConstraint(); - ConceptSpecializationExpr *CSE; - if (const auto *Fold = dyn_cast(IDC)) - CSE = cast(Fold->getLHS()); - else - CSE = cast(IDC); - ArrayRef OldConverted = CSE->getTemplateArguments(); - SmallVector NewConverted; - NewConverted.reserve(OldConverted.size()); - QualType ParamAsArgument(NewTTP->getTypeForDecl(), 0); - if (OldConverted.front().getKind() == TemplateArgument::Pack) { - // The case: - // template concept C = true; - // template T> struct S; -> constraint is C<{T, int}> - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.front().pack_elements().drop_front(1)) - NewConverted.push_back(Arg); - TemplateArgument NewPack(NewConverted); - - NewConverted.clear(); - NewConverted.push_back(NewPack); - assert(OldConverted.size() == 1 && - "Template parameter pack should be the last parameter"); - } else { - assert(OldConverted.front().getKind() == TemplateArgument::Type && - "Unexpected first argument kind for immediately-declared " - "constraint"); - NewConverted.push_back(ParamAsArgument); - for (auto &Arg : OldConverted.drop_front(1)) - NewConverted.push_back(Arg); - } - Expr *NewIDC = ConceptSpecializationExpr::Create(*this, - NestedNameSpecifierLoc(), /*TemplateKWLoc=*/SourceLocation(), - CSE->getConceptNameInfo(), /*FoundDecl=*/CSE->getNamedConcept(), - CSE->getNamedConcept(), - // Actually canonicalizing a TemplateArgumentLoc is difficult so we - // simply omit the ArgsAsWritten - /*ArgsAsWritten=*/nullptr, NewConverted, nullptr); - - if (auto *OrigFold = dyn_cast(IDC)) - NewIDC = new (*this) CXXFoldExpr(OrigFold->getType(), - SourceLocation(), NewIDC, - BinaryOperatorKind::BO_LAnd, - SourceLocation(), /*RHS=*/nullptr, - SourceLocation(), - /*NumExpansions=*/None); - + Expr *NewIDC = canonicalizeImmediatelyDeclaredConstraint( + *this, TC->getImmediatelyDeclaredConstraint(), + ParamAsArgument); + TemplateArgumentListInfo CanonArgsAsWritten; + if (auto *Args = TC->getTemplateArgsAsWritten()) + for (const auto &ArgLoc : Args->arguments()) + CanonArgsAsWritten.addArgument( + TemplateArgumentLoc(ArgLoc.getArgument(), + TemplateArgumentLocInfo())); NewTTP->setTypeConstraint( NestedNameSpecifierLoc(), DeclarationNameInfo(TC->getNamedConcept()->getDeclName(), SourceLocation()), /*FoundDecl=*/nullptr, // Actually canonicalizing a TemplateArgumentLoc is difficult so we // simply omit the ArgsAsWritten - CSE->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); + TC->getNamedConcept(), /*ArgsAsWritten=*/nullptr, NewIDC); } CanonParams.push_back(NewTTP); } else if (const auto *NTTP = dyn_cast(*P)) { @@ -839,6 +849,13 @@ ASTContext::getCanonicalTemplateTemplateParmDecl( NTTP->isParameterPack(), TInfo); } + if (AutoType *AT = T->getContainedAutoType()) { + if (AT->isConstrained()) { + Param->setPlaceholderTypeConstraint( + canonicalizeImmediatelyDeclaredConstraint( + *this, NTTP->getPlaceholderTypeConstraint(), T)); + } + } CanonParams.push_back(Param); } else @@ -943,7 +960,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, Builtin::Context &builtins) : ConstantArrayTypes(this_()), FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), - DependentTemplateSpecializationTypes(this_()), + DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()), SubstTemplateTemplateParmPacks(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), @@ -5124,21 +5141,29 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType, /// getAutoType - Return the uniqued reference to the 'auto' type which has been /// deduced to the given type, or to the canonical undeduced 'auto' type, or the /// canonical deduced-but-dependent 'auto' type. -QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, - bool IsDependent, bool IsPack) const { +QualType +ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, + bool IsDependent, bool IsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) const { assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack"); - if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent) + if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && + !TypeConstraintConcept && !IsDependent) return getAutoDeductType(); // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType, Keyword, IsDependent, IsPack); + AutoType::Profile(ID, *this, DeducedType, Keyword, IsDependent, + TypeConstraintConcept, TypeConstraintArgs); if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); - auto *AT = new (*this, TypeAlignment) - AutoType(DeducedType, Keyword, IsDependent, IsPack); + void *Mem = Allocate(sizeof(AutoType) + + sizeof(TemplateArgument) * TypeConstraintArgs.size(), + TypeAlignment); + auto *AT = new (Mem) AutoType(DeducedType, Keyword, IsDependent, IsPack, + TypeConstraintConcept, TypeConstraintArgs); Types.push_back(AT); if (InsertPos) AutoTypes.InsertNode(AT, InsertPos); @@ -5200,7 +5225,8 @@ QualType ASTContext::getAutoDeductType() const { if (AutoDeductTy.isNull()) AutoDeductTy = QualType( new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, - /*dependent*/false, /*pack*/false), + /*dependent*/false, /*pack*/false, + /*concept*/nullptr, /*args*/{}), 0); return AutoDeductTy; } diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 22fb67478c96..1f2ce30398c9 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1366,9 +1366,21 @@ ExpectedType ASTNodeImporter::VisitAutoType(const AutoType *T) { if (!ToDeducedTypeOrErr) return ToDeducedTypeOrErr.takeError(); - return Importer.getToContext().getAutoType(*ToDeducedTypeOrErr, - T->getKeyword(), - /*IsDependent*/false); + ExpectedDecl ToTypeConstraintConcept = import(T->getTypeConstraintConcept()); + if (!ToTypeConstraintConcept) + return ToTypeConstraintConcept.takeError(); + + SmallVector ToTemplateArgs; + ArrayRef FromTemplateArgs = T->getTypeConstraintArguments(); + if (Error Err = ImportTemplateArguments(FromTemplateArgs.data(), + FromTemplateArgs.size(), + ToTemplateArgs)) + return std::move(Err); + + return Importer.getToContext().getAutoType( + *ToDeducedTypeOrErr, T->getKeyword(), /*IsDependent*/false, + /*IsPack=*/false, cast_or_null(*ToTypeConstraintConcept), + ToTemplateArgs); } ExpectedType ASTNodeImporter::VisitInjectedClassNameType( diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index db48405055cd..91a2f3a8391b 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -729,11 +729,31 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; break; - case Type::Auto: - if (!IsStructurallyEquivalent(Context, cast(T1)->getDeducedType(), - cast(T2)->getDeducedType())) + case Type::Auto: { + auto *Auto1 = cast(T1); + auto *Auto2 = cast(T2); + if (!IsStructurallyEquivalent(Context, Auto1->getDeducedType(), + Auto2->getDeducedType())) return false; + if (Auto1->isConstrained() != Auto2->isConstrained()) + return false; + if (Auto1->isConstrained()) { + if (Auto1->getTypeConstraintConcept() != + Auto2->getTypeConstraintConcept()) + return false; + ArrayRef Auto1Args = + Auto1->getTypeConstraintArguments(); + ArrayRef Auto2Args = + Auto2->getTypeConstraintArguments(); + if (Auto1Args.size() != Auto2Args.size()) + return false; + for (unsigned I = 0, N = Auto1Args.size(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, Auto1Args[I], Auto2Args[I])) + return false; + } + } break; + } case Type::DeducedTemplateSpecialization: { const auto *DT1 = cast(T1); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 58ce49aab400..9bd3b64feb4e 100755 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -164,10 +164,15 @@ static void AdoptTemplateParameterList(TemplateParameterList *Params, void TemplateParameterList:: getAssociatedConstraints(llvm::SmallVectorImpl &AC) const { if (HasConstrainedParameters) - for (const NamedDecl *Param : *this) - if (const auto *TTP = dyn_cast(Param)) + for (const NamedDecl *Param : *this) { + if (const auto *TTP = dyn_cast(Param)) { if (const auto *TC = TTP->getTypeConstraint()) AC.push_back(TC->getImmediatelyDeclaredConstraint()); + } else if (const auto *NTTP = dyn_cast(Param)) { + if (const Expr *E = NTTP->getPlaceholderTypeConstraint()) + AC.push_back(E); + } + } if (HasRequiresClause) AC.push_back(getRequiresClause()); } @@ -687,8 +692,14 @@ NonTypeTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, bool ParameterPack, TypeSourceInfo *TInfo) { - return new (C, DC) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, - T, ParameterPack, TInfo); + AutoType *AT = + C.getLangOpts().ConceptsTS ? T->getContainedAutoType() : nullptr; + return new (C, DC, + additionalSizeToAlloc, + Expr *>(0, + AT && AT->isConstrained() ? 1 : 0)) + NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, ParameterPack, + TInfo); } NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( @@ -696,26 +707,34 @@ NonTypeTemplateParmDecl *NonTypeTemplateParmDecl::Create( SourceLocation IdLoc, unsigned D, unsigned P, IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, ArrayRef ExpandedTypes, ArrayRef ExpandedTInfos) { + AutoType *AT = TInfo->getType()->getContainedAutoType(); return new (C, DC, - additionalSizeToAlloc>( - ExpandedTypes.size())) + additionalSizeToAlloc, + Expr *>( + ExpandedTypes.size(), AT && AT->isConstrained() ? 1 : 0)) NonTypeTemplateParmDecl(DC, StartLoc, IdLoc, D, P, Id, T, TInfo, ExpandedTypes, ExpandedTInfos); } NonTypeTemplateParmDecl * -NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID) { - return new (C, ID) NonTypeTemplateParmDecl(nullptr, SourceLocation(), - SourceLocation(), 0, 0, nullptr, - QualType(), false, nullptr); +NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, + bool HasTypeConstraint) { + return new (C, ID, additionalSizeToAlloc, + Expr *>(0, + HasTypeConstraint ? 1 : 0)) + NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), + 0, 0, nullptr, QualType(), false, nullptr); } NonTypeTemplateParmDecl * NonTypeTemplateParmDecl::CreateDeserialized(ASTContext &C, unsigned ID, - unsigned NumExpandedTypes) { + unsigned NumExpandedTypes, + bool HasTypeConstraint) { auto *NTTP = - new (C, ID, additionalSizeToAlloc>( - NumExpandedTypes)) + new (C, ID, additionalSizeToAlloc, + Expr *>( + NumExpandedTypes, HasTypeConstraint ? 1 : 0)) NonTypeTemplateParmDecl(nullptr, SourceLocation(), SourceLocation(), 0, 0, nullptr, QualType(), nullptr, None, None); diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 27fdca1c4b9c..1f9ff9e407dc 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -857,6 +857,13 @@ class ODRTypeVisitor : public TypeVisitor { void VisitAutoType(const AutoType *T) { ID.AddInteger((unsigned)T->getKeyword()); + ID.AddInteger(T->isConstrained()); + if (T->isConstrained()) { + AddDecl(T->getTypeConstraintConcept()); + ID.AddInteger(T->getNumArgs()); + for (const auto &TA : T->getTypeConstraintArguments()) + Hash.AddTemplateArgument(TA); + } VisitDeducedType(T); } diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp index db16c2a06b64..6f0ebf232e77 100644 --- a/clang/lib/AST/TemplateBase.cpp +++ b/clang/lib/AST/TemplateBase.cpp @@ -561,7 +561,7 @@ const DiagnosticBuilder &clang::operator<<(const DiagnosticBuilder &DB, } const ASTTemplateArgumentListInfo * -ASTTemplateArgumentListInfo::Create(ASTContext &C, +ASTTemplateArgumentListInfo::Create(const ASTContext &C, const TemplateArgumentListInfo &List) { std::size_t size = totalSizeToAlloc(List.size()); void *Mem = C.Allocate(size, alignof(ASTTemplateArgumentListInfo)); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 965ad17fcfa5..c9b571862c19 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1201,6 +1201,11 @@ void TextNodeDumper::VisitAutoType(const AutoType *T) { OS << " decltype(auto)"; if (!T->isDeduced()) OS << " undeduced"; + if (T->isConstrained()) { + dumpDeclRef(T->getTypeConstraintConcept()); + for (const auto &Arg : T->getTypeConstraintArguments()) + VisitTemplateArgument(Arg); + } } void TextNodeDumper::VisitTemplateSpecializationType( diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index c5ad711d872e..5099494da5fd 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1114,7 +1114,9 @@ struct SimpleTransformVisitor : public TypeVisitor { return QualType(T, 0); return Ctx.getAutoType(deducedType, T->getKeyword(), - T->isDependentType()); + T->isDependentType(), /*IsPack=*/false, + T->getTypeConstraintConcept(), + T->getTypeConstraintArguments()); } // FIXME: Non-trivial to implement, but important for C++ @@ -4158,3 +4160,35 @@ void clang::FixedPointValueToString(SmallVectorImpl &Str, /*HasUnsignedPadding=*/false); APFixedPoint(Val, FXSema).toString(Str); } + +AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword, + bool IsDeducedAsDependent, bool IsDeducedAsPack, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) + : DeducedType(Auto, DeducedAsType, IsDeducedAsDependent, + IsDeducedAsDependent, IsDeducedAsPack) { + AutoTypeBits.Keyword = (unsigned)Keyword; + AutoTypeBits.NumArgs = TypeConstraintArgs.size(); + this->TypeConstraintConcept = TypeConstraintConcept; + if (TypeConstraintConcept) { + TemplateArgument *ArgBuffer = getArgBuffer(); + for (const TemplateArgument &Arg : TypeConstraintArgs) { + if (Arg.containsUnexpandedParameterPack()) + setContainsUnexpandedParameterPack(); + + new (ArgBuffer++) TemplateArgument(Arg); + } + } +} + +void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, + QualType Deduced, AutoTypeKeyword Keyword, + bool IsDependent, ConceptDecl *CD, + ArrayRef Arguments) { + ID.AddPointer(Deduced.getAsOpaquePtr()); + ID.AddInteger((unsigned)Keyword); + ID.AddBoolean(IsDependent); + ID.AddPointer(CD); + for (const TemplateArgument &Arg : Arguments) + Arg.Profile(ID, Context); +} diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 6e67ca8e0af7..665a86f2c143 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/TypeLoc.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" @@ -589,3 +590,97 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(ASTContext &Context, } } } + +DeclarationNameInfo AutoTypeLoc::getConceptNameInfo() const { + return DeclarationNameInfo(getNamedConcept()->getDeclName(), + getLocalData()->ConceptNameLoc); +} + +void AutoTypeLoc::initializeLocal(ASTContext &Context, SourceLocation Loc) { + setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + setTemplateKWLoc(Loc); + setConceptNameLoc(Loc); + setFoundDecl(nullptr); + setRAngleLoc(Loc); + setLAngleLoc(Loc); + TemplateSpecializationTypeLoc::initializeArgLocs(Context, getNumArgs(), + getTypePtr()->getArgs(), + getArgInfos(), Loc); + setNameLoc(Loc); +} + + +namespace { + + class GetContainedAutoTypeLocVisitor : + public TypeLocVisitor { + public: + using TypeLocVisitor::Visit; + + TypeLoc VisitAutoTypeLoc(AutoTypeLoc TL) { + return TL; + } + + // Only these types can contain the desired 'auto' type. + + TypeLoc VisitElaboratedTypeLoc(ElaboratedTypeLoc T) { + return Visit(T.getNamedTypeLoc()); + } + + TypeLoc VisitQualifiedTypeLoc(QualifiedTypeLoc T) { + return Visit(T.getUnqualifiedLoc()); + } + + TypeLoc VisitPointerTypeLoc(PointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitBlockPointerTypeLoc(BlockPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitReferenceTypeLoc(ReferenceTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitMemberPointerTypeLoc(MemberPointerTypeLoc T) { + return Visit(T.getPointeeLoc()); + } + + TypeLoc VisitArrayTypeLoc(ArrayTypeLoc T) { + return Visit(T.getElementLoc()); + } + + TypeLoc VisitFunctionTypeLoc(FunctionTypeLoc T) { + return Visit(T.getReturnLoc()); + } + + TypeLoc VisitParenTypeLoc(ParenTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAttributedTypeLoc(AttributedTypeLoc T) { + return Visit(T.getModifiedLoc()); + } + + TypeLoc VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc T) { + return Visit(T.getInnerLoc()); + } + + TypeLoc VisitAdjustedTypeLoc(AdjustedTypeLoc T) { + return Visit(T.getOriginalLoc()); + } + + TypeLoc VisitPackExpansionTypeLoc(PackExpansionTypeLoc T) { + return Visit(T.getPatternLoc()); + } + }; + +} // namespace + +AutoTypeLoc TypeLoc::getContainedAutoTypeLoc() const { + TypeLoc Res = GetContainedAutoTypeLocVisitor().Visit(*this); + if (Res.isNull()) + return AutoTypeLoc(); + return Res.getAs(); +} diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index c2f4baec989e..4a7e765a2bd8 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1046,6 +1046,13 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { if (!T->getDeducedType().isNull()) { printBefore(T->getDeducedType(), OS); } else { + if (T->isConstrained()) { + OS << T->getTypeConstraintConcept()->getName(); + auto Args = T->getTypeConstraintArguments(); + if (!Args.empty()) + printTemplateArgumentList(OS, Args, Policy); + OS << ' '; + } switch (T->getKeyword()) { case AutoTypeKeyword::Auto: OS << "auto"; break; case AutoTypeKeyword::DecltypeAuto: OS << "decltype(auto)"; break; @@ -1234,20 +1241,18 @@ void TypePrinter::printEnumAfter(const EnumType *T, raw_ostream &OS) {} void TypePrinter::printTemplateTypeParmBefore(const TemplateTypeParmType *T, raw_ostream &OS) { - if (IdentifierInfo *Id = T->getIdentifier()) - OS << Id->getName(); - else { - bool IsLambdaAutoParam = false; - if (auto D = T->getDecl()) { - if (auto M = dyn_cast_or_null(D->getDeclContext())) - IsLambdaAutoParam = D->isImplicit() && M->getParent()->isLambda(); + TemplateTypeParmDecl *D = T->getDecl(); + if (D && D->isImplicit()) { + if (auto *TC = D->getTypeConstraint()) { + TC->print(OS, Policy); + OS << ' '; } + OS << "auto"; + } else if (IdentifierInfo *Id = T->getIdentifier()) + OS << Id->getName(); + else + OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - if (IsLambdaAutoParam) - OS << "auto"; - else - OS << "type-parameter-" << T->getDepth() << '-' << T->getIndex(); - } spaceBeforePlaceHolder(OS); } diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index f8b5fec43800..a75965784168 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -133,7 +133,9 @@ NamedDecl *Parser::ParseCXXInlineMethodDef( LexedMethod* LM = new LexedMethod(this, FnD); getCurrentClass().LateParsedDeclarations.push_back(LM); - LM->TemplateScope = getCurScope()->isTemplateParamScope(); + LM->TemplateScope = getCurScope()->isTemplateParamScope() || + (FnD && isa(FnD) && + cast(FnD)->isAbbreviated()); CachedTokens &Toks = LM->Toks; tok::TokenKind kind = Tok.getKind(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 065a82b9298a..4af993c4527f 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2962,6 +2962,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, case Sema::NC_ContextIndependentExpr: case Sema::NC_VarTemplate: case Sema::NC_FunctionTemplate: + case Sema::NC_Concept: // Might be a redeclaration of a prior entity. break; } @@ -3193,6 +3194,18 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; } + if (Next.is(tok::annot_template_id) && + static_cast(Next.getAnnotationValue()) + ->Kind == TNK_Concept_template && + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype)) { + DS.getTypeSpecScope() = SS; + // This is a qualified placeholder-specifier, e.g., ::C auto ... + // Consume the scope annotation and continue to consume the template-id + // as a placeholder-specifier. + ConsumeAnnotationToken(); + continue; + } + if (Next.is(tok::annot_typename)) { DS.getTypeSpecScope() = SS; ConsumeAnnotationToken(); // The C++ scope. @@ -3235,6 +3248,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // C++ doesn't have implicit int. Diagnose it as a typo w.r.t. to the // typename. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; // Eat the scope spec so the identifier is current. ConsumeAnnotationToken(); ParsedAttributesWithRange Attrs(AttrFactory); @@ -3384,6 +3401,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // If this is not a typedef name, don't parse it as part of the declspec, // it must be an implicit int or an error. if (!TypeRep) { + if (TryAnnotateTypeConstraint()) + goto DoneWithDeclSpec; + if (isTypeConstraintAnnotation()) + continue; ParsedAttributesWithRange Attrs(AttrFactory); if (ParseImplicitInt(DS, nullptr, TemplateInfo, AS, DSContext, Attrs)) { if (!Attrs.empty()) { @@ -3433,9 +3454,51 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, continue; } - // type-name + // type-name or placeholder-specifier case tok::annot_template_id: { TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); + if (TemplateId->Kind == TNK_Concept_template) { + if (NextToken().is(tok::identifier)) { + Diag(Loc, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateInsertion(NextToken().getLocation(), "auto"); + // Attempt to continue as if 'auto' was placed here. + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + break; + } + if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) + goto DoneWithDeclSpec; + ConsumeAnnotationToken(); + SourceLocation AutoLoc = Tok.getLocation(); + if (TryConsumeToken(tok::kw_decltype)) { + BalancedDelimiterTracker Tracker(*this, tok::l_paren); + if (Tracker.consumeOpen()) { + // Something like `void foo(Iterator decltype i)` + Diag(Tok, diag::err_expected) << tok::l_paren; + } else { + if (!TryConsumeToken(tok::kw_auto)) { + // Something like `void foo(Iterator decltype(int) i)` + Tracker.skipToEnd(); + Diag(Tok, diag::err_placeholder_expected_auto_or_decltype_auto) + << FixItHint::CreateReplacement(SourceRange(AutoLoc, + Tok.getLocation()), + "auto"); + } else { + Tracker.consumeClose(); + } + } + ConsumedEnd = Tok.getLocation(); + // Even if something went wrong above, continue as if we've seen + // `decltype(auto)`. + isInvalid = DS.SetTypeSpecType(TST_decltype_auto, Loc, PrevSpec, + DiagID, TemplateId, Policy); + } else { + isInvalid = DS.SetTypeSpecType(TST_auto, Loc, PrevSpec, DiagID, + TemplateId, Policy); + } + break; + } + if (TemplateId->Kind != TNK_Type_template && TemplateId->Kind != TNK_Undeclared_template) { // This template-id does not refer to a type name, so we're @@ -6027,11 +6090,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { while (1) { if (Tok.is(tok::l_paren)) { + bool IsFunctionDeclaration = D.isFunctionDeclaratorAFunctionDeclaration(); // Enter function-declaration scope, limiting any declarators to the // function prototype scope, including parameter declarators. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope|Scope::DeclScope| - (D.isFunctionDeclaratorAFunctionDeclaration() + (IsFunctionDeclaration ? Scope::FunctionDeclarationScope : 0)); // The paren may be part of a C++ direct initializer, eg. "int x(1);". @@ -6050,7 +6114,12 @@ void Parser::ParseDirectDeclarator(Declarator &D) { ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); + if (IsFunctionDeclaration) + Actions.ActOnStartFunctionDeclarationDeclarator(D, + TemplateParameterDepth); ParseFunctionDeclarator(D, attrs, T, IsAmbiguous); + if (IsFunctionDeclaration) + Actions.ActOnFinishFunctionDeclarationDeclarator(D); PrototypeScope.Exit(); } else if (Tok.is(tok::l_square)) { ParseBracketDeclarator(D); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 9c7d3c566554..f872aa3a950c 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2642,6 +2642,8 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, } ParsingDeclarator DeclaratorInfo(*this, DS, DeclaratorContext::MemberContext); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(TemplateParams); VirtSpecifiers VS; // Hold late-parsed attributes so we can attach a Decl to them later. diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp index 2ac8be430c31..e96baec0780a 100644 --- a/clang/lib/Parse/ParseTemplate.cpp +++ b/clang/lib/Parse/ParseTemplate.cpp @@ -240,6 +240,8 @@ Decl *Parser::ParseSingleDeclarationAfterTemplate( // Parse the declarator. ParsingDeclarator DeclaratorInfo(*this, DS, (DeclaratorContext)Context); + if (TemplateInfo.TemplateParams) + DeclaratorInfo.setTemplateParameterLists(*TemplateInfo.TemplateParams); ParseDeclarator(DeclaratorInfo); // Error parsing the declarator? if (!DeclaratorInfo.hasName()) { @@ -601,6 +603,7 @@ Parser::TPResult Parser::isStartOfTemplateTypeParameter() { /// typename /// NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { + switch (isStartOfTemplateTypeParameter()) { case TPResult::True: // Is there just a typo in the input code? ('typedef' instead of @@ -618,7 +621,6 @@ NamedDecl *Parser::ParseTemplateParameter(unsigned Depth, unsigned Position) { } return ParseTypeParameter(Depth, Position); - case TPResult::False: break; @@ -678,7 +680,6 @@ bool Parser::isTypeConstraintAnnotation() { bool Parser::TryAnnotateTypeConstraint() { if (!getLangOpts().ConceptsTS) return false; - CXXScopeSpec SS; bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); if (ParseOptionalCXXScopeSpecifier( diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 79e96816f864..ad0a15b0c8a6 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1313,6 +1313,18 @@ class TentativeParseCCC final : public CorrectionCandidateCallback { Parser::TPResult Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, bool *InvalidAsDeclSpec) { + auto IsPlaceholderSpecifier = [&] (TemplateIdAnnotation *TemplateId, + int Lookahead) { + // We have a placeholder-constraint (we check for 'auto' or 'decltype' to + // distinguish 'C;' from 'C auto c = 1;') + return TemplateId->Kind == TNK_Concept_template && + GetLookAheadToken(Lookahead + 1).isOneOf(tok::kw_auto, tok::kw_decltype, + // If we have an identifier here, the user probably forgot the + // 'auto' in the placeholder constraint, e.g. 'C x = 2;' + // This will be diagnosed nicely later, so disambiguate as a + // declaration. + tok::identifier); + }; switch (Tok.getKind()) { case tok::identifier: { // Check for need to substitute AltiVec __vector keyword @@ -1516,6 +1528,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *InvalidAsDeclSpec = NextToken().is(tok::l_paren); return TPResult::Ambiguous; } + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/0)) + return TPResult::True; if (TemplateId->Kind != TNK_Type_template) return TPResult::False; CXXScopeSpec SS; @@ -1529,6 +1543,13 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, if (TryAnnotateTypeOrScopeToken()) return TPResult::Error; if (!Tok.is(tok::annot_typename)) { + if (Tok.is(tok::annot_cxxscope) && + NextToken().is(tok::annot_template_id)) { + TemplateIdAnnotation *TemplateId = + takeTemplateIdAnnotation(NextToken()); + if (IsPlaceholderSpecifier(TemplateId, /*Lookahead=*/1)) + return TPResult::True; + } // If the next token is an identifier or a type qualifier, then this // can't possibly be a valid expression either. if (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::identifier)) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 0194c243f93d..0b778bd24277 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -1136,6 +1136,7 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // Poison SEH identifiers so they are flagged as illegal in function bodies. PoisonSEHIdentifiersRAIIObject PoisonSEHIdentifiers(*this, true); const DeclaratorChunk::FunctionTypeInfo &FTI = D.getFunctionTypeInfo(); + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); // If this is C90 and the declspecs were completely missing, fudge in an // implicit int. We do this here because this is the only place where @@ -1262,6 +1263,15 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, // safe because we're always the sole owner. D.getMutableDeclSpec().abort(); + // With abbreviated function templates - we need to explicitly add depth to + // account for the implicit template parameter list induced by the template. + if (auto *Template = dyn_cast_or_null(Res)) + if (Template->isAbbreviated() && + Template->getTemplateParameters()->getParam(0)->isImplicit()) + // First template parameter is implicit - meaning no explicit template + // parameter list was specified. + CurTemplateDepthTracker.addDepth(1); + if (TryConsumeToken(tok::equal)) { assert(getLangOpts().CPlusPlus && "Only C++ function definitions have '='"); @@ -1732,6 +1742,20 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC) { return ANK_Error; return ANK_Success; } + case Sema::NC_Concept: { + UnqualifiedId Id; + Id.setIdentifier(Name, NameLoc); + if (Next.is(tok::less)) + // We have a concept name followed by '<'. Consume the identifier token so + // we reach the '<' and annotate it. + ConsumeToken(); + if (AnnotateTemplateIdToken( + TemplateTy::make(Classification.getTemplateName()), + Classification.getTemplateNameKind(), SS, SourceLocation(), Id, + /*AllowTypeAnnotation=*/false, /*TypeConstraint=*/true)) + return ANK_Error; + return ANK_Success; + } } // Unable to classify the name, but maybe we can annotate a scope specifier. diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index 639231c87232..94d87974624e 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -784,6 +784,15 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc, return false; } +bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, + unsigned &DiagID, TemplateIdAnnotation *Rep, + const PrintingPolicy &Policy) { + assert(T == TST_auto || T == TST_decltype_auto); + ConstrainedAuto = true; + TemplateIdRep = Rep; + return SetTypeSpecType(T, Loc, PrevSpec, DiagID, Policy); +} + bool DeclSpec::SetTypeSpecType(TST T, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 7eb8c8d2f760..9cfce5a63b1d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -52,6 +52,21 @@ SourceLocation Sema::getLocForEndOfToken(SourceLocation Loc, unsigned Offset) { ModuleLoader &Sema::getModuleLoader() const { return PP.getModuleLoader(); } +IdentifierInfo * +Sema::InventAbbreviatedTemplateParameterTypeName(IdentifierInfo *ParamName, + unsigned int Index) { + std::string InventedName; + llvm::raw_string_ostream OS(InventedName); + + if (!ParamName) + OS << "auto:" << Index + 1; + else + OS << ParamName->getName() << ":auto"; + + OS.flush(); + return &Context.Idents.get(OS.str()); +} + PrintingPolicy Sema::getPrintingPolicy(const ASTContext &Context, const Preprocessor &PP) { PrintingPolicy Policy = Context.getPrintingPolicy(); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 372f3d158597..0bf490336537 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "TreeTransform.h" #include "TypeLocBuilder.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -1153,6 +1154,10 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, return ParsedType::make(T); } + if (isa(FirstDecl)) + return NameClassification::Concept( + TemplateName(cast(FirstDecl))); + // We can have a type template here if we're classifying a template argument. if (isa(FirstDecl) && !isa(FirstDecl) && !isa(FirstDecl)) @@ -8656,11 +8661,21 @@ static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) { NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, - MultiTemplateParamsArg TemplateParamLists, + MultiTemplateParamsArg TemplateParamListsRef, bool &AddToScope) { QualType R = TInfo->getType(); assert(R->isFunctionType()); + SmallVector TemplateParamLists; + for (TemplateParameterList *TPL : TemplateParamListsRef) + TemplateParamLists.push_back(TPL); + if (TemplateParameterList *Invented = D.getInventedTemplateParameterList()) { + if (!TemplateParamLists.empty() && + Invented->getDepth() == TemplateParamLists.back()->getDepth()) + TemplateParamLists.back() = Invented; + else + TemplateParamLists.push_back(Invented); + } // TODO: consider using NameInfo for diagnostic. DeclarationNameInfo NameInfo = GetNameForDeclarator(D); @@ -8740,15 +8755,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Match up the template parameter lists with the scope specifier, then // determine whether we have a template or a template specialization. bool Invalid = false; - if (TemplateParameterList *TemplateParams = - MatchTemplateParametersToScopeSpecifier( - D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), - D.getCXXScopeSpec(), - D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId - ? D.getName().TemplateId - : nullptr, - TemplateParamLists, isFriend, isMemberSpecialization, - Invalid)) { + TemplateParameterList *TemplateParams = + MatchTemplateParametersToScopeSpecifier( + D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(), + D.getCXXScopeSpec(), + D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId + ? D.getName().TemplateId + : nullptr, + TemplateParamLists, isFriend, isMemberSpecialization, + Invalid); + if (TemplateParams) { if (TemplateParams->size() > 0) { // This is a function template @@ -8781,7 +8797,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, // For source fidelity, store the other template param lists. if (TemplateParamLists.size() > 1) { NewFD->setTemplateParameterListsInfo(Context, - TemplateParamLists.drop_back(1)); + ArrayRef(TemplateParamLists) + .drop_back(1)); } } else { // This is a function template specialization. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 9916d3be77e1..9fa5691983a1 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17386,3 +17386,50 @@ MSPropertyDecl *Sema::HandleMSProperty(Scope *S, RecordDecl *Record, return NewPD; } + +void Sema::ActOnStartFunctionDeclarationDeclarator( + Declarator &Declarator, unsigned TemplateParameterDepth) { + auto &Info = InventedParameterInfos.emplace_back(); + TemplateParameterList *ExplicitParams = nullptr; + ArrayRef ExplicitLists = + Declarator.getTemplateParameterLists(); + if (!ExplicitLists.empty()) { + bool IsMemberSpecialization, IsInvalid; + ExplicitParams = MatchTemplateParametersToScopeSpecifier( + Declarator.getBeginLoc(), Declarator.getIdentifierLoc(), + Declarator.getCXXScopeSpec(), /*TemplateId=*/nullptr, + ExplicitLists, /*IsFriend=*/false, IsMemberSpecialization, IsInvalid, + /*SuppressDiagnostic=*/true); + } + if (ExplicitParams) { + Info.AutoTemplateParameterDepth = ExplicitParams->getDepth(); + for (NamedDecl *Param : *ExplicitParams) + Info.TemplateParams.push_back(Param); + Info.NumExplicitTemplateParams = ExplicitParams->size(); + } else { + Info.AutoTemplateParameterDepth = TemplateParameterDepth; + Info.NumExplicitTemplateParams = 0; + } +} + +void Sema::ActOnFinishFunctionDeclarationDeclarator(Declarator &Declarator) { + auto &FSI = InventedParameterInfos.back(); + if (FSI.TemplateParams.size() > FSI.NumExplicitTemplateParams) { + if (FSI.NumExplicitTemplateParams != 0) { + TemplateParameterList *ExplicitParams = + Declarator.getTemplateParameterLists().back(); + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, ExplicitParams->getTemplateLoc(), + ExplicitParams->getLAngleLoc(), FSI.TemplateParams, + ExplicitParams->getRAngleLoc(), + ExplicitParams->getRequiresClause())); + } else { + Declarator.setInventedTemplateParameterList( + TemplateParameterList::Create( + Context, SourceLocation(), SourceLocation(), FSI.TemplateParams, + SourceLocation(), /*RequiresClause=*/nullptr)); + } + } + InventedParameterInfos.pop_back(); +} diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp index c2d14a44f53d..ae89b146c409 100644 --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -791,7 +791,8 @@ QualType Sema::buildLambdaInitCaptureInitialization( // deduce against. QualType DeductType = Context.getAutoDeductType(); TypeLocBuilder TLB; - TLB.pushTypeSpec(DeductType).setNameLoc(Loc); + AutoTypeLoc TL = TLB.push(DeductType); + TL.setNameLoc(Loc); if (ByRef) { DeductType = BuildReferenceType(DeductType, true, Loc, Id); assert(!DeductType.isNull() && "can't build reference to auto"); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 661a66246a53..44b58e1a80af 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1088,6 +1088,50 @@ bool Sema::ActOnTypeConstraint(const CXXScopeSpec &SS, ConstrainedParameter, EllipsisLoc); } +template +static ExprResult formImmediatelyDeclaredConstraint( + Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo, + ConceptDecl *NamedConcept, SourceLocation LAngleLoc, + SourceLocation RAngleLoc, QualType ConstrainedType, + SourceLocation ParamNameLoc, ArgumentLocAppender Appender, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo ConstraintArgs; + ConstraintArgs.addArgument( + S.getTrivialTemplateArgumentLoc(TemplateArgument(ConstrainedType), + /*NTTPType=*/QualType(), ParamNameLoc)); + + ConstraintArgs.setRAngleLoc(RAngleLoc); + ConstraintArgs.setLAngleLoc(LAngleLoc); + Appender(ConstraintArgs); + + // C++2a [temp.param]p4: + // [...] This constraint-expression E is called the immediately-declared + // constraint of T. [...] + CXXScopeSpec SS; + SS.Adopt(NS); + ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId( + SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo, + /*FoundDecl=*/NamedConcept, NamedConcept, &ConstraintArgs); + if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid()) + return ImmediatelyDeclaredConstraint; + + // C++2a [temp.param]p4: + // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). + // + // We have the following case: + // + // template concept C1 = true; + // template struct s1; + // + // The constraint: (C1 && ...) + return S.BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), + ImmediatelyDeclaredConstraint.get(), BO_LAnd, + EllipsisLoc, /*RHS=*/nullptr, + /*RParenLoc=*/SourceLocation(), + /*NumExpansions=*/None); +} + /// Attach a type-constraint to a template parameter. /// \returns true if an error occured. This can happen if the /// immediately-declared constraint could not be formed (e.g. incorrect number @@ -1106,51 +1150,21 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, *TemplateArgs) : nullptr; QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0); - TemplateArgumentListInfo ConstraintArgs; - ConstraintArgs.addArgument( - TemplateArgumentLoc( - TemplateArgument(ParamAsArgument), - TemplateArgumentLocInfo( - Context.getTrivialTypeSourceInfo(ParamAsArgument, - ConstrainedParameter->getLocation())))); - if (TemplateArgs) { - ConstraintArgs.setRAngleLoc(TemplateArgs->getRAngleLoc()); - ConstraintArgs.setLAngleLoc(TemplateArgs->getLAngleLoc()); - for (const TemplateArgumentLoc &ArgLoc : TemplateArgs->arguments()) - ConstraintArgs.addArgument(ArgLoc); - } - // C++2a [temp.param]p4: - // [...] This constraint-expression E is called the immediately-declared - // constraint of T. [...] - CXXScopeSpec SS; - SS.Adopt(NS); - ExprResult ImmediatelyDeclaredConstraint = CheckConceptTemplateId(SS, - /*TemplateKWLoc=*/SourceLocation(), NameInfo, /*FoundDecl=*/NamedConcept, - NamedConcept, &ConstraintArgs); + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, NS, NameInfo, NamedConcept, + TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(), + TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(), + ParamAsArgument, ConstrainedParameter->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + if (TemplateArgs) + for (const auto &ArgLoc : TemplateArgs->arguments()) + ConstraintArgs.addArgument(ArgLoc); + }, EllipsisLoc); if (ImmediatelyDeclaredConstraint.isInvalid()) return true; - if (ConstrainedParameter->isParameterPack()) { - // C++2a [temp.param]p4: - // [...] If T is not a pack, then E is E', otherwise E is (E' && ...). - // - // We have the following case: - // - // template concept C1 = true; - // template struct s1; - // - // The constraint: (C1 && ...) - ImmediatelyDeclaredConstraint = - BuildCXXFoldExpr(/*LParenLoc=*/SourceLocation(), - ImmediatelyDeclaredConstraint.get(), BO_LAnd, - EllipsisLoc, /*RHS=*/nullptr, - /*RParenLoc=*/SourceLocation(), - /*NumExpansions=*/None).get(); - if (ImmediatelyDeclaredConstraint.isInvalid()) - return true; - } - ConstrainedParameter->setTypeConstraint(NS, NameInfo, /*FoundDecl=*/NamedConcept, NamedConcept, ArgsAsWritten, @@ -1158,6 +1172,38 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS, return false; } +bool Sema::AttachTypeConstraint(AutoTypeLoc TL, NonTypeTemplateParmDecl *NTTP, + SourceLocation EllipsisLoc) { + if (NTTP->getType() != TL.getType() || + TL.getAutoKeyword() != AutoTypeKeyword::Auto) { + Diag(NTTP->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + diag::err_unsupported_placeholder_constraint) + << NTTP->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + return true; + } + // FIXME: Concepts: This should be the type of the placeholder, but this is + // unclear in the wording right now. + DeclRefExpr *Ref = BuildDeclRefExpr(NTTP, NTTP->getType(), VK_RValue, + NTTP->getLocation()); + if (!Ref) + return true; + ExprResult ImmediatelyDeclaredConstraint = + formImmediatelyDeclaredConstraint( + *this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(), + TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(), + BuildDecltypeType(Ref, NTTP->getLocation()), NTTP->getLocation(), + [&] (TemplateArgumentListInfo &ConstraintArgs) { + for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I) + ConstraintArgs.addArgument(TL.getArgLoc(I)); + }, EllipsisLoc); + if (ImmediatelyDeclaredConstraint.isInvalid() || + !ImmediatelyDeclaredConstraint.isUsable()) + return true; + + NTTP->setPlaceholderTypeConstraint(ImmediatelyDeclaredConstraint.get()); + return false; +} + /// Check that the type of a non-type template parameter is /// well-formed. /// @@ -1319,6 +1365,11 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D, TInfo); Param->setAccess(AS_public); + if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc()) + if (TL.isConstrained()) + if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc())) + Invalid = true; + if (Invalid) Param->setInvalidDecl(); @@ -2762,7 +2813,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( SourceLocation DeclStartLoc, SourceLocation DeclLoc, const CXXScopeSpec &SS, TemplateIdAnnotation *TemplateId, ArrayRef ParamLists, bool IsFriend, - bool &IsMemberSpecialization, bool &Invalid) { + bool &IsMemberSpecialization, bool &Invalid, bool SuppressDiagnostic) { IsMemberSpecialization = false; Invalid = false; @@ -2870,8 +2921,9 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( auto CheckExplicitSpecialization = [&](SourceRange Range, bool Recovery) { if (SawNonEmptyTemplateParameterList) { - Diag(DeclLoc, diag::err_specialize_member_of_template) - << !Recovery << Range; + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_specialize_member_of_template) + << !Recovery << Range; Invalid = true; IsMemberSpecialization = false; return true; @@ -2892,9 +2944,10 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( else ExpectedTemplateLoc = DeclStartLoc; - Diag(DeclLoc, diag::err_template_spec_needs_header) - << Range - << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_header) + << Range + << FixItHint::CreateInsertion(ExpectedTemplateLoc, "template<> "); return false; }; @@ -2984,12 +3037,13 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( if (ParamIdx < ParamLists.size()) { if (ParamLists[ParamIdx]->size() > 0) { // The header has template parameters when it shouldn't. Complain. - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - diag::err_template_param_list_matches_nontemplate) - << T - << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), - ParamLists[ParamIdx]->getRAngleLoc()) - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + diag::err_template_param_list_matches_nontemplate) + << T + << SourceRange(ParamLists[ParamIdx]->getLAngleLoc(), + ParamLists[ParamIdx]->getRAngleLoc()) + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; return nullptr; } @@ -3025,7 +3079,7 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( if (ExpectedTemplateParams && !TemplateParameterListsAreEqual(ParamLists[ParamIdx], ExpectedTemplateParams, - true, TPL_TemplateMatch)) + !SuppressDiagnostic, TPL_TemplateMatch)) Invalid = true; if (!Invalid && @@ -3037,9 +3091,10 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( continue; } - Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) - << T - << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); + if (!SuppressDiagnostic) + Diag(DeclLoc, diag::err_template_spec_needs_template_parameters) + << T + << getRangeOfTypeInNestedNameSpecifier(Context, T, SS); Invalid = true; continue; } @@ -3075,16 +3130,18 @@ TemplateParameterList *Sema::MatchTemplateParametersToScopeSpecifier( AllExplicitSpecHeaders = false; } - Diag(ParamLists[ParamIdx]->getTemplateLoc(), - AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers - : diag::err_template_spec_extra_headers) - << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), - ParamLists[ParamLists.size() - 2]->getRAngleLoc()); + if (!SuppressDiagnostic) + Diag(ParamLists[ParamIdx]->getTemplateLoc(), + AllExplicitSpecHeaders ? diag::warn_template_spec_extra_headers + : diag::err_template_spec_extra_headers) + << SourceRange(ParamLists[ParamIdx]->getTemplateLoc(), + ParamLists[ParamLists.size() - 2]->getRAngleLoc()); // If there was a specialization somewhere, such that 'template<>' is // not required, and there were any 'template<>' headers, note where the // specialization occurred. - if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader) + if (ExplicitSpecLoc.isValid() && HasAnyExplicitSpecHeader && + !SuppressDiagnostic) Diag(ExplicitSpecLoc, diag::note_explicit_template_spec_does_not_need_header) << NestedTypes.back(); @@ -6530,7 +6587,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, DeductionArg = PE->getPattern(); if (DeduceAutoType( Context.getTrivialTypeSourceInfo(ParamType, Param->getLocation()), - DeductionArg, ParamType, Depth) == DAR_Failed) { + DeductionArg, ParamType, Depth, + // We do not check constraints right now because the + // immediately-declared constraint of the auto type is also an + // associated constraint, and will be checked along with the other + // associated constraints after checking the template argument list. + /*IgnoreConstraints=*/true) == DAR_Failed) { Diag(Arg->getExprLoc(), diag::err_non_type_template_parm_type_deduction_failure) << Param->getDeclName() << Param->getType() << Arg->getType() diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 048a50a741e4..394c81c82794 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -4414,9 +4414,10 @@ namespace { QualType Result = SemaRef.Context.getAutoType( Replacement, TL.getTypePtr()->getKeyword(), Replacement.isNull(), - ReplacementIsPack); + ReplacementIsPack, TL.getTypePtr()->getTypeConstraintConcept(), + TL.getTypePtr()->getTypeConstraintArguments()); auto NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); + NewTL.copy(TL); return Result; } @@ -4451,9 +4452,10 @@ namespace { Sema::DeduceAutoResult Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init, QualType &Result, - Optional DependentDeductionDepth) { + Optional DependentDeductionDepth, + bool IgnoreConstraints) { return DeduceAutoType(Type->getTypeLoc(), Init, Result, - DependentDeductionDepth); + DependentDeductionDepth, IgnoreConstraints); } /// Attempt to produce an informative diagostic explaining why auto deduction @@ -4481,6 +4483,49 @@ static bool diagnoseAutoDeductionFailure(Sema &S, } } +static Sema::DeduceAutoResult +CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type, + AutoTypeLoc TypeLoc, QualType Deduced) { + ConstraintSatisfaction Satisfaction; + ConceptDecl *Concept = Type.getTypeConstraintConcept(); + TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(), + TypeLoc.getRAngleLoc()); + TemplateArgs.addArgument( + TemplateArgumentLoc(TemplateArgument(Deduced), + S.Context.getTrivialTypeSourceInfo( + Deduced, TypeLoc.getNameLoc()))); + for (unsigned I = 0, C = TypeLoc.getNumArgs(); I != C; ++I) + TemplateArgs.addArgument(TypeLoc.getArgLoc(I)); + + llvm::SmallVector Converted; + if (S.CheckTemplateArgumentList(Concept, SourceLocation(), TemplateArgs, + /*PartialTemplateArgs=*/false, Converted)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()}, + Converted, TypeLoc.getLocalSourceRange(), + Satisfaction)) + return Sema::DAR_FailedAlreadyDiagnosed; + if (!Satisfaction.IsSatisfied) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "'" << Concept->getName(); + if (TypeLoc.hasExplicitTemplateArgs()) { + OS << "<"; + for (const auto &Arg : Type.getTypeConstraintArguments()) + Arg.print(S.getPrintingPolicy(), OS); + OS << ">"; + } + OS << "'"; + OS.flush(); + S.Diag(TypeLoc.getConceptNameLoc(), + diag::err_placeholder_constraints_not_satisfied) + << Deduced << Buf << TypeLoc.getLocalSourceRange(); + S.DiagnoseUnsatisfiedConstraint(Satisfaction); + return Sema::DAR_FailedAlreadyDiagnosed; + } + return Sema::DAR_Succeeded; +} + /// Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6) /// /// Note that this is done even if the initializer is dependent. (This is @@ -4495,9 +4540,12 @@ static bool diagnoseAutoDeductionFailure(Sema &S, /// dependent cases. This is necessary for template partial ordering with /// 'auto' template parameters. The value specified is the template /// parameter depth at which we should perform 'auto' deduction. +/// \param IgnoreConstraints Set if we should not fail if the deduced type does +/// not satisfy the type-constraint in the auto type. Sema::DeduceAutoResult Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, - Optional DependentDeductionDepth) { + Optional DependentDeductionDepth, + bool IgnoreConstraints) { if (Init->getType()->isNonOverloadPlaceholderType()) { ExprResult NonPlaceholder = CheckPlaceholderExpr(Init); if (NonPlaceholder.isInvalid()) @@ -4538,6 +4586,14 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; // FIXME: Support a non-canonical deduced type for 'auto'. Deduced = Context.getCanonicalType(Deduced); + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + Deduced); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } Result = SubstituteDeducedTypeTransform(*this, Deduced).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; @@ -4645,6 +4701,17 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result, return DAR_FailedAlreadyDiagnosed; } + if (const auto *AT = Type.getType()->getAs()) { + if (AT->isConstrained() && !IgnoreConstraints) { + auto ConstraintsResult = + CheckDeducedPlaceholderConstraints(*this, *AT, + Type.getContainedAutoTypeLoc(), + DeducedType); + if (ConstraintsResult != DAR_Succeeded) + return ConstraintsResult; + } + } + Result = SubstituteDeducedTypeTransform(*this, DeducedType).Apply(Type); if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 7d60298be2be..8fd7491c45e3 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2685,6 +2685,16 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( D->getDepth() - TemplateArgs.getNumSubstitutedLevels(), D->getPosition(), D->getIdentifier(), T, D->isParameterPack(), DI); + if (AutoTypeLoc AutoLoc = DI->getTypeLoc().getContainedAutoTypeLoc()) + if (AutoLoc.isConstrained()) + if (SemaRef.AttachTypeConstraint( + AutoLoc, Param, + IsExpandedParameterPack + ? DI->getTypeLoc().getAs() + .getEllipsisLoc() + : SourceLocation())) + Invalid = true; + Param->setAccess(AS_public); Param->setImplicit(D->isImplicit()); if (Invalid) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index bafeed6e8447..632dc51c9513 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "TypeLocBuilder.h" +#include "TreeTransform.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" @@ -27,6 +28,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" @@ -1251,6 +1253,26 @@ getImageAccess(const ParsedAttributesView &Attrs) { return OpenCLAccessAttr::Keyword_read_only; } +static QualType ConvertConstrainedAutoDeclSpecToType(Sema &S, DeclSpec &DS, + AutoTypeKeyword AutoKW) { + assert(DS.isConstrainedAuto()); + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc); + TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc); + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + llvm::SmallVector TemplateArgs; + for (auto &ArgLoc : TemplateArgsInfo.arguments()) + TemplateArgs.push_back(ArgLoc.getArgument()); + return S.Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false, + /*IsPack=*/false, + cast(TemplateId->Template.get() + .getAsTemplateDecl()), + TemplateArgs); +} + /// Convert the specified declspec to the appropriate type /// object. /// \param state Specifies the declarator containing the declaration specifier @@ -1595,6 +1617,11 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; case DeclSpec::TST_auto: + if (DS.isConstrainedAuto()) { + Result = ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::Auto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); break; @@ -1603,6 +1630,12 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; case DeclSpec::TST_decltype_auto: + if (DS.isConstrainedAuto()) { + Result = + ConvertConstrainedAutoDeclSpecToType(S, DS, + AutoTypeKeyword::DecltypeAuto); + break; + } Result = Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto, /*IsDependent*/ false); break; @@ -2921,6 +2954,87 @@ static void diagnoseRedundantReturnTypeQualifiers(Sema &S, QualType RetTy, D.getDeclSpec().getUnalignedSpecLoc()); } +static void CopyTypeConstraintFromAutoType(Sema &SemaRef, const AutoType *Auto, + AutoTypeLoc AutoLoc, + TemplateTypeParmDecl *TP, + SourceLocation EllipsisLoc) { + + TemplateArgumentListInfo TAL(AutoLoc.getLAngleLoc(), AutoLoc.getRAngleLoc()); + for (unsigned Idx = 0; Idx < AutoLoc.getNumArgs(); ++Idx) + TAL.addArgument(AutoLoc.getArgLoc(Idx)); + + SemaRef.AttachTypeConstraint( + AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(), + AutoLoc.getNamedConcept(), + AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr, TP, EllipsisLoc); +} + +static QualType InventTemplateParameter( + TypeProcessingState &state, QualType T, TypeSourceInfo *TSI, AutoType *Auto, + InventedTemplateParameterInfo &Info) { + Sema &S = state.getSema(); + Declarator &D = state.getDeclarator(); + + const unsigned TemplateParameterDepth = Info.AutoTemplateParameterDepth; + const unsigned AutoParameterPosition = Info.TemplateParams.size(); + const bool IsParameterPack = D.hasEllipsis(); + + // If auto is mentioned in a lambda parameter or abbreviated function + // template context, convert it to a template parameter type. + + // Create the TemplateTypeParmDecl here to retrieve the corresponding + // template parameter type. Template parameters are temporarily added + // to the TU until the associated TemplateDecl is created. + TemplateTypeParmDecl *InventedTemplateParam = + TemplateTypeParmDecl::Create( + S.Context, S.Context.getTranslationUnitDecl(), + /*KeyLoc=*/D.getDeclSpec().getTypeSpecTypeLoc(), + /*NameLoc=*/D.getIdentifierLoc(), + TemplateParameterDepth, AutoParameterPosition, + S.InventAbbreviatedTemplateParameterTypeName( + D.getIdentifier(), AutoParameterPosition), false, + IsParameterPack, /*HasTypeConstraint=*/Auto->isConstrained()); + InventedTemplateParam->setImplicit(); + Info.TemplateParams.push_back(InventedTemplateParam); + // Attach type constraints + if (Auto->isConstrained()) { + if (TSI) { + CopyTypeConstraintFromAutoType( + S, Auto, TSI->getTypeLoc().getContainedAutoTypeLoc(), + InventedTemplateParam, D.getEllipsisLoc()); + } else { + TemplateIdAnnotation *TemplateId = D.getDeclSpec().getRepAsTemplateId(); + TemplateArgumentListInfo TemplateArgsInfo; + if (TemplateId->LAngleLoc.isValid()) { + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + S.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + } + S.AttachTypeConstraint( + D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context), + DeclarationNameInfo(DeclarationName(TemplateId->Name), + TemplateId->TemplateNameLoc), + cast(TemplateId->Template.get().getAsTemplateDecl()), + TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr, + InventedTemplateParam, D.getEllipsisLoc()); + } + } + + // If TSI is nullptr, this is a constrained declspec auto and the type + // constraint will be attached later in TypeSpecLocFiller + + // Replace the 'auto' in the function parameter with this invented + // template type parameter. + // FIXME: Retain some type sugar to indicate that this was written + // as 'auto'? + return state.ReplaceAutoType( + T, QualType(InventedTemplateParam->getTypeForDecl(), 0)); +} + +static TypeSourceInfo * +GetTypeSourceInfoForDeclarator(TypeProcessingState &State, + QualType T, TypeSourceInfo *ReturnTypeInfo); + static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, TypeSourceInfo *&ReturnTypeInfo) { Sema &SemaRef = state.getSema(); @@ -2991,46 +3105,43 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, break; case DeclaratorContext::ObjCParameterContext: case DeclaratorContext::ObjCResultContext: - case DeclaratorContext::PrototypeContext: Error = 0; break; case DeclaratorContext::RequiresExprContext: - Error = 21; + Error = 22; break; - case DeclaratorContext::LambdaExprParameterContext: - // In C++14, generic lambdas allow 'auto' in their parameters. - if (!SemaRef.getLangOpts().CPlusPlus14 || - !Auto || Auto->getKeyword() != AutoTypeKeyword::Auto) - Error = 16; - else { - // If auto is mentioned in a lambda parameter context, convert it to a - // template parameter type. - sema::LambdaScopeInfo *LSI = SemaRef.getCurLambda(); - assert(LSI && "No LambdaScopeInfo on the stack!"); - const unsigned TemplateParameterDepth = LSI->AutoTemplateParameterDepth; - const unsigned AutoParameterPosition = LSI->TemplateParams.size(); - const bool IsParameterPack = D.hasEllipsis(); - - // Create the TemplateTypeParmDecl here to retrieve the corresponding - // template parameter type. Template parameters are temporarily added - // to the TU until the associated TemplateDecl is created. - TemplateTypeParmDecl *CorrespondingTemplateParam = - TemplateTypeParmDecl::Create( - SemaRef.Context, SemaRef.Context.getTranslationUnitDecl(), - /*KeyLoc*/ SourceLocation(), /*NameLoc*/ D.getBeginLoc(), - TemplateParameterDepth, AutoParameterPosition, - /*Identifier*/ nullptr, false, IsParameterPack, - /*HasTypeConstraint=*/false); - CorrespondingTemplateParam->setImplicit(); - LSI->TemplateParams.push_back(CorrespondingTemplateParam); - // Replace the 'auto' in the function parameter with this invented - // template type parameter. - // FIXME: Retain some type sugar to indicate that this was written - // as 'auto'. - T = state.ReplaceAutoType( - T, QualType(CorrespondingTemplateParam->getTypeForDecl(), 0)); + case DeclaratorContext::PrototypeContext: + case DeclaratorContext::LambdaExprParameterContext: { + InventedTemplateParameterInfo *Info = nullptr; + if (D.getContext() == DeclaratorContext::PrototypeContext) { + // With concepts we allow 'auto' in function parameters. + if (!SemaRef.getLangOpts().ConceptsTS || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 0; + break; + } else if (!SemaRef.getCurScope()->isFunctionDeclarationScope()) { + Error = 21; + break; + } else if (D.hasTrailingReturnType()) { + // This might be OK, but we'll need to convert the trailing return + // type later. + break; + } + + Info = &SemaRef.InventedParameterInfos.back(); + } else { + // In C++14, generic lambdas allow 'auto' in their parameters. + if (!SemaRef.getLangOpts().CPlusPlus14 || !Auto || + Auto->getKeyword() != AutoTypeKeyword::Auto) { + Error = 16; + break; + } + Info = SemaRef.getCurLambda(); + assert(Info && "No LambdaScopeInfo on the stack!"); } + T = InventTemplateParameter(state, T, nullptr, Auto, *Info); break; + } case DeclaratorContext::MemberContext: { if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static || D.isFunctionDeclarator()) @@ -4032,10 +4143,6 @@ static bool DiagnoseMultipleAddrSpaceAttributes(Sema &S, LangAS ASOld, return false; } -static TypeSourceInfo * -GetTypeSourceInfoForDeclarator(TypeProcessingState &State, - QualType T, TypeSourceInfo *ReturnTypeInfo); - static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, QualType declSpecType, TypeSourceInfo *TInfo) { @@ -4611,7 +4718,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } else if (D.getContext() != DeclaratorContext::LambdaExprContext && (T.hasQualifiers() || !isa(T) || cast(T)->getKeyword() != - AutoTypeKeyword::Auto)) { + AutoTypeKeyword::Auto || + cast(T)->isConstrained())) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); @@ -4622,7 +4730,12 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // An error occurred parsing the trailing return type. T = Context.IntTy; D.setInvalidType(true); - } + } else if (S.getLangOpts().ConceptsTS) + // Handle cases like: `auto f() -> auto` or `auto f() -> C auto`. + if (AutoType *Auto = T->getContainedAutoType()) + if (S.getCurScope()->isFunctionDeclarationScope()) + T = InventTemplateParameter(state, T, TInfo, Auto, + S.InventedParameterInfos.back()); } else { // This function type is not the type of the entity being declared, // so checking the 'auto' is not the responsibility of this chunk. @@ -5242,7 +5355,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // // We represent function parameter packs as function parameters whose // type is a pack expansion. - if (!T->containsUnexpandedParameterPack()) { + if (!T->containsUnexpandedParameterPack() && + (!LangOpts.ConceptsTS || !T->getContainedAutoType())) { S.Diag(D.getEllipsisLoc(), diag::err_function_parameter_pack_without_parameter_packs) << T << D.getSourceRange(); @@ -5450,14 +5564,15 @@ static void fillAttributedTypeLoc(AttributedTypeLoc TL, namespace { class TypeSpecLocFiller : public TypeLocVisitor { + Sema &SemaRef; ASTContext &Context; TypeProcessingState &State; const DeclSpec &DS; public: - TypeSpecLocFiller(ASTContext &Context, TypeProcessingState &State, + TypeSpecLocFiller(Sema &S, ASTContext &Context, TypeProcessingState &State, const DeclSpec &DS) - : Context(Context), State(State), DS(DS) {} + : SemaRef(S), Context(Context), State(State), DS(DS) {} void VisitAttributedTypeLoc(AttributedTypeLoc TL) { Visit(TL.getModifiedLoc()); @@ -5585,6 +5700,34 @@ namespace { TL.copy( TInfo->getTypeLoc().castAs()); } + void VisitAutoTypeLoc(AutoTypeLoc TL) { + assert(DS.getTypeSpecType() == TST_auto || + DS.getTypeSpecType() == TST_decltype_auto || + DS.getTypeSpecType() == TST_auto_type || + DS.getTypeSpecType() == TST_unspecified); + TL.setNameLoc(DS.getTypeSpecTypeLoc()); + if (!DS.isConstrainedAuto()) + return; + TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId(); + if (DS.getTypeSpecScope().isNotEmpty()) + TL.setNestedNameSpecifierLoc( + DS.getTypeSpecScope().getWithLocInContext(Context)); + else + TL.setNestedNameSpecifierLoc(NestedNameSpecifierLoc()); + TL.setTemplateKWLoc(TemplateId->TemplateKWLoc); + TL.setConceptNameLoc(TemplateId->TemplateNameLoc); + TL.setFoundDecl(nullptr); + TL.setLAngleLoc(TemplateId->LAngleLoc); + TL.setRAngleLoc(TemplateId->RAngleLoc); + if (TemplateId->NumArgs == 0) + return; + TemplateArgumentListInfo TemplateArgsInfo; + ASTTemplateArgsPtr TemplateArgsPtr(TemplateId->getTemplateArgs(), + TemplateId->NumArgs); + SemaRef.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo); + for (unsigned I = 0; I < TemplateId->NumArgs; ++I) + TL.setArgLocInfo(I, TemplateArgsInfo.arguments()[I].getLocInfo()); + } void VisitTagTypeLoc(TagTypeLoc TL) { TL.setNameLoc(DS.getTypeSpecTypeNameLoc()); } @@ -5854,7 +5997,7 @@ GetTypeSourceInfoForDeclarator(TypeProcessingState &State, assert(TL.getFullDataSize() == CurrTL.getFullDataSize()); memcpy(CurrTL.getOpaqueData(), TL.getOpaqueData(), TL.getFullDataSize()); } else { - TypeSpecLocFiller(S.Context, State, D.getDeclSpec()).Visit(CurrTL); + TypeSpecLocFiller(S, S.Context, State, D.getDeclSpec()).Visit(CurrTL); } return TInfo; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1f7251173838..d6105353bbdf 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -951,12 +951,16 @@ class TreeTransform { /// Build a new C++11 auto type. /// /// By default, builds a new AutoType with the given deduced type. - QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword) { + QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword, + ConceptDecl *TypeConstraintConcept, + ArrayRef TypeConstraintArgs) { // Note, IsDependent is always false here: we implicitly convert an 'auto' // which has been deduced to a dependent type into an undeduced 'auto', so // that we'll retry deduction after the transformation. return SemaRef.Context.getAutoType(Deduced, Keyword, - /*IsDependent*/ false); + /*IsDependent*/ false, /*IsPack=*/false, + TypeConstraintConcept, + TypeConstraintArgs); } /// By default, builds a new DeducedTemplateSpecializationType with the given @@ -4500,7 +4504,10 @@ QualType TreeTransform::RebuildQualifiedType(QualType T, Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Qs); T = SemaRef.Context.getAutoType(Deduced, AutoTy->getKeyword(), - AutoTy->isDependentType()); + AutoTy->isDependentType(), + /*isPack=*/false, + AutoTy->getTypeConstraintConcept(), + AutoTy->getTypeConstraintArguments()); } else { // Otherwise, complain about the addition of a qualifier to an // already-qualified type. @@ -5233,21 +5240,29 @@ bool TreeTransform::TransformFunctionTypeParams( PackExpansionTypeLoc ExpansionTL = TL.castAs(); TypeLoc Pattern = ExpansionTL.getPatternLoc(); SemaRef.collectUnexpandedParameterPacks(Pattern, Unexpanded); - assert(Unexpanded.size() > 0 && "Could not find parameter packs!"); // Determine whether we should expand the parameter packs. bool ShouldExpand = false; bool RetainExpansion = false; - Optional OrigNumExpansions = - ExpansionTL.getTypePtr()->getNumExpansions(); - NumExpansions = OrigNumExpansions; - if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), - Pattern.getSourceRange(), - Unexpanded, - ShouldExpand, - RetainExpansion, - NumExpansions)) { - return true; + Optional OrigNumExpansions; + if (Unexpanded.size() > 0) { + OrigNumExpansions = ExpansionTL.getTypePtr()->getNumExpansions(); + NumExpansions = OrigNumExpansions; + if (getDerived().TryExpandParameterPacks(ExpansionTL.getEllipsisLoc(), + Pattern.getSourceRange(), + Unexpanded, + ShouldExpand, + RetainExpansion, + NumExpansions)) { + return true; + } + } else { +#ifndef NDEBUG + const AutoType *AT = + Pattern.getType().getTypePtr()->getContainedAutoType(); + assert((AT && (!AT->isDeduced() || AT->getDeducedType().isNull())) && + "Could not find parameter packs or undeduced auto type!"); +#endif } if (ShouldExpand) { @@ -5307,6 +5322,9 @@ bool TreeTransform::TransformFunctionTypeParams( indexAdjustment, NumExpansions, /*ExpectParameterPack=*/true); + assert(NewParm->isParameterPack() && + "Parameter pack no longer a parameter pack after " + "transformation."); } else { NewParm = getDerived().TransformFunctionTypeParam( OldParm, indexAdjustment, None, /*ExpectParameterPack=*/ false); @@ -5811,32 +5829,6 @@ QualType TreeTransform::TransformUnaryTransformType( return Result; } -template -QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, - AutoTypeLoc TL) { - const AutoType *T = TL.getTypePtr(); - QualType OldDeduced = T->getDeducedType(); - QualType NewDeduced; - if (!OldDeduced.isNull()) { - NewDeduced = getDerived().TransformType(OldDeduced); - if (NewDeduced.isNull()) - return QualType(); - } - - QualType Result = TL.getType(); - if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || - T->isDependentType()) { - Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword()); - if (Result.isNull()) - return QualType(); - } - - AutoTypeLoc NewTL = TLB.push(Result); - NewTL.setNameLoc(TL.getNameLoc()); - - return Result; -} - template QualType TreeTransform::TransformDeducedTemplateSpecializationType( TypeLocBuilder &TLB, DeducedTemplateSpecializationTypeLoc TL) { @@ -6098,6 +6090,71 @@ QualType TreeTransform::TransformPipeType(TypeLocBuilder &TLB, } }; +template +QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, + AutoTypeLoc TL) { + const AutoType *T = TL.getTypePtr(); + QualType OldDeduced = T->getDeducedType(); + QualType NewDeduced; + if (!OldDeduced.isNull()) { + NewDeduced = getDerived().TransformType(OldDeduced); + if (NewDeduced.isNull()) + return QualType(); + } + + ConceptDecl *NewCD = nullptr; + TemplateArgumentListInfo NewTemplateArgs; + NestedNameSpecifierLoc NewNestedNameSpec; + if (TL.getTypePtr()->isConstrained()) { + NewCD = cast_or_null( + getDerived().TransformDecl( + TL.getConceptNameLoc(), + TL.getTypePtr()->getTypeConstraintConcept())); + + NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc()); + NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc()); + typedef TemplateArgumentLocContainerIterator ArgIterator; + if (getDerived().TransformTemplateArguments(ArgIterator(TL, 0), + ArgIterator(TL, + TL.getNumArgs()), + NewTemplateArgs)) + return QualType(); + + if (TL.getNestedNameSpecifierLoc()) { + NewNestedNameSpec + = getDerived().TransformNestedNameSpecifierLoc( + TL.getNestedNameSpecifierLoc()); + if (!NewNestedNameSpec) + return QualType(); + } + } + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || + T->isDependentType()) { + llvm::SmallVector NewArgList; + NewArgList.reserve(NewArgList.size()); + for (const auto &ArgLoc : NewTemplateArgs.arguments()) + NewArgList.push_back(ArgLoc.getArgument()); + Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword(), NewCD, + NewArgList); + if (Result.isNull()) + return QualType(); + } + + AutoTypeLoc NewTL = TLB.push(Result); + NewTL.setNameLoc(TL.getNameLoc()); + NewTL.setNestedNameSpecifierLoc(NewNestedNameSpec); + NewTL.setTemplateKWLoc(TL.getTemplateKWLoc()); + NewTL.setConceptNameLoc(TL.getConceptNameLoc()); + NewTL.setFoundDecl(TL.getFoundDecl()); + NewTL.setLAngleLoc(TL.getLAngleLoc()); + NewTL.setRAngleLoc(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + NewTL.setArgLocInfo(I, NewTemplateArgs.arguments()[I].getLocInfo()); + + return Result; +} template QualType TreeTransform::TransformTemplateSpecializationType( diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 19e7ebe03a1f..8e8b04451fb1 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6576,6 +6576,17 @@ void TypeLocReader::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { void TypeLocReader::VisitAutoTypeLoc(AutoTypeLoc TL) { TL.setNameLoc(readSourceLocation()); + if (Reader.readBool()) { + TL.setNestedNameSpecifierLoc(ReadNestedNameSpecifierLoc()); + TL.setTemplateKWLoc(readSourceLocation()); + TL.setConceptNameLoc(readSourceLocation()); + TL.setFoundDecl(Reader.readDeclAs()); + TL.setLAngleLoc(readSourceLocation()); + TL.setRAngleLoc(readSourceLocation()); + for (unsigned i = 0, e = TL.getNumArgs(); i != e; ++i) + TL.setArgLocInfo(i, Reader.readTemplateArgumentLocInfo( + TL.getTypePtr()->getArg(i).getKind())); + } } void TypeLocReader::VisitDeducedTemplateSpecializationTypeLoc( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 4fd079e9d8e1..093b69ab19d0 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2317,12 +2317,12 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { D->setDeclaredWithTypename(Record.readInt()); - if (Record.readInt()) { + if (Record.readBool()) { NestedNameSpecifierLoc NNS = Record.readNestedNameSpecifierLoc(); DeclarationNameInfo DN = Record.readDeclarationNameInfo(); - ConceptDecl *NamedConcept = cast(Record.readDecl()); + ConceptDecl *NamedConcept = Record.readDeclAs(); const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; - if (Record.readInt()) + if (Record.readBool()) ArgsAsWritten = Record.readASTTemplateArgumentListInfo(); Expr *ImmediatelyDeclaredConstraint = Record.readExpr(); D->setTypeConstraint(NNS, DN, /*FoundDecl=*/nullptr, NamedConcept, @@ -2340,6 +2340,8 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // TemplateParmPosition. D->setDepth(Record.readInt()); D->setPosition(Record.readInt()); + if (D->hasPlaceholderTypeConstraint()) + D->setPlaceholderTypeConstraint(Record.readExpr()); if (D->isExpandedParameterPack()) { auto TypesAndInfos = D->getTrailingObjects>(); @@ -3823,13 +3825,19 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { HasTypeConstraint); break; } - case DECL_NON_TYPE_TEMPLATE_PARM: - D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID); + case DECL_NON_TYPE_TEMPLATE_PARM: { + bool HasTypeConstraint = Record.readInt(); + D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, + HasTypeConstraint); break; - case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: + } + case DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK: { + bool HasTypeConstraint = Record.readInt(); D = NonTypeTemplateParmDecl::CreateDeserialized(Context, ID, - Record.readInt()); + Record.readInt(), + HasTypeConstraint); break; + } case DECL_TEMPLATE_TEMPLATE_PARM: D = TemplateTemplateParmDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2dfdcc1f4fb3..252853aad1f8 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -349,6 +349,18 @@ void TypeLocWriter::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { void TypeLocWriter::VisitAutoTypeLoc(AutoTypeLoc TL) { Record.AddSourceLocation(TL.getNameLoc()); + Record.push_back(TL.isConstrained()); + if (TL.isConstrained()) { + Record.AddNestedNameSpecifierLoc(TL.getNestedNameSpecifierLoc()); + Record.AddSourceLocation(TL.getTemplateKWLoc()); + Record.AddSourceLocation(TL.getConceptNameLoc()); + Record.AddDeclRef(TL.getFoundDecl()); + Record.AddSourceLocation(TL.getLAngleLoc()); + Record.AddSourceLocation(TL.getRAngleLoc()); + for (unsigned I = 0; I < TL.getNumArgs(); ++I) + Record.AddTemplateArgumentLocInfo(TL.getTypePtr()->getArg(I).getKind(), + TL.getArgLocInfo(I)); + } } void TypeLocWriter::VisitDeducedTemplateSpecializationTypeLoc( diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 459e61713ed1..472136d99a13 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1675,6 +1675,8 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // For an expanded parameter pack, record the number of expansion types here // so that it's easier for deserialization to allocate the right amount of // memory. + Expr *TypeConstraint = D->getPlaceholderTypeConstraint(); + Record.push_back(!!TypeConstraint); if (D->isExpandedParameterPack()) Record.push_back(D->getNumExpansionTypes()); @@ -1682,6 +1684,8 @@ void ASTDeclWriter::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { // TemplateParmPosition. Record.push_back(D->getDepth()); Record.push_back(D->getPosition()); + if (TypeConstraint) + Record.AddStmt(TypeConstraint); if (D->isExpandedParameterPack()) { for (unsigned I = 0, N = D->getNumExpansionTypes(); I != N; ++I) { diff --git a/clang/test/AST/ast-dump-record-definition-data-json.cpp b/clang/test/AST/ast-dump-record-definition-data-json.cpp index 2603eedcd817..44ec2a2f7bc6 100644 --- a/clang/test/AST/ast-dump-record-definition-data-json.cpp +++ b/clang/test/AST/ast-dump-record-definition-data-json.cpp @@ -417,15 +417,24 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 @@ -524,15 +533,24 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 @@ -590,15 +608,24 @@ struct DoesNotAllowConstDefaultInit { // CHECK-NEXT: "id": "0x{{.*}}", // CHECK-NEXT: "kind": "TemplateTypeParmDecl", // CHECK-NEXT: "loc": { -// CHECK-NEXT: "offset": 193, -// CHECK-NEXT: "col": 29, -// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 // CHECK-NEXT: }, // CHECK-NEXT: "range": { -// CHECK-NEXT: "begin": {}, -// CHECK-NEXT: "end": {} +// CHECK-NEXT: "begin": { +// CHECK-NEXT: "offset": 193, +// CHECK-NEXT: "col": 29, +// CHECK-NEXT: "tokLen": 4 +// CHECK-NEXT: }, +// CHECK-NEXT: "end": { +// CHECK-NEXT: "offset": 197, +// CHECK-NEXT: "col": 33, +// CHECK-NEXT: "tokLen": 1 +// CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "isImplicit": true, +// CHECK-NEXT: "name": "auto:1", // CHECK-NEXT: "tagUsed": "class", // CHECK-NEXT: "depth": 0, // CHECK-NEXT: "index": 0 diff --git a/clang/test/CXX/dcl/dcl.fct/p17.cpp b/clang/test/CXX/dcl/dcl.fct/p17.cpp new file mode 100644 index 000000000000..bf19e57e7964 --- /dev/null +++ b/clang/test/CXX/dcl/dcl.fct/p17.cpp @@ -0,0 +1,260 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s +template constexpr bool is_same_v = false; +template constexpr bool is_same_v = true; + +template +struct type_list; + +namespace unconstrained { + decltype(auto) f1(auto x) { return x; } + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f2(auto &x) { return x; } + // expected-note@-1{{candidate function [with x:auto = int] not viable: expects an l-value for 1st argument}} + // expected-note@-2{{candidate function [with x:auto = char] not viable: expects an l-value for 1st argument}} + static_assert(is_same_v); // expected-error{{no matching}} + static_assert(is_same_v); // expected-error{{no matching}} + + decltype(auto) f3(const auto &x) { return x; } + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f4(auto (*x)(auto y)) { return x; } // expected-error{{'auto' not allowed in function prototype}} + + decltype(auto) f5(void (*x)(decltype(auto) y)) { return x; } // expected-error{{'decltype(auto)' not allowed in function prototype}} + + int return_int(); void return_void(); int foo(int); + + decltype(auto) f6(auto (*x)()) { return x; } + // expected-note@-1{{candidate template ignored: failed template argument deduction}} + static_assert(is_same_v); + static_assert(is_same_v); + using f6c1 = decltype(f6(foo)); // expected-error{{no matching}} + + decltype(auto) f7(auto (*x)() -> int) { return x; } + // expected-note@-1{{candidate function not viable: no known conversion from 'void ()' to 'auto (*)() -> int' for 1st argument}} + // expected-note@-2{{candidate function not viable: no known conversion from 'int (int)' to 'auto (*)() -> int' for 1st argument}} + static_assert(is_same_v); + using f7c1 = decltype(f7(return_void)); // expected-error{{no matching}} + using f7c2 = decltype(f7(foo)); // expected-error{{no matching}} + static_assert(is_same_v); + + decltype(auto) f8(auto... x) { return (x + ...); } + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + + decltype(auto) f9(auto &... x) { return (x, ...); } + // expected-note@-1{{candidate function [with x:auto = ] not viable: expects an l-value for 2nd argument}} + using f9c1 = decltype(f9(return_int, 1)); // expected-error{{no matching}} + + decltype(auto) f11(decltype(auto) x) { return x; } // expected-error{{'decltype(auto)' not allowed in function prototype}} + + template + auto f12(auto x, T y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v(1, 'c')), type_list>); + + template + auto f13(T x, auto y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v(1, 'c')), type_list>); + + template + auto f14(auto y) -> type_list; + static_assert(is_same_v('c')), type_list>); + static_assert(is_same_v('c')), type_list>); + + template + auto f15(auto y, U u) -> type_list; + static_assert(is_same_v('c', nullptr)), type_list>); + static_assert(is_same_v('c', nullptr)), type_list>); + + auto f16(auto x, auto y) -> type_list; + static_assert(is_same_v>); + static_assert(is_same_v('c', 1)), type_list>); + static_assert(is_same_v('c', 1)), type_list>); + + void f17(auto x, auto y) requires (sizeof(x) > 1); + // expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = char, y:auto = int]}} + // expected-note@-2{{because 'sizeof (x) > 1' (1 > 1) evaluated to false}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v('c', 1)), void>); + static_assert(is_same_v('c', 1)), void>); + + void f18(auto... x) requires (sizeof...(x) == 2); + // expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = ]}} + // expected-note@-2{{candidate template ignored: constraints not satisfied [with x:auto = ]}} + // expected-note@-3{{because 'sizeof...(x) == 2' (1 == 2) evaluated to false}} + // expected-note@-4{{because 'sizeof...(x) == 2' (3 == 2) evaluated to false}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + + template + struct S { + constexpr auto f1(auto x, T t) -> decltype(x + t); + + template + constexpr auto f2(U u, auto x, T t) -> decltype(x + u + t); + }; + + template + constexpr auto S::f1(auto x, T t) -> decltype(x + t) { return x + t; } + + template + template + constexpr auto S::f2(auto x, U u, T t) -> decltype(x + u + t) { return x + u + t; } + // expected-error@-1 {{out-of-line definition of 'f2' does not match any declaration in 'S'}} + + template + template + constexpr auto S::f2(U u, auto x, T t) -> decltype(x + u + t) { return x + u + t; } + + template<> + template<> + constexpr auto S::f2(double u, char x, int t) -> double { return 42; } + + static_assert(S{}.f1(1, 2) == 3); + static_assert(S{}.f2(1, 2, '\x00') == 3); + static_assert(S{}.f2(1, 2, '\x00') == 3.); + static_assert(S{}.f2(1, '2', '\x00') == 42); +} + +namespace constrained { + template + concept C = is_same_v; + // expected-note@-1 12{{because}} + template + concept C2 = is_same_v; + // expected-note@-1 12{{because}} + + int i; + const int ci = 1; + char c; + const char cc = 'a'; + int g(int); + char h(int); + + void f1(C auto x); + // expected-note@-1 {{candidate template ignored: constraints not satisfied [with x:auto = }} + // expected-note@-2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f2(C auto &x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f3(const C auto &x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f4(C auto (*x)(C auto y)); // expected-error{{'auto' not allowed}} + void f5(C auto (*x)(int y)); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f6(C auto (*x)() -> int); // expected-error{{function with trailing return type must specify return type 'auto', not 'C auto'}} + void f7(C auto... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f8(C auto &... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f9(const C auto &... x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f10(C decltype(auto) x); + auto f11 = [] (C auto x) { }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + + void f12(C2 auto x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f13(C2 auto &x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f14(const C2 auto &x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + static_assert(is_same_v); + void f15(C2 auto (*x)(C2 auto y)); // expected-error{{'auto' not allowed}} + void f16(C2 auto (*x)(int y)); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f17(C2 auto (*x)() -> int); // expected-error{{function with trailing return type must specify return type 'auto', not 'C2 auto'}} + void f18(C2 auto... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f19(C2 auto &... x); + // expected-note@-1 2{{candidate template ignored}} expected-note@-1 2{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + void f20(const C2 auto &... x); + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + void f21(C2 decltype(auto) x); + auto f22 = [] (C2 auto x) { }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); + + struct S1 { S1(C auto); }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + // expected-note@-2 2{{candidate constructor}} + static_assert(is_same_v); + static_assert(is_same_v); + // expected-error@-1{{no matching}} + struct S2 { S2(C2 auto); }; + // expected-note@-1{{candidate template ignored}} expected-note@-1{{because}} + // expected-note@-2 2{{candidate constructor}} + static_assert(is_same_v); + // expected-error@-1{{no matching}} + static_assert(is_same_v); +} diff --git a/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp new file mode 100644 index 000000000000..a4e71d341cd7 --- /dev/null +++ b/clang/test/CXX/dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s + +template +concept LargerThan = sizeof(T) > size; +// expected-note@-1 2{{because 'sizeof(char) > 1U' (1 > 1) evaluated to false}} +// expected-note@-2 {{because 'sizeof(int) > 10U' (4 > 10) evaluated to false}} +// expected-note@-3 {{because 'sizeof(int) > 4U' (4 > 4) evaluated to false}} + +template +concept Large = LargerThan; +// expected-note@-1 2{{because 'LargerThan' evaluated to false}} + +namespace X { + template + concept SmallerThan = sizeof(T) < size; + template + concept Small = SmallerThan; +} + +Large auto test1() { // expected-error{{deduced type 'char' does not satisfy 'Large'}} + Large auto i = 'a'; + // expected-error@-1{{deduced type 'char' does not satisfy 'Large'}} + Large auto j = 10; + ::Large auto k = 10; + LargerThan<1> auto l = 10; + ::LargerThan<1> auto m = 10; + LargerThan<10> auto n = 10; + // expected-error@-1{{deduced type 'int' does not satisfy 'LargerThan<10>'}} + X::Small auto o = 'x'; + X::SmallerThan<5> auto p = 1; + return 'a'; +} + +::Large auto test2() { return 10; } +LargerThan<4> auto test3() { return 10; } +// expected-error@-1{{deduced type 'int' does not satisfy 'LargerThan<4>'}} +::LargerThan<2> auto test4() { return 10; } + +Large auto test5() -> void; +// expected-error@-1{{function with trailing return type must specify return type 'auto', not 'Large auto'}} +auto test6() -> Large auto { return 1; } + +X::Small auto test7() { return 'a'; } +X::SmallerThan<5> auto test8() { return 10; } \ No newline at end of file diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp index 942280e1059f..0c0f820d1689 100644 --- a/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.lambda/expr.prim.lambda.closure/p3.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -std=c++2a -fconcepts-ts -verify %s auto l1 = [] (auto x) requires (sizeof(decltype(x)) == 1) { return x; }; -// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = int]}} +// expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = int]}} // expected-note@-2{{because 'sizeof(decltype(x)) == 1' (4 == 1) evaluated to false}} auto l1t1 = l1('a'); @@ -9,8 +9,8 @@ auto l1t2 = l1(1); // expected-error@-1{{no matching function for call to object of type '(lambda at}} auto l2 = [] (auto... x) requires ((sizeof(decltype(x)) >= 2) && ...) { return (x + ...); }; -// expected-note@-1{{candidate template ignored: constraints not satisfied [with $0 = ]}} -// expected-note@-2{{candidate template ignored: constraints not satisfied [with $0 = ]}} +// expected-note@-1{{candidate template ignored: constraints not satisfied [with x:auto = ]}} +// expected-note@-2{{candidate template ignored: constraints not satisfied [with x:auto = ]}} // expected-note@-3 2{{because 'sizeof(decltype(x)) >= 2' (1 >= 2) evaluated to false}} auto l2t1 = l2('a'); diff --git a/clang/test/CXX/temp/temp.param/p10-2a.cpp b/clang/test/CXX/temp/temp.param/p10-2a.cpp index de40f8cca5d5..3d2be1a92be1 100644 --- a/clang/test/CXX/temp/temp.param/p10-2a.cpp +++ b/clang/test/CXX/temp/temp.param/p10-2a.cpp @@ -94,6 +94,8 @@ concept OneOf = (is_same_v || ...); // expected-note@-5 {{and 'is_same_v' evaluated to false}} // expected-note@-6 3{{because 'is_same_v' evaluated to false}} // expected-note@-7 3{{and 'is_same_v' evaluated to false}} +// expected-note@-8 2{{because 'is_same_v' evaluated to false}} +// expected-note@-9 2{{and 'is_same_v' evaluated to false}} template T, OneOf U> // expected-note@-1 2{{because 'OneOf' evaluated to false}} @@ -114,4 +116,25 @@ using h1 = H; // expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = ]}} using h2 = H; // expected-error@-1 {{constraints not satisfied for alias template 'H' [with Ts = ]}} -using h3 = H; \ No newline at end of file +using h3 = H; + +template auto x> +// expected-note@-1 {{because 'OneOf' evaluated to false}} +using I = int; + +using i1 = I<1>; +using i2 = I<'a'>; +using i3 = I; +// expected-error@-1 {{constraints not satisfied for alias template 'I' [with x = nullptr]}} + +template auto... x> +// expected-note@-1 {{because 'OneOf' evaluated to false}} +using J = int; + +using j1 = J<1, 'b'>; +using j2 = J<'a', nullptr>; +// expected-error@-1 {{constraints not satisfied for alias template 'J' [with x = <'a', nullptr>]}} + +template auto &x> +// expected-error@-1 {{constrained placeholder types other than simple 'auto' on non-type template parameters not supported yet}} +using K = int; diff --git a/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp b/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp new file mode 100644 index 000000000000..a1bd08827b93 --- /dev/null +++ b/clang/test/Parser/cxx2a-placeholder-type-constraint.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++2a -fconcepts-ts -x c++ %s -verify + +template +concept C = true; + +int foo() { + C auto a4 = 1; + C<> auto a5 = 1; + C auto a6 = 1; + const C auto &a7 = 1; + const C<> auto &a8 = 1; + const C auto &a9 = 1; + C decltype(auto) a10 = 1; + C<> decltype(auto) a11 = 1; + C decltype(auto) a12 = 1; + const C<> decltype(auto) &a13 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}} + // expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + const C decltype(auto) &a14 = 1; // expected-error{{'decltype(auto)' cannot be combined with other type specifiers}} + // expected-error@-1{{non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'}} + C a15 = 1; + // expected-error@-1{{expected 'auto' or 'decltype(auto)' after concept name}} + C decltype a19 = 1; + // expected-error@-1{{expected '('}} + C decltype(1) a20 = 1; + // expected-error@-1{{expected 'auto' or 'decltype(auto)' after concept name}} +} \ No newline at end of file diff --git a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp index 087982327c72..13ab7aae6c32 100644 --- a/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -181,7 +181,7 @@ int test() { int (*fp2)(int) = [](auto b) -> int { return b; }; int (*fp3)(char) = [](auto c) -> int { return c; }; char (*fp4)(int) = [](auto d) { return d; }; //expected-error{{no viable conversion}}\ - //expected-note{{candidate function [with $0 = int]}} + //expected-note{{candidate function [with d:auto = int]}} char (*fp5)(char) = [](auto e) -> int { return e; }; //expected-error{{no viable conversion}}\ //expected-note{{candidate template ignored}} diff --git a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp index 276464875342..b2350c900e63 100644 --- a/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp +++ b/clang/test/SemaTemplate/ms-delayed-default-template-args.cpp @@ -93,7 +93,8 @@ namespace test_undeclared_nontype_parm_arg { // template parameter. template struct Bar { T x; }; -template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} expected-note {{declared here}} +template *P> // expected-error {{use of undeclared identifier 'Xylophone'}} +// expected-note@-1{{template parameter is declared here}} struct Foo { }; typedef int Xylophone; From a9b2cf6c625ab15e3cef00350c855e7cd319cf83 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Thu, 23 Jan 2020 23:24:56 +0200 Subject: [PATCH 054/374] [Concepts] Add ExpressionEvaluationContexts to instantiation of constraints Proper ExpressionEvaluationContext were not being entered when instantiating constraint expressions, which caused assertion failures in certain cases, including bug #44614. (cherry picked from commit 4d33a8dfcf67e970ea4d150d514b27de02e79aee) --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 ++++++++ .../SemaTemplate/instantiate-requires-clause.cpp | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 8fd7491c45e3..92f6e0dc1c90 100755 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1848,6 +1848,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl( // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); if (TrailingRequiresClause) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, TemplateArgs); if (SubstRC.isInvalid()) @@ -2186,6 +2188,8 @@ Decl *TemplateDeclInstantiator::VisitCXXMethodDecl( // FIXME: Concepts: Do not substitute into constraint expressions Expr *TrailingRequiresClause = D->getTrailingRequiresClause(); if (TrailingRequiresClause) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult SubstRC = SemaRef.SubstExpr(TrailingRequiresClause, TemplateArgs); if (SubstRC.isInvalid()) @@ -2525,6 +2529,8 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl( TemplateArgumentListInfo InstArgs; if (TemplArgInfo) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc); InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc); if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(), @@ -3729,6 +3735,8 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { // checking satisfaction. Expr *InstRequiresClause = nullptr; if (Expr *E = L->getRequiresClause()) { + EnterExpressionEvaluationContext ConstantEvaluated( + SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated); ExprResult Res = SemaRef.SubstExpr(E, TemplateArgs); if (Res.isInvalid() || !Res.isUsable()) { return nullptr; diff --git a/clang/test/SemaTemplate/instantiate-requires-clause.cpp b/clang/test/SemaTemplate/instantiate-requires-clause.cpp index 04b595717e6d..3b7dc3ddd740 100644 --- a/clang/test/SemaTemplate/instantiate-requires-clause.cpp +++ b/clang/test/SemaTemplate/instantiate-requires-clause.cpp @@ -39,3 +39,15 @@ struct S { }; static_assert(S::f(1)); + +constexpr auto value = 0; + +template +struct S2 { + template requires(value, true) + static constexpr auto f() requires(value, true) { + } +}; + +static_assert((S2::f(), true)); + From 29f14c1df25992db8c908e441c69e1fd4d4e4010 Mon Sep 17 00:00:00 2001 From: Saar Raz Date: Wed, 22 Jan 2020 04:21:09 +0200 Subject: [PATCH 055/374] [Concepts] Implement P1616R1 - Using unconstrained template template parameters with constrained templates Summary: Allow unconstrained template template parameters to accept constrainted templates as arguments. Reviewers: rsmith Subscribers: cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D73155 (cherry picked from commit d42d5eb8ea77b3a3a502a60ba3f053fb81a897f3) --- clang/lib/Sema/SemaTemplate.cpp | 5 +++++ .../test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 44b58e1a80af..c05a08f59acf 100755 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -7164,6 +7164,11 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param, // [temp.constr.order]. SmallVector ParamsAC, TemplateAC; Params->getAssociatedConstraints(ParamsAC); + // C++2a[temp.arg.template]p3 + // [...] In this comparison, if P is unconstrained, the constraints on A + // are not considered. + if (ParamsAC.empty()) + return false; Template->getAssociatedConstraints(TemplateAC); bool IsParamAtLeastAsConstrained; if (IsAtLeastAsConstrained(Param, ParamsAC, Template, TemplateAC, diff --git a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp index 593336163fa1..e7feae31889e 100644 --- a/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp +++ b/clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp @@ -7,9 +7,9 @@ template concept F = T::f(); // expected-note@-1{{similar constraint expressions not considered equivalent}} template class P> struct S1 { }; // expected-note 2{{'P' declared here}} -template struct X { }; // expected-note{{'X' declared here}} +template struct X { }; -template struct Y { }; // expected-note 2{{'Y' declared here}} +template struct Y { }; // expected-note{{'Y' declared here}} template struct Z { }; template struct W { }; // expected-note{{'W' declared here}} @@ -18,10 +18,10 @@ S1 s12; // expected-error{{template template argument 'Y' is more constrained S1 s13; S1 s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}} -template class P> struct S2 { }; // expected-note 2{{'P' declared here}} +template class P> struct S2 { }; -S2 s21; // expected-error{{template template argument 'X' is more constrained than template template parameter 'P'}} -S2 s22; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}} +S2 s21; +S2 s22; S2 s23; template