[llvm-branch-commits] [clang] [clang-tools-extra] [flang] [lldb] [llvm] [clang-tidy] support to detect conversion in `make_optional` for `bugprone-optional-value-conversion` (PR #130417)
Congcong Cai via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Mar 11 02:13:54 PDT 2025
https://github.com/HerrCai0907 updated https://github.com/llvm/llvm-project/pull/130417
>From 5cfc37b3458b89927e76950c9498152ab729803e Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Tue, 11 Mar 2025 14:53:06 +0800
Subject: [PATCH 01/13] [clang-tidy][NFC]clean ConstCorrectnessCheck (#130493)
---
.../clang-tidy/misc/ConstCorrectnessCheck.cpp | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
index 6e412e576e5f9..dbe59233df699 100644
--- a/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
+++ b/clang-tools-extra/clang-tidy/misc/ConstCorrectnessCheck.cpp
@@ -136,16 +136,14 @@ void ConstCorrectnessCheck::check(const MatchFinder::MatchResult &Result) {
return;
VariableCategory VC = VariableCategory::Value;
- if (Variable->getType()->isReferenceType())
+ const QualType VT = Variable->getType();
+ if (VT->isReferenceType())
VC = VariableCategory::Reference;
- if (Variable->getType()->isPointerType())
+ else if (VT->isPointerType())
VC = VariableCategory::Pointer;
- if (Variable->getType()->isArrayType()) {
- if (const auto *ArrayT = dyn_cast<ArrayType>(Variable->getType())) {
- if (ArrayT->getElementType()->isPointerType())
- VC = VariableCategory::Pointer;
- }
- }
+ else if (const auto *ArrayT = dyn_cast<ArrayType>(VT))
+ if (ArrayT->getElementType()->isPointerType())
+ VC = VariableCategory::Pointer;
// Each variable can only be in one category: Value, Pointer, Reference.
// Analysis can be controlled for every category.
>From 1706e3fc5819602febf9dfa554e98eb1e1fea365 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Sat, 8 Mar 2025 21:50:19 +0800
Subject: [PATCH 02/13] [clang-tidy] support to detect conversion in
`make_optional` for `bugprone-optional-value-conversion`
Fixes: #119554
---
.../bugprone/OptionalValueConversionCheck.cpp | 14 ++++++++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 4 ++++
...ptional-value-conversion-construct-from-std.cpp | 13 +++++++++++++
3 files changed, 31 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
index 33e823ac07490..cb5a1c7bea801 100644
--- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
@@ -12,6 +12,7 @@
#include "../utils/OptionsUtils.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
#include <array>
using namespace clang::ast_matchers;
@@ -31,6 +32,7 @@ constexpr std::array<StringRef, 2> MakeSmartPtrList{
"::std::make_unique",
"::std::make_shared",
};
+constexpr StringRef MakeOptional = "::std::make_optional";
} // namespace
@@ -86,6 +88,18 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
callee(functionDecl(
matchers::matchesAnyListedName(MakeSmartPtrList),
hasTemplateArgument(0, refersToType(BindOptionalType)))),
+ hasArgument(0, OptionalDerefMatcher)),
+ callExpr(
+ // match first std::make_optional by limit argument count (1)
+ // and template count (1).
+ // 1. template< class T > constexpr
+ // std::optional<decay_t<T>> make_optional(T&& value);
+ // 2. template< class T, class... Args > constexpr
+ // std::optional<T> make_optional(Args&&... args);
+ argumentCountIs(1),
+ callee(functionDecl(templateArgumentCountIs(1),
+ hasName(MakeOptional),
+ returns(BindOptionalType))),
hasArgument(0, OptionalDerefMatcher))),
unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(matchers::hasUnevaluatedContext())))))
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index fa68b6fabd549..5e2d87e0c2fa1 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -124,6 +124,10 @@ Changes in existing checks
no longer be needed and will be removed. Also fixing false positive from
const reference accessors to objects containing optional member.
+- Improved :doc:`bugprone-optional-value-conversion
+ <clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
+ conversion in argument of ``std::make_optional``.
+
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp
index 768ab1ce014ce..305fd6890710d 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/optional-value-conversion-construct-from-std.cpp
@@ -27,9 +27,19 @@ class unique_ptr {};
template <typename type>
class shared_ptr {};
+template <typename T>
+class initializer_list {};
+
template <class T, class... Args> unique_ptr<T> make_unique(Args &&...args);
template <class T, class... Args> shared_ptr<T> make_shared(Args &&...args);
+template <class T>
+constexpr std::optional<__decay(T)> make_optional(T &&value);
+template <class T, class... Args>
+constexpr std::optional<T> make_optional(Args &&...args);
+template <class T, class U, class... Args>
+constexpr std::optional<T> make_optional(std::initializer_list<U> il, Args &&...args);
+
} // namespace std
struct A {
@@ -45,9 +55,12 @@ void invalid() {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
std::make_shared<std::optional<int>>(opt.value());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
+ std::make_optional(opt.value());
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: conversion from 'std::optional<int>' into 'int' and back into 'std::optional<int>', remove potentially error-prone optional dereference [bugprone-optional-value-conversion]
}
void valid() {
std::make_unique<A>(opt.value());
std::make_shared<A>(opt.value());
+ std::make_optional<int>(opt.value());
}
>From 23d5123a08ad376d9a3cb2700fe2da1f8c1cb006 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Tue, 11 Mar 2025 00:01:13 -0700
Subject: [PATCH 03/13] Revert "[llvm] add support for mustache templating
language" (#130720)
Reverts llvm/llvm-project#105893 since it broke all builders that use
GCC as the compiler.
>From f120b0d6d2629e226e6fa75974fbd17f46206bca Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Tue, 11 Mar 2025 00:21:31 -0700
Subject: [PATCH 04/13] [MC] Remove MCSymbolRefExpr::VK_Invalid in favor of
getVaraintKindForName returning std::optional
so that when the enum members are moved to XXXTargetExpr::VariantKind,,
they do not need to implement an invalid value.
---
llvm/include/llvm/MC/MCAsmInfo.h | 2 +-
llvm/include/llvm/MC/MCExpr.h | 1 -
llvm/lib/MC/MCAsmInfo.cpp | 4 ++--
llvm/lib/MC/MCParser/AsmParser.cpp | 20 ++++++++++----------
llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 4 ++--
5 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h
index aba8eaed82d1a..7f03097eba060 100644
--- a/llvm/include/llvm/MC/MCAsmInfo.h
+++ b/llvm/include/llvm/MC/MCAsmInfo.h
@@ -709,7 +709,7 @@ class MCAsmInfo {
bool shouldUseMotorolaIntegers() const { return UseMotorolaIntegers; }
StringRef getVariantKindName(uint32_t Kind) const;
- uint32_t getVariantKindForName(StringRef Name) const;
+ std::optional<uint32_t> getVariantKindForName(StringRef Name) const;
};
} // end namespace llvm
diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h
index ab96b39d9f296..a302a81ac468a 100644
--- a/llvm/include/llvm/MC/MCExpr.h
+++ b/llvm/include/llvm/MC/MCExpr.h
@@ -198,7 +198,6 @@ class MCSymbolRefExpr : public MCExpr {
// cleaner approach.
enum VariantKind : uint16_t {
VK_None,
- VK_Invalid,
VK_GOT,
VK_GOTENT,
diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp
index 9821bb298d7d4..69374f69bc8e5 100644
--- a/llvm/lib/MC/MCAsmInfo.cpp
+++ b/llvm/lib/MC/MCAsmInfo.cpp
@@ -145,9 +145,9 @@ StringRef MCAsmInfo::getVariantKindName(uint32_t Kind) const {
return It->second;
}
-uint32_t MCAsmInfo::getVariantKindForName(StringRef Name) const {
+std::optional<uint32_t> MCAsmInfo::getVariantKindForName(StringRef Name) const {
auto It = NameToVariantKind.find(Name.lower());
if (It != NameToVariantKind.end())
return It->second;
- return MCSymbolRefExpr::VK_Invalid;
+ return {};
}
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 1e1b63717ded5..0cd691e9298ed 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -1228,10 +1228,10 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
// Lookup the symbol variant if used.
if (!Split.second.empty()) {
- Variant =
- MCSymbolRefExpr::VariantKind(MAI.getVariantKindForName(Split.second));
- if (Variant != MCSymbolRefExpr::VK_Invalid) {
+ auto MaybeVariant = MAI.getVariantKindForName(Split.second);
+ if (MaybeVariant) {
SymbolName = Split.first;
+ Variant = MCSymbolRefExpr::VariantKind(*MaybeVariant);
} else if (MAI.doesAllowAtInName() && !MAI.useParensForSymbolVariant()) {
Variant = MCSymbolRefExpr::VK_None;
} else {
@@ -1279,11 +1279,11 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
std::pair<StringRef, StringRef> Split = IDVal.split('@');
MCSymbolRefExpr::VariantKind Variant = MCSymbolRefExpr::VK_None;
if (Split.first.size() != IDVal.size()) {
- Variant = MCSymbolRefExpr::VariantKind(
- MAI.getVariantKindForName(Split.second));
- if (Variant == MCSymbolRefExpr::VK_Invalid)
+ auto MaybeVariant = MAI.getVariantKindForName(Split.second);
+ if (!MaybeVariant)
return TokError("invalid variant '" + Split.second + "'");
IDVal = Split.first;
+ Variant = MCSymbolRefExpr::VariantKind(*MaybeVariant);
}
if (IDVal == "f" || IDVal == "b") {
MCSymbol *Sym =
@@ -1470,12 +1470,12 @@ bool AsmParser::parseExpression(const MCExpr *&Res, SMLoc &EndLoc) {
if (Lexer.isNot(AsmToken::Identifier))
return TokError("unexpected symbol modifier following '@'");
- auto Variant = MCSymbolRefExpr::VariantKind(
- MAI.getVariantKindForName(getTok().getIdentifier()));
- if (Variant == MCSymbolRefExpr::VK_Invalid)
+ auto Variant = MAI.getVariantKindForName(getTok().getIdentifier());
+ if (!Variant)
return TokError("invalid variant '" + getTok().getIdentifier() + "'");
- const MCExpr *ModifiedRes = applyModifierToExpr(Res, Variant);
+ const MCExpr *ModifiedRes =
+ applyModifierToExpr(Res, MCSymbolRefExpr::VariantKind(*Variant));
if (!ModifiedRes) {
return TokError("invalid modifier '" + getTok().getIdentifier() +
"' (no symbols present)");
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index cd0704cad2462..be0df763029a5 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -109,10 +109,10 @@ struct DenseMapInfo<std::pair<const MCSymbol *, MCSymbolRefExpr::VariantKind>> {
using TOCKey = std::pair<const MCSymbol *, MCSymbolRefExpr::VariantKind>;
static inline TOCKey getEmptyKey() {
- return {nullptr, MCSymbolRefExpr::VariantKind::VK_None};
+ return {nullptr, MCSymbolRefExpr::VK_None};
}
static inline TOCKey getTombstoneKey() {
- return {nullptr, MCSymbolRefExpr::VariantKind::VK_Invalid};
+ return {(const MCSymbol *)1, MCSymbolRefExpr::VK_None};
}
static unsigned getHashValue(const TOCKey &PairVal) {
return detail::combineHashValue(
>From f4218753ad93dd44b019e38bae61dceb93514aee Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Tue, 11 Mar 2025 15:41:56 +0800
Subject: [PATCH 05/13] [Clang] Implement P0963R3 "Structured binding
declaration as a condition" (#130228)
This implements the R2 semantics of P0963.
The R1 semantics, as outlined in the paper, were introduced in Clang 6.
In addition to that, the paper proposes swapping the evaluation order of
condition expressions and the initialization of binding declarations
(i.e. std::tuple-like decompositions).
---
clang/docs/LanguageExtensions.rst | 95 ++++----
clang/docs/ReleaseNotes.rst | 2 +
.../clang/Basic/DiagnosticSemaKinds.td | 8 +-
clang/lib/AST/ByteCode/Compiler.cpp | 37 ++-
clang/lib/AST/ByteCode/Compiler.h | 3 +-
clang/lib/AST/ExprConstant.cpp | 41 +++-
clang/lib/CodeGen/CGDecl.cpp | 18 +-
clang/lib/CodeGen/CGStmt.cpp | 20 +-
clang/lib/CodeGen/CodeGenFunction.cpp | 5 +-
clang/lib/CodeGen/CodeGenFunction.h | 7 +-
clang/lib/Sema/SemaDeclCXX.cpp | 17 +-
clang/test/AST/ByteCode/if.cpp | 2 +-
clang/test/CodeGen/p0963r3.cpp | 212 ++++++++++++++++++
clang/test/Parser/cxx1z-decomposition.cpp | 12 +-
clang/test/Parser/decomposed-condition.cpp | 24 +-
clang/test/SemaCXX/decomposed-condition.cpp | 16 +-
clang/www/cxx_status.html | 2 +-
17 files changed, 409 insertions(+), 112 deletions(-)
create mode 100644 clang/test/CodeGen/p0963r3.cpp
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 578ee02f09b9b..f8d1bc9fe2624 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1604,53 +1604,54 @@ More information could be found `here <https://clang.llvm.org/docs/Modules.html>
Language Extensions Back-ported to Previous Standards
=====================================================
-============================================ ================================ ============= =============
-Feature Feature Test Macro Introduced In Backported To
-============================================ ================================ ============= =============
-variadic templates __cpp_variadic_templates C++11 C++03
-Alias templates __cpp_alias_templates C++11 C++03
-Non-static data member initializers __cpp_nsdmi C++11 C++03
-Range-based ``for`` loop __cpp_range_based_for C++11 C++03
-RValue references __cpp_rvalue_references C++11 C++03
-Attributes __cpp_attributes C++11 C++03
-Lambdas __cpp_lambdas C++11 C++03
-Generalized lambda captures __cpp_init_captures C++14 C++03
-Generic lambda expressions __cpp_generic_lambdas C++14 C++03
-variable templates __cpp_variable_templates C++14 C++03
-Binary literals __cpp_binary_literals C++14 C++03
-Relaxed constexpr __cpp_constexpr C++14 C++11
-Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
-Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
-``if constexpr`` __cpp_if_constexpr C++17 C++11
-fold expressions __cpp_fold_expressions C++17 C++03
-Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
-Attributes on enums __cpp_enumerator_attributes C++17 C++03
-Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
-Hexadecimal floating literals __cpp_hex_float C++17 C++03
-``inline`` variables __cpp_inline_variables C++17 C++03
-Attributes on namespaces __cpp_namespace_attributes C++17 C++11
-Structured bindings __cpp_structured_bindings C++17 C++03
-template template arguments __cpp_template_template_args C++17 C++03
-Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
-``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
-Designated initializers __cpp_designated_initializers C++20 C++03
-Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
-``using enum`` __cpp_using_enum C++20 C++03
-``if consteval`` __cpp_if_consteval C++23 C++20
-``static operator()`` __cpp_static_call_operator C++23 C++03
-Attributes on Lambda-Expressions C++23 C++11
-Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
-Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
-Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
-Pack Indexing __cpp_pack_indexing C++26 C++03
-``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
-Variadic Friends __cpp_variadic_friend C++26 C++03
--------------------------------------------- -------------------------------- ------------- -------------
-Designated initializers (N494) C99 C89
-Array & element qualification (N2607) C23 C89
-Attributes (N2335) C23 C89
-``#embed`` (N3017) C23 C89, C++
-============================================ ================================ ============= =============
+============================================= ================================ ============= =============
+Feature Feature Test Macro Introduced In Backported To
+============================================= ================================ ============= =============
+variadic templates __cpp_variadic_templates C++11 C++03
+Alias templates __cpp_alias_templates C++11 C++03
+Non-static data member initializers __cpp_nsdmi C++11 C++03
+Range-based ``for`` loop __cpp_range_based_for C++11 C++03
+RValue references __cpp_rvalue_references C++11 C++03
+Attributes __cpp_attributes C++11 C++03
+Lambdas __cpp_lambdas C++11 C++03
+Generalized lambda captures __cpp_init_captures C++14 C++03
+Generic lambda expressions __cpp_generic_lambdas C++14 C++03
+variable templates __cpp_variable_templates C++14 C++03
+Binary literals __cpp_binary_literals C++14 C++03
+Relaxed constexpr __cpp_constexpr C++14 C++11
+Static assert with no message __cpp_static_assert >= 201411L C++17 C++11
+Pack expansion in generalized lambda-capture __cpp_init_captures C++17 C++03
+``if constexpr`` __cpp_if_constexpr C++17 C++11
+fold expressions __cpp_fold_expressions C++17 C++03
+Lambda capture of \*this by value __cpp_capture_star_this C++17 C++03
+Attributes on enums __cpp_enumerator_attributes C++17 C++03
+Guaranteed copy elision __cpp_guaranteed_copy_elision C++17 C++03
+Hexadecimal floating literals __cpp_hex_float C++17 C++03
+``inline`` variables __cpp_inline_variables C++17 C++03
+Attributes on namespaces __cpp_namespace_attributes C++17 C++11
+Structured bindings __cpp_structured_bindings C++17 C++03
+template template arguments __cpp_template_template_args C++17 C++03
+Familiar template syntax for generic lambdas __cpp_generic_lambdas C++20 C++03
+``static operator[]`` __cpp_multidimensional_subscript C++20 C++03
+Designated initializers __cpp_designated_initializers C++20 C++03
+Conditional ``explicit`` __cpp_conditional_explicit C++20 C++03
+``using enum`` __cpp_using_enum C++20 C++03
+``if consteval`` __cpp_if_consteval C++23 C++20
+``static operator()`` __cpp_static_call_operator C++23 C++03
+Attributes on Lambda-Expressions C++23 C++11
+Attributes on Structured Bindings __cpp_structured_bindings C++26 C++03
+Packs in Structured Bindings __cpp_structured_bindings C++26 C++03
+Structured binding declaration as a condition __cpp_structured_bindings C++26 C++98
+Static assert with user-generated message __cpp_static_assert >= 202306L C++26 C++11
+Pack Indexing __cpp_pack_indexing C++26 C++03
+``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
+Variadic Friends __cpp_variadic_friend C++26 C++03
+--------------------------------------------- -------------------------------- ------------- -------------
+Designated initializers (N494) C99 C89
+Array & element qualification (N2607) C23 C89
+Attributes (N2335) C23 C89
+``#embed`` (N3017) C23 C89, C++
+============================================= ================================ ============= =============
Builtin type aliases
====================
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7d2eea9a7e25f..9e68e23c15580 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -79,6 +79,8 @@ C++2c Feature Support
- Implemented `P1061R10 Structured Bindings can introduce a Pack <https://wg21.link/P1061R10>`_.
+- Implemented `P0963R3 Structured binding declaration as a condition <https://wg21.link/P0963R3>`_.
+
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b73ac2933b1ca..8e6e6e892cdd7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -529,8 +529,12 @@ def warn_cxx14_compat_decomp_decl : Warning<
def ext_decomp_decl : ExtWarn<
"decomposition declarations are a C++17 extension">, InGroup<CXX17>;
def ext_decomp_decl_cond : ExtWarn<
- "ISO C++17 does not permit structured binding declaration in a condition">,
- InGroup<DiagGroup<"binding-in-condition">>;
+ "structured binding declaration in a condition is a C++2c extenstion">,
+ InGroup<CXX26>;
+def warn_cxx26_decomp_decl_cond : Warning<
+ "structured binding declaration in a condition is incompatible with "
+ "C++ standards before C++2c">,
+ InGroup<CXXPre26Compat>, DefaultIgnore;
def err_decomp_decl_spec : Error<
"decomposition declaration cannot be declared "
"%plural{1:'%1'|:with '%1' specifiers}0">;
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 13b8a3b47add6..b9f88230007b5 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5093,7 +5093,7 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
case Stmt::CompoundStmtClass:
return visitCompoundStmt(cast<CompoundStmt>(S));
case Stmt::DeclStmtClass:
- return visitDeclStmt(cast<DeclStmt>(S));
+ return visitDeclStmt(cast<DeclStmt>(S), /*EvaluateConditionDecl=*/true);
case Stmt::ReturnStmtClass:
return visitReturnStmt(cast<ReturnStmt>(S));
case Stmt::IfStmtClass:
@@ -5147,7 +5147,18 @@ bool Compiler<Emitter>::visitCompoundStmt(const CompoundStmt *S) {
}
template <class Emitter>
-bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
+bool Compiler<Emitter>::maybeEmitDeferredVarInit(const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ for (auto *BD : DD->bindings())
+ if (auto *KD = BD->getHoldingVar(); KD && !this->visitVarDecl(KD))
+ return false;
+ }
+ return true;
+}
+
+template <class Emitter>
+bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
+ bool EvaluateConditionDecl) {
for (const auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, BaseUsingDecl,
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
@@ -5160,13 +5171,8 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS) {
return false;
// Register decomposition decl holding vars.
- if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
- for (auto *BD : DD->bindings())
- if (auto *KD = BD->getHoldingVar()) {
- if (!this->visitVarDecl(KD))
- return false;
- }
- }
+ if (EvaluateConditionDecl && !this->maybeEmitDeferredVarInit(VD))
+ return false;
}
return true;
@@ -5249,6 +5255,9 @@ template <class Emitter> bool Compiler<Emitter>::visitIfStmt(const IfStmt *IS) {
return false;
}
+ if (!this->maybeEmitDeferredVarInit(IS->getConditionVariable()))
+ return false;
+
if (const Stmt *Else = IS->getElse()) {
LabelTy LabelElse = this->getLabel();
LabelTy LabelEnd = this->getLabel();
@@ -5294,6 +5303,10 @@ bool Compiler<Emitter>::visitWhileStmt(const WhileStmt *S) {
if (!this->visitBool(Cond))
return false;
+
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
+
if (!this->jumpFalse(EndLabel))
return false;
@@ -5375,6 +5388,9 @@ bool Compiler<Emitter>::visitForStmt(const ForStmt *S) {
return false;
}
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
+
if (Body && !this->visitStmt(Body))
return false;
@@ -5497,6 +5513,9 @@ bool Compiler<Emitter>::visitSwitchStmt(const SwitchStmt *S) {
if (!this->emitSetLocal(CondT, CondVar, S))
return false;
+ if (!this->maybeEmitDeferredVarInit(S->getConditionVariable()))
+ return false;
+
CaseMap CaseLabels;
// Create labels and comparison ops for all case statements.
for (const SwitchCase *SC = S->getSwitchCaseList(); SC;
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 7683d60f869f6..902da8ffdd330 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -213,7 +213,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
// Statements.
bool visitCompoundStmt(const CompoundStmt *S);
- bool visitDeclStmt(const DeclStmt *DS);
+ bool visitDeclStmt(const DeclStmt *DS, bool EvaluateConditionDecl = false);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
bool visitWhileStmt(const WhileStmt *S);
@@ -389,6 +389,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool compileUnionAssignmentOperator(const CXXMethodDecl *MD);
bool checkLiteralType(const Expr *E);
+ bool maybeEmitDeferredVarInit(const VarDecl *VD);
protected:
/// Variable to storage mapping.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4649cd79371f4..9ed27c38bc4ea 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5227,20 +5227,41 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
return true;
}
-static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
- bool OK = true;
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+ const DecompositionDecl *DD);
+static bool EvaluateDecl(EvalInfo &Info, const Decl *D,
+ bool EvaluateConditionDecl = false) {
+ bool OK = true;
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
OK &= EvaluateVarDecl(Info, VD);
- if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D))
- for (auto *BD : DD->flat_bindings())
- if (auto *VD = BD->getHoldingVar())
- OK &= EvaluateDecl(Info, VD);
+ if (const DecompositionDecl *DD = dyn_cast<DecompositionDecl>(D);
+ EvaluateConditionDecl && DD)
+ OK &= EvaluateDecompositionDeclInit(Info, DD);
+
+ return OK;
+}
+
+static bool EvaluateDecompositionDeclInit(EvalInfo &Info,
+ const DecompositionDecl *DD) {
+ bool OK = true;
+ for (auto *BD : DD->flat_bindings())
+ if (auto *VD = BD->getHoldingVar())
+ OK &= EvaluateDecl(Info, VD, /*EvaluateConditionDecl=*/true);
return OK;
}
+static bool MaybeEvaluateDeferredVarDeclInit(EvalInfo &Info,
+ const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ if (!EvaluateDecompositionDeclInit(Info, DD))
+ return false;
+ }
+ return true;
+}
+
static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) {
assert(E->isValueDependent());
if (Info.noteSideEffect())
@@ -5260,6 +5281,8 @@ static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
return false;
if (!EvaluateAsBooleanCondition(Cond, Result, Info))
return false;
+ if (!MaybeEvaluateDeferredVarDeclInit(Info, CondDecl))
+ return false;
return Scope.destroy();
}
@@ -5344,6 +5367,9 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
if (!EvaluateInteger(SS->getCond(), Value, Info))
return ESR_Failed;
+ if (!MaybeEvaluateDeferredVarDeclInit(Info, SS->getConditionVariable()))
+ return ESR_Failed;
+
if (!CondScope.destroy())
return ESR_Failed;
}
@@ -5568,7 +5594,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
return ESR_Failed;
// Each declaration initialization is its own full-expression.
FullExpressionRAII Scope(Info);
- if (!EvaluateDecl(Info, D) && !Info.noteFailure())
+ if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
+ !Info.noteFailure())
return ESR_Failed;
if (!Scope.destroy())
return ESR_Failed;
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 3ad9ebf624143..eab1ebfb2369b 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -48,7 +48,7 @@ using namespace CodeGen;
static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
"Clang max alignment greater than what LLVM supports?");
-void CodeGenFunction::EmitDecl(const Decl &D) {
+void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
switch (D.getKind()) {
case Decl::BuiltinTemplate:
case Decl::TranslationUnit:
@@ -152,7 +152,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
return;
case Decl::UsingPack:
for (auto *Using : cast<UsingPackDecl>(D).expansions())
- EmitDecl(*Using);
+ EmitDecl(*Using, /*EvaluateConditionDecl=*/EvaluateConditionDecl);
return;
case Decl::UsingDirective: // using namespace X; [C++]
if (CGDebugInfo *DI = getDebugInfo())
@@ -164,10 +164,8 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
assert(VD.isLocalVarDecl() &&
"Should not see file-scope variables inside a function!");
EmitVarDecl(VD);
- if (auto *DD = dyn_cast<DecompositionDecl>(&VD))
- for (auto *B : DD->flat_bindings())
- if (auto *HD = B->getHoldingVar())
- EmitVarDecl(*HD);
+ if (EvaluateConditionDecl)
+ MaybeEmitDeferredVarDeclInit(&VD);
return;
}
@@ -2059,6 +2057,14 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
/*IsAutoInit=*/false);
}
+void CodeGenFunction::MaybeEmitDeferredVarDeclInit(const VarDecl *VD) {
+ if (auto *DD = dyn_cast_if_present<DecompositionDecl>(VD)) {
+ for (auto *B : DD->flat_bindings())
+ if (auto *HD = B->getHoldingVar())
+ EmitVarDecl(*HD);
+ }
+}
+
/// Emit an expression as an initializer for an object (variable, field, etc.)
/// at the given location. The expression is not necessarily the normal
/// initializer for the object, and the address is not necessarily
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index abe799af32c6e..99b6f563d7c82 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -913,6 +913,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
if (CondConstant)
incrementProfileCounter(&S);
if (Executed) {
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
RunCleanupsScope ExecutedScope(*this);
EmitStmt(Executed);
}
@@ -952,10 +953,13 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// there is a 'return' within the body, but this is particularly beneficial
// when one if-stmt is nested within another if-stmt so that all of the MC/DC
// updates are kept linear and consistent.
- if (!CGM.getCodeGenOpts().MCDCCoverage)
- EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH);
- else {
+ if (!CGM.getCodeGenOpts().MCDCCoverage) {
+ EmitBranchOnBoolExpr(S.getCond(), ThenBlock, ElseBlock, ThenCount, LH,
+ /*ConditionalOp=*/nullptr,
+ /*ConditionalDecl=*/S.getConditionVariable());
+ } else {
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
Builder.CreateCondBr(BoolCondVal, ThenBlock, ElseBlock);
}
@@ -1099,6 +1103,8 @@ void CodeGenFunction::EmitWhileStmt(const WhileStmt &S,
// execution of the loop body.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
+
// while(1) is common, avoid extra exit blocks. Be sure
// to correctly handle break/continue though.
llvm::ConstantInt *C = dyn_cast<llvm::ConstantInt>(BoolCondVal);
@@ -1332,6 +1338,9 @@ void CodeGenFunction::EmitForStmt(const ForStmt &S,
// C99 6.8.5p2/p4: The first substatement is executed if the expression
// compares unequal to 0. The condition must be a scalar type.
llvm::Value *BoolCondVal = EvaluateExprAsBool(S.getCond());
+
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
+
llvm::MDNode *Weights =
createProfileWeightsForLoop(S.getCond(), getProfileCount(S.getBody()));
if (!Weights && CGM.getCodeGenOpts().OptimizationLevel)
@@ -1662,7 +1671,7 @@ void CodeGenFunction::EmitDeclStmt(const DeclStmt &S) {
EmitStopPoint(&S);
for (const auto *I : S.decls())
- EmitDecl(*I);
+ EmitDecl(*I, /*EvaluateConditionDecl=*/true);
}
void CodeGenFunction::EmitBreakStmt(const BreakStmt &S) {
@@ -2227,7 +2236,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
// Emit the condition variable if needed inside the entire cleanup scope
// used by this special case for constant folded switches.
if (S.getConditionVariable())
- EmitDecl(*S.getConditionVariable());
+ EmitDecl(*S.getConditionVariable(), /*EvaluateConditionDecl=*/true);
// At this point, we are no longer "within" a switch instance, so
// we can temporarily enforce this to ensure that any embedded case
@@ -2259,6 +2268,7 @@ void CodeGenFunction::EmitSwitchStmt(const SwitchStmt &S) {
if (S.getConditionVariable())
EmitDecl(*S.getConditionVariable());
llvm::Value *CondV = EmitScalarExpr(S.getCond());
+ MaybeEmitDeferredVarDeclInit(S.getConditionVariable());
// Create basic block to hold stuff that comes after switch
// statement. We also need to create a default block now so that
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 63f0bf533fd45..447192bc7f60c 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1846,7 +1846,8 @@ void CodeGenFunction::EmitBranchToCounterBlock(
/// LHS and RHS nodes.
void CodeGenFunction::EmitBranchOnBoolExpr(
const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock,
- uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp) {
+ uint64_t TrueCount, Stmt::Likelihood LH, const Expr *ConditionalOp,
+ const VarDecl *ConditionalDecl) {
Cond = Cond->IgnoreParens();
if (const BinaryOperator *CondBOp = dyn_cast<BinaryOperator>(Cond)) {
@@ -2047,6 +2048,8 @@ void CodeGenFunction::EmitBranchOnBoolExpr(
CondV = EvaluateExprAsBool(Cond);
}
+ MaybeEmitDeferredVarDeclInit(ConditionalDecl);
+
// If not at the top of the logical operator nest, update MCDC temp with the
// boolean result of the evaluated condition.
if (!MCDCLogOpStack.empty()) {
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 018fc66b72a1e..ca00a0e8c6cf4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3377,7 +3377,7 @@ class CodeGenFunction : public CodeGenTypeCache {
/// EmitDecl - Emit a declaration.
///
/// This function can be called with a null (unreachable) insert point.
- void EmitDecl(const Decl &D);
+ void EmitDecl(const Decl &D, bool EvaluateConditionDecl = false);
/// EmitVarDecl - Emit a local variable declaration.
///
@@ -3474,6 +3474,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void emitAutoVarTypeCleanup(const AutoVarEmission &emission,
QualType::DestructionKind dtorKind);
+ void MaybeEmitDeferredVarDeclInit(const VarDecl *var);
+
/// Emits the alloca and debug information for the size expressions for each
/// dimension of an array. It registers the association of its (1-dimensional)
/// QualTypes and size expression's debug node, so that CGDebugInfo can
@@ -5180,7 +5182,8 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock,
llvm::BasicBlock *FalseBlock, uint64_t TrueCount,
Stmt::Likelihood LH = Stmt::LH_None,
- const Expr *ConditionalOp = nullptr);
+ const Expr *ConditionalOp = nullptr,
+ const VarDecl *ConditionalDecl = nullptr);
/// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is
/// nonnull, if \p LHS is marked _Nonnull.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1edbe62f1c79a..96aac7871db1e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -743,13 +743,16 @@ Sema::ActOnDecompositionDeclarator(Scope *S, Declarator &D,
return nullptr;
}
- Diag(Decomp.getLSquareLoc(),
- !getLangOpts().CPlusPlus17
- ? diag::ext_decomp_decl
- : D.getContext() == DeclaratorContext::Condition
- ? diag::ext_decomp_decl_cond
- : diag::warn_cxx14_compat_decomp_decl)
- << Decomp.getSourceRange();
+ unsigned DiagID;
+ if (!getLangOpts().CPlusPlus17)
+ DiagID = diag::ext_decomp_decl;
+ else if (D.getContext() == DeclaratorContext::Condition)
+ DiagID = getLangOpts().CPlusPlus26 ? diag::warn_cxx26_decomp_decl_cond
+ : diag::ext_decomp_decl_cond;
+ else
+ DiagID = diag::warn_cxx14_compat_decomp_decl;
+
+ Diag(Decomp.getLSquareLoc(), DiagID) << Decomp.getSourceRange();
// The semantic context is always just the current context.
DeclContext *const DC = CurContext;
diff --git a/clang/test/AST/ByteCode/if.cpp b/clang/test/AST/ByteCode/if.cpp
index c48b2b8d378c8..909e08a22a283 100644
--- a/clang/test/AST/ByteCode/if.cpp
+++ b/clang/test/AST/ByteCode/if.cpp
@@ -54,7 +54,7 @@ namespace InitDecl {
constexpr char g(char const (&x)[2]) {
return 'x';
if (auto [a, b] = x) // both-error {{an array type is not allowed here}} \
- // both-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ // both-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
}
static_assert(g("x") == 'x');
diff --git a/clang/test/CodeGen/p0963r3.cpp b/clang/test/CodeGen/p0963r3.cpp
new file mode 100644
index 0000000000000..b48b5294e093e
--- /dev/null
+++ b/clang/test/CodeGen/p0963r3.cpp
@@ -0,0 +1,212 @@
+// RUN: %clang_cc1 -std=c++2c -verify -emit-llvm -triple=x86_64-pc-linux-gnu %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -fexperimental-new-constant-interpreter -triple=x86_64-pc-linux-gnu %s
+// expected-no-diagnostics
+
+namespace std {
+
+template <typename T> struct tuple_size;
+
+template <int, typename> struct tuple_element;
+
+} // namespace std
+
+namespace Case1 {
+
+struct S {
+ int a, b;
+ bool flag = false;
+
+ constexpr explicit operator bool() {
+ flag = true;
+ return a != b;
+ }
+
+ constexpr operator int() {
+ flag = true;
+ return a * b;
+ }
+
+ constexpr bool operator==(S rhs) const {
+ return a == rhs.a && b == rhs.b;
+ }
+
+ template <int I>
+ constexpr int& get() {
+ if (!flag)
+ return a = a + b;
+ return I == 0 ? a : b;
+ }
+};
+
+} // namespace Case1
+
+template <> struct std::tuple_size<Case1::S> {
+ static const int value = 2;
+};
+
+template <int I> struct std::tuple_element<I, Case1::S> {
+ using type = int;
+};
+
+namespace Case1 {
+
+void foo() {
+ if (S s(1, 2); auto [a, b] = s) {
+ __builtin_assume(a == 1);
+ __builtin_assume(b == 2);
+ }
+// CHECK: %[[call:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call]], label {{.*}}, label {{.*}}
+
+ if (auto [a, b] = S(1, 2)) {
+ __builtin_assume(a == 1);
+ __builtin_assume(b == 2);
+ }
+// CHECK: %[[call2:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call2]], label {{.*}}, label {{.*}}
+
+ if (S s(3, 4); auto& [a, b] = s) {
+ __builtin_assume(a == 3);
+ __builtin_assume(b == 4);
+ }
+// CHECK: %[[call3:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call3]], label {{.*}}, label {{.*}}
+
+ while (auto [i, j] = S(5, 6))
+ break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call4:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call4]], label {{.*}}, label {{.*}}
+
+ S s(7, 8);
+ while (auto& [i, j] = s)
+ break;
+
+// CHECK: while.cond{{.*}}:
+// CHECK: %[[call5:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call5]], label {{.*}}, label {{.*}}
+
+ for (int k = 0; auto [i, j] = S(24, 42); ++k)
+ break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call6:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call6]], label {{.*}}, label {{.*}}
+
+ for (S s(114, 514); auto& [i, j] = s; ++i)
+ break;
+
+// CHECK: for.cond{{.*}}:
+// CHECK: %[[call7:.+]] = call {{.*}} i1 @_ZN5Case11ScvbEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: br i1 %[[call7]], label {{.*}}, label {{.*}}
+
+ switch (S s(10, 11); auto& [i, j] = s) {
+ case 10 * 11:
+ __builtin_assume(i == 10);
+ __builtin_assume(j == 11);
+ break;
+ default:
+ break;
+ }
+
+// CHECK: %[[call8:.+]] = call {{.*}} i32 @_ZN5Case11ScviEv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi0EEERiv
+// CHECK: %{{.*}} = call {{.*}} ptr @_ZN5Case11S3getILi1EEERiv
+// CHECK: switch i32 %[[call8]], label {{.*}}
+
+}
+
+constexpr int bar(auto) {
+ constexpr auto value = [] {
+ if (S s(1, 2); auto [i, j] = s)
+ return S(i, j);
+ return S(0, 0);
+ }();
+ static_assert(value == S(1, 2));
+
+ // FIXME: The diagnostic message adds a trailing comma "static assertion failed due to requirement 'value == Case1::S((0, 1, ))'"
+ // static_assert(value == S(0, 1));
+
+ constexpr auto value2 = [] {
+ if (auto [a, b] = S(1, 2))
+ return S(a, b);
+ return S(0, 0);
+ }();
+ static_assert(value2 == S(1, 2));
+
+ constexpr auto value3 = [] {
+ if (auto&& [a, b] = S(3, 4))
+ return S(a, b);
+ return S(0, 0);
+ }();
+ static_assert(value3 == S(3, 4));
+
+ constexpr auto value4 = [] {
+ S s(7, 8);
+ int cnt = 0;
+ while (auto& [i, j] = s) {
+ s.flag = false;
+ ++i, ++j;
+ if (++cnt == 10)
+ break;
+ }
+ return s;
+ }();
+ static_assert(value4 == S(17, 18));
+
+ constexpr auto value5 = [] {
+ S s(3, 4);
+ for (int cnt = 0; auto& [x, y] = s; s.flag = false, ++cnt) {
+ if (cnt == 3)
+ break;
+ ++x, ++y;
+ }
+ return s;
+ }();
+ static_assert(value5 == S(6, 7));
+
+ constexpr auto value6 = [] {
+ switch (auto [x, y] = S(3, 4)) {
+ case 3 * 4:
+ return S(x, y);
+ default:
+ return S(y, x);
+ }
+ }();
+ static_assert(value6 == S(3, 4));
+
+ return 42;
+}
+
+constexpr int value = bar(1);
+
+#if 0
+
+// FIXME: This causes clang to ICE, though this is not a regression.
+constexpr int ice(auto) {
+ if constexpr (S s(1, 2); auto [i, j] = s) {
+ static_assert(i == 1);
+ }
+ return 42;
+}
+
+constexpr int value2 = ice(1);
+
+#endif
+
+} // namespace Case1
diff --git a/clang/test/Parser/cxx1z-decomposition.cpp b/clang/test/Parser/cxx1z-decomposition.cpp
index f4a4dc5375bdc..a6ca642269eee 100644
--- a/clang/test/Parser/cxx1z-decomposition.cpp
+++ b/clang/test/Parser/cxx1z-decomposition.cpp
@@ -37,12 +37,12 @@ namespace OtherDecl {
void g() {
// A condition is allowed as a Clang extension.
// See commentary in test/Parser/decomposed-condition.cpp
- for (; auto [a, b, c] = S(); ) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- if (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- if (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
- switch (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
- switch (int n; auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('S' invalid)}}
- while (auto [a, b, c] = S()) {} // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ for (; auto [a, b, c] = S(); ) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ if (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ if (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
+ switch (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+ switch (int n; auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('S' invalid)}}
+ while (auto [a, b, c] = S()) {} // pre2c-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'S' is not contextually convertible to 'bool'}}
// An exception-declaration is not a simple-declaration.
try {}
diff --git a/clang/test/Parser/decomposed-condition.cpp b/clang/test/Parser/decomposed-condition.cpp
index 4c635c4735db8..37a24f8f95093 100644
--- a/clang/test/Parser/decomposed-condition.cpp
+++ b/clang/test/Parser/decomposed-condition.cpp
@@ -33,33 +33,33 @@ Na g();
namespace CondInIf {
int h() {
- if (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ if (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- if (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ if (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- if (auto [value] = Get()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ if (auto [value] = Get()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInIf
namespace CondInWhile {
int h() {
- while (auto [ok, d] = f()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ while (auto [ok, d] = f()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- while (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ while (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- while (auto [value] = Get()) // expected-warning{{ISO C++17 does not permit structured binding declaration in a condition}}
+ while (auto [value] = Get()) // expected-warning{{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInWhile
namespace CondInFor {
int h() {
- for (; auto [ok, d] = f();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ for (; auto [ok, d] = f();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- for (; auto [ok, d] = g();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
+ for (; auto [ok, d] = g();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{value of type 'Na' is not contextually convertible to 'bool'}}
;
- for (; auto [value] = Get();) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ for (; auto [value] = Get();) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
return value;
}
} // namespace CondInFor
@@ -74,11 +74,11 @@ struct IntegerLike {
namespace CondInSwitch {
int h(IntegerLike x) {
- switch (auto [ok, d] = x) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ switch (auto [ok, d] = x) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
;
- switch (auto [ok, d] = g()) // expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
+ switch (auto [ok, d] = g()) // expected-warning {{structured binding declaration in a condition is a C++2c extenstion}} expected-error {{statement requires expression of integer type ('Na' invalid)}}
;
- switch (auto [value] = Get()) {// expected-warning {{ISO C++17 does not permit structured binding declaration in a condition}}
+ switch (auto [value] = Get()) {// expected-warning {{structured binding declaration in a condition is a C++2c extenstion}}
// expected-warning at -1{{switch condition has boolean value}}
case 1:
return value;
diff --git a/clang/test/SemaCXX/decomposed-condition.cpp b/clang/test/SemaCXX/decomposed-condition.cpp
index e55bbee3134ca..14a23af3f5658 100644
--- a/clang/test/SemaCXX/decomposed-condition.cpp
+++ b/clang/test/SemaCXX/decomposed-condition.cpp
@@ -1,5 +1,7 @@
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s
-// RUN: %clang_cc1 -std=c++1z -Wno-binding-in-condition -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s
+// RUN: %clang_cc1 -std=c++17 -Wno-c++26-extensions -verify %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s
+// RUN: %clang_cc1 -std=c++2c -Wpre-c++26-compat -verify=cxx26,expected %s -fexperimental-new-constant-interpreter
struct X {
bool flag;
@@ -14,7 +16,7 @@ struct X {
namespace CondInIf {
constexpr int f(X x) {
- if (auto [ok, d] = x)
+ if (auto [ok, d] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
return d + int(ok);
else
return d * int(ok);
@@ -26,12 +28,13 @@ static_assert(f({true, 2}) == 3);
static_assert(f({false, 2}) == 0);
constexpr char g(char const (&x)[2]) {
- if (auto &[a, b] = x)
+ if (auto &[a, b] = x) // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
return a;
else
return b;
- if (auto [a, b] = x) // expected-error {{an array type is not allowed here}}
+ if (auto [a, b] = x) // expected-error {{an array type is not allowed here}} \
+ // cxx26-warning {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
;
}
@@ -41,6 +44,7 @@ static_assert(g("x") == 'x');
namespace CondInSwitch {
constexpr int f(int n) {
switch (X s = {true, n}; auto [ok, d] = s) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
s = {};
case 0:
return int(ok);
@@ -65,6 +69,7 @@ namespace CondInWhile {
constexpr int f(int n) {
int m = 1;
while (auto [ok, d] = X{n > 1, n}) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
m *= d;
--n;
}
@@ -81,6 +86,7 @@ namespace CondInFor {
constexpr int f(int n) {
int a = 1, b = 1;
for (X x = {true, n}; auto &[ok, d] = x; --d) {
+ // cxx26-warning at -1 {{structured binding declaration in a condition is incompatible with C++ standards before C++2c}}
if (d < 2)
ok = false;
else {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 3797654952319..64b731c19c517 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -223,7 +223,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>Structured binding declaration as a condition</td>
<td><a href="https://wg21.link/P0963R3">P0963R3</a></td>
- <td class="none" align="center">No</td>
+ <td class="unreleased" align="center">Clang 21</td>
</tr>
<!--Poland, Fall 2024-->
<tr>
>From 4a4444c0b2f68bec1db8e2cc8d133982d5a339e3 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Tue, 11 Mar 2025 07:58:29 +0000
Subject: [PATCH 06/13] [CodeModel] Factor getCost out of CostModelPrinter
loop. NFC
This helps in a follow-up so that it can be called multiple times with
different cost types.
---
llvm/lib/Analysis/CostModel.cpp | 30 +++++++++++++++++-------------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Analysis/CostModel.cpp b/llvm/lib/Analysis/CostModel.cpp
index 27a946c846e67..b863bf77c6ddd 100644
--- a/llvm/lib/Analysis/CostModel.cpp
+++ b/llvm/lib/Analysis/CostModel.cpp
@@ -63,6 +63,22 @@ static cl::opt<IntrinsicCostStrategy> IntrinsicCost(
#define CM_NAME "cost-model"
#define DEBUG_TYPE CM_NAME
+static InstructionCost getCost(Instruction &Inst, TTI::TargetCostKind CostKind,
+ TargetTransformInfo &TTI,
+ TargetLibraryInfo &TLI) {
+ auto *II = dyn_cast<IntrinsicInst>(&Inst);
+ if (II && IntrinsicCost != IntrinsicCostStrategy::InstructionCost) {
+ IntrinsicCostAttributes ICA(
+ II->getIntrinsicID(), *II, InstructionCost::getInvalid(),
+ /*TypeBasedOnly=*/IntrinsicCost ==
+ IntrinsicCostStrategy::TypeBasedIntrinsicCost,
+ &TLI);
+ return TTI.getIntrinsicInstrCost(ICA, CostKind);
+ }
+
+ return TTI.getInstructionCost(&Inst, CostKind);
+}
+
PreservedAnalyses CostModelPrinterPass::run(Function &F,
FunctionAnalysisManager &AM) {
auto &TTI = AM.getResult<TargetIRAnalysis>(F);
@@ -72,19 +88,7 @@ PreservedAnalyses CostModelPrinterPass::run(Function &F,
for (Instruction &Inst : B) {
// TODO: Use a pass parameter instead of cl::opt CostKind to determine
// which cost kind to print.
- InstructionCost Cost;
- auto *II = dyn_cast<IntrinsicInst>(&Inst);
- if (II && IntrinsicCost != IntrinsicCostStrategy::InstructionCost) {
- IntrinsicCostAttributes ICA(
- II->getIntrinsicID(), *II, InstructionCost::getInvalid(),
- /*TypeBasedOnly=*/IntrinsicCost ==
- IntrinsicCostStrategy::TypeBasedIntrinsicCost,
- &TLI);
- Cost = TTI.getIntrinsicInstrCost(ICA, CostKind);
- } else {
- Cost = TTI.getInstructionCost(&Inst, CostKind);
- }
-
+ InstructionCost Cost = getCost(Inst, CostKind, TTI, TLI);
if (auto CostVal = Cost.getValue())
OS << "Cost Model: Found an estimated cost of " << *CostVal;
else
>From 8758e5fe47b5cf2d39d94ee6dc8834755c7687d9 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Tue, 11 Mar 2025 09:02:34 +0100
Subject: [PATCH 07/13] [ConstantFolding] Fix handling of index width !=
pointer width (#130608)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Per LangRef:
> The offsets are then added to the low bits of the base address up to
the index type width, with silently-wrapping two’s complement
arithmetic. If the pointer size is larger than the index size, this
means that the bits outside the index type width will not be affected.
The transform as implemented was doubly wrong, because it just truncated
the original base pointer to the index width, losing the top bits
entirely. Make sure we preserve the bits and use wrapping arithmetic
within the low bits.
---
llvm/lib/Analysis/ConstantFolding.cpp | 9 ++++++---
.../ConstProp/inttoptr-gep-index-width.ll | 14 ++++++++++++++
2 files changed, 20 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Transforms/InstSimplify/ConstProp/inttoptr-gep-index-width.ll
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index d645bf8f7b621..b0ba25c3c16ac 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -943,18 +943,21 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP,
// If the base value for this address is a literal integer value, fold the
// getelementptr to the resulting integer value casted to the pointer type.
- APInt BasePtr(BitWidth, 0);
+ APInt BasePtr(DL.getPointerTypeSizeInBits(Ptr->getType()), 0);
if (auto *CE = dyn_cast<ConstantExpr>(Ptr)) {
if (CE->getOpcode() == Instruction::IntToPtr) {
if (auto *Base = dyn_cast<ConstantInt>(CE->getOperand(0)))
- BasePtr = Base->getValue().zextOrTrunc(BitWidth);
+ BasePtr = Base->getValue().zextOrTrunc(BasePtr.getBitWidth());
}
}
auto *PTy = cast<PointerType>(Ptr->getType());
if ((Ptr->isNullValue() || BasePtr != 0) &&
!DL.isNonIntegralPointerType(PTy)) {
- Constant *C = ConstantInt::get(Ptr->getContext(), Offset + BasePtr);
+ // If the index size is smaller than the pointer size, add to the low
+ // bits only.
+ BasePtr.insertBits(BasePtr.trunc(BitWidth) + Offset, 0);
+ Constant *C = ConstantInt::get(Ptr->getContext(), BasePtr);
return ConstantExpr::getIntToPtr(C, ResTy);
}
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/inttoptr-gep-index-width.ll b/llvm/test/Transforms/InstSimplify/ConstProp/inttoptr-gep-index-width.ll
new file mode 100644
index 0000000000000..03056e8361e21
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/inttoptr-gep-index-width.ll
@@ -0,0 +1,14 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=instsimplify < %s | FileCheck %s
+
+target datalayout = "p:16:16:16:8"
+
+; The GEP should only modify the low 8 bits of the pointer.
+define ptr @test() {
+; CHECK-LABEL: define ptr @test() {
+; CHECK-NEXT: ret ptr inttoptr (i16 -256 to ptr)
+;
+ %base = inttoptr i16 -1 to ptr
+ %gep = getelementptr i8, ptr %base, i8 1
+ ret ptr %gep
+}
>From 7ecb0d03a4d78357adc35137a3109ee305ac4fff Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 11 Mar 2025 08:12:46 +0000
Subject: [PATCH 08/13] [lldb][Mangled] Use early-return style in
GetDemangledName (#130622)
This patch refactors `Mangled::GetDemangledName` to use LLVM's preferred
early-return style. I'm planning on introducing a way to force
re-demangling of names in a future patch, and this stylisitc cleanup
makes that easier to reason about.
Also performed small cleanups where I could:
* we can handle `eManglingSchemeNone` inside the switch instead of a
separate if-block
* removed some redundant explicit StringRef<->C-string conversions
---
lldb/source/Core/Mangled.cpp | 89 ++++++++++++++++++------------------
1 file changed, 45 insertions(+), 44 deletions(-)
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index 51c22495a16d7..ddaaedea04183 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -270,50 +270,51 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context,
// name. The result is cached and will be kept until a new string value is
// supplied to this object, or until the end of the object's lifetime.
ConstString Mangled::GetDemangledName() const {
- // Check to make sure we have a valid mangled name and that we haven't
- // already decoded our mangled name.
- if (m_mangled && m_demangled.IsNull()) {
- // Don't bother running anything that isn't mangled
- const char *mangled_name = m_mangled.GetCString();
- ManglingScheme mangling_scheme =
- GetManglingScheme(m_mangled.GetStringRef());
- if (mangling_scheme != eManglingSchemeNone &&
- !m_mangled.GetMangledCounterpart(m_demangled)) {
- // We didn't already mangle this name, demangle it and if all goes well
- // add it to our map.
- char *demangled_name = nullptr;
- switch (mangling_scheme) {
- case eManglingSchemeMSVC:
- demangled_name = GetMSVCDemangledStr(mangled_name);
- break;
- case eManglingSchemeItanium: {
- demangled_name = GetItaniumDemangledStr(mangled_name);
- break;
- }
- case eManglingSchemeRustV0:
- demangled_name = GetRustV0DemangledStr(m_mangled);
- break;
- case eManglingSchemeD:
- demangled_name = GetDLangDemangledStr(m_mangled);
- break;
- case eManglingSchemeSwift:
- // Demangling a swift name requires the swift compiler. This is
- // explicitly unsupported on llvm.org.
- break;
- case eManglingSchemeNone:
- llvm_unreachable("eManglingSchemeNone was handled already");
- }
- if (demangled_name) {
- m_demangled.SetStringWithMangledCounterpart(
- llvm::StringRef(demangled_name), m_mangled);
- free(demangled_name);
- }
- }
- if (m_demangled.IsNull()) {
- // Set the demangled string to the empty string to indicate we tried to
- // parse it once and failed.
- m_demangled.SetCString("");
- }
+ if (!m_mangled)
+ return m_demangled;
+
+ // Re-use previously demangled names.
+ if (!m_demangled.IsNull())
+ return m_demangled;
+
+ if (m_mangled.GetMangledCounterpart(m_demangled) && !m_demangled.IsNull())
+ return m_demangled;
+
+ // We didn't already mangle this name, demangle it and if all goes well
+ // add it to our map.
+ char *demangled_name = nullptr;
+ switch (GetManglingScheme(m_mangled.GetStringRef())) {
+ case eManglingSchemeMSVC:
+ demangled_name = GetMSVCDemangledStr(m_mangled);
+ break;
+ case eManglingSchemeItanium: {
+ demangled_name = GetItaniumDemangledStr(m_mangled.GetCString());
+ break;
+ }
+ case eManglingSchemeRustV0:
+ demangled_name = GetRustV0DemangledStr(m_mangled);
+ break;
+ case eManglingSchemeD:
+ demangled_name = GetDLangDemangledStr(m_mangled);
+ break;
+ case eManglingSchemeSwift:
+ // Demangling a swift name requires the swift compiler. This is
+ // explicitly unsupported on llvm.org.
+ break;
+ case eManglingSchemeNone:
+ // Don't bother demangling anything that isn't mangled.
+ break;
+ }
+
+ if (demangled_name) {
+ m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled);
+ free(demangled_name);
+ }
+
+ if (m_demangled.IsNull()) {
+ // Set the demangled string to the empty string to indicate we tried to
+ // parse it once and failed.
+ m_demangled.SetCString("");
}
return m_demangled;
>From cdd560eead457bcc6dbb28ef88d868bc68cfd7e6 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 11 Mar 2025 08:13:05 +0000
Subject: [PATCH 09/13] [lldb][XcodeSDK] Simplify logic that adjusts sysroot
during XcodeSDK merging (#130640)
The `DW_AT_APPLE_sdk` should always be equal to the filename of the
`DW_AT_LLVM_sysroot`. We can use this property to simplify
`XcodeSDK::Merge` to no longer manually adjust the sysroot filename.
Instead we simply update the sysroot filename with merged SDK name.
This should be an NFC change.
---
lldb/include/lldb/Utility/XcodeSDK.h | 4 +++-
lldb/source/Utility/XcodeSDK.cpp | 14 ++++++--------
lldb/unittests/Utility/XcodeSDKTest.cpp | 11 ++++++-----
3 files changed, 15 insertions(+), 14 deletions(-)
diff --git a/lldb/include/lldb/Utility/XcodeSDK.h b/lldb/include/lldb/Utility/XcodeSDK.h
index 3a6cbb9c98f6b..ceb8abb8c502d 100644
--- a/lldb/include/lldb/Utility/XcodeSDK.h
+++ b/lldb/include/lldb/Utility/XcodeSDK.h
@@ -65,7 +65,9 @@ class XcodeSDK {
/// parameter. For example, "MacOSX.10.14.sdk".
XcodeSDK(std::string &&name) : m_name(std::move(name)) {}
XcodeSDK(std::string name, FileSpec sysroot)
- : m_name(std::move(name)), m_sysroot(std::move(sysroot)) {}
+ : m_name(std::move(name)), m_sysroot(std::move(sysroot)) {
+ assert(!m_sysroot || m_name == m_sysroot.GetFilename().GetStringRef());
+ }
static XcodeSDK GetAnyMacOS() { return XcodeSDK("MacOSX.sdk"); }
/// The merge function follows a strict order to maintain monotonicity:
diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp
index 02cf7866e22fb..004b4717e315b 100644
--- a/lldb/source/Utility/XcodeSDK.cpp
+++ b/lldb/source/Utility/XcodeSDK.cpp
@@ -155,10 +155,6 @@ bool XcodeSDK::Info::operator==(const Info &other) const {
}
void XcodeSDK::Merge(const XcodeSDK &other) {
- auto add_internal_sdk_suffix = [](llvm::StringRef sdk) {
- return (sdk.substr(0, sdk.size() - 3) + "Internal.sdk").str();
- };
-
// The "bigger" SDK always wins.
auto l = Parse();
auto r = other.Parse();
@@ -168,12 +164,14 @@ void XcodeSDK::Merge(const XcodeSDK &other) {
// The Internal flag always wins.
if (!l.internal && r.internal) {
if (llvm::StringRef(m_name).ends_with(".sdk"))
- m_name = add_internal_sdk_suffix(m_name);
-
- if (m_sysroot.GetFileNameExtension() == ".sdk")
- m_sysroot.SetFilename(add_internal_sdk_suffix(m_sysroot.GetFilename()));
+ m_name =
+ m_name.substr(0, m_name.size() - 3) + std::string("Internal.sdk");
}
}
+
+ // We changed the SDK name. Adjust the sysroot accordingly.
+ if (m_sysroot && m_sysroot.GetFilename().GetStringRef() != m_name)
+ m_sysroot.SetFilename(m_name);
}
std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
diff --git a/lldb/unittests/Utility/XcodeSDKTest.cpp b/lldb/unittests/Utility/XcodeSDKTest.cpp
index bca7f1291c425..4db6a50fcf860 100644
--- a/lldb/unittests/Utility/XcodeSDKTest.cpp
+++ b/lldb/unittests/Utility/XcodeSDKTest.cpp
@@ -66,13 +66,14 @@ TEST(XcodeSDKTest, MergeTest) {
empty.Merge(XcodeSDK("MacOSX10.14.Internal.sdk"));
EXPECT_EQ(empty.GetString(), llvm::StringRef("MacOSX10.14.Internal.sdk"));
EXPECT_FALSE(empty.GetSysroot());
- empty.Merge(XcodeSDK("MacOSX9.5.Internal.sdk", FileSpec{"/Path/To/9.5.sdk"}));
+ empty.Merge(XcodeSDK("MacOSX9.5.Internal.sdk",
+ FileSpec{"/Path/To/MacOSX9.5.Internal.sdk"}));
EXPECT_FALSE(empty.GetSysroot());
- empty.Merge(XcodeSDK("MacOSX12.5.sdk", FileSpec{"/Path/To/12.5.sdk"}));
- EXPECT_EQ(empty.GetSysroot(), FileSpec{"/Path/To/12.5.sdk"});
+ empty.Merge(XcodeSDK("MacOSX12.5.sdk", FileSpec{"/Path/To/MacOSX12.5.sdk"}));
+ EXPECT_EQ(empty.GetSysroot(), FileSpec{"/Path/To/MacOSX12.5.sdk"});
empty.Merge(XcodeSDK("MacOSX11.5.Internal.sdk",
- FileSpec{"/Path/To/12.5.Internal.sdk"}));
- EXPECT_EQ(empty.GetSysroot(), FileSpec{"/Path/To/12.5.Internal.sdk"});
+ FileSpec{"/Path/To/MacOSX11.5.Internal.sdk"}));
+ EXPECT_EQ(empty.GetSysroot(), FileSpec{"/Path/To/MacOSX12.5.Internal.sdk"});
}
#ifndef _WIN32
>From cb7298f66d62a3548fcf3bd230304067ecf30d17 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Tue, 11 Mar 2025 16:24:00 +0800
Subject: [PATCH 10/13] [AstMatcher][NFC]fix doc gen for ast matchers (#130726)
1. dump-ast-matchers.py does not depend on pwd
2. fix some warning in python3
---
clang/docs/LibASTMatchersReference.html | 193 ++++++++++++------
clang/docs/tools/dump_ast_matchers.py | 6 +-
clang/include/clang/ASTMatchers/ASTMatchers.h | 3 +-
3 files changed, 139 insertions(+), 63 deletions(-)
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 48dfd9cac0033..2a01d6cda6bed 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1842,12 +1842,6 @@ <h2 id="decl-matchers">Node Matchers</h2>
if (x) {}
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('dependentScopeDeclRefExpr0')"><a name="dependentScopeDeclRefExpr0Anchor">dependentScopeDeclRefExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentScopeDeclRefExpr.html">DependentScopeDeclRefExpr</a>>...</td></tr>
-<tr><td colspan="4" class="doc" id="dependentScopeDeclRefExpr0"><pre>Matches expressions that refer to dependent scope declarations.
-
-Example matches T::v
- template <class T> class X : T { void f() { T::v; } };
-</pre></td></tr>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('declStmt0')"><a name="declStmt0Anchor">declStmt</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DeclStmt.html">DeclStmt</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="declStmt0"><pre>Matches declaration statements.
@@ -1874,6 +1868,14 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('dependentScopeDeclRefExpr0')"><a name="dependentScopeDeclRefExpr0Anchor">dependentScopeDeclRefExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentScopeDeclRefExpr.html">DependentScopeDeclRefExpr</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="dependentScopeDeclRefExpr0"><pre>Matches expressions that refer to dependent scope declarations.
+
+example matches T::v;
+ template <class T> class X : T { void f() { T::v; } };
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('designatedInitExpr0')"><a name="designatedInitExpr0Anchor">designatedInitExpr</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DesignatedInitExpr.html">DesignatedInitExpr</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="designatedInitExpr0"><pre>Matches C99 designated initializer expressions [C99 6.7.8].
@@ -1931,7 +1933,24 @@ <h2 id="decl-matchers">Node Matchers</h2>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('fixedPointLiteral0')"><a name="fixedPointLiteral0Anchor">fixedPointLiteral</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FixedPointLiteral.html">FixedPointLiteral</a>>...</td></tr>
-<tr><td colspan="4" class="doc" id="fixedPointLiteral0"><pre>Matches fixed point literals
+<tr><td colspan="4" class="doc" id="fixedPointLiteral0"><pre>Matches fixed-point literals eg.
+0.5r, 0.5hr, 0.5lr, 0.5uhr, 0.5ur, 0.5ulr
+1.0k, 1.0hk, 1.0lk, 1.0uhk, 1.0uk, 1.0ulk
+Exponents 1.0e10k
+Hexadecimal numbers 0x0.2p2r
+
+Does not match implicit conversions such as first two lines:
+ short _Accum sa = 2;
+ _Accum a = 12.5;
+ _Accum b = 1.25hk;
+ _Fract c = 0.25hr;
+ _Fract v = 0.35uhr;
+ _Accum g = 1.45uhk;
+ _Accum decexp1 = 1.575e1k;
+
+The matcher matches
+but does not
+match and from the code block.
</pre></td></tr>
@@ -2536,26 +2555,6 @@ <h2 id="decl-matchers">Node Matchers</h2>
matches "decltype(i + j)"
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('dependentNameType0')"><a name="dependentNameType0Anchor">dependentNameType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentNameType.html">DependentNameType</a>>...</td></tr>
-<tr><td colspan="4" class="doc" id="dependentNameType0"><pre>Matches a dependent name type.
-
-Example matches T::type
-
- template <typename T> struct declToImport {
- typedef typename T::type dependent_name;
- };
-</pre></td></tr>
-
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('dependentTemplateSpecializationType0')"><a name="dependentTemplateSpecializationType0Anchor">dependentTemplateSpecializationType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentTemplateSpecializationType.html">DependentTemplateSpecializationType</a>>...</td></tr>
-<tr><td colspan="4" class="doc" id="dependentTemplateSpecializationType0"><pre>Matches a dependent template specialization type.
-
-Example matches A<T>::template B<T>
-
- template<typename T> struct A;
- template<typename T> struct declToImport {
- typename A<T>::template B<T> a;
- };
-</pre></td></tr>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('deducedTemplateSpecializationType0')"><a name="deducedTemplateSpecializationType0Anchor">deducedTemplateSpecializationType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DeducedTemplateSpecializationType.html">DeducedTemplateSpecializationType</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="deducedTemplateSpecializationType0"><pre>Matches C++17 deduced template specialization types, e.g. deduced class
@@ -2571,6 +2570,16 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('dependentNameType0')"><a name="dependentNameType0Anchor">dependentNameType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentNameType.html">DependentNameType</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="dependentNameType0"><pre>Matches a dependent name type
+
+Example matches T::type
+ template <typename T> struct declToImport {
+ typedef typename T::type dependent_name;
+ };
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('dependentSizedArrayType0')"><a name="dependentSizedArrayType0Anchor">dependentSizedArrayType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentSizedArrayType.html">DependentSizedArrayType</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="dependentSizedArrayType0"><pre>Matches C++ arrays whose size is a value-dependent expression.
@@ -2598,6 +2607,17 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('dependentTemplateSpecializationType0')"><a name="dependentTemplateSpecializationType0Anchor">dependentTemplateSpecializationType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentTemplateSpecializationType.html">DependentTemplateSpecializationType</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="dependentTemplateSpecializationType0"><pre>Matches a dependent template specialization type
+
+Example matches A<T>::template B<T>
+ template<typename T> struct A;
+ template<typename T> struct declToImport {
+ typename A<T>::template B<T> a;
+ };
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('elaboratedType0')"><a name="elaboratedType0Anchor">elaboratedType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ElaboratedType.html">ElaboratedType</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="elaboratedType0"><pre>Matches types specified with an elaborated type keyword or with a
qualified name.
@@ -3449,34 +3469,6 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentScopeDeclRefExpr.html">DependentScopeDeclRefExpr</a>></td><td class="name" onclick="toggle('hasDependentName0')"><a name="hasDependentName0Anchor">hasDependentName</a></td><td>std::string N</td></tr>
-<tr><td colspan="4" class="doc" id="hasDependentName0"><pre>Matches the dependent name of a DependentScopeDeclRefExpr.
-
-Matches the dependent name of a DependentScopeDeclRefExpr
-
-Given:
-
- template <class T< class X : T { void f() { T::v; } };
-
-dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
-</pre></td></tr>
-
-
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentNameType.html">DependentNameType</a>></td><td class="name" onclick="toggle('hasDependentName1')"><a name="hasDependentName1Anchor">hasDependentName</a></td><td>std::string N</td></tr>
-<tr><td colspan="4" class="doc" id="hasDependentName1"><pre>Matches the dependent name of a DependentNameType.
-
-Matches the dependent name of a DependentNameType
-
-Given:
-
- template <typename T< struct declToImport {
- typedef typename T::type dependent_name;
- };
-
-dependentNameType(hasDependentName("type")) matches `T::type`
-</pre></td></tr>
-
-
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>></td><td class="name" onclick="toggle('memberHasSameNameAsBoundNode0')"><a name="memberHasSameNameAsBoundNode0Anchor">memberHasSameNameAsBoundNode</a></td><td>std::string BindingID</td></tr>
<tr><td colspan="4" class="doc" id="memberHasSameNameAsBoundNode0"><pre>Matches template-dependent, but known, member names against an already-bound
node
@@ -4373,6 +4365,38 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentNameType.html">DependentNameType</a>></td><td class="name" onclick="toggle('hasDependentName1')"><a name="hasDependentName1Anchor">hasDependentName</a></td><td>std::string N</td></tr>
+<tr><td colspan="4" class="doc" id="hasDependentName1"><pre>Matches the dependent name of a DependentScopeDeclRefExpr or
+DependentNameType
+
+Given:
+ template <class T> class X : T { void f() { T::v; } };
+dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
+
+Given:
+ template <typename T> struct declToImport {
+ typedef typename T::type dependent_name;
+ };
+dependentNameType(hasDependentName("type")) matches `T::type`
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DependentScopeDeclRefExpr.html">DependentScopeDeclRefExpr</a>></td><td class="name" onclick="toggle('hasDependentName0')"><a name="hasDependentName0Anchor">hasDependentName</a></td><td>std::string N</td></tr>
+<tr><td colspan="4" class="doc" id="hasDependentName0"><pre>Matches the dependent name of a DependentScopeDeclRefExpr or
+DependentNameType
+
+Given:
+ template <class T> class X : T { void f() { T::v; } };
+dependentScopeDeclRefExpr(hasDependentName("v")) matches `T::v`
+
+Given:
+ template <typename T> struct declToImport {
+ typedef typename T::type dependent_name;
+ };
+dependentNameType(hasDependentName("type")) matches `T::type`
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DesignatedInitExpr.html">DesignatedInitExpr</a>></td><td class="name" onclick="toggle('designatorCountIs0')"><a name="designatorCountIs0Anchor">designatorCountIs</a></td><td>unsigned N</td></tr>
<tr><td colspan="4" class="doc" id="designatorCountIs0"><pre>Matches designated initializer expressions that contain
a specific number of designators.
@@ -6799,7 +6823,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
matches "int const *b"
Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BlockPointerType.html">BlockPointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1MemberPointerType.html">MemberPointerType</a>>,
- Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>>
</pre></td></tr>
@@ -9300,7 +9325,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
matches "int const *b"
Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BlockPointerType.html">BlockPointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1MemberPointerType.html">MemberPointerType</a>>,
- Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>>
</pre></td></tr>
@@ -9414,6 +9440,36 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>></td><td class="name" onclick="toggle('hasType9')"><a name="hasType9Anchor">hasType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasType9"><pre>Overloaded to match the declaration of the expression's or value
+declaration's type.
+
+In case of a value declaration (for example a variable declaration),
+this resolves one layer of indirection. For example, in the value
+declaration "X x;", cxxRecordDecl(hasName("X")) matches the declaration of
+X, while varDecl(hasType(cxxRecordDecl(hasName("X")))) matches the
+declaration of x.
+
+Example matches x (matcher = expr(hasType(cxxRecordDecl(hasName("X")))))
+ and z (matcher = varDecl(hasType(cxxRecordDecl(hasName("X")))))
+ and friend class X (matcher = friendDecl(hasType("X"))
+ and public virtual X (matcher = cxxBaseSpecifier(hasType(
+ cxxRecordDecl(hasName("X"))))
+ class X {};
+ void y(X &x) { x; X z; }
+ class Y { friend class X; };
+ class Z : public virtual X {};
+
+Example matches class Derived
+(matcher = cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))
+class Base {};
+class Derived : Base {};
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1FriendDecl.html">FriendDecl</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html">ValueDecl</a>>,
+Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>>
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>></td><td class="name" onclick="toggle('isDerivedFrom1')"><a name="isDerivedFrom1Anchor">isDerivedFrom</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>> Base</td></tr>
<tr><td colspan="4" class="doc" id="isDerivedFrom1"><pre>Matches C++ classes that are directly or indirectly derived from a class
matching Base, or Objective-C classes that directly or indirectly
@@ -9592,6 +9648,23 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>></td><td class="name" onclick="toggle('pointee4')"><a name="pointee4Anchor">pointee</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td></tr>
+<tr><td colspan="4" class="doc" id="pointee4"><pre>Narrows PointerType (and similar) matchers to those where the
+pointee matches a given matcher.
+
+Given
+ int *a;
+ int const *b;
+ float const *f;
+pointerType(pointee(isConstQualified(), isInteger()))
+ matches "int const *b"
+
+Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BlockPointerType.html">BlockPointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1MemberPointerType.html">MemberPointerType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>>
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCPropertyDecl.html">ObjCPropertyDecl</a>></td><td class="name" onclick="toggle('hasTypeLoc10')"><a name="hasTypeLoc10Anchor">hasTypeLoc</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1TypeLoc.html">TypeLoc</a>> Inner</td></tr>
<tr><td colspan="4" class="doc" id="hasTypeLoc10"><pre>Matches if the type location of a node matches the inner matcher.
@@ -9689,7 +9762,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
matches "int const *b"
Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BlockPointerType.html">BlockPointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1MemberPointerType.html">MemberPointerType</a>>,
- Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>>
</pre></td></tr>
@@ -9858,7 +9932,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
matches "int const *b"
Usable as: Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BlockPointerType.html">BlockPointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1MemberPointerType.html">MemberPointerType</a>>,
- Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1PointerType.html">PointerType</a>>, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ReferenceType.html">ReferenceType</a>>,
+ Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCObjectPointerType.html">ObjCObjectPointerType</a>>
</pre></td></tr>
diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py
index b6f00657ec914..46b7bb718ba08 100755
--- a/clang/docs/tools/dump_ast_matchers.py
+++ b/clang/docs/tools/dump_ast_matchers.py
@@ -106,7 +106,7 @@ def extract_result_types(comment):
def strip_doxygen(comment):
- """Returns the given comment without \-escaped words."""
+ """Returns the given comment without -escaped words."""
# If there is only a doxygen keyword in the line, delete the whole line.
comment = re.sub(r"^\\[^\s]+\n", r"", comment, flags=re.M)
@@ -241,7 +241,7 @@ def act_on_decl(declaration, comment, allowed_types):
# Parse the various matcher definition macros.
m = re.match(
- """.*AST_TYPE(LOC)?_TRAVERSE_MATCHER(?:_DECL)?\(
+ r""".*AST_TYPE(LOC)?_TRAVERSE_MATCHER(?:_DECL)?\(
\s*([^\s,]+\s*),
\s*(?:[^\s,]+\s*),
\s*AST_POLYMORPHIC_SUPPORTED_TYPES\(([^)]*)\)
@@ -615,5 +615,5 @@ def sort_table(matcher_type, matcher_map):
flags=re.S,
)
-with open("../LibASTMatchersReference.html", "w", newline="\n") as output:
+with open(HTML_FILE, "w", newline="\n") as output:
output.write(reference)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 0f7e3a8a01762..5c12bf0f277a0 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -7494,7 +7494,8 @@ extern const AstTypeMatcher<RValueReferenceType> rValueReferenceType;
/// matches "int const *b"
///
/// Usable as: Matcher<BlockPointerType>, Matcher<MemberPointerType>,
-/// Matcher<PointerType>, Matcher<ReferenceType>
+/// Matcher<PointerType>, Matcher<ReferenceType>,
+/// Matcher<ObjCObjectPointerType>
AST_TYPELOC_TRAVERSE_MATCHER_DECL(
pointee, getPointee,
AST_POLYMORPHIC_SUPPORTED_TYPES(BlockPointerType, MemberPointerType,
>From 1ddf18057a5aa1ee7010ec262ccfc80c39b99bf6 Mon Sep 17 00:00:00 2001
From: jeanPerier <jperier at nvidia.com>
Date: Tue, 11 Mar 2025 09:31:03 +0100
Subject: [PATCH 11/13] [flang] introduce fir.copy to avoid load store of
aggregates (#130289)
Introduce a FIR operation to do memcopy/memmove of compile time constant size types.
This is to avoid requiring derived type copies to done with load/store
which is badly supported in LLVM when the aggregate type is "big" (no
threshold can easily be defined here, better to always avoid them for
fir.type).
This was the root cause of the regressions caused by #114002 which introduced a
load/store of fir.type<> which caused hand/asserts to fire in LLVM on
several benchmarks.
See https://llvm.org/docs/Frontend/PerformanceTips.html#avoid-creating-values-of-aggregate-type
---
.../include/flang/Optimizer/Dialect/FIROps.td | 44 +++++++++++++++++++
.../include/flang/Optimizer/Dialect/FIRType.h | 7 +++
flang/lib/Optimizer/CodeGen/CodeGen.cpp | 40 ++++++++++++++---
flang/lib/Optimizer/Dialect/FIROps.cpp | 20 +++++++++
flang/test/Fir/copy-codegen.fir | 35 +++++++++++++++
flang/test/Fir/fir-ops.fir | 9 ++++
flang/test/Fir/invalid.fir | 37 ++++++++++++++++
7 files changed, 187 insertions(+), 5 deletions(-)
create mode 100644 flang/test/Fir/copy-codegen.fir
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index c83c57186b46d..8325468c4b210 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -68,6 +68,12 @@ def IsBoxAddressOrValueTypePred
def fir_BoxAddressOrValueType : Type<IsBoxAddressOrValueTypePred,
"fir.box or fir.class type or reference">;
+def RefOfConstantSizeAggregateTypePred
+ : CPred<"::fir::isRefOfConstantSizeAggregateType($_self)">;
+def AnyRefOfConstantSizeAggregateType : TypeConstraint<
+ RefOfConstantSizeAggregateTypePred,
+ "a reference type to a constant size fir.array, fir.char, or fir.type">;
+
//===----------------------------------------------------------------------===//
// Memory SSA operations
//===----------------------------------------------------------------------===//
@@ -342,6 +348,44 @@ def fir_StoreOp : fir_Op<"store", [FirAliasTagOpInterface]> {
}];
}
+def fir_CopyOp : fir_Op<"copy", []> {
+ let summary = "copy constant size memory";
+
+ let description = [{
+ Copy the memory from a source with compile time constant size to
+ a destination of the same type.
+
+ This is meant to be used for aggregate types where load and store
+ are not appropriate to make a copy because LLVM is not meant to
+ handle load and store of "big" aggregates.
+
+ Its "no_overlap" attribute allows indicating that the source and destination
+ are known to not overlap at compile time.
+
+ ```
+ !t =!fir.type<t{x:!fir.array<1000xi32>}>
+ fir.copy %x to %y : !fir.ref<!t>, !fir.ref<!t>
+ ```
+ TODO: add FirAliasTagOpInterface to carry TBAA.
+ }];
+
+ let arguments = (ins Arg<AnyRefOfConstantSizeAggregateType, "", [MemRead]>:$source,
+ Arg<AnyRefOfConstantSizeAggregateType, "", [MemWrite]>:$destination,
+ OptionalAttr<UnitAttr>:$no_overlap);
+
+ let builders = [OpBuilder<(ins "mlir::Value":$source,
+ "mlir::Value":$destination,
+ CArg<"bool", "false">:$no_overlap)>];
+
+ let assemblyFormat = [{
+ $source `to` $destination (`no_overlap` $no_overlap^)?
+ attr-dict `:` type(operands)
+ }];
+
+ let hasVerifier = 1;
+}
+
+
def fir_SaveResultOp : fir_Op<"save_result", [AttrSizedOperandSegments]> {
let summary = [{
save an array, box, or record function result SSA-value to a memory location
diff --git a/flang/include/flang/Optimizer/Dialect/FIRType.h b/flang/include/flang/Optimizer/Dialect/FIRType.h
index 3d30f4e673682..76e0aa352bcd9 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRType.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRType.h
@@ -498,6 +498,13 @@ inline bool isBoxProcAddressType(mlir::Type t) {
return t && mlir::isa<fir::BoxProcType>(t);
}
+inline bool isRefOfConstantSizeAggregateType(mlir::Type t) {
+ t = fir::dyn_cast_ptrEleTy(t);
+ return t &&
+ mlir::isa<fir::CharacterType, fir::RecordType, fir::SequenceType>(t) &&
+ !hasDynamicSize(t);
+}
+
/// Return a string representation of `ty`.
///
/// fir.array<10x10xf32> -> prefix_10x10xf32
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index b5b2f393f6ca0..5548f5f981b12 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -3545,6 +3545,36 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
}
};
+/// `fir.copy` --> `llvm.memcpy` or `llvm.memmove`
+struct CopyOpConversion : public fir::FIROpConversion<fir::CopyOp> {
+ using FIROpConversion::FIROpConversion;
+
+ llvm::LogicalResult
+ matchAndRewrite(fir::CopyOp copy, OpAdaptor adaptor,
+ mlir::ConversionPatternRewriter &rewriter) const override {
+ mlir::Location loc = copy.getLoc();
+ mlir::Value llvmSource = adaptor.getSource();
+ mlir::Value llvmDestination = adaptor.getDestination();
+ mlir::Type i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
+ mlir::Type copyTy = fir::unwrapRefType(copy.getSource().getType());
+ mlir::Value copySize =
+ genTypeStrideInBytes(loc, i64Ty, rewriter, convertType(copyTy));
+
+ mlir::LLVM::AliasAnalysisOpInterface newOp;
+ if (copy.getNoOverlap())
+ newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
+ loc, llvmDestination, llvmSource, copySize, /*isVolatile=*/false);
+ else
+ newOp = rewriter.create<mlir::LLVM::MemmoveOp>(
+ loc, llvmDestination, llvmSource, copySize, /*isVolatile=*/false);
+
+ // TODO: propagate TBAA once FirAliasTagOpInterface added to CopyOp.
+ attachTBAATag(newOp, copyTy, copyTy, nullptr);
+ rewriter.eraseOp(copy);
+ return mlir::success();
+ }
+};
+
namespace {
/// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
@@ -4148,11 +4178,11 @@ void fir::populateFIRToLLVMConversionPatterns(
BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
- DTEntryOpConversion, DeclareOpConversion, DivcOpConversion,
- EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
- ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
- FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
- InsertOnRangeOpConversion, IsPresentOpConversion,
+ CopyOpConversion, DTEntryOpConversion, DeclareOpConversion,
+ DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
+ EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
+ FirEndOpConversion, FreeMemOpConversion, GlobalLenOpConversion,
+ GlobalOpConversion, InsertOnRangeOpConversion, IsPresentOpConversion,
LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 7efb733eb565c..203a72af61b92 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -3940,6 +3940,26 @@ void fir::StoreOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
build(builder, result, value, memref, {});
}
+//===----------------------------------------------------------------------===//
+// CopyOp
+//===----------------------------------------------------------------------===//
+
+void fir::CopyOp::build(mlir::OpBuilder &builder, mlir::OperationState &result,
+ mlir::Value source, mlir::Value destination,
+ bool noOverlap) {
+ mlir::UnitAttr noOverlapAttr =
+ noOverlap ? builder.getUnitAttr() : mlir::UnitAttr{};
+ build(builder, result, source, destination, noOverlapAttr);
+}
+
+llvm::LogicalResult fir::CopyOp::verify() {
+ mlir::Type sourceType = fir::unwrapRefType(getSource().getType());
+ mlir::Type destinationType = fir::unwrapRefType(getDestination().getType());
+ if (sourceType != destinationType)
+ return emitOpError("source and destination must have the same value type");
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// StringLitOp
//===----------------------------------------------------------------------===//
diff --git a/flang/test/Fir/copy-codegen.fir b/flang/test/Fir/copy-codegen.fir
new file mode 100644
index 0000000000000..eef1885c6a49c
--- /dev/null
+++ b/flang/test/Fir/copy-codegen.fir
@@ -0,0 +1,35 @@
+// Test fir.copy codegen.
+// RUN: fir-opt --fir-to-llvm-ir %s -o - | FileCheck %s
+
+!t=!fir.type<sometype{i:!fir.array<9xi32>}>
+
+module attributes {llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"} {
+
+func.func @test_copy_1(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
+ fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t>
+ return
+}
+// CHECK-LABEL: llvm.func @test_copy_1(
+// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr,
+// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr) {
+// CHECK: %[[VAL_2:.*]] = llvm.mlir.zero : !llvm.ptr
+// CHECK: %[[VAL_3:.*]] = llvm.getelementptr %[[VAL_2]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<"sometype", (array<9 x i32>)>
+// CHECK: %[[VAL_4:.*]] = llvm.ptrtoint %[[VAL_3]] : !llvm.ptr to i64
+// CHECK: "llvm.intr.memcpy"(%[[VAL_1]], %[[VAL_0]], %[[VAL_4]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i64) -> ()
+// CHECK: llvm.return
+// CHECK: }
+
+func.func @test_copy_2(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
+ fir.copy %arg0 to %arg1 : !fir.ref<!t>, !fir.ref<!t>
+ return
+}
+// CHECK-LABEL: llvm.func @test_copy_2(
+// CHECK-SAME: %[[VAL_0:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr,
+// CHECK-SAME: %[[VAL_1:[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*]]: !llvm.ptr) {
+// CHECK: %[[VAL_2:.*]] = llvm.mlir.zero : !llvm.ptr
+// CHECK: %[[VAL_3:.*]] = llvm.getelementptr %[[VAL_2]][1] : (!llvm.ptr) -> !llvm.ptr, !llvm.struct<"sometype", (array<9 x i32>)>
+// CHECK: %[[VAL_4:.*]] = llvm.ptrtoint %[[VAL_3]] : !llvm.ptr to i64
+// CHECK: "llvm.intr.memmove"(%[[VAL_1]], %[[VAL_0]], %[[VAL_4]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i64) -> ()
+// CHECK: llvm.return
+// CHECK: }
+}
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 1bfcb3a9f3dc8..06b0bbbf0bd20 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -933,3 +933,12 @@ func.func @test_call_arg_attrs_indirect(%arg0: i16, %arg1: (i16)-> i16) -> i16 {
%0 = fir.call %arg1(%arg0) : (i16 {llvm.noundef, llvm.signext}) -> (i16 {llvm.signext})
return %0 : i16
}
+
+// CHECK-LABEL: @test_copy(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.type<sometype{i:i32}>>,
+// CHECK-SAME: %[[VAL_1:.*]]: !fir.ptr<!fir.type<sometype{i:i32}>>
+func.func @test_copy(%arg0: !fir.ref<!fir.type<sometype{i:i32}>>, %arg1: !fir.ptr<!fir.type<sometype{i:i32}>>) {
+ // CHECK: fir.copy %[[VAL_0]] to %[[VAL_1]] no_overlap : !fir.ref<!fir.type<sometype{i:i32}>>, !fir.ptr<!fir.type<sometype{i:i32}>>
+ fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!fir.type<sometype{i:i32}>>, !fir.ptr<!fir.type<sometype{i:i32}>>
+ return
+}
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 7e3f9d6498412..feb2cd55b3786 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -1018,3 +1018,40 @@ func.func @bad_is_assumed_size(%arg0: !fir.ref<!fir.array<*:none>>) {
%1 = fir.is_assumed_size %arg0 : (!fir.ref<!fir.array<*:none>>) -> i1
return
}
+
+// -----
+
+!t=!fir.type<sometype{i:i32}>
+!t2=!fir.type<sometype2{j:i32}>
+func.func @bad_copy_1(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t2>) {
+ // expected-error at +1{{'fir.copy' op source and destination must have the same value type}}
+ fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t2>
+ return
+}
+
+// -----
+
+!t=!fir.type<sometype{i:i32}>
+func.func @bad_copy_2(%arg0: !fir.ref<!t>, %arg1: !t) {
+ // expected-error at +1{{'fir.copy' op operand #0 must be a reference type to a constant size fir.array, fir.char, or fir.type, but got '!fir.type<sometype{i:i32}>'}}
+ fir.copy %arg1 to %arg0 no_overlap : !t, !fir.ref<!t>
+ return
+}
+
+// -----
+
+!t=!fir.array<?xi32>
+func.func @bad_copy_3(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
+ // expected-error at +1{{'fir.copy' op operand #0 must be a reference type to a constant size fir.array, fir.char, or fir.type, but got '!fir.ref<!fir.array<?xi32>>'}}
+ fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t>
+ return
+}
+
+// -----
+
+!t=f32
+func.func @bad_copy_4(%arg0: !fir.ref<!t>, %arg1: !fir.ref<!t>) {
+ // expected-error at +1{{'fir.copy' op operand #0 must be a reference type to a constant size fir.array, fir.char, or fir.type, but got '!fir.ref<f32>'}}
+ fir.copy %arg0 to %arg1 no_overlap : !fir.ref<!t>, !fir.ref<!t>
+ return
+}
>From 976e41302411e511ab0e99922288185b5939bf54 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 11 Mar 2025 08:59:05 +0000
Subject: [PATCH 12/13] [lldb][Mangled][NFC] Clean up member variable doxygen
comments
---
lldb/include/lldb/Core/Mangled.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 165e67838e84b..9ca28917ccffa 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -276,10 +276,12 @@ class Mangled {
void Encode(DataEncoder &encoder, ConstStringTable &strtab) const;
private:
- /// Mangled member variables.
- ConstString m_mangled; ///< The mangled version of the name
- mutable ConstString m_demangled; ///< Mutable so we can get it on demand with
- ///a const version of this object
+ ///< The mangled version of the name.
+ ConstString m_mangled;
+
+ ///< Mutable so we can get it on demand with
+ ///< a const version of this object.
+ mutable ConstString m_demangled;
};
Stream &operator<<(Stream &s, const Mangled &obj);
>From d71fe1e943a5b2365b6a127b776aad19ba3e8ccf Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Tue, 11 Mar 2025 17:12:43 +0800
Subject: [PATCH 13/13] fix
---
.../bugprone/OptionalValueConversionCheck.cpp | 29 +++++++++++--------
clang-tools-extra/docs/ReleaseNotes.rst | 8 ++---
2 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
index cb5a1c7bea801..cda9288c0531a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/OptionalValueConversionCheck.cpp
@@ -85,21 +85,26 @@ void OptionalValueConversionCheck::registerMatchers(MatchFinder *Finder) {
// known template methods in std
callExpr(
argumentCountIs(1),
- callee(functionDecl(
- matchers::matchesAnyListedName(MakeSmartPtrList),
- hasTemplateArgument(0, refersToType(BindOptionalType)))),
+ anyOf(
+ // match std::make_unique std::make_shared
+ callee(functionDecl(
+ matchers::matchesAnyListedName(MakeSmartPtrList),
+ hasTemplateArgument(
+ 0, refersToType(BindOptionalType)))),
+ // match first std::make_optional by limit argument count
+ // (1) and template count (1).
+ // 1. template< class T > constexpr
+ // std::optional<decay_t<T>> make_optional(T&& value);
+ // 2. template< class T, class... Args > constexpr
+ // std::optional<T> make_optional(Args&&... args);
+ callee(functionDecl(templateArgumentCountIs(1),
+ hasName(MakeOptional),
+ returns(BindOptionalType)))),
hasArgument(0, OptionalDerefMatcher)),
callExpr(
- // match first std::make_optional by limit argument count (1)
- // and template count (1).
- // 1. template< class T > constexpr
- // std::optional<decay_t<T>> make_optional(T&& value);
- // 2. template< class T, class... Args > constexpr
- // std::optional<T> make_optional(Args&&... args);
+
argumentCountIs(1),
- callee(functionDecl(templateArgumentCountIs(1),
- hasName(MakeOptional),
- returns(BindOptionalType))),
+
hasArgument(0, OptionalDerefMatcher))),
unless(anyOf(hasAncestor(typeLoc()),
hasAncestor(expr(matchers::hasUnevaluatedContext())))))
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 5e2d87e0c2fa1..8f494c091f2a1 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -112,6 +112,10 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^
+- Improved :doc:`bugprone-optional-value-conversion
+ <clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
+ conversion in argument of ``std::make_optional``.
+
- Improved :doc:`bugprone-string-constructor
<clang-tidy/checks/bugprone/string-constructor>` check to find suspicious
calls of ``std::string`` constructor with char pointer, start position and
@@ -124,10 +128,6 @@ Changes in existing checks
no longer be needed and will be removed. Also fixing false positive from
const reference accessors to objects containing optional member.
-- Improved :doc:`bugprone-optional-value-conversion
- <clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
- conversion in argument of ``std::make_optional``.
-
- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
More information about the llvm-branch-commits
mailing list