6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^

- New :doc:`readability-use-std-min-max
<clang-tidy/checks/readability/use-std-min-max>` check.

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.

New check aliases
^^^^^^^^^^^^^^^^^

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ Clang-Tidy Checks
:doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes"
:doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
:doc:`readability-use-anyofallof <readability/use-anyofallof>`,
:doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
:doc:`zircon-temporary-objects <zircon/temporary-objects>`,


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.. title:: clang-tidy - readability-use-std-min-max

readability-use-std-min-max
===========================

Replaces certain conditional statements with equivalent calls to
``std::min`` or ``std::max``.
Note: This may impact performance in critical code due to potential
additional stores compared to the original if statement.

Before:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
if (a < b)
a = b;
}


After:

.. code-block:: c++

void foo() {
int a = 2, b = 3;
a = std::max(a, b);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// RUN: %check_clang_tidy -std=c++11-or-later %s readability-use-std-min-max %t -- -- -fno-delayed-template-parsing
#define MY_MACRO_MIN(a, b) ((a) < (b) ? (a) : (b))

constexpr int myConstexprMin(int a, int b) {
return a < b ? a : b;
}

constexpr int myConstexprMax(int a, int b) {
return a > b ? a : b;
}

#define MY_IF_MACRO(condition, statement) \
if (condition) { \
statement \
}

class MyClass {
public:
int member1;
int member2;
};

template<typename T>

void foo(T value7) {
int value1,value2,value3;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value2);
if (value1 < value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value1, value2);
if (value1 < value2)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value2, value1);
if (value2 > value1)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value2, value1);
if (value2 > value1)
value1 = value2;

// No suggestion needed here
if (value1 == value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max<int>(value1, value4);
short value4;
if(value1<value4)
value1=value4;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value3 = std::min(value1+value2, value3);
if(value1+value2<value3)
value3 = value1+value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, myConstexprMin(value2, value3));
if (value1 < myConstexprMin(value2, value3))
value1 = myConstexprMin(value2, value3);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::min(value1, myConstexprMax(value2, value3));
if (value1 > myConstexprMax(value2, value3))
value1 = myConstexprMax(value2, value3);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value1, value2);
if (value1 <= value2)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value2);
if (value1 <= value2)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value2, value1);
if (value2 >= value1)
value1 = value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value2 = std::min(value2, value1);
if (value2 >= value1)
value2 = value1;

// CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max(obj.member1, obj.member2);
MyClass obj;
if (obj.member1 < obj.member2)
obj.member1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member1, obj.member2);
if (obj.member1 < obj.member2)
obj.member2 = obj.member1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member2, obj.member1);
if (obj.member2 > obj.member1)
obj.member2 = obj.member1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max(obj.member2, obj.member1);
if (obj.member2 > obj.member1)
obj.member1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: obj.member1 = std::max<int>(obj.member1, value4);
if (obj.member1 < value4)
obj.member1 = value4;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value3 = std::min(obj.member1 + value2, value3);
if (obj.member1 + value2 < value3)
value3 = obj.member1 + value2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(value1, obj.member2);
if (value1 <= obj.member2)
obj.member2 = value1;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, obj.member2);
if (value1 <= obj.member2)
value1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(obj.member2, value1);
if (obj.member2 >= value1)
value1 = obj.member2;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::min` instead of `>=` [readability-use-std-min-max]
// CHECK-FIXES: obj.member2 = std::min(obj.member2, value1);
if (obj.member2 >= value1)
obj.member2 = value1;

// No suggestion needed here
if (MY_MACRO_MIN(value1, value2) < value3)
value3 = MY_MACRO_MIN(value1, value2);

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value4 = std::max<int>(value4, value2);
if (value4 < value2){
value4 = value2;
}

// No suggestion needed here
if(value1 < value2)
value2 = value1;
else
value2 = value3;

// No suggestion needed here
if(value1<value2){
value2 = value1;
}
else{
value2 = value3;
}

// No suggestion needed here
if(value1<value2){
value2 = value1;
int res = value1 + value2;
}

// No suggestion needed here
MY_IF_MACRO(value1 < value2, value1 = value2;)

// No suggestion needed here
if(value1<value2){
value1 = value2;
}
else if(value1>value2){
value2 = value1;
}

// CHECK-MESSAGES: :[[@LINE+3]]:5: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max(value1, value3);
if(value1 == value2){
if(value1<value3)
value1 = value3;
}

// CHECK-MESSAGES: :[[@LINE+5]]:7: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value1 = std::max<int>(value1, value4);
if(value1 == value2){
if(value2 == value3){
value3+=1;
if(value1<value4){
value1 = value4;
}
}
else if(value3>value2){
value2 = value3;
}
}

// CHECK-MESSAGES: :[[@LINE+4]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value6 = std::min<unsigned int>(value5, value6);
unsigned int value5;
unsigned char value6;
if(value5<value6){
value6 = value5;
}

//No suggestion needed here
if(value7<value6){
value6 = value7;
}

//CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
//CHECK-FIXES: value1 = std::min(value8, value1);
const int value8 = 5;
if(value8<value1)
value1 = value8;

//CHECK-MESSAGES: :[[@LINE+3]]:3: warning: use `std::min` instead of `<` [readability-use-std-min-max]
//CHECK-FIXES: value1 = std::min(value9, value1);
volatile int value9 = 6;
if(value9<value1)
value1 = value9;
}

using my_size = unsigned long long;

template<typename T>
struct MyVector
{
using size_type = my_size;
size_type size() const;
};

void testVectorSizeType() {
MyVector<int> v;
unsigned int value;

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `>` [readability-use-std-min-max]
// CHECK-FIXES: value = std::max<my_size>(v.size(), value);
if (v.size() > value)
value = v.size();

// CHECK-MESSAGES: :[[@LINE+2]]:3: warning: use `std::max` instead of `<` [readability-use-std-min-max]
// CHECK-FIXES: value = std::max<my_size>(value, v.size());
if (value < v.size())
value = v.size();
}
18 changes: 18 additions & 0 deletions clang/docs/ClangLinkerWrapper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,30 @@ only for the linker wrapper will be forwarded to the wrapped linker job.
--pass-remarks=<value> Pass remarks for LTO
--print-wrapped-module Print the wrapped module's IR for testing
--ptxas-arg=<value> Argument to pass to the 'ptxas' invocation
--relocatable Link device code to create a relocatable offloading application
--save-temps Save intermediate results
--sysroot<value> Set the system root
--verbose Verbose output from tools
--v Display the version number and exit
-- The separator for the wrapped linker arguments
Relocatable Linking
===================

The ``clang-linker-wrapper`` handles linking embedded device code and then
registering it with the appropriate runtime. Normally, this is only done when
the executable is created so other files containing device code can be linked
together. This can be somewhat problematic for users who wish to ship static
libraries that contain offloading code to users without a compatible offloading
toolchain.

When using a relocatable link with ``-r``, the ``clang-linker-wrapper`` will
perform the device linking and registration eagerly. This will remove the
embedded device code and register it correctly with the runtime. Semantically,
this is similar to creating a shared library object. If standard relocatable
linking is desired, simply do not run the binaries through the
``clang-linker-wrapper``. This will simply append the embedded device code so
that it can be linked later.

Example
=======
Expand Down
34 changes: 34 additions & 0 deletions clang/docs/OffloadingDesign.rst
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,37 @@ We can see the steps created by clang to generate the offloading code using the
# "nvptx64-nvidia-cuda" - "NVPTX::Assembler", inputs: ["/tmp/zaxpy-07f434.s"], output: "/tmp/zaxpy-0af7b7.o"
# "x86_64-unknown-linux-gnu" - "clang", inputs: ["/tmp/zaxpy-e6a41b.bc", "/tmp/zaxpy-0af7b7.o"], output: "/tmp/zaxpy-416cad.o"
# "x86_64-unknown-linux-gnu" - "Offload::Linker", inputs: ["/tmp/zaxpy-416cad.o"], output: "a.out"
Relocatable Linking
-------------------

The offloading compilation pipeline normally will defer the final device linking
and runtime registration until the ``clang-linker-wrapper`` is run to create the
executable. This is the standard behaviour when compiling for OpenMP offloading
or CUDA and HIP in ``-fgpu-rdc`` mode. However, there are some cases where the
user may wish to perform this device handling prematurely. This is described in
the :doc:`linker wrapper documentation<ClangLinkerWrapper>`.

Effectively, this allows the user to handle offloading specific linking ahead of
time when shipping objects or static libraries. This can be thought of as
performing a standard ``-fno-gpu-rdc`` compilation on a subset of object files.
This can be useful to reduce link time, prevent users from interacting with the
library's device code, or for shipping libraries to incompatible compilers.

Normally, if a relocatable link is done using ``clang -r`` it will simply merge
the ``.llvm.offloading`` sections which will then be linked later when the
executable is created. However, if the ``-r`` flag is used with the offloading
toolchain, it will perform the device linking and registration phases and then
merge the registration code into the final relocatable object file.

The following example shows how using the relocatable link with the offloading
pipeline can create a static library with offloading code that can be
redistributed without requiring any additional handling.

.. code-block:: console
$ clang++ -fopenmp -fopenmp-targets=nvptx64 foo.cpp -c
$ clang++ -lomptarget.devicertl --offload-link -r foo.o -o merged.o
$ llvm-ar rcs libfoo.a merged.o
# g++ app.cpp -L. -lfoo
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ Bug Fixes to C++ Support
parameter where we did an incorrect specialization of the initialization of
the default parameter.
Fixes (`#68490 <https://github.com/llvm/llvm-project/issues/68490>`_)
- Fix a crash when trying to call a varargs function that also has an explicit object parameter.
Fixes (`#80971 ICE when explicit object parameter be a function parameter pack`)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
/// Return true if this is a fixed point or integer type.
bool isFixedPointOrIntegerType() const;

/// Return true if this can be converted to (or from) a fixed point type.
bool isConvertibleToFixedPointType() const;

/// Return true if this is a saturated fixed point type according to
/// ISO/IEC JTC1 SC22 WG14 N1169. This type can be signed or unsigned.
bool isSaturatedFixedPointType() const;
Expand Down Expand Up @@ -7493,6 +7496,10 @@ inline bool Type::isFixedPointOrIntegerType() const {
return isFixedPointType() || isIntegerType();
}

inline bool Type::isConvertibleToFixedPointType() const {
return isRealFloatingType() || isFixedPointOrIntegerType();
}

inline bool Type::isSaturatedFixedPointType() const {
if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
return BT->getKind() >= BuiltinType::SatShortAccum &&
Expand Down
26 changes: 24 additions & 2 deletions clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
Expand All @@ -31,7 +30,6 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include <memory>
#include <type_traits>
#include <utility>

Expand Down Expand Up @@ -81,6 +79,8 @@ class Environment {
return ComparisonResult::Unknown;
}

/// DEPRECATED. Override `join` and/or `widen`, instead.
///
/// Modifies `MergedVal` to approximate both `Val1` and `Val2`. This could
/// be a strict lattice join or a more general widening operation.
///
Expand All @@ -105,6 +105,28 @@ class Environment {
return true;
}

/// Modifies `JoinedVal` to approximate both `Val1` and `Val2`. This should
/// obey the properties of a lattice join.
///
/// `Env1` and `Env2` can be used to query child values and path condition
/// implications of `Val1` and `Val2` respectively.
///
/// Requirements:
///
/// `Val1` and `Val2` must be distinct.
///
/// `Val1`, `Val2`, and `JoinedVal` must model values of type `Type`.
///
/// `Val1` and `Val2` must be assigned to the same storage location in
/// `Env1` and `Env2` respectively.
virtual void join(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2,
Value &JoinedVal, Environment &JoinedEnv) {
[[maybe_unused]] bool ShouldKeep =
merge(Type, Val1, Env1, Val2, Env2, JoinedVal, JoinedEnv);
assert(ShouldKeep && "dropping merged value is unsupported");
}

/// This function may widen the current value -- replace it with an
/// approximation that can reach a fixed point more quickly than iterated
/// application of the transfer function alone. The previous value is
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticCommonKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def warn_pragma_debug_unexpected_argument : Warning<
def warn_fp_nan_inf_when_disabled : Warning<
"use of %select{infinity|NaN}0%select{| via a macro}1 is undefined behavior "
"due to the currently enabled floating-point options">,
InGroup<DiagGroup<"nan-infinity-disabled">>;
InGroup<DiagGroup<"nan-infinity-disabled", [], NanInfDisabledDocs>>;
}

