19 changes: 18 additions & 1 deletion clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ using namespace CodeGen;
// Experiment to make sanitizers easier to debug
static llvm::cl::opt<bool> ClSanitizeDebugDeoptimization(
"ubsan-unique-traps", llvm::cl::Optional,
llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check"));
llvm::cl::desc("Deoptimize traps for UBSAN so there is 1 trap per check."));

// TODO: Introduce frontend options to enabled per sanitizers, similar to
// `fsanitize-trap`.
static llvm::cl::opt<bool> ClSanitizeGuardChecks(
"ubsan-guard-checks", llvm::cl::Optional,
llvm::cl::desc("Guard UBSAN checks with `llvm.allow.ubsan.check()`."));

//===--------------------------------------------------------------------===//
// Miscellaneous Helper Methods
Expand Down Expand Up @@ -3522,6 +3528,17 @@ void CodeGenFunction::EmitCheck(
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
}

if (ClSanitizeGuardChecks) {
llvm::Value *Allow =
Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::allow_ubsan_check),
llvm::ConstantInt::get(CGM.Int8Ty, CheckHandler));

for (llvm::Value **Cond : {&FatalCond, &RecoverableCond, &TrapCond}) {
if (*Cond)
*Cond = Builder.CreateOr(*Cond, Builder.CreateNot(Allow));
}
}

if (TrapCond)
EmitTrapCheck(TrapCond, CheckHandler);
if (!FatalCond && !RecoverableCond)
Expand Down
20 changes: 6 additions & 14 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#include "CodeGenModule.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/TargetOptions.h"
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/IntrinsicsSPIRV.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FormatVariadic.h"
Expand Down Expand Up @@ -117,6 +115,10 @@ GlobalVariable *replaceBuffer(CGHLSLRuntime::Buffer &Buf) {

} // namespace

llvm::Triple::ArchType CGHLSLRuntime::getArch() {
return CGM.getTarget().getTriple().getArch();
}

void CGHLSLRuntime::addConstant(VarDecl *D, Buffer &CB) {
if (D->getStorageClass() == SC_Static) {
// For static inside cbuffer, take as global static.
Expand Down Expand Up @@ -343,18 +345,8 @@ llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
return B.CreateCall(FunctionCallee(DxGroupIndex));
}
if (D.hasAttr<HLSLSV_DispatchThreadIDAttr>()) {
llvm::Function *ThreadIDIntrinsic;
switch (CGM.getTarget().getTriple().getArch()) {
case llvm::Triple::dxil:
ThreadIDIntrinsic = CGM.getIntrinsic(Intrinsic::dx_thread_id);
break;
case llvm::Triple::spirv:
ThreadIDIntrinsic = CGM.getIntrinsic(Intrinsic::spv_thread_id);
break;
default:
llvm_unreachable("Input semantic not supported by target");
break;
}
llvm::Function *ThreadIDIntrinsic =
CGM.getIntrinsic(getThreadIdIntrinsic());
return buildVectorInput(B, ThreadIDIntrinsic, Ty);
}
assert(false && "Unhandled parameter attribute");
Expand Down
32 changes: 32 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
#define LLVM_CLANG_LIB_CODEGEN_CGHLSLRUNTIME_H

#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsDirectX.h"
#include "llvm/IR/IntrinsicsSPIRV.h"

#include "clang/Basic/Builtins.h"
#include "clang/Basic/HLSLRuntime.h"

#include "llvm/ADT/SmallVector.h"
Expand All @@ -26,6 +30,22 @@
#include <optional>
#include <vector>

// A function generator macro for picking the right intrinsic
// for the target backend
#define GENERATE_HLSL_INTRINSIC_FUNCTION(FunctionName, IntrinsicPostfix) \
llvm::Intrinsic::ID get##FunctionName##Intrinsic() { \
llvm::Triple::ArchType Arch = getArch(); \
switch (Arch) { \
case llvm::Triple::dxil: \
return llvm::Intrinsic::dx_##IntrinsicPostfix; \
case llvm::Triple::spirv: \
return llvm::Intrinsic::spv_##IntrinsicPostfix; \
default: \
llvm_unreachable("Intrinsic " #IntrinsicPostfix \
" not supported by target architecture"); \
} \
}

