40 changes: 20 additions & 20 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5781,6 +5781,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
// NVPTX/AMDGPU does not care about the code model and will accept
// whatever works for the host.
Ok = true;
} else if (Triple.isSPARC64()) {
if (CM == "medlow")
CM = "small";
else if (CM == "medmid")
CM = "medium";
else if (CM == "medany")
CM = "large";
Ok = CM == "small" || CM == "medium" || CM == "large";
}
if (Ok) {
CmdArgs.push_back(Args.MakeArgString("-mcmodel=" + CM));
Expand Down Expand Up @@ -5944,6 +5952,17 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-ffunction-sections");
}

if (Arg *A = Args.getLastArg(options::OPT_fbasic_block_address_map,
options::OPT_fno_basic_block_address_map)) {
if (Triple.isX86() && Triple.isOSBinFormatELF()) {
if (A->getOption().matches(options::OPT_fbasic_block_address_map))
A->render(Args, CmdArgs);
} else {
D.Diag(diag::err_drv_unsupported_opt_for_target)
<< A->getAsString(Args) << TripleStr;
}
}

if (Arg *A = Args.getLastArg(options::OPT_fbasic_block_sections_EQ)) {
StringRef Val = A->getValue();
if (Triple.isX86() && Triple.isOSBinFormatELF()) {
Expand Down Expand Up @@ -7654,26 +7673,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,

addMachineOutlinerArgs(D, Args, CmdArgs, Triple, /*IsLTO=*/false);

if (Arg *A = Args.getLastArg(options::OPT_moutline_atomics,
options::OPT_mno_outline_atomics)) {
// Option -moutline-atomics supported for AArch64 target only.
if (!Triple.isAArch64()) {
D.Diag(diag::warn_drv_moutline_atomics_unsupported_opt)
<< Triple.getArchName() << A->getOption().getName();
} else {
if (A->getOption().matches(options::OPT_moutline_atomics)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
} else {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("-outline-atomics");
}
}
} else if (Triple.isAArch64() &&
getToolChain().IsAArch64OutlineAtomicsDefault(Args)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
}
addOutlineAtomicsArgs(D, getToolChain(), Args, CmdArgs, Triple);

if (Triple.isAArch64() &&
(Args.hasArg(options::OPT_mno_fmv) ||
Expand Down
33 changes: 32 additions & 1 deletion clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2650,7 +2650,7 @@ getAMDGPUCodeObjectArgument(const Driver &D, const llvm::opt::ArgList &Args) {
void tools::checkAMDGPUCodeObjectVersion(const Driver &D,
const llvm::opt::ArgList &Args) {
const unsigned MinCodeObjVer = 4;
const unsigned MaxCodeObjVer = 5;
const unsigned MaxCodeObjVer = 6;

if (auto *CodeObjArg = getAMDGPUCodeObjectArgument(D, Args)) {
if (CodeObjArg->getOption().getID() ==
Expand All @@ -2661,6 +2661,12 @@ void tools::checkAMDGPUCodeObjectVersion(const Driver &D,
if (Remnant || CodeObjVer < MinCodeObjVer || CodeObjVer > MaxCodeObjVer)
D.Diag(diag::err_drv_invalid_int_value)
<< CodeObjArg->getAsString(Args) << CodeObjArg->getValue();

// COV6 is only supported by LLVM at the time of writing this, and it's
// expected to take some time before all ROCm components fully
// support it. In the meantime, make sure users are aware of this.
if (CodeObjVer == 6)
D.Diag(diag::warn_drv_amdgpu_cov6);
}
}
}
Expand Down Expand Up @@ -2790,3 +2796,28 @@ void tools::addHIPRuntimeLibArgs(const ToolChain &TC, Compilation &C,
}
}
}

void tools::addOutlineAtomicsArgs(const Driver &D, const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const llvm::Triple &Triple) {
if (Arg *A = Args.getLastArg(options::OPT_moutline_atomics,
options::OPT_mno_outline_atomics)) {
// Option -moutline-atomics supported for AArch64 target only.
if (!Triple.isAArch64()) {
D.Diag(diag::warn_drv_moutline_atomics_unsupported_opt)
<< Triple.getArchName() << A->getOption().getName();
} else {
if (A->getOption().matches(options::OPT_moutline_atomics)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
} else {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("-outline-atomics");
}
}
} else if (Triple.isAArch64() && TC.IsAArch64OutlineAtomicsDefault(Args)) {
CmdArgs.push_back("-target-feature");
CmdArgs.push_back("+outline-atomics");
}
}
6 changes: 6 additions & 0 deletions clang/lib/Driver/ToolChains/CommonArgs.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ void addMachineOutlinerArgs(const Driver &D, const llvm::opt::ArgList &Args,
void addOpenMPDeviceRTL(const Driver &D, const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args,
StringRef BitcodeSuffix, const llvm::Triple &Triple);

void addOutlineAtomicsArgs(const Driver &D, const ToolChain &TC,
const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
const llvm::Triple &Triple);

} // end namespace tools
} // end namespace driver
} // end namespace clang
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ void Flang::addTargetOptions(const ArgList &Args,
CmdArgs.push_back(Args.MakeArgString(CPU));
}

addOutlineAtomicsArgs(D, getToolChain(), Args, CmdArgs, Triple);

// Add the target features.
switch (TC.getArch()) {
default:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Driver/ToolChains/HIPAMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ Tool *HIPAMDToolChain::buildLinker() const {
}

void HIPAMDToolChain::addClangWarningOptions(ArgStringList &CC1Args) const {
AMDGPUToolChain::addClangWarningOptions(CC1Args);
HostTC.addClangWarningOptions(CC1Args);
}

Expand Down
14 changes: 11 additions & 3 deletions clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,17 @@ bool ContinuationIndenter::canBreak(const LineState &State) {

// Don't break after very short return types (e.g. "void") as that is often
// unexpected.
if (Current.is(TT_FunctionDeclarationName) && State.Column < 6) {
if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None)
if (Current.is(TT_FunctionDeclarationName)) {
if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None &&
State.Column < 6) {
return false;
}

if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_ExceptShortType) {
assert(State.Column >= State.FirstIndent);
if (State.Column - State.FirstIndent < 6)
return false;
}
}

// If binary operators are moved to the next line (including commas for some
Expand Down Expand Up @@ -587,7 +595,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
!State.Line->ReturnTypeWrapped &&
// Don't break before a C# function when no break after return type.
(!Style.isCSharp() ||
Style.AlwaysBreakAfterReturnType != FormatStyle::RTBS_None) &&
Style.AlwaysBreakAfterReturnType > FormatStyle::RTBS_ExceptShortType) &&
// Don't always break between a JavaScript `function` and the function
// name.
!Style.isJavaScript() && Previous.isNot(tok::kw_template) &&
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,8 @@ template <>
struct ScalarEnumerationTraits<FormatStyle::ReturnTypeBreakingStyle> {
static void enumeration(IO &IO, FormatStyle::ReturnTypeBreakingStyle &Value) {
IO.enumCase(Value, "None", FormatStyle::RTBS_None);
IO.enumCase(Value, "Automatic", FormatStyle::RTBS_Automatic);
IO.enumCase(Value, "ExceptShortType", FormatStyle::RTBS_ExceptShortType);
IO.enumCase(Value, "All", FormatStyle::RTBS_All);
IO.enumCase(Value, "TopLevel", FormatStyle::RTBS_TopLevel);
IO.enumCase(Value, "TopLevelDefinitions",
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3434,6 +3434,8 @@ bool TokenAnnotator::mustBreakForReturnType(const AnnotatedLine &Line) const {

switch (Style.AlwaysBreakAfterReturnType) {
case FormatStyle::RTBS_None:
case FormatStyle::RTBS_Automatic:
case FormatStyle::RTBS_ExceptShortType:
return false;
case FormatStyle::RTBS_All:
case FormatStyle::RTBS_TopLevel:
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Frontend/TextDiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ highlightLines(StringRef FileData, unsigned StartLineNumber,
unsigned LineLength = 0;
for (unsigned I = 0; I <= Spelling.size(); ++I) {
// This line is done.
if (isVerticalWhitespace(Spelling[I]) || I == Spelling.size()) {
if (I == Spelling.size() || isVerticalWhitespace(Spelling[I])) {
SmallVector<TextDiagnostic::StyleRange> &LineRanges =
SnippetRanges[L - StartLineNumber];

Expand Down Expand Up @@ -1349,7 +1349,7 @@ void TextDiagnostic::emitSnippetAndCaret(
// Prepare source highlighting information for the lines we're about to
// emit, starting from the first line.
std::unique_ptr<SmallVector<StyleRange>[]> SourceStyles =
highlightLines(BufStart, Lines.first, Lines.second, PP, LangOpts,
highlightLines(BufData, Lines.first, Lines.second, PP, LangOpts,
DiagOpts->ShowColors, FID, SM);

SmallVector<LineRange> LineRanges =
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,12 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
return false;
else if (II->getBuiltinID() != 0) {
switch (II->getBuiltinID()) {
case Builtin::BI__builtin_cpu_is:
return getTargetInfo().supportsCpuIs();
case Builtin::BI__builtin_cpu_init:
return getTargetInfo().supportsCpuInit();
case Builtin::BI__builtin_cpu_supports:
return getTargetInfo().supportsCpuSupports();
case Builtin::BI__builtin_operator_new:
case Builtin::BI__builtin_operator_delete:
// denotes date of behavior change to support calling arbitrary
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6663,12 +6663,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
}

bool HadScope = D.getCXXScopeSpec().isValid();
SourceLocation TemplateKWLoc;
if (ParseUnqualifiedId(D.getCXXScopeSpec(),
/*ObjectType=*/nullptr,
/*ObjectHadErrors=*/false,
/*EnteringContext=*/true,
/*AllowDestructorName=*/true, AllowConstructorName,
AllowDeductionGuide, nullptr, D.getName()) ||
AllowDeductionGuide, &TemplateKWLoc,
D.getName()) ||
// Once we're past the identifier, if the scope was bad, mark the
// whole declarator bad.
D.getCXXScopeSpec().isInvalid()) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1725,6 +1725,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
tok::kw___is_member_pointer,
tok::kw___is_nothrow_assignable,
tok::kw___is_nothrow_constructible,
tok::kw___is_nothrow_convertible,
tok::kw___is_nothrow_destructible,
tok::kw___is_nullptr,
tok::kw___is_object,
Expand Down
135 changes: 116 additions & 19 deletions clang/lib/Rewrite/HTMLRewrite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
using namespace clang;

using namespace clang;
using namespace llvm;
using namespace html;

/// HighlightRange - Highlight a range in the source code with the specified
/// start/end tags. B/E must be in the same file. This ensures that
Expand Down Expand Up @@ -104,6 +106,32 @@ void html::HighlightRange(RewriteBuffer &RB, unsigned B, unsigned E,
}
}

namespace clang::html {
struct RelexRewriteCache {
// These structs mimic input arguments of HighlightRange().
struct Highlight {
SourceLocation B, E;
std::string StartTag, EndTag;
bool IsTokenRange;
};
struct RawHighlight {
unsigned B, E;
std::string StartTag, EndTag;
};

// SmallVector isn't appropriate because these vectors are almost never small.
using HighlightList = std::vector<Highlight>;
using RawHighlightList = std::vector<RawHighlight>;

DenseMap<FileID, RawHighlightList> SyntaxHighlights;
DenseMap<FileID, HighlightList> MacroHighlights;
};
} // namespace clang::html

html::RelexRewriteCacheRef html::instantiateRelexRewriteCache() {
return std::make_shared<RelexRewriteCache>();
}

void html::EscapeText(Rewriter &R, FileID FID,
bool EscapeSpaces, bool ReplaceTabs) {

Expand Down Expand Up @@ -442,13 +470,18 @@ input.spoilerhider:checked + label + .spoiler{
/// information about keywords, macro expansions etc. This uses the macro
/// table state from the end of the file, so it won't be perfectly perfect,
/// but it will be reasonably close.
void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
RewriteBuffer &RB = R.getEditBuffer(FID);
static void SyntaxHighlightImpl(
Rewriter &R, FileID FID, const Preprocessor &PP,
llvm::function_ref<void(RewriteBuffer &, unsigned, unsigned, const char *,
const char *, const char *)>
HighlightRangeCallback) {

RewriteBuffer &RB = R.getEditBuffer(FID);
const SourceManager &SM = PP.getSourceManager();
llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
const char *BufferStart = FromFile.getBuffer().data();

Lexer L(FID, FromFile, SM, PP.getLangOpts());
const char *BufferStart = L.getBuffer().data();

// Inform the preprocessor that we want to retain comments as tokens, so we
// can highlight them.
Expand All @@ -475,13 +508,13 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {

// If this is a pp-identifier, for a keyword, highlight it as such.
if (Tok.isNot(tok::identifier))
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
"<span class='keyword'>", "</span>");
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
"<span class='keyword'>", "</span>");
break;
}
case tok::comment:
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
"<span class='comment'>", "</span>");
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
"<span class='comment'>", "</span>");
break;
case tok::utf8_string_literal:
// Chop off the u part of u8 prefix
Expand All @@ -498,8 +531,8 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
[[fallthrough]];
case tok::string_literal:
// FIXME: Exclude the optional ud-suffix from the highlighted range.
HighlightRange(RB, TokOffs, TokOffs+TokLen, BufferStart,
"<span class='string_literal'>", "</span>");
HighlightRangeCallback(RB, TokOffs, TokOffs + TokLen, BufferStart,
"<span class='string_literal'>", "</span>");
break;
case tok::hash: {
// If this is a preprocessor directive, all tokens to end of line are too.
Expand All @@ -516,8 +549,8 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
}

// Find end of line. This is a hack.
HighlightRange(RB, TokOffs, TokEnd, BufferStart,
"<span class='directive'>", "</span>");
HighlightRangeCallback(RB, TokOffs, TokEnd, BufferStart,
"<span class='directive'>", "</span>");

// Don't skip the next token.
continue;
Expand All @@ -527,12 +560,43 @@ void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP) {
L.LexFromRawLexer(Tok);
}
}
void html::SyntaxHighlight(Rewriter &R, FileID FID, const Preprocessor &PP,
RelexRewriteCacheRef Cache) {
RewriteBuffer &RB = R.getEditBuffer(FID);
const SourceManager &SM = PP.getSourceManager();
llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID);
const char *BufferStart = FromFile.getBuffer().data();

if (Cache) {
auto CacheIt = Cache->SyntaxHighlights.find(FID);
if (CacheIt != Cache->SyntaxHighlights.end()) {
for (const RelexRewriteCache::RawHighlight &H : CacheIt->second) {
HighlightRange(RB, H.B, H.E, BufferStart, H.StartTag.data(),
H.EndTag.data());
}
return;
}
}

// "Every time you would call HighlightRange, cache the inputs as well."
auto HighlightRangeCallback = [&](RewriteBuffer &RB, unsigned B, unsigned E,
const char *BufferStart,
const char *StartTag, const char *EndTag) {
HighlightRange(RB, B, E, BufferStart, StartTag, EndTag);

if (Cache)
Cache->SyntaxHighlights[FID].push_back({B, E, StartTag, EndTag});
};

SyntaxHighlightImpl(R, FID, PP, HighlightRangeCallback);
}

static void HighlightMacrosImpl(
Rewriter &R, FileID FID, const Preprocessor &PP,
llvm::function_ref<void(Rewriter &, SourceLocation, SourceLocation,
const char *, const char *, bool)>
HighlightRangeCallback) {

/// HighlightMacros - This uses the macro table state from the end of the
/// file, to re-expand macros and insert (into the HTML) information about the
/// macro expansions. This won't be perfectly perfect, but it will be
/// reasonably close.
void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
// Re-lex the raw token stream into a token buffer.
const SourceManager &SM = PP.getSourceManager();
std::vector<Token> TokenStream;
Expand Down Expand Up @@ -659,11 +723,44 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) {
// get highlighted.
Expansion = "<span class='macro_popup'>" + Expansion + "</span></span>";

HighlightRange(R, LLoc.getBegin(), LLoc.getEnd(), "<span class='macro'>",
Expansion.c_str(), LLoc.isTokenRange());
HighlightRangeCallback(R, LLoc.getBegin(), LLoc.getEnd(),
"<span class='macro'>", Expansion.c_str(),
LLoc.isTokenRange());
}

// Restore the preprocessor's old state.
TmpPP.setDiagnostics(*OldDiags);
TmpPP.setPragmasEnabled(PragmasPreviouslyEnabled);
}

/// HighlightMacros - This uses the macro table state from the end of the
/// file, to re-expand macros and insert (into the HTML) information about the
/// macro expansions. This won't be perfectly perfect, but it will be
/// reasonably close.
void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor &PP,
RelexRewriteCacheRef Cache) {
if (Cache) {
auto CacheIt = Cache->MacroHighlights.find(FID);
if (CacheIt != Cache->MacroHighlights.end()) {
for (const RelexRewriteCache::Highlight &H : CacheIt->second) {
HighlightRange(R, H.B, H.E, H.StartTag.data(), H.EndTag.data(),
H.IsTokenRange);
}
return;
}
}

// "Every time you would call HighlightRange, cache the inputs as well."
auto HighlightRangeCallback = [&](Rewriter &R, SourceLocation B,
SourceLocation E, const char *StartTag,
const char *EndTag, bool isTokenRange) {
HighlightRange(R, B, E, StartTag, EndTag, isTokenRange);

if (Cache) {
Cache->MacroHighlights[FID].push_back(
{B, E, StartTag, EndTag, isTokenRange});
}
};

HighlightMacrosImpl(R, FID, PP, HighlightRangeCallback);
}
50 changes: 23 additions & 27 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7513,47 +7513,43 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}

// If the callee uses AArch64 SME ZA state but the caller doesn't define
// any, then this is an error.
FunctionType::ArmStateValue ArmZAState =
FunctionType::ArmStateValue CalleeArmZAState =
FunctionType::getArmZAState(ExtInfo.AArch64SMEAttributes);
if (ArmZAState != FunctionType::ARM_None) {
FunctionType::ArmStateValue CalleeArmZT0State =
FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
if (CalleeArmZAState != FunctionType::ARM_None ||
CalleeArmZT0State != FunctionType::ARM_None) {
bool CallerHasZAState = false;
bool CallerHasZT0State = false;
if (const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
auto *Attr = CallerFD->getAttr<ArmNewAttr>();
if (Attr && Attr->isNewZA())
CallerHasZAState = true;
else if (const auto *FPT =
CallerFD->getType()->getAs<FunctionProtoType>())
CallerHasZAState = FunctionType::getArmZAState(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
}

if (!CallerHasZAState)
Diag(Loc, diag::err_sme_za_call_no_za_state);
}

// If the callee uses AArch64 SME ZT0 state but the caller doesn't define
// any, then this is an error.
FunctionType::ArmStateValue ArmZT0State =
FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
if (ArmZT0State != FunctionType::ARM_None) {
bool CallerHasZT0State = false;
if (const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
auto *Attr = CallerFD->getAttr<ArmNewAttr>();
if (Attr && Attr->isNewZT0())
CallerHasZT0State = true;
else if (const auto *FPT =
CallerFD->getType()->getAs<FunctionProtoType>())
CallerHasZT0State =
if (const auto *FPT = CallerFD->getType()->getAs<FunctionProtoType>()) {
CallerHasZAState |=
FunctionType::getArmZAState(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
CallerHasZT0State |=
FunctionType::getArmZT0State(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
}
}

if (!CallerHasZT0State)
if (CalleeArmZAState != FunctionType::ARM_None && !CallerHasZAState)
Diag(Loc, diag::err_sme_za_call_no_za_state);

if (CalleeArmZT0State != FunctionType::ARM_None && !CallerHasZT0State)
Diag(Loc, diag::err_sme_zt0_call_no_zt0_state);

if (CallerHasZAState && CalleeArmZAState == FunctionType::ARM_None &&
CalleeArmZT0State != FunctionType::ARM_None) {
Diag(Loc, diag::err_sme_unimplemented_za_save_restore);
Diag(Loc, diag::note_sme_use_preserves_za);
}
}
}

Expand Down
60 changes: 45 additions & 15 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6196,13 +6196,17 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC,
///
/// \param Loc The location of the name of the entity being declared.
///
/// \param IsTemplateId Whether the name is a (simple-)template-id, and thus
/// we're declaring an explicit / partial specialization / instantiation.
/// \param IsMemberSpecialization Whether we are declaring a member
/// specialization.
///
/// \param TemplateId The template-id, if any.
///
/// \returns true if we cannot safely recover from this error, false otherwise.
bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
DeclarationName Name,
SourceLocation Loc, bool IsTemplateId) {
SourceLocation Loc,
TemplateIdAnnotation *TemplateId,
bool IsMemberSpecialization) {
DeclContext *Cur = CurContext;
while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
Cur = Cur->getParent();
Expand Down Expand Up @@ -6231,7 +6235,7 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
// Check whether the qualifying scope encloses the scope of the original
// declaration. For a template-id, we perform the checks in
// CheckTemplateSpecializationScope.
if (!Cur->Encloses(DC) && !IsTemplateId) {
if (!Cur->Encloses(DC) && !(TemplateId || IsMemberSpecialization)) {
if (Cur->isRecord())
Diag(Loc, diag::err_member_qualification)
<< Name << SS.getRange();
Expand Down Expand Up @@ -6277,12 +6281,32 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
return false;
}

// C++23 [temp.names]p5:
// The keyword template shall not appear immediately after a declarative
// nested-name-specifier.
//
// First check the template-id (if any), and then check each component of the
// nested-name-specifier in reverse order.
//
// FIXME: nested-name-specifiers in friend declarations are declarative,
// but we don't call diagnoseQualifiedDeclaration for them. We should.
if (TemplateId && TemplateId->TemplateKWLoc.isValid())
Diag(Loc, diag::ext_template_after_declarative_nns)
<< FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);

NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
while (SpecLoc.getPrefix()) {
if (SpecLoc.getNestedNameSpecifier()->getKind() ==
NestedNameSpecifier::TypeSpecWithTemplate)
Diag(Loc, diag::ext_template_after_declarative_nns)
<< FixItHint::CreateRemoval(
SpecLoc.getTypeLoc().getTemplateKeywordLoc());

SpecLoc = SpecLoc.getPrefix();
}
// C++11 [dcl.meaning]p1:
// [...] "The nested-name-specifier of the qualified declarator-id shall
// not begin with a decltype-specifer"
NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
while (SpecLoc.getPrefix())
SpecLoc = SpecLoc.getPrefix();
if (isa_and_nonnull<DecltypeType>(
SpecLoc.getNestedNameSpecifier()->getAsType()))
Diag(Loc, diag::err_decltype_in_declarator)
Expand Down Expand Up @@ -6350,9 +6374,13 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
return nullptr;
}
if (!D.getDeclSpec().isFriendSpecified()) {
if (diagnoseQualifiedDeclaration(
D.getCXXScopeSpec(), DC, Name, D.getIdentifierLoc(),
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId)) {
TemplateIdAnnotation *TemplateId =
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
? D.getName().TemplateId
: nullptr;
if (diagnoseQualifiedDeclaration(D.getCXXScopeSpec(), DC, Name,
D.getIdentifierLoc(), TemplateId,
/*IsMemberSpecialization=*/false)) {
if (DC->isRecord())
return nullptr;

Expand Down Expand Up @@ -7727,7 +7755,7 @@ NamedDecl *Sema::ActOnVariableDeclarator(
? TemplateParamLists[0]->getTemplateLoc()
: SourceLocation();
DeclResult Res = ActOnVarTemplateSpecialization(
S, D, TInfo, TemplateKWLoc, TemplateParams, SC,
S, D, TInfo, Previous, TemplateKWLoc, TemplateParams, SC,
IsPartialSpecialization);
if (Res.isInvalid())
return nullptr;
Expand Down Expand Up @@ -8070,8 +8098,8 @@ NamedDecl *Sema::ActOnVariableDeclarator(
D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));
} else {
// If this is an explicit specialization of a static data member, check it.
if (IsMemberSpecialization && !NewVD->isInvalidDecl() &&
CheckMemberSpecialization(NewVD, Previous))
if (IsMemberSpecialization && !IsVariableTemplateSpecialization &&
!NewVD->isInvalidDecl() && CheckMemberSpecialization(NewVD, Previous))
NewVD->setInvalidDecl();

// Merge the decl with the existing one if appropriate.
Expand All @@ -8086,15 +8114,16 @@ NamedDecl *Sema::ActOnVariableDeclarator(
Previous.clear();
NewVD->setInvalidDecl();
}
} else if (D.getCXXScopeSpec().isSet()) {
} else if (D.getCXXScopeSpec().isSet() &&
!IsVariableTemplateSpecialization) {
// No previous declaration in the qualifying scope.
Diag(D.getIdentifierLoc(), diag::err_no_member)
<< Name << computeDeclContext(D.getCXXScopeSpec(), true)
<< D.getCXXScopeSpec().getRange();
NewVD->setInvalidDecl();
}

if (!IsVariableTemplateSpecialization && !IsPlaceholderVariable)
if (!IsPlaceholderVariable)
D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous));

// CheckVariableDeclaration will set NewVD as invalid if something is in
Expand Down Expand Up @@ -17956,6 +17985,7 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
// nested-name-specifier against the current context.
if ((TUK == TUK_Definition || TUK == TUK_Declaration) &&
diagnoseQualifiedDeclaration(SS, DC, OrigName, Loc,
/*TemplateId=*/nullptr,
isMemberSpecialization))
Invalid = true;

Expand Down
14 changes: 9 additions & 5 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3621,14 +3621,18 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
// class X {
// int X::member;
// };
if (DeclContext *DC = computeDeclContext(SS, false))
if (DeclContext *DC = computeDeclContext(SS, false)) {
TemplateIdAnnotation *TemplateId =
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
? D.getName().TemplateId
: nullptr;
diagnoseQualifiedDeclaration(SS, DC, Name, D.getIdentifierLoc(),
D.getName().getKind() ==
UnqualifiedIdKind::IK_TemplateId);
else
TemplateId,
/*IsMemberSpecialization=*/false);
} else {
Diag(D.getIdentifierLoc(), diag::err_member_qualification)
<< Name << SS.getRange();

}
SS.clear();
}

Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5779,7 +5779,8 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
return Self.Context.typesAreCompatible(Lhs, Rhs);
}
case BTT_IsConvertible:
case BTT_IsConvertibleTo: {
case BTT_IsConvertibleTo:
case BTT_IsNothrowConvertible: {
// C++0x [meta.rel]p4:
// Given the following function prototype:
//
Expand Down Expand Up @@ -5840,7 +5841,13 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
return false;

ExprResult Result = Init.Perform(Self, To, Kind, FromPtr);
return !Result.isInvalid() && !SFINAE.hasErrorOccurred();
if (Result.isInvalid() || SFINAE.hasErrorOccurred())
return false;

if (BTT != BTT_IsNothrowConvertible)
return true;

return Self.canThrow(Result.get()) == CT_Cannot;
}

case BTT_IsAssignable:
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7623,7 +7623,8 @@ void Sema::AddTemplateOverloadCandidate(
// functions. In such a case, the candidate functions generated from each
// function template are combined with the set of non-template candidate
// functions.
TemplateDeductionInfo Info(CandidateSet.getLocation());
TemplateDeductionInfo Info(CandidateSet.getLocation(),
FunctionTemplate->getTemplateDepth());
FunctionDecl *Specialization = nullptr;
ConversionSequenceList Conversions;
if (TemplateDeductionResult Result = DeduceTemplateArguments(
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaRISCVVectorLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ void RISCVIntrinsicManagerImpl::ConstructRVVIntrinsics(
{"zvknhb", RVV_REQ_Zvknhb},
{"zvksed", RVV_REQ_Zvksed},
{"zvksh", RVV_REQ_Zvksh},
{"zvfbfwma", RVV_REQ_Zvfbfwma},
{"experimental", RVV_REQ_Experimental}};

// Construction of RVVIntrinsicRecords need to sync with createRVVIntrinsics
Expand Down
38 changes: 23 additions & 15 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1890,8 +1890,12 @@ DeclResult Sema::CheckClassTemplate(
ContextRAII SavedContext(*this, SemanticContext);
if (RebuildTemplateParamsInCurrentInstantiation(TemplateParams))
Invalid = true;
} else if (TUK != TUK_Friend && TUK != TUK_Reference)
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc, false);
}

if (TUK != TUK_Friend && TUK != TUK_Reference)
diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
/*TemplateId-*/ nullptr,
/*IsMemberSpecialization*/ false);

LookupQualifiedName(Previous, SemanticContext);
} else {
Expand Down Expand Up @@ -4601,9 +4605,9 @@ void Sema::CheckDeductionGuideTemplate(FunctionTemplateDecl *TD) {
}

DeclResult Sema::ActOnVarTemplateSpecialization(
Scope *S, Declarator &D, TypeSourceInfo *DI, SourceLocation TemplateKWLoc,
TemplateParameterList *TemplateParams, StorageClass SC,
bool IsPartialSpecialization) {
Scope *S, Declarator &D, TypeSourceInfo *DI, LookupResult &Previous,
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization) {
// D must be variable template id.
assert(D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId &&
"Variable template specialization is declared with a template id.");
Expand Down Expand Up @@ -4783,17 +4787,12 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
// Note that this is an explicit specialization.
Specialization->setSpecializationKind(TSK_ExplicitSpecialization);

if (PrevDecl) {
// Check that this isn't a redefinition of this specialization,
// merging with previous declarations.
LookupResult PrevSpec(*this, GetNameForDeclarator(D), LookupOrdinaryName,
forRedeclarationInCurContext());
PrevSpec.addDecl(PrevDecl);
D.setRedeclaration(CheckVariableDeclaration(Specialization, PrevSpec));
} else if (Specialization->isStaticDataMember() &&
Specialization->isOutOfLine()) {
Previous.clear();
if (PrevDecl)
Previous.addDecl(PrevDecl);
else if (Specialization->isStaticDataMember() &&
Specialization->isOutOfLine())
Specialization->setAccess(VarTemplate->getAccess());
}

return Specialization;
}
Expand Down Expand Up @@ -8831,6 +8830,15 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
bool isMemberSpecialization = false;
bool isPartialSpecialization = false;

if (SS.isSet()) {
if (TUK != TUK_Reference && TUK != TUK_Friend &&
diagnoseQualifiedDeclaration(SS, ClassTemplate->getDeclContext(),
ClassTemplate->getDeclName(),
TemplateNameLoc, &TemplateId,
/*IsMemberSpecialization=*/false))
return true;
}

// Check the validity of the template headers that introduce this
// template.
// FIXME: We probably shouldn't complain about these headers for
Expand Down
14 changes: 12 additions & 2 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3049,6 +3049,7 @@ bool Sema::SubstDefaultArgument(
// default argument expression appears.
ContextRAII SavedContext(*this, FD);
std::unique_ptr<LocalInstantiationScope> LIS;
MultiLevelTemplateArgumentList NewTemplateArgs = TemplateArgs;

if (ForCallExpr) {
// When instantiating a default argument due to use in a call expression,
Expand All @@ -3061,11 +3062,20 @@ bool Sema::SubstDefaultArgument(
/*ForDefinition*/ false);
if (addInstantiatedParametersToScope(FD, PatternFD, *LIS, TemplateArgs))
return true;
const FunctionTemplateDecl *PrimaryTemplate = FD->getPrimaryTemplate();
if (PrimaryTemplate && PrimaryTemplate->isOutOfLine()) {
TemplateArgumentList *CurrentTemplateArgumentList =
TemplateArgumentList::CreateCopy(getASTContext(),
TemplateArgs.getInnermost());
NewTemplateArgs = getTemplateInstantiationArgs(
FD, FD->getDeclContext(), /*Final=*/false,
CurrentTemplateArgumentList->asArray(), /*RelativeToPrimary=*/true);
}
}

runWithSufficientStackSpace(Loc, [&] {
Result = SubstInitializer(PatternExpr, TemplateArgs,
/*DirectInit*/false);
Result = SubstInitializer(PatternExpr, NewTemplateArgs,
/*DirectInit*/ false);
});
}
if (Result.isInvalid())
Expand Down
13 changes: 11 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5907,15 +5907,24 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// - the type-id in the default argument of a type-parameter, or
// - the type-id of a template-argument for a type-parameter
//
// C++23 [dcl.fct]p6 (P0847R7)
// ... A member-declarator with an explicit-object-parameter-declaration
// shall not include a ref-qualifier or a cv-qualifier-seq and shall not be
// declared static or virtual ...
//
// FIXME: Checking this here is insufficient. We accept-invalid on:
//
// template<typename T> struct S { void f(T); };
// S<int() const> s;
//
// ... for instance.
if (IsQualifiedFunction &&
!(Kind == Member && !D.isExplicitObjectMemberFunction() &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_static) &&
// Check for non-static member function and not and
// explicit-object-parameter-declaration
(Kind != Member || D.isExplicitObjectMemberFunction() ||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static ||
(D.getContext() == clang::DeclaratorContext::Member &&
D.isStaticMember())) &&
!IsTypedefName && D.getContext() != DeclaratorContext::TemplateArg &&
D.getContext() != DeclaratorContext::TemplateTypeArg) {
SourceLocation Loc = D.getBeginLoc();
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -4394,7 +4394,8 @@ NestedNameSpecifierLoc TreeTransform<Derived>::TransformNestedNameSpecifierLoc(
SS.Adopt(ETL.getQualifierLoc());
TL = ETL.getNamedTypeLoc();
}
SS.Extend(SemaRef.Context, /*FIXME:*/ SourceLocation(), TL,

SS.Extend(SemaRef.Context, TL.getTemplateKeywordLoc(), TL,
Q.getLocalEndLoc());
break;
}
Expand Down
391 changes: 318 additions & 73 deletions clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
default:
return false;

case Builtin::BI__builtin_assume: {
case Builtin::BI__builtin_assume:
case Builtin::BI__assume: {
assert (Call.getNumArgs() > 0);
SVal Arg = Call.getArgSVal(0);
if (Arg.isUndef())
Expand Down
10 changes: 4 additions & 6 deletions clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
const Preprocessor &PP;
const bool SupportsCrossFileDiagnostics;
llvm::StringSet<> EmittedHashes;
html::RelexRewriteCacheRef RewriterCache =
html::instantiateRelexRewriteCache();

public:
HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
Expand Down Expand Up @@ -309,10 +311,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
return;
}

// FIXME: This causes each file to be re-parsed and syntax-highlighted
// and macro-expanded separately for each report. We could cache such rewrites
// across all reports and only re-do the part that's actually different:
// the warning/note bubbles.
std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
if (report.empty()) {
llvm::errs() << "warning: no diagnostics generated for main file.\n";
Expand Down Expand Up @@ -882,8 +880,8 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,
// If we have a preprocessor, relex the file and syntax highlight.
// We might not have a preprocessor if we come from a deserialized AST file,
// for example.
html::SyntaxHighlight(R, FID, PP);
html::HighlightMacros(R, FID, PP);
html::SyntaxHighlight(R, FID, PP, RewriterCache);
html::HighlightMacros(R, FID, PP, RewriterCache);
}

void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
Expand Down
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/atomic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected %s
// RUN: %clang_cc1 -verify=both,ref %s

/// FIXME: Copied from test/Sema/atomic-expr.c.
/// this expression seems to be rejected for weird reasons,
/// but we imitate the current interpreter's behavior.
_Atomic int ai = 0;
// FIXME: &ai is an address constant, so this should be accepted as an
// initializer, but the bit-cast inserted due to the pointer conversion is
// tripping up the test for whether the initializer is a constant expression.
// The warning is correct but the error is not.
_Atomic(int *) aip3 = &ai; // both-warning {{incompatible pointer types initializing '_Atomic(int *)' with an expression of type '_Atomic(int) *'}} \
// both-error {{initializer element is not a compile-time constant}}
17 changes: 17 additions & 0 deletions clang/test/AST/Interp/atomic.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++11 %s
// RUN: %clang_cc1 -verify=both,ref -std=c++11 %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++98 %s
// RUN: %clang_cc1 -verify=both,ref -std=c++98 %s



// expected-no-diagnostics
// ref-no-diagnostics


/// Rejected in c++98
#if __cplusplus >= 201103L
constexpr _Atomic(bool) B = true;
static_assert(B, "");
#endif

10 changes: 6 additions & 4 deletions clang/test/AST/Interp/builtins.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify -fms-extensions
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -fms-extensions -S -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated -fms-extensions
// RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -fms-extensions -S -emit-llvm -o - | FileCheck %s

// expected-no-diagnostics
// ref-no-diagnostics
Expand All @@ -26,6 +26,8 @@ bool is_this_constant() {
constexpr bool assume() {
__builtin_assume(true);
__builtin_assume(false);
__assume(1);
__assume(false);
return true;
}
static_assert(assume(), "");
34 changes: 34 additions & 0 deletions clang/test/AST/Interp/c.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ typedef __INTPTR_TYPE__ intptr_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;

_Static_assert(1, "");

_Static_assert(__objc_yes, "");
_Static_assert(!__objc_no, "");

_Static_assert(0 != 1, "");
_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
// pedantic-expected-warning {{not an integer constant expression}}
Expand Down Expand Up @@ -95,3 +99,33 @@ void f (int z) {
// pedantic-ref-error {{'default' statement not in switch}}
}
}

int expr;
int chooseexpr[__builtin_choose_expr(1, 1, expr)];

int somefunc(int i) {
return (i, 65537) * 65537; // expected-warning {{left operand of comma operator has no effect}} \
// expected-warning {{overflow in expression; result is 131073}} \
// pedantic-expected-warning {{left operand of comma operator has no effect}} \
// pedantic-expected-warning {{overflow in expression; result is 131073}} \
// ref-warning {{left operand of comma operator has no effect}} \
// ref-warning {{overflow in expression; result is 131073}} \
// pedantic-ref-warning {{left operand of comma operator has no effect}} \
// pedantic-ref-warning {{overflow in expression; result is 131073}}

}

/// FIXME: The following test is incorrect in the new interpreter.
/// The null pointer returns 16 from its getIntegerRepresentation().
#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
struct ArrayStruct {
char n[1];
};
char name2[(int)&((struct ArrayStruct*)0)->n]; // expected-warning {{folded to constant array}} \
// pedantic-expected-warning {{folded to constant array}} \
// ref-warning {{folded to constant array}} \
// pedantic-ref-warning {{folded to constant array}}
_Static_assert(sizeof(name2) == 0, ""); // expected-error {{failed}} \
// expected-note {{evaluates to}} \
// pedantic-expected-error {{failed}} \
// pedantic-expected-note {{evaluates to}}
15 changes: 15 additions & 0 deletions clang/test/AST/Interp/complex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,18 @@ namespace Sub {
}

}

namespace ZeroInit {
typedef _Complex float fcomplex;
typedef _Complex unsigned icomplex;

constexpr fcomplex test7 = fcomplex();
static_assert(__real(test7) == 0.0f, "");
static_assert(__imag(test7) == 0.0f, "");

constexpr icomplex test8 = icomplex();
static_assert(__real(test8) == 0, "");
static_assert(__imag(test8) == 0, "");

constexpr int ignored = (fcomplex(), 0);
}
8 changes: 8 additions & 0 deletions clang/test/AST/Interp/cxx98.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ int NCI; // both-note {{declared here}}
int NCIA[NCI]; // both-warning {{variable length array}} \
// both-error {{variable length array}} \\
// both-note {{read of non-const variable 'NCI'}}


struct V {
char c[1];
banana V() : c("i") {} // both-error {{unknown type name 'banana'}} \
// both-error {{constructor cannot have a return type}}
};
_Static_assert(V().c[0], ""); // both-error {{is not an integral constant expression}}
3 changes: 3 additions & 0 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ static_assert(number != 10, ""); // expected-error{{failed}} \
// expected-note{{evaluates to}} \
// ref-note{{evaluates to}}

static_assert(__objc_yes, "");
static_assert(!__objc_no, "");

constexpr bool b = number;
static_assert(b, "");
constexpr int one = true;
Expand Down
25 changes: 25 additions & 0 deletions clang/test/AST/ast-dump-pack-indexing-crash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// RUN: not %clang_cc1 -std=c++2c -ast-dump %s | FileCheck %s

namespace InvalidPacksShouldNotCrash {

struct NotAPack;
template <typename T, auto V, template<typename> typename Tp>
void not_pack() {
int i = 0;
i...[0]; // expected-error {{i does not refer to the name of a parameter pack}}
V...[0]; // expected-error {{V does not refer to the name of a parameter pack}}
NotAPack...[0] a; // expected-error{{'NotAPack' does not refer to the name of a parameter pack}}
T...[0] b; // expected-error{{'T' does not refer to the name of a parameter pack}}
Tp...[0] c; // expected-error{{'Tp' does not refer to the name of a parameter pack}}
}

// CHECK: FunctionDecl {{.*}} not_pack 'void ()'
// CHECK: DeclStmt {{.*}}
// CHECK: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} a 'NotAPack...{{.*}}'
// CHECK-NEXT: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} 'T...{{.*}}'
// CHECK-NEXT: DeclStmt {{.*}}
// CHECK-NEXT: VarDecl {{.*}} c 'Tp...{{.*}}'

}
18 changes: 18 additions & 0 deletions clang/test/Analysis/builtin-functions.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core,debug.ExprInspection %s -std=c++11 -verify
// RUN: %clang_analyze_cc1 -triple x86_64-pc-windows-msvc19.11.0 -fms-extensions -analyzer-checker=core,debug.ExprInspection %s -std=c++11 -verify

void clang_analyzer_eval(bool);
void clang_analyzer_warnIfReached();
Expand Down Expand Up @@ -65,6 +66,23 @@ void g(int i) {
}
}

#ifdef _WIN32
namespace ms {
void f(int i) {
__assume(i < 10);
clang_analyzer_eval(i < 15); // expected-warning {{TRUE}}
}

void g(int i) {
if (i > 5) {
__assume(i < 5);
clang_analyzer_warnIfReached(); // Assumtion contradicts constraints.
// We give up the analysis on this path.
}
}
} // namespace ms
#endif

void test_constant_p(void *ptr) {
int i = 1;
const int j = 2;
Expand Down
29 changes: 29 additions & 0 deletions clang/test/Analysis/html_diagnostics/counter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: rm -fR %t
// RUN: mkdir %t
// RUN: %clang_analyze_cc1 -analyzer-checker=core \
// RUN: -analyzer-output=html -o %t -verify %s
// RUN: grep -v CHECK %t/report-*.html | FileCheck %s


void foo() {
int *x = 0;
*x = __COUNTER__; // expected-warning{{Dereference of null pointer (loaded from variable 'x')}}
}

void bar() {
int *y;
*y = __COUNTER__; // expected-warning{{Dereference of undefined pointer value (loaded from variable 'y')}}
}

// The checks below confirm that both reports have the same values for __COUNTER__.
//
// FIXME: The correct values are (0, 1, 0, 1). Because we re-lex the file in order
// to detect macro expansions for HTML report purposes, they turn into (2, 3, 2, 3)
// by the time we emit HTML. But at least it's better than (2, 3, 4, 5)
// which would have been the case if we re-lexed the file *each* time we
// emitted an HTML report.

// CHECK: <span class='macro'>__COUNTER__<span class='macro_popup'>2</span></span>
// CHECK: <span class='macro'>__COUNTER__<span class='macro_popup'>3</span></span>
// CHECK: <span class='macro'>__COUNTER__<span class='macro_popup'>2</span></span>
// CHECK: <span class='macro'>__COUNTER__<span class='macro_popup'>3</span></span>
142 changes: 90 additions & 52 deletions clang/test/Analysis/out-of-bounds-diagnostics.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \
// RUN: -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,alpha.security.taint -verify %s

int array[10];
int TenElements[10];

void arrayUnderflow(void) {
array[-3] = 5;
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
// expected-note@-2 {{Access of 'array' at negative byte offset -12}}
TenElements[-3] = 5;
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset -12}}
}

int underflowWithDeref(void) {
int *p = array;
int *p = TenElements;
--p;
return *p;
// expected-warning@-1 {{Out of bound access to memory preceding 'array'}}
// expected-note@-2 {{Access of 'array' at negative byte offset -4}}
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset -4}}
}

int scanf(const char *restrict fmt, ...);
Expand All @@ -24,88 +24,89 @@ void taintedIndex(void) {
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
array[index] = 5;
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
TenElements[index] = 5;
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
// expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
}

int *taintedIndexAfterTheEndPtr(void) {
// NOTE: Technically speaking, this testcase does not trigger any UB because
// &array[10] is the after-the-end pointer which is well-defined; but this is
// a bug-prone situation and far from the idiomatic use of `&array[size]`, so
// it's better to report an error. This report can be easily silenced by
// writing array+index instead of &array[index].
// &TenElements[10] is the after-the-end pointer which is well-defined; but
// this is a bug-prone situation and far from the idiomatic use of
// `&TenElements[size]`, so it's better to report an error. This report can
// be easily silenced by writing TenElements+index instead of
// &TenElements[index].
int index;
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
if (index < 0 || index > 10)
return array;
return TenElements;
// expected-note@-2 {{Assuming 'index' is >= 0}}
// expected-note@-3 {{Left side of '||' is false}}
// expected-note@-4 {{Assuming 'index' is <= 10}}
// expected-note@-5 {{Taking false branch}}
return &array[index];
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted index}}
// expected-note@-2 {{Access of 'array' with a tainted index that may be too large}}
return &TenElements[index];
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted index}}
// expected-note@-2 {{Access of 'TenElements' with a tainted index that may be too large}}
}