// Parse && Sema
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,12 @@ program by treating all string literals as having type ``const char *``
instead of ``char *``. This can cause unexpected behaviors with type-sensitive
constructs like ``_Generic``.
}];

defvar NanInfDisabledDocs = [{
This warning is enabled when source code using the macros ``INFINITY`` or ``NAN``
is compiled with floating-point options preventing these two values. This can
lead to undefined behavior. Check the order of command line arguments that modify
this behavior, such as ``-ffast-math``, ``-fhonor-infinities``, and
``-fhonor-nans`` (etc), as well as ``#pragma`` directives if this diagnostic is
generated unexpectedly.
}];
6 changes: 3 additions & 3 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,10 @@ def err_verify_inconsistent_diags : Error<
"'%0' diagnostics %select{expected|seen}1 but not %select{seen|expected}1: "
"%2">;
def err_verify_invalid_no_diags : Error<
"%select{expected|'expected-no-diagnostics'}0 directive cannot follow "
"%select{'expected-no-diagnostics' directive|other expected directives}0">;
"%select{expected|'%0-no-diagnostics'}1 directive cannot follow "
"%select{'%0-no-diagnostics' directive|other expected directives}1">;
def err_verify_no_directives : Error<
"no expected directives found: consider use of 'expected-no-diagnostics'">;
"no expected directives found: consider use of '%0-no-diagnostics'">;
def err_verify_nonconst_addrspace : Error<
"qualifier 'const' is needed for variables in address space '%0'">;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2982,6 +2982,9 @@ def err_trailing_requires_clause_on_deduction_guide : Error<
"deduction guide cannot have a requires clause">;
def err_constrained_non_templated_function
: Error<"non-templated function cannot have a requires clause">;
def err_non_temp_spec_requires_clause : Error<
"%select{explicit|friend}0 specialization cannot have a trailing requires clause "
"unless it declares a function template">;
def err_reference_to_function_with_unsatisfied_constraints : Error<
"invalid reference to function %0: constraints not satisfied">;
def err_requires_expr_local_parameter_default_argument : Error<
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/arm_neon.td
Original file line number Diff line number Diff line change
Expand Up @@ -1354,9 +1354,9 @@ let isScalarNarrowShift = 1 in {
// Signed/Unsigned Saturating Rounded Shift Right Narrow (Immediate)
def SCALAR_SQRSHRN_N: SInst<"vqrshrn_n", "(1<)1I", "SsSiSlSUsSUiSUl">;
// Signed Saturating Shift Right Unsigned Narrow (Immediate)
def SCALAR_SQSHRUN_N: SInst<"vqshrun_n", "(1<)1I", "SsSiSl">;
def SCALAR_SQSHRUN_N: SInst<"vqshrun_n", "(1<U)1I", "SsSiSl">;
// Signed Saturating Rounded Shift Right Unsigned Narrow (Immediate)
def SCALAR_SQRSHRUN_N: SInst<"vqrshrun_n", "(1<)1I", "SsSiSl">;
def SCALAR_SQRSHRUN_N: SInst<"vqrshrun_n", "(1<U)1I", "SsSiSl">;
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
11 changes: 6 additions & 5 deletions clang/include/clang/Lex/Preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2838,7 +2838,8 @@ class Preprocessor {
return AnnotationInfos.find(II)->second;
}

void emitMacroExpansionWarnings(const Token &Identifier) const {
void emitMacroExpansionWarnings(const Token &Identifier,
bool IsIfnDef = false) const {
IdentifierInfo *Info = Identifier.getIdentifierInfo();
if (Info->isDeprecatedMacro())
emitMacroDeprecationWarning(Identifier);
Expand All @@ -2847,12 +2848,12 @@ class Preprocessor {
!SourceMgr.isInMainFile(Identifier.getLocation()))
emitRestrictExpansionWarning(Identifier);

if (Info->getName() == "INFINITY")
if (getLangOpts().NoHonorInfs)
if (!IsIfnDef) {
if (Info->getName() == "INFINITY" && getLangOpts().NoHonorInfs)
emitRestrictInfNaNWarning(Identifier, 0);
if (Info->getName() == "NAN")
if (getLangOpts().NoHonorNaNs)
if (Info->getName() == "NAN" && getLangOpts().NoHonorNaNs)
emitRestrictInfNaNWarning(Identifier, 1);
}
}

static void processPathForFileMacro(SmallVectorImpl<char> &Path,
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11427,6 +11427,10 @@ class FixedPointExprEvaluator
return true;
}

bool ZeroInitialization(const Expr *E) {
return Success(0, E);
}

//===--------------------------------------------------------------------===//
// Visitor Methods
//===--------------------------------------------------------------------===//
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "Opcode.h"
#include "Program.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Basic/Builtins.h"
#include <type_traits>
Expand Down Expand Up @@ -116,7 +117,8 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl))
IsEligibleForCompilation = MD->isLambdaStaticInvoker();
if (!IsEligibleForCompilation)
IsEligibleForCompilation = FuncDecl->isConstexpr();
IsEligibleForCompilation =
FuncDecl->isConstexpr() || FuncDecl->hasAttr<MSConstexprAttr>();