namespace llvm {
class GlobalVariable;
class Function;
Expand All @@ -48,6 +68,17 @@ class CodeGenModule;

class CGHLSLRuntime {
public:
//===----------------------------------------------------------------------===//
// Start of reserved area for HLSL intrinsic getters.
//===----------------------------------------------------------------------===//

GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)

//===----------------------------------------------------------------------===//
// End of reserved area for HLSL intrinsic getters.
//===----------------------------------------------------------------------===//

struct BufferResBinding {
// The ID like 2 in register(b2, space1).
std::optional<unsigned> Reg;
Expand Down Expand Up @@ -96,6 +127,7 @@ class CGHLSLRuntime {
BufferResBinding &Binding);
void addConstant(VarDecl *D, Buffer &CB);
void addBufferDecls(const DeclContext *DC, Buffer &CB);
llvm::Triple::ArchType getArch();
llvm::SmallVector<Buffer> Buffers;
};

Expand Down
9 changes: 8 additions & 1 deletion clang/lib/CodeGen/CodeGenTBAA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "clang/AST/Mangle.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/LLVMContext.h"
Expand Down Expand Up @@ -319,7 +320,13 @@ CodeGenTBAA::CollectFields(uint64_t BaseOffset,
// base type.
if ((*i)->isBitField()) {
const CGBitFieldInfo &Info = CGRL.getBitFieldInfo(*i);
if (Info.Offset != 0)
// For big endian targets the first bitfield in the consecutive run is
// at the most-significant end; see CGRecordLowering::setBitFieldInfo
// for more information.
bool IsBE = Context.getTargetInfo().isBigEndian();
bool IsFirst = IsBE ? Info.StorageSize - (Info.Offset + Info.Size) == 0
: Info.Offset == 0;
if (!IsFirst)
continue;
unsigned CurrentBitFieldSize = Info.StorageSize;
uint64_t Size =
Expand Down
62 changes: 41 additions & 21 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,18 @@ static void addDashXForInput(const ArgList &Args, const InputInfo &Input,

void Flang::addFortranDialectOptions(const ArgList &Args,
ArgStringList &CmdArgs) const {
Args.addAllArgs(CmdArgs, {options::OPT_ffixed_form,
options::OPT_ffree_form,
options::OPT_ffixed_line_length_EQ,
options::OPT_fopenmp,
options::OPT_fopenmp_version_EQ,
options::OPT_fopenacc,
options::OPT_finput_charset_EQ,
options::OPT_fimplicit_none,
options::OPT_fno_implicit_none,
options::OPT_fbackslash,
options::OPT_fno_backslash,
options::OPT_flogical_abbreviations,
options::OPT_fno_logical_abbreviations,
options::OPT_fxor_operator,
options::OPT_fno_xor_operator,
options::OPT_falternative_parameter_statement,
options::OPT_fdefault_real_8,
options::OPT_fdefault_integer_8,
options::OPT_fdefault_double_8,
options::OPT_flarge_sizes,
options::OPT_fno_automatic});
Args.addAllArgs(
CmdArgs, {options::OPT_ffixed_form, options::OPT_ffree_form,
options::OPT_ffixed_line_length_EQ, options::OPT_fopenacc,
options::OPT_finput_charset_EQ, options::OPT_fimplicit_none,
options::OPT_fno_implicit_none, options::OPT_fbackslash,
options::OPT_fno_backslash, options::OPT_flogical_abbreviations,
options::OPT_fno_logical_abbreviations,
options::OPT_fxor_operator, options::OPT_fno_xor_operator,
options::OPT_falternative_parameter_statement,
options::OPT_fdefault_real_8, options::OPT_fdefault_integer_8,
options::OPT_fdefault_double_8, options::OPT_flarge_sizes,
options::OPT_fno_automatic});
}

void Flang::addPreprocessingOptions(const ArgList &Args,
Expand Down Expand Up @@ -763,6 +754,35 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
// Add other compile options
addOtherOptions(Args, CmdArgs);

// Forward flags for OpenMP. We don't do this if the current action is an
// device offloading action other than OpenMP.
if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
options::OPT_fno_openmp, false) &&
(JA.isDeviceOffloading(Action::OFK_None) ||
JA.isDeviceOffloading(Action::OFK_OpenMP))) {
switch (D.getOpenMPRuntime(Args)) {
case Driver::OMPRT_OMP:
case Driver::OMPRT_IOMP5:
// Clang can generate useful OpenMP code for these two runtime libraries.
CmdArgs.push_back("-fopenmp");
Args.AddAllArgs(CmdArgs, options::OPT_fopenmp_version_EQ);

// FIXME: Clang supports a whole bunch more flags here.
break;
default:
// By default, if Clang doesn't know how to generate useful OpenMP code
// for a specific runtime library, we just don't pass the '-fopenmp' flag
// down to the actual compilation.
// FIXME: It would be better to have a mode which *only* omits IR
// generation based on the OpenMP support so that we get consistent
// semantic analysis, etc.
const Arg *A = Args.getLastArg(options::OPT_fopenmp_EQ);
D.Diag(diag::warn_drv_unsupported_openmp_library)
<< A->getSpelling() << A->getValue();
break;
}
}

// Offloading related options
addOffloadOptions(C, Inputs, JA, Args, CmdArgs);

Expand Down
17 changes: 10 additions & 7 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,20 +495,22 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
};
SmallVector<StackEntry, 8> LBraceStack;
assert(Tok->is(tok::l_brace));

do {
// Get next non-comment, non-preprocessor token.
FormatToken *NextTok;
do {
NextTok = Tokens->getNextToken();
} while (NextTok->is(tok::comment));
if (!Style.isTableGen()) {
// InTableGen, '#' is like binary operator. Not a preprocessor directive.
while (NextTok->is(tok::hash) && !Line->InMacroBody) {
NextTok = Tokens->getNextToken();

if (!Line->InMacroBody && !Style.isTableGen()) {
// Skip PPDirective lines and comments.
while (NextTok->is(tok::hash)) {
do {
NextTok = Tokens->getNextToken();
} while (NextTok->is(tok::comment) ||
(NextTok->NewlinesBefore == 0 && NextTok->isNot(tok::eof)));
} while (NextTok->NewlinesBefore == 0 && NextTok->isNot(tok::eof));

while (NextTok->is(tok::comment))
NextTok = Tokens->getNextToken();
}
}

Expand Down Expand Up @@ -640,6 +642,7 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
default:
break;
}

PrevTok = Tok;
Tok = NextTok;
} while (Tok->isNot(tok::eof) && !LBraceStack.empty());
Expand Down
16 changes: 8 additions & 8 deletions clang/lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -533,10 +533,10 @@ static T extractMaskValue(T KeyPath) {

#define PARSE_OPTION_WITH_MARSHALLING( \
ARGS, DIAGS, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS, \
FLAGS, VISIBILITY, PARAM, HELPTEXT, METAVAR, VALUES, SHOULD_PARSE, \
ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, \
NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
if ((VISIBILITY)&options::CC1Option) { \
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
if ((VISIBILITY) & options::CC1Option) { \
KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE); \
if (IMPLIED_CHECK) \
KEYPATH = MERGER(KEYPATH, IMPLIED_VALUE); \
Expand All @@ -550,10 +550,10 @@ static T extractMaskValue(T KeyPath) {
// with lifetime extension of the reference.
#define GENERATE_OPTION_WITH_MARSHALLING( \
CONSUMER, PREFIX_TYPE, SPELLING, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
VISIBILITY, PARAM, HELPTEXT, METAVAR, VALUES, SHOULD_PARSE, ALWAYS_EMIT, \
KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, \
DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
if ((VISIBILITY)&options::CC1Option) { \
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
if ((VISIBILITY) & options::CC1Option) { \
[&](const auto &Extracted) { \
if (ALWAYS_EMIT || \
(Extracted != \
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "BuildingBuiltinDumpStructCall";
case CodeSynthesisContext::BuildingDeductionGuides:
return "BuildingDeductionGuides";
case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation:
return "TypeAliasTemplateInstantiation";
}
return "";
}
Expand Down
112 changes: 112 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,118 @@ double3 abs(double3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_abs)
double4 abs(double4);

//===----------------------------------------------------------------------===//
// all builtins
//===----------------------------------------------------------------------===//

/// \fn bool all(T x)
/// \brief Returns True if all components of the \a x parameter are non-zero;
/// otherwise, false. \param x The input value.

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

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(half);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(half2);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(half3);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(half4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(bool);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(bool2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(bool3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(bool4);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(float);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(float2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(float3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(float4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int64_t);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int64_t2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int64_t3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(int64_t4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint64_t);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint64_t2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint64_t3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(uint64_t4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(double);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(double2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(double3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_all)
bool all(double4);

//===----------------------------------------------------------------------===//
// any builtins
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5563,6 +5563,7 @@ void SetElementTypeAsReturnType(Sema *S, CallExpr *TheCall,
// returning an ExprError
bool Sema::CheckHLSLBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
switch (BuiltinID) {
case Builtin::BI__builtin_hlsl_elementwise_all:
case Builtin::BI__builtin_hlsl_elementwise_any: {
if (checkArgCount(*this, TheCall, 1))
return true;
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -615,10 +615,12 @@ bool Sema::SetupConstraintScope(
// reference the original primary template.
// We walk up the instantiated template chain so that nested lambdas get
// handled properly.
for (FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate();
FromMemTempl;
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) {
// We should only collect instantiated parameters from the primary template.
// Otherwise, we may have mismatched template parameter depth!
if (FunctionTemplateDecl *FromMemTempl =
PrimaryTemplate->getInstantiatedFromMemberTemplate()) {
while (FromMemTempl->getInstantiatedFromMemberTemplate())
FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate();
if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(),
Scope, MLTAL))
return true;
Expand Down
21 changes: 19 additions & 2 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1537,6 +1537,10 @@ void Sema::PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext) {
cast<FunctionDecl>(D)->isFunctionTemplateSpecialization())
return;

if (isa<UsingEnumDecl>(D) && D->getDeclName().isEmpty()) {
S->AddDecl(D);
return;
}
// If this replaces anything in the current scope,
IdentifierResolver::iterator I = IdResolver.begin(D->getDeclName()),
IEnd = IdResolver.end();
Expand Down Expand Up @@ -2188,8 +2192,21 @@ void Sema::DiagnoseUnusedButSetDecl(const VarDecl *VD,

assert(iter->getSecond() >= 0 &&
"Found a negative number of references to a VarDecl");
if (iter->getSecond() != 0)
return;
if (int RefCnt = iter->getSecond(); RefCnt > 0) {
// Assume the given VarDecl is "used" if its ref count stored in
// `RefMinusAssignments` is positive, with one exception.
//
// For a C++ variable whose decl (with initializer) entirely consist the
// condition expression of a if/while/for construct,
// Clang creates a DeclRefExpr for the condition expression rather than a
// BinaryOperator of AssignmentOp. Thus, the C++ variable's ref
// count stored in `RefMinusAssignment` equals 1 when the variable is never
// used in the body of the if/while/for construct.
bool UnusedCXXCondDecl = VD->isCXXCondDecl() && (RefCnt == 1);
if (!UnusedCXXCondDecl)
return;
}

unsigned DiagID = isa<ParmVarDecl>(VD) ? diag::warn_unused_but_set_parameter
: diag::warn_unused_but_set_variable;
DiagReceiver(VD->getLocation(), PDiag(DiagID) << VD);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18564,6 +18564,9 @@ DeclResult Sema::ActOnCXXConditionDeclaration(Scope *S, Declarator &D) {
return true;
}

if (auto *VD = dyn_cast<VarDecl>(Dcl))
VD->setCXXCondDecl();

return Dcl;
}

Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4383,6 +4383,11 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
if (Inst.isInvalid())
return QualType();

InstantiatingTemplate InstTemplate(
*this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(),
/*Template=*/AliasTemplate,
/*TemplateArgs=*/TemplateArgLists.getInnermost());

std::optional<ContextRAII> SavedContext;
if (!AliasTemplate->getDeclContext()->isFileContext())
SavedContext.emplace(*this, AliasTemplate->getDeclContext());
Expand Down
160 changes: 152 additions & 8 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,81 @@ struct Response {
return R;
}
};

// Retrieve the primary template for a lambda call operator. It's
// unfortunate that we only have the mappings of call operators rather
// than lambda classes.
const FunctionDecl *
getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) {
while (true) {
if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>(
LambdaCallOperator->getDescribedTemplate());
FTD && FTD->getInstantiatedFromMemberTemplate()) {
LambdaCallOperator =
FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl();
} else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator)
->getInstantiatedFromMemberFunction())
LambdaCallOperator = Prev;
else
break;
}
return LambdaCallOperator;
}

struct EnclosingTypeAliasTemplateDetails {
TypeAliasTemplateDecl *Template = nullptr;
TypeAliasTemplateDecl *PrimaryTypeAliasDecl = nullptr;
ArrayRef<TemplateArgument> AssociatedTemplateArguments;

explicit operator bool() noexcept { return Template; }
};

// Find the enclosing type alias template Decl from CodeSynthesisContexts, as
// well as its primary template and instantiating template arguments.
EnclosingTypeAliasTemplateDetails
getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) {
for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) {
if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind::
TypeAliasTemplateInstantiation)
continue;
EnclosingTypeAliasTemplateDetails Result;
auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity),
*Next = TATD->getInstantiatedFromMemberTemplate();
Result = {
/*Template=*/TATD,
/*PrimaryTypeAliasDecl=*/TATD,
/*AssociatedTemplateArguments=*/CSC.template_arguments(),
};
while (Next) {
Result.PrimaryTypeAliasDecl = Next;
Next = Next->getInstantiatedFromMemberTemplate();
}
return Result;
}
return {};
}

// Check if we are currently inside of a lambda expression that is
// surrounded by a using alias declaration. e.g.
// template <class> using type = decltype([](auto) { ^ }());
// By checking if:
// 1. The lambda expression and the using alias declaration share the
// same declaration context.
// 2. They have the same template depth.
// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never
// a DeclContext, nor does it have an associated specialization Decl from which
// we could collect these template arguments.
bool isLambdaEnclosedByTypeAliasDecl(
const FunctionDecl *PrimaryLambdaCallOperator,
const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) {
return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext())
->getTemplateDepth() ==
PrimaryTypeAliasDecl->getTemplateDepth() &&
getLambdaAwareParentOfDeclContext(
const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) ==
PrimaryTypeAliasDecl->getDeclContext();
}

// Add template arguments from a variable template instantiation.
Response
HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
Expand Down Expand Up @@ -176,7 +251,7 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
return Response::UseNextDecl(ClassTemplSpec);
}

Response HandleFunction(const FunctionDecl *Function,
Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
MultiLevelTemplateArgumentList &Result,
const FunctionDecl *Pattern, bool RelativeToPrimary,
bool ForConstraintInstantiation) {
Expand Down Expand Up @@ -207,8 +282,23 @@ Response HandleFunction(const FunctionDecl *Function,

// If this function is a generic lambda specialization, we are done.
if (!ForConstraintInstantiation &&
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function))
isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) {
// TypeAliasTemplateDecls should be taken into account, e.g.
// when we're deducing the return type of a lambda.
//
// template <class> int Value = 0;
// template <class T>
// using T = decltype([]<int U = 0>() { return Value<T>; }());
//
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
if (isLambdaEnclosedByTypeAliasDecl(
/*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda(
Function),
/*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl))
return Response::UseNextDecl(Function);
}
return Response::Done();
}

} else if (Function->getDescribedFunctionTemplate()) {
assert(
Expand Down Expand Up @@ -283,7 +373,7 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD,
return Response::ChangeDecl(FTD->getLexicalDeclContext());
}

Response HandleRecordDecl(const CXXRecordDecl *Rec,
Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec,
MultiLevelTemplateArgumentList &Result,
ASTContext &Context,
bool ForConstraintInstantiation) {
Expand Down Expand Up @@ -312,11 +402,39 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec,
return Response::ChangeDecl(Rec->getLexicalDeclContext());
}

// This is to make sure we pick up the VarTemplateSpecializationDecl that this
// lambda is defined inside of.
if (Rec->isLambda())
// This is to make sure we pick up the VarTemplateSpecializationDecl or the
// TypeAliasTemplateDecl that this lambda is defined inside of.
if (Rec->isLambda()) {
if (const Decl *LCD = Rec->getLambdaContextDecl())
return Response::ChangeDecl(LCD);
// Retrieve the template arguments for a using alias declaration.
// This is necessary for constraint checking, since we always keep
// constraints relative to the primary template.
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) {
const FunctionDecl *PrimaryLambdaCallOperator =
getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator());
if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator,
TypeAlias.PrimaryTypeAliasDecl)) {
Result.addOuterTemplateArguments(TypeAlias.Template,
TypeAlias.AssociatedTemplateArguments,
/*Final=*/false);
// Visit the parent of the current type alias declaration rather than
// the lambda thereof.
// E.g., in the following example:
// struct S {
// template <class> using T = decltype([]<Concept> {} ());
// };
// void foo() {
// S::T var;
// }
// The instantiated lambda expression (which we're visiting at 'var')
// has a function DeclContext 'foo' rather than the Record DeclContext
// S. This seems to be an oversight to me that we may want to set a
// Sema Context from the CXXScopeSpec before substituting into T.
return Response::ChangeDecl(TypeAlias.Template->getDeclContext());
}
}
}

return Response::UseNextDecl(Rec);
}
Expand Down Expand Up @@ -410,10 +528,11 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
SkipForSpecialization);
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary,
ForConstraintInstantiation);
} else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) {
R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation);
R = HandleRecordDecl(*this, Rec, Result, Context,
ForConstraintInstantiation);
} else if (const auto *CSD =
dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) {
R = HandleImplicitConceptSpecializationDecl(CSD, Result);
Expand Down Expand Up @@ -470,6 +589,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case BuildingBuiltinDumpStructCall:
case LambdaExpressionSubstitution:
case BuildingDeductionGuides:
case TypeAliasTemplateInstantiation:
return false;

// This function should never be called when Kind's value is Memoization.
Expand Down Expand Up @@ -615,6 +735,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
PointOfInstantiation, InstantiationRange, Param, Template,
TemplateArgs) {}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs,
SourceRange InstantiationRange)
: InstantiatingTemplate(
SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation,
PointOfInstantiation, InstantiationRange, /*Entity=*/Template,
/*Template=*/nullptr, TemplateArgs) {}

Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template,
NamedDecl *Param, ArrayRef<TemplateArgument> TemplateArgs,
Expand Down Expand Up @@ -1132,6 +1261,8 @@ void Sema::PrintInstantiationStack() {
Diags.Report(Active->PointOfInstantiation,
diag::note_building_deduction_guide_here);
break;
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
break;
}
}
}
Expand Down Expand Up @@ -1209,6 +1340,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const {
break;

case CodeSynthesisContext::Memoization:
case CodeSynthesisContext::TypeAliasTemplateInstantiation:
break;
}