void taintedOffset(void) {
int index;
scanf("%d", &index);
// expected-note@-1 {{Taint originated here}}
// expected-note@-2 {{Taint propagated to the 2nd argument}}
int *p = array + index;
int *p = TenElements + index;
p[0] = 5;
// expected-warning@-1 {{Potential out of bound access to 'array' with tainted offset}}
// expected-note@-2 {{Access of 'array' with a tainted offset that may be too large}}
// expected-warning@-1 {{Potential out of bound access to 'TenElements' with tainted offset}}
// expected-note@-2 {{Access of 'TenElements' with a tainted offset that may be too large}}
}

void arrayOverflow(void) {
array[12] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
TenElements[12] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
}

void flippedOverflow(void) {
12[array] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 12, while it holds only 10 'int' elements}}
12[TenElements] = 5;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 12, while it holds only 10 'int' elements}}
}

int *afterTheEndPtr(void) {
// This is an unusual but standard-compliant way of writing (array + 10).
return &array[10]; // no-warning
// This is an unusual but standard-compliant way of writing (TenElements + 10).
return &TenElements[10]; // no-warning
}

int useAfterTheEndPtr(void) {
// ... but dereferencing the after-the-end pointer is still invalid.
return *afterTheEndPtr();
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 10, while it holds only 10 'int' elements}}
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 10, while it holds only 10 'int' elements}}
}