// Compile the function body.
if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {
Expand Down
57 changes: 56 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
// Special case for C++'s three-way/spaceship operator <=>, which
// returns a std::{strong,weak,partial}_ordering (which is a class, so doesn't
// have a PrimType).
if (!T) {
if (!T && Ctx.getLangOpts().CPlusPlus) {
if (DiscardResult)
return true;
const ComparisonCategoryInfo *CmpInfo =
Expand Down Expand Up @@ -820,6 +820,19 @@ bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueIni
return true;
}

if (QT->isAnyComplexType()) {
assert(Initializing);
QualType ElemQT = QT->getAs<ComplexType>()->getElementType();
PrimType ElemT = classifyPrim(ElemQT);
for (unsigned I = 0; I < 2; ++I) {
if (!this->visitZeroInitializer(ElemT, ElemQT, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

return false;
}

Expand Down Expand Up @@ -850,6 +863,10 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
assert(E->getType()->isRecordType());
const Record *R = getRecord(E->getType());

if (Inits.size() == 1 && E->getType() == Inits[0]->getType()) {
return this->visitInitializer(Inits[0]);
}

unsigned InitIndex = 0;
for (const Expr *Init : Inits) {
if (!this->emitDupPtr(E))
Expand Down Expand Up @@ -1072,6 +1089,12 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr(

if (Kind == UETT_SizeOf) {
QualType ArgType = E->getTypeOfArgument();

// C++ [expr.sizeof]p2: "When applied to a reference or a reference type,
// the result is the size of the referenced type."
if (const auto *Ref = ArgType->getAs<ReferenceType>())
ArgType = Ref->getPointeeType();

CharUnits Size;
if (ArgType->isVoidType() || ArgType->isFunctionType())
Size = CharUnits::One();
Expand Down Expand Up @@ -3009,21 +3032,53 @@ bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
// pointer to the actual value) instead of a pointer to the pointer to the
// value.
bool IsReference = D->getType()->isReferenceType();
// Complex values are copied in the AST via a simply assignment or
// ltor cast. But we represent them as two-element arrays, which means
// we pass them around as pointers. So, to assignm from them, we will
// have to copy both (primitive) elements instead.
bool IsComplex = D->getType()->isAnyComplexType();

// Check for local/global variables and parameters.
if (auto It = Locals.find(D); It != Locals.end()) {
const unsigned Offset = It->second.Offset;
// FIXME: Fix the code duplication here with the code in the global case.
if (Initializing && IsComplex) {
PrimType ElemT = classifyComplexElementType(D->getType());
for (unsigned I = 0; I != 2; ++I) {
if (!this->emitGetPtrLocal(Offset, E))
return false;
if (!this->emitArrayElemPop(ElemT, I, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

if (IsReference)
return this->emitGetLocal(PT_Ptr, Offset, E);
return this->emitGetPtrLocal(Offset, E);
} else if (auto GlobalIndex = P.getGlobal(D)) {
if (Initializing && IsComplex) {
PrimType ElemT = classifyComplexElementType(D->getType());
for (unsigned I = 0; I != 2; ++I) {
if (!this->emitGetPtrGlobal(*GlobalIndex, E))
return false;
if (!this->emitArrayElemPop(ElemT, I, E))
return false;
if (!this->emitInitElem(ElemT, I, E))
return false;
}
return true;
}

if (IsReference)
return this->emitGetGlobalPtr(*GlobalIndex, E);

return this->emitGetPtrGlobal(*GlobalIndex, E);
} else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) {
if (auto It = this->Params.find(PVD); It != this->Params.end()) {
// FIXME: _Complex initializing case?
if (IsReference || !It->second.IsPtr)
return this->emitGetParamPtr(It->second.Offset, E);

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ bool ByteCodeStmtGen<Emitter>::visitCompoundStmt(
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
for (auto *D : DS->decls()) {
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl>(D))
if (isa<StaticAssertDecl, TagDecl, TypedefNameDecl, UsingEnumDecl>(D))
continue;

const auto *VD = dyn_cast<VarDecl>(D);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) {
template <typename T>
static void moveArrayTy(Block *, const std::byte *Src, std::byte *Dst,
const Descriptor *D) {
// FIXME: Need to copy the InitMap?
Src += sizeof(InitMapPtr);
Dst += sizeof(InitMapPtr);
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
const auto *SrcPtr = &reinterpret_cast<const T *>(Src)[I];
auto *DstPtr = &reinterpret_cast<T *>(Dst)[I];
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,14 @@ inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
return NarrowPtr(S, OpPC);
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool ArrayElemPop(InterpState &S, CodePtr OpPC, uint32_t Index) {
const Pointer &Ptr = S.Stk.pop<Pointer>();

S.Stk.push<T>(Ptr.atIndex(Index).deref<T>());
return true;
}

/// Just takes a pointer and checks if it's an incomplete
/// array type.
inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,19 @@ static bool interp__builtin_move(InterpState &S, CodePtr OpPC,
return Func->getDecl()->isConstexpr();
}

static bool interp__builtin_eh_return_data_regno(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *Func,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Arg = peekToAPSInt(S.Stk, ArgT);

int Result =
S.getCtx().getTargetInfo().getEHDataRegisterNumber(Arg.getZExtValue());
pushInt(S, Result);
return true;
}

bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call) {
InterpFrame *Frame = S.Current;
Expand Down Expand Up @@ -869,6 +882,11 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
return false;
break;

case Builtin::BI__builtin_eh_return_data_regno:
if (!interp__builtin_eh_return_data_regno(S, OpPC, Frame, F, Call))
return false;
break;

default:
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ def ExpandPtr : Opcode;
def ArrayElemPtr : AluOpcode;
def ArrayElemPtrPop : AluOpcode;

def ArrayElemPop : Opcode {
let Args = [ArgUint32];
let Types = [AllTypeClass];
let HasGroup = 1;
}

//===----------------------------------------------------------------------===//
// Direct field accessors
//===----------------------------------------------------------------------===//
Expand Down
59 changes: 28 additions & 31 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ static bool compareDistinctValues(QualType Type, Value &Val1,
llvm_unreachable("All cases covered in switch");
}

/// Attempts to merge distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Merging generally produces a single
/// Attempts to join distinct values `Val1` and `Val2` in `Env1` and `Env2`,
/// respectively, of the same type `Type`. Joining generally produces a single
/// value that (soundly) approximates the two inputs, although the actual
/// meaning depends on `Model`.
static Value *mergeDistinctValues(QualType Type, Value &Val1,
const Environment &Env1, Value &Val2,
const Environment &Env2,
Environment &MergedEnv,
Environment::ValueModel &Model) {
static Value *joinDistinctValues(QualType Type, Value &Val1,
const Environment &Env1, Value &Val2,
const Environment &Env2,
Environment &JoinedEnv,
Environment::ValueModel &Model) {
// Join distinct boolean values preserving information about the constraints
// in the respective path conditions.
if (isa<BoolValue>(&Val1) && isa<BoolValue>(&Val2)) {
Expand All @@ -113,42 +113,39 @@ static Value *mergeDistinctValues(QualType Type, Value &Val1,
// ```
auto &Expr1 = cast<BoolValue>(Val1).formula();
auto &Expr2 = cast<BoolValue>(Val2).formula();
auto &A = MergedEnv.arena();
auto &MergedVal = A.makeAtomRef(A.makeAtom());
MergedEnv.assume(
auto &A = JoinedEnv.arena();
auto &JoinedVal = A.makeAtomRef(A.makeAtom());
JoinedEnv.assume(
A.makeOr(A.makeAnd(A.makeAtomRef(Env1.getFlowConditionToken()),
A.makeEquals(MergedVal, Expr1)),
A.makeEquals(JoinedVal, Expr1)),
A.makeAnd(A.makeAtomRef(Env2.getFlowConditionToken()),
A.makeEquals(MergedVal, Expr2))));
return &A.makeBoolValue(MergedVal);
A.makeEquals(JoinedVal, Expr2))));
return &A.makeBoolValue(JoinedVal);
}

Value *MergedVal = nullptr;
Value *JoinedVal = nullptr;
if (auto *RecordVal1 = dyn_cast<RecordValue>(&Val1)) {
auto *RecordVal2 = cast<RecordValue>(&Val2);

if (&RecordVal1->getLoc() == &RecordVal2->getLoc())
// `RecordVal1` and `RecordVal2` may have different properties associated
// with them. Create a new `RecordValue` with the same location but
// without any properties so that we soundly approximate both values. If a
// particular analysis needs to merge properties, it should do so in
// `DataflowAnalysis::merge()`.
MergedVal = &MergedEnv.create<RecordValue>(RecordVal1->getLoc());
// particular analysis needs to join properties, it should do so in
// `DataflowAnalysis::join()`.
JoinedVal = &JoinedEnv.create<RecordValue>(RecordVal1->getLoc());
else
// If the locations for the two records are different, need to create a
// completely new value.
MergedVal = MergedEnv.createValue(Type);
JoinedVal = JoinedEnv.createValue(Type);
} else {
MergedVal = MergedEnv.createValue(Type);
JoinedVal = JoinedEnv.createValue(Type);
}

// FIXME: Consider destroying `MergedValue` immediately if `ValueModel::merge`
// returns false to avoid storing unneeded values in `DACtx`.
if (MergedVal)
if (Model.merge(Type, Val1, Env1, Val2, Env2, *MergedVal, MergedEnv))
return MergedVal;
if (JoinedVal)
Model.join(Type, Val1, Env1, Val2, Env2, *JoinedVal, JoinedEnv);

return nullptr;
return JoinedVal;
}

// When widening does not change `Current`, return value will equal `&Prev`.
Expand Down Expand Up @@ -240,9 +237,9 @@ joinLocToVal(const llvm::MapVector<const StorageLocation *, Value *> &LocToVal,
continue;
}

if (Value *MergedVal = mergeDistinctValues(
if (Value *JoinedVal = joinDistinctValues(
Loc->getType(), *Val, Env1, *It->second, Env2, JoinedEnv, Model)) {
Result.insert({Loc, MergedVal});
Result.insert({Loc, JoinedVal});
}
}

Expand Down Expand Up @@ -657,10 +654,10 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB,
// cast.
auto *Func = dyn_cast<FunctionDecl>(EnvA.CallStack.back());
assert(Func != nullptr);
if (Value *MergedVal =
mergeDistinctValues(Func->getReturnType(), *EnvA.ReturnVal, EnvA,
*EnvB.ReturnVal, EnvB, JoinedEnv, Model))
JoinedEnv.ReturnVal = MergedVal;
if (Value *JoinedVal =
joinDistinctValues(Func->getReturnType(), *EnvA.ReturnVal, EnvA,
*EnvB.ReturnVal, EnvB, JoinedEnv, Model))
JoinedEnv.ReturnVal = JoinedVal;
}

if (EnvA.ReturnLoc == EnvB.ReturnLoc)
Expand Down
37 changes: 22 additions & 15 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2870,19 +2870,6 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
#endif
it = FixablesForAllVars.byVar.erase(it);
} else if (Tracker.hasUnclaimedUses(it->first)) {
#ifndef NDEBUG
auto AllUnclaimed = Tracker.getUnclaimedUses(it->first);
for (auto UnclaimedDRE : AllUnclaimed) {
std::string UnclaimedUseTrace =
getDREAncestorString(UnclaimedDRE, D->getASTContext());

Handler.addDebugNoteForVar(
it->first, UnclaimedDRE->getBeginLoc(),
("failed to produce fixit for '" + it->first->getNameAsString() +
"' : has an unclaimed use\nThe unclaimed DRE trace: " +
UnclaimedUseTrace));
}
#endif
it = FixablesForAllVars.byVar.erase(it);
} else if (it->first->isInitCapture()) {
#ifndef NDEBUG
Expand All @@ -2892,10 +2879,30 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
"' : init capture"));
#endif
it = FixablesForAllVars.byVar.erase(it);
}else {
++it;
} else {
++it;
}
}

#ifndef NDEBUG
for (const auto &it : UnsafeOps.byVar) {
const VarDecl *const UnsafeVD = it.first;
auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
if (UnclaimedDREs.empty())
continue;
const auto UnfixedVDName = UnsafeVD->getNameAsString();
for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
std::string UnclaimedUseTrace =
getDREAncestorString(UnclaimedDRE, D->getASTContext());

Handler.addDebugNoteForVar(
UnsafeVD, UnclaimedDRE->getBeginLoc(),
("failed to produce fixit for '" + UnfixedVDName +
"' : has an unclaimed use\nThe unclaimed DRE trace: " +
UnclaimedUseTrace));
}
}
#endif

// Fixpoint iteration for pointer assignments
using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
Expand Down
22 changes: 15 additions & 7 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ class ScalarExprEmitter
void EmitUndefinedBehaviorIntegerDivAndRemCheck(const BinOpInfo &Ops,
llvm::Value *Zero,bool isDiv);
// Common helper for getting how wide LHS of shift is.
static Value *GetWidthMinusOneValue(Value* LHS,Value* RHS);
static Value *GetMaximumShiftAmount(Value *LHS, Value *RHS);

// Used for shifting constraints for OpenCL, do mask for powers of 2, URem for
// non powers of two.
Expand Down Expand Up @@ -4115,13 +4115,21 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
return Builder.CreateExactSDiv(diffInChars, divisor, "sub.ptr.div");
}

Value *ScalarExprEmitter::GetWidthMinusOneValue(Value* LHS,Value* RHS) {
Value *ScalarExprEmitter::GetMaximumShiftAmount(Value *LHS, Value *RHS) {
llvm::IntegerType *Ty;
if (llvm::VectorType *VT = dyn_cast<llvm::VectorType>(LHS->getType()))
Ty = cast<llvm::IntegerType>(VT->getElementType());
else
Ty = cast<llvm::IntegerType>(LHS->getType());
return llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth() - 1);
// For a given type of LHS the maximum shift amount is width(LHS)-1, however
// it can occur that width(LHS)-1 > range(RHS). Since there is no check for
// this in ConstantInt::get, this results in the value getting truncated.
// Constrain the return value to be max(RHS) in this case.
llvm::Type *RHSTy = RHS->getType();
llvm::APInt RHSMax = llvm::APInt::getMaxValue(RHSTy->getScalarSizeInBits());
if (RHSMax.ult(Ty->getBitWidth()))
return llvm::ConstantInt::get(RHSTy, RHSMax);
return llvm::ConstantInt::get(RHSTy, Ty->getBitWidth() - 1);
}

Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS,
Expand All @@ -4133,7 +4141,7 @@ Value *ScalarExprEmitter::ConstrainShiftValue(Value *LHS, Value *RHS,
Ty = cast<llvm::IntegerType>(LHS->getType());

if (llvm::isPowerOf2_64(Ty->getBitWidth()))
return Builder.CreateAnd(RHS, GetWidthMinusOneValue(LHS, RHS), Name);
return Builder.CreateAnd(RHS, GetMaximumShiftAmount(LHS, RHS), Name);

return Builder.CreateURem(
RHS, llvm::ConstantInt::get(RHS->getType(), Ty->getBitWidth()), Name);
Expand Down Expand Up @@ -4166,7 +4174,7 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
isa<llvm::IntegerType>(Ops.LHS->getType())) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
SmallVector<std::pair<Value *, SanitizerMask>, 2> Checks;
llvm::Value *WidthMinusOne = GetWidthMinusOneValue(Ops.LHS, Ops.RHS);
llvm::Value *WidthMinusOne = GetMaximumShiftAmount(Ops.LHS, Ops.RHS);
llvm::Value *ValidExponent = Builder.CreateICmpULE(Ops.RHS, WidthMinusOne);

if (SanitizeExponent) {
Expand All @@ -4184,7 +4192,7 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
Builder.CreateCondBr(ValidExponent, CheckShiftBase, Cont);
llvm::Value *PromotedWidthMinusOne =
(RHS == Ops.RHS) ? WidthMinusOne
: GetWidthMinusOneValue(Ops.LHS, RHS);
: GetMaximumShiftAmount(Ops.LHS, RHS);
CGF.EmitBlock(CheckShiftBase);
llvm::Value *BitsShiftedOff = Builder.CreateLShr(
Ops.LHS, Builder.CreateSub(PromotedWidthMinusOne, RHS, "shl.zeros",
Expand Down Expand Up @@ -4235,7 +4243,7 @@ Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
isa<llvm::IntegerType>(Ops.LHS->getType())) {
CodeGenFunction::SanitizerScope SanScope(&CGF);
llvm::Value *Valid =
Builder.CreateICmpULE(RHS, GetWidthMinusOneValue(Ops.LHS, RHS));
Builder.CreateICmpULE(Ops.RHS, GetMaximumShiftAmount(Ops.LHS, Ops.RHS));
EmitBinOpCheck(std::make_pair(Valid, SanitizerKind::ShiftExponent), Ops);
}

Expand Down
20 changes: 11 additions & 9 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1443,16 +1443,18 @@ Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
const ToolChain &TC = getToolChain(
*UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));

// Check if the environment version is valid.
// Check if the environment version is valid except wasm case.
llvm::Triple Triple = TC.getTriple();
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
StringRef TripleObjectFormat =
Triple.getObjectFormatTypeName(Triple.getObjectFormat());
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" &&
TripleVersionName != TripleObjectFormat) {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
if (!Triple.isWasm()) {
StringRef TripleVersionName = Triple.getEnvironmentVersionString();
StringRef TripleObjectFormat =
Triple.getObjectFormatTypeName(Triple.getObjectFormat());
if (Triple.getEnvironmentVersion().empty() && TripleVersionName != "" &&
TripleVersionName != TripleObjectFormat) {
Diags.Report(diag::err_drv_triple_version_invalid)
<< TripleVersionName << TC.getTripleString();
ContainsError = true;
}
}

// Report warning when arm64EC option is overridden by specified target
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Format/BreakableToken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,11 +449,11 @@ const FormatToken &BreakableComment::tokenAt(unsigned LineIndex) const {

static bool mayReflowContent(StringRef Content) {
Content = Content.trim(Blanks);
// Lines starting with '@' commonly have special meaning.
// Lines starting with '@' or '\' commonly have special meaning.
// Lines starting with '-', '-#', '+' or '*' are bulleted/numbered lists.
bool hasSpecialMeaningPrefix = false;
for (StringRef Prefix :
{"@", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) {
{"@", "\\", "TODO", "FIXME", "XXX", "-# ", "- ", "+ ", "* "}) {
if (Content.starts_with(Prefix)) {
hasSpecialMeaningPrefix = true;
break;
Expand Down
13 changes: 10 additions & 3 deletions clang/lib/Frontend/VerifyDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ class VerifyDiagnosticConsumer::MarkerTracker {
}
};

static std::string DetailedErrorString(const DiagnosticsEngine &Diags) {
if (Diags.getDiagnosticOptions().VerifyPrefixes.empty())
return "expected";
return *Diags.getDiagnosticOptions().VerifyPrefixes.begin();
}

/// ParseDirective - Go through the comment and see if it indicates expected
/// diagnostics. If so, then put them in the appropriate directive list.
///
Expand Down Expand Up @@ -478,14 +484,14 @@ static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM,
if (NoDiag) {
if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives)
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
<< /*IsExpectedNoDiagnostics=*/true;
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/true;
else
Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics;
continue;
}
if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) {
Diags.Report(Pos, diag::err_verify_invalid_no_diags)
<< /*IsExpectedNoDiagnostics=*/false;
<< DetailedErrorString(Diags) << /*IsExpectedNoDiagnostics=*/false;
continue;
}
Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives;
Expand Down Expand Up @@ -1104,7 +1110,8 @@ void VerifyDiagnosticConsumer::CheckDiagnostics() {
// Produce an error if no expected-* directives could be found in the
// source file(s) processed.
if (Status == HasNoDirectives) {
Diags.Report(diag::err_verify_no_directives).setForceEmit();
Diags.Report(diag::err_verify_no_directives).setForceEmit()
<< DetailedErrorString(Diags);
++NumErrors;
Status = HasNoDirectivesReported;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Headers/cpuid.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@
#define bit_AMXINT8 0x02000000

/* Features in %eax for leaf 7 sub-leaf 1 */
#define bit_SHA512 0x00000001
#define bit_SM3 0x00000002
#define bit_SM4 0x00000004
#define bit_RAOINT 0x00000008
#define bit_AVXVNNI 0x00000010
#define bit_AVX512BF16 0x00000020
Expand All @@ -211,7 +214,11 @@
/* Features in %edx for leaf 7 sub-leaf 1 */
#define bit_AVXVNNIINT8 0x00000010
#define bit_AVXNECONVERT 0x00000020
#define bit_AMXCOMPLEX 0x00000100
#define bit_AVXVNNIINT16 0x00000400
#define bit_PREFETCHI 0x00004000
#define bit_USERMSR 0x00008000
#define bit_AVX10 0x00080000

/* Features in %eax for leaf 13 sub-leaf 1 */
#define bit_XSAVEOPT 0x00000001
Expand Down Expand Up @@ -244,6 +251,9 @@
#define bit_RDPRU 0x00000010
#define bit_WBNOINVD 0x00000200

/* Features in %ebx for leaf 0x24 */
#define bit_AVX10_256 0x00020000
#define bit_AVX10_512 0x00040000

#if __i386__
#define __cpuid(__leaf, __eax, __ebx, __ecx, __edx) \
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Lex/PPDirectives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3288,7 +3288,7 @@ void Preprocessor::HandleIfdefDirective(Token &Result,
return;
}

emitMacroExpansionWarnings(MacroNameTok);
emitMacroExpansionWarnings(MacroNameTok, /*IsIfnDef=*/true);

// Check to see if this is the last token on the #if[n]def line.
CheckEndOfDirective(isIfndef ? "ifndef" : "ifdef");
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Lex/PPExpressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ static bool EvaluateDefined(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
Result.Val.setIsUnsigned(false); // Result is signed intmax_t.
DT.IncludedUndefinedIds = !Macro;

PP.emitMacroExpansionWarnings(PeekTok);
PP.emitMacroExpansionWarnings(
PeekTok,
(II->getName() == "INFINITY" || II->getName() == "NAN") ? true : false);

// If there is a macro, mark it used.
if (Result.Val != 0 && ValueLive)
Expand Down
103 changes: 54 additions & 49 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10440,6 +10440,60 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
diag::ext_operator_new_delete_declared_inline)
<< NewFD->getDeclName();

if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
// C++20 [dcl.decl.general]p4:
// The optional requires-clause in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function.
//
// C++20 [temp.pre]p8:
// An entity is templated if it is
// - a template,
// - an entity defined or created in a templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression appearing in the
// declaration of a templated entity.
//
// [Note 6: A local class, a local or block variable, or a friend
// function defined in a templated entity is a templated entity.
// — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.
if (!FunctionTemplate) {
if (isFunctionTemplateSpecialization || isMemberSpecialization) {
// C++ [temp.expl.spec]p8 (proposed resolution for CWG2847):
// An explicit specialization shall not have a trailing
// requires-clause unless it declares a function template.
//
// Since a friend function template specialization cannot be
// definition, and since a non-template friend declaration with a
// trailing requires-clause must be a definition, we diagnose
// friend function template specializations with trailing
// requires-clauses on the same path as explicit specializations
// even though they aren't necessarily prohibited by the same
// language rule.
Diag(TRC->getBeginLoc(), diag::err_non_temp_spec_requires_clause)
<< isFriend;
} else if (isFriend && NewFD->isTemplated() &&
!D.isFunctionDefinition()) {
// C++ [temp.friend]p9:
// A non-template friend declaration with a requires-clause shall be
// a definition.
Diag(NewFD->getBeginLoc(),
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
NewFD->setInvalidDecl();
} else if (!NewFD->isTemplated() ||
!(isa<CXXMethodDecl>(NewFD) || D.isFunctionDefinition())) {
Diag(TRC->getBeginLoc(),
diag::err_constrained_non_templated_function);
}
}
}

// We do not add HD attributes to specializations here because
// they may have different constexpr-ness compared to their
// templates and, after maybeAddCUDAHostDeviceAttrs() is applied,
Expand Down Expand Up @@ -12063,55 +12117,6 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
checkThisInStaticMemberFunctionType(Method);
}

if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
// C++20: dcl.decl.general p4:
// The optional requires-clause ([temp.pre]) in an init-declarator or
// member-declarator shall be present only if the declarator declares a
// templated function ([dcl.fct]).
//
// [temp.pre]/8:
// An entity is templated if it is
// - a template,
// - an entity defined ([basic.def]) or created ([class.temporary]) in a
// templated entity,
// - a member of a templated entity,
// - an enumerator for an enumeration that is a templated entity, or
// - the closure type of a lambda-expression ([expr.prim.lambda.closure])
// appearing in the declaration of a templated entity. [Note 6: A local
// class, a local or block variable, or a friend function defined in a
// templated entity is a templated entity. — end note]
//
// A templated function is a function template or a function that is
// templated. A templated class is a class template or a class that is
// templated. A templated variable is a variable template or a variable
// that is templated.

bool IsTemplate = NewFD->getDescribedFunctionTemplate();
bool IsFriend = NewFD->getFriendObjectKind();
if (!IsTemplate && // -a template
// defined... in a templated entity
!(DeclIsDefn && NewFD->isTemplated()) &&
// a member of a templated entity
!(isa<CXXMethodDecl>(NewFD) && NewFD->isTemplated()) &&
// Don't complain about instantiations, they've already had these
// rules + others enforced.
!NewFD->isTemplateInstantiation() &&
// If the function violates [temp.friend]p9 because it is missing
// a definition, and adding a definition would make it templated,
// then let that error take precedence.
!(!DeclIsDefn && IsFriend && NewFD->isTemplated())) {
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
} else if (!DeclIsDefn && !IsTemplate && IsFriend &&
!NewFD->isTemplateInstantiation()) {
// C++ [temp.friend]p9:
// A non-template friend declaration with a requires-clause shall be a
// definition.
Diag(NewFD->getBeginLoc(),
diag::err_non_temp_friend_decl_with_requires_clause_must_be_def);
NewFD->setInvalidDecl();
}
}

if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
ActOnConversionDeclarator(Conversion);

Expand Down
9 changes: 6 additions & 3 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2177,7 +2177,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
From->isIntegerConstantExpr(S.getASTContext())) {
SCS.Second = ICK_Compatible_Conversion;
FromType = ToType;
} else if (ToType->isFixedPointType() || FromType->isFixedPointType()) {
} else if ((ToType->isFixedPointType() &&
FromType->isConvertibleToFixedPointType()) ||
(FromType->isFixedPointType() &&
ToType->isConvertibleToFixedPointType())) {
SCS.Second = ICK_Fixed_Point_Conversion;
FromType = ToType;
} else {
Expand Down Expand Up @@ -7719,8 +7722,8 @@ bool Sema::CheckNonDependentConversions(
unsigned Offset =
Method && Method->hasCXXExplicitFunctionObjectParameter() ? 1 : 0;

for (unsigned I = 0, N = std::min(ParamTypes.size(), Args.size()); I != N;
++I) {
for (unsigned I = 0, N = std::min(ParamTypes.size() - Offset, Args.size());
I != N; ++I) {
QualType ParamType = ParamTypes[I + Offset];
if (!ParamType->isDependentType()) {
unsigned ConvIdx;
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
}
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
return {E, true};
if (auto *decl = memberCall->getMethodDecl()) {
std::optional<bool> IsGetterOfRefCt =
isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
return {E, true};
}
continue;
}
continue;
}
}

Expand Down
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,16 @@ namespace ffs {
char ffs6[__builtin_ffsl(0x10L) == 5 ? 1 : -1];
char ffs7[__builtin_ffsll(0x100LL) == 9 ? 1 : -1];
}

namespace EhReturnDataRegno {
void test11(int X) {
switch (X) {
case __builtin_eh_return_data_regno(0): // constant foldable.
break;
}

__builtin_eh_return_data_regno(X); // expected-error {{argument to '__builtin_eh_return_data_regno' must be a constant integer}} \
// ref-error {{argument to '__builtin_eh_return_data_regno' must be a constant integer}}

}
}
28 changes: 28 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ static_assert(__imag(I3) == 0, "");
// constexpr _Complex _BitInt(8) A = 0;// = {4};


constexpr _Complex double Doubles[4] = {{1.0, 2.0}};
static_assert(__real(Doubles[0]) == 1.0, "");
static_assert(__imag(Doubles[0]) == 2.0, "");
static_assert(__real(Doubles[1]) == 0.0, "");
static_assert(__imag(Doubles[1]) == 0.0, "");
static_assert(__real(Doubles[2]) == 0.0, "");
static_assert(__imag(Doubles[2]) == 0.0, "");
static_assert(__real(Doubles[3]) == 0.0, "");
static_assert(__imag(Doubles[3]) == 0.0, "");

void func(void) {
__complex__ int arr;
_Complex int result;
Expand Down Expand Up @@ -194,3 +204,21 @@ namespace ZeroInit {

constexpr int ignored = (fcomplex(), 0);
}

namespace DeclRefCopy {
constexpr _Complex int ComplexInt = 42 + 24i;

constexpr _Complex int B = ComplexInt;
constexpr _Complex int ArrayOfComplexInt[4] = {ComplexInt, ComplexInt, ComplexInt, ComplexInt};
static_assert(__real(ArrayOfComplexInt[0]) == 42, "");
static_assert(__imag(ArrayOfComplexInt[0]) == 24, "");
static_assert(__real(ArrayOfComplexInt[3]) == 42, "");
static_assert(__imag(ArrayOfComplexInt[3]) == 24, "");

constexpr int localComplexArray() {
_Complex int A = 42 + 24i;
_Complex int ArrayOfComplexInt[4] = {A, A, A, A};
return __real(ArrayOfComplexInt[0]) + __imag(ArrayOfComplexInt[3]);
}
static_assert(localComplexArray() == (24 + 42), "");
}
2 changes: 2 additions & 0 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ namespace PointerComparison {
}

namespace SizeOf {
static_assert(alignof(char&) == 1, "");

constexpr int soint = sizeof(int);
constexpr int souint = sizeof(unsigned int);
static_assert(soint == souint, "");
Expand Down
4 changes: 4 additions & 0 deletions clang/test/AST/Interp/records.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
// RUN: %clang_cc1 -verify=ref -std=c++20 %s
// RUN: %clang_cc1 -verify=ref -triple i686 %s

/// Used to crash.
struct Empty {};
constexpr Empty e = {Empty()};

struct BoolPair {
bool first;
bool second;
Expand Down
22 changes: 8 additions & 14 deletions clang/test/AST/Interp/switch.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -std=c++17 -fexperimental-new-constant-interpreter -verify %s
// RUN: %clang_cc1 -std=c++17 -verify=ref %s
// RUN: %clang_cc1 -std=c++17 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++17 -verify=ref,both %s

constexpr bool isEven(int a) {
bool v = false;
Expand Down Expand Up @@ -75,20 +75,14 @@ constexpr int test(int val) {
}
static_assert(test(1) == 100, "");

constexpr int bad(int val) { return val / 0; } // expected-warning {{division by zero}} \
// ref-warning {{division by zero}}
constexpr int another_test(int val) { // expected-note {{declared here}} \
// ref-note {{declared here}}
constexpr int bad(int val) { return val / 0; } // both-warning {{division by zero}}
constexpr int another_test(int val) { // both-note {{declared here}}
switch (val) {
case bad(val): return 100; // expected-error {{case value is not a constant expression}} \
// expected-note {{cannot be used in a constant expression}} \
// ref-error {{case value is not a constant expression}} \
// ref-note {{cannot be used in a constant expression}}
case bad(val): return 100; // both-error {{case value is not a constant expression}} \
// both-note {{cannot be used in a constant expression}}
default: return -1;
}
return 0;
}
static_assert(another_test(1) == 100, ""); // expected-error {{static assertion failed}} \
// expected-note {{evaluates to}} \
// ref-error {{static assertion failed}} \
// ref-note {{evaluates to}}
static_assert(another_test(1) == 100, ""); // both-error {{static assertion failed}} \
// both-note {{evaluates to}}
5 changes: 5 additions & 0 deletions clang/test/AST/fixed-point-zero-init.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: %clang_cc1 -ffixed-point %s -verify
// expected-no-diagnostics

constexpr _Accum a[2] = {};
static_assert(a[0] == 0 && a[0] != 1);
1 change: 1 addition & 0 deletions clang/test/AST/ms-constexpr.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump -verify %s | FileCheck %s
// RUN: %clang_cc1 -fms-compatibility -fms-compatibility-version=19.33 -std=c++20 -ast-dump -verify %s -fexperimental-new-constant-interpreter | FileCheck %s
// expected-no-diagnostics

// CHECK: used f1 'bool ()'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncountedLocalVarsChecker -verify %s

#include "mock-types.h"

class RenderStyle;

class FillLayer {
public:
void ref() const;
void deref() const;
};

class FillLayersPropertyWrapper {
public:
typedef const FillLayer& (RenderStyle::*LayersGetter)() const;

private:
bool canInterpolate(const RenderStyle& from) const
{
auto* fromLayer = &(from.*m_layersGetter)();
// expected-warning@-1{{Local variable 'fromLayer' is uncounted and unsafe}}
return true;
}

LayersGetter m_layersGetter;
};
60 changes: 60 additions & 0 deletions clang/test/CXX/drs/dr28xx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: %clang_cc1 -std=c++98 -verify=expected %s
// RUN: %clang_cc1 -std=c++11 -verify=expected %s
// RUN: %clang_cc1 -std=c++14 -verify=expected %s
// RUN: %clang_cc1 -std=c++17 -verify=expected %s
// RUN: %clang_cc1 -std=c++20 -verify=expected,since-cxx20 %s
// RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s
// RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s

#if __cplusplus < 202002L
// expected-no-diagnostics
#endif

namespace dr2847 { // dr2847: 19

#if __cplusplus >= 202002L

template<typename>
void i();

struct A {
template<typename>
void f() requires true;

template<>
void f<int>() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

friend void i<int>() requires true;
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
};

template<typename>
struct B {
void f() requires true;

template<typename>
void g() requires true;

template<typename>
void h() requires true;

template<>
void h<int>() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

friend void i<int>() requires true;
// since-cxx20-error@-1 {{friend specialization cannot have a trailing requires clause unless it declares a function template}}
};

template<>
void B<int>::f() requires true;
// since-cxx20-error@-1 {{explicit specialization cannot have a trailing requires clause unless it declares a function template}}

template<>
template<typename T>
void B<int>::g() requires true;

#endif

} // namespace dr2847
12 changes: 6 additions & 6 deletions clang/test/CodeGen/aarch64-neon-intrinsics.c
Original file line number Diff line number Diff line change
Expand Up @@ -14132,24 +14132,24 @@ int32_t test_vqshrund_n_s64(int64_t a) {
// CHECK: [[VQRSHRUNH_N_S16:%.*]] = call <8 x i8> @llvm.aarch64.neon.sqrshrun.v8i8(<8 x i16> [[TMP0]], i32 8)
// CHECK: [[TMP1:%.*]] = extractelement <8 x i8> [[VQRSHRUNH_N_S16]], i64 0
// CHECK: ret i8 [[TMP1]]
int8_t test_vqrshrunh_n_s16(int16_t a) {
return (int8_t)vqrshrunh_n_s16(a, 8);
uint8_t test_vqrshrunh_n_s16(int16_t a) {
return (uint8_t)vqrshrunh_n_s16(a, 8);
}

// CHECK-LABEL: @test_vqrshruns_n_s32(
// CHECK: [[TMP0:%.*]] = insertelement <4 x i32> poison, i32 %a, i64 0
// CHECK: [[VQRSHRUNS_N_S32:%.*]] = call <4 x i16> @llvm.aarch64.neon.sqrshrun.v4i16(<4 x i32> [[TMP0]], i32 16)
// CHECK: [[TMP1:%.*]] = extractelement <4 x i16> [[VQRSHRUNS_N_S32]], i64 0
// CHECK: ret i16 [[TMP1]]
int16_t test_vqrshruns_n_s32(int32_t a) {
return (int16_t)vqrshruns_n_s32(a, 16);
uint16_t test_vqrshruns_n_s32(int32_t a) {
return (uint16_t)vqrshruns_n_s32(a, 16);
}

// CHECK-LABEL: @test_vqrshrund_n_s64(
// CHECK: [[VQRSHRUND_N_S64:%.*]] = call i32 @llvm.aarch64.neon.sqrshrun.i32(i64 %a, i32 32)
// CHECK: ret i32 [[VQRSHRUND_N_S64]]
int32_t test_vqrshrund_n_s64(int64_t a) {
return (int32_t)vqrshrund_n_s64(a, 32);
uint32_t test_vqrshrund_n_s64(int64_t a) {
return (uint32_t)vqrshrund_n_s64(a, 32);
}

// CHECK-LABEL: @test_vcvts_n_f32_s32(
Expand Down
36 changes: 36 additions & 0 deletions clang/test/CodeGen/ubsan-shift-bitint.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %clang_cc1 %s -O0 -fsanitize=shift-exponent -emit-llvm -std=c2x -triple=x86_64-unknown-linux -o - | FileCheck %s

// Checking that the code generation is using the unextended/untruncated
// exponent values and capping the values accordingly

// CHECK-LABEL: define{{.*}} i32 @test_left_variable
int test_left_variable(unsigned _BitInt(5) b, unsigned _BitInt(2) e) {
// CHECK: [[E_REG:%.+]] = load [[E_SIZE:i2]]
// CHECK: icmp ule [[E_SIZE]] [[E_REG]], -1
return b << e;
}

// CHECK-LABEL: define{{.*}} i32 @test_right_variable
int test_right_variable(unsigned _BitInt(2) b, unsigned _BitInt(3) e) {
// CHECK: [[E_REG:%.+]] = load [[E_SIZE:i3]]
// CHECK: icmp ule [[E_SIZE]] [[E_REG]], 1
return b >> e;
}

// Old code generation would give false positives on left shifts when:
// value(e) > (width(b) - 1 % 2 ** width(e))
// CHECK-LABEL: define{{.*}} i32 @test_left_literal
int test_left_literal(unsigned _BitInt(5) b) {
// CHECK-NOT: br i1 false, label %cont, label %handler.shift_out_of_bounds
// CHECK: br i1 true, label %cont, label %handler.shift_out_of_bounds
return b << 3uwb;
}

// Old code generation would give false positives on right shifts when:
// (value(e) % 2 ** width(b)) < width(b)
// CHECK-LABEL: define{{.*}} i32 @test_right_literal
int test_right_literal(unsigned _BitInt(2) b) {
// CHECK-NOT: br i1 true, label %cont, label %handler.shift_out_of_bounds
// CHECK: br i1 false, label %cont, label %handler.shift_out_of_bounds
return b >> 4uwb;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,18 @@
// RUN: FileCheck --check-prefix=CHECK-TARGET %s

// CHECK-TARGET: "aarch64-unknown-linux-android31"

// RUN: not %clang --target=armv7-linux-gnuS -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-ERROR2 %s

// CHECK-ERROR2: error: version 'S' in target triple 'armv7-unknown-linux-gnuS' is invalid

// RUN: %clang --target=wasm32-unknown-wasi-preview2 -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-WASM %s

// CHECK-WASM: "-triple" "wasm32-unknown-wasi-preview2"

// RUN: %clang --target=wasm32-wasi-pthread -c %s -### 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-WASM1 %s

// CHECK-WASM1: "-triple" "wasm32-unknown-wasi-pthread"
8 changes: 8 additions & 0 deletions clang/test/Driver/linker-wrapper-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=OPENMP,OPENMP-ELF
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run -r --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=OPENMP-ELF,OPENMP-REL
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-windows-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=OPENMP,OPENMP-COFF

Expand All @@ -19,6 +21,8 @@
// OPENMP-COFF: @__start_omp_offloading_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "omp_offloading_entries$OA"
// OPENMP-COFF-NEXT: @__stop_omp_offloading_entries = weak_odr hidden constant [0 x %struct.__tgt_offload_entry] zeroinitializer, section "omp_offloading_entries$OZ"

// OPENMP-REL: @.omp_offloading.device_image = internal unnamed_addr constant [[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section ".llvm.offloading.relocatable", align 8

// OPENMP: @.omp_offloading.device_image = internal unnamed_addr constant [[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section ".llvm.offloading", align 8
// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr constant [1 x %__tgt_device_image] [%__tgt_device_image { ptr getelementptr inbounds ([[[BEGIN:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 1, i64 0), ptr getelementptr inbounds ([[[END:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 1, i64 0), ptr @__start_omp_offloading_entries, ptr @__stop_omp_offloading_entries }]
// OPENMP-NEXT: @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_omp_offloading_entries, ptr @__stop_omp_offloading_entries }
Expand All @@ -42,6 +46,8 @@
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=CUDA,CUDA-ELF
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run -r --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=CUDA,CUDA-ELF
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-windows-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=CUDA,CUDA-COFF

Expand Down Expand Up @@ -140,6 +146,8 @@
// RUN: -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=HIP,HIP-ELF
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu -r \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=HIP,HIP-ELF
// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-windows-gnu \
// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefixes=HIP,HIP-COFF

Expand Down
32 changes: 29 additions & 3 deletions clang/test/Driver/linker-wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,36 @@ __attribute__((visibility("protected"), used)) int x;
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
// RUN: --image=file=%t.elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: llvm-ar rcs %t.a %t.o
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld.lld -- -r --whole-archive %t.a --no-whole-archive \
// RUN: --linker-path=/usr/bin/ld.lld -- -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK

// RELOCATABLE-LINK-NOT: clang{{.*}} -o {{.*}}.img --target=x86_64-unknown-linux-gnu
// RELOCATABLE-LINK: clang{{.*}} -o {{.*}}.img --target=x86_64-unknown-linux-gnu
// RELOCATABLE-LINK: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90a \
// RUN: --image=file=%t.elf.o,kind=hip,triple=amdgcn-amd-amdhsa,arch=gfx90a
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld.lld -- -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK-HIP

// RELOCATABLE-LINK-HIP: clang{{.*}} -o {{.*}}.img --target=amdgcn-amd-amdhsa
// RELOCATABLE-LINK-HIP: clang-offload-bundler{{.*}} -type=o -bundle-align=4096 -targets=host-x86_64-unknown-linux,hipv4-amdgcn-amd-amdhsa--gfx90a -input=/dev/null -input={{.*}} -output={{.*}}
// RELOCATABLE-LINK-HIP: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK-HIP: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading

// RUN: clang-offload-packager -o %t.out \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_89 \
// RUN: --image=file=%t.elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_89
// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
// RUN: --linker-path=/usr/bin/ld.lld -- -r %t.o \
// RUN: %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=RELOCATABLE-LINK-CUDA

// RELOCATABLE-LINK-CUDA: clang{{.*}} -o {{.*}}.img --target=nvptx64-nvidia-cuda
// RELOCATABLE-LINK-CUDA: fatbinary{{.*}} -64 --create {{.*}}.fatbin --image=profile=sm_89,file={{.*}}.img
// RELOCATABLE-LINK-CUDA: /usr/bin/ld.lld{{.*}}-r
// RELOCATABLE-LINK-CUDA: llvm-objcopy{{.*}}a.out --remove-section .llvm.offloading
70 changes: 54 additions & 16 deletions clang/test/Driver/wasm-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,89 @@

// CHECK: "-fvisibility=hidden"

// RUN: %clang --target=wasm32-unknown-unknown -### %s 2>&1 | FileCheck %s -check-prefix=DEFAULT
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcpu=mvp 2>&1 | FileCheck %s -check-prefix=MVP
// RUN: %clang --target=wasm32-unknown-unknown -### %s 2>&1 | FileCheck %s -check-prefix=GENERIC
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcpu=generic 2>&1 | FileCheck %s -check-prefix=GENERIC
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mcpu=bleeding-edge 2>&1 | FileCheck %s -check-prefix=BLEEDING-EDGE

// MVP: "-target-cpu" "mvp"
// GENERIC: "-target-cpu" "generic"
// BLEEDING-EDGE: "-target-cpu" "bleeding-edge"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mbulk-memory 2>&1 | FileCheck %s -check-prefix=BULK-MEMORY
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-bulk-memory 2>&1 | FileCheck %s -check-prefix=NO-BULK-MEMORY

// BULK-MEMORY: "-target-feature" "+bulk-memory"
// NO-BULK-MEMORY: "-target-feature" "-bulk-memory"
// DEFAULT-NOT: "-target-feature" "-bulk-memory"
// MVP-NOT: "-target-feature" "+bulk-memory"
// BLEEDING-EDGE-NOT: "-target-feature" "-bulk-memory"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mmutable-globals 2>&1 | FileCheck %s -check-prefix=MUTABLE-GLOBALS
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-mutable-globals 2>&1 | FileCheck %s -check-prefix=NO-MUTABLE-GLOBALS

// MUTABLE-GLOBALS: "-target-feature" "+mutable-globals"
// NO-MUTABLE-GLOBALS: "-target-feature" "-mutable-globals"
// DEFAULT-NOT: "-target-feature" "-mutable-globals"
// MVP-NOT: "-target-feature" "+mutable-globals"
// BLEEDING-EDGE-NOT: "-target-feature" "-mutable-globals"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -msign-ext 2>&1 | FileCheck %s -check-prefix=SIGN-EXT
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-sign-ext 2>&1 | FileCheck %s -check-prefix=NO-SIGN-EXT

// SIGN-EXT: "-target-feature" "+sign-ext"
// NO-SIGN-EXT: "-target-feature" "-sign-ext"
// DEFAULT-NOT: "-target-feature" "-sign-ext"
// MVP-NOT: "-target-feature" "+sign-ext"
// BLEEDING-EDGE-NOT: "-target-feature" "-sign-ext"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mnontrapping-fptoint 2>&1 | FileCheck %s -check-prefix=NONTRAPPING-FPTOINT
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-nontrapping-fptoint 2>&1 | FileCheck %s -check-prefix=NO-NONTRAPPING-FPTOINT

// NONTRAPPING-FPTOINT: "-target-feature" "+nontrapping-fptoint"
// NO-NONTRAPPING-FPTOINT: "-target-feature" "-nontrapping-fptoint"
// DEFAULT-NOT: "-target-feature" "-nontrapping-fptoint"
// MVP-NOT: "-target-feature" "+nontrapping-fptoint"
// BLEEDING-EDGE-NOT: "-target-feature" "-nontrapping-fptoint"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mmultivalue 2>&1 | FileCheck %s -check-prefix=MULTIVALUE
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-multivalue 2>&1 | FileCheck %s -check-prefix=NO-MULTIVALUE

// MULTIVALUE: "-target-feature" "+multivalue"
// NO-MULTIVALUE: "-target-feature" "-multivalue"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mmultimemory 2>&1 | FileCheck %s -check-prefix=MULTIMEMORY
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-multimemory 2>&1 | FileCheck %s -check-prefix=NO-MULTIMEMORY

// MULTIMEMORY: "-target-feature" "+multimemory"
// NO-MULTIMEMORY: "-target-feature" "-multimemory"
// DEFAULT-NOT: "-target-feature" "-multimemory"
// MVP-NOT: "-target-feature" "+multimemory"
// BLEEDING-EDGE-NOT: "-target-feature" "-multimemory"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -matomics 2>&1 | FileCheck %s -check-prefix=ATOMICS
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-atomics 2>&1 | FileCheck %s -check-prefix=NO-ATOMICS

// ATOMICS: "-target-feature" "+atomics"
// NO-ATOMICS: "-target-feature" "-atomics"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mtail-call 2>&1 | FileCheck %s -check-prefix=TAIL-CALL
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-tail-call 2>&1 | FileCheck %s -check-prefix=NO-TAIL-CALL

// TAIL-CALL: "-target-feature" "+tail-call"
// NO-TAIL-CALL: "-target-feature" "-tail-call"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mreference-types 2>&1 | FileCheck %s -check-prefix=REFERENCE-TYPES
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-reference-types 2>&1 | FileCheck %s -check-prefix=NO-REFERENCE-TYPES

// REFERENCE-TYPES: "-target-feature" "+reference-types"
// NO-REFERENCE-TYPES: "-target-feature" "-reference-types"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -msimd128 2>&1 | FileCheck %s -check-prefix=SIMD128
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-simd128 2>&1 | FileCheck %s -check-prefix=NO-SIMD128

// SIMD128: "-target-feature" "+simd128"
// NO-SIMD128: "-target-feature" "-simd128"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mrelaxed-simd 2>&1 | FileCheck %s -check-prefix=RELAXED-SIMD
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-relaxed-simd 2>&1 | FileCheck %s -check-prefix=NO-RELAXED-SIMD

// RELAXED-SIMD: "-target-feature" "+relaxed-simd"
// NO-RELAXED-SIMD: "-target-feature" "-relaxed-simd"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mexception-handling 2>&1 | FileCheck %s -check-prefix=EXCEPTION-HANDLING
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-exception-handling 2>&1 | FileCheck %s -check-prefix=NO-EXCEPTION-HANDLING

// EXCEPTION-HANDLING: "-target-feature" "+exception-handling"
// NO-EXCEPTION-HANDLING: "-target-feature" "-exception-handling"

// RUN: %clang --target=wasm32-unknown-unknown -### %s -mextended-const 2>&1 | FileCheck %s -check-prefix=EXTENDED-CONST
// RUN: %clang --target=wasm32-unknown-unknown -### %s -mno-extended-const 2>&1 | FileCheck %s -check-prefix=NO-EXTENDED-CONST

// EXTENDED-CONST: "-target-feature" "+extended-const"
// NO-EXTENDED-CONST: "-target-feature" "-extended-const"
1 change: 0 additions & 1 deletion clang/test/Format/clang-format-ignore.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// UNSUPPORTED: system-windows
// RUN: rm -rf %t.dir
// RUN: mkdir -p %t.dir/level1/level2

Expand Down
4 changes: 4 additions & 0 deletions clang/test/Frontend/fixed_point_errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ int fract_int = 10r; // expected-error{{invalid suffix 'r' on integer consta
float accum_flt = 0.0k; // expected-error{{invalid suffix 'k' on floating constant}}
float fract_flt = 0.0r; // expected-error{{invalid suffix 'r' on floating constant}}
#endif

#ifndef WITHOUT_FIXED_POINT
const char *c = 10.0k; // expected-error{{cannot initialize a variable of type 'const char *' with an rvalue of type '_Accum'}}
#endif
29 changes: 29 additions & 0 deletions clang/test/Frontend/verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,32 @@ unexpected b; // expected-error@33 1-1 {{unknown type}}
#endif

#endif

#ifdef TEST10
// RUN: not %clang_cc1 -DTEST10 -verify=foo %s 2>&1 | FileCheck -check-prefix=CHECK10 %s

// CHECK10: error: no expected directives found: consider use of 'foo-no-diagnostics'
#endif

#ifdef TEST11
// RUN: not %clang_cc1 -DTEST11 -verify=foo %s 2>&1 | FileCheck -check-prefix=CHECK11 %s

// foo-no-diagnostics
// foo-note {{}}

// CHECK11: error: 'foo-error' diagnostics seen but not expected:
// CHECK11-NEXT: Line 201: expected directive cannot follow 'foo-no-diagnostics' directive
// CHECK11-NEXT: 1 error generated.
#endif

#ifdef TEST12
// RUN: not %clang_cc1 -DTEST12 -verify=foo %s 2>&1 | FileCheck -check-prefix=CHECK12 %s

#warning X
// foo-warning@-1 {{X}}
// foo-no-diagnostics

// CHECK12: error: 'foo-error' diagnostics seen but not expected:
// CHECK12-NEXT: Line 213: 'foo-no-diagnostics' directive cannot follow other expected directives
// CHECK12-NEXT: 1 error generated.
#endif
17 changes: 13 additions & 4 deletions clang/test/Preprocessor/riscv-target-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@
// CHECK-NOT: __riscv_smnpm{{.*$}}
// CHECK-NOT: __riscv_ssnpm{{.*$}}
// CHECK-NOT: __riscv_sspm{{.*$}}
// CHECK-NOT: __riscv_ssqosid{{.*$}}
// CHECK-NOT: __riscv_supm{{.*$}}
// CHECK-NOT: __riscv_zaamo {{.*$}}
// CHECK-NOT: __riscv_zacas {{.*$}}
Expand Down Expand Up @@ -1599,19 +1600,27 @@
// RUN: %clang --target=riscv32 -menable-experimental-extensions \
// RUN: -march=rv32i_sspm0p8 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SSPM-EXT %s
// RUN: %clang --target=riscv64 \
// RUN: -march=rv64i_sspm0p8 -E -dM %s -menable-experimental-extensions \
// RUN: %clang --target=riscv64 -menable-experimental-extensions \
// RUN: -march=rv64i_sspm0p8 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SSPM-EXT %s
// CHECK-SSPM-EXT: __riscv_sspm 8000{{$}}

// RUN: %clang --target=riscv32 -menable-experimental-extensions \
// RUN: -march=rv32i_supm0p8 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SUPM-EXT %s
// RUN: %clang --target=riscv64 \
// RUN: -march=rv64i_supm0p8 -E -dM %s -menable-experimental-extensions \
// RUN: %clang --target=riscv64 -menable-experimental-extensions \
// RUN: -march=rv64i_supm0p8 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SUPM-EXT %s
// CHECK-SUPM-EXT: __riscv_supm 8000{{$}}

// RUN: %clang --target=riscv32 -menable-experimental-extensions \
// RUN: -march=rv32i_ssqosid1p0 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SSQOSID-EXT %s
// RUN: %clang --target=riscv64 -menable-experimental-extensions \
// RUN: -march=rv64i_ssqosid1p0 -E -dM %s \
// RUN: -o - | FileCheck --check-prefix=CHECK-SSQOSID-EXT %s
// CHECK-SSQOSID-EXT: __riscv_ssqosid 1000000{{$}}

// Misaligned

// RUN: %clang --target=riscv32-unknown-linux-gnu -march=rv32i -E -dM %s \
Expand Down
23 changes: 22 additions & 1 deletion clang/test/Preprocessor/wasm-target-features.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,27 @@
// MVP-NOT:#define __wasm_reference_types__
// MVP-NOT:#define __wasm_extended_const__
// MVP-NOT:#define __wasm_multimemory__
// MVP-NOT:#define __wasm_relaxed_simd__

// RUN: %clang -E -dM %s -o - 2>&1 \
// RUN: -target wasm32-unknown-unknown -mcpu=generic \
// RUN: | FileCheck %s -check-prefix=GENERIC
// RUN: %clang -E -dM %s -o - 2>&1 \
// RUN: -target wasm64-unknown-unknown -mcpu=generic \
// RUN: | FileCheck %s -check-prefix=GENERIC
//
// GENERIC-DAG:#define __wasm_sign_ext__ 1{{$}}
// GENERIC-DAG:#define __wasm_mutable_globals__ 1{{$}}
// GENERIC-NOT:#define __wasm_nontrapping_fptoint__ 1{{$}}
// GENERIC-NOT:#define __wasm_bulk_memory__ 1{{$}}
// GENERIC-NOT:#define __wasm_simd128__ 1{{$}}
// GENERIC-NOT:#define __wasm_atomics__ 1{{$}}
// GENERIC-NOT:#define __wasm_tail_call__ 1{{$}}
// GENERIC-NOT:#define __wasm_multimemory__ 1{{$}}
// GENERIC-NOT:#define __wasm_exception_handling__ 1{{$}}
// GENERIC-NOT:#define __wasm_multivalue__ 1{{$}}
// GENERIC-NOT:#define __wasm_reference_types__ 1{{$}}
// GENERIC-NOT:#define __wasm_extended_const__ 1{{$}}

// RUN: %clang -E -dM %s -o - 2>&1 \
// RUN: -target wasm32-unknown-unknown -mcpu=bleeding-edge \
Expand All @@ -160,11 +181,11 @@
// BLEEDING-EDGE-DAG:#define __wasm_mutable_globals__ 1{{$}}
// BLEEDING-EDGE-DAG:#define __wasm_tail_call__ 1{{$}}
// BLEEDING-EDGE-DAG:#define __wasm_multimemory__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_unimplemented_simd128__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_exception_handling__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_multivalue__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_reference_types__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_extended_const__ 1{{$}}
// BLEEDING-EDGE-NOT:#define __wasm_relaxed_simd__ 1{{$}}

// RUN: %clang -E -dM %s -o - 2>&1 \
// RUN: -target wasm32-unknown-unknown -mcpu=bleeding-edge -mno-simd128 \
Expand Down
1 change: 1 addition & 0 deletions clang/test/Sema/struct-cast.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only %s -verify
// RUN: %clang_cc1 -fsyntax-only %s -fexperimental-new-constant-interpreter -verify
// expected-no-diagnostics

struct S {
Expand Down
60 changes: 53 additions & 7 deletions clang/test/Sema/warn-infinity-nan-disabled-lnx.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs -menable-no-nans
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
// RUN: -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown %s
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs
// RUN: -menable-no-infs -funsafe-math-optimizations -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-nans
// RUN: -funsafe-math-optimizations -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -Wno-nan-infinity-disabled -menable-no-infs -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -Wno-nan-infinity-disabled -menable-no-nans -std=c++23

// no-fast-no-diagnostics

Expand Down Expand Up @@ -133,13 +151,41 @@ int compareit(float a, float b) {
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
p = __builtin_isfinite(a);

// These should NOT warn, since they are not using NaN or infinity.
// These should NOT warn, since they are not using NaN or infinity.
j = a > 1.1;
j = b < 1.1;
j = a >= 1.1;
j = b <= 1.1;
j = isunorderedf(a, b);

#ifndef INFINITY
j = a;
#endif
#ifndef NAN
j = b;
#endif
#ifdef INFINITY
j = a;
#endif
#ifdef NAN
j = b;
#endif
#if defined(INFINITY)
j = a;
#elifndef(INFINITY)
j = b;
#endif
#if defined(INFINITY)
j = a;
#elifndef(NAN)
j = b;
#endif
#if defined(NAN)
j = a;
#elifndef(INFINITY)
j = b;
#endif

// no-inf-no-nan-warning@+4 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
// no-inf-no-nan-warning@+3 {{use of NaN is undefined behavior due to the currently enabled floating-point options}}
// no-nan-warning@+2 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
Expand Down Expand Up @@ -173,4 +219,4 @@ int compareit(float a, float b) {
j = numeric_limits<float>::infinity();
return 0;

}
}
60 changes: 53 additions & 7 deletions clang/test/Sema/warn-infinity-nan-disabled-win.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
// Use of NAN macro will trigger a warning "infinity defined in macro" because
// on Windows the NAN macro is defined using INFINITY. See below.

// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs -menable-no-nans
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
// RUN: -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown %s
// RUN: %clang_cc1 -x c++ -verify=no-inf-no-nan \
// RUN: -triple powerpc64le-unknown-unknown %s -menable-no-infs \
// RUN: -menable-no-nans -funsafe-math-optimizations -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-inf -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-infs
// RUN: -menable-no-infs -funsafe-math-optimizations -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-nan -triple powerpc64le-unknown-unknown %s \
// RUN: -menable-no-nans
// RUN: -funsafe-math-optimizations -menable-no-nans -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -Wno-nan-infinity-disabled -menable-no-infs -std=c++23

// RUN: %clang_cc1 -x c++ -verify=no-fast -triple powerpc64le-unknown-unknown \
// RUN: %s -Wno-nan-infinity-disabled -menable-no-nans -std=c++23

// no-fast-no-diagnostics

Expand Down Expand Up @@ -136,13 +154,41 @@ int compareit(float a, float b) {
// no-inf-warning@+1 {{use of infinity is undefined behavior due to the currently enabled floating-point options}}
p = __builtin_isfinite(a);

// These should NOT warn, since they are not using NaN or infinity.
// These should NOT warn, since they are not using NaN or infinity.
j = a > 1.1;
j = b < 1.1;
j = a >= 1.1;
j = b <= 1.1;
j = isunorderedf(a, b);

#ifndef INFINITY
j = a;
#endif
#ifndef NAN
j = b;
#endif
#ifdef INFINITY
j = a;
#endif
#ifdef NAN
j = b;
#endif
#if defined(INFINITY)
j = a;
#elifndef(INFINITY)
j = b;
#endif
#if defined(INFINITY)
j = a;
#elifndef(NAN)
j = b;
#endif
#if defined(NAN)
j = a;
#elifndef(INFINITY)
j = b;
#endif

// no-inf-no-nan-warning@+4 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point option}}
// no-inf-no-nan-warning@+3 {{use of NaN via a macro is undefined behavior due to the currently enabled floating-point options}}
// no-inf-warning@+2 {{use of infinity via a macro is undefined behavior due to the currently enabled floating-point options}}
Expand Down Expand Up @@ -176,4 +222,4 @@ int compareit(float a, float b) {
j = numeric_limits<float>::infinity();
return 0;

}
}
1 change: 1 addition & 0 deletions clang/test/SemaCXX/cxx1z-copy-omission.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++1z -verify -Wno-unused %s
// RUN: %clang_cc1 -std=c++1z -verify -Wno-unused %s -fexperimental-new-constant-interpreter

struct Noncopyable {
Noncopyable();
Expand Down
3 changes: 2 additions & 1 deletion clang/test/SemaCXX/cxx20-using-enum.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s -fexperimental-new-constant-interpreter
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s -fexperimental-new-constant-interpreter

// p1099 'using enum ELABORATED-ENUM-SPECIFIER ;'

Expand Down
10 changes: 10 additions & 0 deletions clang/test/SemaCXX/cxx2b-deducing-this.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,13 @@ struct D {
}
};
}

namespace GH80971 {
struct S {
auto f(this auto self...) { }
};

int bug() {
S{}.f(0);
}
}
7 changes: 7 additions & 0 deletions clang/test/SemaCXX/warn-unsafe-buffer-usage-debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,10 @@ void test_struct_claim_use() {
x[6] = 8; // expected-warning{{unsafe buffer access}}
x++; // expected-warning{{unsafe pointer arithmetic}}
}

void use(int*);
void array2d(int idx) {
int buffer[10][5]; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
use(buffer[idx]); // expected-note{{used in buffer access here}} \
// debug-note{{safe buffers debug: failed to produce fixit for 'buffer' : has an unclaimed use}}
}
77 changes: 70 additions & 7 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,70 @@ Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
return *Path;
}

/// Returns the hashed value for a constant string.
std::string getHash(StringRef Str) {
llvm::MD5 Hasher;
llvm::MD5::MD5Result Hash;
Hasher.update(Str);
Hasher.final(Hash);
return llvm::utohexstr(Hash.low(), /*LowerCase=*/true);
}

/// Renames offloading entry sections in a relocatable link so they do not
/// conflict with a later link job.
Error relocateOffloadSection(const ArgList &Args, StringRef Output) {
llvm::Triple Triple(
Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
if (Triple.isOSWindows())
return createStringError(
inconvertibleErrorCode(),
"Relocatable linking is not supported on COFF targets");

Expected<std::string> ObjcopyPath =
findProgram("llvm-objcopy", {getMainExecutable("llvm-objcopy")});
if (!ObjcopyPath)
return ObjcopyPath.takeError();

// Use the linker output file to get a unique hash. This creates a unique
// identifier to rename the sections to that is deterministic to the contents.
auto BufferOrErr = DryRun ? MemoryBuffer::getMemBuffer("")
: MemoryBuffer::getFileOrSTDIN(Output);
if (!BufferOrErr)
return createStringError(inconvertibleErrorCode(), "Failed to open %s",
Output.str().c_str());
std::string Suffix = "_" + getHash((*BufferOrErr)->getBuffer());

SmallVector<StringRef> ObjcopyArgs = {
*ObjcopyPath,
Output,
};

// Remove the old .llvm.offloading section to prevent further linking.
ObjcopyArgs.emplace_back("--remove-section");
ObjcopyArgs.emplace_back(".llvm.offloading");
for (StringRef Prefix : {"omp", "cuda", "hip"}) {
auto Section = (Prefix + "_offloading_entries").str();
// Rename the offloading entires to make them private to this link unit.
ObjcopyArgs.emplace_back("--rename-section");
ObjcopyArgs.emplace_back(
Args.MakeArgString(Section + "=" + Section + Suffix));

// Rename the __start_ / __stop_ symbols appropriately to iterate over the
// newly renamed section containing the offloading entries.
ObjcopyArgs.emplace_back("--redefine-sym");
ObjcopyArgs.emplace_back(Args.MakeArgString("__start_" + Section + "=" +
"__start_" + Section + Suffix));
ObjcopyArgs.emplace_back("--redefine-sym");
ObjcopyArgs.emplace_back(Args.MakeArgString("__stop_" + Section + "=" +
"__stop_" + Section + Suffix));
}

if (Error Err = executeCommands(*ObjcopyPath, ObjcopyArgs))
return Err;

return Error::success();
}

/// Runs the wrapped linker job with the newly created input.
Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {
llvm::TimeTraceScope TimeScope("Execute host linker");
Expand All @@ -265,6 +329,10 @@ Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {
LinkerArgs.push_back(Arg);
if (Error Err = executeCommands(LinkerPath, LinkerArgs))
return Err;

if (Args.hasArg(OPT_relocatable))
return relocateOffloadSection(Args, ExecutableName);

return Error::success();
}

Expand Down Expand Up @@ -910,7 +978,8 @@ wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
case OFK_OpenMP:
if (Error Err = offloading::wrapOpenMPBinaries(
M, BuffersToWrap,
offloading::getOffloadEntryArray(M, "omp_offloading_entries")))
offloading::getOffloadEntryArray(M, "omp_offloading_entries"),
/*Suffix=*/"", /*Relocatable=*/Args.hasArg(OPT_relocatable)))
return std::move(Err);
break;
case OFK_Cuda:
Expand Down Expand Up @@ -1356,12 +1425,6 @@ Expected<SmallVector<SmallVector<OffloadFile>>>
getDeviceInput(const ArgList &Args) {
llvm::TimeTraceScope TimeScope("ExtractDeviceCode");

// If the user is requesting a reloctable link we ignore the device code. The
// actual linker will merge the embedded device code sections so they can be
// linked when the executable is finally created.
if (Args.hasArg(OPT_relocatable))
return SmallVector<SmallVector<OffloadFile>>{};

StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);
SmallVector<StringRef> LibraryPaths;
for (const opt::Arg *Arg : Args.filtered(OPT_library_path, OPT_libpath))
Expand Down
5 changes: 3 additions & 2 deletions clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,9 @@ def version : Flag<["--", "-"], "version">, Flags<[HelpHidden]>, Alias<v>;
def whole_archive : Flag<["--", "-"], "whole-archive">, Flags<[HelpHidden]>;
def no_whole_archive : Flag<["--", "-"], "no-whole-archive">, Flags<[HelpHidden]>;

def relocatable : Flag<["--", "-"], "relocatable">, Flags<[HelpHidden]>;
def r : Flag<["-"], "r">, Alias<relocatable>, Flags<[HelpHidden]>;
def relocatable : Flag<["--", "-"], "relocatable">,
HelpText<"Link device code to create a relocatable offloading application">;
def r : Flag<["-"], "r">, Alias<relocatable>;

// link.exe-style linker options.
def out : Joined<["/", "-", "/?", "-?"], "out:">, Flags<[HelpHidden]>;
Expand Down
58 changes: 28 additions & 30 deletions clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,60 +364,58 @@ class SignPropagationAnalysis
LatticeTransferState State(L, Env);
TransferMatchSwitch(Elt, getASTContext(), State);
}
bool merge(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) override;
void join(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) override;

private:
CFGMatchSwitch<TransferState<NoopLattice>> TransferMatchSwitch;
};

// Copied from crubit.
BoolValue &mergeBoolValues(BoolValue &Bool1, const Environment &Env1,
BoolValue &Bool2, const Environment &Env2,
Environment &MergedEnv) {
BoolValue &joinBoolValues(BoolValue &Bool1, const Environment &Env1,
BoolValue &Bool2, const Environment &Env2,
Environment &JoinedEnv) {
if (&Bool1 == &Bool2) {
return Bool1;
}

auto &B1 = Bool1.formula();
auto &B2 = Bool2.formula();

auto &A = MergedEnv.arena();
auto &MergedBool = MergedEnv.makeAtomicBoolValue();
auto &A = JoinedEnv.arena();
auto &JoinedBool = JoinedEnv.makeAtomicBoolValue();

// If `Bool1` and `Bool2` is constrained to the same true / false value,
// `MergedBool` can be constrained similarly without needing to consider the
// path taken - this simplifies the flow condition tracked in `MergedEnv`.
// `JoinedBool` can be constrained similarly without needing to consider the
// path taken - this simplifies the flow condition tracked in `JoinedEnv`.
// Otherwise, information about which path was taken is used to associate
// `MergedBool` with `Bool1` and `Bool2`.
// `JoinedBool` with `Bool1` and `Bool2`.
if (Env1.proves(B1) && Env2.proves(B2)) {
MergedEnv.assume(MergedBool.formula());
JoinedEnv.assume(JoinedBool.formula());
} else if (Env1.proves(A.makeNot(B1)) && Env2.proves(A.makeNot(B2))) {
MergedEnv.assume(A.makeNot(MergedBool.formula()));
JoinedEnv.assume(A.makeNot(JoinedBool.formula()));
}
return MergedBool;
return JoinedBool;
}

bool SignPropagationAnalysis::merge(QualType Type, const Value &Val1,
const Environment &Env1, const Value &Val2,
const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) {
void SignPropagationAnalysis::join(QualType Type, const Value &Val1,
const Environment &Env1, const Value &Val2,
const Environment &Env2, Value &JoinedVal,
Environment &JoinedEnv) {
if (!Type->isIntegerType())
return false;
return;
SignProperties Ps1 = getSignProperties(Val1, Env1);
SignProperties Ps2 = getSignProperties(Val2, Env2);
if (!Ps1.Neg || !Ps2.Neg)
return false;
BoolValue &MergedNeg =
mergeBoolValues(*Ps1.Neg, Env1, *Ps2.Neg, Env2, MergedEnv);
BoolValue &MergedZero =
mergeBoolValues(*Ps1.Zero, Env1, *Ps2.Zero, Env2, MergedEnv);
BoolValue &MergedPos =
mergeBoolValues(*Ps1.Pos, Env1, *Ps2.Pos, Env2, MergedEnv);
setSignProperties(MergedVal,
SignProperties{&MergedNeg, &MergedZero, &MergedPos});
return true;
return;
BoolValue &JoinedNeg =
joinBoolValues(*Ps1.Neg, Env1, *Ps2.Neg, Env2, JoinedEnv);
BoolValue &JoinedZero =
joinBoolValues(*Ps1.Zero, Env1, *Ps2.Zero, Env2, JoinedEnv);
BoolValue &JoinedPos =
joinBoolValues(*Ps1.Pos, Env1, *Ps2.Pos, Env2, JoinedEnv);
setSignProperties(JoinedVal,
SignProperties{&JoinedNeg, &JoinedZero, &JoinedPos});
}

template <typename Matcher>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,26 +672,23 @@ class NullPointerAnalysis final
: ComparisonResult::Different;
}

bool merge(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2, Value &MergedVal,
Environment &MergedEnv) override {
// Nothing to say about a value that is not a pointer.
void join(QualType Type, const Value &Val1, const Environment &Env1,
const Value &Val2, const Environment &Env2, Value &JoinedVal,
Environment &JoinedEnv) override {
// Nothing to say about a value that is not a pointer...
if (!Type->isPointerType())
return false;
return;

// ... or, a pointer without the `is_null` property.
auto *IsNull1 = cast_or_null<BoolValue>(Val1.getProperty("is_null"));
if (IsNull1 == nullptr)
return false;

auto *IsNull2 = cast_or_null<BoolValue>(Val2.getProperty("is_null"));
if (IsNull2 == nullptr)
return false;
if (IsNull1 == nullptr || IsNull2 == nullptr)
return;

if (IsNull1 == IsNull2)
MergedVal.setProperty("is_null", *IsNull1);
JoinedVal.setProperty("is_null", *IsNull1);
else
MergedVal.setProperty("is_null", MergedEnv.makeTopBoolValue());
return true;
JoinedVal.setProperty("is_null", JoinedEnv.makeTopBoolValue());
}
};

Expand Down Expand Up @@ -1176,7 +1173,7 @@ TEST_F(FlowConditionTest, Join) {
// Note: currently, arbitrary function calls are uninterpreted, so the test
// exercises this case. If and when we change that, this test will not add to
// coverage (although it may still test a valuable case).
TEST_F(FlowConditionTest, OpaqueFlowConditionMergesToOpaqueBool) {
TEST_F(FlowConditionTest, OpaqueFlowConditionJoinsToOpaqueBool) {
std::string Code = R"(
bool foo();

Expand Down Expand Up @@ -1211,7 +1208,7 @@ TEST_F(FlowConditionTest, OpaqueFlowConditionMergesToOpaqueBool) {
// the first instance), so the test exercises this case. If and when we change
// that, this test will not add to coverage (although it may still test a
// valuable case).
TEST_F(FlowConditionTest, OpaqueFieldFlowConditionMergesToOpaqueBool) {
TEST_F(FlowConditionTest, OpaqueFieldFlowConditionJoinsToOpaqueBool) {
std::string Code = R"(
struct Rec {
Rec* Next;
Expand Down Expand Up @@ -1249,7 +1246,7 @@ TEST_F(FlowConditionTest, OpaqueFieldFlowConditionMergesToOpaqueBool) {
// condition is not meaningfully interpreted. Adds to above by nesting the
// interestnig case inside a normal branch. This protects against degenerate
// solutions which only test for empty flow conditions, for example.
TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchMergesToOpaqueBool) {
TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchJoinsToOpaqueBool) {
std::string Code = R"(
bool foo();

Expand Down
Loading