Expand Down Expand Up @@ -1534,6 +1666,18 @@ namespace {
SubstTemplateTypeParmPackTypeLoc TL,
bool SuppressObjCLifetime);

CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
auto &CCS = SemaRef.CodeSynthesisContexts.back();
if (CCS.Kind ==
Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) {
unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth();
if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
}
return inherited::ComputeLambdaDependency(LSI);
}

ExprResult TransformLambdaExpr(LambdaExpr *E) {
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,13 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) {
return nullptr;

TypeAliasDecl *Pattern = D->getTemplatedDecl();
Sema::InstantiatingTemplate InstTemplate(
SemaRef, D->getBeginLoc(), D,
D->getTemplateDepth() >= TemplateArgs.getNumLevels()
? ArrayRef<TemplateArgument>()
: (TemplateArgs.begin() + TemplateArgs.getNumLevels() - 1 -
D->getTemplateDepth())
->Args);

TypeAliasTemplateDecl *PrevAliasTemplate = nullptr;
if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) {
Expand Down
46 changes: 46 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,12 @@ class TreeTransform {
/// the body.
StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);

CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
return static_cast<CXXRecordDecl::LambdaDependencyKind>(
LSI->Lambda->getLambdaDependencyKind());
}

QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);

StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr);
Expand Down Expand Up @@ -13992,6 +13998,46 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
/*IsInstantiation*/ true);
SavedContext.pop();

// Recompute the dependency of the lambda so that we can defer the lambda call
// construction until after we have all the necessary template arguments. For
// example, given
//
// template <class> struct S {
// template <class U>
// using Type = decltype([](U){}(42.0));
// };
// void foo() {
// using T = S<int>::Type<float>;
// ^~~~~~
// }
//
// We would end up here from instantiating S<int> when ensuring its
// completeness. That would transform the lambda call expression regardless of
// the absence of the corresponding argument for U.
//
// Going ahead with unsubstituted type U makes things worse: we would soon
// compare the argument type (which is float) against the parameter U
// somewhere in Sema::BuildCallExpr. Then we would quickly run into a bogus
// error suggesting unmatched types 'U' and 'float'!
//
// That said, everything will be fine if we defer that semantic checking.
// Fortunately, we have such a mechanism that bypasses it if the CallExpr is
// dependent. Since the CallExpr's dependency boils down to the lambda's
// dependency in this case, we can harness that by recomputing the dependency
// from the instantiation arguments.
//
// FIXME: Creating the type of a lambda requires us to have a dependency
// value, which happens before its substitution. We update its dependency
// *after* the substitution in case we can't decide the dependency
// so early, e.g. because we want to see if any of the *substituted*
// parameters are dependent.
DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy);
Class->setLambdaDependencyKind(DependencyKind);
// Clean up the type cache created previously. Then, we re-create a type for
// such Decl with the new DependencyKind.
Class->setTypeForDecl(nullptr);
getSema().Context.getTypeDeclType(Class);

return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(),
&LSICopy);
}
Expand Down
76 changes: 48 additions & 28 deletions clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,34 +124,45 @@ class CStringChecker : public Checker< eval::Call,
const CallEvent &)>;

