Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,31 @@
readability-container-contains
==============================

Finds usages of ``container.count()`` and ``container.find() == container.end()`` which should be replaced by a call to the ``container.contains()`` method introduced in C++20.
Finds usages of ``container.count()`` and
``container.find() == container.end()`` which should be replaced by a call to
the ``container.contains()`` method.

Whether an element is contained inside a container should be checked with ``contains`` instead of ``count``/``find`` because ``contains`` conveys the intent more clearly. Furthermore, for containers which permit multiple entries per key (``multimap``, ``multiset``, ...), ``contains`` is more efficient than ``count`` because ``count`` has to do unnecessary additional work.
Whether an element is contained inside a container should be checked with
``contains`` instead of ``count``/``find`` because ``contains`` conveys the
intent more clearly. Furthermore, for containers which permit multiple entries
per key (``multimap``, ``multiset``, ...), ``contains`` is more efficient than
``count`` because ``count`` has to do unnecessary additional work.

Examples:

=========================================== ==============================
Initial expression Result
------------------------------------------- ------------------------------
``myMap.find(x) == myMap.end()`` ``!myMap.contains(x)``
``myMap.find(x) != myMap.end()`` ``myMap.contains(x)``
``if (myMap.count(x))`` ``if (myMap.contains(x))``
``bool exists = myMap.count(x)`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) > 0`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) >= 1`` ``bool exists = myMap.contains(x)``
``bool missing = myMap.count(x) == 0`` ``bool missing = !myMap.contains(x)``
=========================================== ==============================
====================================== =====================================
Initial expression Result
-------------------------------------- -------------------------------------
``myMap.find(x) == myMap.end()`` ``!myMap.contains(x)``
``myMap.find(x) != myMap.end()`` ``myMap.contains(x)``
``if (myMap.count(x))`` ``if (myMap.contains(x))``
``bool exists = myMap.count(x)`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) > 0`` ``bool exists = myMap.contains(x)``
``bool exists = myMap.count(x) >= 1`` ``bool exists = myMap.contains(x)``
``bool missing = myMap.count(x) == 0`` ``bool missing = !myMap.contains(x)``
====================================== =====================================

This check applies to ``std::set``, ``std::unordered_set``, ``std::map``, ``std::unordered_map`` and the corresponding multi-key variants.
It is only active for C++20 and later, as the ``contains`` method was only added in C++20.
This check will apply to any class that has a ``contains`` method, notably
including ``std::set``, ``std::unordered_set``, ``std::map``, and
``std::unordered_map`` as of C++20, and ``std::string`` and ``std::string_view``
as of C++23.
1 change: 0 additions & 1 deletion clang-tools-extra/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Contents
clang-include-fixer
modularize
pp-trace
clang-rename
clangd <https://clangd.llvm.org/>
clang-doc

Expand Down
3 changes: 0 additions & 3 deletions clang-tools-extra/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ configure_lit_site_cfg(
)

set(CLANG_TOOLS_TEST_DEPS
# For the clang-apply-replacements test that uses clang-rename.
clang-rename

# For the clang-doc tests that emit bitcode files.
llvm-bcanalyzer

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// RUN: %check_clang_tidy -std=c++14-or-later %s performance-unnecessary-value-param %t

// The test case used to crash clang-tidy.
// https://github.com/llvm/llvm-project/issues/108963

struct A
{
template<typename T> A(T&&) {}
};

struct B
{
~B();
};

struct C
{
A a;
C(B, int i) : a(i) {}
// CHECK-MESSAGES: [[@LINE-1]]:6: warning: the parameter #1 is copied for each invocation but only used as a const reference; consider making it a const reference
};

C c(B(), 0);
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ int testMacroExpansion(std::unordered_set<int> &MySet) {
return 0;
}

// The following map has the same interface like `std::map`.
// The following map has the same interface as `std::map`.
template <class Key, class T>
struct CustomMap {
unsigned count(const Key &K) const;
Expand All @@ -249,13 +249,180 @@ struct CustomMap {
void *end();
};

// The clang-tidy check is currently hard-coded against the `std::` containers
// and hence won't annotate the following instance. We might change this in the
// future and also detect the following case.
void *testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
if (MyMap.count(0))
// NO-WARNING.
// CHECK-FIXES: if (MyMap.count(0))
return nullptr;
return MyMap.find(2);
void testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};

MyMap.find(0) != MyMap.end();
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: MyMap.contains(0);
}

struct MySubmap : public CustomMap<int, int> {};

void testSubclass(MySubmap& MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};
}

using UsingMap = CustomMap<int, int>;
struct MySubmap2 : public UsingMap {};
using UsingMap2 = MySubmap2;

void testUsing(UsingMap2& MyMap) {
if (MyMap.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0)) {};
}

template <class Key, class T>
struct CustomMapContainsDeleted {
unsigned count(const Key &K) const;
bool contains(const Key &K) const = delete;
void *find(const Key &K);
void *end();
};

struct SubmapContainsDeleted : public CustomMapContainsDeleted<int, int> {};

void testContainsDeleted(CustomMapContainsDeleted<int, int> &MyMap,
SubmapContainsDeleted &MyMap2) {
// No warning if the `contains` method is deleted.
if (MyMap.count(0)) {};
if (MyMap2.count(0)) {};
}

template <class Key, class T>
struct CustomMapPrivateContains {
unsigned count(const Key &K) const;
void *find(const Key &K);
void *end();

private:
bool contains(const Key &K) const;
};

struct SubmapPrivateContains : public CustomMapPrivateContains<int, int> {};

void testPrivateContains(CustomMapPrivateContains<int, int> &MyMap,
SubmapPrivateContains &MyMap2) {
// No warning if the `contains` method is not public.
if (MyMap.count(0)) {};
if (MyMap2.count(0)) {};
}

struct MyString {};

struct WeirdNonMatchingContains {
unsigned count(char) const;
bool contains(const MyString&) const;
};

void testWeirdNonMatchingContains(WeirdNonMatchingContains &MyMap) {
// No warning if there is no `contains` method with the right type.
if (MyMap.count('a')) {};
}

template <class T>
struct SmallPtrSet {
using ConstPtrType = const T*;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

template <class T>
struct SmallPtrPtrSet {
using ConstPtrType = const T**;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

template <class T>
struct SmallPtrPtrPtrSet {
using ConstPtrType = const T***;
unsigned count(ConstPtrType Ptr) const;
bool contains(ConstPtrType Ptr) const;
};

void testSmallPtrSet(const int ***Ptr,
SmallPtrSet<int> &MySet,
SmallPtrPtrSet<int> &MySet2,
SmallPtrPtrPtrSet<int> &MySet3) {
if (MySet.count(**Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet.contains(**Ptr)) {};
if (MySet2.count(*Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet2.contains(*Ptr)) {};
if (MySet3.count(Ptr)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MySet3.contains(Ptr)) {};
}

struct X {};
struct Y : public X {};

void testSubclassEntry(SmallPtrSet<X>& Set, Y* Entry) {
if (Set.count(Entry)) {}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (Set.contains(Entry)) {}
}

struct WeirdPointerApi {
unsigned count(int** Ptr) const;
bool contains(int* Ptr) const;
};

void testWeirdApi(WeirdPointerApi& Set, int* E) {
if (Set.count(&E)) {}
}

void testIntUnsigned(std::set<int>& S, unsigned U) {
if (S.count(U)) {}
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (S.contains(U)) {}
}

template <class T>
struct CustomSetConvertible {
unsigned count(const T &K) const;
bool contains(const T &K) const;
};

struct A {};
struct B { B() = default; B(const A&) {} };
struct C { operator A() const; };

void testConvertibleTypes() {
CustomSetConvertible<B> MyMap;
if (MyMap.count(A())) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(A())) {};

CustomSetConvertible<A> MyMap2;
if (MyMap2.count(C())) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap2.contains(C())) {};

if (MyMap2.count(C()) != 0) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap2.contains(C())) {};
}

template<class U>
using Box = const U& ;

template <class T>
struct CustomBoxedSet {
unsigned count(Box<T> K) const;
bool contains(Box<T> K) const;
};

void testBox() {
CustomBoxedSet<int> Set;
if (Set.count(0)) {};
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (Set.contains(0)) {};
}
5 changes: 0 additions & 5 deletions clang/docs/ClangFormattedStatus.rst
Original file line number Diff line number Diff line change
Expand Up @@ -809,11 +809,6 @@ tree in terms of conformance to :doc:`ClangFormat` as of: March 06, 2022 17:32:2
- `4`
- `0`
- :good:`100%`
* - clang/tools/clang-rename
- `1`
- `1`
- `0`
- :good:`100%`
* - clang/tools/clang-repl
- `1`
- `1`
Expand Down
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
T __builtin_elementwise_log(T x) return the natural logarithm of x floating point types
T __builtin_elementwise_log2(T x) return the base 2 logarithm of x floating point types
T __builtin_elementwise_log10(T x) return the base 10 logarithm of x floating point types
T __builtin_elementwise_popcount(T x) return the number of 1 bits in x integer types
T __builtin_elementwise_pow(T x, T y) return x raised to the power of y floating point types
T __builtin_elementwise_bitreverse(T x) return the integer represented after reversing the bits of x integer types
T __builtin_elementwise_exp(T x) returns the base-e exponential, e^x, of the specified value floating point types
Expand Down
15 changes: 13 additions & 2 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ code bases.

- The ``le32`` and ``le64`` targets have been removed.

- The ``clang-rename`` tool has been removed.

C/C++ Language Potentially Breaking Changes
-------------------------------------------

Expand Down Expand Up @@ -114,6 +116,7 @@ C++ Language Changes

- Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension.

- Add ``__builtin_elementwise_popcount`` builtin for integer types only.

C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -252,7 +255,10 @@ Attribute Changes in Clang
(#GH106864)

- Introduced a new attribute ``[[clang::coro_await_elidable]]`` on coroutine return types
to express elideability at call sites where the coroutine is co_awaited as a prvalue.
to express elideability at call sites where the coroutine is invoked under a safe elide context.

- Introduced a new attribute ``[[clang::coro_await_elidable_argument]]`` on function parameters
to propagate safe elide context to arguments if such function is also under a safe elide context.

Improvements to Clang's diagnostics
-----------------------------------
Expand Down Expand Up @@ -392,8 +398,11 @@ Bug Fixes to C++ Support
- A follow-up fix was added for (#GH61460), as the previous fix was not entirely correct. (#GH86361)
- Fixed a crash in the typo correction of an invalid CTAD guide. (#GH107887)
- Fixed a crash when clang tries to subtitute parameter pack while retaining the parameter
pack. #GH63819, #GH107560
pack. (#GH63819), (#GH107560)
- Fix a crash when a static assert declaration has an invalid close location. (#GH108687)
- Avoided a redundant friend declaration instantiation under a certain ``consteval`` context. (#GH107175)
- Fixed an assertion failure in debug mode, and potential crashes in release mode, when
diagnosing a failed cast caused indirectly by a failed implicit conversion to the type of the constructor parameter.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -418,6 +427,8 @@ Miscellaneous Clang Crashes Fixed
- Fixed a crash when function has more than 65536 parameters.
Now a diagnostic is emitted. (#GH35741)

- Fixed ``-ast-dump`` crashes on codes involving ``concept`` with ``-ast-dump-decl-types``. (#GH94928)

OpenACC Specific Changes
------------------------

Expand Down
1 change: 0 additions & 1 deletion clang/docs/tools/clang-formatted-files.txt
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,6 @@ clang/tools/clang-refactor/ClangRefactor.cpp
clang/tools/clang-refactor/TestSupport.cpp
clang/tools/clang-refactor/TestSupport.h
clang/tools/clang-refactor/ToolRefactoringResultConsumer.h
clang/tools/clang-rename/ClangRename.cpp
clang/tools/clang-repl/ClangRepl.cpp
clang/tools/clang-scan-deps/ClangScanDeps.cpp
clang/tools/clang-shlib/clang-shlib.cpp
Expand Down
17 changes: 13 additions & 4 deletions clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,19 @@ class FunctionParmMutationAnalyzer {
static FunctionParmMutationAnalyzer *
getFunctionParmMutationAnalyzer(const FunctionDecl &Func, ASTContext &Context,
ExprMutationAnalyzer::Memoized &Memorized) {
auto [it, Inserted] = Memorized.FuncParmAnalyzer.try_emplace(&Func);
if (Inserted)
it->second = std::unique_ptr<FunctionParmMutationAnalyzer>(
new FunctionParmMutationAnalyzer(Func, Context, Memorized));
auto it = Memorized.FuncParmAnalyzer.find(&Func);
if (it == Memorized.FuncParmAnalyzer.end()) {
// Creating a new instance of FunctionParmMutationAnalyzer below may add
// additional elements to FuncParmAnalyzer. If we did try_emplace before
// creating a new instance, the returned iterator of try_emplace could be
// invalidated.
it =
Memorized.FuncParmAnalyzer
.try_emplace(&Func, std::unique_ptr<FunctionParmMutationAnalyzer>(
new FunctionParmMutationAnalyzer(
Func, Context, Memorized)))
.first;
}
return it->getSecond().get();
}

Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,14 @@ def CoroAwaitElidable : InheritableAttr {
let SimpleHandler = 1;
}

def CoroAwaitElidableArgument : InheritableAttr {
let Spellings = [Clang<"coro_await_elidable_argument">];
let Subjects = SubjectList<[ParmVar]>;
let LangOpts = [CPlusPlus];
let Documentation = [CoroAwaitElidableArgumentDoc];
let SimpleHandler = 1;
}

// OSObject-based attributes.
def OSConsumed : InheritableParamAttr {
let Spellings = [Clang<"os_consumed">];
Expand Down
83 changes: 73 additions & 10 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -8258,15 +8258,23 @@ but do not pass them to the underlying coroutine or pass them by value.
def CoroAwaitElidableDoc : Documentation {
let Category = DocCatDecl;
let Content = [{
The ``[[clang::coro_await_elidable]]`` is a class attribute which can be applied
to a coroutine return type.
The ``[[clang::coro_await_elidable]]`` is a class attribute which can be
applied to a coroutine return type. It provides a hint to the compiler to apply
Heap Allocation Elision more aggressively.

When a coroutine function that returns such a type calls another coroutine function,
the compiler performs heap allocation elision when the call to the coroutine function
is immediately co_awaited as a prvalue. In this case, the coroutine frame for the
callee will be a local variable within the enclosing braces in the caller's stack
frame. And the local variable, like other variables in coroutines, may be collected
into the coroutine frame, which may be allocated on the heap.
When a coroutine function returns such a type, a direct call expression therein
that returns a prvalue of a type attributed ``[[clang::coro_await_elidable]]``
is said to be under a safe elide context if one of the following is true:
- it is the immediate right-hand side operand to a co_await expression.
- it is an argument to a ``[[clang::coro_await_elidable_argument]]`` parameter
or parameter pack of another direct call expression under a safe elide context.

Do note that the safe elide context applies only to the call expression itself,
and the context does not transitively include any of its subexpressions unless
exceptional rules of ``[[clang::coro_await_elidable_argument]]`` apply.

The compiler performs heap allocation elision on call expressions under a safe
elide context, if the callee is a coroutine.

Example:

Expand All @@ -8281,8 +8289,63 @@ Example:
co_await t;
}

The behavior is undefined if the caller coroutine is destroyed earlier than the
callee coroutine.
Such elision replaces the heap allocated activation frame of the callee coroutine
with a local variable within the enclosing braces in the caller's stack frame.
The local variable, like other variables in coroutines, may be collected into the
coroutine frame, which may be allocated on the heap. The behavior is undefined
if the caller coroutine is destroyed earlier than the callee coroutine.

}];
}

def CoroAwaitElidableArgumentDoc : Documentation {
let Category = DocCatDecl;
let Content = [{

The ``[[clang::coro_await_elidable_argument]]`` is a function parameter attribute.
It works in conjunction with ``[[clang::coro_await_elidable]]`` to propagate a
safe elide context to a parameter or parameter pack if the function is called
under a safe elide context.

This is sometimes necessary on utility functions used to compose or modify the
behavior of a callee coroutine.

Example:

.. code-block:: c++

template <typename T>
class [[clang::coro_await_elidable]] Task { ... };

template <typename... T>
class [[clang::coro_await_elidable]] WhenAll { ... };

// `when_all` is a utility function that composes coroutines. It does not
// need to be a coroutine to propagate.
template <typename... T>
WhenAll<T...> when_all([[clang::coro_await_elidable_argument]] Task<T> tasks...);

Task<int> foo();
Task<int> bar();
Task<void> example1() {
// `when_all``, `foo``, and `bar` are all elide safe because `when_all` is
// under a safe elide context and, thanks to the [[clang::coro_await_elidable_argument]]
// attribute, such context is propagated to foo and bar.
co_await when_all(foo(), bar());
}

Task<void> example2() {
// `when_all` and `bar` are elide safe. `foo` is not elide safe.
auto f = foo();
co_await when_all(f, bar());
}


Task<void> example3() {
// None of the calls are elide safe.
auto t = when_all(foo(), bar());
co_await t;
}

}];
}
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,12 @@ def ElementwiseLog10 : Builtin {
let Prototype = "void(...)";
}

def ElementwisePopcount : Builtin {
let Spellings = ["__builtin_elementwise_popcount"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwisePow : Builtin {
let Spellings = ["__builtin_elementwise_pow"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down
270 changes: 90 additions & 180 deletions clang/include/clang/Basic/Diagnostic.h

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions clang/include/clang/Basic/DiagnosticIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

namespace clang {
class DiagnosticsEngine;
class DiagnosticBuilder;
class SourceLocation;

// Import the diagnostic enums themselves.
Expand Down Expand Up @@ -486,11 +487,13 @@ class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
///
/// \returns \c true if the diagnostic was emitted, \c false if it was
/// suppressed.
bool ProcessDiag(DiagnosticsEngine &Diag) const;
bool ProcessDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder) const;

/// Used to emit a diagnostic that is finally fully formed,
/// ignoring suppression.
void EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const;
void EmitDiag(DiagnosticsEngine &Diag, const DiagnosticBuilder &DiagBuilder,
Level DiagLevel) const;

/// Whether the diagnostic may leave the AST in a state where some
/// invariants can break.
Expand Down
9 changes: 6 additions & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,12 @@ def ext_cxx11_enum_fixed_underlying_type : Extension<
def ext_ms_c_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a Microsoft extension">,
InGroup<MicrosoftFixedEnum>;
def ext_clang_c_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a Clang extension">,
InGroup<DiagGroup<"fixed-enum-extension">>;
def ext_c23_enum_fixed_underlying_type : Extension<
"enumeration types with a fixed underlying type are a C23 extension">,
InGroup<C23>;
def warn_c17_compat_enum_fixed_underlying_type : Warning<
"enumeration types with a fixed underlying type are incompatible with C standards before C23">,
DefaultIgnore, InGroup<CPre23Compat>;
def warn_cxx98_compat_enum_fixed_underlying_type : Warning<
"enumeration types with a fixed underlying type are incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3290,7 +3290,7 @@ def err_attribute_unsupported_m_profile
def err_duplicate_target_attribute
: Error<"%select{unsupported|duplicate|unknown}0%select{| CPU|"
" tune CPU}1 '%2' in the '%select{target|target_clones|target_version}3' "
"attribute string; ">;
"attribute string;">;
// The err_*_attribute_argument_not_int are separate because they're used by
// VerifyIntegerConstantExpression.
def err_aligned_attribute_argument_not_int : Error<
Expand Down Expand Up @@ -9915,7 +9915,7 @@ def err_defaulted_comparison_constexpr_mismatch : Error<
"three-way comparison operator}0 cannot be "
"declared %select{constexpr|consteval}2 because "
"%select{it|for which the corresponding implicit 'operator==' }0 "
"invokes a non-constexpr comparison function ">;
"invokes a non-constexpr comparison function">;
def note_defaulted_comparison_not_constexpr : Note<
"non-constexpr comparison function would be used to compare "
"%select{|member %1|base class %1}0">;
Expand Down Expand Up @@ -11559,7 +11559,7 @@ def err_omp_wrong_device_function_call : Error<
"function with 'device_type(%0)' is not available on %select{device|host}1">;
def note_omp_marked_device_type_here : Note<"marked as 'device_type(%0)' here">;
def err_omp_declare_target_has_local_vars : Error<
"local variable '%0' should not be used in 'declare target' directive; ">;
"local variable '%0' should not be used in 'declare target' directive;">;
def warn_omp_declare_target_after_first_use : Warning<
"declaration marked as declare target after first use, it may lead to incorrect results">,
InGroup<OpenMPTarget>;
Expand Down
5 changes: 1 addition & 4 deletions clang/include/clang/Basic/PartialDiagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,10 @@ class PartialDiagnostic : public StreamingDiagnostic {

void EmitToString(DiagnosticsEngine &Diags,
SmallVectorImpl<char> &Buf) const {
// FIXME: It should be possible to render a diagnostic to a string without
// messing with the state of the diagnostics engine.
DiagnosticBuilder DB(Diags.Report(getDiagID()));
Emit(DB);
Diagnostic(&Diags).FormatDiagnostic(Buf);
Diagnostic(&Diags, DB).FormatDiagnostic(Buf);
DB.Clear();
Diags.Clear();
}

/// Clear out this partial diagnostic, giving it a new diagnostic ID
Expand Down
6 changes: 3 additions & 3 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,10 +626,10 @@ class Sema final : public SemaBase {
const llvm::MapVector<FieldDecl *, DeleteLocs> &
getMismatchingDeleteExpressions() const;

/// Cause the active diagnostic on the DiagosticsEngine to be
/// emitted. This is closely coupled to the SemaDiagnosticBuilder class and
/// Cause the built diagnostic to be emitted on the DiagosticsEngine.
/// This is closely coupled to the SemaDiagnosticBuilder class and
/// should not be used elsewhere.
void EmitCurrentDiagnostic(unsigned DiagID);
void EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB);

void addImplicitTypedef(StringRef Name, QualType T);

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3377,7 +3377,8 @@ static void encodeTypeForFunctionPointerAuth(const ASTContext &Ctx,
#include "clang/Basic/HLSLIntangibleTypes.def"
case BuiltinType::Dependent:
llvm_unreachable("should never get here");
case BuiltinType::AMDGPUBufferRsrc:
#define AMDGPU_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/AMDGPUTypes.def"
case BuiltinType::WasmExternRef:
#define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id:
#include "clang/Basic/RISCVVTypes.def"
Expand Down
85 changes: 42 additions & 43 deletions clang/lib/Basic/Diagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,7 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
TrapNumErrorsOccurred = 0;
TrapNumUnrecoverableErrorsOccurred = 0;

CurDiagID = std::numeric_limits<unsigned>::max();
LastDiagLevel = DiagnosticIDs::Ignored;
DelayedDiagID = 0;

if (!soft) {
// Clear state related to #pragma diagnostic.
Expand All @@ -143,23 +141,6 @@ void DiagnosticsEngine::Reset(bool soft /*=false*/) {
}
}

void DiagnosticsEngine::SetDelayedDiagnostic(unsigned DiagID, StringRef Arg1,
StringRef Arg2, StringRef Arg3) {
if (DelayedDiagID)
return;

DelayedDiagID = DiagID;
DelayedDiagArg1 = Arg1.str();
DelayedDiagArg2 = Arg2.str();
DelayedDiagArg3 = Arg3.str();
}

void DiagnosticsEngine::ReportDelayed() {
unsigned ID = DelayedDiagID;
DelayedDiagID = 0;
Report(ID) << DelayedDiagArg1 << DelayedDiagArg2 << DelayedDiagArg3;
}

DiagnosticMapping &
DiagnosticsEngine::DiagState::getOrAddMapping(diag::kind Diag) {
std::pair<iterator, bool> Result =
Expand Down Expand Up @@ -503,39 +484,31 @@ void DiagnosticsEngine::setSeverityForAll(diag::Flavor Flavor,
}

void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) {
assert(CurDiagID == std::numeric_limits<unsigned>::max() &&
"Multiple diagnostics in flight at once!");

CurDiagLoc = storedDiag.getLocation();
CurDiagID = storedDiag.getID();
DiagStorage.NumDiagArgs = 0;

DiagStorage.DiagRanges.clear();
DiagnosticStorage DiagStorage;
DiagStorage.DiagRanges.append(storedDiag.range_begin(),
storedDiag.range_end());

DiagStorage.FixItHints.clear();
DiagStorage.FixItHints.append(storedDiag.fixit_begin(),
storedDiag.fixit_end());

assert(Client && "DiagnosticConsumer not set!");
Level DiagLevel = storedDiag.getLevel();
Diagnostic Info(this, storedDiag.getMessage());
Diagnostic Info(this, storedDiag.getLocation(), storedDiag.getID(),
DiagStorage, storedDiag.getMessage());
Client->HandleDiagnostic(DiagLevel, Info);
if (Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticsEngine::Warning)
++NumWarnings;
}

CurDiagID = std::numeric_limits<unsigned>::max();
}

bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
bool DiagnosticsEngine::EmitDiagnostic(const DiagnosticBuilder &DB,
bool Force) {
assert(getClient() && "DiagnosticClient not set!");

bool Emitted;
if (Force) {
Diagnostic Info(this);
Diagnostic Info(this, DB);

// Figure out the diagnostic level of this message.
DiagnosticIDs::Level DiagLevel
Expand All @@ -544,24 +517,50 @@ bool DiagnosticsEngine::EmitCurrentDiagnostic(bool Force) {
Emitted = (DiagLevel != DiagnosticIDs::Ignored);
if (Emitted) {
// Emit the diagnostic regardless of suppression level.
Diags->EmitDiag(*this, DiagLevel);
Diags->EmitDiag(*this, DB, DiagLevel);
}
} else {
// Process the diagnostic, sending the accumulated information to the
// DiagnosticConsumer.
Emitted = ProcessDiag();
Emitted = ProcessDiag(DB);
}

// Clear out the current diagnostic object.
Clear();
return Emitted;
}

DiagnosticBuilder::DiagnosticBuilder(DiagnosticsEngine *DiagObj,
SourceLocation DiagLoc, unsigned DiagID)
: StreamingDiagnostic(DiagObj->DiagAllocator), DiagObj(DiagObj),
DiagLoc(DiagLoc), DiagID(DiagID), IsActive(true) {
assert(DiagObj && "DiagnosticBuilder requires a valid DiagnosticsEngine!");
}

// If there was a delayed diagnostic, emit it now.
if (!Force && DelayedDiagID)
ReportDelayed();
DiagnosticBuilder::DiagnosticBuilder(const DiagnosticBuilder &D)
: StreamingDiagnostic() {
DiagLoc = D.DiagLoc;
DiagID = D.DiagID;
FlagValue = D.FlagValue;
DiagObj = D.DiagObj;
DiagStorage = D.DiagStorage;
D.DiagStorage = nullptr;
Allocator = D.Allocator;
IsActive = D.IsActive;
IsForceEmit = D.IsForceEmit;
D.Clear();
}

return Emitted;
Diagnostic::Diagnostic(const DiagnosticsEngine *DO,
const DiagnosticBuilder &DiagBuilder)
: DiagObj(DO), DiagLoc(DiagBuilder.DiagLoc), DiagID(DiagBuilder.DiagID),
FlagValue(DiagBuilder.FlagValue), DiagStorage(*DiagBuilder.getStorage()) {
}

Diagnostic::Diagnostic(const DiagnosticsEngine *DO, SourceLocation DiagLoc,
unsigned DiagID, const DiagnosticStorage &DiagStorage,
StringRef StoredDiagMessage)
: DiagObj(DO), DiagLoc(DiagLoc), DiagID(DiagID), DiagStorage(DiagStorage),
StoredDiagMessage(StoredDiagMessage) {}

DiagnosticConsumer::~DiagnosticConsumer() = default;

void DiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
Expand Down Expand Up @@ -1216,13 +1215,13 @@ bool ForwardingDiagnosticConsumer::IncludeInDiagnosticCounts() const {
return Target.IncludeInDiagnosticCounts();
}

PartialDiagnostic::DiagStorageAllocator::DiagStorageAllocator() {
DiagStorageAllocator::DiagStorageAllocator() {
for (unsigned I = 0; I != NumCached; ++I)
FreeList[I] = Cached + I;
NumFreeListEntries = NumCached;
}

PartialDiagnostic::DiagStorageAllocator::~DiagStorageAllocator() {
DiagStorageAllocator::~DiagStorageAllocator() {
// Don't assert if we are in a CrashRecovery context, as this invariant may
// be invalidated during a crash.
assert((NumFreeListEntries == NumCached ||
Expand Down
21 changes: 11 additions & 10 deletions clang/lib/Basic/DiagnosticIDs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc,

// If explicitly requested, map fatal errors to errors.
if (Result == diag::Severity::Fatal &&
Diag.CurDiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
DiagID != diag::fatal_too_many_errors && Diag.FatalsAsError)
Result = diag::Severity::Error;

bool ShowInSystemHeader =
Expand Down Expand Up @@ -800,8 +800,9 @@ StringRef DiagnosticIDs::getNearestOption(diag::Flavor Flavor,

/// ProcessDiag - This is the method used to report a diagnostic that is
/// finally fully formed.
bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
Diagnostic Info(&Diag);
bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder) const {
Diagnostic Info(&Diag, DiagBuilder);

assert(Diag.getClient() && "DiagnosticClient not set!");

Expand Down Expand Up @@ -867,31 +868,31 @@ bool DiagnosticIDs::ProcessDiag(DiagnosticsEngine &Diag) const {
// stop a flood of bogus errors.
if (Diag.ErrorLimit && Diag.NumErrors > Diag.ErrorLimit &&
DiagLevel == DiagnosticIDs::Error) {
Diag.SetDelayedDiagnostic(diag::fatal_too_many_errors);
Diag.Report(diag::fatal_too_many_errors);
return false;
}
}

// Make sure we set FatalErrorOccurred to ensure that the notes from the
// diagnostic that caused `fatal_too_many_errors` won't be emitted.
if (Diag.CurDiagID == diag::fatal_too_many_errors)
if (Info.getID() == diag::fatal_too_many_errors)
Diag.FatalErrorOccurred = true;
// Finally, report it.
EmitDiag(Diag, DiagLevel);
EmitDiag(Diag, DiagBuilder, DiagLevel);
return true;
}

void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const {
Diagnostic Info(&Diag);
void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag,
const DiagnosticBuilder &DiagBuilder,
Level DiagLevel) const {
Diagnostic Info(&Diag, DiagBuilder);
assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!");

Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info);
if (Diag.Client->IncludeInDiagnosticCounts()) {
if (DiagLevel == DiagnosticIDs::Warning)
++Diag.NumWarnings;
}

Diag.CurDiagID = ~0U;
}

bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const {
Expand Down
23 changes: 4 additions & 19 deletions clang/lib/Basic/SourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,8 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// the file could also have been removed during processing. Since we can't
// really deal with this situation, just create an empty buffer.
if (!BufferOrError) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_cannot_open_file,
ContentsEntry->getName(),
BufferOrError.getError().message());
else
Diag.Report(Loc, diag::err_cannot_open_file)
<< ContentsEntry->getName() << BufferOrError.getError().message();
Diag.Report(Loc, diag::err_cannot_open_file)
<< ContentsEntry->getName() << BufferOrError.getError().message();

return std::nullopt;
}
Expand All @@ -153,12 +148,7 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// ContentsEntry::getSize() could have the wrong size. Use
// MemoryBuffer::getBufferSize() instead.
if (Buffer->getBufferSize() >= std::numeric_limits<unsigned>::max()) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_file_too_large,
ContentsEntry->getName());
else
Diag.Report(Loc, diag::err_file_too_large)
<< ContentsEntry->getName();
Diag.Report(Loc, diag::err_file_too_large) << ContentsEntry->getName();

return std::nullopt;
}
Expand All @@ -168,12 +158,7 @@ ContentCache::getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
// have come from a stat cache).
if (!ContentsEntry->isNamedPipe() &&
Buffer->getBufferSize() != (size_t)ContentsEntry->getSize()) {
if (Diag.isDiagnosticInFlight())
Diag.SetDelayedDiagnostic(diag::err_file_modified,
ContentsEntry->getName());
else
Diag.Report(Loc, diag::err_file_modified)
<< ContentsEntry->getName();
Diag.Report(Loc, diag::err_file_modified) << ContentsEntry->getName();

return std::nullopt;
}
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3834,6 +3834,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_elementwise_floor:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::floor, "elt.floor"));
case Builtin::BI__builtin_elementwise_popcount:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::ctpop, "elt.ctpop"));
case Builtin::BI__builtin_elementwise_roundeven:
return RValue::get(emitBuiltinWithOneOverloadedType<1>(
*this, E, llvm::Intrinsic::roundeven, "elt.roundeven"));
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Driver/ToolChains/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,6 @@ void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
Args.MakeArgString("-plugin-opt=-mattr=" + llvm::join(Features, ",")));
}

addGPULibraries(getToolChain(), Args, CmdArgs);

CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
C.addCommand(std::make_unique<Command>(
Expand Down Expand Up @@ -1089,4 +1087,4 @@ bool AMDGPUToolChain::shouldSkipSanitizeOption(
return true;
}
return false;
}
}
19 changes: 19 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9223,6 +9223,25 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
A->claim();
}

// Pass in the C library for GPUs if present and not disabled.
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_r, options::OPT_nogpulib,
options::OPT_nodefaultlibs, options::OPT_nolibc,
options::OPT_nogpulibc)) {
forAllAssociatedToolChains(C, JA, getToolChain(), [&](const ToolChain &TC) {
// The device C library is only available for NVPTX and AMDGPU targets
// currently.
if (!TC.getTriple().isNVPTX() && !TC.getTriple().isAMDGPU())
return;
bool HasLibC = TC.getStdlibIncludePath().has_value();
if (HasLibC) {
CmdArgs.push_back(Args.MakeArgString(
"--device-linker=" + TC.getTripleString() + "=" + "-lc"));
CmdArgs.push_back(Args.MakeArgString(
"--device-linker=" + TC.getTripleString() + "=" + "-lm"));
}
});
}

// If we disable the GPU C library support it needs to be forwarded to the
// link job.
if (!Args.hasFlag(options::OPT_gpulibc, options::OPT_nogpulibc, true))
Expand Down
16 changes: 0 additions & 16 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,22 +510,6 @@ void tools::addLinkerCompressDebugSectionsOption(
}
}

void tools::addGPULibraries(const ToolChain &TC, const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) {
if (Args.hasArg(options::OPT_nostdlib, options::OPT_r,
options::OPT_nodefaultlibs, options::OPT_nolibc,
options::OPT_nogpulibc))
return;

// If the user's toolchain has the 'include/<triple>/` path, we assume it
// supports the standard C libraries for the GPU and include them.
bool HasLibC = TC.getStdlibIncludePath().has_value();
if (HasLibC) {
CmdArgs.push_back("-lc");
CmdArgs.push_back("-lm");
}
}