int *afterAfterTheEndPtr(void) {
// This is UB, it's invalid to form an after-after-the-end pointer.
return &array[11];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 11, while it holds only 10 'int' elements}}
return &TenElements[11];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 11, while it holds only 10 'int' elements}}
}

int *potentialAfterTheEndPtr(int idx) {
if (idx < 10) { /* ...do something... */ }
// expected-note@-1 {{Assuming 'idx' is >= 10}}
// expected-note@-2 {{Taking false branch}}
return &array[idx];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
return &TenElements[idx];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
// NOTE: On the idx >= 10 branch the normal "optimistic" behavior would've
// been continuing with the assumption that idx == 10 and the return value is
// a legitimate after-the-end pointer. The checker deviates from this by
// reporting an error because this situation is very suspicious and far from
// the idiomatic `&array[size]` expressions. If the report is FP, the
// developer can easily silence it by writing array+idx instead of
// &array[idx].
// the idiomatic `&TenElements[size]` expressions. If the report is FP, the
// developer can easily silence it by writing TenElements+idx instead of
// &TenElements[idx].
}

int scalar;
Expand Down Expand Up @@ -156,9 +157,9 @@ int arrayOfStructsArrow(void) {
}

short convertedArray(void) {
return ((short*)array)[47];
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at index 47, while it holds only 20 'short' elements}}
return ((short*)TenElements)[47];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 47, while it holds only 20 'short' elements}}
}

