From 2db782047b295730cd018b2641a16461f87ce55e Mon Sep 17 00:00:00 2001 From: Ayush Sahay Date: Thu, 25 Apr 2024 22:30:02 +0530 Subject: [PATCH 001/468] [lldb] [llgs] Fix assertion in Handle_qfThreadInfo (#88301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo asserts if the number of processes under debug isn’t 1 and the multiprocess feature isn’t supported. This is so that we don't string IDs of threads belonging to different processes together without including the IDs of the processes themselves in the response when there are multiple processes under debug. However, it’s conceivable that we have no process under debug and the multiprocess feature isn’t supported. So, have GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo assert if the number of processes under debug is greater than 1 and the multiprocess feature isn’t supported. --- .../Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 3d37bb226a65..ae1a77e5be83 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -2087,7 +2087,7 @@ void GDBRemoteCommunicationServerLLGS::AddProcessThreads( GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::Handle_qfThreadInfo( StringExtractorGDBRemote &packet) { - assert(m_debugged_processes.size() == 1 || + assert(m_debugged_processes.size() <= 1 || bool(m_extensions_supported & NativeProcessProtocol::Extension::multiprocess)); -- GitLab From 5fb59e744783cf686e6a355c8331eeab90678c00 Mon Sep 17 00:00:00 2001 From: Amir Ayupov Date: Thu, 25 Apr 2024 19:08:51 +0200 Subject: [PATCH 002/468] [BOLT] Print program stats in perf2bolt/aggregate-only mode (#89763) --- bolt/include/bolt/Passes/BinaryPasses.h | 3 +-- bolt/lib/Profile/DataAggregator.cpp | 2 ++ bolt/lib/Rewrite/BinaryPassManager.cpp | 2 +- bolt/lib/Rewrite/BoltDiff.cpp | 2 +- bolt/test/X86/pre-aggregated-perf.test | 9 ++++++++- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/bolt/include/bolt/Passes/BinaryPasses.h b/bolt/include/bolt/Passes/BinaryPasses.h index 8d89ef8b5484..5d7692559eda 100644 --- a/bolt/include/bolt/Passes/BinaryPasses.h +++ b/bolt/include/bolt/Passes/BinaryPasses.h @@ -400,8 +400,7 @@ public: /// dyno stats categories. class PrintProgramStats : public BinaryFunctionPass { public: - explicit PrintProgramStats(const cl::opt &PrintPass) - : BinaryFunctionPass(PrintPass) {} + explicit PrintProgramStats() : BinaryFunctionPass(false) {} const char *getName() const override { return "print-stats"; } bool shouldPrint(const BinaryFunction &) const override { return false; } diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp index 0b2a4e86561f..70e324cc0165 100644 --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -14,6 +14,7 @@ #include "bolt/Profile/DataAggregator.h" #include "bolt/Core/BinaryContext.h" #include "bolt/Core/BinaryFunction.h" +#include "bolt/Passes/BinaryPasses.h" #include "bolt/Profile/BoltAddressTranslation.h" #include "bolt/Profile/Heatmap.h" #include "bolt/Profile/YAMLProfileWriter.h" @@ -611,6 +612,7 @@ Error DataAggregator::readProfile(BinaryContext &BC) { if (std::error_code EC = writeBATYAML(BC, opts::SaveProfile)) report_error("cannot create output data file", EC); } + BC.logBOLTErrorsAndQuitOnFatal(PrintProgramStats().runOnFunctions(BC)); } return Error::success(); diff --git a/bolt/lib/Rewrite/BinaryPassManager.cpp b/bolt/lib/Rewrite/BinaryPassManager.cpp index be4888ccfa56..cbb7199a53dd 100644 --- a/bolt/lib/Rewrite/BinaryPassManager.cpp +++ b/bolt/lib/Rewrite/BinaryPassManager.cpp @@ -356,7 +356,7 @@ Error BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) { // order they're registered. // Run this pass first to use stats for the original functions. - Manager.registerPass(std::make_unique(NeverPrint)); + Manager.registerPass(std::make_unique()); if (opts::PrintProfileStats) Manager.registerPass(std::make_unique(NeverPrint)); diff --git a/bolt/lib/Rewrite/BoltDiff.cpp b/bolt/lib/Rewrite/BoltDiff.cpp index fa43b7a2f92c..74b5ca18abce 100644 --- a/bolt/lib/Rewrite/BoltDiff.cpp +++ b/bolt/lib/Rewrite/BoltDiff.cpp @@ -292,7 +292,7 @@ class RewriteInstanceDiff { } } } - PrintProgramStats PPS(opts::NeverPrint); + PrintProgramStats PPS; outs() << "* BOLT-DIFF: Starting print program stats pass for binary 1\n"; RI1.BC->logBOLTErrorsAndQuitOnFatal(PPS.runOnFunctions(*RI1.BC)); outs() << "* BOLT-DIFF: Starting print program stats pass for binary 2\n"; diff --git a/bolt/test/X86/pre-aggregated-perf.test b/bolt/test/X86/pre-aggregated-perf.test index e8c3f64239a2..0bd44720f1b7 100644 --- a/bolt/test/X86/pre-aggregated-perf.test +++ b/bolt/test/X86/pre-aggregated-perf.test @@ -11,7 +11,14 @@ REQUIRES: system-linux RUN: yaml2obj %p/Inputs/blarge.yaml &> %t.exe RUN: perf2bolt %t.exe -o %t --pa -p %p/Inputs/pre-aggregated.txt -w %t.new \ -RUN: --profile-use-dfs +RUN: --profile-use-dfs | FileCheck %s + +RUN: llvm-bolt %t.exe -data %t -o %t.null | FileCheck %s +RUN: llvm-bolt %t.exe -data %t.new -o %t.null | FileCheck %s +RUN: llvm-bolt %t.exe -p %p/Inputs/pre-aggregated.txt --pa -o %t.null | FileCheck %s + +CHECK: BOLT-INFO: 4 out of 7 functions in the binary (57.1%) have non-empty execution profile + RUN: cat %t | sort | FileCheck %s -check-prefix=PERF2BOLT RUN: cat %t.new | FileCheck %s -check-prefix=NEWFORMAT -- GitLab From d94aeb507d71d72f4153b4c87c77fcb5187b3e9a Mon Sep 17 00:00:00 2001 From: Ryan Holt Date: Thu, 25 Apr 2024 13:12:55 -0400 Subject: [PATCH 003/468] [mlir][linalg] Add runtime verification for linalg ops (#89917) This commit implements runtime verification for LinalgStructuredOps using the existing `RuntimeVerifiableOpInterface`. The verification checks that the runtime sizes of the operands match the runtime sizes inferred by composing the loop ranges with the op's indexing maps. --- .../Linalg/Transforms/RuntimeOpVerification.h | 21 ++ mlir/include/mlir/InitAllDialects.h | 2 + .../RuntimeVerifiableOpInterface.td | 6 + .../Dialect/Linalg/Transforms/CMakeLists.txt | 2 + .../Transforms/RuntimeOpVerification.cpp | 135 ++++++++ .../Transforms/RuntimeOpVerification.cpp | 54 ++-- .../RuntimeVerifiableOpInterface.cpp | 21 ++ .../Dialect/Linalg/runtime-verification.mlir | 43 +++ .../Linalg/CPU/runtime-verification.mlir | 298 ++++++++++++++++++ 9 files changed, 549 insertions(+), 33 deletions(-) create mode 100644 mlir/include/mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h create mode 100644 mlir/lib/Dialect/Linalg/Transforms/RuntimeOpVerification.cpp create mode 100644 mlir/test/Dialect/Linalg/runtime-verification.mlir create mode 100644 mlir/test/Integration/Dialect/Linalg/CPU/runtime-verification.mlir diff --git a/mlir/include/mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h b/mlir/include/mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h new file mode 100644 index 000000000000..6c3643f7835c --- /dev/null +++ b/mlir/include/mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h @@ -0,0 +1,21 @@ +//===- RuntimeOpVerification.h - Op Verification ----------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_LINALG_RUNTIMEOPVERIFICATION_H +#define MLIR_DIALECT_LINALG_RUNTIMEOPVERIFICATION_H + +namespace mlir { +class DialectRegistry; + +namespace linalg { +void registerRuntimeVerifiableOpInterfaceExternalModels( + DialectRegistry ®istry); +} // namespace linalg +} // namespace mlir + +#endif // MLIR_DIALECT_LINALG_RUNTIMEOPVERIFICATION_H diff --git a/mlir/include/mlir/InitAllDialects.h b/mlir/include/mlir/InitAllDialects.h index c4d788cf8ed3..d9db21073e15 100644 --- a/mlir/include/mlir/InitAllDialects.h +++ b/mlir/include/mlir/InitAllDialects.h @@ -45,6 +45,7 @@ #include "mlir/Dialect/LLVMIR/ROCDLDialect.h" #include "mlir/Dialect/Linalg/IR/Linalg.h" #include "mlir/Dialect/Linalg/Transforms/AllInterfaces.h" +#include "mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h" #include "mlir/Dialect/MLProgram/IR/MLProgram.h" #include "mlir/Dialect/MLProgram/Transforms/BufferizableOpInterfaceImpl.h" #include "mlir/Dialect/MPI/IR/MPI.h" @@ -161,6 +162,7 @@ inline void registerAllDialects(DialectRegistry ®istry) { cf::registerBufferDeallocationOpInterfaceExternalModels(registry); gpu::registerBufferDeallocationOpInterfaceExternalModels(registry); linalg::registerAllDialectInterfaceImplementations(registry); + linalg::registerRuntimeVerifiableOpInterfaceExternalModels(registry); memref::registerAllocationOpInterfaceExternalModels(registry); memref::registerBufferViewFlowOpInterfaceExternalModels(registry); memref::registerRuntimeVerifiableOpInterfaceExternalModels(registry); diff --git a/mlir/include/mlir/Interfaces/RuntimeVerifiableOpInterface.td b/mlir/include/mlir/Interfaces/RuntimeVerifiableOpInterface.td index d5f11d00cc3d..6fd0df59d9d2 100644 --- a/mlir/include/mlir/Interfaces/RuntimeVerifiableOpInterface.td +++ b/mlir/include/mlir/Interfaces/RuntimeVerifiableOpInterface.td @@ -35,6 +35,12 @@ def RuntimeVerifiableOpInterface : OpInterface<"RuntimeVerifiableOpInterface"> { "::mlir::Location":$loc) >, ]; + + let extraClassDeclaration = [{ + /// Generate the error message that will be printed to the user when + /// verification fails. + static std::string generateErrorMessage(Operation *op, const std::string &msg); + }]; } #endif // MLIR_INTERFACES_RUNTIMEVERIFIABLEOPINTERFACE diff --git a/mlir/lib/Dialect/Linalg/Transforms/CMakeLists.txt b/mlir/lib/Dialect/Linalg/Transforms/CMakeLists.txt index ee6e391d0cc6..3b5282a09569 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/Linalg/Transforms/CMakeLists.txt @@ -27,6 +27,7 @@ add_mlir_dialect_library(MLIRLinalgTransforms NamedOpConversions.cpp Padding.cpp Promotion.cpp + RuntimeOpVerification.cpp Specialize.cpp Split.cpp SplitReduction.cpp @@ -60,6 +61,7 @@ add_mlir_dialect_library(MLIRLinalgTransforms MLIRFuncDialect MLIRFuncToLLVM MLIRFuncTransforms + MLIRIndexDialect MLIRInferTypeOpInterface MLIRIR MLIRMemRefDialect diff --git a/mlir/lib/Dialect/Linalg/Transforms/RuntimeOpVerification.cpp b/mlir/lib/Dialect/Linalg/Transforms/RuntimeOpVerification.cpp new file mode 100644 index 000000000000..b30182dc8407 --- /dev/null +++ b/mlir/lib/Dialect/Linalg/Transforms/RuntimeOpVerification.cpp @@ -0,0 +1,135 @@ +//===- RuntimeOpVerification.cpp - Op Verification ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "mlir/Dialect/Linalg/Transforms/RuntimeOpVerification.h" + +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Arith/IR/Arith.h" +#include "mlir/Dialect/Arith/Utils/Utils.h" +#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" +#include "mlir/Dialect/Index/IR/IndexAttrs.h" +#include "mlir/Dialect/Index/IR/IndexDialect.h" +#include "mlir/Dialect/Index/IR/IndexOps.h" +#include "mlir/Dialect/Linalg/IR/Linalg.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/Tensor/IR/Tensor.h" +#include "mlir/Interfaces/RuntimeVerifiableOpInterface.h" + +namespace mlir { +namespace linalg { +namespace { +/// Verify that the runtime sizes of the operands to linalg structured ops are +/// compatible with the runtime sizes inferred by composing the loop ranges with +/// the linalg op's indexing maps. This is similar to the verifier except that +/// here we insert IR to perform the verification at runtime. +template +struct StructuredOpInterface + : public RuntimeVerifiableOpInterface::ExternalModel< + StructuredOpInterface, T> { + void generateRuntimeVerification(Operation *op, OpBuilder &builder, + Location loc) const { + auto linalgOp = llvm::cast(op); + + SmallVector loopRanges = linalgOp.createLoopRanges(builder, loc); + auto [starts, ends, _] = getOffsetsSizesAndStrides(loopRanges); + + auto zero = builder.create(loc, 0); + auto one = builder.create(loc, 1); + + // Subtract one from the loop ends before composing with the indexing map + transform(ends, ends.begin(), [&](OpFoldResult end) { + auto endValue = getValueOrCreateConstantIndexOp(builder, loc, end); + return builder.createOrFold(loc, endValue, one); + }); + + for (OpOperand &opOperand : linalgOp->getOpOperands()) { + AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand); + auto startIndices = affine::makeComposedFoldedMultiResultAffineApply( + builder, loc, indexingMap, starts); + auto endIndices = affine::makeComposedFoldedMultiResultAffineApply( + builder, loc, indexingMap, ends); + + for (auto dim : llvm::seq(linalgOp.getRank(&opOperand))) { + auto startIndex = + getValueOrCreateConstantIndexOp(builder, loc, startIndices[dim]); + auto endIndex = + getValueOrCreateConstantIndexOp(builder, loc, endIndices[dim]); + + // Generate: + // minIndex = min(startIndex, endIndex) + // assert(minIndex >= 0) + // To ensure we do not generate a negative index. We take the minimum of + // the start and end indices in order to handle reverse loops such as + // `affine_map<(i) -> (3 - i)>` + auto min = + builder.createOrFold(loc, startIndex, endIndex); + auto cmpOp = builder.createOrFold( + loc, index::IndexCmpPredicate::SGE, min, zero); + auto msg = RuntimeVerifiableOpInterface::generateErrorMessage( + linalgOp, "unexpected negative result on dimension #" + + std::to_string(dim) + " of input/output operand #" + + std::to_string(opOperand.getOperandNumber())); + builder.createOrFold(loc, cmpOp, msg); + + // Generate: + // inferredDimSize = max(startIndex, endIndex) + 1 + // actualDimSize = dim(operand) + // assert(inferredDimSize <= actualDimSize) + // To ensure that we do not index past the bounds of the operands. + auto max = + builder.createOrFold(loc, startIndex, endIndex); + + auto inferredDimSize = + builder.createOrFold(loc, max, one); + + auto actualDimSize = + createOrFoldDimOp(builder, loc, opOperand.get(), dim); + + // Similar to the verifier, when the affine expression in the indexing + // map is complicated, we just check that the inferred dimension sizes + // are in the boundary of the operands' size. Being more precise than + // that is difficult. + auto predicate = isa(indexingMap.getResult(dim)) + ? index::IndexCmpPredicate::EQ + : index::IndexCmpPredicate::SLE; + + cmpOp = builder.createOrFold( + loc, predicate, inferredDimSize, actualDimSize); + msg = RuntimeVerifiableOpInterface::generateErrorMessage( + linalgOp, "dimension #" + std::to_string(dim) + + " of input/output operand #" + + std::to_string(opOperand.getOperandNumber()) + + " is incompatible with inferred dimension size"); + builder.createOrFold(loc, cmpOp, msg); + } + } + } +}; + +template +void attachInterface(MLIRContext *ctx) { + (OpTs::template attachInterface>(*ctx), ...); +} +} // namespace +} // namespace linalg +} // namespace mlir + +void mlir::linalg::registerRuntimeVerifiableOpInterfaceExternalModels( + DialectRegistry ®istry) { + registry.addExtension(+[](MLIRContext *ctx, LinalgDialect *) { + attachInterface< +#define GET_OP_LIST +#include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc" + >(ctx); + + // Load additional dialects of which ops may get created. + ctx->loadDialect(); + }); +} diff --git a/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp b/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp index 05b813a3b1e9..450bfa0cec0c 100644 --- a/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp +++ b/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp @@ -20,25 +20,6 @@ using namespace mlir; -/// Generate an error message string for the given op and the specified error. -static std::string generateErrorMessage(Operation *op, const std::string &msg) { - std::string buffer; - llvm::raw_string_ostream stream(buffer); - OpPrintingFlags flags; - // We may generate a lot of error messages and so we need to ensure the - // printing is fast. - flags.elideLargeElementsAttrs(); - flags.printGenericOpForm(); - flags.skipRegions(); - flags.useLocalScope(); - stream << "ERROR: Runtime op verification failed\n"; - op->print(stream, flags); - stream << "\n^ " << msg; - stream << "\nLocation: "; - op->getLoc().print(stream); - return stream.str(); -} - namespace mlir { namespace memref { namespace { @@ -62,8 +43,10 @@ struct CastOpInterface builder.create(loc, resultType.getRank()); Value isSameRank = builder.create( loc, arith::CmpIPredicate::eq, srcRank, resultRank); - builder.create(loc, isSameRank, - generateErrorMessage(op, "rank mismatch")); + builder.create( + loc, isSameRank, + RuntimeVerifiableOpInterface::generateErrorMessage(op, + "rank mismatch")); } // Get source offset and strides. We do not have an op to get offsets and @@ -101,8 +84,8 @@ struct CastOpInterface loc, arith::CmpIPredicate::eq, srcDimSz, resultDimSz); builder.create( loc, isSameSz, - generateErrorMessage(op, "size mismatch of dim " + - std::to_string(it.index()))); + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "size mismatch of dim " + std::to_string(it.index()))); } // Get result offset and strides. @@ -119,8 +102,10 @@ struct CastOpInterface builder.create(loc, resultOffset); Value isSameOffset = builder.create( loc, arith::CmpIPredicate::eq, srcOffset, resultOffsetVal); - builder.create(loc, isSameOffset, - generateErrorMessage(op, "offset mismatch")); + builder.create( + loc, isSameOffset, + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "offset mismatch")); } // Check strides. @@ -137,8 +122,8 @@ struct CastOpInterface loc, arith::CmpIPredicate::eq, srcStride, resultStrideVal); builder.create( loc, isSameStride, - generateErrorMessage(op, "stride mismatch of dim " + - std::to_string(it.index()))); + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "stride mismatch of dim " + std::to_string(it.index()))); } } }; @@ -178,7 +163,9 @@ struct LoadStoreOpInterface : andOp; } builder.create( - loc, assertCond, generateErrorMessage(op, "out-of-bounds access")); + loc, assertCond, + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "out-of-bounds access")); } }; @@ -248,7 +235,7 @@ struct ReinterpretCastOpInterface builder.create( loc, assertCond, - generateErrorMessage( + RuntimeVerifiableOpInterface::generateErrorMessage( op, "result of reinterpret_cast is out-of-bounds of the base memref")); } @@ -293,8 +280,8 @@ struct SubViewOpInterface builder.create( loc, assertCond, - generateErrorMessage(op, - "subview is out-of-bounds of the base memref")); + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "subview is out-of-bounds of the base memref")); } }; @@ -334,8 +321,9 @@ struct ExpandShapeOpInterface builder.create(loc, 0)); builder.create( loc, isModZero, - generateErrorMessage(op, "static result dims in reassoc group do not " - "divide src dim evenly")); + RuntimeVerifiableOpInterface::generateErrorMessage( + op, "static result dims in reassoc group do not " + "divide src dim evenly")); } } }; diff --git a/mlir/lib/Interfaces/RuntimeVerifiableOpInterface.cpp b/mlir/lib/Interfaces/RuntimeVerifiableOpInterface.cpp index 9205d8d8c34a..561e8d338687 100644 --- a/mlir/lib/Interfaces/RuntimeVerifiableOpInterface.cpp +++ b/mlir/lib/Interfaces/RuntimeVerifiableOpInterface.cpp @@ -11,6 +11,27 @@ namespace mlir { class Location; class OpBuilder; + +/// Generate an error message string for the given op and the specified error. +std::string +RuntimeVerifiableOpInterface::generateErrorMessage(Operation *op, + const std::string &msg) { + std::string buffer; + llvm::raw_string_ostream stream(buffer); + OpPrintingFlags flags; + // We may generate a lot of error messages and so we need to ensure the + // printing is fast. + flags.elideLargeElementsAttrs(); + flags.printGenericOpForm(); + flags.skipRegions(); + flags.useLocalScope(); + stream << "ERROR: Runtime op verification failed\n"; + op->print(stream, flags); + stream << "\n^ " << msg; + stream << "\nLocation: "; + op->getLoc().print(stream); + return stream.str(); +} } // namespace mlir /// Include the definitions of the interface. diff --git a/mlir/test/Dialect/Linalg/runtime-verification.mlir b/mlir/test/Dialect/Linalg/runtime-verification.mlir new file mode 100644 index 000000000000..a4f29d8457e5 --- /dev/null +++ b/mlir/test/Dialect/Linalg/runtime-verification.mlir @@ -0,0 +1,43 @@ +// RUN: mlir-opt %s -generate-runtime-verification | FileCheck %s + +// Most of the tests for linalg runtime-verification are implemented as integration tests. + +#identity = affine_map<(d0) -> (d0)> + +// CHECK-LABEL: @static_dims +func.func @static_dims(%arg0: tensor<5xf32>, %arg1: tensor<5xf32>) -> (tensor<5xf32>) { + // CHECK: %[[TRUE:.*]] = index.bool.constant true + // CHECK: cf.assert %[[TRUE]] + %result = tensor.empty() : tensor<5xf32> + %0 = linalg.generic { + indexing_maps = [#identity, #identity, #identity], + iterator_types = ["parallel"] + } ins(%arg0, %arg1 : tensor<5xf32>, tensor<5xf32>) + outs(%result : tensor<5xf32>) { + ^bb0(%gen_arg1: f32, %gen_arg2: f32, %out: f32) : + %tmp1 = arith.addf %gen_arg1, %gen_arg2 : f32 + linalg.yield %tmp1 : f32 + } -> tensor<5xf32> + return %0 : tensor<5xf32> +} + +// ----- + +#map = affine_map<() -> ()> + +// CHECK-LABEL: @scalars +func.func @scalars(%arg0: tensor, %arg1: tensor) -> (tensor) { + // No runtime checks are required if the operands are all scalars + // CHECK-NOT: cf.assert + %result = tensor.empty() : tensor + %0 = linalg.generic { + indexing_maps = [#map, #map, #map], + iterator_types = [] + } ins(%arg0, %arg1 : tensor, tensor) + outs(%result : tensor) { + ^bb0(%gen_arg1: f32, %gen_arg2: f32, %out: f32) : + %tmp1 = arith.addf %gen_arg1, %gen_arg2 : f32 + linalg.yield %tmp1 : f32 + } -> tensor + return %0 : tensor +} diff --git a/mlir/test/Integration/Dialect/Linalg/CPU/runtime-verification.mlir b/mlir/test/Integration/Dialect/Linalg/CPU/runtime-verification.mlir new file mode 100644 index 000000000000..b05ef9422e59 --- /dev/null +++ b/mlir/test/Integration/Dialect/Linalg/CPU/runtime-verification.mlir @@ -0,0 +1,298 @@ +// RUN: mlir-opt %s -generate-runtime-verification \ +// RUN: -one-shot-bufferize="bufferize-function-boundaries" \ +// RUN: -convert-linalg-to-loops \ +// RUN: -expand-strided-metadata \ +// RUN: -lower-affine \ +// RUN: -convert-scf-to-cf \ +// RUN: -test-cf-assert \ +// RUN: -convert-index-to-llvm \ +// RUN: -finalize-memref-to-llvm \ +// RUN: -convert-func-to-llvm \ +// RUN: -reconcile-unrealized-casts | \ +// RUN: mlir-cpu-runner -e main -entry-point-result=void \ +// RUN: -shared-libs=%mlir_runner_utils \ +// RUN: -shared-libs=%mlir_c_runner_utils 2>&1 | \ +// RUN: FileCheck %s + +func.func @main() { + %c5x = arith.constant dense<0.0> : tensor<5xf32> + %c4x = arith.constant dense<0.0> : tensor<4xf32> + %d5x = tensor.cast %c5x : tensor<5xf32> to tensor + %d4x = tensor.cast %c4x : tensor<4xf32> to tensor + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @simple_add(%d5x, %d5x) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #0 of input/output operand #1 is incompatible with inferred dimension size + func.call @simple_add(%d5x, %d4x) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #0 of input/output operand #1 is incompatible with inferred dimension size + func.call @simple_add(%d4x, %d5x) : (tensor, tensor) -> (tensor) + + %c1x1 = arith.constant dense<0.0> : tensor<1x1xf32> + %c1x4 = arith.constant dense<0.0> : tensor<1x4xf32> + %c4x4 = arith.constant dense<0.0> : tensor<4x4xf32> + %c4x5 = arith.constant dense<0.0> : tensor<4x5xf32> + %c5x4 = arith.constant dense<0.0> : tensor<5x4xf32> + %d1x1 = tensor.cast %c1x1 : tensor<1x1xf32> to tensor + %d1x4 = tensor.cast %c1x4 : tensor<1x4xf32> to tensor + %d4x4 = tensor.cast %c4x4 : tensor<4x4xf32> to tensor + %d4x5 = tensor.cast %c4x5 : tensor<4x5xf32> to tensor + %d5x4 = tensor.cast %c5x4 : tensor<5x4xf32> to tensor + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @broadcast_add(%d1x1, %d1x1) : (tensor, tensor) -> (tensor) + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @broadcast_add(%d1x1, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @broadcast_add(%d4x4, %d1x4) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #1 of input/output operand #1 is incompatible with inferred dimension size + func.call @broadcast_add(%d1x4, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #0 of input/output operand #1 is incompatible with inferred dimension size + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #1 of input/output operand #1 is incompatible with inferred dimension size + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #1 of input/output operand #2 is incompatible with inferred dimension size + func.call @broadcast_add(%d5x4, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @matmul_generic(%d5x4, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: ^ dimension #0 of input/output operand #1 is incompatible with inferred dimension size + func.call @matmul_generic(%d4x5, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @matmul_named(%d5x4, %d4x5) : (tensor, tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.matmul + // CHECK: ^ dimension #0 of input/output operand #1 is incompatible with inferred dimension size + func.call @matmul_named(%d4x5, %d4x5) : (tensor, tensor) -> (tensor) + + %c64x57 = arith.constant dense<0.0> : tensor<16x29xf32> + %c3x4 = arith.constant dense<0.0> : tensor<3x4xf32> + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @conv(%c64x57, %c3x4) : (tensor<16x29xf32>, tensor<3x4xf32>) -> (tensor<5x7xf32>) + + // CHECK-NOT: ERROR: Runtime op verification failed + func.call @reverse_from_3(%d4x) : (tensor) -> (tensor) + + // CHECK: ERROR: Runtime op verification failed + // CHECK: linalg.generic + // CHECK: unexpected negative result on dimension #0 of input/output operand #0 + func.call @reverse_from_3(%d5x) : (tensor) -> (tensor) + + return +} + + +#identity1D = affine_map<(d0) -> (d0)> + +func.func @simple_add(%arg0: tensor, %arg1: tensor) -> (tensor) { + %c0 = arith.constant 0 : index + %dim = tensor.dim %arg0, %c0 : tensor + %result = tensor.empty(%dim) : tensor + %0 = linalg.generic { + indexing_maps = [#identity1D, #identity1D, #identity1D], + iterator_types = ["parallel"] + } ins(%arg0, %arg1 : tensor, tensor) + outs(%result : tensor) { + ^bb0(%gen_arg1: f32, %gen_arg2: f32, %out: f32) : + %tmp1 = arith.addf %gen_arg1, %gen_arg2 : f32 + linalg.yield %tmp1 : f32 + } -> tensor + return %0 : tensor +} + +#broadcastD0 = affine_map<(d0, d1) -> (0, d1)> +#broadcastD1 = affine_map<(d0, d1) -> (d0, 0)> +#identity2D = affine_map<(d0, d1) -> (d0, d1)> + +func.func @broadcast_add(%arg0: tensor, %arg1: tensor) -> tensor { + // Calculate maximum dimension 0 + %c0 = arith.constant 0 : index + %dim = tensor.dim %arg0, %c0 : tensor + %dim_0 = tensor.dim %arg1, %c0 : tensor + %0 = arith.maxui %dim, %dim_0 : index + + // Calculate maximum dimension 1 + %c1 = arith.constant 1 : index + %dim_1 = tensor.dim %arg0, %c1 : tensor + %dim_2 = tensor.dim %arg1, %c1 : tensor + %1 = arith.maxui %dim_1, %dim_2 : index + + // Broadcast dimension 0 of %arg0 + %dim_3 = tensor.dim %arg0, %c0 : tensor + %2 = arith.cmpi eq, %dim_3, %c1 : index + %3 = scf.if %2 -> (tensor) { + %dim_7 = tensor.dim %arg0, %c1 : tensor + %12 = tensor.empty(%0, %dim_7) : tensor + %13 = linalg.generic { + indexing_maps = [#broadcastD0, #identity2D], + iterator_types = ["parallel", "parallel"] + } ins(%arg0 : tensor) outs(%12 : tensor) { + ^bb0(%in: f32, %out: f32): + linalg.yield %in : f32 + } -> tensor + scf.yield %13 : tensor + } else { + scf.yield %arg0 : tensor + } + + // Broadcast dimension 1 of %arg0 + %dim_4 = tensor.dim %3, %c1 : tensor + %4 = arith.cmpi eq, %dim_4, %c1 : index + %5 = scf.if %4 -> (tensor) { + %dim_7 = tensor.dim %3, %c0 : tensor + %12 = tensor.empty(%dim_7, %1) : tensor + %13 = linalg.generic { + indexing_maps = [#broadcastD1, #identity2D], + iterator_types = ["parallel", "parallel"] + } ins(%3 : tensor) outs(%12 : tensor) { + ^bb0(%in: f32, %out: f32): + linalg.yield %in : f32 + } -> tensor + scf.yield %13 : tensor + } else { + scf.yield %3 : tensor + } + + // Broadcast dimension 0 of %arg1 + %dim_5 = tensor.dim %arg1, %c0 : tensor + %6 = arith.cmpi eq, %dim_5, %c1 : index + %7 = scf.if %6 -> (tensor) { + %dim_7 = tensor.dim %arg1, %c1 : tensor + %12 = tensor.empty(%0, %dim_7) : tensor + %13 = linalg.generic { + indexing_maps = [#broadcastD0, #identity2D], + iterator_types = ["parallel", "parallel"] + } ins(%arg1 : tensor) outs(%12 : tensor) { + ^bb0(%in: f32, %out: f32): + linalg.yield %in : f32 + } -> tensor + scf.yield %13 : tensor + } else { + scf.yield %arg1 : tensor + } + + // Broadcast dimension 1 of %arg1 + %dim_6 = tensor.dim %7, %c1 : tensor + %8 = arith.cmpi eq, %dim_6, %c1 : index + %9 = scf.if %8 -> (tensor) { + %dim_7 = tensor.dim %7, %c0 : tensor + %12 = tensor.empty(%dim_7, %1) : tensor + %13 = linalg.generic { + indexing_maps = [#broadcastD1, #identity2D], + iterator_types = ["parallel", "parallel"] + } ins(%7 : tensor) outs(%12 : tensor) { + ^bb0(%in: f32, %out: f32): + linalg.yield %in : f32 + } -> tensor + scf.yield %13 : tensor + } else { + scf.yield %7 : tensor + } + + // Perform element-wise computation + %10 = tensor.empty(%0, %1) : tensor + %11 = linalg.generic { + indexing_maps = [#identity2D, #identity2D, #identity2D], + iterator_types = ["parallel", "parallel"] + } ins(%5, %9 : tensor, tensor) outs(%10 : tensor) { + ^bb0(%in: f32, %in_7: f32, %out: f32): + %12 = arith.addf %in, %in_7 : f32 + linalg.yield %12 : f32 + } -> tensor + return %11 : tensor +} + +#matmul_accesses = [ + affine_map<(m, n, k) -> (m, k)>, + affine_map<(m, n, k) -> (k, n)>, + affine_map<(m, n, k) -> (m, n)> +] +#matmul_trait = { + iterator_types = ["parallel", "parallel", "reduction"], + indexing_maps = #matmul_accesses +} + +func.func @matmul_generic(%arg0: tensor, %arg1: tensor) -> tensor { + %cf0 = arith.constant 0.0 : f32 + %ci0 = arith.constant 0 : index + %ci1 = arith.constant 1 : index + %d0 = tensor.dim %arg0, %ci0 : tensor + %d1 = tensor.dim %arg1, %ci1 : tensor + %splat = tensor.splat %cf0[%d0, %d1] : tensor + %0 = linalg.generic #matmul_trait ins(%arg0, %arg1 : tensor, tensor) outs(%splat : tensor) { + ^bb0(%in: f32, %in_0: f32, %out: f32): + %1 = arith.mulf %in, %in_0 : f32 + %2 = arith.addf %out, %1 : f32 + linalg.yield %2 : f32 + } -> tensor + return %0 : tensor +} + +func.func @matmul_named(%arg0: tensor, %arg1: tensor) -> tensor { + %cf0 = arith.constant 0.0 : f32 + %ci0 = arith.constant 0 : index + %ci1 = arith.constant 1 : index + %d0 = tensor.dim %arg0, %ci0 : tensor + %d1 = tensor.dim %arg1, %ci1 : tensor + %splat = tensor.splat %cf0[%d0, %d1] : tensor + %0 = linalg.matmul ins(%arg0, %arg1 : tensor, tensor) outs(%splat : tensor) -> tensor + return %0 : tensor +} + +#conv_trait = { + indexing_maps = [affine_map<(d0, d1, d2, d3) -> (d0 * 3 + d2, d1 * 4 + d3)>, affine_map<(d0, d1, d2, d3) -> (d2, d3)>, affine_map<(d0, d1, d2, d3) -> (d0, d1)>], + iterator_types = ["parallel", "parallel", "reduction", "reduction"] +} + +func.func @conv(%arg0: tensor<16x29xf32>, %arg1: tensor<3x4xf32>) -> (tensor<5x7xf32>) { + %c0 = arith.constant 0.0 : f32 + %splat = tensor.splat %c0 : tensor<5x7xf32> + %result = linalg.generic #conv_trait ins(%arg0, %arg1 : tensor<16x29xf32>, tensor<3x4xf32>) outs(%splat : tensor<5x7xf32>) { + ^bb0(%in: f32, %in_64: f32, %out: f32): + %5 = arith.mulf %in, %in_64 : f32 + %6 = arith.addf %out, %5 : f32 + linalg.yield %6 : f32 + } -> tensor<5x7xf32> + return %result : tensor<5x7xf32> +} + +#reverse_trait = { + indexing_maps = [ + affine_map<(i) -> (3 - i)>, + affine_map<(i) -> (i)> + ], + iterator_types = ["parallel"] +} + +func.func @reverse_from_3(%arg0: tensor) -> (tensor) { + %cf0 = arith.constant 0.0 : f32 + %ci0 = arith.constant 0 : index + %d0 = tensor.dim %arg0, %ci0 : tensor + %splat = tensor.splat %cf0[%d0] : tensor + %result = linalg.generic #reverse_trait ins(%arg0: tensor) outs(%splat: tensor) { + ^bb0(%a: f32, %b: f32): + linalg.yield %a : f32 + } -> tensor + return %result : tensor +} -- GitLab From 8dc7db7a24633f55ef28f2ab4b379386a34505f8 Mon Sep 17 00:00:00 2001 From: Bhuminjay Soni Date: Thu, 25 Apr 2024 22:49:59 +0530 Subject: [PATCH 004/468] [clang-tidy] Add clang-tidy check readability-math-missing-parentheses (#84481) This commit closes #80850 where author suggests adding a readability check to detect missing parentheses around mathematical expressions when operators of different priorities are used. Signed-off-by: 11happy --- .../clang-tidy/readability/CMakeLists.txt | 1 + .../MathMissingParenthesesCheck.cpp | 97 ++++++++++++++ .../readability/MathMissingParenthesesCheck.h | 34 +++++ .../readability/ReadabilityTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 6 + .../docs/clang-tidy/checks/list.rst | 1 + .../readability/math-missing-parentheses.rst | 27 ++++ .../readability/math-missing-parentheses.cpp | 120 ++++++++++++++++++ 8 files changed, 289 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index dd772d692025..41065fc8e878 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -28,6 +28,7 @@ add_clang_library(clangTidyReadabilityModule IsolateDeclarationCheck.cpp MagicNumbersCheck.cpp MakeMemberFunctionConstCheck.cpp + MathMissingParenthesesCheck.cpp MisleadingIndentationCheck.cpp MisplacedArrayIndexCheck.cpp NamedParameterCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp new file mode 100644 index 000000000000..d1e20b9074ce --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.cpp @@ -0,0 +1,97 @@ +//===--- MathMissingParenthesesCheck.cpp - clang-tidy ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MathMissingParenthesesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +void MathMissingParenthesesCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(binaryOperator(unless(hasParent(binaryOperator())), + unless(isAssignmentOperator()), + unless(isComparisonOperator()), + unless(hasAnyOperatorName("&&", "||")), + hasDescendant(binaryOperator())) + .bind("binOp"), + this); +} + +static int getPrecedence(const BinaryOperator *BinOp) { + if (!BinOp) + return 0; + switch (BinOp->getOpcode()) { + case BO_Mul: + case BO_Div: + case BO_Rem: + return 5; + case BO_Add: + case BO_Sub: + return 4; + case BO_And: + return 3; + case BO_Xor: + return 2; + case BO_Or: + return 1; + default: + return 0; + } +} +static void addParantheses(const BinaryOperator *BinOp, + const BinaryOperator *ParentBinOp, + ClangTidyCheck *Check, + const clang::SourceManager &SM, + const clang::LangOptions &LangOpts) { + if (!BinOp) + return; + + int Precedence1 = getPrecedence(BinOp); + int Precedence2 = getPrecedence(ParentBinOp); + + if (ParentBinOp != nullptr && Precedence1 != Precedence2) { + const clang::SourceLocation StartLoc = BinOp->getBeginLoc(); + const clang::SourceLocation EndLoc = + clang::Lexer::getLocForEndOfToken(BinOp->getEndLoc(), 0, SM, LangOpts); + if (EndLoc.isInvalid()) + return; + + Check->diag(StartLoc, + "'%0' has higher precedence than '%1'; add parentheses to " + "explicitly specify the order of operations") + << (Precedence1 > Precedence2 ? BinOp->getOpcodeStr() + : ParentBinOp->getOpcodeStr()) + << (Precedence1 > Precedence2 ? ParentBinOp->getOpcodeStr() + : BinOp->getOpcodeStr()) + << FixItHint::CreateInsertion(StartLoc, "(") + << FixItHint::CreateInsertion(EndLoc, ")") + << SourceRange(StartLoc, EndLoc); + } + + addParantheses(dyn_cast(BinOp->getLHS()->IgnoreImpCasts()), + BinOp, Check, SM, LangOpts); + addParantheses(dyn_cast(BinOp->getRHS()->IgnoreImpCasts()), + BinOp, Check, SM, LangOpts); +} + +void MathMissingParenthesesCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *BinOp = Result.Nodes.getNodeAs("binOp"); + std::vector< + std::pair>> + Insertions; + const SourceManager &SM = *Result.SourceManager; + const clang::LangOptions &LO = Result.Context->getLangOpts(); + addParantheses(BinOp, nullptr, this, SM, LO); +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h new file mode 100644 index 000000000000..9a9d2b3cfaab --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/MathMissingParenthesesCheck.h @@ -0,0 +1,34 @@ +//===--- MathMissingParenthesesCheck.h - clang-tidy -------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Check for mising parantheses in mathematical expressions that involve +/// operators of different priorities. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/math-missing-parentheses.html +class MathMissingParenthesesCheck : public ClangTidyCheck { +public: + MathMissingParenthesesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MATHMISSINGPARENTHESESCHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index 376b84683df7..d61c0ba39658 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -32,6 +32,7 @@ #include "IsolateDeclarationCheck.h" #include "MagicNumbersCheck.h" #include "MakeMemberFunctionConstCheck.h" +#include "MathMissingParenthesesCheck.h" #include "MisleadingIndentationCheck.h" #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" @@ -105,6 +106,8 @@ public: "readability-identifier-naming"); CheckFactories.registerCheck( "readability-implicit-bool-conversion"); + CheckFactories.registerCheck( + "readability-math-missing-parentheses"); CheckFactories.registerCheck( "readability-redundant-inline-specifier"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8616794ec575..2867fc958030 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -149,6 +149,12 @@ New checks Enforces consistent style for enumerators' initialization, covering three styles: none, first only, or all initialized explicitly. +- New :doc:`readability-math-missing-parentheses + ` check. + + Check for missing parentheses in mathematical expressions that involve + operators of different priorities. + - New :doc:`readability-use-std-min-max ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 139088752bc0..49747ff896ba 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -364,6 +364,7 @@ Clang-Tidy Checks :doc:`readability-isolate-declaration `, "Yes" :doc:`readability-magic-numbers `, :doc:`readability-make-member-function-const `, "Yes" + :doc:`readability-math-missing-parentheses `, "Yes" :doc:`readability-misleading-indentation `, :doc:`readability-misplaced-array-index `, "Yes" :doc:`readability-named-parameter `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst new file mode 100644 index 000000000000..21d66daab334 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/math-missing-parentheses.rst @@ -0,0 +1,27 @@ +.. title:: clang-tidy - readability-math-missing-parentheses + +readability-math-missing-parentheses +==================================== + +Check for missing parentheses in mathematical expressions that involve operators +of different priorities. + +Parentheses in mathematical expressions clarify the order +of operations, especially with different-priority operators. Lengthy or multiline +expressions can obscure this order, leading to coding errors. IDEs can aid clarity +by highlighting parentheses. Explicitly using parentheses also clarifies what the +developer had in mind when writing the expression. Ensuring their presence reduces +ambiguity and errors, promoting clearer and more maintainable code. + +Before: + +.. code-block:: c++ + + int x = 1 + 2 * 3 - 4 / 5; + + +After: + +.. code-block:: c++ + + int x = 1 + (2 * 3) - (4 / 5); \ No newline at end of file diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp new file mode 100644 index 000000000000..edbe2e1c37c7 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/math-missing-parentheses.cpp @@ -0,0 +1,120 @@ +// RUN: %check_clang_tidy %s readability-math-missing-parentheses %t + +#define MACRO_AND & +#define MACRO_ADD + +#define MACRO_OR | +#define MACRO_MULTIPLY * +#define MACRO_XOR ^ +#define MACRO_SUBTRACT - +#define MACRO_DIVIDE / + +int foo(){ + return 5; +} + +int bar(){ + return 4; +} + +class fun{ +public: + int A; + double B; + fun(){ + A = 5; + B = 5.4; + } +}; + +void f(){ + //CHECK-MESSAGES: :[[@LINE+2]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int a = 1 + (2 * 3); + int a = 1 + 2 * 3; + + int a_negative = 1 + (2 * 3); // No warning + + int b = 1 + 2 + 3; // No warning + + int c = 1 * 2 * 3; // No warning + + //CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+2]]:25: warning: '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int d = 1 + (2 * 3) - (4 / 5); + int d = 1 + 2 * 3 - 4 / 5; + + int d_negative = 1 + (2 * 3) - (4 / 5); // No warning + + //CHECK-MESSAGES: :[[@LINE+4]]:13: warning: '&' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '+' has higher precedence than '&'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+2]]:25: warning: '*' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int e = (1 & (2 + 3)) | (4 * 5); + int e = 1 & 2 + 3 | 4 * 5; + + int e_negative = (1 & (2 + 3)) | (4 * 5); // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int f = (1 * -2) + 4; + int f = 1 * -2 + 4; + + int f_negative = (1 * -2) + 4; // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int g = (1 * 2 * 3) + 4 + 5; + int g = 1 * 2 * 3 + 4 + 5; + + int g_negative = (1 * 2 * 3) + 4 + 5; // No warning + + //CHECK-MESSAGES: :[[@LINE+4]]:13: warning: '&' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+3]]:19: warning: '+' has higher precedence than '&'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+2]]:27: warning: '*' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int h = (120 & (2 + 3)) | (22 * 5); + int h = 120 & 2 + 3 | 22 * 5; + + int h_negative = (120 & (2 + 3)) | (22 * 5); // No warning + + int i = 1 & 2 & 3; // No warning + + int j = 1 | 2 | 3; // No warning + + int k = 1 ^ 2 ^ 3; // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '+' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int l = (1 + 2) ^ 3; + int l = 1 + 2 ^ 3; + + int l_negative = (1 + 2) ^ 3; // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int m = (2 * foo()) + bar(); + int m = 2 * foo() + bar(); + + int m_negative = (2 * foo()) + bar(); // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:13: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int n = (1.05 * foo()) + double(bar()); + int n = 1.05 * foo() + double(bar()); + + int n_negative = (1.05 * foo()) + double(bar()); // No warning + + //CHECK-MESSAGES: :[[@LINE+3]]:17: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int o = 1 + (obj.A * 3) + obj.B; + fun obj; + int o = 1 + obj.A * 3 + obj.B; + + int o_negative = 1 + (obj.A * 3) + obj.B; // No warning + + //CHECK-MESSAGES: :[[@LINE+2]]:18: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int p = 1U + (2 * 3); + int p = 1U + 2 * 3; + + int p_negative = 1U + (2 * 3); // No warning + + //CHECK-MESSAGES: :[[@LINE+7]]:13: warning: '+' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+6]]:25: warning: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+5]]:53: warning: '&' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+4]]:53: warning: '^' has higher precedence than '|'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+3]]:77: warning: '-' has higher precedence than '^'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-MESSAGES: :[[@LINE+2]]:94: warning: '/' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses] + //CHECK-FIXES: int q = (1 MACRO_ADD (2 MACRO_MULTIPLY 3)) MACRO_OR ((4 MACRO_AND 5) MACRO_XOR (6 MACRO_SUBTRACT (7 MACRO_DIVIDE 8))); + int q = 1 MACRO_ADD 2 MACRO_MULTIPLY 3 MACRO_OR 4 MACRO_AND 5 MACRO_XOR 6 MACRO_SUBTRACT 7 MACRO_DIVIDE 8; // No warning +} -- GitLab From 39adc8f423297c5741bb731bb8b1e545d558502c Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Thu, 25 Apr 2024 10:22:03 -0700 Subject: [PATCH 005/468] [NFC] Generalize ArraySections to work for OpenACC in the future (#89639) OpenACC is going to need an array sections implementation that is a simpler version/more restrictive version of the OpenMP version. This patch moves `OMPArraySectionExpr` to `Expr.h` and renames it `ArraySectionExpr`, then adds an enum to choose between the two. This also fixes a couple of 'drive-by' issues that I discovered on the way, but leaves the OpenACC Sema parts reasonably unimplemented (no semantic analysis implementation), as that will be a followup patch. --- clang/include/clang-c/Index.h | 3 +- clang/include/clang/AST/ASTContext.h | 3 +- clang/include/clang/AST/BuiltinTypes.def | 2 +- clang/include/clang/AST/ComputeDependence.h | 4 +- clang/include/clang/AST/Expr.h | 269 ++++++++++++++++++ clang/include/clang/AST/ExprOpenMP.h | 124 -------- clang/include/clang/AST/RecursiveASTVisitor.h | 2 +- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/include/clang/Basic/StmtNodes.td | 2 +- clang/include/clang/Sema/SemaOpenACC.h | 6 + .../include/clang/Serialization/ASTBitCodes.h | 6 +- clang/lib/AST/ASTContext.cpp | 12 +- clang/lib/AST/ComputeDependence.cpp | 7 +- clang/lib/AST/Expr.cpp | 6 +- clang/lib/AST/ExprClassification.cpp | 2 +- clang/lib/AST/ExprConstant.cpp | 2 +- clang/lib/AST/ItaniumMangle.cpp | 2 +- clang/lib/AST/NSAPI.cpp | 2 +- clang/lib/AST/StmtPrinter.cpp | 4 +- clang/lib/AST/StmtProfile.cpp | 2 +- clang/lib/AST/Type.cpp | 6 +- clang/lib/AST/TypeLoc.cpp | 2 +- clang/lib/CodeGen/CGExpr.cpp | 18 +- clang/lib/CodeGen/CGOpenMPRuntime.cpp | 43 ++- clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp | 4 +- clang/lib/CodeGen/CGStmtOpenMP.cpp | 4 +- clang/lib/CodeGen/CodeGenFunction.h | 4 +- clang/lib/Parse/ParseExpr.cpp | 26 +- clang/lib/Sema/SemaChecking.cpp | 6 +- clang/lib/Sema/SemaExceptionSpec.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 24 +- clang/lib/Sema/SemaInit.cpp | 4 +- clang/lib/Sema/SemaOpenACC.cpp | 15 + clang/lib/Sema/SemaOpenMP.cpp | 71 +++-- clang/lib/Sema/TreeTransform.h | 51 ++-- clang/lib/Serialization/ASTCommon.cpp | 4 +- clang/lib/Serialization/ASTReader.cpp | 6 +- clang/lib/Serialization/ASTReaderStmt.cpp | 18 +- clang/lib/Serialization/ASTWriterStmt.cpp | 13 +- .../Checkers/DereferenceChecker.cpp | 4 +- .../Checkers/IdenticalExprChecker.cpp | 2 +- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 2 +- clang/test/OpenMP/task_depend_messages.cpp | 2 +- .../ParserOpenACC/parse-cache-construct.cpp | 4 +- clang/test/ParserOpenACC/parse-clauses.c | 4 +- clang/tools/libclang/CIndex.cpp | 4 +- clang/tools/libclang/CXCursor.cpp | 4 +- 47 files changed, 513 insertions(+), 296 deletions(-) diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 7a8bd985a91f..365b607c7411 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -1644,8 +1644,9 @@ enum CXCursorKind { CXCursor_ObjCSelfExpr = 146, /** OpenMP 5.0 [2.1.5, Array Section]. + * OpenACC 3.3 [2.7.1, Data Specification for Data Clauses (Sub Arrays)] */ - CXCursor_OMPArraySectionExpr = 147, + CXCursor_ArraySectionExpr = 147, /** Represents an @available(...) check. */ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 24388ad5dea5..a662d94994ec 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1127,7 +1127,8 @@ public: CanQualType OCLSamplerTy, OCLEventTy, OCLClkEventTy; CanQualType OCLQueueTy, OCLReserveIDTy; CanQualType IncompleteMatrixIdxTy; - CanQualType OMPArraySectionTy, OMPArrayShapingTy, OMPIteratorTy; + CanQualType ArraySectionTy; + CanQualType OMPArrayShapingTy, OMPIteratorTy; #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ CanQualType Id##Ty; #include "clang/Basic/OpenCLExtensionTypes.def" diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index c04f6f6f1271..0a36fdc5d9c0 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -320,7 +320,7 @@ PLACEHOLDER_TYPE(ARCUnbridgedCast, ARCUnbridgedCastTy) PLACEHOLDER_TYPE(IncompleteMatrixIdx, IncompleteMatrixIdxTy) // A placeholder type for OpenMP array sections. -PLACEHOLDER_TYPE(OMPArraySection, OMPArraySectionTy) +PLACEHOLDER_TYPE(ArraySection, ArraySectionTy) // A placeholder type for OpenMP array shaping operation. PLACEHOLDER_TYPE(OMPArrayShaping, OMPArrayShapingTy) diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h index 7abf9141237d..6d3a51c379f9 100644 --- a/clang/include/clang/AST/ComputeDependence.h +++ b/clang/include/clang/AST/ComputeDependence.h @@ -94,7 +94,7 @@ class DesignatedInitExpr; class ParenListExpr; class PseudoObjectExpr; class AtomicExpr; -class OMPArraySectionExpr; +class ArraySectionExpr; class OMPArrayShapingExpr; class OMPIteratorExpr; class ObjCArrayLiteral; @@ -189,7 +189,7 @@ ExprDependence computeDependence(ParenListExpr *E); ExprDependence computeDependence(PseudoObjectExpr *E); ExprDependence computeDependence(AtomicExpr *E); -ExprDependence computeDependence(OMPArraySectionExpr *E); +ExprDependence computeDependence(ArraySectionExpr *E); ExprDependence computeDependence(OMPArrayShapingExpr *E); ExprDependence computeDependence(OMPIteratorExpr *E); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 2bfefeabc348..f2bf667636dc 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -6610,6 +6610,275 @@ public: }; +/// This class represents BOTH the OpenMP Array Section and OpenACC 'subarray', +/// with a boolean differentiator. +/// OpenMP 5.0 [2.1.5, Array Sections]. +/// To specify an array section in an OpenMP construct, array subscript +/// expressions are extended with the following syntax: +/// \code +/// [ lower-bound : length : stride ] +/// [ lower-bound : length : ] +/// [ lower-bound : length ] +/// [ lower-bound : : stride ] +/// [ lower-bound : : ] +/// [ lower-bound : ] +/// [ : length : stride ] +/// [ : length : ] +/// [ : length ] +/// [ : : stride ] +/// [ : : ] +/// [ : ] +/// \endcode +/// The array section must be a subset of the original array. +/// Array sections are allowed on multidimensional arrays. Base language array +/// subscript expressions can be used to specify length-one dimensions of +/// multidimensional array sections. +/// Each of the lower-bound, length, and stride expressions if specified must be +/// an integral type expressions of the base language. When evaluated +/// they represent a set of integer values as follows: +/// \code +/// { lower-bound, lower-bound + stride, lower-bound + 2 * stride,... , +/// lower-bound + ((length - 1) * stride) } +/// \endcode +/// The lower-bound and length must evaluate to non-negative integers. +/// The stride must evaluate to a positive integer. +/// When the size of the array dimension is not known, the length must be +/// specified explicitly. +/// When the stride is absent it defaults to 1. +/// When the length is absent it defaults to ⌈(size − lower-bound)/stride⌉, +/// where size is the size of the array dimension. When the lower-bound is +/// absent it defaults to 0. +/// +/// +/// OpenACC 3.3 [2.7.1 Data Specification in Data Clauses] +/// In C and C++, a subarray is an array name followed by an extended array +/// range specification in brackets, with start and length, such as +/// +/// AA[2:n] +/// +/// If the lower bound is missing, zero is used. If the length is missing and +/// the array has known size, the size of the array is used; otherwise the +/// length is required. The subarray AA[2:n] means elements AA[2], AA[3], . . . +/// , AA[2+n-1]. In C and C++, a two dimensional array may be declared in at +/// least four ways: +/// +/// -Statically-sized array: float AA[100][200]; +/// -Pointer to statically sized rows: typedef float row[200]; row* BB; +/// -Statically-sized array of pointers: float* CC[200]; +/// -Pointer to pointers: float** DD; +/// +/// Each dimension may be statically sized, or a pointer to dynamically +/// allocated memory. Each of these may be included in a data clause using +/// subarray notation to specify a rectangular array: +/// +/// -AA[2:n][0:200] +/// -BB[2:n][0:m] +/// -CC[2:n][0:m] +/// -DD[2:n][0:m] +/// +/// Multidimensional rectangular subarrays in C and C++ may be specified for any +/// array with any combination of statically-sized or dynamically-allocated +/// dimensions. For statically sized dimensions, all dimensions except the first +/// must specify the whole extent to preserve the contiguous data restriction, +/// discussed below. For dynamically allocated dimensions, the implementation +/// will allocate pointers in device memory corresponding to the pointers in +/// local memory and will fill in those pointers as appropriate. +/// +/// In Fortran, a subarray is an array name followed by a comma-separated list +/// of range specifications in parentheses, with lower and upper bound +/// subscripts, such as +/// +/// arr(1:high,low:100) +/// +/// If either the lower or upper bounds are missing, the declared or allocated +/// bounds of the array, if known, are used. All dimensions except the last must +/// specify the whole extent, to preserve the contiguous data restriction, +/// discussed below. +/// +/// Restrictions +/// +/// -In Fortran, the upper bound for the last dimension of an assumed-size dummy +/// array must be specified. +/// +/// -In C and C++, the length for dynamically allocated dimensions of an array +/// must be explicitly specified. +/// +/// -In C and C++, modifying pointers in pointer arrays during the data +/// lifetime, either on the host or on the device, may result in undefined +/// behavior. +/// +/// -If a subarray appears in a data clause, the implementation may choose to +/// allocate memory for only that subarray on the accelerator. +/// +/// -In Fortran, array pointers may appear, but pointer association is not +/// preserved in device memory. +/// +/// -Any array or subarray in a data clause, including Fortran array pointers, +/// must be a contiguous section of memory, except for dynamic multidimensional +/// C arrays. +/// +/// -In C and C++, if a variable or array of composite type appears, all the +/// data members of the struct or class are allocated and copied, as +/// appropriate. If a composite member is a pointer type, the data addressed by +/// that pointer are not implicitly copied. +/// +/// -In Fortran, if a variable or array of composite type appears, all the +/// members of that derived type are allocated and copied, as appropriate. If +/// any member has the allocatable or pointer attribute, the data accessed +/// through that member are not copied. +/// +/// -If an expression is used in a subscript or subarray expression in a clause +/// on a data construct, the same value is used when copying data at the end of +/// the data region, even if the values of variables in the expression change +/// during the data region. +class ArraySectionExpr : public Expr { + friend class ASTStmtReader; + friend class ASTStmtWriter; + +public: + enum ArraySectionType { OMPArraySection, OpenACCArraySection }; + +private: + enum { + BASE, + LOWER_BOUND, + LENGTH, + STRIDE, + END_EXPR, + OPENACC_END_EXPR = STRIDE + }; + + ArraySectionType ASType = OMPArraySection; + Stmt *SubExprs[END_EXPR] = {nullptr}; + SourceLocation ColonLocFirst; + SourceLocation ColonLocSecond; + SourceLocation RBracketLoc; + +public: + // Constructor for OMP array sections, which include a 'stride'. + ArraySectionExpr(Expr *Base, Expr *LowerBound, Expr *Length, Expr *Stride, + QualType Type, ExprValueKind VK, ExprObjectKind OK, + SourceLocation ColonLocFirst, SourceLocation ColonLocSecond, + SourceLocation RBracketLoc) + : Expr(ArraySectionExprClass, Type, VK, OK), ASType(OMPArraySection), + ColonLocFirst(ColonLocFirst), ColonLocSecond(ColonLocSecond), + RBracketLoc(RBracketLoc) { + setBase(Base); + setLowerBound(LowerBound); + setLength(Length); + setStride(Stride); + setDependence(computeDependence(this)); + } + + // Constructor for OpenACC sub-arrays, which do not permit a 'stride'. + ArraySectionExpr(Expr *Base, Expr *LowerBound, Expr *Length, QualType Type, + ExprValueKind VK, ExprObjectKind OK, SourceLocation ColonLoc, + SourceLocation RBracketLoc) + : Expr(ArraySectionExprClass, Type, VK, OK), ASType(OpenACCArraySection), + ColonLocFirst(ColonLoc), RBracketLoc(RBracketLoc) { + setBase(Base); + setLowerBound(LowerBound); + setLength(Length); + setDependence(computeDependence(this)); + } + + /// Create an empty array section expression. + explicit ArraySectionExpr(EmptyShell Shell) + : Expr(ArraySectionExprClass, Shell) {} + + /// Return original type of the base expression for array section. + static QualType getBaseOriginalType(const Expr *Base); + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ArraySectionExprClass; + } + + bool isOMPArraySection() const { return ASType == OMPArraySection; } + bool isOpenACCArraySection() const { return ASType == OpenACCArraySection; } + + /// Get base of the array section. + Expr *getBase() { return cast(SubExprs[BASE]); } + const Expr *getBase() const { return cast(SubExprs[BASE]); } + + /// Get lower bound of array section. + Expr *getLowerBound() { return cast_or_null(SubExprs[LOWER_BOUND]); } + const Expr *getLowerBound() const { + return cast_or_null(SubExprs[LOWER_BOUND]); + } + + /// Get length of array section. + Expr *getLength() { return cast_or_null(SubExprs[LENGTH]); } + const Expr *getLength() const { return cast_or_null(SubExprs[LENGTH]); } + + /// Get stride of array section. + Expr *getStride() { + assert(ASType != OpenACCArraySection && + "Stride not valid in OpenACC subarrays"); + return cast_or_null(SubExprs[STRIDE]); + } + + const Expr *getStride() const { + assert(ASType != OpenACCArraySection && + "Stride not valid in OpenACC subarrays"); + return cast_or_null(SubExprs[STRIDE]); + } + + SourceLocation getBeginLoc() const LLVM_READONLY { + return getBase()->getBeginLoc(); + } + SourceLocation getEndLoc() const LLVM_READONLY { return RBracketLoc; } + + SourceLocation getColonLocFirst() const { return ColonLocFirst; } + SourceLocation getColonLocSecond() const { + assert(ASType != OpenACCArraySection && + "second colon for stride not valid in OpenACC subarrays"); + return ColonLocSecond; + } + SourceLocation getRBracketLoc() const { return RBracketLoc; } + + SourceLocation getExprLoc() const LLVM_READONLY { + return getBase()->getExprLoc(); + } + + child_range children() { + return child_range( + &SubExprs[BASE], + &SubExprs[ASType == OMPArraySection ? END_EXPR : OPENACC_END_EXPR]); + } + + const_child_range children() const { + return const_child_range( + &SubExprs[BASE], + &SubExprs[ASType == OMPArraySection ? END_EXPR : OPENACC_END_EXPR]); + } + +private: + /// Set base of the array section. + void setBase(Expr *E) { SubExprs[BASE] = E; } + + /// Set lower bound of the array section. + void setLowerBound(Expr *E) { SubExprs[LOWER_BOUND] = E; } + + /// Set length of the array section. + void setLength(Expr *E) { SubExprs[LENGTH] = E; } + + /// Set length of the array section. + void setStride(Expr *E) { + assert(ASType != OpenACCArraySection && + "Stride not valid in OpenACC subarrays"); + SubExprs[STRIDE] = E; + } + + void setColonLocFirst(SourceLocation L) { ColonLocFirst = L; } + + void setColonLocSecond(SourceLocation L) { + assert(ASType != OpenACCArraySection && + "second colon for stride not valid in OpenACC subarrays"); + ColonLocSecond = L; + } + void setRBracketLoc(SourceLocation L) { RBracketLoc = L; } +}; + /// Frontend produces RecoveryExprs on semantic errors that prevent creating /// other well-formed expressions. E.g. when type-checking of a binary operator /// fails, we cannot produce a BinaryOperator expression. Instead, we can choose diff --git a/clang/include/clang/AST/ExprOpenMP.h b/clang/include/clang/AST/ExprOpenMP.h index be5b1f3fdd11..54a0c203f656 100644 --- a/clang/include/clang/AST/ExprOpenMP.h +++ b/clang/include/clang/AST/ExprOpenMP.h @@ -17,130 +17,6 @@ #include "clang/AST/Expr.h" namespace clang { -/// OpenMP 5.0 [2.1.5, Array Sections]. -/// To specify an array section in an OpenMP construct, array subscript -/// expressions are extended with the following syntax: -/// \code -/// [ lower-bound : length : stride ] -/// [ lower-bound : length : ] -/// [ lower-bound : length ] -/// [ lower-bound : : stride ] -/// [ lower-bound : : ] -/// [ lower-bound : ] -/// [ : length : stride ] -/// [ : length : ] -/// [ : length ] -/// [ : : stride ] -/// [ : : ] -/// [ : ] -/// \endcode -/// The array section must be a subset of the original array. -/// Array sections are allowed on multidimensional arrays. Base language array -/// subscript expressions can be used to specify length-one dimensions of -/// multidimensional array sections. -/// Each of the lower-bound, length, and stride expressions if specified must be -/// an integral type expressions of the base language. When evaluated -/// they represent a set of integer values as follows: -/// \code -/// { lower-bound, lower-bound + stride, lower-bound + 2 * stride,... , -/// lower-bound + ((length - 1) * stride) } -/// \endcode -/// The lower-bound and length must evaluate to non-negative integers. -/// The stride must evaluate to a positive integer. -/// When the size of the array dimension is not known, the length must be -/// specified explicitly. -/// When the stride is absent it defaults to 1. -/// When the length is absent it defaults to ⌈(size − lower-bound)/stride⌉, -/// where size is the size of the array dimension. When the lower-bound is -/// absent it defaults to 0. -class OMPArraySectionExpr : public Expr { - enum { BASE, LOWER_BOUND, LENGTH, STRIDE, END_EXPR }; - Stmt *SubExprs[END_EXPR]; - SourceLocation ColonLocFirst; - SourceLocation ColonLocSecond; - SourceLocation RBracketLoc; - -public: - OMPArraySectionExpr(Expr *Base, Expr *LowerBound, Expr *Length, Expr *Stride, - QualType Type, ExprValueKind VK, ExprObjectKind OK, - SourceLocation ColonLocFirst, - SourceLocation ColonLocSecond, SourceLocation RBracketLoc) - : Expr(OMPArraySectionExprClass, Type, VK, OK), - ColonLocFirst(ColonLocFirst), ColonLocSecond(ColonLocSecond), - RBracketLoc(RBracketLoc) { - SubExprs[BASE] = Base; - SubExprs[LOWER_BOUND] = LowerBound; - SubExprs[LENGTH] = Length; - SubExprs[STRIDE] = Stride; - setDependence(computeDependence(this)); - } - - /// Create an empty array section expression. - explicit OMPArraySectionExpr(EmptyShell Shell) - : Expr(OMPArraySectionExprClass, Shell) {} - - /// An array section can be written only as Base[LowerBound:Length]. - - /// Get base of the array section. - Expr *getBase() { return cast(SubExprs[BASE]); } - const Expr *getBase() const { return cast(SubExprs[BASE]); } - /// Set base of the array section. - void setBase(Expr *E) { SubExprs[BASE] = E; } - - /// Return original type of the base expression for array section. - static QualType getBaseOriginalType(const Expr *Base); - - /// Get lower bound of array section. - Expr *getLowerBound() { return cast_or_null(SubExprs[LOWER_BOUND]); } - const Expr *getLowerBound() const { - return cast_or_null(SubExprs[LOWER_BOUND]); - } - /// Set lower bound of the array section. - void setLowerBound(Expr *E) { SubExprs[LOWER_BOUND] = E; } - - /// Get length of array section. - Expr *getLength() { return cast_or_null(SubExprs[LENGTH]); } - const Expr *getLength() const { return cast_or_null(SubExprs[LENGTH]); } - /// Set length of the array section. - void setLength(Expr *E) { SubExprs[LENGTH] = E; } - - /// Get stride of array section. - Expr *getStride() { return cast_or_null(SubExprs[STRIDE]); } - const Expr *getStride() const { return cast_or_null(SubExprs[STRIDE]); } - /// Set length of the array section. - void setStride(Expr *E) { SubExprs[STRIDE] = E; } - - SourceLocation getBeginLoc() const LLVM_READONLY { - return getBase()->getBeginLoc(); - } - SourceLocation getEndLoc() const LLVM_READONLY { return RBracketLoc; } - - SourceLocation getColonLocFirst() const { return ColonLocFirst; } - void setColonLocFirst(SourceLocation L) { ColonLocFirst = L; } - - SourceLocation getColonLocSecond() const { return ColonLocSecond; } - void setColonLocSecond(SourceLocation L) { ColonLocSecond = L; } - - SourceLocation getRBracketLoc() const { return RBracketLoc; } - void setRBracketLoc(SourceLocation L) { RBracketLoc = L; } - - SourceLocation getExprLoc() const LLVM_READONLY { - return getBase()->getExprLoc(); - } - - static bool classof(const Stmt *T) { - return T->getStmtClass() == OMPArraySectionExprClass; - } - - child_range children() { - return child_range(&SubExprs[BASE], &SubExprs[END_EXPR]); - } - - const_child_range children() const { - return const_child_range(&SubExprs[BASE], &SubExprs[END_EXPR]); - } -}; - /// An explicit cast in C or a C-style cast in C++, which uses the syntax /// ([s1][s2]...[sn])expr. For example: @c ([3][3])f. class OMPArrayShapingExpr final diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 7eb92e304a38..f9b145b4e86a 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2740,7 +2740,7 @@ DEF_TRAVERSE_STMT(CXXMemberCallExpr, {}) DEF_TRAVERSE_STMT(AddrLabelExpr, {}) DEF_TRAVERSE_STMT(ArraySubscriptExpr, {}) DEF_TRAVERSE_STMT(MatrixSubscriptExpr, {}) -DEF_TRAVERSE_STMT(OMPArraySectionExpr, {}) +DEF_TRAVERSE_STMT(ArraySectionExpr, {}) DEF_TRAVERSE_STMT(OMPArrayShapingExpr, {}) DEF_TRAVERSE_STMT(OMPIteratorExpr, {}) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6732a1a98452..fdca82934cb4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11161,7 +11161,7 @@ def err_omp_declare_mapper_redefinition : Error< "redefinition of user-defined mapper for type %0 with name %1">; def err_omp_invalid_mapper: Error< "cannot find a valid user-defined mapper for type %0 with name %1">; -def err_omp_array_section_use : Error<"OpenMP array section is not allowed here">; +def err_array_section_use : Error<"%select{OpenACC sub-array|OpenMP array section}0 is not allowed here">; def err_omp_array_shaping_use : Error<"OpenMP array shaping operation is not allowed here">; def err_omp_iterator_use : Error<"OpenMP iterator is not allowed here">; def err_omp_typecheck_section_value : Error< diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index b4e3ae573b95..305f19daa4a9 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -71,7 +71,7 @@ def OffsetOfExpr : StmtNode; def UnaryExprOrTypeTraitExpr : StmtNode; def ArraySubscriptExpr : StmtNode; def MatrixSubscriptExpr : StmtNode; -def OMPArraySectionExpr : StmtNode; +def ArraySectionExpr : StmtNode; def OMPIteratorExpr : StmtNode; def CallExpr : StmtNode; def MemberExpr : StmtNode; diff --git a/clang/include/clang/Sema/SemaOpenACC.h b/clang/include/clang/Sema/SemaOpenACC.h index ea28617f79b8..da19503c2902 100644 --- a/clang/include/clang/Sema/SemaOpenACC.h +++ b/clang/include/clang/Sema/SemaOpenACC.h @@ -193,6 +193,12 @@ public: /// conversions and diagnostics to 'int'. ExprResult ActOnIntExpr(OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation Loc, Expr *IntExpr); + + /// Checks and creates an Array Section used in an OpenACC construct/clause. + ExprResult ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc, + Expr *LowerBound, + SourceLocation ColonLocFirst, Expr *Length, + SourceLocation RBLoc); }; } // namespace clang diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 186c3b722ced..a8df5a0bda08 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -973,8 +973,8 @@ enum PredefinedTypeIDs { /// OpenCL reserve_id type. PREDEF_TYPE_RESERVE_ID_ID = 41, - /// The placeholder type for OpenMP array section. - PREDEF_TYPE_OMP_ARRAY_SECTION = 42, + /// The placeholder type for an array section. + PREDEF_TYPE_ARRAY_SECTION = 42, /// The '__float128' type PREDEF_TYPE_FLOAT128_ID = 43, @@ -1926,7 +1926,7 @@ enum StmtCode { STMT_OMP_TARGET_TEAMS_GENERIC_LOOP_DIRECTIVE, STMT_OMP_PARALLEL_GENERIC_LOOP_DIRECTIVE, STMT_OMP_TARGET_PARALLEL_GENERIC_LOOP_DIRECTIVE, - EXPR_OMP_ARRAY_SECTION, + EXPR_ARRAY_SECTION, EXPR_OMP_ARRAY_SHAPING, EXPR_OMP_ITERATOR, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 475b47afa639..cbf4932aff9a 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1321,16 +1321,14 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, // Placeholder type for OMP array sections. if (LangOpts.OpenMP) { - InitBuiltinType(OMPArraySectionTy, BuiltinType::OMPArraySection); + InitBuiltinType(ArraySectionTy, BuiltinType::ArraySection); InitBuiltinType(OMPArrayShapingTy, BuiltinType::OMPArrayShaping); InitBuiltinType(OMPIteratorTy, BuiltinType::OMPIterator); } - // Placeholder type for OpenACC array sections. - if (LangOpts.OpenACC) { - // FIXME: Once we implement OpenACC array sections in Sema, this will either - // be combined with the OpenMP type, or given its own type. In the meantime, - // just use the OpenMP type so that parsing can work. - InitBuiltinType(OMPArraySectionTy, BuiltinType::OMPArraySection); + // Placeholder type for OpenACC array sections, if we are ALSO in OMP mode, + // don't bother, as we're just using the same type as OMP. + if (LangOpts.OpenACC && !LangOpts.OpenMP) { + InitBuiltinType(ArraySectionTy, BuiltinType::ArraySection); } if (LangOpts.MatrixTypes) InitBuiltinType(IncompleteMatrixIdxTy, BuiltinType::IncompleteMatrixIdx); diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 5ec3013fabba..bad8e75b2f87 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -443,12 +443,17 @@ ExprDependence clang::computeDependence(ObjCIndirectCopyRestoreExpr *E) { return E->getSubExpr()->getDependence(); } -ExprDependence clang::computeDependence(OMPArraySectionExpr *E) { +ExprDependence clang::computeDependence(ArraySectionExpr *E) { auto D = E->getBase()->getDependence(); if (auto *LB = E->getLowerBound()) D |= LB->getDependence(); if (auto *Len = E->getLength()) D |= Len->getDependence(); + + if (E->isOMPArraySection()) { + if (auto *Stride = E->getStride()) + D |= Stride->getDependence(); + } return D; } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 9eec7edc9d1a..63dcdb919c71 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -3680,7 +3680,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, case ParenExprClass: case ArraySubscriptExprClass: case MatrixSubscriptExprClass: - case OMPArraySectionExprClass: + case ArraySectionExprClass: case OMPArrayShapingExprClass: case OMPIteratorExprClass: case MemberExprClass: @@ -5060,9 +5060,9 @@ QualType AtomicExpr::getValueType() const { return T; } -QualType OMPArraySectionExpr::getBaseOriginalType(const Expr *Base) { +QualType ArraySectionExpr::getBaseOriginalType(const Expr *Base) { unsigned ArraySectionCount = 0; - while (auto *OASE = dyn_cast(Base->IgnoreParens())) { + while (auto *OASE = dyn_cast(Base->IgnoreParens())) { Base = OASE->getBase(); ++ArraySectionCount; } diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp index 7026fca8554c..2bb8f9aeedc7 100644 --- a/clang/lib/AST/ExprClassification.cpp +++ b/clang/lib/AST/ExprClassification.cpp @@ -145,7 +145,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::FunctionParmPackExprClass: case Expr::MSPropertyRefExprClass: case Expr::MSPropertySubscriptExprClass: - case Expr::OMPArraySectionExprClass: + case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: return Cl::CL_LValue; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index de3c2a63913e..ea3e7304a742 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -16130,7 +16130,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::StringLiteralClass: case Expr::ArraySubscriptExprClass: case Expr::MatrixSubscriptExprClass: - case Expr::OMPArraySectionExprClass: + case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: case Expr::MemberExprClass: diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 106c69dd5bee..ed9e6eeb36c7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -4715,7 +4715,7 @@ recurse: case Expr::MSPropertySubscriptExprClass: case Expr::TypoExprClass: // This should no longer exist in the AST by now. case Expr::RecoveryExprClass: - case Expr::OMPArraySectionExprClass: + case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: case Expr::CXXInheritedCtorInitExprClass: diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index ecc56c13fb75..6f586173edb0 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -462,7 +462,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::PseudoObject: case BuiltinType::BuiltinFn: case BuiltinType::IncompleteMatrixIdx: - case BuiltinType::OMPArraySection: + case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: case BuiltinType::BFloat16: diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 5855ab3141ed..f010d36513a4 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -1521,7 +1521,7 @@ void StmtPrinter::VisitMatrixSubscriptExpr(MatrixSubscriptExpr *Node) { OS << "]"; } -void StmtPrinter::VisitOMPArraySectionExpr(OMPArraySectionExpr *Node) { +void StmtPrinter::VisitArraySectionExpr(ArraySectionExpr *Node) { PrintExpr(Node->getBase()); OS << "["; if (Node->getLowerBound()) @@ -1531,7 +1531,7 @@ void StmtPrinter::VisitOMPArraySectionExpr(OMPArraySectionExpr *Node) { if (Node->getLength()) PrintExpr(Node->getLength()); } - if (Node->getColonLocSecond().isValid()) { + if (Node->isOMPArraySection() && Node->getColonLocSecond().isValid()) { OS << ":"; if (Node->getStride()) PrintExpr(Node->getStride()); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index c81724f84dd9..a95f5c6103e2 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -1435,7 +1435,7 @@ void StmtProfiler::VisitMatrixSubscriptExpr(const MatrixSubscriptExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitOMPArraySectionExpr(const OMPArraySectionExpr *S) { +void StmtProfiler::VisitArraySectionExpr(const ArraySectionExpr *S) { VisitExpr(S); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index cb22c91a12aa..8aaa6801d85b 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3413,8 +3413,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "reserve_id_t"; case IncompleteMatrixIdx: return ""; - case OMPArraySection: - return ""; + case ArraySection: + return ""; case OMPArrayShaping: return ""; case OMPIterator: @@ -4710,7 +4710,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const { case BuiltinType::BuiltinFn: case BuiltinType::NullPtr: case BuiltinType::IncompleteMatrixIdx: - case BuiltinType::OMPArraySection: + case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: return false; diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 21e152f6aea8..ce45b47d5cfe 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -429,7 +429,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { #include "clang/Basic/WebAssemblyReferenceTypes.def" case BuiltinType::BuiltinFn: case BuiltinType::IncompleteMatrixIdx: - case BuiltinType::OMPArraySection: + case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: return TST_unspecified; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 931cb391342e..c94322f51e46 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1621,8 +1621,8 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E, return EmitArraySubscriptExpr(cast(E)); case Expr::MatrixSubscriptExprClass: return EmitMatrixSubscriptExpr(cast(E)); - case Expr::OMPArraySectionExprClass: - return EmitOMPArraySectionExpr(cast(E)); + case Expr::ArraySectionExprClass: + return EmitArraySectionExpr(cast(E)); case Expr::ExtVectorElementExprClass: return EmitExtVectorElementExpr(cast(E)); case Expr::CXXThisExprClass: @@ -4363,8 +4363,8 @@ static Address emitOMPArraySectionBase(CodeGenFunction &CGF, const Expr *Base, QualType BaseTy, QualType ElTy, bool IsLowerBound) { LValue BaseLVal; - if (auto *ASE = dyn_cast(Base->IgnoreParenImpCasts())) { - BaseLVal = CGF.EmitOMPArraySectionExpr(ASE, IsLowerBound); + if (auto *ASE = dyn_cast(Base->IgnoreParenImpCasts())) { + BaseLVal = CGF.EmitArraySectionExpr(ASE, IsLowerBound); if (BaseTy->isArrayType()) { Address Addr = BaseLVal.getAddress(CGF); BaseInfo = BaseLVal.getBaseInfo(); @@ -4396,9 +4396,13 @@ static Address emitOMPArraySectionBase(CodeGenFunction &CGF, const Expr *Base, return CGF.EmitPointerWithAlignment(Base, &BaseInfo, &TBAAInfo); } -LValue CodeGenFunction::EmitOMPArraySectionExpr(const OMPArraySectionExpr *E, - bool IsLowerBound) { - QualType BaseTy = OMPArraySectionExpr::getBaseOriginalType(E->getBase()); +LValue CodeGenFunction::EmitArraySectionExpr(const ArraySectionExpr *E, + bool IsLowerBound) { + + assert(!E->isOpenACCArraySection() && + "OpenACC Array section codegen not implemented"); + + QualType BaseTy = ArraySectionExpr::getBaseOriginalType(E->getBase()); QualType ResultExprTy; if (auto *AT = getContext().getAsArrayType(BaseTy)) ResultExprTy = AT->getElementType(); diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index 2ae11e129c75..e39c7c58d278 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -742,8 +742,8 @@ LValue ReductionCodeGen::emitSharedLValue(CodeGenFunction &CGF, const Expr *E) { LValue ReductionCodeGen::emitSharedLValueUB(CodeGenFunction &CGF, const Expr *E) { - if (const auto *OASE = dyn_cast(E)) - return CGF.EmitOMPArraySectionExpr(OASE, /*IsLowerBound=*/false); + if (const auto *OASE = dyn_cast(E)) + return CGF.EmitArraySectionExpr(OASE, /*IsLowerBound=*/false); return LValue(); } @@ -800,7 +800,7 @@ void ReductionCodeGen::emitSharedOrigLValue(CodeGenFunction &CGF, unsigned N) { void ReductionCodeGen::emitAggregateType(CodeGenFunction &CGF, unsigned N) { QualType PrivateType = getPrivateType(N); - bool AsArraySection = isa(ClausesData[N].Ref); + bool AsArraySection = isa(ClausesData[N].Ref); if (!PrivateType->isVariablyModifiedType()) { Sizes.emplace_back( CGF.getTypeSize(OrigAddresses[N].first.getType().getNonReferenceType()), @@ -941,9 +941,9 @@ static Address castToBase(CodeGenFunction &CGF, QualType BaseTy, QualType ElTy, static const VarDecl *getBaseDecl(const Expr *Ref, const DeclRefExpr *&DE) { const VarDecl *OrigVD = nullptr; - if (const auto *OASE = dyn_cast(Ref)) { + if (const auto *OASE = dyn_cast(Ref)) { const Expr *Base = OASE->getBase()->IgnoreParenImpCasts(); - while (const auto *TempOASE = dyn_cast(Base)) + while (const auto *TempOASE = dyn_cast(Base)) Base = TempOASE->getBase()->IgnoreParenImpCasts(); while (const auto *TempASE = dyn_cast(Base)) Base = TempASE->getBase()->IgnoreParenImpCasts(); @@ -3570,9 +3570,8 @@ getPointerAndSize(CodeGenFunction &CGF, const Expr *E) { SizeVal = CGF.Builder.CreateNUWMul(SizeVal, Sz); } } else if (const auto *ASE = - dyn_cast(E->IgnoreParenImpCasts())) { - LValue UpAddrLVal = - CGF.EmitOMPArraySectionExpr(ASE, /*IsLowerBound=*/false); + dyn_cast(E->IgnoreParenImpCasts())) { + LValue UpAddrLVal = CGF.EmitArraySectionExpr(ASE, /*IsLowerBound=*/false); Address UpAddrAddress = UpAddrLVal.getAddress(CGF); llvm::Value *UpAddr = CGF.Builder.CreateConstGEP1_32( UpAddrAddress.getElementType(), UpAddrAddress.emitRawPointer(CGF), @@ -6672,8 +6671,8 @@ private: // Given that an array section is considered a built-in type, we need to // do the calculation based on the length of the section instead of relying // on CGF.getTypeSize(E->getType()). - if (const auto *OAE = dyn_cast(E)) { - QualType BaseTy = OMPArraySectionExpr::getBaseOriginalType( + if (const auto *OAE = dyn_cast(E)) { + QualType BaseTy = ArraySectionExpr::getBaseOriginalType( OAE->getBase()->IgnoreParenImpCasts()) .getCanonicalType(); @@ -6779,7 +6778,7 @@ private: /// Return true if the provided expression is a final array section. A /// final array section, is one whose length can't be proved to be one. bool isFinalArraySectionExpression(const Expr *E) const { - const auto *OASE = dyn_cast(E); + const auto *OASE = dyn_cast(E); // It is not an array section and therefore not a unity-size one. if (!OASE) @@ -6795,7 +6794,7 @@ private: // for this dimension. Also, we should always expect a length if the // base type is pointer. if (!Length) { - QualType BaseQTy = OMPArraySectionExpr::getBaseOriginalType( + QualType BaseQTy = ArraySectionExpr::getBaseOriginalType( OASE->getBase()->IgnoreParenImpCasts()) .getCanonicalType(); if (const auto *ATy = dyn_cast(BaseQTy.getTypePtr())) @@ -7027,7 +7026,7 @@ private: Address BP = Address::invalid(); const Expr *AssocExpr = I->getAssociatedExpression(); const auto *AE = dyn_cast(AssocExpr); - const auto *OASE = dyn_cast(AssocExpr); + const auto *OASE = dyn_cast(AssocExpr); const auto *OAShE = dyn_cast(AssocExpr); if (isa(AssocExpr)) { @@ -7179,14 +7178,14 @@ private: // special treatment for array sections given that they are built-in // types. const auto *OASE = - dyn_cast(I->getAssociatedExpression()); + dyn_cast(I->getAssociatedExpression()); const auto *OAShE = dyn_cast(I->getAssociatedExpression()); const auto *UO = dyn_cast(I->getAssociatedExpression()); const auto *BO = dyn_cast(I->getAssociatedExpression()); bool IsPointer = OAShE || - (OASE && OMPArraySectionExpr::getBaseOriginalType(OASE) + (OASE && ArraySectionExpr::getBaseOriginalType(OASE) .getCanonicalType() ->isAnyPointerType()) || I->getAssociatedExpression()->getType()->isAnyPointerType(); @@ -7207,7 +7206,7 @@ private: assert((Next == CE || isa(Next->getAssociatedExpression()) || isa(Next->getAssociatedExpression()) || - isa(Next->getAssociatedExpression()) || + isa(Next->getAssociatedExpression()) || isa(Next->getAssociatedExpression()) || isa(Next->getAssociatedExpression()) || isa(Next->getAssociatedExpression())) && @@ -7439,7 +7438,7 @@ private: PartialStruct.LowestElem = {FieldIndex, LowestElem}; if (IsFinalArraySection) { Address HB = - CGF.EmitOMPArraySectionExpr(OASE, /*IsLowerBound=*/false) + CGF.EmitArraySectionExpr(OASE, /*IsLowerBound=*/false) .getAddress(CGF); PartialStruct.HighestElem = {FieldIndex, HB}; } else { @@ -7452,7 +7451,7 @@ private: } else if (FieldIndex > PartialStruct.HighestElem.first) { if (IsFinalArraySection) { Address HB = - CGF.EmitOMPArraySectionExpr(OASE, /*IsLowerBound=*/false) + CGF.EmitArraySectionExpr(OASE, /*IsLowerBound=*/false) .getAddress(CGF); PartialStruct.HighestElem = {FieldIndex, HB}; } else { @@ -7510,12 +7509,12 @@ private: for (const OMPClauseMappableExprCommon::MappableComponent &Component : Components) { const Expr *AssocExpr = Component.getAssociatedExpression(); - const auto *OASE = dyn_cast(AssocExpr); + const auto *OASE = dyn_cast(AssocExpr); if (!OASE) continue; - QualType Ty = OMPArraySectionExpr::getBaseOriginalType(OASE->getBase()); + QualType Ty = ArraySectionExpr::getBaseOriginalType(OASE->getBase()); auto *CAT = Context.getAsConstantArrayType(Ty); auto *VAT = Context.getAsVariableArrayType(Ty); @@ -7589,7 +7588,7 @@ private: continue; } - const auto *OASE = dyn_cast(AssocExpr); + const auto *OASE = dyn_cast(AssocExpr); if (!OASE) continue; @@ -8780,7 +8779,7 @@ static ValueDecl *getDeclFromThisExpr(const Expr *E) { if (!E) return nullptr; - if (const auto *OASE = dyn_cast(E->IgnoreParenCasts())) + if (const auto *OASE = dyn_cast(E->IgnoreParenCasts())) if (const MemberExpr *ME = dyn_cast(OASE->getBase()->IgnoreParenImpCasts())) return ME->getMemberDecl(); diff --git a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp index eb716520e5ff..87496c8e488c 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntimeGPU.cpp @@ -92,9 +92,9 @@ static const ValueDecl *getPrivateItem(const Expr *RefExpr) { while (const auto *TempASE = dyn_cast(Base)) Base = TempASE->getBase()->IgnoreParenImpCasts(); RefExpr = Base; - } else if (auto *OASE = dyn_cast(RefExpr)) { + } else if (auto *OASE = dyn_cast(RefExpr)) { const Expr *Base = OASE->getBase()->IgnoreParenImpCasts(); - while (const auto *TempOASE = dyn_cast(Base)) + while (const auto *TempOASE = dyn_cast(Base)) Base = TempOASE->getBase()->IgnoreParenImpCasts(); while (const auto *TempASE = dyn_cast(Base)) Base = TempASE->getBase()->IgnoreParenImpCasts(); diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp index a0a8a07c76ba..ef3aa3a8e0dc 100644 --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -1256,7 +1256,7 @@ void CodeGenFunction::EmitOMPReductionClauseInit( const auto *LHSVD = cast(cast(*ILHS)->getDecl()); const auto *RHSVD = cast(cast(*IRHS)->getDecl()); QualType Type = PrivateVD->getType(); - bool isaOMPArraySectionExpr = isa(IRef); + bool isaOMPArraySectionExpr = isa(IRef); if (isaOMPArraySectionExpr && Type->isVariablyModifiedType()) { // Store the address of the original variable associated with the LHS // implicit variable. @@ -7289,7 +7289,7 @@ void CodeGenFunction::EmitOMPUseDevicePtrClause( static const VarDecl *getBaseDecl(const Expr *Ref) { const Expr *Base = Ref->IgnoreParenImpCasts(); - while (const auto *OASE = dyn_cast(Base)) + while (const auto *OASE = dyn_cast(Base)) Base = OASE->getBase()->IgnoreParenImpCasts(); while (const auto *ASE = dyn_cast(Base)) Base = ASE->getBase()->IgnoreParenImpCasts(); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index a751649cdb59..33fb7a41912b 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4169,8 +4169,8 @@ public: LValue EmitArraySubscriptExpr(const ArraySubscriptExpr *E, bool Accessed = false); LValue EmitMatrixSubscriptExpr(const MatrixSubscriptExpr *E); - LValue EmitOMPArraySectionExpr(const OMPArraySectionExpr *E, - bool IsLowerBound = true); + LValue EmitArraySectionExpr(const ArraySectionExpr *E, + bool IsLowerBound = true); LValue EmitExtVectorElementExpr(const ExtVectorElementExpr *E); LValue EmitMemberExpr(const MemberExpr *E); LValue EmitObjCIsaExpr(const ObjCIsaExpr *E); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 32d96f81c4c8..7d6febb04a82 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -31,6 +31,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCUDA.h" +#include "clang/Sema/SemaOpenACC.h" #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/SemaSYCL.h" #include "clang/Sema/TypoCorrection.h" @@ -2070,15 +2071,22 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { if (!LHS.isInvalid() && !HasError && !Length.isInvalid() && !Stride.isInvalid() && Tok.is(tok::r_square)) { if (ColonLocFirst.isValid() || ColonLocSecond.isValid()) { - // FIXME: OpenACC hasn't implemented Sema/Array section handling at a - // semantic level yet. For now, just reuse the OpenMP implementation - // as it gets the parsing/type management mostly right, and we can - // replace this call to ActOnOpenACCArraySectionExpr in the future. - // Eventually we'll genericize the OPenMPArraySectionExpr type as - // well. - LHS = Actions.OpenMP().ActOnOMPArraySectionExpr( - LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0], - ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), RLoc); + // Like above, AllowOpenACCArraySections is 'more specific' and only + // enabled when actively parsing a 'var' in a 'var-list' during + // clause/'cache' construct parsing, so it is more specific. So we + // should do it first, so that the correct node gets created. + if (AllowOpenACCArraySections) { + assert(!Stride.isUsable() && !ColonLocSecond.isValid() && + "Stride/second colon not allowed for OpenACC"); + LHS = Actions.OpenACC().ActOnArraySectionExpr( + LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0], + ColonLocFirst, Length.get(), RLoc); + } else { + LHS = Actions.OpenMP().ActOnOMPArraySectionExpr( + LHS.get(), Loc, ArgExprs.empty() ? nullptr : ArgExprs[0], + ColonLocFirst, ColonLocSecond, Length.get(), Stride.get(), + RLoc); + } } else { LHS = Actions.ActOnArraySubscriptExpr(getCurScope(), LHS.get(), Loc, ArgExprs, RLoc); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 67132701b41c..e33113ab9c4c 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -18724,8 +18724,10 @@ void Sema::CheckArrayAccess(const Expr *expr) { expr = cast(expr)->getBase(); break; } - case Stmt::OMPArraySectionExprClass: { - const OMPArraySectionExpr *ASE = cast(expr); + case Stmt::ArraySectionExprClass: { + const ArraySectionExpr *ASE = cast(expr); + // FIXME: We should probably be checking all of the elements to the + // 'length' here as well. if (ASE->getLowerBound()) CheckArrayAccess(ASE->getBase(), ASE->getLowerBound(), /*ASE=*/nullptr, AllowOnePastEnd > 0); diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp index 00384f9dc16a..c9dd6bb2413e 100644 --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -1314,7 +1314,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) { // Some might be dependent for other reasons. case Expr::ArraySubscriptExprClass: case Expr::MatrixSubscriptExprClass: - case Expr::OMPArraySectionExprClass: + case Expr::ArraySectionExprClass: case Expr::OMPArrayShapingExprClass: case Expr::OMPIteratorExprClass: case Expr::BinaryOperatorClass: diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 5c861467bc10..50f92c496a53 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5069,11 +5069,18 @@ ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation rbLoc) { if (base && !base->getType().isNull() && - base->hasPlaceholderType(BuiltinType::OMPArraySection)) - return OpenMP().ActOnOMPArraySectionExpr(base, lbLoc, ArgExprs.front(), - SourceLocation(), SourceLocation(), - /*Length*/ nullptr, - /*Stride=*/nullptr, rbLoc); + base->hasPlaceholderType(BuiltinType::ArraySection)) { + auto *AS = cast(base); + if (AS->isOMPArraySection()) + return OpenMP().ActOnOMPArraySectionExpr( + base, lbLoc, ArgExprs.front(), SourceLocation(), SourceLocation(), + /*Length*/ nullptr, + /*Stride=*/nullptr, rbLoc); + + return OpenACC().ActOnArraySectionExpr(base, lbLoc, ArgExprs.front(), + SourceLocation(), /*Length*/ nullptr, + rbLoc); + } // Since this might be a postfix expression, get rid of ParenListExprs. if (isa(base)) { @@ -6361,7 +6368,7 @@ static bool isPlaceholderToRemoveAsArg(QualType type) { case BuiltinType::BoundMember: case BuiltinType::BuiltinFn: case BuiltinType::IncompleteMatrixIdx: - case BuiltinType::OMPArraySection: + case BuiltinType::ArraySection: case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: return true; @@ -21343,8 +21350,9 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) { return ExprError(); // Expressions of unknown type. - case BuiltinType::OMPArraySection: - Diag(E->getBeginLoc(), diag::err_omp_array_section_use); + case BuiltinType::ArraySection: + Diag(E->getBeginLoc(), diag::err_array_section_use) + << cast(E)->isOMPArraySection(); return ExprError(); // Expressions of unknown type. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 793e16df1789..003a157990d3 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7753,9 +7753,9 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, break; } - case Stmt::OMPArraySectionExprClass: { + case Stmt::ArraySectionExprClass: { visitLocalsRetainedByInitializer(Path, - cast(Init)->getBase(), + cast(Init)->getBase(), Visit, true, EnableLifetimeWarnings); break; } diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index ba69e71e30a1..d5cfe82a5d70 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -423,6 +423,21 @@ ExprResult SemaOpenACC::ActOnIntExpr(OpenACCDirectiveKind DK, return IntExpr; } +ExprResult SemaOpenACC::ActOnArraySectionExpr(Expr *Base, SourceLocation LBLoc, + Expr *LowerBound, + SourceLocation ColonLoc, + Expr *Length, + SourceLocation RBLoc) { + ASTContext &Context = getASTContext(); + + // TODO OpenACC: We likely have to reproduce a lot of the same logic from the + // OMP version of this, but at the moment we don't have a good way to test it, + // so for now we'll just create the node. + return new (Context) + ArraySectionExpr(Base, LowerBound, Length, Context.ArraySectionTy, + VK_LValue, OK_Ordinary, ColonLoc, RBLoc); +} + bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc) { return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true); diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 5ba09926acf2..cee8da495c54 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -2230,7 +2230,7 @@ bool SemaOpenMP::isOpenMPCapturedByRef(const ValueDecl *D, unsigned Level, dyn_cast(Last->getAssociatedExpression()); if ((UO && UO->getOpcode() == UO_Deref) || isa(Last->getAssociatedExpression()) || - isa(Last->getAssociatedExpression()) || + isa(Last->getAssociatedExpression()) || isa(EI->getAssociatedExpression()) || isa(Last->getAssociatedExpression())) { IsVariableAssociatedWithSection = true; @@ -3884,7 +3884,7 @@ public: MappableComponent &MC) { return MC.getAssociatedDeclaration() == nullptr && - (isa( + (isa( MC.getAssociatedExpression()) || isa( MC.getAssociatedExpression()) || @@ -4062,7 +4062,7 @@ public: // Do both expressions have the same kind? if (CCI->getAssociatedExpression()->getStmtClass() != SC.getAssociatedExpression()->getStmtClass()) - if (!((isa( + if (!((isa( SC.getAssociatedExpression()) || isa( SC.getAssociatedExpression())) && @@ -5428,9 +5428,9 @@ static std::pair getPrivateItem(Sema &S, Expr *&RefExpr, Base = TempASE->getBase()->IgnoreParenImpCasts(); RefExpr = Base; IsArrayExpr = ArraySubscript; - } else if (auto *OASE = dyn_cast_or_null(RefExpr)) { + } else if (auto *OASE = dyn_cast_or_null(RefExpr)) { Expr *Base = OASE->getBase()->IgnoreParenImpCasts(); - while (auto *TempOASE = dyn_cast(Base)) + while (auto *TempOASE = dyn_cast(Base)) Base = TempOASE->getBase()->IgnoreParenImpCasts(); while (auto *TempASE = dyn_cast(Base)) Base = TempASE->getBase()->IgnoreParenImpCasts(); @@ -6060,10 +6060,10 @@ processImplicitMapsWithDefaultMappers(Sema &S, DSAStackTy *Stack, // Array section - need to check for the mapping of the array section // element. QualType CanonType = E->getType().getCanonicalType(); - if (CanonType->isSpecificBuiltinType(BuiltinType::OMPArraySection)) { - const auto *OASE = cast(E->IgnoreParenImpCasts()); + if (CanonType->isSpecificBuiltinType(BuiltinType::ArraySection)) { + const auto *OASE = cast(E->IgnoreParenImpCasts()); QualType BaseType = - OMPArraySectionExpr::getBaseOriginalType(OASE->getBase()); + ArraySectionExpr::getBaseOriginalType(OASE->getBase()); QualType ElemType; if (const auto *ATy = BaseType->getAsArrayTypeUnsafe()) ElemType = ATy->getElementType(); @@ -19513,7 +19513,7 @@ struct ReductionData { } // namespace static bool checkOMPArraySectionConstantForReduction( - ASTContext &Context, const OMPArraySectionExpr *OASE, bool &SingleElement, + ASTContext &Context, const ArraySectionExpr *OASE, bool &SingleElement, SmallVectorImpl &ArraySizes) { const Expr *Length = OASE->getLength(); if (Length == nullptr) { @@ -19540,7 +19540,7 @@ static bool checkOMPArraySectionConstantForReduction( // We require length = 1 for all array sections except the right-most to // guarantee that the memory region is contiguous and has no holes in it. - while (const auto *TempOASE = dyn_cast(Base)) { + while (const auto *TempOASE = dyn_cast(Base)) { Length = TempOASE->getLength(); if (Length == nullptr) { // For array sections of the form [1:] or [:], we would need to analyze @@ -19745,12 +19745,12 @@ static bool actOnOMPReductionKindClause( Expr *TaskgroupDescriptor = nullptr; QualType Type; auto *ASE = dyn_cast(RefExpr->IgnoreParens()); - auto *OASE = dyn_cast(RefExpr->IgnoreParens()); + auto *OASE = dyn_cast(RefExpr->IgnoreParens()); if (ASE) { Type = ASE->getType().getNonReferenceType(); } else if (OASE) { QualType BaseType = - OMPArraySectionExpr::getBaseOriginalType(OASE->getBase()); + ArraySectionExpr::getBaseOriginalType(OASE->getBase()); if (const auto *ATy = BaseType->getAsArrayTypeUnsafe()) Type = ATy->getElementType(); else @@ -21284,10 +21284,10 @@ OMPClause *SemaOpenMP::ActOnOpenMPDependClause( // List items used in depend clauses cannot be zero-length array // sections. QualType ExprTy = RefExpr->getType().getNonReferenceType(); - const auto *OASE = dyn_cast(SimpleExpr); + const auto *OASE = dyn_cast(SimpleExpr); if (OASE) { QualType BaseType = - OMPArraySectionExpr::getBaseOriginalType(OASE->getBase()); + ArraySectionExpr::getBaseOriginalType(OASE->getBase()); if (BaseType.isNull()) return nullptr; if (const auto *ATy = BaseType->getAsArrayTypeUnsafe()) @@ -21346,7 +21346,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPDependClause( Res = SemaRef.CreateBuiltinUnaryOp(ELoc, UO_AddrOf, RefExpr->IgnoreParenImpCasts()); } - if (!Res.isUsable() && !isa(SimpleExpr) && + if (!Res.isUsable() && !isa(SimpleExpr) && !isa(SimpleExpr)) { Diag(ELoc, diag::err_omp_expected_addressable_lvalue_or_array_item) << (getLangOpts().OpenMP >= 50 ? 1 : 0) @@ -21447,7 +21447,7 @@ static bool checkTypeMappable(SourceLocation SL, SourceRange SR, Sema &SemaRef, static bool checkArrayExpressionDoesNotReferToWholeSize(Sema &SemaRef, const Expr *E, QualType BaseQTy) { - const auto *OASE = dyn_cast(E); + const auto *OASE = dyn_cast(E); // If this is an array subscript, it refers to the whole size if the size of // the dimension is constant and equals 1. Also, an array section assumes the @@ -21505,7 +21505,7 @@ static bool checkArrayExpressionDoesNotReferToWholeSize(Sema &SemaRef, static bool checkArrayExpressionDoesNotReferToUnitySize(Sema &SemaRef, const Expr *E, QualType BaseQTy) { - const auto *OASE = dyn_cast(E); + const auto *OASE = dyn_cast(E); // An array subscript always refer to a single element. Also, an array section // assumes the format of an array subscript if no colon is used. @@ -21720,14 +21720,14 @@ public: return RelevantExpr || Visit(E); } - bool VisitOMPArraySectionExpr(OMPArraySectionExpr *OASE) { + bool VisitArraySectionExpr(ArraySectionExpr *OASE) { // After OMP 5.0 Array section in reduction clause will be implicitly // mapped assert(!(SemaRef.getLangOpts().OpenMP < 50 && NoDiagnose) && "Array sections cannot be implicitly mapped."); Expr *E = OASE->getBase()->IgnoreParenImpCasts(); QualType CurType = - OMPArraySectionExpr::getBaseOriginalType(E).getCanonicalType(); + ArraySectionExpr::getBaseOriginalType(E).getCanonicalType(); // OpenMP 4.5 [2.15.5.1, map Clause, Restrictions, C++, p.1] // If the type of a list item is a reference to a type T then the type @@ -21900,7 +21900,7 @@ static const Expr *checkMapClauseExpressionBase( auto CE = CurComponents.rend(); for (; CI != CE; ++CI) { const auto *OASE = - dyn_cast(CI->getAssociatedExpression()); + dyn_cast(CI->getAssociatedExpression()); if (!OASE) continue; if (OASE && OASE->getLength()) @@ -21970,10 +21970,10 @@ static bool checkMapConflicts( // variable in map clauses of the same construct. if (CurrentRegionOnly && (isa(CI->getAssociatedExpression()) || - isa(CI->getAssociatedExpression()) || + isa(CI->getAssociatedExpression()) || isa(CI->getAssociatedExpression())) && (isa(SI->getAssociatedExpression()) || - isa(SI->getAssociatedExpression()) || + isa(SI->getAssociatedExpression()) || isa(SI->getAssociatedExpression()))) { SemaRef.Diag(CI->getAssociatedExpression()->getExprLoc(), diag::err_omp_multiple_array_items_in_map_clause) @@ -22001,11 +22001,10 @@ static bool checkMapConflicts( if (const auto *ASE = dyn_cast(SI->getAssociatedExpression())) { Type = ASE->getBase()->IgnoreParenImpCasts()->getType(); - } else if (const auto *OASE = dyn_cast( + } else if (const auto *OASE = dyn_cast( SI->getAssociatedExpression())) { const Expr *E = OASE->getBase()->IgnoreParenImpCasts(); - Type = - OMPArraySectionExpr::getBaseOriginalType(E).getCanonicalType(); + Type = ArraySectionExpr::getBaseOriginalType(E).getCanonicalType(); } else if (const auto *OASE = dyn_cast( SI->getAssociatedExpression())) { Type = OASE->getBase()->getType()->getPointeeType(); @@ -22480,13 +22479,13 @@ static void checkMappableExpressionList( (void)I; QualType Type; auto *ASE = dyn_cast(VE->IgnoreParens()); - auto *OASE = dyn_cast(VE->IgnoreParens()); + auto *OASE = dyn_cast(VE->IgnoreParens()); auto *OAShE = dyn_cast(VE->IgnoreParens()); if (ASE) { Type = ASE->getType().getNonReferenceType(); } else if (OASE) { QualType BaseType = - OMPArraySectionExpr::getBaseOriginalType(OASE->getBase()); + ArraySectionExpr::getBaseOriginalType(OASE->getBase()); if (const auto *ATy = BaseType->getAsArrayTypeUnsafe()) Type = ATy->getElementType(); else @@ -23955,7 +23954,7 @@ SemaOpenMP::ActOnOpenMPUseDeviceAddrClause(ArrayRef VarList, MVLI.VarBaseDeclarations.push_back(D); MVLI.VarComponents.emplace_back(); Expr *Component = SimpleRefExpr; - if (VD && (isa(RefExpr->IgnoreParenImpCasts()) || + if (VD && (isa(RefExpr->IgnoreParenImpCasts()) || isa(RefExpr->IgnoreParenImpCasts()))) Component = SemaRef.DefaultFunctionArrayLvalueConversion(SimpleRefExpr).get(); @@ -24105,7 +24104,7 @@ SemaOpenMP::ActOnOpenMPHasDeviceAddrClause(ArrayRef VarList, // against other clauses later on. Expr *Component = SimpleRefExpr; auto *VD = dyn_cast(D); - if (VD && (isa(RefExpr->IgnoreParenImpCasts()) || + if (VD && (isa(RefExpr->IgnoreParenImpCasts()) || isa(RefExpr->IgnoreParenImpCasts()))) Component = SemaRef.DefaultFunctionArrayLvalueConversion(SimpleRefExpr).get(); @@ -24519,7 +24518,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPAffinityClause( Sema::TentativeAnalysisScope Trap(SemaRef); Res = SemaRef.CreateBuiltinUnaryOp(ELoc, UO_AddrOf, SimpleExpr); } - if (!Res.isUsable() && !isa(SimpleExpr) && + if (!Res.isUsable() && !isa(SimpleExpr) && !isa(SimpleExpr)) { Diag(ELoc, diag::err_omp_expected_addressable_lvalue_or_array_item) << 1 << 0 << RefExpr->getSourceRange(); @@ -24632,7 +24631,7 @@ ExprResult SemaOpenMP::ActOnOMPArraySectionExpr( Expr *Stride, SourceLocation RBLoc) { ASTContext &Context = getASTContext(); if (Base->hasPlaceholderType() && - !Base->hasPlaceholderType(BuiltinType::OMPArraySection)) { + !Base->hasPlaceholderType(BuiltinType::ArraySection)) { ExprResult Result = SemaRef.CheckPlaceholderExpr(Base); if (Result.isInvalid()) return ExprError(); @@ -24672,13 +24671,13 @@ ExprResult SemaOpenMP::ActOnOMPArraySectionExpr( (LowerBound->isTypeDependent() || LowerBound->isValueDependent())) || (Length && (Length->isTypeDependent() || Length->isValueDependent())) || (Stride && (Stride->isTypeDependent() || Stride->isValueDependent()))) { - return new (Context) OMPArraySectionExpr( + return new (Context) ArraySectionExpr( Base, LowerBound, Length, Stride, Context.DependentTy, VK_LValue, OK_Ordinary, ColonLocFirst, ColonLocSecond, RBLoc); } // Perform default conversions. - QualType OriginalTy = OMPArraySectionExpr::getBaseOriginalType(Base); + QualType OriginalTy = ArraySectionExpr::getBaseOriginalType(Base); QualType ResultTy; if (OriginalTy->isAnyPointerType()) { ResultTy = OriginalTy->getPointeeType(); @@ -24801,14 +24800,14 @@ ExprResult SemaOpenMP::ActOnOMPArraySectionExpr( } } - if (!Base->hasPlaceholderType(BuiltinType::OMPArraySection)) { + if (!Base->hasPlaceholderType(BuiltinType::ArraySection)) { ExprResult Result = SemaRef.DefaultFunctionArrayLvalueConversion(Base); if (Result.isInvalid()) return ExprError(); Base = Result.get(); } - return new (Context) OMPArraySectionExpr( - Base, LowerBound, Length, Stride, Context.OMPArraySectionTy, VK_LValue, + return new (Context) ArraySectionExpr( + Base, LowerBound, Length, Stride, Context.ArraySectionTy, VK_LValue, OK_Ordinary, ColonLocFirst, ColonLocSecond, RBLoc); } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 1d30ba31e179..f47bc219e6fa 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -2784,15 +2784,23 @@ public: /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildOMPArraySectionExpr(Expr *Base, SourceLocation LBracketLoc, - Expr *LowerBound, - SourceLocation ColonLocFirst, - SourceLocation ColonLocSecond, - Expr *Length, Expr *Stride, - SourceLocation RBracketLoc) { - return getSema().OpenMP().ActOnOMPArraySectionExpr( - Base, LBracketLoc, LowerBound, ColonLocFirst, ColonLocSecond, Length, - Stride, RBracketLoc); + ExprResult RebuildArraySectionExpr(bool IsOMPArraySection, Expr *Base, + SourceLocation LBracketLoc, + Expr *LowerBound, + SourceLocation ColonLocFirst, + SourceLocation ColonLocSecond, + Expr *Length, Expr *Stride, + SourceLocation RBracketLoc) { + if (IsOMPArraySection) + return getSema().OpenMP().ActOnOMPArraySectionExpr( + Base, LBracketLoc, LowerBound, ColonLocFirst, ColonLocSecond, Length, + Stride, RBracketLoc); + + assert(Stride == nullptr && !ColonLocSecond.isValid() && + "Stride/second colon not allowed for OpenACC"); + + return getSema().OpenACC().ActOnArraySectionExpr( + Base, LBracketLoc, LowerBound, ColonLocFirst, Length, RBracketLoc); } /// Build a new array shaping expression. @@ -11742,7 +11750,7 @@ TreeTransform::TransformMatrixSubscriptExpr(MatrixSubscriptExpr *E) { template ExprResult -TreeTransform::TransformOMPArraySectionExpr(OMPArraySectionExpr *E) { +TreeTransform::TransformArraySectionExpr(ArraySectionExpr *E) { ExprResult Base = getDerived().TransformExpr(E->getBase()); if (Base.isInvalid()) return ExprError(); @@ -11762,20 +11770,25 @@ TreeTransform::TransformOMPArraySectionExpr(OMPArraySectionExpr *E) { } ExprResult Stride; - if (Expr *Str = E->getStride()) { - Stride = getDerived().TransformExpr(Str); - if (Stride.isInvalid()) - return ExprError(); + if (E->isOMPArraySection()) { + if (Expr *Str = E->getStride()) { + Stride = getDerived().TransformExpr(Str); + if (Stride.isInvalid()) + return ExprError(); + } } if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase() && - LowerBound.get() == E->getLowerBound() && Length.get() == E->getLength()) + LowerBound.get() == E->getLowerBound() && + Length.get() == E->getLength() && + (E->isOpenACCArraySection() || Stride.get() == E->getStride())) return E; - return getDerived().RebuildOMPArraySectionExpr( - Base.get(), E->getBase()->getEndLoc(), LowerBound.get(), - E->getColonLocFirst(), E->getColonLocSecond(), Length.get(), Stride.get(), - E->getRBracketLoc()); + return getDerived().RebuildArraySectionExpr( + E->isOMPArraySection(), Base.get(), E->getBase()->getEndLoc(), + LowerBound.get(), E->getColonLocFirst(), + E->isOMPArraySection() ? E->getColonLocSecond() : SourceLocation{}, + Length.get(), Stride.get(), E->getRBracketLoc()); } template diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index f8d54c0c3989..e017f5bdb488 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -261,8 +261,8 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::IncompleteMatrixIdx: ID = PREDEF_TYPE_INCOMPLETE_MATRIX_IDX; break; - case BuiltinType::OMPArraySection: - ID = PREDEF_TYPE_OMP_ARRAY_SECTION; + case BuiltinType::ArraySection: + ID = PREDEF_TYPE_ARRAY_SECTION; break; case BuiltinType::OMPArrayShaping: ID = PREDEF_TYPE_OMP_ARRAY_SHAPING; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index c99d6ed1c36c..0ef57a3ea804 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7385,11 +7385,11 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_INCOMPLETE_MATRIX_IDX: T = Context.IncompleteMatrixIdxTy; break; - case PREDEF_TYPE_OMP_ARRAY_SECTION: - T = Context.OMPArraySectionTy; + case PREDEF_TYPE_ARRAY_SECTION: + T = Context.ArraySectionTy; break; case PREDEF_TYPE_OMP_ARRAY_SHAPING: - T = Context.OMPArraySectionTy; + T = Context.OMPArrayShapingTy; break; case PREDEF_TYPE_OMP_ITERATOR: T = Context.OMPIteratorTy; diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index baded0fe1983..7d3930022a69 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -956,14 +956,22 @@ void ASTStmtReader::VisitMatrixSubscriptExpr(MatrixSubscriptExpr *E) { E->setRBracketLoc(readSourceLocation()); } -void ASTStmtReader::VisitOMPArraySectionExpr(OMPArraySectionExpr *E) { +void ASTStmtReader::VisitArraySectionExpr(ArraySectionExpr *E) { VisitExpr(E); + E->ASType = Record.readEnum(); + E->setBase(Record.readSubExpr()); E->setLowerBound(Record.readSubExpr()); E->setLength(Record.readSubExpr()); - E->setStride(Record.readSubExpr()); + + if (E->isOMPArraySection()) + E->setStride(Record.readSubExpr()); + E->setColonLocFirst(readSourceLocation()); - E->setColonLocSecond(readSourceLocation()); + + if (E->isOMPArraySection()) + E->setColonLocSecond(readSourceLocation()); + E->setRBracketLoc(readSourceLocation()); } @@ -3090,8 +3098,8 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) MatrixSubscriptExpr(Empty); break; - case EXPR_OMP_ARRAY_SECTION: - S = new (Context) OMPArraySectionExpr(Empty); + case EXPR_ARRAY_SECTION: + S = new (Context) ArraySectionExpr(Empty); break; case EXPR_OMP_ARRAY_SHAPING: diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index cd5f733baf76..39aec31b6d87 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -880,16 +880,21 @@ void ASTStmtWriter::VisitMatrixSubscriptExpr(MatrixSubscriptExpr *E) { Code = serialization::EXPR_ARRAY_SUBSCRIPT; } -void ASTStmtWriter::VisitOMPArraySectionExpr(OMPArraySectionExpr *E) { +void ASTStmtWriter::VisitArraySectionExpr(ArraySectionExpr *E) { VisitExpr(E); + Record.writeEnum(E->ASType); Record.AddStmt(E->getBase()); Record.AddStmt(E->getLowerBound()); Record.AddStmt(E->getLength()); - Record.AddStmt(E->getStride()); + if (E->isOMPArraySection()) + Record.AddStmt(E->getStride()); Record.AddSourceLocation(E->getColonLocFirst()); - Record.AddSourceLocation(E->getColonLocSecond()); + + if (E->isOMPArraySection()) + Record.AddSourceLocation(E->getColonLocSecond()); + Record.AddSourceLocation(E->getRBracketLoc()); - Code = serialization::EXPR_OMP_ARRAY_SECTION; + Code = serialization::EXPR_ARRAY_SECTION; } void ASTStmtWriter::VisitOMPArrayShapingExpr(OMPArrayShapingExpr *E) { diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index a678c3827e7f..1cebfbbee77d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -188,9 +188,9 @@ void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State, os << DerefStr1; break; } - case Stmt::OMPArraySectionExprClass: { + case Stmt::ArraySectionExprClass: { os << "Array access"; - const OMPArraySectionExpr *AE = cast(S); + const ArraySectionExpr *AE = cast(S); AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), State.get(), N->getLocationContext()); os << DerefStr1; diff --git a/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp index 1cf81b54e77d..7ac34ef8164e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -350,7 +350,7 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, return false; case Stmt::CallExprClass: case Stmt::ArraySubscriptExprClass: - case Stmt::OMPArraySectionExprClass: + case Stmt::ArraySectionExprClass: case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: case Stmt::ImplicitCastExprClass: diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 09c69f9612d9..0b1edf3e5c96 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -1948,7 +1948,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CXXPseudoDestructorExprClass: case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXNullPtrLiteralExprClass: - case Stmt::OMPArraySectionExprClass: + case Stmt::ArraySectionExprClass: case Stmt::OMPArrayShapingExprClass: case Stmt::OMPIteratorExprClass: case Stmt::SYCLUniqueStableNameExprClass: diff --git a/clang/test/OpenMP/task_depend_messages.cpp b/clang/test/OpenMP/task_depend_messages.cpp index 388595bef4de..3f39c55527b5 100644 --- a/clang/test/OpenMP/task_depend_messages.cpp +++ b/clang/test/OpenMP/task_depend_messages.cpp @@ -62,7 +62,7 @@ int main(int argc, char **argv, char *env[]) { #pragma omp task depend(in : argv[ : argc][1 : argc - 1]) #pragma omp task depend(in : arr[0]) #pragma omp task depend(depobj:argc) // omp45-error {{expected 'in', 'out', 'inout' or 'mutexinoutset' in OpenMP clause 'depend'}} omp50-error {{expected lvalue expression of 'omp_depend_t' type, not 'int'}} omp51-error {{expected lvalue expression of 'omp_depend_t' type, not 'int'}} - #pragma omp task depend(depobj : argv[ : argc][1 : argc - 1]) // omp45-error {{expected 'in', 'out', 'inout' or 'mutexinoutset' in OpenMP clause 'depend'}} omp50-error {{expected lvalue expression of 'omp_depend_t' type, not ''}} omp51-error {{expected lvalue expression of 'omp_depend_t' type, not ''}} + #pragma omp task depend(depobj : argv[ : argc][1 : argc - 1]) // omp45-error {{expected 'in', 'out', 'inout' or 'mutexinoutset' in OpenMP clause 'depend'}} omp50-error {{expected lvalue expression of 'omp_depend_t' type, not ''}} omp51-error {{expected lvalue expression of 'omp_depend_t' type, not ''}} #pragma omp task depend(depobj : arr[0]) // omp45-error {{expected 'in', 'out', 'inout' or 'mutexinoutset' in OpenMP clause 'depend'}} #pragma omp task depend(in : ([ // expected-error {{expected variable name or 'this' in lambda capture list}} expected-error {{expected ')'}} expected-note {{to match this '('}} #pragma omp task depend(in : ([] // expected-error {{expected body of lambda expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} diff --git a/clang/test/ParserOpenACC/parse-cache-construct.cpp b/clang/test/ParserOpenACC/parse-cache-construct.cpp index f0a35824696d..1ab2153a68be 100644 --- a/clang/test/ParserOpenACC/parse-cache-construct.cpp +++ b/clang/test/ParserOpenACC/parse-cache-construct.cpp @@ -74,12 +74,12 @@ void use() { for (int i = 0; i < 10; ++i) { // FIXME: Once we have a new array-section type to represent OpenACC as // well, change this error message. - // expected-error@+2{{OpenMP array section is not allowed here}} + // expected-error@+2{{OpenACC sub-array is not allowed here}} // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} #pragma acc cache(Arrs.MemArr[3:4].array[1:4]) } for (int i = 0; i < 10; ++i) { - // expected-error@+2{{OpenMP array section is not allowed here}} + // expected-error@+2{{OpenACC sub-array is not allowed here}} // expected-warning@+1{{OpenACC construct 'cache' not yet implemented, pragma ignored}} #pragma acc cache(Arrs.MemArr[3:4].array[4]) } diff --git a/clang/test/ParserOpenACC/parse-clauses.c b/clang/test/ParserOpenACC/parse-clauses.c index 799f22b8c120..ee2cb2d1501d 100644 --- a/clang/test/ParserOpenACC/parse-clauses.c +++ b/clang/test/ParserOpenACC/parse-clauses.c @@ -482,13 +482,13 @@ void VarListClauses() { #pragma acc serial copy(HasMem.MemArr[3].array[1:4]), seq for(;;){} - // expected-error@+3{{OpenMP array section is not allowed here}} + // expected-error@+3{{OpenACC sub-array is not allowed here}} // expected-warning@+2{{OpenACC clause 'copy' not yet implemented, clause ignored}} // expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}} #pragma acc serial copy(HasMem.MemArr[1:3].array[1]), seq for(;;){} - // expected-error@+3{{OpenMP array section is not allowed here}} + // expected-error@+3{{OpenACC sub-array is not allowed here}} // expected-warning@+2{{OpenACC clause 'copy' not yet implemented, clause ignored}} // expected-warning@+1{{OpenACC clause 'seq' not yet implemented, clause ignored}} #pragma acc serial copy(HasMem.MemArr[1:3].array[1:2]), seq diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 74163f30e19b..398a11a57035 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5713,8 +5713,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("UnaryOperator"); case CXCursor_ArraySubscriptExpr: return cxstring::createRef("ArraySubscriptExpr"); - case CXCursor_OMPArraySectionExpr: - return cxstring::createRef("OMPArraySectionExpr"); + case CXCursor_ArraySectionExpr: + return cxstring::createRef("ArraySectionExpr"); case CXCursor_OMPArrayShapingExpr: return cxstring::createRef("OMPArrayShapingExpr"); case CXCursor_OMPIteratorExpr: diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp index 454bf7549861..9325a16d2a84 100644 --- a/clang/tools/libclang/CXCursor.cpp +++ b/clang/tools/libclang/CXCursor.cpp @@ -423,8 +423,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_UnexposedExpr; break; - case Stmt::OMPArraySectionExprClass: - K = CXCursor_OMPArraySectionExpr; + case Stmt::ArraySectionExprClass: + K = CXCursor_ArraySectionExpr; break; case Stmt::OMPArrayShapingExprClass: -- GitLab From f9a0b467dd3da17e9b5d3b50bff624a60dc2950c Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Thu, 25 Apr 2024 10:24:19 -0700 Subject: [PATCH 006/468] [memprof] Remove getFullSchema in MemProfTest.cpp (#90072) This patch removes getFullSchema in MemProfTest.cpp in favor of llvm::memprof::PortableMemInfoBlock::getFullSchema as they do exactly the same thing. --- llvm/unittests/ProfileData/MemProfTest.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/llvm/unittests/ProfileData/MemProfTest.cpp b/llvm/unittests/ProfileData/MemProfTest.cpp index 7e00a80cacf9..5e72b3a11f8e 100644 --- a/llvm/unittests/ProfileData/MemProfTest.cpp +++ b/llvm/unittests/ProfileData/MemProfTest.cpp @@ -122,14 +122,6 @@ MATCHER_P4(FrameContains, FunctionName, LineOffset, Column, Inline, "") { return false; } -MemProfSchema getFullSchema() { - MemProfSchema Schema; -#define MIBEntryDef(NameTag, Name, Type) Schema.push_back(Meta::Name); -#include "llvm/ProfileData/MIBEntryDef.inc" -#undef MIBEntryDef - return Schema; -} - TEST(MemProf, FillsValue) { std::unique_ptr Symbolizer(new MockSymbolizer()); @@ -248,7 +240,7 @@ TEST(MemProf, PortableWrapper) { /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3, /*dealloc_cpu=*/4); - const auto Schema = getFullSchema(); + const auto Schema = llvm::memprof::PortableMemInfoBlock::getFullSchema(); PortableMemInfoBlock WriteBlock(Info); std::string Buffer; @@ -271,7 +263,7 @@ TEST(MemProf, PortableWrapper) { // Version0 and Version1 serialize IndexedMemProfRecord in the same format, so // we share one test. TEST(MemProf, RecordSerializationRoundTripVersion0And1) { - const MemProfSchema Schema = getFullSchema(); + const auto Schema = llvm::memprof::PortableMemInfoBlock::getFullSchema(); MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000, /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3, @@ -305,7 +297,7 @@ TEST(MemProf, RecordSerializationRoundTripVersion0And1) { } TEST(MemProf, RecordSerializationRoundTripVerion2) { - const MemProfSchema Schema = getFullSchema(); + const auto Schema = llvm::memprof::PortableMemInfoBlock::getFullSchema(); MemInfoBlock Info(/*size=*/16, /*access_count=*/7, /*alloc_timestamp=*/1000, /*dealloc_timestamp=*/2000, /*alloc_cpu=*/3, -- GitLab From f5953f46aa0a664461584b78c14cb141a3be2b9d Mon Sep 17 00:00:00 2001 From: erichkeane Date: Thu, 25 Apr 2024 10:36:31 -0700 Subject: [PATCH 007/468] Fix lldb build failure caused by 39adc8f42329 We changed the name of one of the types, which is consumed by LLDB. My patch local build + CI didn't catch it, but a build bot did! This commit fixes it by updating the name in LLDB. --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 5da94adb771f..8fc0f9103f55 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4861,7 +4861,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::Kind::OCLQueue: case clang::BuiltinType::Kind::OCLReserveID: case clang::BuiltinType::Kind::OCLSampler: - case clang::BuiltinType::Kind::OMPArraySection: + case clang::BuiltinType::Kind::ArraySection: case clang::BuiltinType::Kind::OMPArrayShaping: case clang::BuiltinType::Kind::OMPIterator: case clang::BuiltinType::Kind::Overload: @@ -6013,7 +6013,7 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { case clang::BuiltinType::ARCUnbridgedCast: case clang::BuiltinType::PseudoObject: case clang::BuiltinType::BuiltinFn: - case clang::BuiltinType::OMPArraySection: + case clang::BuiltinType::ArraySection: return 1; default: return 0; -- GitLab From 5a1d85051fa4847b6a3fe4cae30e0a11843bec41 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Fri, 26 Apr 2024 01:42:10 +0800 Subject: [PATCH 008/468] [InstCombine] Canonicalize `gep T, (gep i8, base, C1), (Index + C2)` into `gep T, (gep i8, base, C1 + C2 * sizeof(T)), Index` (#76177) This patch tries to canonicalize `gep T, (gep i8, base, C1), (Index + C2)` into `gep T, (gep i8, base, C1 + C2 * sizeof(T)), Index`. Alive2: https://alive2.llvm.org/ce/z/dxShKF Fixes regressions found in https://github.com/llvm/llvm-project/pull/68882. --- .../InstCombine/InstructionCombining.cpp | 40 +++ .../Transforms/InstCombine/gepofconstgepi8.ll | 292 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 llvm/test/Transforms/InstCombine/gepofconstgepi8.ll diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 282badd43693..58b2d8e9dec1 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2339,6 +2339,43 @@ static Instruction *foldSelectGEP(GetElementPtrInst &GEP, return SelectInst::Create(Cond, NewTrueC, NewFalseC, "", nullptr, Sel); } +// Canonicalization: +// gep T, (gep i8, base, C1), (Index + C2) into +// gep T, (gep i8, base, C1 + C2 * sizeof(T)), Index +static Instruction *canonicalizeGEPOfConstGEPI8(GetElementPtrInst &GEP, + GEPOperator *Src, + InstCombinerImpl &IC) { + if (GEP.getNumIndices() != 1) + return nullptr; + auto &DL = IC.getDataLayout(); + Value *Base; + const APInt *C1; + if (!match(Src, m_PtrAdd(m_Value(Base), m_APInt(C1)))) + return nullptr; + Value *VarIndex; + const APInt *C2; + Type *PtrTy = Src->getType()->getScalarType(); + unsigned IndexSizeInBits = DL.getIndexTypeSizeInBits(PtrTy); + if (!match(GEP.getOperand(1), m_AddLike(m_Value(VarIndex), m_APInt(C2)))) + return nullptr; + if (C1->getBitWidth() != IndexSizeInBits || + C2->getBitWidth() != IndexSizeInBits) + return nullptr; + Type *BaseType = GEP.getSourceElementType(); + if (isa(BaseType)) + return nullptr; + APInt TypeSize(IndexSizeInBits, DL.getTypeAllocSize(BaseType)); + APInt NewOffset = TypeSize * *C2 + *C1; + if (NewOffset.isZero() || + (Src->hasOneUse() && GEP.getOperand(1)->hasOneUse())) { + Value *GEPConst = + IC.Builder.CreatePtrAdd(Base, IC.Builder.getInt(NewOffset)); + return GetElementPtrInst::Create(BaseType, GEPConst, VarIndex); + } + + return nullptr; +} + Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP, GEPOperator *Src) { // Combine Indices - If the source pointer to this getelementptr instruction @@ -2347,6 +2384,9 @@ Instruction *InstCombinerImpl::visitGEPOfGEP(GetElementPtrInst &GEP, if (!shouldMergeGEPs(*cast(&GEP), *Src)) return nullptr; + if (auto *I = canonicalizeGEPOfConstGEPI8(GEP, Src, *this)) + return I; + // For constant GEPs, use a more general offset-based folding approach. Type *PtrTy = Src->getType()->getScalarType(); if (GEP.hasAllConstantIndices() && diff --git a/llvm/test/Transforms/InstCombine/gepofconstgepi8.ll b/llvm/test/Transforms/InstCombine/gepofconstgepi8.ll new file mode 100644 index 000000000000..7b7c6fba699c --- /dev/null +++ b/llvm/test/Transforms/InstCombine/gepofconstgepi8.ll @@ -0,0 +1,292 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt < %s -S -passes=instcombine | FileCheck %s + +declare void @use64(i64) +declare void @useptr(ptr) + +define ptr @test_zero(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_zero( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[A]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_nonzero(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_nonzero( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr [[BASE]], i64 4 +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[TMP0]], i64 [[A]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 2 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_or_disjoint(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_or_disjoint( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[A]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = or disjoint i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_zero_multiuse_index(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_zero_multiuse_index( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[INDEX:%.*]] = add i64 [[A]], 1 +; CHECK-NEXT: call void @use64(i64 [[INDEX]]) +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[A]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 1 + call void @use64(i64 %index) + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_zero_multiuse_ptr(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_zero_multiuse_ptr( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: call void @useptr(ptr [[P1]]) +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[A]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + call void @useptr(ptr %p1) + %index = add i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_zero_sext_add_nsw(ptr %base, i32 %a) { +; CHECK-LABEL: define ptr @test_zero_sext_add_nsw( +; CHECK-SAME: ptr [[BASE:%.*]], i32 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[A]] to i64 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[P1]], i64 [[TMP0]] +; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP1]], i64 4 +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add nsw i32 %a, 1 + %p2 = getelementptr i32, ptr %p1, i32 %index + ret ptr %p2 +} + +define ptr @test_zero_trunc_add(ptr %base, i128 %a) { +; CHECK-LABEL: define ptr @test_zero_trunc_add( +; CHECK-SAME: ptr [[BASE:%.*]], i128 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = trunc i128 [[A]] to i64 +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[BASE]], i64 [[TMP0]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i128 %a, 1 + %p2 = getelementptr i32, ptr %p1, i128 %index + ret ptr %p2 +} + +define ptr @test_non_i8(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_non_i8( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[P1]], i64 [[A]] +; CHECK-NEXT: ret ptr [[TMP0]] +; +entry: + %p1 = getelementptr i16, ptr %base, i64 -4 + %index = add i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_non_const(ptr %base, i64 %a, i64 %b) { +; CHECK-LABEL: define ptr @test_non_const( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[B]] +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[P1]], i64 [[A]] +; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 4 +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 %b + %index = add i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_too_many_indices(ptr %base, i64 %a, i64 %b) { +; CHECK-LABEL: define ptr @test_too_many_indices( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 [[B]] +; CHECK-NEXT: [[INDEX:%.*]] = add i64 [[A]], 1 +; CHECK-NEXT: [[P2:%.*]] = getelementptr [8 x i32], ptr [[P1]], i64 1, i64 [[INDEX]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 %b + %index = add i64 %a, 1 + %p2 = getelementptr [8 x i32], ptr %p1, i64 1, i64 %index + ret ptr %p2 +} + +define ptr @test_wrong_op(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_wrong_op( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[INDEX:%.*]] = xor i64 [[A]], 1 +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P1]], i64 [[INDEX]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = xor i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_sext_add_without_nsw(ptr %base, i32 %a) { +; CHECK-LABEL: define ptr @test_sext_add_without_nsw( +; CHECK-SAME: ptr [[BASE:%.*]], i32 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[INDEX:%.*]] = add i32 [[A]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[INDEX]] to i64 +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P1]], i64 [[TMP0]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i32 %a, 1 + %p2 = getelementptr i32, ptr %p1, i32 %index + ret ptr %p2 +} + +define ptr @test_or_without_disjoint(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_or_without_disjoint( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[INDEX:%.*]] = or i64 [[A]], 1 +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P1]], i64 [[INDEX]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = or i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_smul_overflow(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_smul_overflow( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -12 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[P1]], i64 [[A]] +; CHECK-NEXT: ret ptr [[TMP0]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 9223372036854775806 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_sadd_overflow(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_sadd_overflow( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -9223372036854775808 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[P1]], i64 [[A]] +; CHECK-NEXT: ret ptr [[TMP0]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 9223372036854775804 + %index = add i64 %a, 1 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_nonzero_multiuse_index(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_nonzero_multiuse_index( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[INDEX:%.*]] = add i64 [[A]], 2 +; CHECK-NEXT: call void @use64(i64 [[INDEX]]) +; CHECK-NEXT: [[P2:%.*]] = getelementptr i32, ptr [[P1]], i64 [[INDEX]] +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 2 + call void @use64(i64 %index) + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_nonzero_multiuse_ptr(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_nonzero_multiuse_ptr( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: call void @useptr(ptr [[P1]]) +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[P1]], i64 [[A]] +; CHECK-NEXT: [[P2:%.*]] = getelementptr i8, ptr [[TMP0]], i64 8 +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + call void @useptr(ptr %p1) + %index = add i64 %a, 2 + %p2 = getelementptr i32, ptr %p1, i64 %index + ret ptr %p2 +} + +define ptr @test_scalable(ptr %base, i64 %a) { +; CHECK-LABEL: define ptr @test_scalable( +; CHECK-SAME: ptr [[BASE:%.*]], i64 [[A:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[P1:%.*]] = getelementptr i8, ptr [[BASE]], i64 -4 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr , ptr [[P1]], i64 [[A]] +; CHECK-NEXT: [[P2:%.*]] = getelementptr , ptr [[TMP0]], i64 1 +; CHECK-NEXT: ret ptr [[P2]] +; +entry: + %p1 = getelementptr i8, ptr %base, i64 -4 + %index = add i64 %a, 1 + %p2 = getelementptr , ptr %p1, i64 %index + ret ptr %p2 +} -- GitLab From d3c9a97705d807afeb3fd92bb0d65fa895c6d139 Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Thu, 25 Apr 2024 17:44:28 +0000 Subject: [PATCH 009/468] [gn build] Port 8dc7db7a2463 --- .../secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn index 59dc38c8c4d8..815c5a93c72f 100644 --- a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn +++ b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/readability/BUILD.gn @@ -35,6 +35,7 @@ static_library("readability") { "IsolateDeclarationCheck.cpp", "MagicNumbersCheck.cpp", "MakeMemberFunctionConstCheck.cpp", + "MathMissingParenthesesCheck.cpp", "MisleadingIndentationCheck.cpp", "MisplacedArrayIndexCheck.cpp", "NamedParameterCheck.cpp", -- GitLab From eb05a2e89dccec734625aa336b553197b75f2340 Mon Sep 17 00:00:00 2001 From: Alexander Shaposhnikov <6532716+alexander-shaposhnikov@users.noreply.github.com> Date: Thu, 25 Apr 2024 10:54:30 -0700 Subject: [PATCH 010/468] [Flang] Add fallthrough annotations in visit.h (#90014) Add fallthrough annotations to avoid warnings if -Wimplicit-fallthrough is enabled. Test plan: ninja check-all --- flang/include/flang/Common/visit.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/flang/include/flang/Common/visit.h b/flang/include/flang/Common/visit.h index 4d0897301e01..7fe9ff839917 100644 --- a/flang/include/flang/Common/visit.h +++ b/flang/include/flang/Common/visit.h @@ -23,6 +23,7 @@ #include "variant.h" #include "flang/Common/api-attrs.h" +#include "llvm/Support/Compiler.h" #include namespace Fortran::common { @@ -40,11 +41,17 @@ inline RT_API_ATTRS RESULT Log2VisitHelper( return visitor(std::get<(LOW + N)>(std::forward(u))...); \ } VISIT_CASE_N(1) + LLVM_FALLTHROUGH; VISIT_CASE_N(2) + LLVM_FALLTHROUGH; VISIT_CASE_N(3) + LLVM_FALLTHROUGH; VISIT_CASE_N(4) + LLVM_FALLTHROUGH; VISIT_CASE_N(5) + LLVM_FALLTHROUGH; VISIT_CASE_N(6) + LLVM_FALLTHROUGH; VISIT_CASE_N(7) #undef VISIT_CASE_N } -- GitLab From 63ecd2a72523fa591aacf54d310478aabcd30d08 Mon Sep 17 00:00:00 2001 From: Joshua Cranmer Date: Thu, 25 Apr 2024 10:57:32 -0700 Subject: [PATCH 011/468] Disable FTZ/DAZ when compiling shared libraries by default. (#80475) This fixes https://github.com/llvm/llvm-project/issues/57589, and aligns Clang with the behavior of current versions of gcc. There is a new option, -mdaz-ftz, to control the linking of the file that sets FTZ/DAZ on startup, and this flag is on by default if -ffast-math is present and -shared isn't. This also partially reverts fa7cd549d60 in that it disables the attempt to set the IR denormal-fp-math attribute based on whether or not -ffast-math is applied as it is insufficiently reliable. --- clang/docs/ReleaseNotes.rst | 10 ++++++++ clang/docs/UsersManual.rst | 15 +++++++---- clang/include/clang/Driver/Options.td | 5 ++++ clang/lib/Driver/ToolChain.cpp | 15 +++++++++-- clang/lib/Driver/ToolChains/Linux.cpp | 19 -------------- clang/lib/Driver/ToolChains/Linux.h | 4 --- clang/test/Driver/default-denormal-fp-math.c | 9 ------- clang/test/Driver/linux-ld.c | 26 ++++++++++++++++++++ 8 files changed, 64 insertions(+), 39 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0bad03eda8cb..f5e5d3a2e6ea 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -207,6 +207,16 @@ Non-comprehensive list of changes in this release - ``__typeof_unqual__`` is available in all C modes as an extension, which behaves like ``typeof_unqual`` from C23, similar to ``__typeof__`` and ``typeof``. + +* Shared libraries linked with either the ``-ffast-math``, ``-Ofast``, or + ``-funsafe-math-optimizations`` flags will no longer enable flush-to-zero + floating-point mode by default. This decision can be overridden with use of + ``-mdaz-ftz``. This behavior now matches GCC's behavior. + (`#57589 `_) + +* ``-fdenormal-fp-math=preserve-sign`` is no longer implied by ``-ffast-math`` + on x86 systems. + New Compiler Flags ------------------ - ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 8df40566fcba..d0326f01d251 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -1506,7 +1506,8 @@ floating point semantic models: precise (the default), strict, and fast. * ``-ffp-contract=fast`` - Note: ``-ffast-math`` causes ``crtfastmath.o`` to be linked with code. See + Note: ``-ffast-math`` causes ``crtfastmath.o`` to be linked with code unless + ``-shared`` or ``-mno-daz-ftz`` is present. See :ref:`crtfastmath.o` for more details. .. option:: -fno-fast-math @@ -1560,7 +1561,8 @@ floating point semantic models: precise (the default), strict, and fast. ``-ffp-contract``. Note: ``-fno-fast-math`` implies ``-fdenormal-fp-math=ieee``. - ``-fno-fast-math`` causes ``crtfastmath.o`` to not be linked with code. + ``-fno-fast-math`` causes ``crtfastmath.o`` to not be linked with code + unless ``-mdaz-ftz`` is present. .. option:: -fdenormal-fp-math= @@ -1938,10 +1940,13 @@ by using ``#pragma STDC FENV_ROUND`` with a value other than ``FE_DYNAMIC``. A note about ``crtfastmath.o`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``-ffast-math`` and ``-funsafe-math-optimizations`` cause ``crtfastmath.o`` to be -automatically linked, which adds a static constructor that sets the FTZ/DAZ +``-ffast-math`` and ``-funsafe-math-optimizations`` without the ``-shared`` +option cause ``crtfastmath.o`` to be +automatically linked, which adds a static constructor that sets the FTZ/DAZ bits in MXCSR, affecting not only the current compilation unit but all static -and shared libraries included in the program. +and shared libraries included in the program. This decision can be overridden +by using either the flag ``-mdaz-ftz`` or ``-mno-daz-ftz`` to respectively +link or not link ``crtfastmath.o``. .. _FLT_EVAL_METHOD: diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 922bda721dc7..4cb0b840df87 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2615,6 +2615,11 @@ defm protect_parens : BoolFOption<"protect-parens", "floating-point expressions are evaluated">, NegFlag>; +defm daz_ftz : SimpleMFlag<"daz-ftz", + "Globally set", "Do not globally set", + " the denormals-are-zero (DAZ) and flush-to-zero (FTZ) bits in the " + "floating-point control register on program startup">; + def ffor_scope : Flag<["-"], "ffor-scope">, Group; def fno_for_scope : Flag<["-"], "fno-for-scope">, Group; diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 237092ed07e5..341d6202a9ca 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -1307,9 +1307,14 @@ void ToolChain::AddCCKextLibArgs(const ArgList &Args, bool ToolChain::isFastMathRuntimeAvailable(const ArgList &Args, std::string &Path) const { + // Don't implicitly link in mode-changing libraries in a shared library, since + // this can have very deleterious effects. See the various links from + // https://github.com/llvm/llvm-project/issues/57589 for more information. + bool Default = !Args.hasArgNoClaim(options::OPT_shared); + // Do not check for -fno-fast-math or -fno-unsafe-math when -Ofast passed // (to keep the linker options consistent with gcc and clang itself). - if (!isOptimizationLevelFast(Args)) { + if (Default && !isOptimizationLevelFast(Args)) { // Check if -ffast-math or -funsafe-math. Arg *A = Args.getLastArg(options::OPT_ffast_math, options::OPT_fno_fast_math, @@ -1318,8 +1323,14 @@ bool ToolChain::isFastMathRuntimeAvailable(const ArgList &Args, if (!A || A->getOption().getID() == options::OPT_fno_fast_math || A->getOption().getID() == options::OPT_fno_unsafe_math_optimizations) - return false; + Default = false; } + + // Whatever decision came as a result of the above implicit settings, either + // -mdaz-ftz or -mno-daz-ftz is capable of overriding it. + if (!Args.hasFlag(options::OPT_mdaz_ftz, options::OPT_mno_daz_ftz, Default)) + return false; + // If crtfastmath.o exists add it to the arguments. Path = GetFilePath("crtfastmath.o"); return (Path != "crtfastmath.o"); // Not found. diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index fb65881061ef..db2c20d7b461 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -842,25 +842,6 @@ void Linux::addProfileRTLibs(const llvm::opt::ArgList &Args, ToolChain::addProfileRTLibs(Args, CmdArgs); } -llvm::DenormalMode -Linux::getDefaultDenormalModeForType(const llvm::opt::ArgList &DriverArgs, - const JobAction &JA, - const llvm::fltSemantics *FPType) const { - switch (getTriple().getArch()) { - case llvm::Triple::x86: - case llvm::Triple::x86_64: { - std::string Unused; - // DAZ and FTZ are turned on in crtfastmath.o - if (!DriverArgs.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles) && - isFastMathRuntimeAvailable(DriverArgs, Unused)) - return llvm::DenormalMode::getPreserveSign(); - return llvm::DenormalMode::getIEEE(); - } - default: - return llvm::DenormalMode::getIEEE(); - } -} - void Linux::addExtraOpts(llvm::opt::ArgStringList &CmdArgs) const { for (const auto &Opt : ExtraOpts) CmdArgs.push_back(Opt.c_str()); diff --git a/clang/lib/Driver/ToolChains/Linux.h b/clang/lib/Driver/ToolChains/Linux.h index 524391743090..2d9e674e50a6 100644 --- a/clang/lib/Driver/ToolChains/Linux.h +++ b/clang/lib/Driver/ToolChains/Linux.h @@ -59,10 +59,6 @@ public: std::vector ExtraOpts; - llvm::DenormalMode getDefaultDenormalModeForType( - const llvm::opt::ArgList &DriverArgs, const JobAction &JA, - const llvm::fltSemantics *FPType = nullptr) const override; - const char *getDefaultLinker() const override; protected: diff --git a/clang/test/Driver/default-denormal-fp-math.c b/clang/test/Driver/default-denormal-fp-math.c index 5f87e151df49..c04ad5c08b8d 100644 --- a/clang/test/Driver/default-denormal-fp-math.c +++ b/clang/test/Driver/default-denormal-fp-math.c @@ -3,15 +3,6 @@ // RUN: %clang -### -target x86_64-unknown-linux-gnu --sysroot=%S/Inputs/basic_linux_tree -c %s -v 2>&1 | FileCheck -check-prefix=CHECK-IEEE %s -// crtfastmath enables ftz and daz -// RUN: %clang -### -target x86_64-unknown-linux-gnu -ffast-math --sysroot=%S/Inputs/basic_linux_tree -c %s -v 2>&1 | FileCheck -check-prefix=CHECK-PRESERVESIGN %s - -// crt not linked in with nostartfiles -// RUN: %clang -### -target x86_64-unknown-linux-gnu -ffast-math -nostartfiles --sysroot=%S/Inputs/basic_linux_tree -c %s -v 2>&1 | FileCheck -check-prefix=CHECK-IEEE %s - -// If there's no crtfastmath, don't assume ftz/daz -// RUN: %clang -### -target x86_64-unknown-linux-gnu -ffast-math --sysroot=/dev/null -c %s -v 2>&1 | FileCheck -check-prefix=CHECK-IEEE %s - // RUN: %clang -### -target x86_64-scei-ps4 -c %s -v 2>&1 | FileCheck -check-prefix=CHECK-PRESERVESIGN %s // Flag omitted for default diff --git a/clang/test/Driver/linux-ld.c b/clang/test/Driver/linux-ld.c index d918f4f2d7db..958e682b6c3c 100644 --- a/clang/test/Driver/linux-ld.c +++ b/clang/test/Driver/linux-ld.c @@ -1446,6 +1446,32 @@ // RUN: %clang --target=i386-unknown-linux -no-pie -### %s -ffast-math \ // RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s +// Don't link crtfastmath.o with -shared +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -ffast-math -shared \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -Ofast -shared \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s +// Check for effects of -mdaz-ftz +// RUN: %clang --target=x86_64-unknown-linux -### %s -ffast-math -shared -mdaz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -ffast-math -mdaz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -mdaz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-CRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -### %s -ffast-math -shared -mno-daz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -ffast-math -mno-daz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s +// RUN: %clang --target=x86_64-unknown-linux -no-pie -### %s -mno-daz-ftz \ +// RUN: --sysroot=%S/Inputs/basic_linux_tree 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOCRTFASTMATH %s // CHECK-CRTFASTMATH: usr/lib/gcc/x86_64-unknown-linux/10.2.0{{/|\\\\}}crtfastmath.o // CHECK-NOCRTFASTMATH-NOT: crtfastmath.o -- GitLab From 0b01b2143735a0becf2ed09825ddd33b98b1c5b5 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 25 Apr 2024 11:25:37 -0700 Subject: [PATCH 012/468] [LLVMgold] Suppress -Wcast-function-type-mismatch diagnostic llvm/cmake/modules/HandleLLVMOptions.cmake adds -Wextra. -Wcast-function-type-mismatch was recently added to -Wextra, leading to a warning for the `get_wrap_symbols` code (https://reviews.llvm.org/D44235). Suppress the diagnostic. Pull Request: https://github.com/llvm/llvm-project/pull/89994 --- llvm/tools/gold/gold-plugin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/llvm/tools/gold/gold-plugin.cpp b/llvm/tools/gold/gold-plugin.cpp index b8a33f74bd57..5503f7343cb6 100644 --- a/llvm/tools/gold/gold-plugin.cpp +++ b/llvm/tools/gold/gold-plugin.cpp @@ -434,8 +434,10 @@ ld_plugin_status onload(ld_plugin_tv *tv) { // FIXME: When binutils 2.31 (containing gold 1.16) is the minimum // required version, this should be changed to: // get_wrap_symbols = tv->tv_u.tv_get_wrap_symbols; - get_wrap_symbols = - (ld_plugin_get_wrap_symbols)tv->tv_u.tv_message; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" + get_wrap_symbols = (ld_plugin_get_wrap_symbols)tv->tv_u.tv_message; +#pragma GCC diagnostic pop break; default: break; -- GitLab From 02660e274242b2dd61543a06d7ab4dc0efd2517d Mon Sep 17 00:00:00 2001 From: Jake Egan Date: Wed, 24 Apr 2024 18:17:00 -0400 Subject: [PATCH 013/468] [NFC] Enable atomic tests on AIX These tests pass on AIX. --- .../atomics.types.generic/atomics.types.float/fetch_add.pass.cpp | 1 - .../atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp | 1 - .../atomics.types.float/operator.minus_equals.pass.cpp | 1 - .../atomics.types.float/operator.plus_equals.pass.cpp | 1 - 4 files changed, 4 deletions(-) diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp index 7350c1ddf0e9..4119c39772e5 100644 --- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp +++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: LIBCXX-AIX-FIXME // XFAIL: !has-64-bit-atomics // https://github.com/llvm/llvm-project/issues/72893 diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp index 84dcde5f2784..2460765a3c86 100644 --- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp +++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: LIBCXX-AIX-FIXME // XFAIL: !has-64-bit-atomics // https://github.com/llvm/llvm-project/issues/72893 diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp index 386a393e3550..4bd303022c0d 100644 --- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp +++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: LIBCXX-AIX-FIXME // XFAIL: !has-64-bit-atomics // floating-point-type operator-=(floating-point-type) volatile noexcept; diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp index afd06d537c7a..69abb9ae63c3 100644 --- a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp +++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp @@ -6,7 +6,6 @@ // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14, c++17 -// UNSUPPORTED: LIBCXX-AIX-FIXME // XFAIL: !has-64-bit-atomics // floating-point-type operator+=(floating-point-type) volatile noexcept; -- GitLab From 2f2e31c3c980407b2660b4f5d10e7cdb3fa79138 Mon Sep 17 00:00:00 2001 From: jeffreytan81 Date: Thu, 25 Apr 2024 11:49:10 -0700 Subject: [PATCH 014/468] Initial step in targets DAP support (#86623) This patch provides the initial implementation for the "Step Into Specific/Step In Targets" feature in VSCode DAP. The implementation disassembles all the call instructions in step range and try to resolve operand name (assuming one operand) using debug info. Later, the call target function name is chosen by end user and specified in the StepInto() API call. It is v1 because of using the existing step in target function name API. This implementation has several limitations: * Won't for indirect/virtual function call -- in most cases, our disassembler won't be able to solve the indirect call target address/name. * Won't work for target function without debug info -- if the target function has symbol but not debug info, the existing ThreadPlanStepInRange won't stop. * Relying on function names can be fragile -- if there is some middle glue/thunk code, our disassembler can only resolve the glue/thunk code's name not the real target function name. It can be fragile to depend compiler/linker emits the same names for both. * Does not support step into raw address call sites -- it is a valid scenario that in Visual Studio debugger, user can explicitly choose a raw address to step into which land in the function without debug info/symbol, then choose UI to load the debug info on-demand for that module/frame to continue exploring. A more reliable design could be extending the ThreadPlanStepInRange to support step in based on call-site instruction offset/PC which I will propose in next iteration. --------- Co-authored-by: jeffreytan81 --- lldb/include/lldb/API/SBLineEntry.h | 3 + lldb/include/lldb/API/SBSymbolContextList.h | 1 + lldb/include/lldb/API/SBTarget.h | 4 + .../test/tools/lldb-dap/dap_server.py | 21 ++- .../test/tools/lldb-dap/lldbdap_testcase.py | 4 +- lldb/source/API/SBLineEntry.cpp | 15 ++ lldb/source/API/SBTarget.cpp | 24 +++ .../API/tools/lldb-dap/stepInTargets/Makefile | 6 + .../stepInTargets/TestDAP_stepInTargets.py | 68 ++++++++ .../API/tools/lldb-dap/stepInTargets/main.cpp | 11 ++ lldb/tools/lldb-dap/DAP.h | 2 + lldb/tools/lldb-dap/lldb-dap.cpp | 146 +++++++++++++++++- 12 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 lldb/test/API/tools/lldb-dap/stepInTargets/Makefile create mode 100644 lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py create mode 100644 lldb/test/API/tools/lldb-dap/stepInTargets/main.cpp diff --git a/lldb/include/lldb/API/SBLineEntry.h b/lldb/include/lldb/API/SBLineEntry.h index 7c2431ba3c8a..d70c4fac6ec7 100644 --- a/lldb/include/lldb/API/SBLineEntry.h +++ b/lldb/include/lldb/API/SBLineEntry.h @@ -29,6 +29,9 @@ public: lldb::SBAddress GetEndAddress() const; + lldb::SBAddress + GetSameLineContiguousAddressRangeEnd(bool include_inlined_functions) const; + explicit operator bool() const; bool IsValid() const; diff --git a/lldb/include/lldb/API/SBSymbolContextList.h b/lldb/include/lldb/API/SBSymbolContextList.h index 4026afc21357..95100d219df2 100644 --- a/lldb/include/lldb/API/SBSymbolContextList.h +++ b/lldb/include/lldb/API/SBSymbolContextList.h @@ -44,6 +44,7 @@ public: protected: friend class SBModule; friend class SBTarget; + friend class SBCompileUnit; lldb_private::SymbolContextList *operator->() const; diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index 823615e6a36d..feeaa1cb7113 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -879,6 +879,10 @@ public: uint32_t count, const char *flavor_string); + lldb::SBInstructionList ReadInstructions(lldb::SBAddress start_addr, + lldb::SBAddress end_addr, + const char *flavor_string); + lldb::SBInstructionList GetInstructions(lldb::SBAddress base_addr, const void *buf, size_t size); diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py index 27a76a652f40..5838281bcb1a 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py @@ -811,23 +811,34 @@ class DebugCommunication(object): command_dict = {"command": "next", "type": "request", "arguments": args_dict} return self.send_recv(command_dict) - def request_stepIn(self, threadId): + def request_stepIn(self, threadId, targetId): if self.exit_status is not None: - raise ValueError("request_continue called after process exited") - args_dict = {"threadId": threadId} + raise ValueError("request_stepIn called after process exited") + args_dict = {"threadId": threadId, "targetId": targetId} command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict} return self.send_recv(command_dict) + def request_stepInTargets(self, frameId): + if self.exit_status is not None: + raise ValueError("request_stepInTargets called after process exited") + args_dict = {"frameId": frameId} + command_dict = { + "command": "stepInTargets", + "type": "request", + "arguments": args_dict, + } + return self.send_recv(command_dict) + def request_stepOut(self, threadId): if self.exit_status is not None: - raise ValueError("request_continue called after process exited") + raise ValueError("request_stepOut called after process exited") args_dict = {"threadId": threadId} command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict} return self.send_recv(command_dict) def request_pause(self, threadId=None): if self.exit_status is not None: - raise ValueError("request_continue called after process exited") + raise ValueError("request_pause called after process exited") if threadId is None: threadId = self.get_thread_id() args_dict = {"threadId": threadId} diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py index 23f650d2d36f..d56ea5dca14b 100644 --- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py +++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py @@ -218,8 +218,8 @@ class DAPTestCaseBase(TestBase): """Set a top level global variable only.""" return self.dap_server.request_setVariable(2, name, str(value), id=id) - def stepIn(self, threadId=None, waitForStop=True): - self.dap_server.request_stepIn(threadId=threadId) + def stepIn(self, threadId=None, targetId=None, waitForStop=True): + self.dap_server.request_stepIn(threadId=threadId, targetId=targetId) if waitForStop: return self.dap_server.wait_for_stopped() return None diff --git a/lldb/source/API/SBLineEntry.cpp b/lldb/source/API/SBLineEntry.cpp index 99a7b8fe644c..216ea6d18eab 100644 --- a/lldb/source/API/SBLineEntry.cpp +++ b/lldb/source/API/SBLineEntry.cpp @@ -67,6 +67,21 @@ SBAddress SBLineEntry::GetEndAddress() const { return sb_address; } +SBAddress SBLineEntry::GetSameLineContiguousAddressRangeEnd( + bool include_inlined_functions) const { + LLDB_INSTRUMENT_VA(this); + + SBAddress sb_address; + if (m_opaque_up) { + AddressRange line_range = m_opaque_up->GetSameLineContiguousAddressRange( + include_inlined_functions); + + sb_address.SetAddress(line_range.GetBaseAddress()); + sb_address.OffsetAddress(line_range.GetByteSize()); + } + return sb_address; +} + bool SBLineEntry::IsValid() const { LLDB_INSTRUMENT_VA(this); return this->operator bool(); diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 75f0444f6291..962ce9ba83cc 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -2011,6 +2011,30 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr, return sb_instructions; } +lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress start_addr, + lldb::SBAddress end_addr, + const char *flavor_string) { + LLDB_INSTRUMENT_VA(this, start_addr, end_addr, flavor_string); + + SBInstructionList sb_instructions; + + TargetSP target_sp(GetSP()); + if (target_sp) { + lldb::addr_t start_load_addr = start_addr.GetLoadAddress(*this); + lldb::addr_t end_load_addr = end_addr.GetLoadAddress(*this); + if (end_load_addr > start_load_addr) { + lldb::addr_t size = end_load_addr - start_load_addr; + + AddressRange range(start_load_addr, size); + const bool force_live_memory = true; + sb_instructions.SetDisassembler(Disassembler::DisassembleRange( + target_sp->GetArchitecture(), nullptr, flavor_string, *target_sp, + range, force_live_memory)); + } + } + return sb_instructions; +} + lldb::SBInstructionList SBTarget::GetInstructions(lldb::SBAddress base_addr, const void *buf, size_t size) { diff --git a/lldb/test/API/tools/lldb-dap/stepInTargets/Makefile b/lldb/test/API/tools/lldb-dap/stepInTargets/Makefile new file mode 100644 index 000000000000..f772575cd561 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stepInTargets/Makefile @@ -0,0 +1,6 @@ + +ENABLE_THREADS := YES + +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py new file mode 100644 index 000000000000..6296f6554d07 --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py @@ -0,0 +1,68 @@ +""" +Test lldb-dap stepInTargets request +""" + +import dap_server +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +import lldbdap_testcase +from lldbsuite.test import lldbutil + + +class TestDAP_stepInTargets(lldbdap_testcase.DAPTestCaseBase): + @skipIf( + archs=no_match(["x86_64"]) + ) # InstructionControlFlowKind for ARM is not supported yet. + def test_basic(self): + """ + Tests the basic stepping in targets with directly calls. + """ + program = self.getBuildArtifact("a.out") + self.build_and_launch(program) + source = "main.cpp" + + breakpoint_line = line_number(source, "// set breakpoint here") + lines = [breakpoint_line] + # Set breakpoint in the thread function so we can step the threads + breakpoint_ids = self.set_source_breakpoints(source, lines) + self.assertEqual( + len(breakpoint_ids), len(lines), "expect correct number of breakpoints" + ) + self.continue_to_breakpoints(breakpoint_ids) + + threads = self.dap_server.get_threads() + self.assertEqual(len(threads), 1, "expect one thread") + tid = threads[0]["id"] + + leaf_frame = self.dap_server.get_stackFrame() + self.assertIsNotNone(leaf_frame, "expect a leaf frame") + + # Request all step in targets list and verify the response. + step_in_targets_response = self.dap_server.request_stepInTargets( + leaf_frame["id"] + ) + self.assertEqual(step_in_targets_response["success"], True, "expect success") + self.assertIn( + "body", step_in_targets_response, "expect body field in response body" + ) + self.assertIn( + "targets", + step_in_targets_response["body"], + "expect targets field in response body", + ) + + step_in_targets = step_in_targets_response["body"]["targets"] + self.assertEqual(len(step_in_targets), 3, "expect 3 step in targets") + + # Verify the target names are correct. + self.assertEqual(step_in_targets[0]["label"], "bar()", "expect bar()") + self.assertEqual(step_in_targets[1]["label"], "bar2()", "expect bar2()") + self.assertEqual( + step_in_targets[2]["label"], "foo(int, int)", "expect foo(int, int)" + ) + + # Choose to step into second target and verify that we are in bar2() + self.stepIn(threadId=tid, targetId=step_in_targets[1]["id"], waitForStop=True) + leaf_frame = self.dap_server.get_stackFrame() + self.assertIsNotNone(leaf_frame, "expect a leaf frame") + self.assertEqual(leaf_frame["name"], "bar2()") diff --git a/lldb/test/API/tools/lldb-dap/stepInTargets/main.cpp b/lldb/test/API/tools/lldb-dap/stepInTargets/main.cpp new file mode 100644 index 000000000000..d3c3dbcc139e --- /dev/null +++ b/lldb/test/API/tools/lldb-dap/stepInTargets/main.cpp @@ -0,0 +1,11 @@ + +int foo(int val, int extra) { return val + extra; } + +int bar() { return 22; } + +int bar2() { return 54; } + +int main(int argc, char const *argv[]) { + foo(bar(), bar2()); // set breakpoint here + return 0; +} diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h index 8015dec9ba8f..5c70a056fea4 100644 --- a/lldb/tools/lldb-dap/DAP.h +++ b/lldb/tools/lldb-dap/DAP.h @@ -162,6 +162,8 @@ struct DAP { std::vector exit_commands; std::vector stop_commands; std::vector terminate_commands; + // Map step in target id to list of function targets that user can choose. + llvm::DenseMap step_in_targets; // A copy of the last LaunchRequest or AttachRequest so we can reuse its // arguments if we get a RestartRequest. std::optional last_launch_or_attach_request; diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp index 16c50ed5791b..d0fbb9155715 100644 --- a/lldb/tools/lldb-dap/lldb-dap.cpp +++ b/lldb/tools/lldb-dap/lldb-dap.cpp @@ -1650,7 +1650,7 @@ void request_initialize(const llvm::json::Object &request) { // The debug adapter supports the gotoTargetsRequest. body.try_emplace("supportsGotoTargetsRequest", false); // The debug adapter supports the stepInTargetsRequest. - body.try_emplace("supportsStepInTargetsRequest", false); + body.try_emplace("supportsStepInTargetsRequest", true); // The debug adapter supports the completions request. body.try_emplace("supportsCompletionsRequest", true); // The debug adapter supports the disassembly request. @@ -3185,14 +3185,155 @@ void request_stepIn(const llvm::json::Object &request) { llvm::json::Object response; FillResponse(request, response); auto arguments = request.getObject("arguments"); + + std::string step_in_target; + uint64_t target_id = GetUnsigned(arguments, "targetId", 0); + auto it = g_dap.step_in_targets.find(target_id); + if (it != g_dap.step_in_targets.end()) + step_in_target = it->second; + + const bool single_thread = GetBoolean(arguments, "singleThread", false); + lldb::RunMode run_mode = + single_thread ? lldb::eOnlyThisThread : lldb::eOnlyDuringStepping; lldb::SBThread thread = g_dap.GetLLDBThread(*arguments); if (thread.IsValid()) { // Remember the thread ID that caused the resume so we can set the // "threadCausedFocus" boolean value in the "stopped" events. g_dap.focus_tid = thread.GetThreadID(); - thread.StepInto(); + thread.StepInto(step_in_target.c_str(), run_mode); + } else { + response["success"] = llvm::json::Value(false); + } + g_dap.SendJSON(llvm::json::Value(std::move(response))); +} + +// "StepInTargetsRequest": { +// "allOf": [ { "$ref": "#/definitions/Request" }, { +// "type": "object", +// "description": "This request retrieves the possible step-in targets for +// the specified stack frame.\nThese targets can be used in the `stepIn` +// request.\nClients should only call this request if the corresponding +// capability `supportsStepInTargetsRequest` is true.", "properties": { +// "command": { +// "type": "string", +// "enum": [ "stepInTargets" ] +// }, +// "arguments": { +// "$ref": "#/definitions/StepInTargetsArguments" +// } +// }, +// "required": [ "command", "arguments" ] +// }] +// }, +// "StepInTargetsArguments": { +// "type": "object", +// "description": "Arguments for `stepInTargets` request.", +// "properties": { +// "frameId": { +// "type": "integer", +// "description": "The stack frame for which to retrieve the possible +// step-in targets." +// } +// }, +// "required": [ "frameId" ] +// }, +// "StepInTargetsResponse": { +// "allOf": [ { "$ref": "#/definitions/Response" }, { +// "type": "object", +// "description": "Response to `stepInTargets` request.", +// "properties": { +// "body": { +// "type": "object", +// "properties": { +// "targets": { +// "type": "array", +// "items": { +// "$ref": "#/definitions/StepInTarget" +// }, +// "description": "The possible step-in targets of the specified +// source location." +// } +// }, +// "required": [ "targets" ] +// } +// }, +// "required": [ "body" ] +// }] +// } +void request_stepInTargets(const llvm::json::Object &request) { + llvm::json::Object response; + FillResponse(request, response); + auto arguments = request.getObject("arguments"); + + g_dap.step_in_targets.clear(); + lldb::SBFrame frame = g_dap.GetLLDBFrame(*arguments); + if (frame.IsValid()) { + lldb::SBAddress pc_addr = frame.GetPCAddress(); + lldb::SBAddress line_end_addr = + pc_addr.GetLineEntry().GetSameLineContiguousAddressRangeEnd(true); + lldb::SBInstructionList insts = g_dap.target.ReadInstructions( + pc_addr, line_end_addr, /*flavor_string=*/nullptr); + + if (!insts.IsValid()) { + response["success"] = false; + response["message"] = "Failed to get instructions for frame."; + g_dap.SendJSON(llvm::json::Value(std::move(response))); + return; + } + + llvm::json::Array step_in_targets; + const auto num_insts = insts.GetSize(); + for (size_t i = 0; i < num_insts; ++i) { + lldb::SBInstruction inst = insts.GetInstructionAtIndex(i); + if (!inst.IsValid()) + break; + + lldb::addr_t inst_addr = inst.GetAddress().GetLoadAddress(g_dap.target); + + // Note: currently only x86/x64 supports flow kind. + lldb::InstructionControlFlowKind flow_kind = + inst.GetControlFlowKind(g_dap.target); + if (flow_kind == lldb::eInstructionControlFlowKindCall) { + // Use call site instruction address as id which is easy to debug. + llvm::json::Object step_in_target; + step_in_target["id"] = inst_addr; + + llvm::StringRef call_operand_name = inst.GetOperands(g_dap.target); + lldb::addr_t call_target_addr; + if (call_operand_name.getAsInteger(0, call_target_addr)) + continue; + + lldb::SBAddress call_target_load_addr = + g_dap.target.ResolveLoadAddress(call_target_addr); + if (!call_target_load_addr.IsValid()) + continue; + + // The existing ThreadPlanStepInRange only accept step in target + // function with debug info. + lldb::SBSymbolContext sc = g_dap.target.ResolveSymbolContextForAddress( + call_target_load_addr, lldb::eSymbolContextFunction); + + // The existing ThreadPlanStepInRange only accept step in target + // function with debug info. + std::string step_in_target_name; + if (sc.IsValid() && sc.GetFunction().IsValid()) + step_in_target_name = sc.GetFunction().GetDisplayName(); + + // Skip call sites if we fail to resolve its symbol name. + if (step_in_target_name.empty()) + continue; + + g_dap.step_in_targets.try_emplace(inst_addr, step_in_target_name); + step_in_target.try_emplace("label", step_in_target_name); + step_in_targets.emplace_back(std::move(step_in_target)); + } + } + llvm::json::Object body; + body.try_emplace("targets", std::move(step_in_targets)); + response.try_emplace("body", std::move(body)); } else { response["success"] = llvm::json::Value(false); + response["message"] = "Failed to get frame for input frameId."; } g_dap.SendJSON(llvm::json::Value(std::move(response))); } @@ -3909,6 +4050,7 @@ void RegisterRequestCallbacks() { g_dap.RegisterRequestCallback("source", request_source); g_dap.RegisterRequestCallback("stackTrace", request_stackTrace); g_dap.RegisterRequestCallback("stepIn", request_stepIn); + g_dap.RegisterRequestCallback("stepInTargets", request_stepInTargets); g_dap.RegisterRequestCallback("stepOut", request_stepOut); g_dap.RegisterRequestCallback("threads", request_threads); g_dap.RegisterRequestCallback("variables", request_variables); -- GitLab From a8fd0d029dca7d17eee72d0445223c2fe1ee7758 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Thu, 25 Apr 2024 14:50:53 -0400 Subject: [PATCH 015/468] [Clang][Sema] Diagnose class member access expressions naming non-existent members of the current instantiation prior to instantiation in the absence of dependent base classes (#84050) Consider the following: ```cpp template struct A { auto f() { return this->x; } }; ``` Although `A` has no dependent base classes and the lookup context for `x` is the current instantiation, we currently do not diagnose the absence of a member `x` until `A::f` is instantiated. This patch moves the point of diagnosis for such expressions to occur at the point of definition (i.e. prior to instantiation). --- .../clangd/unittests/FindTargetTests.cpp | 8 +- .../unittests/SemanticHighlightingTests.cpp | 2 +- .../cppcoreguidelines/owning-memory.cpp | 2 + .../modernize/use-equals-default-copy.cpp | 12 + clang/docs/ReleaseNotes.rst | 12 + clang/include/clang/Sema/Lookup.h | 4 +- clang/include/clang/Sema/Sema.h | 14 +- clang/lib/AST/Expr.cpp | 2 +- clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 7 +- clang/lib/Sema/SemaAttr.cpp | 2 +- clang/lib/Sema/SemaDecl.cpp | 7 +- clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaExpr.cpp | 20 +- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/lib/Sema/SemaExprMember.cpp | 182 +++---- clang/lib/Sema/SemaLookup.cpp | 114 ++++- clang/lib/Sema/SemaOpenMP.cpp | 17 +- clang/lib/Sema/SemaTemplate.cpp | 32 +- clang/lib/Sema/TreeTransform.h | 20 + .../AST/HLSL/this-reference-template.hlsl | 2 +- clang/test/CXX/drs/dr2xx.cpp | 10 +- clang/test/CXX/drs/dr3xx.cpp | 16 +- .../temp.res/temp.dep/temp.dep.type/p4.cpp | 456 ++++++++++++++++++ .../test/CXX/temp/temp.res/temp.local/p3.cpp | 3 +- clang/test/CodeGenCXX/mangle.cpp | 8 - .../Index/annotate-nested-name-specifier.cpp | 4 +- clang/test/SemaCXX/member-expr.cpp | 4 +- .../SemaTemplate/instantiate-function-1.cpp | 14 +- .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 5 +- 30 files changed, 765 insertions(+), 224 deletions(-) create mode 100644 clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index 799a549ff081..94437857cecc 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -854,7 +854,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Similar to above but base expression involves a function call. Code = R"cpp( @@ -872,7 +872,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Similar to above but uses a function pointer. Code = R"cpp( @@ -891,7 +891,7 @@ TEST_F(TargetDeclTest, DependentExprs) { } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()"); + EXPECT_DECLS("MemberExpr", "void foo()"); // Base expression involves a member access into this. Code = R"cpp( @@ -962,7 +962,7 @@ TEST_F(TargetDeclTest, DependentExprs) { void Foo() { this->[[find]](); } }; )cpp"; - EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()"); + EXPECT_DECLS("MemberExpr", "void find()"); } TEST_F(TargetDeclTest, DependentTypes) { diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp index 4156921d83ed..30b9b1902aa9 100644 --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -621,7 +621,7 @@ sizeof...($TemplateParameter[[Elements]]); struct $Class_def[[Foo]] { int $Field_decl[[Waldo]]; void $Method_def[[bar]]() { - $Class[[Foo]]().$Field_dependentName[[Waldo]]; + $Class[[Foo]]().$Field[[Waldo]]; } template $Bracket[[<]]typename $TemplateParameter_def[[U]]$Bracket[[>]] void $Method_def[[bar1]]() { diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp index 574efe7bd914..ae61b17ca14d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/owning-memory.cpp @@ -309,6 +309,8 @@ struct HeapArray { // Ok, since destruc HeapArray(HeapArray &&other) : _data(other._data), size(other.size) { // Ok other._data = nullptr; // Ok + // CHECK-NOTES: [[@LINE-1]]:5: warning: expected assignment source to be of type 'gsl::owner<>'; got 'std::nullptr_t' + // FIXME: This warning is emitted because an ImplicitCastExpr for the NullToPointer conversion isn't created for dependent types. other.size = 0; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp index 559031cf4d9b..4abb9c855597 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-equals-default-copy.cpp @@ -260,6 +260,8 @@ template struct Template { Template() = default; Template(const Template &Other) : Field(Other.Field) {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use '= default' + // CHECK-FIXES: Template(const Template &Other) = default; Template &operator=(const Template &Other); void foo(const T &t); int Field; @@ -269,8 +271,12 @@ Template &Template::operator=(const Template &Other) { Field = Other.Field; return *this; } +// CHECK-MESSAGES: :[[@LINE-4]]:27: warning: use '= default' +// CHECK-FIXES: Template &Template::operator=(const Template &Other) = default; + Template T1; + // Dependent types. template struct DT1 { @@ -284,6 +290,9 @@ DT1 &DT1::operator=(const DT1 &Other) { Field = Other.Field; return *this; } +// CHECK-MESSAGES: :[[@LINE-4]]:17: warning: use '= default' +// CHECK-FIXES: DT1 &DT1::operator=(const DT1 &Other) = default; + DT1 Dt1; template @@ -303,6 +312,9 @@ DT2 &DT2::operator=(const DT2 &Other) { struct T { typedef int TT; }; +// CHECK-MESSAGES: :[[@LINE-8]]:17: warning: use '= default' +// CHECK-FIXES: DT2 &DT2::operator=(const DT2 &Other) = default; + DT2 Dt2; // Default arguments. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f5e5d3a2e6ea..00c684e773a2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -385,6 +385,18 @@ Improvements to Clang's diagnostics - Clang now diagnoses requires expressions with explicit object parameters. +- Clang now looks up members of the current instantiation in the template definition context + if the current instantiation has no dependent base classes. + + .. code-block:: c++ + + template + struct A { + int f() { + return this->x; // error: no member named 'x' in 'A' + } + }; + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Sema/Lookup.h b/clang/include/clang/Sema/Lookup.h index 0db5b847038f..b0a08a05ac6a 100644 --- a/clang/include/clang/Sema/Lookup.h +++ b/clang/include/clang/Sema/Lookup.h @@ -499,7 +499,9 @@ public: /// Note that while no result was found in the current instantiation, /// there were dependent base classes that could not be searched. void setNotFoundInCurrentInstantiation() { - assert(ResultKind == NotFound && Decls.empty()); + assert((ResultKind == NotFound || + ResultKind == NotFoundInCurrentInstantiation) && + Decls.empty()); ResultKind = NotFoundInCurrentInstantiation; } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1ca523ec88c2..aa182b15e66e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7472,7 +7472,7 @@ public: bool LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, CXXScopeSpec &SS); bool LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - bool AllowBuiltinCreation = false, + QualType ObjectType, bool AllowBuiltinCreation = false, bool EnteringContext = false); ObjCProtocolDecl *LookupProtocol( IdentifierInfo *II, SourceLocation IdLoc, @@ -8881,11 +8881,13 @@ public: /// functions (but no function templates). FoundFunctions, }; - bool LookupTemplateName( - LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, - bool EnteringContext, bool &MemberOfUnknownSpecialization, - RequiredTemplateKind RequiredTemplate = SourceLocation(), - AssumedTemplateKind *ATK = nullptr, bool AllowTypoCorrection = true); + + bool + LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, + QualType ObjectType, bool EnteringContext, + RequiredTemplateKind RequiredTemplate = SourceLocation(), + AssumedTemplateKind *ATK = nullptr, + bool AllowTypoCorrection = true); TemplateNameKind isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 63dcdb919c71..d2e40be59d6f 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -103,7 +103,7 @@ const Expr *Expr::skipRValueSubobjectAdjustments( } } else if (const auto *ME = dyn_cast(E)) { if (!ME->isArrow()) { - assert(ME->getBase()->getType()->isRecordType()); + assert(ME->getBase()->getType()->getAsRecordDecl()); if (const auto *Field = dyn_cast(ME->getMemberDecl())) { if (!Field->isBitField() && !Field->getType()->isReferenceType()) { E = ME->getBase(); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 05ad5ecbfaa0..53a33fa4add5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2998,7 +2998,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, << TokenName << TagName << getLangOpts().CPlusPlus << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); - if (Actions.LookupParsedName(R, getCurScope(), SS)) { + if (Actions.LookupName(R, getCurScope())) { for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 1a1febf7a352..bb283c54b3d2 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -126,12 +126,15 @@ struct BuiltinTypeDeclBuilder { static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S, StringRef Name) { - CXXScopeSpec SS; IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); DeclarationNameInfo NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); - S.LookupParsedName(R, S.getCurScope(), &SS, false); + // AllowBuiltinCreation is false but LookupDirect will create + // the builtin when searching the global scope anyways... + S.LookupName(R, S.getCurScope()); + // FIXME: If the builtin function was user-declared in global scope, + // this assert *will* fail. Should this call LookupBuiltin instead? assert(R.isSingleResult() && "Since this is a builtin it should always resolve!"); auto *VD = cast(R.getFoundDecl()); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index a5dd158808f2..a83b1e8afadb 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -837,7 +837,7 @@ void Sema::ActOnPragmaUnused(const Token &IdTok, Scope *curScope, IdentifierInfo *Name = IdTok.getIdentifierInfo(); LookupResult Lookup(*this, Name, IdTok.getLocation(), LookupOrdinaryName); - LookupParsedName(Lookup, curScope, nullptr, true); + LookupName(Lookup, curScope, /*AllowBuiltinCreation=*/true); if (Lookup.empty()) { Diag(PragmaLoc, diag::warn_pragma_unused_undeclared_var) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e0745fe9a453..4e275dc15fbb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -832,7 +832,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, IdentifierInfo *&Name, SourceLocation NameLoc) { LookupResult R(SemaRef, Name, NameLoc, Sema::LookupTagName); - SemaRef.LookupParsedName(R, S, &SS); + SemaRef.LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (TagDecl *Tag = R.getAsSingle()) { StringRef FixItTagName; switch (Tag->getTagKind()) { @@ -869,7 +869,7 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, // Replace lookup results with just the tag decl. Result.clear(Sema::LookupTagName); - SemaRef.LookupParsedName(Result, S, &SS); + SemaRef.LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType()); return true; } @@ -896,7 +896,8 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, } LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); - LookupParsedName(Result, S, &SS, !CurMethod); + LookupParsedName(Result, S, &SS, /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/!CurMethod); if (SS.isInvalid()) return NameClassification::Error(); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index abdbc9d8830c..4d5836720a65 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4517,7 +4517,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD, DS.getBeginLoc(), DS.getEllipsisLoc()); } else { LookupResult R(*this, MemberOrBase, IdLoc, LookupOrdinaryName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); TypeDecl *TyD = R.getAsSingle(); if (!TyD) { @@ -12262,7 +12262,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, // Lookup namespace name. LookupResult R(*this, NamespcName, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (R.isAmbiguous()) return nullptr; @@ -13721,7 +13721,7 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, SourceLocation NamespaceLoc, // Lookup the namespace name. LookupResult R(*this, Ident, IdentLoc, LookupNamespaceName); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); if (R.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 50f92c496a53..0c37f43f7540 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -673,8 +673,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { // expressions of certain types in C++. if (getLangOpts().CPlusPlus && (E->getType() == Context.OverloadTy || - T->isDependentType() || - T->isRecordType())) + // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied + // to pointer types even if the pointee type is dependent. + (T->isDependentType() && !T->isPointerType()) || T->isRecordType())) return E; // The C standard is actually really unclear on this point, and @@ -2751,8 +2752,8 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, if (isBoundsAttrContext() && !getLangOpts().CPlusPlus && S->isClassScope()) { // See if this is reference to a field of struct. LookupResult R(*this, NameInfo, LookupMemberName); - // LookupParsedName handles a name lookup from within anonymous struct. - if (LookupParsedName(R, S, &SS)) { + // LookupName handles a name lookup from within anonymous struct. + if (LookupName(R, S)) { if (auto *VD = dyn_cast(R.getFoundDecl())) { QualType type = VD->getType().getNonReferenceType(); // This will eventually be translated into MemberExpr upon @@ -2773,20 +2774,19 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, // lookup to determine that it was a template name in the first place. If // this becomes a performance hit, we can work harder to preserve those // results until we get here but it's likely not worth it. - bool MemberOfUnknownSpecialization; AssumedTemplateKind AssumedTemplate; - if (LookupTemplateName(R, S, SS, QualType(), /*EnteringContext=*/false, - MemberOfUnknownSpecialization, TemplateKWLoc, + if (LookupTemplateName(R, S, SS, /*ObjectType=*/QualType(), + /*EnteringContext=*/false, TemplateKWLoc, &AssumedTemplate)) return ExprError(); - if (MemberOfUnknownSpecialization || - (R.getResultKind() == LookupResult::NotFoundInCurrentInstantiation)) + if (R.wasNotFoundInCurrentInstantiation()) return ActOnDependentIdExpression(SS, TemplateKWLoc, NameInfo, IsAddressOfOperand, TemplateArgs); } else { bool IvarLookupFollowUp = II && !SS.isSet() && getCurMethodDecl(); - LookupParsedName(R, S, &SS, !IvarLookupFollowUp); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/!IvarLookupFollowUp); // If the result might be in a dependent base class, this is a dependent // id-expression. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 779a41620033..c1cb03e4ec7a 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -9157,7 +9157,7 @@ Sema::CheckMicrosoftIfExistsSymbol(Scope *S, // Do the redeclaration lookup in the current scope. LookupResult R(*this, TargetNameInfo, Sema::LookupAnyName, RedeclarationKind::NotForRedeclaration); - LookupParsedName(R, S, &SS); + LookupParsedName(R, S, &SS, /*ObjectType=*/QualType()); R.suppressDiagnostics(); switch (R.getResultKind()) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 6e30716b9ae4..0eeb7b1faa0a 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -667,8 +667,8 @@ namespace { // classes, one of its base classes. class RecordMemberExprValidatorCCC final : public CorrectionCandidateCallback { public: - explicit RecordMemberExprValidatorCCC(const RecordType *RTy) - : Record(RTy->getDecl()) { + explicit RecordMemberExprValidatorCCC(QualType RTy) + : Record(RTy->getAsRecordDecl()) { // Don't add bare keywords to the consumer since they will always fail // validation by virtue of not being associated with any decls. WantTypeSpecifiers = false; @@ -713,58 +713,36 @@ private: } static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, - Expr *BaseExpr, - const RecordType *RTy, + Expr *BaseExpr, QualType RTy, SourceLocation OpLoc, bool IsArrow, CXXScopeSpec &SS, bool HasTemplateArgs, SourceLocation TemplateKWLoc, TypoExpr *&TE) { SourceRange BaseRange = BaseExpr ? BaseExpr->getSourceRange() : SourceRange(); - RecordDecl *RDecl = RTy->getDecl(); - if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) && - SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), - diag::err_typecheck_incomplete_tag, - BaseRange)) + if (!RTy->isDependentType() && + !SemaRef.isThisOutsideMemberFunctionBody(RTy) && + SemaRef.RequireCompleteType( + OpLoc, RTy, diag::err_typecheck_incomplete_tag, BaseRange)) return true; - if (HasTemplateArgs || TemplateKWLoc.isValid()) { - // LookupTemplateName doesn't expect these both to exist simultaneously. - QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); + // LookupTemplateName/LookupParsedName don't expect these both to exist + // simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : RTy; + if (HasTemplateArgs || TemplateKWLoc.isValid()) + return SemaRef.LookupTemplateName(R, + /*S=*/nullptr, SS, ObjectType, + /*EnteringContext=*/false, TemplateKWLoc); - bool MOUS; - return SemaRef.LookupTemplateName(R, nullptr, SS, ObjectType, false, MOUS, - TemplateKWLoc); - } - - DeclContext *DC = RDecl; - if (SS.isSet()) { - // If the member name was a qualified-id, look into the - // nested-name-specifier. - DC = SemaRef.computeDeclContext(SS, false); - - if (SemaRef.RequireCompleteDeclContext(SS, DC)) { - SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) - << SS.getRange() << DC; - return true; - } - - assert(DC && "Cannot handle non-computable dependent contexts in lookup"); - - if (!isa(DC)) { - SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) - << DC << SS.getRange(); - return true; - } - } + SemaRef.LookupParsedName(R, /*S=*/nullptr, &SS, ObjectType); - // The record definition is complete, now look up the member. - SemaRef.LookupQualifiedName(R, DC, SS); - - if (!R.empty()) + if (!R.empty() || R.wasNotFoundInCurrentInstantiation()) return false; DeclarationName Typo = R.getLookupName(); SourceLocation TypoLoc = R.getNameLoc(); + // Recompute the lookup context. + DeclContext *DC = SS.isSet() ? SemaRef.computeDeclContext(SS) + : SemaRef.computeDeclContext(RTy); struct QueryState { Sema &SemaRef; @@ -788,7 +766,8 @@ static bool LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, << Typo << DC << DroppedSpecifier << SS.getRange()); } else { - SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << DC << BaseRange; + SemaRef.Diag(TypoLoc, diag::err_no_member) + << Typo << DC << (SS.isSet() ? SS.getRange() : BaseRange); } }, [=](Sema &SemaRef, TypoExpr *TE, TypoCorrection TC) mutable { @@ -814,34 +793,25 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, Decl *ObjCImpDecl, bool HasTemplateArgs, SourceLocation TemplateKWLoc); -ExprResult -Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, - SourceLocation OpLoc, bool IsArrow, - CXXScopeSpec &SS, - SourceLocation TemplateKWLoc, - NamedDecl *FirstQualifierInScope, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs, - const Scope *S, - ActOnMemberAccessExtraArgs *ExtraArgs) { - if (BaseType->isDependentType() || - (SS.isSet() && isDependentScopeSpecifier(SS)) || - NameInfo.getName().isDependentName()) - return ActOnDependentMemberExpr(Base, BaseType, - IsArrow, OpLoc, - SS, TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs); - +ExprResult Sema::BuildMemberReferenceExpr( + Expr *Base, QualType BaseType, SourceLocation OpLoc, bool IsArrow, + CXXScopeSpec &SS, SourceLocation TemplateKWLoc, + NamedDecl *FirstQualifierInScope, const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs, const Scope *S, + ActOnMemberAccessExtraArgs *ExtraArgs) { LookupResult R(*this, NameInfo, LookupMemberName); + if (SS.isInvalid()) + return ExprError(); + // Implicit member accesses. if (!Base) { TypoExpr *TE = nullptr; QualType RecordTy = BaseType; if (IsArrow) RecordTy = RecordTy->castAs()->getPointeeType(); - if (LookupMemberExprInRecord( - *this, R, nullptr, RecordTy->castAs(), OpLoc, IsArrow, - SS, TemplateArgs != nullptr, TemplateKWLoc, TE)) + if (LookupMemberExprInRecord(*this, R, nullptr, RecordTy, OpLoc, IsArrow, + SS, TemplateArgs != nullptr, TemplateKWLoc, + TE)) return ExprError(); if (TE) return TE; @@ -1033,6 +1003,12 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, const Scope *S, bool SuppressQualifierCheck, ActOnMemberAccessExtraArgs *ExtraArgs) { + assert(!SS.isInvalid() && "nested-name-specifier cannot be invalid"); + if (R.wasNotFoundInCurrentInstantiation()) + return ActOnDependentMemberExpr(BaseExpr, BaseExprType, IsArrow, OpLoc, SS, + TemplateKWLoc, FirstQualifierInScope, + R.getLookupNameInfo(), TemplateArgs); + QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); @@ -1040,6 +1016,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } R.setBaseObjectType(BaseType); + assert((SS.isEmpty() + ? !BaseType->isDependentType() || computeDeclContext(BaseType) + : !isDependentScopeSpecifier(SS) || computeDeclContext(SS)) && + "dependent lookup context that isn't the current instantiation?"); + // C++1z [expr.ref]p2: // For the first option (dot) the first expression shall be a glvalue [...] if (!IsArrow && BaseExpr && BaseExpr->isPRValue()) { @@ -1069,13 +1050,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, if (R.empty()) { // Rederive where we looked up. - DeclContext *DC = (SS.isSet() - ? computeDeclContext(SS, false) - : BaseType->castAs()->getDecl()); - + DeclContext *DC = + (SS.isSet() ? computeDeclContext(SS) : computeDeclContext(BaseType)); if (ExtraArgs) { ExprResult RetryExpr; - if (!IsArrow && BaseExpr) { + if (!IsArrow && BaseExpr && !BaseExpr->isTypeDependent()) { SFINAETrap Trap(*this, true); ParsedType ObjectType; bool MayBePseudoDestructor = false; @@ -1098,9 +1077,12 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, } } + assert(DC); Diag(R.getNameLoc(), diag::err_no_member) - << MemberName << DC - << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); + << MemberName << DC + << (SS.isSet() + ? SS.getRange() + : (BaseExpr ? BaseExpr->getSourceRange() : SourceRange())); return ExprError(); } @@ -1330,7 +1312,6 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, return ExprError(); QualType BaseType = BaseExpr.get()->getType(); - assert(!BaseType->isDependentType()); DeclarationName MemberName = R.getLookupName(); SourceLocation MemberLoc = R.getNameLoc(); @@ -1342,29 +1323,31 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, if (IsArrow) { if (const PointerType *Ptr = BaseType->getAs()) BaseType = Ptr->getPointeeType(); - else if (const ObjCObjectPointerType *Ptr - = BaseType->getAs()) - BaseType = Ptr->getPointeeType(); - else if (BaseType->isRecordType()) { - // Recover from arrow accesses to records, e.g.: - // struct MyRecord foo; - // foo->bar - // This is actually well-formed in C++ if MyRecord has an - // overloaded operator->, but that should have been dealt with - // by now--or a diagnostic message already issued if a problem - // was encountered while looking for the overloaded operator->. - if (!S.getLangOpts().CPlusPlus) { - S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "."); + else if (!BaseType->isDependentType()) { + if (const ObjCObjectPointerType *Ptr = + BaseType->getAs()) + BaseType = Ptr->getPointeeType(); + else if (BaseType->isRecordType()) { + // Recover from arrow accesses to records, e.g.: + // struct MyRecord foo; + // foo->bar + // This is actually well-formed in C++ if MyRecord has an + // overloaded operator->, but that should have been dealt with + // by now--or a diagnostic message already issued if a problem + // was encountered while looking for the overloaded operator->. + if (!S.getLangOpts().CPlusPlus) { + S.Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "."); + } + IsArrow = false; + } else if (BaseType->isFunctionType()) { + goto fail; + } else { + S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) + << BaseType << BaseExpr.get()->getSourceRange(); + return ExprError(); } - IsArrow = false; - } else if (BaseType->isFunctionType()) { - goto fail; - } else { - S.Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) - << BaseType << BaseExpr.get()->getSourceRange(); - return ExprError(); } } @@ -1384,10 +1367,10 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, } // Handle field access to simple records. - if (const RecordType *RTy = BaseType->getAs()) { + if (BaseType->getAsRecordDecl() || BaseType->isDependentType()) { TypoExpr *TE = nullptr; - if (LookupMemberExprInRecord(S, R, BaseExpr.get(), RTy, OpLoc, IsArrow, SS, - HasTemplateArgs, TemplateKWLoc, TE)) + if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, IsArrow, + SS, HasTemplateArgs, TemplateKWLoc, TE)) return ExprError(); // Returning valid-but-null is how we indicate to the caller that @@ -1824,13 +1807,6 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, if (Result.isInvalid()) return ExprError(); Base = Result.get(); - if (Base->getType()->isDependentType() || Name.isDependentName() || - isDependentScopeSpecifier(SS)) { - return ActOnDependentMemberExpr(Base, Base->getType(), IsArrow, OpLoc, SS, - TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs); - } - ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; ExprResult Res = BuildMemberReferenceExpr( Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 55af414df39f..a537eccc2eba 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -1282,6 +1282,31 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) { if (DeclContext *DC = PreS->getEntity()) DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); } + // C++23 [temp.dep.general]p2: + // The component name of an unqualified-id is dependent if + // - it is a conversion-function-id whose conversion-type-id + // is dependent, or + // - it is operator= and the current class is a templated entity, or + // - the unqualified-id is the postfix-expression in a dependent call. + if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && + Name.getCXXNameType()->isDependentType()) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + + // If this is the name of an implicitly-declared special member function, + // go through the scope stack to implicitly declare + if (isImplicitlyDeclaredMemberFunctionName(Name)) { + for (Scope *PreS = S; PreS; PreS = PreS->getParent()) + if (DeclContext *DC = PreS->getEntity()) { + if (DC->isDependentContext() && isa(DC) && + Name.getCXXOverloadedOperator() == OO_Equal) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + DeclareImplicitMemberFunctionsWithName(*this, Name, R.getNameLoc(), DC); + } + } // Implicitly declare member functions with the name we're looking for, if in // fact we are in a scope where it matters. @@ -2446,10 +2471,33 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, } } QL(LookupCtx); + CXXRecordDecl *LookupRec = dyn_cast(LookupCtx); + // FIXME: Per [temp.dep.general]p2, an unqualified name is also dependent + // if it's a dependent conversion-function-id or operator= where the current + // class is a templated entity. This should be handled in LookupName. + if (!InUnqualifiedLookup && !R.isForRedeclaration()) { + // C++23 [temp.dep.type]p5: + // A qualified name is dependent if + // - it is a conversion-function-id whose conversion-type-id + // is dependent, or + // - [...] + // - its lookup context is the current instantiation and it + // is operator=, or + // - [...] + if (DeclarationName Name = R.getLookupName(); + (Name.getNameKind() == DeclarationName::CXXConversionFunctionName && + Name.getCXXNameType()->isDependentType()) || + (Name.getCXXOverloadedOperator() == OO_Equal && LookupRec && + LookupRec->isDependentContext())) { + R.setNotFoundInCurrentInstantiation(); + return false; + } + } + if (LookupDirect(*this, R, LookupCtx)) { R.resolveKind(); - if (isa(LookupCtx)) - R.setNamingClass(cast(LookupCtx)); + if (LookupRec) + R.setNamingClass(LookupRec); return true; } @@ -2471,7 +2519,6 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // If this isn't a C++ class, we aren't allowed to look into base // classes, we're done. - CXXRecordDecl *LookupRec = dyn_cast(LookupCtx); if (!LookupRec || !LookupRec->getDefinition()) return false; @@ -2718,38 +2765,54 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, /// /// @returns True if any decls were found (but possibly ambiguous) bool Sema::LookupParsedName(LookupResult &R, Scope *S, CXXScopeSpec *SS, - bool AllowBuiltinCreation, bool EnteringContext) { - if (SS && SS->isInvalid()) { - // When the scope specifier is invalid, don't even look for - // anything. + QualType ObjectType, bool AllowBuiltinCreation, + bool EnteringContext) { + // When the scope specifier is invalid, don't even look for anything. + if (SS && SS->isInvalid()) return false; - } - if (SS && SS->isSet()) { - NestedNameSpecifier *NNS = SS->getScopeRep(); - if (NNS->getKind() == NestedNameSpecifier::Super) + // Determine where to perform name lookup + DeclContext *DC = nullptr; + bool IsDependent = false; + if (!ObjectType.isNull()) { + // This nested-name-specifier occurs in a member access expression, e.g., + // x->B::f, and we are looking into the type of the object. + assert((!SS || SS->isEmpty()) && + "ObjectType and scope specifier cannot coexist"); + DC = computeDeclContext(ObjectType); + IsDependent = !DC && ObjectType->isDependentType(); + assert(((!DC && ObjectType->isDependentType()) || + !ObjectType->isIncompleteType() || !ObjectType->getAs() || + ObjectType->castAs()->isBeingDefined()) && + "Caller should have completed object type"); + } else if (SS && SS->isNotEmpty()) { + if (NestedNameSpecifier *NNS = SS->getScopeRep(); + NNS->getKind() == NestedNameSpecifier::Super) return LookupInSuper(R, NNS->getAsRecordDecl()); - - if (DeclContext *DC = computeDeclContext(*SS, EnteringContext)) { - // We have resolved the scope specifier to a particular declaration - // contex, and will perform name lookup in that context. + // This nested-name-specifier occurs after another nested-name-specifier, + // so long into the context associated with the prior nested-name-specifier. + if (DC = computeDeclContext(*SS, EnteringContext)) { + // The declaration context must be complete. if (!DC->isDependentContext() && RequireCompleteDeclContext(*SS, DC)) return false; - R.setContextRange(SS->getRange()); - return LookupQualifiedName(R, DC); } + IsDependent = !DC && isDependentScopeSpecifier(*SS); + } else { + // Perform unqualified name lookup starting in the given scope. + return LookupName(R, S, AllowBuiltinCreation); + } + // If we were able to compute a declaration context, perform qualified name + // lookup in that context. + if (DC) + return LookupQualifiedName(R, DC); + else if (IsDependent) // We could not resolve the scope specified to a specific declaration // context, which means that SS refers to an unknown specialization. // Name lookup can't find anything in this case. R.setNotFoundInCurrentInstantiation(); - R.setContextRange(SS->getRange()); - return false; - } - - // Perform unqualified name lookup starting in the given scope. - return LookupName(R, S, AllowBuiltinCreation); + return false; } /// Perform qualified name lookup into all base classes of the given @@ -5018,8 +5081,9 @@ static void LookupPotentialTypoResult(Sema &SemaRef, return; } - SemaRef.LookupParsedName(Res, S, SS, /*AllowBuiltinCreation=*/false, - EnteringContext); + SemaRef.LookupParsedName(Res, S, SS, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/false, EnteringContext); // Fake ivar lookup; this should really be part of // LookupParsedName. diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index cee8da495c54..cf5447f223d4 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -3061,7 +3061,9 @@ ExprResult SemaOpenMP::ActOnOpenMPIdExpression(Scope *CurScope, OpenMPDirectiveKind Kind) { ASTContext &Context = getASTContext(); LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/true); if (Lookup.isAmbiguous()) return ExprError(); @@ -7407,7 +7409,8 @@ void SemaOpenMP::ActOnStartOfFunctionDefinitionInOpenMPDeclareVariantScope( const IdentifierInfo *BaseII = D.getIdentifier(); LookupResult Lookup(SemaRef, DeclarationName(BaseII), D.getIdentifierLoc(), Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec()); + SemaRef.LookupParsedName(Lookup, S, &D.getCXXScopeSpec(), + /*ObjectType=*/QualType()); TypeSourceInfo *TInfo = SemaRef.GetTypeForDeclarator(D); QualType FType = TInfo->getType(); @@ -19311,7 +19314,8 @@ buildDeclareReductionRef(Sema &SemaRef, SourceLocation Loc, SourceRange Range, if (S) { LookupResult Lookup(SemaRef, ReductionId, Sema::LookupOMPReductionName); Lookup.suppressDiagnostics(); - while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec)) { + while (S && SemaRef.LookupParsedName(Lookup, S, &ReductionIdScopeSpec, + /*ObjectType=*/QualType())) { NamedDecl *D = Lookup.getRepresentativeDecl(); do { S = S->getParent(); @@ -22180,7 +22184,8 @@ static ExprResult buildUserDefinedMapperRef(Sema &SemaRef, Scope *S, LookupResult Lookup(SemaRef, MapperId, Sema::LookupOMPMapperName); Lookup.suppressDiagnostics(); if (S) { - while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec)) { + while (S && SemaRef.LookupParsedName(Lookup, S, &MapperIdScopeSpec, + /*ObjectType=*/QualType())) { NamedDecl *D = Lookup.getRepresentativeDecl(); while (S && !S->isDeclScope(D)) S = S->getParent(); @@ -23497,7 +23502,9 @@ void SemaOpenMP::DiagnoseUnterminatedOpenMPDeclareTarget() { NamedDecl *SemaOpenMP::lookupOpenMPDeclareTargetName( Scope *CurScope, CXXScopeSpec &ScopeSpec, const DeclarationNameInfo &Id) { LookupResult Lookup(SemaRef, Id, Sema::LookupOrdinaryName); - SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, true); + SemaRef.LookupParsedName(Lookup, CurScope, &ScopeSpec, + /*ObjectType=*/QualType(), + /*AllowBuiltinCreation=*/true); if (Lookup.isAmbiguous()) return nullptr; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index bbcb7c33a985..72bf6370ca82 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -210,10 +210,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S, AssumedTemplateKind AssumedTemplate; LookupResult R(*this, TName, Name.getBeginLoc(), LookupOrdinaryName); if (LookupTemplateName(R, S, SS, ObjectType, EnteringContext, - MemberOfUnknownSpecialization, SourceLocation(), + /*RequiredTemplate=*/SourceLocation(), &AssumedTemplate, /*AllowTypoCorrection=*/!Disambiguation)) return TNK_Non_template; + MemberOfUnknownSpecialization = R.wasNotFoundInCurrentInstantiation(); if (AssumedTemplate != AssumedTemplateKind::None) { TemplateResult = TemplateTy::make(Context.getAssumedTemplateName(TName)); @@ -320,15 +321,12 @@ TemplateNameKind Sema::isTemplateName(Scope *S, bool Sema::isDeductionGuideName(Scope *S, const IdentifierInfo &Name, SourceLocation NameLoc, CXXScopeSpec &SS, ParsedTemplateTy *Template /*=nullptr*/) { - bool MemberOfUnknownSpecialization = false; - // We could use redeclaration lookup here, but we don't need to: the // syntactic form of a deduction guide is enough to identify it even // if we can't look up the template name at all. LookupResult R(*this, DeclarationName(&Name), NameLoc, LookupOrdinaryName); if (LookupTemplateName(R, S, SS, /*ObjectType*/ QualType(), - /*EnteringContext*/ false, - MemberOfUnknownSpecialization)) + /*EnteringContext*/ false)) return false; if (R.empty()) return false; @@ -374,11 +372,8 @@ bool Sema::DiagnoseUnknownTemplateName(const IdentifierInfo &II, return true; } -bool Sema::LookupTemplateName(LookupResult &Found, - Scope *S, CXXScopeSpec &SS, - QualType ObjectType, - bool EnteringContext, - bool &MemberOfUnknownSpecialization, +bool Sema::LookupTemplateName(LookupResult &Found, Scope *S, CXXScopeSpec &SS, + QualType ObjectType, bool EnteringContext, RequiredTemplateKind RequiredTemplate, AssumedTemplateKind *ATK, bool AllowTypoCorrection) { @@ -391,7 +386,6 @@ bool Sema::LookupTemplateName(LookupResult &Found, Found.setTemplateNameLookup(true); // Determine where to perform name lookup - MemberOfUnknownSpecialization = false; DeclContext *LookupCtx = nullptr; bool IsDependent = false; if (!ObjectType.isNull()) { @@ -548,7 +542,7 @@ bool Sema::LookupTemplateName(LookupResult &Found, FilterAcceptableTemplateNames(Found, AllowFunctionTemplatesInLookup); if (Found.empty()) { if (IsDependent) { - MemberOfUnknownSpecialization = true; + Found.setNotFoundInCurrentInstantiation(); return false; } @@ -5595,11 +5589,9 @@ Sema::BuildQualifiedTemplateIdExpr(CXXScopeSpec &SS, RequireCompleteDeclContext(SS, DC)) return BuildDependentDeclRefExpr(SS, TemplateKWLoc, NameInfo, TemplateArgs); - bool MemberOfUnknownSpecialization; LookupResult R(*this, NameInfo, LookupOrdinaryName); if (LookupTemplateName(R, (Scope *)nullptr, SS, QualType(), - /*Entering*/false, MemberOfUnknownSpecialization, - TemplateKWLoc)) + /*Entering*/ false, TemplateKWLoc)) return ExprError(); if (R.isAmbiguous()) @@ -5720,14 +5712,13 @@ TemplateNameKind Sema::ActOnTemplateName(Scope *S, DeclarationNameInfo DNI = GetNameFromUnqualifiedId(Name); LookupResult R(*this, DNI.getName(), Name.getBeginLoc(), LookupOrdinaryName); - bool MOUS; // Tell LookupTemplateName that we require a template so that it diagnoses // cases where it finds a non-template. RequiredTemplateKind RTK = TemplateKWLoc.isValid() ? RequiredTemplateKind(TemplateKWLoc) : TemplateNameIsRequired; - if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, MOUS, - RTK, nullptr, /*AllowTypoCorrection=*/false) && + if (!LookupTemplateName(R, S, SS, ObjectType.get(), EnteringContext, RTK, + /*ATK=*/nullptr, /*AllowTypoCorrection=*/false) && !R.isAmbiguous()) { if (LookupCtx) Diag(Name.getBeginLoc(), diag::err_no_member) @@ -5816,7 +5807,7 @@ bool Sema::CheckTemplateTypeArgument( if (auto *II = NameInfo.getName().getAsIdentifierInfo()) { LookupResult Result(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Result, CurScope, &SS); + LookupParsedName(Result, CurScope, &SS, /*ObjectType=*/QualType()); if (Result.getAsSingle() || Result.getResultKind() == @@ -11179,7 +11170,8 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, : TSK_ExplicitInstantiationDeclaration; LookupResult Previous(*this, NameInfo, LookupOrdinaryName); - LookupParsedName(Previous, S, &D.getCXXScopeSpec()); + LookupParsedName(Previous, S, &D.getCXXScopeSpec(), + /*ObjectType=*/QualType()); if (!R->isFunctionType()) { // C++ [temp.explicit]p1: diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index f47bc219e6fa..28d3d1b79a74 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -13217,6 +13217,26 @@ bool TreeTransform::TransformOverloadExprDecls(OverloadExpr *Old, // Resolve a kind, but don't do any further analysis. If it's // ambiguous, the callee needs to deal with it. R.resolveKind(); + + if (Old->hasTemplateKeyword() && !R.empty()) { + NamedDecl *FoundDecl = R.getRepresentativeDecl()->getUnderlyingDecl(); + getSema().FilterAcceptableTemplateNames(R, + /*AllowFunctionTemplates=*/true, + /*AllowDependent=*/true); + if (R.empty()) { + // If a 'template' keyword was used, a lookup that finds only non-template + // names is an error. + getSema().Diag(R.getNameLoc(), + diag::err_template_kw_refers_to_non_template) + << R.getLookupName() << Old->getQualifierLoc().getSourceRange() + << Old->hasTemplateKeyword() << Old->getTemplateKeywordLoc(); + getSema().Diag(FoundDecl->getLocation(), + diag::note_template_kw_refers_to_non_template) + << R.getLookupName(); + return true; + } + } + return false; } diff --git a/clang/test/AST/HLSL/this-reference-template.hlsl b/clang/test/AST/HLSL/this-reference-template.hlsl index 60e057986ebf..d427e73044b7 100644 --- a/clang/test/AST/HLSL/this-reference-template.hlsl +++ b/clang/test/AST/HLSL/this-reference-template.hlsl @@ -24,7 +24,7 @@ void main() { // CHECK: -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} line:8:5 getFirst 'K ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} // CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} -// CHECK-NEXT:-CXXDependentScopeMemberExpr 0x{{[0-9A-Fa-f]+}} '' lvalue .First +// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} 'K' lvalue .First 0x{{[0-9A-Fa-f]+}} // CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} 'Pair' lvalue this // CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} line:12:5 getSecond 'V ()' implicit-inline // CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp index 5d3e8ce4bea3..2b3131be3305 100644 --- a/clang/test/CXX/drs/dr2xx.cpp +++ b/clang/test/CXX/drs/dr2xx.cpp @@ -561,9 +561,9 @@ namespace cwg244 { // cwg244: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg244::~B(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} B_ptr->cwg244::~B_alias(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg244'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg244'}} } template @@ -836,7 +836,7 @@ namespace cwg258 { // cwg258: 2.8 namespace cwg259 { // cwg259: 4 template struct A {}; - template struct A; // #cwg259-A-int + template struct A; // #cwg259-A-int template struct A; // expected-error@-1 {{duplicate explicit instantiation of 'A'}} // expected-note@#cwg259-A-int {{previous explicit instantiation is here}} @@ -997,7 +997,7 @@ namespace cwg275 { // cwg275: no // expected-error@-1 {{no function template matches function template specialization 'f'}} } - template void g(T) {} // #cwg275-g + template void g(T) {} // #cwg275-g template <> void N::f(char) {} template <> void f(int) {} @@ -1164,7 +1164,7 @@ namespace cwg285 { // cwg285: yes namespace cwg286 { // cwg286: 2.8 template struct A { class C { - template struct B {}; // #cwg286-B + template struct B {}; // #cwg286-B }; }; diff --git a/clang/test/CXX/drs/dr3xx.cpp b/clang/test/CXX/drs/dr3xx.cpp index 3e9228fe21fb..94227dc031c6 100644 --- a/clang/test/CXX/drs/dr3xx.cpp +++ b/clang/test/CXX/drs/dr3xx.cpp @@ -34,7 +34,7 @@ namespace cwg301 { // cwg301: 3.5 bool b = (void(*)(S, S))operator- < (void(*)(S, S))operator-; // cxx98-17-warning@-1 {{ordered comparison of function pointers ('void (*)(S, S)' and 'void (*)(S, S)')}} // cxx20-23-error@-2 {{expected '>'}} - // cxx20-23-note@-3 {{to match this '<'}} + // cxx20-23-note@-3 {{to match this '<'}} bool c = (void(*)(S, S))operator+ < (void(*)(S, S))operator-; // expected-error@-1 {{expected '>'}} // expected-note@-2 {{to match this '<'}} @@ -642,7 +642,7 @@ namespace cwg339 { // cwg339: 2.8 char xxx(int); char (&xxx(float))[2]; - template A f(T) {} // #cwg339-f + template A f(T) {} // #cwg339-f void test() { A<1> a = f(0); @@ -828,7 +828,7 @@ namespace cwg352 { // cwg352: 2.8 void g(A::E e) { foo(e, &arg); // expected-error@-1 {{no matching function for call to 'foo'}} - // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} + // expected-note@#cwg352-foo {{candidate template ignored: couldn't infer template argument 'R'}} using A::foo; foo(e, &arg); // ok, uses non-template @@ -929,7 +929,7 @@ namespace cwg352 { // cwg352: 2.8 namespace example5 { template class A {}; - template void g(A); // #cwg352-g + template void g(A); // #cwg352-g template void f(A, A); void h(A<1> a1, A<2> a2) { g(a1); @@ -1256,7 +1256,7 @@ namespace cwg373 { // cwg373: 5 } }; - struct A { struct B {}; }; // #cwg373-A + struct A { struct B {}; }; // #cwg373-A namespace X = A::B; // expected-error@-1 {{expected namespace name}} // expected-note@#cwg373-A {{'A' declared here}} @@ -1608,7 +1608,7 @@ namespace cwg395 { // cwg395: 3.0 // expected-error@-2 {{conversion function cannot have any parameters}} // expected-error@-3 {{cannot specify any part of a return type in the declaration of a conversion function}} // expected-error@-4 {{conversion function cannot convert to a function type}} - + }; struct null1_t { @@ -1721,9 +1721,9 @@ namespace cwg399 { // cwg399: 11 B_ptr->B_alias::~B(); B_ptr->B_alias::~B_alias(); B_ptr->cwg399::~B(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} B_ptr->cwg399::~B_alias(); - // expected-error@-1 {{qualified member access refers to a member in namespace 'cwg399'}} + // expected-error@-1 {{no member named '~B' in namespace 'cwg399'}} } template diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp new file mode 100644 index 000000000000..b1d2859be863 --- /dev/null +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -0,0 +1,456 @@ +// RUN: %clang_cc1 -Wno-unused-value -verify %s + +namespace N0 { + struct A { + int x0; + static int y0; + int x1; + static int y1; + + void f0(); + static void g0(); + void f1(); + static void g1(); + + using M0 = int; + using M1 = int; + + struct C0 { }; + struct C1 { }; + }; + + template + struct B : A { + int x2; + static int y2; + + void f2(); + static void g2(); + + using M2 = int; + + struct C2 { }; + + using A::x1; + using A::y1; + using A::f1; + using A::g1; + using A::M1; + using A::C1; + + using T::x3; + using T::y3; + using T::f3; + using T::g3; + using typename T::M3; + using typename T::C3; + + void not_instantiated(B *a, B &b) { + // All of the following should be found in the current instantiation. + + new M0; + new B::M0; + new A::M0; + new B::A::M0; + new C0; + new B::C0; + new A::C0; + new B::A::C0; + new M1; + new B::M1; + new A::M1; + new B::A::M1; + new C1; + new B::C1; + new A::C1; + new B::A::C1; + new M2; + new B::M2; + new C2; + new B::C2; + new M3; + new B::M3; + new C3; + new B::C3; + + x0; + B::x0; + A::x0; + B::A::x0; + y0; + B::y0; + A::y0; + B::A::y0; + x1; + B::x1; + A::x1; + B::A::x1; + y1; + B::y1; + A::y1; + B::A::y1; + x2; + B::x2; + y2; + B::y2; + x3; + B::x3; + y3; + B::y3; + + f0(); + B::f0(); + A::f0(); + B::A::f0(); + g0(); + B::g0(); + A::g0(); + B::A::g0(); + f1(); + B::f1(); + A::f1(); + B::A::f1(); + g1(); + B::g1(); + A::g1(); + B::A::g1(); + f2(); + B::f2(); + g2(); + B::g2(); + f3(); + B::f3(); + g3(); + B::g3(); + + this->x0; + this->B::x0; + this->A::x0; + this->B::A::x0; + this->y0; + this->B::y0; + this->A::y0; + this->B::A::y0; + this->x1; + this->B::x1; + this->A::x1; + this->B::A::x1; + this->y1; + this->B::y1; + this->A::y1; + this->B::A::y1; + this->x2; + this->B::x2; + this->y2; + this->B::y2; + this->x3; + this->B::x3; + this->y3; + this->B::y3; + + this->f0(); + this->B::f0(); + this->A::f0(); + this->B::A::f0(); + this->g0(); + this->B::g0(); + this->A::g0(); + this->B::A::g0(); + this->f1(); + this->B::f1(); + this->A::f1(); + this->B::A::f1(); + this->g1(); + this->B::g1(); + this->A::g1(); + this->B::A::g1(); + this->f2(); + this->B::f2(); + this->g2(); + this->B::g2(); + this->f3(); + this->B::f3(); + this->g3(); + this->B::g3(); + + a->x0; + a->B::x0; + a->A::x0; + a->B::A::x0; + a->y0; + a->B::y0; + a->A::y0; + a->B::A::y0; + a->x1; + a->B::x1; + a->A::x1; + a->B::A::x1; + a->y1; + a->B::y1; + a->A::y1; + a->B::A::y1; + a->x2; + a->B::x2; + a->y2; + a->B::y2; + a->x3; + a->B::x3; + a->y3; + a->B::y3; + + a->f0(); + a->B::f0(); + a->A::f0(); + a->B::A::f0(); + a->g0(); + a->B::g0(); + a->A::g0(); + a->B::A::g0(); + a->f1(); + a->B::f1(); + a->A::f1(); + a->B::A::f1(); + a->g1(); + a->B::g1(); + a->A::g1(); + a->B::A::g1(); + a->f2(); + a->B::f2(); + a->g2(); + a->B::g2(); + a->f3(); + a->B::f3(); + a->g3(); + a->B::g3(); + + (*this).x0; + (*this).B::x0; + (*this).A::x0; + (*this).B::A::x0; + (*this).y0; + (*this).B::y0; + (*this).A::y0; + (*this).B::A::y0; + (*this).x1; + (*this).B::x1; + (*this).A::x1; + (*this).B::A::x1; + (*this).y1; + (*this).B::y1; + (*this).A::y1; + (*this).B::A::y1; + (*this).x2; + (*this).B::x2; + (*this).y2; + (*this).B::y2; + (*this).x3; + (*this).B::x3; + (*this).y3; + (*this).B::y3; + + (*this).f0(); + (*this).B::f0(); + (*this).A::f0(); + (*this).B::A::f0(); + (*this).g0(); + (*this).B::g0(); + (*this).A::g0(); + (*this).B::A::g0(); + (*this).f1(); + (*this).B::f1(); + (*this).A::f1(); + (*this).B::A::f1(); + (*this).g1(); + (*this).B::g1(); + (*this).A::g1(); + (*this).B::A::g1(); + (*this).f2(); + (*this).B::f2(); + (*this).g2(); + (*this).B::g2(); + (*this).f3(); + (*this).B::f3(); + (*this).g3(); + (*this).B::g3(); + + b.x0; + b.B::x0; + b.A::x0; + b.B::A::x0; + b.y0; + b.B::y0; + b.A::y0; + b.B::A::y0; + b.x1; + b.B::x1; + b.A::x1; + b.B::A::x1; + b.y1; + b.B::y1; + b.A::y1; + b.B::A::y1; + b.x2; + b.B::x2; + b.y2; + b.B::y2; + b.x3; + b.B::x3; + b.y3; + b.B::y3; + + b.f0(); + b.B::f0(); + b.A::f0(); + b.B::A::f0(); + b.g0(); + b.B::g0(); + b.A::g0(); + b.B::A::g0(); + b.f1(); + b.B::f1(); + b.A::f1(); + b.B::A::f1(); + b.g1(); + b.B::g1(); + b.A::g1(); + b.B::A::g1(); + b.f2(); + b.B::f2(); + b.g2(); + b.B::g2(); + b.f3(); + b.B::f3(); + b.g3(); + b.B::g3(); + + // None of the following should be found in the current instantiation. + + new M4; // expected-error{{unknown type name 'M4'}} + new B::M4; // expected-error{{no type named 'M4' in 'B'}} + new A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} + new B::A::M4; // expected-error{{no type named 'M4' in 'N0::A'}} + + x4; // expected-error{{use of undeclared identifier 'x4'}} + B::x4; // expected-error{{no member named 'x4' in 'B'}} + A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + f4(); // expected-error{{use of undeclared identifier 'f4'}} + B::f4(); // expected-error{{no member named 'f4' in 'B'}} + A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + this->x4; // expected-error{{no member named 'x4' in 'B'}} + this->B::x4; // expected-error{{no member named 'x4' in 'B'}} + this->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + this->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + this->f4(); // expected-error{{no member named 'f4' in 'B'}} + this->B::f4(); // expected-error{{no member named 'f4' in 'B'}} + this->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + this->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + a->x4; // expected-error{{no member named 'x4' in 'B'}} + a->B::x4; // expected-error{{no member named 'x4' in 'B'}} + a->A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + a->B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + a->f4(); // expected-error{{no member named 'f4' in 'B'}} + a->B::f4(); // expected-error{{no member named 'f4' in 'B'}} + a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + + // FIXME: An overloaded unary 'operator*' is built for these + // even though the operand is a pointer (to a dependent type). + // Type::isOverloadableType should return false for such cases. + (*this).x4; + (*this).B::x4; + (*this).A::x4; + (*this).B::A::x4; + (*this).f4(); + (*this).B::f4(); + (*this).A::f4(); + (*this).B::A::f4(); + + b.x4; // expected-error{{no member named 'x4' in 'B'}} + b.B::x4; // expected-error{{no member named 'x4' in 'B'}} + b.A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + b.B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + b.f4(); // expected-error{{no member named 'f4' in 'B'}} + b.B::f4(); // expected-error{{no member named 'f4' in 'B'}} + b.A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + b.B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + } + }; +} // namespace N0 + +namespace N1 { + struct A { + template + void f(); + }; + + template + struct B { + template + void f(); + + A x; + A g(); + + void not_instantiated(B *a, B &b) { + f<0>(); + this->f<0>(); + a->f<0>(); + // FIXME: This should not require 'template'! + (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.f<0>(); + + x.f<0>(); + this->x.f<0>(); + a->x.f<0>(); + // FIXME: This should not require 'template'! + (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.x.f<0>(); + + // FIXME: None of these should require 'template'! + g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + this->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + a->g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + b.g().f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + } + }; +} // namespace N1 + +namespace N2 { + template + struct A { + struct B { + using C = A; + + void not_instantiated(A *a, B *b) { + b->x; // expected-error{{no member named 'x' in 'N2::A::B'}} + b->B::x; // expected-error{{no member named 'x' in 'N2::A::B'}} + a->B::C::x; // expected-error{{no member named 'x' in 'A'}} + } + }; + + void not_instantiated(A *a, B *b) { + b->x; + b->B::x; + a->B::C::x; + } + }; +} + +namespace N3 { + struct A { }; + + template + struct B : A { + void not_instantiated() { + // Dependent, lookup context is the current instantiation. + this->operator=(*this); + // Not dependent, the lookup context is A (not the current instantiation). + this->A::operator=(*this); + } + }; +} diff --git a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp index 87589e1e5bcd..b9b29d22736e 100644 --- a/clang/test/CXX/temp/temp.res/temp.local/p3.cpp +++ b/clang/test/CXX/temp/temp.res/temp.local/p3.cpp @@ -16,8 +16,7 @@ template struct Derived: Base, Base { void g(X0 *t) { t->Derived::Base::f(); t->Base::f(); - t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} \ - // expected-error{{no member named 'f' in 'X0'}} + t->Base::f(); // expected-error{{member 'Base' found in multiple base classes of different types}} } }; diff --git a/clang/test/CodeGenCXX/mangle.cpp b/clang/test/CodeGenCXX/mangle.cpp index 31467d943840..d0800af55c87 100644 --- a/clang/test/CodeGenCXX/mangle.cpp +++ b/clang/test/CodeGenCXX/mangle.cpp @@ -1032,10 +1032,6 @@ namespace test51 { template decltype(S1().~S1(), S1().~S1()) fun4() {}; template - decltype(S1().~S1()) fun5(){}; - template