void tools::AddTargetFeature(const ArgList &Args,
std::vector<StringRef> &Features,
OptSpecifier OnOpt, OptSpecifier OffOpt,
Expand Down
3 changes: 0 additions & 3 deletions clang/lib/Driver/ToolChains/CommonArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ void addLinkerCompressDebugSectionsOption(const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs);

void addGPULibraries(const ToolChain &TC, const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs);

void claimNoWarnArgs(const llvm::opt::ArgList &Args);

bool addSanitizerRuntimes(const ToolChain &TC, const llvm::opt::ArgList &Args,
Expand Down
2 changes: 0 additions & 2 deletions clang/lib/Driver/ToolChains/Cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,6 @@ void NVPTX::Linker::ConstructJob(Compilation &C, const JobAction &JA,
for (StringRef Feature : Features)
CmdArgs.append({"--feature", Args.MakeArgString(Feature)});

addGPULibraries(getToolChain(), Args, CmdArgs);

// Add paths for the default clang library path.
SmallString<256> DefaultLibPath =
llvm::sys::path::parent_path(TC.getDriver().Dir);
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
if (Tok.Previous->isIf())
return Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak;
return !Tok.Previous->isOneOf(TT_CastRParen, tok::kw_for, tok::kw_while,
tok::kw_switch);
tok::kw_switch) &&
!(Style.isJavaScript() && Tok.Previous->is(Keywords.kw_await));
};
auto IsFunctionCallParen = [](const FormatToken &Tok) {
return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Frontend/ASTConsumers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ namespace {
if (DumpDeclTypes) {
Decl *InnerD = D;
if (auto *TD = dyn_cast<TemplateDecl>(D))
InnerD = TD->getTemplatedDecl();
if (Decl *TempD = TD->getTemplatedDecl())
InnerD = TempD;

// FIXME: Support OutputFormat in type dumping.
// FIXME: Support combining -ast-dump-decl-types with -ast-dump-lookups.
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/Frontend/Rewrite/FixItRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,8 @@ void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
/// Emit a diagnostic via the adapted diagnostic client.
void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
// When producing this diagnostic, we temporarily bypass ourselves,
// clear out any current diagnostic, and let the downstream client
// format the diagnostic.
// and let the downstream client format the diagnostic.
Diags.setClient(Client, false);
Diags.Clear();
Diags.Report(Loc, DiagID);
Diags.setClient(this, false);
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Frontend/TextDiagnosticPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ static void printDiagnosticOptions(raw_ostream &OS,
if (!Opt.empty()) {
OS << (Started ? "," : " [")
<< (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt;
StringRef OptValue = Info.getDiags()->getFlagValue();
StringRef OptValue = Info.getFlagValue();
if (!OptValue.empty())
OS << "=" << OptValue;
Started = true;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Headers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ set(x86_files
avx10_2_512satcvtintrin.h
avx10_2bf16intrin.h
avx10_2convertintrin.h
avx10_2copyintrin.h
avx10_2minmaxintrin.h
avx10_2niintrin.h
avx10_2satcvtdsintrin.h
Expand Down
34 changes: 34 additions & 0 deletions clang/lib/Headers/avx10_2copyintrin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*===---- avx10_2copyintrin.h - AVX10.2 Copy intrinsics -------------------===
*
* Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
* See https://llvm.org/LICENSE.txt for license information.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*
*===-----------------------------------------------------------------------===
*/
#ifndef __IMMINTRIN_H
#error \
"Never use <avx10_2copyintrin.h> directly; include <immintrin.h> instead."
#endif // __IMMINTRIN_H

#ifndef __AVX10_2COPYINTRIN_H
#define __AVX10_2COPYINTRIN_H

/* Define the default attributes for the functions in this file. */
#define __DEFAULT_FN_ATTRS128 \
__attribute__((__always_inline__, __nodebug__, __target__("avx10.2-256"), \
__min_vector_width__(128)))

static __inline__ __m128i __DEFAULT_FN_ATTRS128 _mm_move_epi32(__m128i __A) {
return (__m128i)__builtin_shufflevector(
(__v4si)__A, (__v4si)_mm_setzero_si128(), 0, 4, 4, 4);
}

static __inline__ __m128i __DEFAULT_FN_ATTRS128 _mm_move_epi16(__m128i __A) {
return (__m128i)__builtin_shufflevector(
(__v8hi)__A, (__v8hi)_mm_setzero_si128(), 0, 8, 8, 8, 8, 8, 8, 8);
}

#undef __DEFAULT_FN_ATTRS128

#endif // __AVX10_2COPYINTRIN_H
71 changes: 71 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,77 @@ float3 cosh(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_cosh)
float4 cosh(float4);

//===----------------------------------------------------------------------===//
// count bits builtins
//===----------------------------------------------------------------------===//

/// \fn T countbits(T Val)
/// \brief Return the number of bits (per component) set in the input integer.
/// \param Val The input value.

#ifdef __HLSL_ENABLE_16_BIT
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int16_t countbits(int16_t);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int16_t2 countbits(int16_t2);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int16_t3 countbits(int16_t3);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int16_t4 countbits(int16_t4);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint16_t countbits(uint16_t);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint16_t2 countbits(uint16_t2);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint16_t3 countbits(uint16_t3);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint16_t4 countbits(uint16_t4);
#endif

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int countbits(int);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int2 countbits(int2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int3 countbits(int3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int4 countbits(int4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint countbits(uint);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint2 countbits(uint2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint3 countbits(uint3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint4 countbits(uint4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int64_t countbits(int64_t);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int64_t2 countbits(int64_t2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int64_t3 countbits(int64_t3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
int64_t4 countbits(int64_t4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint64_t countbits(uint64_t);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint64_t2 countbits(uint64_t2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint64_t3 countbits(uint64_t3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_popcount)
uint64_t4 countbits(uint64_t4);

//===----------------------------------------------------------------------===//
// dot product builtins
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Headers/immintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ _storebe_i64(void * __P, long long __D) {
#if !defined(__SCE__) || __has_feature(modules) || defined(__AVX10_2__)
#include <avx10_2bf16intrin.h>
#include <avx10_2convertintrin.h>
#include <avx10_2copyintrin.h>
#include <avx10_2minmaxintrin.h>
#include <avx10_2niintrin.h>
#include <avx10_2satcvtdsintrin.h>
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5439,18 +5439,20 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,

BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd());

if (!getLangOpts().ObjC && !getLangOpts().C23) {
if (!getLangOpts().ObjC) {
if (getLangOpts().CPlusPlus11)
Diag(ColonLoc, diag::warn_cxx98_compat_enum_fixed_underlying_type)
<< BaseRange;
else if (getLangOpts().CPlusPlus)
Diag(ColonLoc, diag::ext_cxx11_enum_fixed_underlying_type)
<< BaseRange;
else if (getLangOpts().MicrosoftExt)
else if (getLangOpts().MicrosoftExt && !getLangOpts().C23)
Diag(ColonLoc, diag::ext_ms_c_enum_fixed_underlying_type)
<< BaseRange;
else
Diag(ColonLoc, diag::ext_clang_c_enum_fixed_underlying_type)
Diag(ColonLoc, getLangOpts().C23
? diag::warn_c17_compat_enum_fixed_underlying_type
: diag::ext_c23_enum_fixed_underlying_type)
<< BaseRange;
}
}
Expand Down
19 changes: 6 additions & 13 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1590,17 +1590,17 @@ LangAS Sema::getDefaultCXXMethodAddrSpace() const {
return LangAS::Default;
}

void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
void Sema::EmitDiagnostic(unsigned DiagID, const DiagnosticBuilder &DB) {
// FIXME: It doesn't make sense to me that DiagID is an incoming argument here
// and yet we also use the current diag ID on the DiagnosticsEngine. This has
// been made more painfully obvious by the refactor that introduced this
// function, but it is possible that the incoming argument can be
// eliminated. If it truly cannot be (for example, there is some reentrancy
// issue I am not seeing yet), then there should at least be a clarifying
// comment somewhere.
Diagnostic DiagInfo(&Diags, DB);
if (std::optional<TemplateDeductionInfo *> Info = isSFINAEContext()) {
switch (DiagnosticIDs::getDiagnosticSFINAEResponse(
Diags.getCurrentDiagID())) {
switch (DiagnosticIDs::getDiagnosticSFINAEResponse(DiagInfo.getID())) {
case DiagnosticIDs::SFINAE_Report:
// We'll report the diagnostic below.
break;
Expand All @@ -1613,13 +1613,11 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information.
if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
Diagnostic DiagInfo(&Diags);
(*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
}

Diags.setLastDiagnosticIgnored(true);
Diags.Clear();
return;

case DiagnosticIDs::SFINAE_AccessControl: {
Expand All @@ -1630,24 +1628,21 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
if (!AccessCheckingSFINAE && !getLangOpts().CPlusPlus11)
break;

SourceLocation Loc = Diags.getCurrentDiagLoc();
SourceLocation Loc = DiagInfo.getLocation();

// Suppress this diagnostic.
++NumSFINAEErrors;

// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information.
if (*Info && !(*Info)->hasSFINAEDiagnostic()) {
Diagnostic DiagInfo(&Diags);
(*Info)->addSFINAEDiagnostic(DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
}

Diags.setLastDiagnosticIgnored(true);
Diags.Clear();

// Now the diagnostic state is clear, produce a C++98 compatibility
// warning.
// Now produce a C++98 compatibility warning.
Diag(Loc, diag::warn_cxx98_compat_sfinae_access_control);

// The last diagnostic which Sema produced was ignored. Suppress any
Expand All @@ -1660,14 +1655,12 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
// Make a copy of this suppressed diagnostic and store it with the
// template-deduction information;
if (*Info) {
Diagnostic DiagInfo(&Diags);
(*Info)->addSuppressedDiagnostic(DiagInfo.getLocation(),
PartialDiagnostic(DiagInfo, Context.getDiagAllocator()));
}

// Suppress this diagnostic.
Diags.setLastDiagnosticIgnored(true);
Diags.Clear();
return;
}
}
Expand All @@ -1677,7 +1670,7 @@ void Sema::EmitCurrentDiagnostic(unsigned DiagID) {
Context.setPrintingPolicy(getPrintingPolicy());

// Emit the diagnostic.
if (!Diags.EmitCurrentDiagnostic())
if (!Diags.EmitDiagnostic(DB))
return;

// If this is not a note, and we're in a template instantiation
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ SemaBase::ImmediateDiagBuilder::~ImmediateDiagBuilder() {
Clear();

// Dispatch to Sema to emit the diagnostic.
SemaRef.EmitCurrentDiagnostic(DiagID);
SemaRef.EmitDiagnostic(DiagID, *this);
}

PartialDiagnostic SemaBase::PDiag(unsigned DiagID) {
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,12 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT,
: InitializationKind::CreateCast(/*type range?*/ range);
InitializationSequence sequence(S, entity, initKind, src);

assert(sequence.Failed() && "initialization succeeded on second try?");
// It could happen that a constructor failed to be used because
// it requires a temporary of a broken type. Still, it will be found when
// looking for a match.
if (!sequence.Failed())
return false;

switch (sequence.getFailureKind()) {
default: return false;

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2795,7 +2795,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
if (BuiltinElementwiseMath(TheCall))
return ExprError();
break;

case Builtin::BI__builtin_elementwise_popcount:
case Builtin::BI__builtin_elementwise_bitreverse: {
if (PrepareBuiltinElementwiseMathOneArgCall(TheCall))
return ExprError();
Expand Down
40 changes: 27 additions & 13 deletions clang/lib/Sema/SemaCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,12 +849,28 @@ static bool isAttributedCoroAwaitElidable(const QualType &QT) {
return Record && Record->hasAttr<CoroAwaitElidableAttr>();
}

static bool isCoroAwaitElidableCall(Expr *Operand) {
if (!Operand->isPRValue()) {
return false;
}
static void applySafeElideContext(Expr *Operand) {
auto *Call = dyn_cast<CallExpr>(Operand->IgnoreImplicit());
if (!Call || !Call->isPRValue())
return;

if (!isAttributedCoroAwaitElidable(Call->getType()))
return;

Call->setCoroElideSafe();

return isAttributedCoroAwaitElidable(Operand->getType());
// Check parameter
auto *Fn = llvm::dyn_cast_if_present<FunctionDecl>(Call->getCalleeDecl());
if (!Fn)
return;

size_t ParmIdx = 0;
for (ParmVarDecl *PD : Fn->parameters()) {
if (PD->hasAttr<CoroAwaitElidableArgumentAttr>())
applySafeElideContext(Call->getArg(ParmIdx));

ParmIdx++;
}
}

// Attempts to resolve and build a CoawaitExpr from "raw" inputs, bailing out to
Expand All @@ -880,14 +896,12 @@ ExprResult Sema::BuildUnresolvedCoawaitExpr(SourceLocation Loc, Expr *Operand,
}

auto *RD = Promise->getType()->getAsCXXRecordDecl();
bool AwaitElidable =
isCoroAwaitElidableCall(Operand) &&
isAttributedCoroAwaitElidable(
getCurFunctionDecl(/*AllowLambda=*/true)->getReturnType());

if (AwaitElidable)
if (auto *Call = dyn_cast<CallExpr>(Operand->IgnoreImplicit()))
Call->setCoroElideSafe();

bool CurFnAwaitElidable = isAttributedCoroAwaitElidable(
getCurFunctionDecl(/*AllowLambda=*/true)->getReturnType());

if (CurFnAwaitElidable)
applySafeElideContext(Operand);

Expr *Transformed = Operand;
if (lookupMember(*this, "await_transform", RD, Loc)) {
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17557,7 +17557,7 @@ static void RemoveNestedImmediateInvocation(
else
break;
}
/// ConstantExpr are the first layer of implicit node to be removed so if
/// ConstantExprs are the first layer of implicit node to be removed so if
/// Init isn't a ConstantExpr, no ConstantExpr will be skipped.
if (auto *CE = dyn_cast<ConstantExpr>(Init);
CE && CE->isImmediateInvocation())
Expand All @@ -17570,7 +17570,7 @@ static void RemoveNestedImmediateInvocation(
}
ExprResult TransformLambdaExpr(LambdaExpr *E) {
// Do not rebuild lambdas to avoid creating a new type.
// Lambdas have already been processed inside their eval context.
// Lambdas have already been processed inside their eval contexts.
return E;
}
bool AlwaysRebuild() { return false; }
Expand Down
308 changes: 119 additions & 189 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,48 @@
#include <utility>

using namespace clang;
using llvm::dxil::ResourceClass;

enum class RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };

static RegisterType getRegisterType(ResourceClass RC) {
switch (RC) {
case ResourceClass::SRV:
return RegisterType::SRV;
case ResourceClass::UAV:
return RegisterType::UAV;
case ResourceClass::CBuffer:
return RegisterType::CBuffer;
case ResourceClass::Sampler:
return RegisterType::Sampler;
}
llvm_unreachable("unexpected ResourceClass value");
}

static RegisterType getRegisterType(StringRef Slot) {
switch (Slot[0]) {
case 't':
case 'T':
return RegisterType::SRV;
case 'u':
case 'U':
return RegisterType::UAV;
case 'b':
case 'B':
return RegisterType::CBuffer;
case 's':
case 'S':
return RegisterType::Sampler;
case 'c':
case 'C':
return RegisterType::C;
case 'i':
case 'I':
return RegisterType::I;
default:
return RegisterType::Invalid;
}
}

SemaHLSL::SemaHLSL(Sema &S) : SemaBase(S) {}

Expand Down Expand Up @@ -586,8 +628,7 @@ bool clang::CreateHLSLAttributedResourceType(
LocEnd = A->getRange().getEnd();
switch (A->getKind()) {
case attr::HLSLResourceClass: {
llvm::dxil::ResourceClass RC =
cast<HLSLResourceClassAttr>(A)->getResourceClass();
ResourceClass RC = cast<HLSLResourceClassAttr>(A)->getResourceClass();
if (HasResourceClass) {
S.Diag(A->getLocation(), ResAttrs.ResourceClass == RC
? diag::warn_duplicate_attribute_exact
Expand Down Expand Up @@ -672,7 +713,7 @@ bool SemaHLSL::handleResourceTypeAttr(const ParsedAttr &AL) {
SourceLocation ArgLoc = Loc->Loc;

// Validate resource class value
llvm::dxil::ResourceClass RC;
ResourceClass RC;
if (!HLSLResourceClassAttr::ConvertStrToResourceClass(Identifier, RC)) {
Diag(ArgLoc, diag::warn_attribute_type_not_supported)
<< "ResourceClass" << Identifier;
Expand Down Expand Up @@ -750,28 +791,6 @@ SemaHLSL::TakeLocForHLSLAttribute(const HLSLAttributedResourceType *RT) {
return LocInfo;
}

struct RegisterBindingFlags {
bool Resource = false;
bool UDT = false;
bool Other = false;
bool Basic = false;

bool SRV = false;
bool UAV = false;
bool CBV = false;
bool Sampler = false;

bool ContainsNumeric = false;
bool DefaultGlobals = false;

// used only when Resource == true
std::optional<llvm::dxil::ResourceClass> ResourceClass;
};

static bool isDeclaredWithinCOrTBuffer(const Decl *TheDecl) {
return TheDecl && isa<HLSLBufferDecl>(TheDecl->getDeclContext());
}

// get the record decl from a var decl that we expect
// represents a resource
static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
Expand All @@ -786,24 +805,6 @@ static CXXRecordDecl *getRecordDeclFromVarDecl(VarDecl *VD) {
return TheRecordDecl;
}

static void updateResourceClassFlagsFromDeclResourceClass(
RegisterBindingFlags &Flags, llvm::hlsl::ResourceClass DeclResourceClass) {
switch (DeclResourceClass) {
case llvm::hlsl::ResourceClass::SRV:
Flags.SRV = true;
break;
case llvm::hlsl::ResourceClass::UAV:
Flags.UAV = true;
break;
case llvm::hlsl::ResourceClass::CBuffer:
Flags.CBV = true;
break;
case llvm::hlsl::ResourceClass::Sampler:
Flags.Sampler = true;
break;
}
}

const HLSLAttributedResourceType *
findAttributedResourceTypeOnField(VarDecl *VD) {
assert(VD != nullptr && "expected VarDecl");
Expand All @@ -817,8 +818,10 @@ findAttributedResourceTypeOnField(VarDecl *VD) {
return nullptr;
}

static void updateResourceClassFlagsFromRecordType(RegisterBindingFlags &Flags,
const RecordType *RT) {
// Iterate over RecordType fields and return true if any of them matched the
// register type
static bool ContainsResourceForRegisterType(Sema &S, const RecordType *RT,
RegisterType RegType) {
llvm::SmallVector<const Type *> TypesToScan;
TypesToScan.emplace_back(RT);

Expand All @@ -827,8 +830,8 @@ static void updateResourceClassFlagsFromRecordType(RegisterBindingFlags &Flags,
while (T->isArrayType())
T = T->getArrayElementTypeNoTypeQual();
if (T->isIntegralOrEnumerationType() || T->isFloatingType()) {
Flags.ContainsNumeric = true;
continue;
if (RegType == RegisterType::C)
return true;
}
const RecordType *RT = T->getAs<RecordType>();
if (!RT)
Expand All @@ -839,100 +842,84 @@ static void updateResourceClassFlagsFromRecordType(RegisterBindingFlags &Flags,
const Type *FieldTy = FD->getType().getTypePtr();
if (const HLSLAttributedResourceType *AttrResType =
dyn_cast<HLSLAttributedResourceType>(FieldTy)) {
updateResourceClassFlagsFromDeclResourceClass(
Flags, AttrResType->getAttrs().ResourceClass);
continue;
ResourceClass RC = AttrResType->getAttrs().ResourceClass;
if (getRegisterType(RC) == RegType)
return true;
} else {
TypesToScan.emplace_back(FD->getType().getTypePtr());
}
TypesToScan.emplace_back(FD->getType().getTypePtr());
}
}
return false;
}

static RegisterBindingFlags HLSLFillRegisterBindingFlags(Sema &S,
Decl *TheDecl) {
RegisterBindingFlags Flags;
static void CheckContainsResourceForRegisterType(Sema &S,
SourceLocation &ArgLoc,
Decl *D, RegisterType RegType,
bool SpecifiedSpace) {
int RegTypeNum = static_cast<int>(RegType);

// check if the decl type is groupshared
if (TheDecl->hasAttr<HLSLGroupSharedAddressSpaceAttr>()) {
Flags.Other = true;
return Flags;
if (D->hasAttr<HLSLGroupSharedAddressSpaceAttr>()) {
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
return;
}

// Cbuffers and Tbuffers are HLSLBufferDecl types
if (HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(TheDecl)) {
Flags.Resource = true;
Flags.ResourceClass = CBufferOrTBuffer->isCBuffer()
? llvm::dxil::ResourceClass::CBuffer
: llvm::dxil::ResourceClass::SRV;
if (HLSLBufferDecl *CBufferOrTBuffer = dyn_cast<HLSLBufferDecl>(D)) {
ResourceClass RC = CBufferOrTBuffer->isCBuffer() ? ResourceClass::CBuffer
: ResourceClass::SRV;
if (RegType != getRegisterType(RC))
S.Diag(D->getLocation(), diag::err_hlsl_binding_type_mismatch)
<< RegTypeNum;
return;
}

// Samplers, UAVs, and SRVs are VarDecl types
else if (VarDecl *TheVarDecl = dyn_cast<VarDecl>(TheDecl)) {
if (const HLSLAttributedResourceType *AttrResType =
findAttributedResourceTypeOnField(TheVarDecl)) {
Flags.Resource = true;
Flags.ResourceClass = AttrResType->getAttrs().ResourceClass;
} else {
const clang::Type *TheBaseType = TheVarDecl->getType().getTypePtr();
while (TheBaseType->isArrayType())
TheBaseType = TheBaseType->getArrayElementTypeNoTypeQual();

if (TheBaseType->isArithmeticType()) {
Flags.Basic = true;
if (!isDeclaredWithinCOrTBuffer(TheDecl) &&
(TheBaseType->isIntegralType(S.getASTContext()) ||
TheBaseType->isFloatingType()))
Flags.DefaultGlobals = true;
} else if (TheBaseType->isRecordType()) {
Flags.UDT = true;
const RecordType *TheRecordTy = TheBaseType->getAs<RecordType>();
updateResourceClassFlagsFromRecordType(Flags, TheRecordTy);
} else
Flags.Other = true;
}
} else {
llvm_unreachable("expected be VarDecl or HLSLBufferDecl");
assert(isa<VarDecl>(D) && "D is expected to be VarDecl or HLSLBufferDecl");
VarDecl *VD = cast<VarDecl>(D);

// Resource
if (const HLSLAttributedResourceType *AttrResType =
findAttributedResourceTypeOnField(VD)) {
if (RegType != getRegisterType(AttrResType->getAttrs().ResourceClass))
S.Diag(D->getLocation(), diag::err_hlsl_binding_type_mismatch)
<< RegTypeNum;
return;
}
return Flags;
}

enum class RegisterType { SRV, UAV, CBuffer, Sampler, C, I, Invalid };
const clang::Type *Ty = VD->getType().getTypePtr();
while (Ty->isArrayType())
Ty = Ty->getArrayElementTypeNoTypeQual();

static RegisterType getRegisterType(llvm::dxil::ResourceClass RC) {
switch (RC) {
case llvm::dxil::ResourceClass::SRV:
return RegisterType::SRV;
case llvm::dxil::ResourceClass::UAV:
return RegisterType::UAV;
case llvm::dxil::ResourceClass::CBuffer:
return RegisterType::CBuffer;
case llvm::dxil::ResourceClass::Sampler:
return RegisterType::Sampler;
}
llvm_unreachable("unexpected ResourceClass value");
}
// Basic types
if (Ty->isArithmeticType()) {
bool DeclaredInCOrTBuffer = isa<HLSLBufferDecl>(D->getDeclContext());
if (SpecifiedSpace && !DeclaredInCOrTBuffer)
S.Diag(ArgLoc, diag::err_hlsl_space_on_global_constant);

static RegisterType getRegisterType(StringRef Slot) {
switch (Slot[0]) {
case 't':
case 'T':
return RegisterType::SRV;
case 'u':
case 'U':
return RegisterType::UAV;
case 'b':
case 'B':
return RegisterType::CBuffer;
case 's':
case 'S':
return RegisterType::Sampler;
case 'c':
case 'C':
return RegisterType::C;
case 'i':
case 'I':
return RegisterType::I;
default:
return RegisterType::Invalid;
if (!DeclaredInCOrTBuffer &&
(Ty->isIntegralType(S.getASTContext()) || Ty->isFloatingType())) {
// Default Globals
if (RegType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
else if (RegType != RegisterType::C)
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
} else {
if (RegType == RegisterType::C)
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
else
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
}
} else if (Ty->isRecordType()) {
// Class/struct types - walk the declaration and check each field and
// subclass
if (!ContainsResourceForRegisterType(S, Ty->getAs<RecordType>(), RegType))
S.Diag(D->getLocation(), diag::warn_hlsl_user_defined_type_missing_member)
<< RegTypeNum;
} else {
// Anything else is an error
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
}
}

Expand Down Expand Up @@ -969,76 +956,19 @@ static void ValidateMultipleRegisterAnnotations(Sema &S, Decl *TheDecl,
}

static void DiagnoseHLSLRegisterAttribute(Sema &S, SourceLocation &ArgLoc,
Decl *TheDecl, RegisterType RegType,
const bool SpecifiedSpace) {
Decl *D, RegisterType RegType,
bool SpecifiedSpace) {

// exactly one of these two types should be set
assert(((isa<VarDecl>(TheDecl) && !isa<HLSLBufferDecl>(TheDecl)) ||
(!isa<VarDecl>(TheDecl) && isa<HLSLBufferDecl>(TheDecl))) &&
assert(((isa<VarDecl>(D) && !isa<HLSLBufferDecl>(D)) ||
(!isa<VarDecl>(D) && isa<HLSLBufferDecl>(D))) &&
"expecting VarDecl or HLSLBufferDecl");

RegisterBindingFlags Flags = HLSLFillRegisterBindingFlags(S, TheDecl);
assert((int)Flags.Other + (int)Flags.Resource + (int)Flags.Basic +
(int)Flags.UDT ==
1 &&
"only one resource analysis result should be expected");

int RegTypeNum = static_cast<int>(RegType);

// first, if "other" is set, emit an error
if (Flags.Other) {
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
return;
}
// check if the declaration contains resource matching the register type
CheckContainsResourceForRegisterType(S, ArgLoc, D, RegType, SpecifiedSpace);

// next, if multiple register annotations exist, check that none conflict.
ValidateMultipleRegisterAnnotations(S, TheDecl, RegType);

// next, if resource is set, make sure the register type in the register
// annotation is compatible with the variable's resource type.
if (Flags.Resource) {
RegisterType ExpRegType = getRegisterType(Flags.ResourceClass.value());
if (RegType != ExpRegType) {
S.Diag(TheDecl->getLocation(), diag::err_hlsl_binding_type_mismatch)
<< RegTypeNum;
}

return;
}

// next, handle diagnostics for when the "basic" flag is set
if (Flags.Basic) {
if (SpecifiedSpace && !isDeclaredWithinCOrTBuffer(TheDecl))
S.Diag(ArgLoc, diag::err_hlsl_space_on_global_constant);

if (Flags.DefaultGlobals) {
if (RegType == RegisterType::CBuffer)
S.Diag(ArgLoc, diag::warn_hlsl_deprecated_register_type_b);
else if (RegType != RegisterType::C)
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;
return;
}

if (RegType == RegisterType::C)
S.Diag(ArgLoc, diag::warn_hlsl_register_type_c_packoffset);
else
S.Diag(ArgLoc, diag::err_hlsl_binding_type_mismatch) << RegTypeNum;

return;
}

// finally, we handle the udt case
if (Flags.UDT) {
const bool ExpectedRegisterTypesForUDT[] = {
Flags.SRV, Flags.UAV, Flags.CBV, Flags.Sampler, Flags.ContainsNumeric};
assert((size_t)RegTypeNum < std::size(ExpectedRegisterTypesForUDT) &&
"regType has unexpected value");

if (!ExpectedRegisterTypesForUDT[RegTypeNum])
S.Diag(TheDecl->getLocation(),
diag::warn_hlsl_user_defined_type_missing_member)
<< RegTypeNum;
}
ValidateMultipleRegisterAnnotations(S, D, RegType);
}

void SemaHLSL::handleResourceBindingAttr(Decl *TheDecl, const ParsedAttr &AL) {
Expand Down
55 changes: 18 additions & 37 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5508,50 +5508,31 @@ bool Sema::CheckTemplateArgumentList(
}

// Check whether we have a default argument.
TemplateArgumentLoc Arg;
bool HasDefaultArg;

// Retrieve the default template argument from the template
// parameter. For each kind of template parameter, we substitute the
// template arguments provided thus far and any "outer" template arguments
// (when the template parameter was part of a nested template) into
// the default argument.
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param)) {
if (!hasReachableDefaultArgument(TTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP,
TemplateArgumentLoc Arg = SubstDefaultTemplateArgumentIfAvailable(
Template, TemplateLoc, RAngleLoc, *Param, SugaredConverted,
CanonicalConverted, HasDefaultArg);

if (Arg.getArgument().isNull()) {
if (!HasDefaultArg) {
if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(*Param))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TTP,
NewArgs);
if (NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(*Param))
return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP,
NewArgs);
return diagnoseMissingArgument(*this, TemplateLoc, Template,
cast<TemplateTemplateParmDecl>(*Param),
NewArgs);

if (SubstDefaultTemplateArgument(*this, Template, TemplateLoc, RAngleLoc,
TTP, SugaredConverted,
CanonicalConverted, Arg))
return true;
} else if (NonTypeTemplateParmDecl *NTTP
= dyn_cast<NonTypeTemplateParmDecl>(*Param)) {
if (!hasReachableDefaultArgument(NTTP))
return diagnoseMissingArgument(*this, TemplateLoc, Template, NTTP,
NewArgs);

if (SubstDefaultTemplateArgument(*this, Template, TemplateLoc, RAngleLoc,
NTTP, SugaredConverted,
CanonicalConverted, Arg))
return true;
} else {
TemplateTemplateParmDecl *TempParm
= cast<TemplateTemplateParmDecl>(*Param);

if (!hasReachableDefaultArgument(TempParm))
return diagnoseMissingArgument(*this, TemplateLoc, Template, TempParm,
NewArgs);

NestedNameSpecifierLoc QualifierLoc;
TemplateName Name = SubstDefaultTemplateArgument(
*this, Template, TemplateLoc, RAngleLoc, TempParm, SugaredConverted,
CanonicalConverted, QualifierLoc);
if (Name.isNull())
return true;

Arg = TemplateArgumentLoc(
Context, TemplateArgument(Name), QualifierLoc,
TempParm->getDefaultArgument().getTemplateNameLoc());
}
return true;
}

// Introduce an instantiation record that describes where we are using
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5505,8 +5505,11 @@ static TemplateDeductionResult CheckDeductionConsistency(
Sema::ArgumentPackSubstitutionIndexRAII PackIndex(
S, ArgIdx != -1 ? ::getPackIndexForParam(S, FTD, MLTAL, ArgIdx) : -1);
bool IsIncompleteSubstitution = false;
QualType InstP = S.SubstType(P, MLTAL, FTD->getLocation(), FTD->getDeclName(),
&IsIncompleteSubstitution);
// FIXME: A substitution can be incomplete on a non-structural part of the
// type. Use the canonical type for now, until the TemplateInstantiator can
// deal with that.
QualType InstP = S.SubstType(P.getCanonicalType(), MLTAL, FTD->getLocation(),
FTD->getDeclName(), &IsIncompleteSubstitution);
if (InstP.isNull() && !IsIncompleteSubstitution)
return TemplateDeductionResult::SubstitutionFailure;
if (!CheckConsistency)
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1673,6 +1673,10 @@ namespace {
}

ExprResult TransformLambdaExpr(LambdaExpr *E) {
// Do not rebuild lambdas to avoid creating a new type.
// Lambdas have already been processed inside their eval contexts.
if (SemaRef.RebuildingImmediateInvocation)
return E;
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);

Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6294,9 +6294,13 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,

if (!SubstRecord) {
// T can be a dependent TemplateSpecializationType when performing a
// substitution for building a deduction guide.
assert(CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// substitution for building a deduction guide or for template
// argument deduction in the process of rebuilding immediate
// expressions. (Because the default argument that involves a lambda
// is untransformed and thus could be dependent at this point.)
assert(SemaRef.RebuildingImmediateInvocation ||
CodeSynthesisContexts.back().Kind ==
CodeSynthesisContext::BuildingDeductionGuides);
// Return a nullptr as a sentinel value, we handle it properly in
// the TemplateInstantiator::TransformInjectedClassNameType
// override, which we transform it to a TemplateSpecializationType.
Expand Down
15 changes: 5 additions & 10 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M,

void ASTReader::Error(StringRef Msg) const {
Error(diag::err_fe_pch_malformed, Msg);
if (PP.getLangOpts().Modules && !Diags.isDiagnosticInFlight() &&
if (PP.getLangOpts().Modules &&
!PP.getHeaderSearchInfo().getModuleCachePath().empty()) {
Diag(diag::note_module_cache_path)
<< PP.getHeaderSearchInfo().getModuleCachePath();
Expand All @@ -1391,10 +1391,7 @@ void ASTReader::Error(StringRef Msg) const {

void ASTReader::Error(unsigned DiagID, StringRef Arg1, StringRef Arg2,
StringRef Arg3) const {
if (Diags.isDiagnosticInFlight())
Diags.SetDelayedDiagnostic(DiagID, Arg1, Arg2, Arg3);
else
Diag(DiagID) << Arg1 << Arg2 << Arg3;
Diag(DiagID) << Arg1 << Arg2 << Arg3;
}

void ASTReader::Error(llvm::Error &&Err) const {
Expand Down Expand Up @@ -2713,7 +2710,7 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) {

// For an overridden file, there is nothing to validate.
if (!Overridden && FileChange.Kind != Change::None) {
if (Complain && !Diags.isDiagnosticInFlight()) {
if (Complain) {
// Build a list of the PCH imports that got us here (in reverse).
SmallVector<ModuleFile *, 4> ImportStack(1, &F);
while (!ImportStack.back()->ImportedBy.empty())
Expand Down Expand Up @@ -3689,10 +3686,8 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
SourceMgr.AllocateLoadedSLocEntries(F.LocalNumSLocEntries,
SLocSpaceSize);
if (!F.SLocEntryBaseID) {
if (!Diags.isDiagnosticInFlight()) {
Diags.Report(SourceLocation(), diag::remark_sloc_usage);
SourceMgr.noteSLocAddressSpaceUsage(Diags);
}
Diags.Report(SourceLocation(), diag::remark_sloc_usage);
SourceMgr.noteSLocAddressSpaceUsage(Diags);
return llvm::createStringError(std::errc::invalid_argument,
"ran out of source locations");
}
Expand Down
50 changes: 45 additions & 5 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,46 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
return StateNotNull;
}

namespace {
class StreamClosedVisitor final : public BugReporterVisitor {
const SymbolRef StreamSym;
bool Satisfied = false;

public:
explicit StreamClosedVisitor(SymbolRef StreamSym) : StreamSym(StreamSym) {}

static void *getTag() {
static int Tag = 0;
return &Tag;
}

void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.AddPointer(getTag());
ID.AddPointer(StreamSym);
}

PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override {
if (Satisfied)
return nullptr;
const StreamState *PredSS =
N->getFirstPred()->getState()->get<StreamMap>(StreamSym);
if (PredSS && PredSS->isClosed())
return nullptr;

const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
Satisfied = true;
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
llvm::StringLiteral Msg = "Stream is closed here";
return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg);
}
};
} // namespace

ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
CheckerContext &C,
ProgramStateRef State) const {
Expand All @@ -1849,11 +1889,11 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
if (SS->isClosed()) {
// Using a stream pointer after 'fclose' causes undefined behavior
// according to cppreference.com .
ExplodedNode *N = C.generateErrorNode();
if (N) {
C.emitReport(std::make_unique<PathSensitiveBugReport>(
BT_UseAfterClose,
"Stream might be already closed. Causes undefined behaviour.", N));
if (ExplodedNode *N = C.generateErrorNode()) {
auto R = std::make_unique<PathSensitiveBugReport>(
BT_UseAfterClose, "Use of a stream that might be already closed", N);
R->addVisitor<StreamClosedVisitor>(Sym);
C.emitReport(std::move(R));
return nullptr;
}

Expand Down
19 changes: 16 additions & 3 deletions clang/test/AST/ast-dump-concepts.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++2a -ast-dump -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++2a -ast-dump -ast-dump-decl-types -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s

// Test with serialization:
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++20 -triple x86_64-unknown-unknown -include-pch %t \
// RUN: -ast-dump-all -ast-dump-filter Foo /dev/null \
// RUN: -ast-dump-all -ast-dump-decl-types -ast-dump-filter Foo /dev/null \
// RUN: | FileCheck --strict-whitespace %s

template <typename T>
Expand Down Expand Up @@ -56,6 +56,9 @@ struct Foo {
// CHECK: CXXFoldExpr {{.*}} <col:13, col:34>
template <variadic_concept<int>... Ts>
Foo();

// CHECK:InjectedClassNameType
// CHECK-NEXT: CXXRecord {{.*}} 'Foo'
};

namespace GH82628 {
Expand All @@ -75,20 +78,28 @@ template <typename T>
concept Foo = C<T>;

// CHECK: TemplateTypeParmDecl {{.*}} Concept {{.*}} 'C' (UsingShadow {{.*}} 'C')
// CHECK: QualType
// CHECK-NEXT: `-BuiltinType {{.*}} 'bool'
template <C T>
constexpr bool FooVar = false;

// CHECK: ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
// CHECK: QualType
// CHECK-NEXT: `-BuiltinType {{.*}} 'bool'
template <typename T> requires C<T>
constexpr bool FooVar2 = true;

// CHECK: SimpleRequirement
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
// CHECK: QualType
// CHECK-NEXT: `-BuiltinType {{.*}} 'bool'
template <typename T> requires requires (T) { C<T>; }
constexpr bool FooVar3 = true;

// CHECK: NonTypeTemplateParmDecl
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
// CHECK: QualType
// CHECK-NEXT: `-BuiltinType {{.*}} 'bool'
template <C auto T>
constexpr bool FooVar4 = bool(T());

Expand All @@ -97,7 +108,9 @@ constexpr bool FooVar4 = bool(T());
// CHECK: NonTypeTemplateParmDecl {{.*}} depth 0 index 1 U
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
// CHECK: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'C' (UsingShadow {{.*}} 'C') depth 0 index 2 V:auto

// CHECK: FunctionProtoType
// CHECK: `-Concept {{.*}} 'C'
// CHECK: `-TemplateTypeParm {{.*}} 'V:auto'
template <C... T, C auto U>
auto FooFunc(C auto V) -> C decltype(auto) {
// FIXME: TypeLocs inside of the function body cannot be dumped via -ast-dump for now.
Expand Down
22 changes: 11 additions & 11 deletions clang/test/Analysis/stream-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void error_fread(void) {
}
}
fclose(F);
Ret = fread(Buf, 1, 10, F); // expected-warning {{Stream might be already closed}}
Ret = fread(Buf, 1, 10, F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fwrite(void) {
Expand All @@ -113,7 +113,7 @@ void error_fwrite(void) {
fwrite(0, 1, 10, F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}}
Ret = fwrite(0, 1, 10, F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fgetc(void) {
Expand All @@ -135,7 +135,7 @@ void error_fgetc(void) {
}
}
fclose(F);
fgetc(F); // expected-warning {{Stream might be already closed}}
fgetc(F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fgets(void) {
Expand All @@ -158,7 +158,7 @@ void error_fgets(void) {
}
}
fclose(F);
fgets(Buf, sizeof(Buf), F); // expected-warning {{Stream might be already closed}}
fgets(Buf, sizeof(Buf), F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fputc(int fd) {
Expand All @@ -176,7 +176,7 @@ void error_fputc(int fd) {
fputc('Y', F); // no-warning
}
fclose(F);
fputc('A', F); // expected-warning {{Stream might be already closed}}
fputc('A', F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fputs(void) {
Expand All @@ -194,7 +194,7 @@ void error_fputs(void) {
fputs("QWD", F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
fputs("ABC", F); // expected-warning {{Stream might be already closed}}
fputs("ABC", F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fprintf(void) {
Expand All @@ -211,7 +211,7 @@ void error_fprintf(void) {
fprintf(F, "bbb"); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
fprintf(F, "ccc"); // expected-warning {{Stream might be already closed}}
fprintf(F, "ccc"); // expected-warning {{Use of a stream that might be already closed}}
}

void error_fscanf(int *A) {
Expand All @@ -236,7 +236,7 @@ void error_fscanf(int *A) {
}
}
fclose(F);
fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}}
fscanf(F, "ccc"); // expected-warning {{Use of a stream that might be already closed}}
}

void error_ungetc(int TestIndeterminate) {
Expand All @@ -256,7 +256,7 @@ void error_ungetc(int TestIndeterminate) {
ungetc('X', F); // expected-warning {{might be 'indeterminate'}}
}
fclose(F);
ungetc('A', F); // expected-warning {{Stream might be already closed}}
ungetc('A', F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_getdelim(char *P, size_t Sz) {
Expand All @@ -278,7 +278,7 @@ void error_getdelim(char *P, size_t Sz) {
}
}
fclose(F);
getdelim(&P, &Sz, '\n', F); // expected-warning {{Stream might be already closed}}
getdelim(&P, &Sz, '\n', F); // expected-warning {{Use of a stream that might be already closed}}
}

void error_getline(char *P, size_t Sz) {
Expand All @@ -300,7 +300,7 @@ void error_getline(char *P, size_t Sz) {
}
}
fclose(F);
getline(&P, &Sz, F); // expected-warning {{Stream might be already closed}}
getline(&P, &Sz, F); // expected-warning {{Use of a stream that might be already closed}}
}

void write_after_eof_is_allowed(void) {
Expand Down
9 changes: 9 additions & 0 deletions clang/test/Analysis/stream-note.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,12 @@ void error_fseek_read_eof(void) {
fgetc(F); // no warning
fclose(F);
}

void check_note_at_use_after_close(void) {
FILE *F = tmpfile();
if (!F) // expected-note {{'F' is non-null}} expected-note {{Taking false branch}}
return;
fclose(F); // expected-note {{Stream is closed here}}
rewind(F); // expected-warning {{Use of a stream that might be already closed}}
// expected-note@-1 {{Use of a stream that might be already closed}}
}
10 changes: 5 additions & 5 deletions clang/test/Analysis/stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ void f_double_close(void) {
if (!p)
return;
fclose(p);
fclose(p); // expected-warning {{Stream might be already closed}}
fclose(p); // expected-warning {{Use of a stream that might be already closed}}
}

void f_double_close_alias(void) {
Expand All @@ -194,15 +194,15 @@ void f_double_close_alias(void) {
return;
FILE *p2 = p1;
fclose(p1);
fclose(p2); // expected-warning {{Stream might be already closed}}
fclose(p2); // expected-warning {{Use of a stream that might be already closed}}
}

void f_use_after_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fclose(p);
clearerr(p); // expected-warning {{Stream might be already closed}}
clearerr(p); // expected-warning {{Use of a stream that might be already closed}}
}

void f_open_after_close(void) {
Expand Down Expand Up @@ -266,7 +266,7 @@ void check_freopen_2(void) {
if (f2) {
// Check if f1 and f2 point to the same stream.
fclose(f1);
fclose(f2); // expected-warning {{Stream might be already closed.}}
fclose(f2); // expected-warning {{Use of a stream that might be already closed}}
} else {
// Reopen failed.
// f1 is non-NULL but points to a possibly invalid stream.
Expand Down Expand Up @@ -370,7 +370,7 @@ void fflush_after_fclose(void) {
if ((Ret = fflush(F)) != 0)
clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
fclose(F);
fflush(F); // expected-warning {{Stream might be already closed}}
fflush(F); // expected-warning {{Use of a stream that might be already closed}}
}

void fflush_on_open_failed_stream(void) {
Expand Down
93 changes: 93 additions & 0 deletions clang/test/C/C23/n3030.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// RUN: %clang_cc1 -verify -triple x86_64-unknown-linux-gnu -fsyntax-only -std=c23 %s -pedantic -Wall

#include <limits.h>

enum us : unsigned short {
us_max = USHRT_MAX,
us_violation, // expected-error {{enumerator value 65536 is not representable in the underlying type 'unsigned short'}}
us_violation_2 = us_max + 1, // expected-error {{enumerator value is not representable in the underlying type 'unsigned short'}}
us_wrap_around_to_zero = (unsigned short)(USHRT_MAX + 1) /* Okay: conversion
done in constant expression before conversion to
underlying type: unsigned semantics okay. */
};

enum ui : unsigned int {
ui_max = UINT_MAX,
ui_violation, // expected-error {{enumerator value 4294967296 is not representable in the underlying type 'unsigned int'}}
ui_no_violation = ui_max + 1,
ui_wrap_around_to_zero = (unsigned int)(UINT_MAX + 1)
};

enum E1 : short;
enum E2 : short; // expected-note {{previous}}
enum E3; // expected-warning {{ISO C forbids forward references to 'enum' types}}
enum E4 : unsigned long long;

enum E1 : short { m11, m12 };
enum E1 x = m11;

enum E2 : long { // expected-error {{enumeration redeclared with different underlying type 'long' (was 'short')}}
m21,
m22
};

enum E3 { // expected-note {{definition of 'enum E3' is not complete until the closing '}'}}
// expected-note@-1 {{previous}}
m31,
m32,
m33 = sizeof(enum E3) // expected-error {{invalid application of 'sizeof' to an incomplete type 'enum E3'}}
};
enum E3 : int; // expected-error {{enumeration previously declared with nonfixed underlying type}}

enum E4 : unsigned long long {
m40 = sizeof(enum E4),
m41 = ULLONG_MAX,
m42 // expected-error {{enumerator value 18446744073709551616 is not representable in the underlying type 'unsigned long long'}}
};

enum E5 y; // expected-error {{tentative definition has type 'enum E5' that is never completed}}
// expected-warning@-1 {{ISO C forbids forward references to 'enum' types}}
// expected-note@-2 {{forward declaration of 'enum E5'}}
enum E6 : long int z; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
enum E7 : long int = 0; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
// expected-error@-1 {{expected identifier or '('}}

enum underlying : unsigned char { b0 };

constexpr int a = _Generic(b0, int: 2, unsigned char: 1, default: 0);
constexpr int b = _Generic((enum underlying)b0, int: 2, unsigned char: 1, default: 0);
static_assert(a == 1);
static_assert(b == 1);

void f1(enum a : long b); // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
// expected-warning@-1 {{declaration of 'enum a' will not be visible outside of this function}}
void f2(enum c : long{x} d); // expected-warning {{declaration of 'enum c' will not be visible outside of this function}}
enum e : int f3(); // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}

typedef enum t u; // expected-warning {{ISO C forbids forward references to 'enum' types}}
typedef enum v : short W; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
typedef enum q : short { s } R;

struct s1 {
int x;
enum e:int : 1; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
int y;
};

enum forward; // expected-warning {{ISO C forbids forward references to 'enum' types}}
extern enum forward fwd_val0; /* Constraint violation: incomplete type */
extern enum forward *fwd_ptr0; // expected-note {{previous}}
extern int
*fwd_ptr0; // expected-error {{redeclaration of 'fwd_ptr0' with a different type: 'int *' vs 'enum forward *'}}

enum forward1 : int;
extern enum forward1 fwd_val1;
extern int fwd_val1;
extern enum forward1 *fwd_ptr1;
extern int *fwd_ptr1;

enum ee1 : short;
enum e : short f = 0; // expected-error {{non-defining declaration of enumeration with a fixed underlying type is only permitted as a standalone declaration; missing list of enumerators?}}
enum g : short { yyy } h = yyy;

enum ee2 : typeof ((enum ee3 : short { A })0, (short)0);
1 change: 0 additions & 1 deletion clang/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ list(APPEND CLANG_TEST_DEPS
clang-tblgen
clang-offload-bundler
clang-import-test
clang-rename
clang-refactor
clang-diff
clang-installapi
Expand Down
17 changes: 17 additions & 0 deletions clang/test/CodeGen/X86/avx512copy-builtins.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %clang_cc1 %s -flax-vector-conversions=none -ffreestanding -triple=x86_64-unknown-unknown -target-feature +avx10.2-512 \
// RUN: -emit-llvm -o - -Wall -Werror -pedantic -Wno-gnu-statement-expression | FileCheck %s

#include <immintrin.h>
#include <stddef.h>

__m128i test_mm_move_epi32(__m128i A) {
// CHECK-LABEL: test_mm_move_epi32
// CHECK: shufflevector <4 x i32> %{{.*}}, <4 x i32> %{{.*}}, <4 x i32> <i32 0, i32 4, i32 4, i32 4>
return _mm_move_epi32(A);
}

__m128i test_mm_move_epi16(__m128i A) {
// CHECK-LABEL: test_mm_move_epi16
// CHECK: shufflevector <8 x i16> %{{.*}}, <8 x i16> %{{.*}}, <8 x i32> <i32 0, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8, i32 8>
return _mm_move_epi16(A);
}
37 changes: 37 additions & 0 deletions clang/test/CodeGen/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,43 @@ void test_builtin_elementwise_log2(float f1, float f2, double d1, double d2,
vf2 = __builtin_elementwise_log2(vf1);
}

void test_builtin_elementwise_popcount(si8 vi1, si8 vi2,
long long int i1, long long int i2, short si,
_BitInt(31) bi1, _BitInt(31) bi2) {


// CHECK: [[I1:%.+]] = load i64, ptr %i1.addr, align 8
// CHECK-NEXT: call i64 @llvm.ctpop.i64(i64 [[I1]])
i2 = __builtin_elementwise_popcount(i1);

// CHECK: [[VI1:%.+]] = load <8 x i16>, ptr %vi1.addr, align 16
// CHECK-NEXT: call <8 x i16> @llvm.ctpop.v8i16(<8 x i16> [[VI1]])
vi2 = __builtin_elementwise_popcount(vi1);

// CHECK: [[CVI2:%.+]] = load <8 x i16>, ptr %cvi2, align 16
// CHECK-NEXT: call <8 x i16> @llvm.ctpop.v8i16(<8 x i16> [[CVI2]])
const si8 cvi2 = vi2;
vi2 = __builtin_elementwise_popcount(cvi2);

// CHECK: [[BI1:%.+]] = load i32, ptr %bi1.addr, align 4
// CHECK-NEXT: [[LOADEDV:%.+]] = trunc i32 [[BI1]] to i31
// CHECK-NEXT: call i31 @llvm.ctpop.i31(i31 [[LOADEDV]])
bi2 = __builtin_elementwise_popcount(bi1);

// CHECK: [[IA1:%.+]] = load i32, ptr addrspace(1) @int_as_one, align 4
// CHECK-NEXT: call i32 @llvm.ctpop.i32(i32 [[IA1]])
b = __builtin_elementwise_popcount(int_as_one);

// CHECK: call i32 @llvm.ctpop.i32(i32 -10)
b = __builtin_elementwise_popcount(-10);

// CHECK: [[SI:%.+]] = load i16, ptr %si.addr, align 2
// CHECK-NEXT: [[SI_EXT:%.+]] = sext i16 [[SI]] to i32
// CHECK-NEXT: [[RES:%.+]] = call i32 @llvm.ctpop.i32(i32 [[SI_EXT]])
// CHECK-NEXT: = trunc i32 [[RES]] to i16
si = __builtin_elementwise_popcount(si);
}

void test_builtin_elementwise_pow(float f1, float f2, double d1, double d2,
float4 vf1, float4 vf2) {

Expand Down
40 changes: 40 additions & 0 deletions clang/test/CodeGenCoroutines/coro-await-elidable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,44 @@ Task<int> nonelidable() {
co_return 1;
}

// CHECK-LABEL: define{{.*}} @_Z8addTasksO4TaskIiES1_{{.*}} {
Task<int> addTasks([[clang::coro_await_elidable_argument]] Task<int> &&t1, Task<int> &&t2) {
int i1 = co_await t1;
int i2 = co_await t2;
co_return i1 + i2;
}

// CHECK-LABEL: define{{.*}} @_Z10returnSamei{{.*}} {
Task<int> returnSame(int i) {
co_return i;
}

// CHECK-LABEL: define{{.*}} @_Z21elidableWithMustAwaitv{{.*}} {
Task<int> elidableWithMustAwait() {
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 2) #[[ELIDE_SAFE]]
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 3){{$}}
co_return co_await addTasks(returnSame(2), returnSame(3));
}

template <typename... Args>
Task<int> sumAll([[clang::coro_await_elidable_argument]] Args && ... tasks);

// CHECK-LABEL: define{{.*}} @_Z16elidableWithPackv{{.*}} {
Task<int> elidableWithPack() {
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 1){{$}}
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 2) #[[ELIDE_SAFE]]
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 3) #[[ELIDE_SAFE]]
auto t = returnSame(1);
co_return co_await sumAll(t, returnSame(2), returnSame(3));
}


// CHECK-LABEL: define{{.*}} @_Z25elidableWithPackRecursivev{{.*}} {
Task<int> elidableWithPackRecursive() {
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 1) #[[ELIDE_SAFE]]
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 2){{$}}
// CHECK: call void @_Z10returnSamei(ptr {{.*}}, i32 noundef 3) #[[ELIDE_SAFE]]
co_return co_await sumAll(addTasks(returnSame(1), returnSame(2)), returnSame(3));
}

// CHECK: attributes #[[ELIDE_SAFE]] = { coro_elide_safe }
80 changes: 80 additions & 0 deletions clang/test/CodeGenHLSL/builtins/countbits.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
// RUN: -emit-llvm -disable-llvm-passes -O3 -o - | FileCheck %s

#ifdef __HLSL_ENABLE_16_BIT
// CHECK-LABEL: test_countbits_ushort
// CHECK: call i16 @llvm.ctpop.i16
uint16_t test_countbits_ushort(uint16_t p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_ushort2
// CHECK: call <2 x i16> @llvm.ctpop.v2i16
uint16_t2 test_countbits_ushort2(uint16_t2 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_ushort3
// CHECK: call <3 x i16> @llvm.ctpop.v3i16
uint16_t3 test_countbits_ushort3(uint16_t3 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_ushort4
// CHECK: call <4 x i16> @llvm.ctpop.v4i16
uint16_t4 test_countbits_ushort4(uint16_t4 p0)
{
return countbits(p0);
}
#endif

// CHECK-LABEL: test_countbits_uint
// CHECK: call i32 @llvm.ctpop.i32
int test_countbits_uint(uint p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_uint2
// CHECK: call <2 x i32> @llvm.ctpop.v2i32
uint2 test_countbits_uint2(uint2 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_uint3
// CHECK: call <3 x i32> @llvm.ctpop.v3i32
uint3 test_countbits_uint3(uint3 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_uint4
// CHECK: call <4 x i32> @llvm.ctpop.v4i32
uint4 test_countbits_uint4(uint4 p0)
{
return countbits(p0);
}

// CHECK-LABEL: test_countbits_long
// CHECK: call i64 @llvm.ctpop.i64
uint64_t test_countbits_long(uint64_t p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_long2
// CHECK: call <2 x i64> @llvm.ctpop.v2i64
uint64_t2 test_countbits_long2(uint64_t2 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_long3
// CHECK: call <3 x i64> @llvm.ctpop.v3i64
uint64_t3 test_countbits_long3(uint64_t3 p0)
{
return countbits(p0);
}
// CHECK-LABEL: test_countbits_long4
// CHECK: call <4 x i64> @llvm.ctpop.v4i64
uint64_t4 test_countbits_long4(uint64_t4 p0)
{
return countbits(p0);
}
2 changes: 1 addition & 1 deletion clang/test/Driver/openmp-offload-gpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,4 +377,4 @@
// RUN: --cuda-path=%S/Inputs/CUDA_102/usr/local/cuda \
// RUN: --offload-arch=sm_52 -nogpulibc -nogpuinc %s 2>&1 \
// RUN: | FileCheck --check-prefix=LIBC-GPU %s
// LIBC-GPU: clang-linker-wrapper{{.*}}"--device-compiler=-nolibc"
// LIBC-GPU-NOT: clang-linker-wrapper{{.*}}"--device-linker"
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
// CHECK-NEXT: ConsumableSetOnRead (SubjectMatchRule_record)
// CHECK-NEXT: Convergent (SubjectMatchRule_function)
// CHECK-NEXT: CoroAwaitElidable (SubjectMatchRule_record)
// CHECK-NEXT: CoroAwaitElidableArgument (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: CoroDisableLifetimeBound (SubjectMatchRule_function)
// CHECK-NEXT: CoroLifetimeBound (SubjectMatchRule_record)
// CHECK-NEXT: CoroOnlyDestroyWhenComplete (SubjectMatchRule_record)
Expand Down
Loading