struct two_bytes {
Expand Down Expand Up @@ -195,12 +196,22 @@ void *malloc(size_t size);

int *mallocRegion(void) {
int *mem = (int*)malloc(2*sizeof(int));

mem[3] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
return mem;
}

int *mallocRegionDeref(void) {
int *mem = (int*)malloc(2*sizeof(int));

*(mem + 3) = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of the heap area at index 3, while it holds only 2 'int' elements}}
return mem;
}

void *alloca(size_t size);

int allocaRegion(void) {
Expand All @@ -211,34 +222,61 @@ int allocaRegion(void) {
return *mem;
}

int *unknownExtent(int arg) {
if (arg >= 2)
int *symbolicExtent(int arg) {
// expected-note@+2 {{Assuming 'arg' is < 5}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 5)
return 0;
int *mem = (int*)malloc(arg);

// TODO: without the following reference to 'arg', the analyzer would discard
// the range information about (the symbolic value of) 'arg'. This is
// incorrect because while the variable itself is inaccessible, it becomes
// the symbolic extent of 'mem', so we still want to reason about its
// potential values.
(void)arg;

mem[8] = -2;
// FIXME: this should produce
// {{Out of bound access to memory after the end of the heap area}}
// {{Access of 'int' element in the heap area at index 8}}
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of 'int' element in the heap area at index 8}}
return mem;
}