CallDescriptionMap<FnCheck> Callbacks = {
{{CDM::CLibrary, {"memcpy"}, 3},
{{CDM::CLibraryMaybeHardened, {"memcpy"}, 3},
std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)},
{{CDM::CLibrary, {"wmemcpy"}, 3},
{{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3},
std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)},
{{CDM::CLibrary, {"mempcpy"}, 3},
{{CDM::CLibraryMaybeHardened, {"mempcpy"}, 3},
std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)},
{{CDM::Unspecified, {"wmempcpy"}, 3},
{{CDM::CLibraryMaybeHardened, {"wmempcpy"}, 3},
std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)},
{{CDM::CLibrary, {"memcmp"}, 3},
std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
{{CDM::CLibrary, {"wmemcmp"}, 3},
std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)},
{{CDM::CLibrary, {"memmove"}, 3},
{{CDM::CLibraryMaybeHardened, {"memmove"}, 3},
std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)},
{{CDM::CLibrary, {"wmemmove"}, 3},
{{CDM::CLibraryMaybeHardened, {"wmemmove"}, 3},
std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)},
{{CDM::CLibrary, {"memset"}, 3}, &CStringChecker::evalMemset},
{{CDM::CLibraryMaybeHardened, {"memset"}, 3},
&CStringChecker::evalMemset},
{{CDM::CLibrary, {"explicit_memset"}, 3}, &CStringChecker::evalMemset},
{{CDM::CLibrary, {"strcpy"}, 2}, &CStringChecker::evalStrcpy},
{{CDM::CLibrary, {"strncpy"}, 3}, &CStringChecker::evalStrncpy},
{{CDM::CLibrary, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy},
{{CDM::CLibrary, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy},
{{CDM::CLibrary, {"strcat"}, 2}, &CStringChecker::evalStrcat},
{{CDM::CLibrary, {"strncat"}, 3}, &CStringChecker::evalStrncat},
{{CDM::CLibrary, {"strlcat"}, 3}, &CStringChecker::evalStrlcat},
{{CDM::CLibrary, {"strlen"}, 1}, &CStringChecker::evalstrLength},
// FIXME: C23 introduces 'memset_explicit', maybe also model that
{{CDM::CLibraryMaybeHardened, {"strcpy"}, 2},
&CStringChecker::evalStrcpy},
{{CDM::CLibraryMaybeHardened, {"strncpy"}, 3},
&CStringChecker::evalStrncpy},
{{CDM::CLibraryMaybeHardened, {"stpcpy"}, 2},
&CStringChecker::evalStpcpy},
{{CDM::CLibraryMaybeHardened, {"strlcpy"}, 3},
&CStringChecker::evalStrlcpy},
{{CDM::CLibraryMaybeHardened, {"strcat"}, 2},
&CStringChecker::evalStrcat},
{{CDM::CLibraryMaybeHardened, {"strncat"}, 3},
&CStringChecker::evalStrncat},
{{CDM::CLibraryMaybeHardened, {"strlcat"}, 3},
&CStringChecker::evalStrlcat},
{{CDM::CLibraryMaybeHardened, {"strlen"}, 1},
&CStringChecker::evalstrLength},
{{CDM::CLibrary, {"wcslen"}, 1}, &CStringChecker::evalstrLength},
{{CDM::CLibrary, {"strnlen"}, 2}, &CStringChecker::evalstrnLength},
{{CDM::CLibraryMaybeHardened, {"strnlen"}, 2},
&CStringChecker::evalstrnLength},
{{CDM::CLibrary, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength},
{{CDM::CLibrary, {"strcmp"}, 2}, &CStringChecker::evalStrcmp},
{{CDM::CLibrary, {"strncmp"}, 3}, &CStringChecker::evalStrncmp},
Expand All @@ -162,9 +173,19 @@ class CStringChecker : public Checker< eval::Call,
{{CDM::CLibrary, {"bcmp"}, 3},
std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
{{CDM::CLibrary, {"bzero"}, 2}, &CStringChecker::evalBzero},
{{CDM::CLibrary, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero},
{{CDM::CLibrary, {"sprintf"}, 2}, &CStringChecker::evalSprintf},
{{CDM::CLibrary, {"snprintf"}, 2}, &CStringChecker::evalSnprintf},
{{CDM::CLibraryMaybeHardened, {"explicit_bzero"}, 2},
&CStringChecker::evalBzero},

// When recognizing calls to the following variadic functions, we accept
// any number of arguments in the call (std::nullopt = accept any
// number), but check that in the declaration there are 2 and 3
// parameters respectively. (Note that the parameter count does not
// include the "...". Calls where the number of arguments is too small
// will be discarded by the callback.)
{{CDM::CLibraryMaybeHardened, {"sprintf"}, std::nullopt, 2},
&CStringChecker::evalSprintf},
{{CDM::CLibraryMaybeHardened, {"snprintf"}, std::nullopt, 3},
&CStringChecker::evalSnprintf},
};

// These require a bit of special handling.
Expand Down Expand Up @@ -218,7 +239,7 @@ class CStringChecker : public Checker< eval::Call,
void evalSprintf(CheckerContext &C, const CallEvent &Call) const;
void evalSnprintf(CheckerContext &C, const CallEvent &Call) const;
void evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
bool IsBounded, bool IsBuiltin) const;
bool IsBounded) const;

// Utility methods
std::pair<ProgramStateRef , ProgramStateRef >
Expand Down Expand Up @@ -2467,27 +2488,26 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallEvent &Call) const {
void CStringChecker::evalSprintf(CheckerContext &C,
const CallEvent &Call) const {
CurrentFunctionDescription = "'sprintf'";
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___sprintf_chk;
evalSprintfCommon(C, Call, /* IsBounded */ false, IsBI);
evalSprintfCommon(C, Call, /* IsBounded = */ false);
}

void CStringChecker::evalSnprintf(CheckerContext &C,
const CallEvent &Call) const {
CurrentFunctionDescription = "'snprintf'";
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
bool IsBI = CE->getBuiltinCallee() == Builtin::BI__builtin___snprintf_chk;
evalSprintfCommon(C, Call, /* IsBounded */ true, IsBI);
evalSprintfCommon(C, Call, /* IsBounded = */ true);
}

void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
bool IsBounded, bool IsBuiltin) const {
bool IsBounded) const {
ProgramStateRef State = C.getState();
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};

const auto NumParams = Call.parameters().size();
assert(CE->getNumArgs() >= NumParams);
if (CE->getNumArgs() < NumParams) {
// This is an invalid call, let's just ignore it.
return;
}

const auto AllArguments =
llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
Expand Down
27 changes: 15 additions & 12 deletions clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,20 +718,23 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
{{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
{{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},

{{CDM::CLibrary, {BI.getName(Builtin::BIstrncat)}},
{{CDM::CLibraryMaybeHardened, {BI.getName(Builtin::BIstrncat)}},
TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {BI.getName(Builtin::BIstrlcpy)}},
{{CDM::CLibraryMaybeHardened, {BI.getName(Builtin::BIstrlcpy)}},
TR::Prop({{1, 2}}, {{0}})},
{{CDM::CLibrary, {BI.getName(Builtin::BIstrlcat)}},
{{CDM::CLibraryMaybeHardened, {BI.getName(Builtin::BIstrlcat)}},
TR::Prop({{1, 2}}, {{0}})},
{{CDM::CLibrary, {{"snprintf"}}},
{{CDM::CLibraryMaybeHardened, {{"snprintf"}}},
TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"sprintf"}}},
{{CDM::CLibraryMaybeHardened, {{"sprintf"}}},
TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"strcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"stpcpy"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"strcat"}}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"wcsncat"}}},
{{CDM::CLibraryMaybeHardened, {{"strcpy"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibraryMaybeHardened, {{"stpcpy"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibraryMaybeHardened, {{"strcat"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibraryMaybeHardened, {{"wcsncat"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
{{CDM::CLibrary, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
{{CDM::CLibrary, {{"strdupa"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
Expand Down Expand Up @@ -759,13 +762,13 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},

// SinkProps
{{CDM::CLibrary, BI.getName(Builtin::BImemcpy)},
{{CDM::CLibraryMaybeHardened, BI.getName(Builtin::BImemcpy)},
TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
MsgTaintedBufferSize)},
{{CDM::CLibrary, {BI.getName(Builtin::BImemmove)}},
{{CDM::CLibraryMaybeHardened, {BI.getName(Builtin::BImemmove)}},
TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
MsgTaintedBufferSize)},
{{CDM::CLibrary, {BI.getName(Builtin::BIstrncpy)}},
{{CDM::CLibraryMaybeHardened, {BI.getName(Builtin::BIstrncpy)}},
TR::SinkProp({{2}}, {{1, 2}}, {{0, ReturnValueIndex}},
MsgTaintedBufferSize)},
{{CDM::CLibrary, {BI.getName(Builtin::BIstrndup)}},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
// something like derived regions if we want to construct SVal from
// Sym. Instead, we take the value that is definitely stored in that
// region, thus guaranteeing that trackStoredValue will work.
bugreporter::trackStoredValue(AllVarBindings[0].second.castAs<KnownSVal>(),
bugreporter::trackStoredValue(AllVarBindings[0].second,
AllocBindingToReport, *this);
} else {
AllocBindingToReport = AllocFirstBinding;
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,7 @@ class StoreSiteFinder final : public TrackingBugReporterVisitor {
/// changes to its value in a nested stackframe could be pruned, and
/// this visitor can prevent that without polluting the bugpath too
/// much.
StoreSiteFinder(bugreporter::TrackerRef ParentTracker, KnownSVal V,
StoreSiteFinder(bugreporter::TrackerRef ParentTracker, SVal V,
const MemRegion *R, TrackingOptions Options,
const StackFrameContext *OriginSFC = nullptr)
: TrackingBugReporterVisitor(ParentTracker), R(R), V(V), Options(Options),
Expand Down Expand Up @@ -2539,9 +2539,9 @@ class DefaultExpressionHandler final : public ExpressionHandler {
Report.addVisitor<UndefOrNullArgVisitor>(L->getRegion());
Result.FoundSomethingToTrack = true;

if (auto KV = RVal.getAs<KnownSVal>())
if (!RVal.isUnknown())
Result.combineWith(
getParentTracker().track(*KV, L->getRegion(), Opts, SFC));
getParentTracker().track(RVal, L->getRegion(), Opts, SFC));
}

const MemRegion *RegionRVal = RVal.getAsRegion();
Expand Down Expand Up @@ -2663,8 +2663,8 @@ Tracker::Result Tracker::track(const Expr *E, const ExplodedNode *N,

Tracker::Result Tracker::track(SVal V, const MemRegion *R, TrackingOptions Opts,
const StackFrameContext *Origin) {
if (auto KV = V.getAs<KnownSVal>()) {
Report.addVisitor<StoreSiteFinder>(this, *KV, R, Opts, Origin);
if (!V.isUnknown()) {
Report.addVisitor<StoreSiteFinder>(this, V, R, Opts, Origin);
return {true};
}
return {};
Expand Down Expand Up @@ -2692,7 +2692,7 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
.FoundSomethingToTrack;
}

void bugreporter::trackStoredValue(KnownSVal V, const MemRegion *R,
void bugreporter::trackStoredValue(SVal V, const MemRegion *R,
PathSensitiveBugReport &Report,
TrackingOptions Opts,
const StackFrameContext *Origin) {
Expand Down
122 changes: 61 additions & 61 deletions clang/lib/StaticAnalyzer/Core/CallDescription.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,48 @@ bool ento::CallDescription::matchesAsWritten(const CallExpr &CE) const {
return matchesImpl(FD, CE.getNumArgs(), FD->param_size());
}

bool ento::CallDescription::matchNameOnly(const NamedDecl *ND) const {
DeclarationName Name = ND->getDeclName();
if (const auto *NameII = Name.getAsIdentifierInfo()) {
if (!II)
II = &ND->getASTContext().Idents.get(getFunctionName());

return NameII == *II; // Fast case.
}

// Fallback to the slow stringification and comparison for:
// C++ overloaded operators, constructors, destructors, etc.
// FIXME This comparison is way SLOWER than comparing pointers.
// At some point in the future, we should compare FunctionDecl pointers.
return Name.getAsString() == getFunctionName();
}

bool ento::CallDescription::matchQualifiedNameParts(const Decl *D) const {
const auto FindNextNamespaceOrRecord =
[](const DeclContext *Ctx) -> const DeclContext * {
while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
Ctx = Ctx->getParent();
return Ctx;
};

auto QualifierPartsIt = begin_qualified_name_parts();
const auto QualifierPartsEndIt = end_qualified_name_parts();

// Match namespace and record names. Skip unrelated names if they don't
// match.
const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
// If not matched just continue and try matching for the next one.
if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
continue;
++QualifierPartsIt;
}

// We matched if we consumed all expected qualifier segments.
return QualifierPartsIt == QualifierPartsEndIt;
}

bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,
size_t ParamCount) const {
if (!FD)
Expand All @@ -88,76 +130,34 @@ bool ento::CallDescription::matchesImpl(const FunctionDecl *FD, size_t ArgCount,
if (MatchAs == Mode::CXXMethod && !isMethod)
return false;

if (MatchAs == Mode::CLibrary) {
return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
(!RequiredArgs || *RequiredArgs <= ArgCount) &&
(!RequiredParams || *RequiredParams <= ParamCount);
}

if (!II) {
II = &FD->getASTContext().Idents.get(getFunctionName());
}

const auto MatchNameOnly = [](const CallDescription &CD,
const NamedDecl *ND) -> bool {
DeclarationName Name = ND->getDeclName();
if (const auto *II = Name.getAsIdentifierInfo())
return II == *CD.II; // Fast case.

// Fallback to the slow stringification and comparison for:
// C++ overloaded operators, constructors, destructors, etc.
// FIXME This comparison is way SLOWER than comparing pointers.
// At some point in the future, we should compare FunctionDecl pointers.
return Name.getAsString() == CD.getFunctionName();
};

const auto ExactMatchArgAndParamCounts =
[](size_t ArgCount, size_t ParamCount,
const CallDescription &CD) -> bool {
const bool ArgsMatch = !CD.RequiredArgs || *CD.RequiredArgs == ArgCount;
const bool ParamsMatch =
!CD.RequiredParams || *CD.RequiredParams == ParamCount;
return ArgsMatch && ParamsMatch;
};

const auto MatchQualifiedNameParts = [](const CallDescription &CD,
const Decl *D) -> bool {
const auto FindNextNamespaceOrRecord =
[](const DeclContext *Ctx) -> const DeclContext * {
while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
Ctx = Ctx->getParent();
return Ctx;
};

auto QualifierPartsIt = CD.begin_qualified_name_parts();
const auto QualifierPartsEndIt = CD.end_qualified_name_parts();

// Match namespace and record names. Skip unrelated names if they don't
// match.
const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
// If not matched just continue and try matching for the next one.
if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
continue;
++QualifierPartsIt;
if (MatchAs == Mode::CLibraryMaybeHardened) {
// In addition to accepting FOO() with CLibrary rules, we also want to
// accept calls to __FOO_chk() and __builtin___FOO_chk().
if (CheckerContext::isCLibraryFunction(FD) &&
CheckerContext::isHardenedVariantOf(FD, getFunctionName())) {
// Check that the actual argument/parameter counts are greater or equal
// to the required counts. (Setting a requirement to std::nullopt matches
// anything, so in that case value_or ensures that the value is compared
// with itself.)
return (RequiredArgs.value_or(ArgCount) <= ArgCount &&
RequiredParams.value_or(ParamCount) <= ParamCount);
}
}

// We matched if we consumed all expected qualifier segments.
return QualifierPartsIt == QualifierPartsEndIt;
};

// Let's start matching...
if (!ExactMatchArgAndParamCounts(ArgCount, ParamCount, *this))
if (RequiredArgs.value_or(ArgCount) != ArgCount ||
RequiredParams.value_or(ParamCount) != ParamCount)
return false;

if (!MatchNameOnly(*this, FD))
if (MatchAs == Mode::CLibrary || MatchAs == Mode::CLibraryMaybeHardened)
return CheckerContext::isCLibraryFunction(FD, getFunctionName());

if (!matchNameOnly(FD))
return false;

if (!hasQualifiedNameParts())
return true;

return MatchQualifiedNameParts(*this, FD);
return matchQualifiedNameParts(FD);
}

ento::CallDescriptionSet::CallDescriptionSet(
Expand Down
19 changes: 15 additions & 4 deletions clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,24 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (FName.starts_with("__inline") && FName.contains(Name))
return true;

if (FName.starts_with("__") && FName.ends_with("_chk") &&
FName.contains(Name))
return true;

return false;
}

bool CheckerContext::isHardenedVariantOf(const FunctionDecl *FD,
StringRef Name) {
const IdentifierInfo *II = FD->getIdentifier();
if (!II)
return false;

auto CompletelyMatchesParts = [II](auto... Parts) -> bool {
StringRef FName = II->getName();
return (FName.consume_front(Parts) && ...) && FName.empty();
};

return CompletelyMatchesParts("__", Name, "_chk") ||
CompletelyMatchesParts("__builtin_", "__", Name, "_chk");
}

StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) {
if (Loc.isMacroID())
return Lexer::getImmediateMacroName(Loc, getSourceManager(),
Expand Down
101 changes: 64 additions & 37 deletions clang/test/CodeGen/allow-ubsan-check.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-trap=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=TRAP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -emit-llvm -o - %s -fsanitize=signed-integer-overflow,integer-divide-by-zero,null -mllvm -ubsan-guard-checks -fsanitize-recover=signed-integer-overflow,integer-divide-by-zero,null | FileCheck %s --check-prefixes=RECOVER


// CHECK-LABEL: define dso_local i32 @div(
Expand All @@ -18,11 +18,14 @@
// CHECK-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// CHECK-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// CHECK: handler.divrem_overflow:
// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_divrem_overflow_abort(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_divrem_overflow_abort(ptr @[[GLOB1:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4:[0-9]+]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
Expand All @@ -42,9 +45,12 @@
// TRAP-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// TRAP-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// TRAP-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]]
// TRAP-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// TRAP-NEXT: call void @llvm.ubsantrap(i8 3) #[[ATTR4:[0-9]+]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
Expand All @@ -64,11 +70,14 @@
// RECOVER-NEXT: [[TMP4:%.*]] = icmp ne i32 [[TMP1]], -1, !nosanitize [[META2]]
// RECOVER-NEXT: [[OR:%.*]] = or i1 [[TMP3]], [[TMP4]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP5:%.*]] = and i1 [[TMP2]], [[OR]], !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 3), !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_DIVREM_OVERFLOW:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
// RECOVER: handler.divrem_overflow:
// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_divrem_overflow(ptr @[[GLOB1:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_divrem_overflow(ptr @[[GLOB1:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4:[0-9]+]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: [[DIV:%.*]] = sdiv i32 [[TMP0]], [[TMP1]]
Expand All @@ -85,14 +94,17 @@ int div(int x, int y) {
// CHECK-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]]
// CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK: handler.type_mismatch:
// CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_type_mismatch_v1_abort(ptr @[[GLOB2:[0-9]+]], i64 [[TMP5]]) #[[ATTR4]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
// CHECK-NEXT: ret i32 [[TMP3]]
// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP0]], align 4
// CHECK-NEXT: ret i32 [[TMP6]]
//
// TRAP-LABEL: define dso_local i32 @null(
// TRAP-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] {
Expand All @@ -101,13 +113,16 @@ int div(int x, int y) {
// TRAP-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// TRAP-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// TRAP-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]]
// TRAP-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR3]], !nosanitize [[META2]]
// TRAP-NEXT: call void @llvm.ubsantrap(i8 22) #[[ATTR4]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP0]], align 4
// TRAP-NEXT: ret i32 [[TMP2]]
// TRAP-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP0]], align 4
// TRAP-NEXT: ret i32 [[TMP5]]
//
// RECOVER-LABEL: define dso_local i32 @null(
// RECOVER-SAME: ptr noundef [[X:%.*]]) #[[ATTR0]] {
Expand All @@ -116,14 +131,17 @@ int div(int x, int y) {
// RECOVER-NEXT: store ptr [[X]], ptr [[X_ADDR]], align 8
// RECOVER-NEXT: [[TMP0:%.*]] = load ptr, ptr [[X_ADDR]], align 8
// RECOVER-NEXT: [[TMP1:%.*]] = icmp ne ptr [[TMP0]], null, !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP1]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP2:%.*]] = call i1 @llvm.allow.ubsan.check(i8 22), !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP4:%.*]] = or i1 [[TMP1]], [[TMP3]], !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP4]], label [[CONT:%.*]], label [[HANDLER_TYPE_MISMATCH:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER: handler.type_mismatch:
// RECOVER-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_type_mismatch_v1(ptr @[[GLOB2:[0-9]+]], i64 [[TMP2]]) #[[ATTR3]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP5:%.*]] = ptrtoint ptr [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_type_mismatch_v1(ptr @[[GLOB2:[0-9]+]], i64 [[TMP5]]) #[[ATTR4]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
// RECOVER-NEXT: ret i32 [[TMP3]]
// RECOVER-NEXT: [[TMP6:%.*]] = load i32, ptr [[TMP0]], align 4
// RECOVER-NEXT: ret i32 [[TMP6]]
//
int null(int* x) {
return *x;
Expand All @@ -142,11 +160,14 @@ int null(int* x) {
// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// CHECK-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// CHECK: handler.add_overflow:
// CHECK-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]]
// CHECK-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// CHECK-NEXT: call void @__ubsan_handle_add_overflow_abort(ptr @[[GLOB3:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4]], !nosanitize [[META2]]
// CHECK-NEXT: unreachable, !nosanitize [[META2]]
// CHECK: cont:
// CHECK-NEXT: ret i32 [[TMP3]]
Expand All @@ -164,9 +185,12 @@ int null(int* x) {
// TRAP-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]]
// TRAP-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// TRAP-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// TRAP-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[TRAP:%.*]], !nosanitize [[META2]]
// TRAP: trap:
// TRAP-NEXT: call void @llvm.ubsantrap(i8 0) #[[ATTR3]], !nosanitize [[META2]]
// TRAP-NEXT: call void @llvm.ubsantrap(i8 0) #[[ATTR4]], !nosanitize [[META2]]
// TRAP-NEXT: unreachable, !nosanitize [[META2]]
// TRAP: cont:
// TRAP-NEXT: ret i32 [[TMP3]]
Expand All @@ -184,11 +208,14 @@ int null(int* x) {
// RECOVER-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP5:%.*]] = xor i1 [[TMP4]], true, !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP5]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP6:%.*]] = call i1 @llvm.allow.ubsan.check(i8 0), !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = xor i1 [[TMP6]], true, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP8:%.*]] = or i1 [[TMP5]], [[TMP7]], !nosanitize [[META2]]
// RECOVER-NEXT: br i1 [[TMP8]], label [[CONT:%.*]], label [[HANDLER_ADD_OVERFLOW:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
// RECOVER: handler.add_overflow:
// RECOVER-NEXT: [[TMP6:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP7:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_add_overflow(ptr @[[GLOB3:[0-9]+]], i64 [[TMP6]], i64 [[TMP7]]) #[[ATTR3]], !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP9:%.*]] = zext i32 [[TMP0]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: [[TMP10:%.*]] = zext i32 [[TMP1]] to i64, !nosanitize [[META2]]
// RECOVER-NEXT: call void @__ubsan_handle_add_overflow(ptr @[[GLOB3:[0-9]+]], i64 [[TMP9]], i64 [[TMP10]]) #[[ATTR4]], !nosanitize [[META2]]
// RECOVER-NEXT: br label [[CONT]], !nosanitize [[META2]]
// RECOVER: cont:
// RECOVER-NEXT: ret i32 [[TMP3]]
Expand Down
15 changes: 0 additions & 15 deletions clang/test/CodeGen/remote-traps.c

This file was deleted.

10 changes: 3 additions & 7 deletions clang/test/CodeGen/tbaa-struct-bitfield-endianness.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// RUN: %clang_cc1 -triple aarch64_be-apple-darwin -emit-llvm -o - -O1 %s | \
// RUN: FileCheck -check-prefixes=CHECK,CHECK-BE %s
// RUN: FileCheck -check-prefixes=CHECK %s
// RUN: %clang_cc1 -triple aarch64-apple-darwin -emit-llvm -o - -O1 %s | \
// RUN: FileCheck -check-prefixes=CHECK,CHECK-LE %s
// RUN: FileCheck -check-prefixes=CHECK %s
//
// Check that TBAA metadata for structs containing bitfields is
// consistent between big and little endian layouts.
//
// FIXME: The metadata below is invalid for the big endian layout: the
// start offset of 2 is incorrect.

struct NamedBitfields {
int f1 : 8;
Expand All @@ -28,8 +25,7 @@ void copy(NamedBitfields *a1, NamedBitfields *a2) {
*a1 = *a2;
}

// CHECK-BE: [[TBAA_STRUCT2]] = !{i64 2, i64 4, [[META3:![0-9]+]], i64 4, i64 4, [[META6:![0-9]+]], i64 8, i64 8, [[META8:![0-9]+]]}
// CHECK-LE: [[TBAA_STRUCT2]] = !{i64 0, i64 4, [[META3:![0-9]+]], i64 4, i64 4, [[META6:![0-9]+]], i64 8, i64 8, [[META8:![0-9]+]]}
// CHECK: [[TBAA_STRUCT2]] = !{i64 0, i64 4, [[META3:![0-9]+]], i64 4, i64 4, [[META6:![0-9]+]], i64 8, i64 8, [[META8:![0-9]+]]}
// CHECK: [[META3]] = !{[[META4:![0-9]+]], [[META4]], i64 0}
// CHECK: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
// CHECK: [[META5]] = !{!"Simple C++ TBAA"}
Expand Down
277 changes: 277 additions & 0 deletions clang/test/CodeGenHLSL/builtins/all.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-compute %s -fnative-half-type \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=CHECK,NATIVE_HALF,SPIR_NATIVE_HALF,SPIR_CHECK
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,SPIR_NO_HALF,SPIR_CHECK
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=CHECK,NATIVE_HALF,DXIL_NATIVE_HALF,DXIL_CHECK
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,DXIL_NO_HALF,DXIL_CHECK

#ifdef __HLSL_ENABLE_16_BIT
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_int16_t(int16_t p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v2i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v2i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_int16_t2(int16_t2 p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v3i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v3i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_int16_t3(int16_t3 p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v4i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v4i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_int16_t4(int16_t4 p0) { return all(p0); }

// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_uint16_t(uint16_t p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v2i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v2i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_uint16_t2(uint16_t2 p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v3i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v3i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_uint16_t3(uint16_t3 p0) { return all(p0); }
// DXIL_NATIVE_HALF: define noundef i1 @
// SPIR_NATIVE_HALF: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v4i16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v4i16
// NATIVE_HALF: ret i1 %hlsl.all
bool test_all_uint16_t4(uint16_t4 p0) { return all(p0); }
#endif // __HLSL_ENABLE_16_BIT

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.f16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.f16
// DXIL_NO_HALF: %hlsl.all = call i1 @llvm.dx.all.f32
// SPIR_NO_HALF: %hlsl.all = call i1 @llvm.spv.all.f32
// CHECK: ret i1 %hlsl.all
bool test_all_half(half p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v2f16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v2f16
// DXIL_NO_HALF: %hlsl.all = call i1 @llvm.dx.all.v2f32
// SPIR_NO_HALF: %hlsl.all = call i1 @llvm.spv.all.v2f32
// CHECK: ret i1 %hlsl.all
bool test_all_half2(half2 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v3f16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v3f16
// DXIL_NO_HALF: %hlsl.all = call i1 @llvm.dx.all.v3f32
// SPIR_NO_HALF: %hlsl.all = call i1 @llvm.spv.all.v3f32
// CHECK: ret i1 %hlsl.all
bool test_all_half3(half3 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_NATIVE_HALF: %hlsl.all = call i1 @llvm.dx.all.v4f16
// SPIR_NATIVE_HALF: %hlsl.all = call i1 @llvm.spv.all.v4f16
// DXIL_NO_HALF: %hlsl.all = call i1 @llvm.dx.all.v4f32
// SPIR_NO_HALF: %hlsl.all = call i1 @llvm.spv.all.v4f32
// CHECK: ret i1 %hlsl.all
bool test_all_half4(half4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.f32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.f32
// CHECK: ret i1 %hlsl.all
bool test_all_float(float p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2f32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2f32
// CHECK: ret i1 %hlsl.all
bool test_all_float2(float2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3f32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3f32
// CHECK: ret i1 %hlsl.all
bool test_all_float3(float3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4f32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4f32
// CHECK: ret i1 %hlsl.all
bool test_all_float4(float4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.f64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.f64
// CHECK: ret i1 %hlsl.all
bool test_all_double(double p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2f64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2f64
// CHECK: ret i1 %hlsl.all
bool test_all_double2(double2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3f64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3f64
// CHECK: ret i1 %hlsl.all
bool test_all_double3(double3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4f64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4f64
// CHECK: ret i1 %hlsl.all
bool test_all_double4(double4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.i32
// CHECK: ret i1 %hlsl.all
bool test_all_int(int p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2i32
// CHECK: ret i1 %hlsl.all
bool test_all_int2(int2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3i32
// CHECK: ret i1 %hlsl.all
bool test_all_int3(int3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4i32
// CHECK: ret i1 %hlsl.all
bool test_all_int4(int4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.i32
// CHECK: ret i1 %hlsl.all
bool test_all_uint(uint p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2i32
// CHECK: ret i1 %hlsl.all
bool test_all_uint2(uint2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3i32
// CHECK: ret i1 %hlsl.all
bool test_all_uint3(uint3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4i32
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4i32
// CHECK: ret i1 %hlsl.all
bool test_all_uint4(uint4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.i64
// CHECK: ret i1 %hlsl.all
bool test_all_int64_t(int64_t p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2i64
// CHECK: ret i1 %hlsl.all
bool test_all_int64_t2(int64_t2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3i64
// CHECK: ret i1 %hlsl.all
bool test_all_int64_t3(int64_t3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4i64
// CHECK: ret i1 %hlsl.all
bool test_all_int64_t4(int64_t4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.i64
// CHECK: ret i1 %hlsl.all
bool test_all_uint64_t(uint64_t p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2i64
// CHECK: ret i1 %hlsl.all
bool test_all_uint64_t2(uint64_t2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3i64
// CHECK: ret i1 %hlsl.all
bool test_all_uint64_t3(uint64_t3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4i64
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4i64
// CHECK: ret i1 %hlsl.all
bool test_all_uint64_t4(uint64_t4 p0) { return all(p0); }

// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.i1
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.i1
// CHECK: ret i1 %hlsl.all
bool test_all_bool(bool p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v2i1
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v2i1
// CHECK: ret i1 %hlsl.all
bool test_all_bool2(bool2 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v3i1
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v3i1
// CHECK: ret i1 %hlsl.all
bool test_all_bool3(bool3 p0) { return all(p0); }
// DXIL_CHECK: define noundef i1 @
// SPIR_CHECK: define spir_func noundef i1 @
// DXIL_CHECK: %hlsl.all = call i1 @llvm.dx.all.v4i1
// SPIR_CHECK: %hlsl.all = call i1 @llvm.spv.all.v4i1
// CHECK: ret i1 %hlsl.all
bool test_all_bool4(bool4 p0) { return all(p0); }
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@interface NSObject
@end

typedef unsigned char BOOL;
#ifndef NS_AVAILABLE
#define NS_AVAILABLE(x,y) __attribute__((availability(macosx,introduced=x)))
#endif
#ifndef NS_UNAVAILABLE
#define NS_UNAVAILABLE __attribute__((unavailable))
#endif
#ifndef NS_DEPRECATED_MAC
#define NS_DEPRECATED_MAC(x,y) __attribute__((availability(macosx,introduced=x,deprecated=y,message="" )));
#endif

@interface NSManagedObject
@end

@interface NSSet
@end
22 changes: 1 addition & 21 deletions clang/test/InstallAPI/extra-exclude-headers.test
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
; RUN: split-file %s %t
; RUN: mkdir -p %t/System/Library/Frameworks
; RUN: cp -r %S/Inputs/Simple/Simple.framework %t/System/Library/Frameworks/
; RUN: cp -r %S/Inputs/Foundation/Foundation.framework %t/System/Library/Frameworks/
; RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
; RUN: yaml2obj %S/Inputs/Simple/Simple.yaml -o %t/Simple

Expand Down Expand Up @@ -184,24 +185,3 @@
],
"version": "3"
}

;--- System/Library/Frameworks/Foundation.framework/Headers/Foundation.h
@interface NSObject
@end

typedef unsigned char BOOL;
#ifndef NS_AVAILABLE
#define NS_AVAILABLE(x,y) __attribute__((availability(macosx,introduced=x)))
#endif
#ifndef NS_UNAVAILABLE
#define NS_UNAVAILABLE __attribute__((unavailable))
#endif
#ifndef NS_DEPRECATED_MAC
#define NS_DEPRECATED_MAC(x,y) __attribute__((availability(macosx,introduced=x,deprecated=y,message="" )));
#endif

@interface NSManagedObject
@end

@interface NSSet
@end
34 changes: 34 additions & 0 deletions clang/test/InstallAPI/forwarded-search-paths.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
; RUN: rm -rf %t
; RUN: split-file %s %t
; RUN: sed -e "s|DSTROOT|%/t|g" %t/input.json.in > %t/input.json

; RUN: mkdir -p %t/System/Library/Frameworks
; RUN: cp -r %S/Inputs/Foundation/Foundation.framework %t/System/Library/Frameworks/
; RUN: cp -r %S/Inputs/Simple/Simple.framework %t/System/Library/Frameworks/
; RUN: yaml2obj %S/Inputs/Simple/Simple.yaml -o %t/Simple
; RUN: mkdir -p %t/usr/include/after

; RUN: clang-installapi -target x86_64-apple-macosx10.12 \
; RUN: -install_name /System/Library/Frameworks/Simple.framework/Versions/A/Simple \
; RUN: -current_version 1.2.3 -compatibility_version 1 -o %t/Simple.tbd \
; RUN: -idirafter %t/usr/include/after \
; RUN: -F %t/System/Library/Frameworks \
; RUN: --verify-against=%t/Simple --verify-mode=ErrorsOnly \
; RUN: %t/input.json -v 2>&1 | FileCheck %s

; CHECK: "-idirafter" {{.*}}/usr/include/after"
; CHECK: #include "..." search starts here:
; CHECK: #include <...> search starts here:
; CHECK: usr/include/after
; CHECK-NEXT: End of search list.

;--- input.json.in
{
"version" : "3",
"headers" : [
{
"type" : "public",
"path" : "DSTROOT/System/Library/Frameworks/Simple.framework/Headers/Basic.h"
}
]
}
34 changes: 34 additions & 0 deletions clang/test/OpenMP/thread_limit_amdgpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Test target codegen - host bc file has to be created first.
// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-linux-gnu -fopenmp-targets=amdgcn-amd-amdhsa -emit-llvm-bc %s -o %t-x86-host.bc
// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple amdgcn-amd-amdhsa -fopenmp-targets=amdgcn-amd-amdhsa -emit-llvm %s -fopenmp-is-target-device -fopenmp-host-ir-file-path %t-x86-host.bc -o - | FileCheck %s
// expected-no-diagnostics

#ifndef HEADER
#define HEADER

void foo(int N) {
#pragma omp target teams distribute parallel for simd
for (int i = 0; i < N; ++i)
;
#pragma omp target teams distribute parallel for simd thread_limit(4)
for (int i = 0; i < N; ++i)
;
#pragma omp target teams distribute parallel for simd ompx_attribute(__attribute__((launch_bounds(42, 42))))
for (int i = 0; i < N; ++i)
;
#pragma omp target teams distribute parallel for simd ompx_attribute(__attribute__((launch_bounds(42, 42)))) num_threads(22)
for (int i = 0; i < N; ++i)
;
}

#endif

// CHECK: define weak_odr protected amdgpu_kernel void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+__Z3fooi_}}l10({{.*}}) #[[ATTR1:.+]] {
// CHECK: define weak_odr protected amdgpu_kernel void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+__Z3fooi_}}l13({{.*}}) #[[ATTR2:.+]] {
// CHECK: define weak_odr protected amdgpu_kernel void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+__Z3fooi_}}l16({{.*}}) #[[ATTR3:.+]] {
// CHECK: define weak_odr protected amdgpu_kernel void @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+__Z3fooi_}}l19({{.*}}) #[[ATTR4:.+]] {

// CHECK: attributes #[[ATTR1]] = { {{.*}} "amdgpu-flat-work-group-size"="1,256" {{.*}} }
// CHECK: attributes #[[ATTR2]] = { {{.*}} "amdgpu-flat-work-group-size"="1,4" {{.*}} }
// CHECK: attributes #[[ATTR3]] = { {{.*}} "amdgpu-flat-work-group-size"="1,42" "amdgpu-max-num-workgroups"="42,1,1"{{.*}} }
// CHECK: attributes #[[ATTR4]] = { {{.*}} "amdgpu-flat-work-group-size"="1,22" "amdgpu-max-num-workgroups"="42,1,1"{{.*}} }
32 changes: 32 additions & 0 deletions clang/test/SemaCXX/PR86790.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s

enum {A, S, D, F};
int main() {
using asdf = decltype(A);
using enum asdf; // this line causes the crash
return 0;
}

namespace N1 {
enum {A, S, D, F};
constexpr struct T {
using asdf = decltype(A);
using enum asdf;
} t;

static_assert(t.D == D);
static_assert(T::S == S);
}

namespace N2 {
enum {A, S, D, F};
constexpr struct T {
struct {
using asdf = decltype(A);
using enum asdf;
} inner;
} t;

static_assert(t.inner.D == D);
static_assert(t.D == D); // expected-error {{no member named 'D' in 'N2::T'}}
}
8 changes: 8 additions & 0 deletions clang/test/SemaCXX/warn-unused-but-set-variables-cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ template <typename T> void f5() {
SWarnUnused swu;
++swu;
}

void f6() {
if (int x = 123) {} // expected-warning{{variable 'x' set but not used}}

while (int x = 123) {} // expected-warning{{variable 'x' set but not used}}

for (; int x = 123;) {} // expected-warning{{variable 'x' set but not used}}
}
105 changes: 105 additions & 0 deletions clang/test/SemaTemplate/alias-template-with-lambdas.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s
namespace lambda_calls {

template <class>
concept True = true;

template <class>
concept False = false; // #False

template <class T> struct S {
template <class... U> using type = decltype([](U...) {}(U()...));
template <class U> using type2 = decltype([](auto) {}(1));
template <class U> using type3 = decltype([](True auto) {}(1));
template <class>
using type4 = decltype([](auto... pack) { return sizeof...(pack); }(1, 2));

template <class U> using type5 = decltype([](False auto...) {}(1)); // #Type5

template <class U>
using type6 = decltype([]<True> {}.template operator()<char>());
template <class U>
using type7 = decltype([]<False> {}.template operator()<char>()); // #Type7

template <class U>
using type8 = decltype([]() // #Type8
requires(sizeof(U) == 32) // #Type8-requirement
{}());

template <class... U>
using type9 = decltype([]<True>(U...) {}.template operator()<char>(U()...));
// https://github.com/llvm/llvm-project/issues/76674
template <class U>
using type10 = decltype([]<class V> { return V(); }.template operator()<U>());

template <class U> using type11 = decltype([] { return U{}; });
};

template <class> using Meow = decltype([]<True> {}.template operator()<int>());

template <class... U>
using MeowMeow = decltype([]<True>(U...) {}.template operator()<char>(U()...));

// https://github.com/llvm/llvm-project/issues/70601
template <class> using U = decltype([]<True> {}.template operator()<int>());

U<int> foo();

void bar() {
using T = S<int>::type<int, int, int>;
using T2 = S<int>::type2<int>;
using T3 = S<int>::type3<char>;
using T4 = S<int>::type4<void>;
using T5 = S<int>::type5<void>; // #T5
// expected-error@#Type5 {{no matching function for call}}
// expected-note@#T5 {{type alias 'type5' requested here}}
// expected-note@#Type5 {{constraints not satisfied [with auto:1 = <int>]}}
// expected-note@#Type5 {{because 'int' does not satisfy 'False'}}
// expected-note@#False {{because 'false' evaluated to false}}

using T6 = S<int>::type6<void>;
using T7 = S<int>::type7<void>; // #T7
// expected-error@#Type7 {{no matching member function for call}}
// expected-note@#T7 {{type alias 'type7' requested here}}
// expected-note@#Type7 {{constraints not satisfied [with $0 = char]}}
// expected-note@#Type7 {{because 'char' does not satisfy 'False'}}
// expected-note@#False {{because 'false' evaluated to false}}

using T8 = S<int>::type8<char>; // #T8
// expected-error@#Type8 {{no matching function for call}}
// expected-note@#T8 {{type alias 'type8' requested here}}
// expected-note@#Type8 {{constraints not satisfied}}
// expected-note@#Type8-requirement {{because 'sizeof(char) == 32' (1 == 32) evaluated to false}}

using T9 = S<int>::type9<long, long, char>;
using T10 = S<int>::type10<int>;
using T11 = S<int>::type11<int>;
int x = T11()();
using T12 = Meow<int>;
using T13 = MeowMeow<char, int, long, unsigned>;

static_assert(__is_same(T, void));
static_assert(__is_same(T2, void));
static_assert(__is_same(T3, void));
static_assert(__is_same(T4, decltype(sizeof(0))));
static_assert(__is_same(T6, void));
static_assert(__is_same(T9, void));
static_assert(__is_same(T10, int));
static_assert(__is_same(T12, void));
static_assert(__is_same(T13, void));
}

namespace GH82104 {

template <typename, typename...> int Zero = 0;

template <typename T, typename...U>
using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }());

template <typename T> using T15 = T14<T, T>;

static_assert(__is_same(T15<char>, int));

} // namespace GH82104

} // namespace lambda_calls
18 changes: 15 additions & 3 deletions clang/tools/clang-installapi/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,21 @@ static constexpr const ArrayRef<StringLiteral>
/// Create table mapping all options defined in InstallAPIOpts.td.
static constexpr OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
VISIBILITY, PARAM, HELPTEXT, METAVAR, VALUES) \
{PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \
PARAM, FLAGS, VISIBILITY, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, \
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
VALUES) \
{PREFIX, \
NAME, \
HELPTEXT, \
HELPTEXTSFORVARIANTS, \
METAVAR, \
OPT_##ID, \
Option::KIND##Class, \
PARAM, \
FLAGS, \
VISIBILITY, \
OPT_##GROUP, \
OPT_##ALIAS, \
ALIASARGS, \
VALUES},
#include "InstallAPIOpts.inc"
#undef OPTION
Expand Down
3 changes: 2 additions & 1 deletion clang/tools/clang-installapi/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ class Options {
enum ID {
OPT_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
VISIBILITY, PARAM, HELPTEXT, METAVAR, VALUES) \
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
VALUES) \
OPT_##ID,
#include "InstallAPIOpts.inc"
LastOption
Expand Down
28 changes: 28 additions & 0 deletions clang/unittests/AST/ASTImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5317,6 +5317,34 @@ TEST_P(ASTImporterOptionSpecificTestBase,
EXPECT_FALSE(ToX);
}

TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateDeclInlineWithCXX17) {
Decl *FromTU = getTuDecl(
R"(
struct S {
template <unsigned> static constexpr bool X = true;
};
)",
Lang_CXX17, "input1.cc");
Decl *FromTU2 = getTuDecl(
R"(
struct S {
template <unsigned> static constexpr bool X = true;
template <typename T> void get() { X<sizeof(T)>; }
};
template <typename U> U qvariant_cast(const S &v) { return v.get; }
)",
Lang_CXX17, "input2.cc");
auto *FromX = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX17);
ASSERT_TRUE(ToX);
auto *FromX2 = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU2, varTemplateDecl(hasName("X")));
auto *ToX2 = Import(FromX2, Lang_CXX17);
EXPECT_TRUE(ToX2);
EXPECT_EQ(ToX, ToX2);
}

TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateParameterDeclContext) {
constexpr auto Code =
R"(
Expand Down
12 changes: 12 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27272,6 +27272,18 @@ TEST_F(FormatTest, PPBranchesInBracedInit) {
"};");
}

TEST_F(FormatTest, PPDirectivesAndCommentsInBracedInit) {
verifyFormat("{\n"
" char *a[] = {\n"
" /* abc */ \"abc\",\n"
"#if FOO\n"
" /* xyz */ \"xyz\",\n"
"#endif\n"
" /* last */ \"last\"};\n"
"}",
getLLVMStyleWithColumns(30));
}

TEST_F(FormatTest, StreamOutputOperator) {
verifyFormat("std::cout << \"foo\" << \"bar\" << baz;");
verifyFormat("std::cout << \"foo\\n\"\n"
Expand Down
13 changes: 13 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2809,6 +2809,19 @@ TEST_F(TokenAnnotatorTest, BraceKind) {
EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace);
EXPECT_BRACE_KIND(Tokens[13], BK_Block);
EXPECT_BRACE_KIND(Tokens[14], BK_Block);

Tokens = annotate("{\n"
" char *a[] = {\n"
" /* abc */ \"abc\",\n"
"#if FOO\n"
" /* xyz */ \"xyz\",\n"
"#endif\n"
" /* last */ \"last\"};\n"
"}");
ASSERT_EQ(Tokens.size(), 25u) << Tokens;
EXPECT_BRACE_KIND(Tokens[0], BK_Block);
EXPECT_BRACE_KIND(Tokens[7], BK_BracedInit);
EXPECT_BRACE_KIND(Tokens[21], BK_BracedInit);
}

TEST_F(TokenAnnotatorTest, StreamOperator) {
Expand Down
Loading