void unknownIndex(int arg) {
int *symbolicExtentDiscardedRangeInfo(int arg) {
// This is a copy of the case 'symbolicExtent' without the '(void)arg' hack.
// TODO: if the analyzer can detect the out-of-bounds access within this
// testcase, then remove this and the `(void)arg` hack from `symbolicExtent`.
if (arg >= 5)
return 0;
int *mem = (int*)malloc(arg);
mem[8] = -2;
return mem;
}

void symbolicIndex(int arg) {
// expected-note@+2 {{Assuming 'arg' is >= 12}}
// expected-note@+1 {{Taking true branch}}
if (arg >= 12)
array[arg] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of 'array'}}
// expected-note@-2 {{Access of 'array' at an overflowing index, while it holds only 10 'int' elements}}
TenElements[arg] = -2;
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
}

int *nothingIsCertain(int x, int y) {
if (x >= 2)
return 0;
int *mem = (int*)malloc(x);

if (y >= 8)
mem[y] = -2;
// FIXME: this should produce
// {{Out of bound access to memory after the end of the heap area}}
// {{Access of 'int' element in the heap area at an overflowing index}}
// but apparently the analyzer isn't smart enough to deduce this.

// Keep constraints alive. (Without this, the overeager garbage collection of
// constraints would _also_ prevent the intended behavior in this testcase.)
(void)x;

return mem;
}
199 changes: 199 additions & 0 deletions clang/test/Analysis/out-of-bounds-notes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text \
// RUN: -analyzer-checker=core,alpha.security.ArrayBoundV2,unix.Malloc,alpha.security.taint -verify %s

int TenElements[10];

int irrelevantAssumptions(int arg) {
int a = TenElements[arg];
// Here the analyzer assumes that `arg` is in bounds, but doesn't report this
// because `arg` is not interesting for the bug.
int b = TenElements[13];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at index 13, while it holds only 10 'int' elements}}
return a + b;
}


int assumingBoth(int arg) {
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg]; // no additional note, we already assumed that 'arg' is in bounds
int c = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b + c;
}

int assumingBothPointerToMiddle(int arg) {
// If we're accessing an TenElements through a pointer pointing to its middle, the checker
// will speak about the "byte offset" measured from the beginning of the TenElements.
int *p = TenElements + 2;
int a = p[arg];
// FIXME: The following note does not appear:
// {{Assuming byte offset is non-negative and less than 40, the extent of 'TenElements'}}
// It seems that the analyzer "gives up" modeling this pointer arithmetics
// and says that `p[arg]` is just an UnknownVal (instead of calculating that
// it's equivalent to `TenElements[2+arg]`).

int b = TenElements[arg]; // This is normal access, and only the lower bound is new.
// expected-note@-1 {{Assuming index is non-negative}}
int c = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b + c;
}

int assumingLower(int arg) {
// expected-note@+2 {{Assuming 'arg' is < 10}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 10)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is non-negative}}
int b = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b;
}

int assumingUpper(int arg) {
// expected-note@+2 {{Assuming 'arg' is >= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg < 0)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

int assumingUpperIrrelevant(int arg) {
// FIXME: The assumption "assuming index is less than 10" is printed because
// it's assuming something about the interesting variable `arg`; however,
// it's irrelevant because in this testcase the out of bound access is
// deduced from the _lower_ bound on `arg`. Currently the analyzer cannot
// filter out assumptions that are logically irrelevant but "touch"
// interesting symbols; eventually it would be good to add support for this.

// expected-note@+2 {{Assuming 'arg' is >= 0}}
// expected-note@+1 {{Taking false branch}}
if (arg < 0)
return 0;
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[arg + 10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return a + b;
}

int assumingUpperUnsigned(unsigned arg) {
int a = TenElements[arg];
// expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
int b = TenElements[(int)arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

int assumingNothing(unsigned arg) {
// expected-note@+2 {{Assuming 'arg' is < 10}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 10)
return 0;
int a = TenElements[arg]; // no note here, we already know that 'arg' is in bounds
int b = TenElements[(int)arg - 10];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b;
}

short assumingConvertedToCharP(int arg) {
// When indices are reported, the note will use the element type that's the
// result type of the subscript operator.
char *cp = (char*)TenElements;
char a = cp[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 40, the number of 'char' elements in 'TenElements'}}
char b = cp[arg]; // no additional note, we already assumed that 'arg' is in bounds
char c = cp[arg + 40];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 40 'char' elements}}
return a + b + c;
}

struct foo {
int num;
char a[8];
char b[5];
};

int assumingConvertedToIntP(struct foo f, int arg) {
// When indices are reported, the note will use the element type that's the
// result type of the subscript operator.
int a = ((int*)(f.a))[arg];
// expected-note@-1 {{Assuming index is non-negative and less than 2, the number of 'int' elements in 'f.a'}}
// However, if the extent of the memory region is not divisible by the
// element size, the checker measures the offset and extent in bytes.
int b = ((int*)(f.b))[arg];
// expected-note@-1 {{Assuming byte offset is less than 5, the extent of 'f.b'}}
int c = TenElements[arg-2];
// expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
return a + b + c;
}

int assumingPlainOffset(struct foo f, int arg) {
// This TC is intended to check the corner case that the checker prints the
// shorter "offset" instead of "byte offset" when it's irrelevant that the
// offset is measured in bytes.

// expected-note@+2 {{Assuming 'arg' is < 2}}
// expected-note@+1 {{Taking false branch}}
if (arg >= 2)
return 0;

int b = ((int*)(f.b))[arg];
// expected-note@-1 {{Assuming byte offset is non-negative and less than 5, the extent of 'f.b'}}
// FIXME: this should be {{Assuming offset is non-negative}}
// but the current simplification algorithm doesn't realize that arg <= 1
// implies that the byte offset arg*4 will be less than 5.

int c = TenElements[arg+10];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
return b + c;
}

typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t size);
void free(void *ptr);

int assumingExtent(int arg) {
// Verify that the assumption note is printed when the extent is interesting
// (even if the index isn't interesting).
int *mem = (int*)malloc(arg);

mem[12] = 123;
// expected-note@-1 {{Assuming index '12' is less than the number of 'int' elements in the heap area}}

free(mem);

return TenElements[arg];
// expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
// expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
}

int *extentInterestingness(int arg) {
// Verify that in an out-of-bounds access issue the extent is marked as
// interesting (so assumptions about its value are printed).
int *mem = (int*)malloc(arg);

TenElements[arg] = 123;
// expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}

return &mem[12];
// expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
// expected-note@-2 {{Access of 'int' element in the heap area at index 12}}
}
112 changes: 112 additions & 0 deletions clang/test/CXX/dcl.decl/dcl.meaning/dcl.meaning.general/p3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

namespace N0 {
template<typename T>
void f0();

template<typename T>
int x0 = 0;

template<typename T>
class C0;
}
using namespace N0;

template<>
void f0<int>(); // expected-error {{no function template matches}}

template<>
int x0<int>;

template<>
class C0<int>;

namespace N1 {
namespace N2 {
template<typename T>
void f2();

template<typename T>
int x2 = 0;

template<typename T>
class C2;
}
using namespace N2;
}

template<>
void N1::f2<int>(); // expected-error {{no function template matches}}

template<>
int N1::x2<int>;

template<>
class N1::C2<int>;

namespace N3 {
namespace N4 {
template<typename T>
void f4();

template<typename T>
int x4 = 0;

template<typename T>
class C4;
}
using N4::f4;
using N4::x4;
using N4::C4;
}

template<>
void N3::f4<int>(); // expected-error {{no function template matches}}

template<>
int N3::x4<int>;

template<>
class N3::C4<int>;

inline namespace N5 {
template<typename T>
void f5();

template<typename T>
int x5 = 0;

template<typename T>
class C5;
}

template<>
void f5<int>();

template<>
int x5<int>;

template<>
class C5<int>;

namespace N6 {
inline namespace N7 {
template<typename T>
void f7();

template<typename T>
int x7 = 0;

template<typename T>
class C7;
}
}

template<>
void N6::f7<int>();

template<>
int N6::x7<int>;

template<>
class N6::C7<int>;
4 changes: 2 additions & 2 deletions clang/test/CXX/drs/dr23xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ struct Bad2 { int a, b; };
} // namespace dr2386
namespace std {
template <typename T> struct tuple_size;
template <> struct std::tuple_size<dr2386::Bad1> {};
template <> struct std::tuple_size<dr2386::Bad2> {
template <> struct tuple_size<dr2386::Bad1> {};
template <> struct tuple_size<dr2386::Bad2> {
static const int value = 42;
};
} // namespace std
Expand Down
6 changes: 4 additions & 2 deletions clang/test/CXX/drs/dr7xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ namespace dr727 { // dr727: partial
// expected-note@#dr727-N {{explicitly specialized declaration is here}}

template<> struct A::C<double>;
// expected-error@-1 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
// expected-error@-2 {{class template specialization of 'C' not in class 'A' or an enclosing namespace}}
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
template<> void A::f<double>();
// expected-error@-1 {{o function template matches function template specialization 'f'}}
Expand All @@ -116,7 +117,8 @@ namespace dr727 { // dr727: partial
// expected-note@#dr727-N {{explicitly specialized declaration is here}}

template<typename T> struct A::C<T***>;
// expected-error@-1 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
// expected-error@-1 {{non-friend class member 'C' cannot have a qualified name}}
// expected-error@-2 {{class template partial specialization of 'C' not in class 'A' or an enclosing namespace}}
// expected-note@#dr727-C {{explicitly specialized declaration is here}}
template<typename T> static int A::N<T***>;
// expected-error@-1 {{non-friend class member 'N' cannot have a qualified name}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ struct X1 {

template<typename T>
template<typename U>
void X1<T>::template B<U>::f() { }
void X1<T>::template B<U>::f() { } // expected-warning{{'template' cannot be used after a declarative}}

// PR5527
template <template <class> class T>
Expand Down
206 changes: 206 additions & 0 deletions clang/test/CXX/temp/temp.names/p5.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// RUN: %clang_cc1 -fsyntax-only -pedantic-errors -verify %s

template<typename T> struct A {
template<typename U> struct B {
// FIXME: The standard does not seem to consider non-friend elaborated-type-specifiers that
// declare partial specializations/explicit specializations/explicit instantiations to be
// declarative, see https://lists.isocpp.org/core/2024/01/15325.php
struct C;
template<typename V> struct D;

void f();
template<typename V> void g();

static int x;
template<typename V> static int y;

enum class E;
};
};

template<typename T>
template<typename U>
struct A<T>::template B<U>::C { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
struct A<int>::template B<bool>::C; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
struct A<int>::template B<bool>::C { }; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
struct A<T>::template B<U>::D<V*>; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
struct A<T>::B<U>::template D<V**>; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
struct A<T>::template B<U>::D { }; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
struct A<T>::template B<U>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
struct A<T>::B<U>::template D<V**> { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::template B<bool>::D; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
struct A<int>::template B<bool>::D<short>; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
struct A<int>::B<bool>::template D<long>; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::template B<bool>::D<V*>; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::B<bool>::template D<V**>; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::template B<bool>::D { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
struct A<int>::template B<bool>::D<short> { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
struct A<int>::B<bool>::template D<long> { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::template B<bool>::D<V*> { }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
struct A<int>::B<bool>::template D<V**> { }; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
void A<T>::template B<U>::f() { } // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
void A<int>::template B<bool>::f() { } // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
void A<T>::template B<U>::g() { } // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
void A<int>::B<bool>::template g<short>() { } // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
void A<int>::template B<bool>::g<long>() { } // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
void A<int>::template B<bool>::g() { } // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
int A<T>::template B<U>::x = 0; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
int A<T>::template B<U>::y = 0; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
int A<T>::template B<U>::y<V*> = 0; // expected-error{{'template' cannot be used after a declarative}}

template<typename T>
template<typename U>
template<typename V>
int A<T>::B<U>::template y<V**> = 0; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
int A<int>::template B<bool>::y = 0; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
int A<int>::template B<bool>::y<short> = 0; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<>
int A<int>::B<bool>::template y<long> = 0; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
int A<int>::template B<bool>::y<V*> = 0; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
template<typename V>
int A<int>::B<bool>::template y<V**> = 0; // expected-error{{'template' cannot be used after a declarative}}
template<typename T>
template<typename U>
enum class A<T>::template B<U>::E { a }; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
enum class A<int>::template B<bool>::E; // expected-error{{'template' cannot be used after a declarative}}

template<>
template<>
enum class A<int>::template B<bool>::E { a }; // expected-error{{'template' cannot be used after a declarative}}

// FIXME: We don't call Sema::diagnoseQualifiedDeclaration for friend declarations right now
template<typename T>
struct F {
// FIXME: f should be assumed to name a template per [temp.names] p3.4
friend void T::f<int>();
// expected-error@-1{{use 'template' keyword to treat 'f' as a dependent template name}}
// expected-error@-2{{no candidate function template was found for}}

// FIXME: We should diagnose the presence of 'template' here
friend void T::template f<int>(); // expected-error{{no candidate function template was found for}}
friend void T::template U<int>::f();

// These should be allowed
friend class T::template U<int>;
friend class T::template U<int>::V;
};
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.spec/part.spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -478,4 +478,4 @@ template <typename T> class PCTT6<TestClass::PrivateClass, T> {
};
template <typename T1> template <typename, typename> class PCTT6<TestClass::PrivateClass, T1>::NCT4 final {};
// expected-error@+1 2{{is a private member of}}
template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::template NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};
template <typename T1> template <typename T2> struct PCTT6<TestClass::PrivateClass, T1>::NCT3<T2, TestClass::TemplatePrivateClass<TestClass::TemplateProtectedClass<TestClass::PublicClass>>> : PCTT6<TestClass::PrivateClass, T1>::NCT4<T2, TestClass::TemplatePrivateClass<int>> {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
// REQUIRES: riscv-registered-target
// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
// RUN: -target-feature +experimental-zvfbfmin -disable-O0-optnone \
// RUN: -emit-llvm %s -o - | opt -S -passes=mem2reg | \
// RUN: FileCheck --check-prefix=CHECK-RV64 %s

#include <riscv_vector.h>

// CHECK-RV64-LABEL: define dso_local <vscale x 1 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf4(
// CHECK-RV64-SAME: <vscale x 1 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv1bf16.nxv1f32.i64(<vscale x 1 x bfloat> poison, <vscale x 1 x float> [[VS2]], i64 7, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 1 x bfloat> [[TMP0]]
//
vbfloat16mf4_t test_vfncvtbf16_f_f_w_bf16mf4(vfloat32mf2_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf4(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf2(
// CHECK-RV64-SAME: <vscale x 2 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv2bf16.nxv2f32.i64(<vscale x 2 x bfloat> poison, <vscale x 2 x float> [[VS2]], i64 7, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 2 x bfloat> [[TMP0]]
//
vbfloat16mf2_t test_vfncvtbf16_f_f_w_bf16mf2(vfloat32m1_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf2(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x bfloat> @test_vfncvtbf16_f_f_w_bf16m1(
// CHECK-RV64-SAME: <vscale x 4 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv4bf16.nxv4f32.i64(<vscale x 4 x bfloat> poison, <vscale x 4 x float> [[VS2]], i64 7, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x bfloat> [[TMP0]]
//
vbfloat16m1_t test_vfncvtbf16_f_f_w_bf16m1(vfloat32m2_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m1(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x bfloat> @test_vfncvtbf16_f_f_w_bf16m2(
// CHECK-RV64-SAME: <vscale x 8 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv8bf16.nxv8f32.i64(<vscale x 8 x bfloat> poison, <vscale x 8 x float> [[VS2]], i64 7, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x bfloat> [[TMP0]]
//
vbfloat16m2_t test_vfncvtbf16_f_f_w_bf16m2(vfloat32m4_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m2(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x bfloat> @test_vfncvtbf16_f_f_w_bf16m4(
// CHECK-RV64-SAME: <vscale x 16 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv16bf16.nxv16f32.i64(<vscale x 16 x bfloat> poison, <vscale x 16 x float> [[VS2]], i64 7, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x bfloat> [[TMP0]]
//
vbfloat16m4_t test_vfncvtbf16_f_f_w_bf16m4(vfloat32m8_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m4(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 1 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf4_m(
// CHECK-RV64-SAME: <vscale x 1 x i1> [[VM:%.*]], <vscale x 1 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv1bf16.nxv1f32.i64(<vscale x 1 x bfloat> poison, <vscale x 1 x float> [[VS2]], <vscale x 1 x i1> [[VM]], i64 7, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 1 x bfloat> [[TMP0]]
//
vbfloat16mf4_t test_vfncvtbf16_f_f_w_bf16mf4_m(vbool64_t vm, vfloat32mf2_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf4_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf2_m(
// CHECK-RV64-SAME: <vscale x 2 x i1> [[VM:%.*]], <vscale x 2 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv2bf16.nxv2f32.i64(<vscale x 2 x bfloat> poison, <vscale x 2 x float> [[VS2]], <vscale x 2 x i1> [[VM]], i64 7, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 2 x bfloat> [[TMP0]]
//
vbfloat16mf2_t test_vfncvtbf16_f_f_w_bf16mf2_m(vbool32_t vm, vfloat32m1_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf2_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x bfloat> @test_vfncvtbf16_f_f_w_bf16m1_m(
// CHECK-RV64-SAME: <vscale x 4 x i1> [[VM:%.*]], <vscale x 4 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv4bf16.nxv4f32.i64(<vscale x 4 x bfloat> poison, <vscale x 4 x float> [[VS2]], <vscale x 4 x i1> [[VM]], i64 7, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 4 x bfloat> [[TMP0]]
//
vbfloat16m1_t test_vfncvtbf16_f_f_w_bf16m1_m(vbool16_t vm, vfloat32m2_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m1_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x bfloat> @test_vfncvtbf16_f_f_w_bf16m2_m(
// CHECK-RV64-SAME: <vscale x 8 x i1> [[VM:%.*]], <vscale x 8 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv8bf16.nxv8f32.i64(<vscale x 8 x bfloat> poison, <vscale x 8 x float> [[VS2]], <vscale x 8 x i1> [[VM]], i64 7, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 8 x bfloat> [[TMP0]]
//
vbfloat16m2_t test_vfncvtbf16_f_f_w_bf16m2_m(vbool8_t vm, vfloat32m4_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m2_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x bfloat> @test_vfncvtbf16_f_f_w_bf16m4_m(
// CHECK-RV64-SAME: <vscale x 16 x i1> [[VM:%.*]], <vscale x 16 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv16bf16.nxv16f32.i64(<vscale x 16 x bfloat> poison, <vscale x 16 x float> [[VS2]], <vscale x 16 x i1> [[VM]], i64 7, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 16 x bfloat> [[TMP0]]
//
vbfloat16m4_t test_vfncvtbf16_f_f_w_bf16m4_m(vbool4_t vm, vfloat32m8_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m4_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 1 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf4_rm(
// CHECK-RV64-SAME: <vscale x 1 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv1bf16.nxv1f32.i64(<vscale x 1 x bfloat> poison, <vscale x 1 x float> [[VS2]], i64 0, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 1 x bfloat> [[TMP0]]
//
vbfloat16mf4_t test_vfncvtbf16_f_f_w_bf16mf4_rm(vfloat32mf2_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf4_rm(vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf2_rm(
// CHECK-RV64-SAME: <vscale x 2 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv2bf16.nxv2f32.i64(<vscale x 2 x bfloat> poison, <vscale x 2 x float> [[VS2]], i64 0, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 2 x bfloat> [[TMP0]]
//
vbfloat16mf2_t test_vfncvtbf16_f_f_w_bf16mf2_rm(vfloat32m1_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf2_rm(vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x bfloat> @test_vfncvtbf16_f_f_w_bf16m1_rm(
// CHECK-RV64-SAME: <vscale x 4 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv4bf16.nxv4f32.i64(<vscale x 4 x bfloat> poison, <vscale x 4 x float> [[VS2]], i64 0, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x bfloat> [[TMP0]]
//
vbfloat16m1_t test_vfncvtbf16_f_f_w_bf16m1_rm(vfloat32m2_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m1_rm(vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x bfloat> @test_vfncvtbf16_f_f_w_bf16m2_rm(
// CHECK-RV64-SAME: <vscale x 8 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv8bf16.nxv8f32.i64(<vscale x 8 x bfloat> poison, <vscale x 8 x float> [[VS2]], i64 0, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x bfloat> [[TMP0]]
//
vbfloat16m2_t test_vfncvtbf16_f_f_w_bf16m2_rm(vfloat32m4_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m2_rm(vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x bfloat> @test_vfncvtbf16_f_f_w_bf16m4_rm(
// CHECK-RV64-SAME: <vscale x 16 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.nxv16bf16.nxv16f32.i64(<vscale x 16 x bfloat> poison, <vscale x 16 x float> [[VS2]], i64 0, i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x bfloat> [[TMP0]]
//
vbfloat16m4_t test_vfncvtbf16_f_f_w_bf16m4_rm(vfloat32m8_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m4_rm(vs2, __RISCV_FRM_RNE, vl);
}

vbfloat16mf4_t
// CHECK-RV64-LABEL: define dso_local <vscale x 1 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf4_rm_m(
// CHECK-RV64-SAME: <vscale x 1 x i1> [[VM:%.*]], <vscale x 1 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv1bf16.nxv1f32.i64(<vscale x 1 x bfloat> poison, <vscale x 1 x float> [[VS2]], <vscale x 1 x i1> [[VM]], i64 0, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 1 x bfloat> [[TMP0]]
//
test_vfncvtbf16_f_f_w_bf16mf4_rm_m(vbool64_t vm, vfloat32mf2_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf4_rm_m(vm, vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x bfloat> @test_vfncvtbf16_f_f_w_bf16mf2_rm_m(
// CHECK-RV64-SAME: <vscale x 2 x i1> [[VM:%.*]], <vscale x 2 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv2bf16.nxv2f32.i64(<vscale x 2 x bfloat> poison, <vscale x 2 x float> [[VS2]], <vscale x 2 x i1> [[VM]], i64 0, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 2 x bfloat> [[TMP0]]
//
vbfloat16mf2_t test_vfncvtbf16_f_f_w_bf16mf2_rm_m(vbool32_t vm,
vfloat32m1_t vs2, size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16mf2_rm_m(vm, vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x bfloat> @test_vfncvtbf16_f_f_w_bf16m1_rm_m(
// CHECK-RV64-SAME: <vscale x 4 x i1> [[VM:%.*]], <vscale x 4 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv4bf16.nxv4f32.i64(<vscale x 4 x bfloat> poison, <vscale x 4 x float> [[VS2]], <vscale x 4 x i1> [[VM]], i64 0, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 4 x bfloat> [[TMP0]]
//
vbfloat16m1_t test_vfncvtbf16_f_f_w_bf16m1_rm_m(vbool16_t vm, vfloat32m2_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m1_rm_m(vm, vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x bfloat> @test_vfncvtbf16_f_f_w_bf16m2_rm_m(
// CHECK-RV64-SAME: <vscale x 8 x i1> [[VM:%.*]], <vscale x 8 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv8bf16.nxv8f32.i64(<vscale x 8 x bfloat> poison, <vscale x 8 x float> [[VS2]], <vscale x 8 x i1> [[VM]], i64 0, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 8 x bfloat> [[TMP0]]
//
vbfloat16m2_t test_vfncvtbf16_f_f_w_bf16m2_rm_m(vbool8_t vm, vfloat32m4_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m2_rm_m(vm, vs2, __RISCV_FRM_RNE, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x bfloat> @test_vfncvtbf16_f_f_w_bf16m4_rm_m(
// CHECK-RV64-SAME: <vscale x 16 x i1> [[VM:%.*]], <vscale x 16 x float> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x bfloat> @llvm.riscv.vfncvtbf16.f.f.w.mask.nxv16bf16.nxv16f32.i64(<vscale x 16 x bfloat> poison, <vscale x 16 x float> [[VS2]], <vscale x 16 x i1> [[VM]], i64 0, i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 16 x bfloat> [[TMP0]]
//
vbfloat16m4_t test_vfncvtbf16_f_f_w_bf16m4_rm_m(vbool4_t vm, vfloat32m8_t vs2,
size_t vl) {
return __riscv_vfncvtbf16_f_f_w_bf16m4_rm_m(vm, vs2, __RISCV_FRM_RNE, vl);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4
// REQUIRES: riscv-registered-target
// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
// RUN: -target-feature +experimental-zvfbfmin -disable-O0-optnone \
// RUN: -emit-llvm %s -o - | opt -S -passes=mem2reg | \
// RUN: FileCheck --check-prefix=CHECK-RV64 %s

#include <riscv_vector.h>

// CHECK-RV64-LABEL: define dso_local <vscale x 1 x float> @test_vfwcvtbf16_f_f_v_f32mf2(
// CHECK-RV64-SAME: <vscale x 1 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x float> @llvm.riscv.vfwcvtbf16.f.f.v.nxv1f32.nxv1bf16.i64(<vscale x 1 x float> poison, <vscale x 1 x bfloat> [[VS2]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 1 x float> [[TMP0]]
//
vfloat32mf2_t test_vfwcvtbf16_f_f_v_f32mf2(vbfloat16mf4_t vs2, size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32mf2(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x float> @test_vfwcvtbf16_f_f_v_f32m1(
// CHECK-RV64-SAME: <vscale x 2 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x float> @llvm.riscv.vfwcvtbf16.f.f.v.nxv2f32.nxv2bf16.i64(<vscale x 2 x float> poison, <vscale x 2 x bfloat> [[VS2]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 2 x float> [[TMP0]]
//
vfloat32m1_t test_vfwcvtbf16_f_f_v_f32m1(vbfloat16mf2_t vs2, size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m1(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x float> @test_vfwcvtbf16_f_f_v_f32m2(
// CHECK-RV64-SAME: <vscale x 4 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x float> @llvm.riscv.vfwcvtbf16.f.f.v.nxv4f32.nxv4bf16.i64(<vscale x 4 x float> poison, <vscale x 4 x bfloat> [[VS2]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 4 x float> [[TMP0]]
//
vfloat32m2_t test_vfwcvtbf16_f_f_v_f32m2(vbfloat16m1_t vs2, size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m2(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x float> @test_vfwcvtbf16_f_f_v_f32m4(
// CHECK-RV64-SAME: <vscale x 8 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x float> @llvm.riscv.vfwcvtbf16.f.f.v.nxv8f32.nxv8bf16.i64(<vscale x 8 x float> poison, <vscale x 8 x bfloat> [[VS2]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 8 x float> [[TMP0]]
//
vfloat32m4_t test_vfwcvtbf16_f_f_v_f32m4(vbfloat16m2_t vs2, size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m4(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x float> @test_vfwcvtbf16_f_f_v_f32m8(
// CHECK-RV64-SAME: <vscale x 16 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x float> @llvm.riscv.vfwcvtbf16.f.f.v.nxv16f32.nxv16bf16.i64(<vscale x 16 x float> poison, <vscale x 16 x bfloat> [[VS2]], i64 [[VL]])
// CHECK-RV64-NEXT: ret <vscale x 16 x float> [[TMP0]]
//
vfloat32m8_t test_vfwcvtbf16_f_f_v_f32m8(vbfloat16m4_t vs2, size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m8(vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 1 x float> @test_vfwcvtbf16_f_f_v_f32mf2_m(
// CHECK-RV64-SAME: <vscale x 1 x i1> [[VM:%.*]], <vscale x 1 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 1 x float> @llvm.riscv.vfwcvtbf16.f.f.v.mask.nxv1f32.nxv1bf16.i64(<vscale x 1 x float> poison, <vscale x 1 x bfloat> [[VS2]], <vscale x 1 x i1> [[VM]], i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 1 x float> [[TMP0]]
//
vfloat32mf2_t test_vfwcvtbf16_f_f_v_f32mf2_m(vbool64_t vm, vbfloat16mf4_t vs2,
size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32mf2_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 2 x float> @test_vfwcvtbf16_f_f_v_f32m1_m(
// CHECK-RV64-SAME: <vscale x 2 x i1> [[VM:%.*]], <vscale x 2 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 2 x float> @llvm.riscv.vfwcvtbf16.f.f.v.mask.nxv2f32.nxv2bf16.i64(<vscale x 2 x float> poison, <vscale x 2 x bfloat> [[VS2]], <vscale x 2 x i1> [[VM]], i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 2 x float> [[TMP0]]
//
vfloat32m1_t test_vfwcvtbf16_f_f_v_f32m1_m(vbool32_t vm, vbfloat16mf2_t vs2,
size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m1_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 4 x float> @test_vfwcvtbf16_f_f_v_f32m2_m(
// CHECK-RV64-SAME: <vscale x 4 x i1> [[VM:%.*]], <vscale x 4 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 4 x float> @llvm.riscv.vfwcvtbf16.f.f.v.mask.nxv4f32.nxv4bf16.i64(<vscale x 4 x float> poison, <vscale x 4 x bfloat> [[VS2]], <vscale x 4 x i1> [[VM]], i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 4 x float> [[TMP0]]
//
vfloat32m2_t test_vfwcvtbf16_f_f_v_f32m2_m(vbool16_t vm, vbfloat16m1_t vs2,
size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m2_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 8 x float> @test_vfwcvtbf16_f_f_v_f32m4_m(
// CHECK-RV64-SAME: <vscale x 8 x i1> [[VM:%.*]], <vscale x 8 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 8 x float> @llvm.riscv.vfwcvtbf16.f.f.v.mask.nxv8f32.nxv8bf16.i64(<vscale x 8 x float> poison, <vscale x 8 x bfloat> [[VS2]], <vscale x 8 x i1> [[VM]], i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 8 x float> [[TMP0]]
//
vfloat32m4_t test_vfwcvtbf16_f_f_v_f32m4_m(vbool8_t vm, vbfloat16m2_t vs2,
size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m4_m(vm, vs2, vl);
}

// CHECK-RV64-LABEL: define dso_local <vscale x 16 x float> @test_vfwcvtbf16_f_f_v_f32m8_m(
// CHECK-RV64-SAME: <vscale x 16 x i1> [[VM:%.*]], <vscale x 16 x bfloat> [[VS2:%.*]], i64 noundef [[VL:%.*]]) #[[ATTR0]] {
// CHECK-RV64-NEXT: entry:
// CHECK-RV64-NEXT: [[TMP0:%.*]] = call <vscale x 16 x float> @llvm.riscv.vfwcvtbf16.f.f.v.mask.nxv16f32.nxv16bf16.i64(<vscale x 16 x float> poison, <vscale x 16 x bfloat> [[VS2]], <vscale x 16 x i1> [[VM]], i64 [[VL]], i64 3)
// CHECK-RV64-NEXT: ret <vscale x 16 x float> [[TMP0]]
//
vfloat32m8_t test_vfwcvtbf16_f_f_v_f32m8_m(vbool4_t vm, vbfloat16m4_t vs2,
size_t vl) {
return __riscv_vfwcvtbf16_f_f_v_f32m8_m(vm, vs2, vl);
}
Loading