31 changes: 18 additions & 13 deletions clang/lib/Driver/ToolChains/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ static void getRISCFeaturesFromMcpu(const Driver &D, const Arg *A,
D.Diag(clang::diag::err_drv_unsupported_option_argument)
<< A->getSpelling() << Mcpu;
}

if (llvm::RISCV::hasFastUnalignedAccess(Mcpu)) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
}
}

void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
Expand All @@ -82,6 +77,8 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
if (!getArchFeatures(D, MArch, Features, Args))
return;

bool CPUFastUnaligned = false;

// If users give march and mcpu, get std extension feature from MArch
// and other features (ex. mirco architecture feature) from mcpu
if (Arg *A = Args.getLastArg(options::OPT_mcpu_EQ)) {
Expand All @@ -90,6 +87,9 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
CPU = llvm::sys::getHostCPUName();

getRISCFeaturesFromMcpu(D, A, Triple, CPU, Features);

if (llvm::RISCV::hasFastUnalignedAccess(CPU))
CPUFastUnaligned = true;
}

// Handle features corresponding to "-ffixed-X" options
Expand Down Expand Up @@ -169,18 +169,23 @@ void riscv::getRISCVTargetFeatures(const Driver &D, const llvm::Triple &Triple,
Features.push_back("-relax");
}

// Android requires fast unaligned access on RISCV64.
if (Triple.isAndroid()) {
// If -mstrict-align or -mno-strict-align is passed, use it. Otherwise, the
// unaligned-*-mem is enabled if the CPU supports it or the target is
// Android.
if (const Arg *A = Args.getLastArg(options::OPT_mno_strict_align,
options::OPT_mstrict_align)) {
if (A->getOption().matches(options::OPT_mno_strict_align)) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
} else {
Features.push_back("-unaligned-scalar-mem");
Features.push_back("-unaligned-vector-mem");
}
} else if (CPUFastUnaligned || Triple.isAndroid()) {
Features.push_back("+unaligned-scalar-mem");
Features.push_back("+unaligned-vector-mem");
}

// -mstrict-align is default, unless -mno-strict-align is specified.
AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
options::OPT_mstrict_align, "unaligned-scalar-mem");
AddTargetFeature(Args, Features, options::OPT_mno_strict_align,
options::OPT_mstrict_align, "unaligned-vector-mem");

// Now add any that the user explicitly requested on the command line,
// which may override the defaults.
handleTargetFeaturesGroup(D, Triple, Args, Features,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5678,6 +5678,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
115 changes: 88 additions & 27 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,14 @@ static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
return true;
}

static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()))
return false;

return true;
}

static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
switch (Triple.getArch()) {
default:
Expand All @@ -176,38 +184,91 @@ static bool mustUseNonLeafFramePointerForTarget(const llvm::Triple &Triple) {
}
}

// True if a target-specific option requires the frame chain to be preserved,
// even if new frame records are not created.
static bool mustMaintainValidFrameChain(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs and -mframe-chain=aapcs+leaf
// options require the frame pointer register to be reserved (or point to a
// new AAPCS-compilant frame record), even with -fno-omit-frame-pointer.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V != "none";
}
return false;
}
return false;
}

// True if a target-specific option causes -fno-omit-frame-pointer to also
// cause frame records to be created in leaf functions.
static bool framePointerImpliesLeafFramePointer(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
if (Triple.isARM() || Triple.isThumb()) {
// For 32-bit Arm, the -mframe-chain=aapcs+leaf option causes the
// -fno-omit-frame-pointer optiion to imply -mno-omit-leaf-frame-pointer,
// but does not by itself imply either option.
if (Arg *A = Args.getLastArg(options::OPT_mframe_chain)) {
StringRef V = A->getValue();
return V == "aapcs+leaf";
}
return false;
}
return false;
}

clang::CodeGenOptions::FramePointerKind
getFramePointerKind(const llvm::opt::ArgList &Args,
const llvm::Triple &Triple) {
// We have 4 states:
// There are three things to consider here:
// * Should a frame record be created for non-leaf functions?
// * Should a frame record be created for leaf functions?
// * Is the frame pointer register reserved, i.e. must it always point to
// either a new, valid frame record or be un-modified?
//
// 00) leaf retained, non-leaf retained
// 01) leaf retained, non-leaf omitted (this is invalid)
// 10) leaf omitted, non-leaf retained
// (what -momit-leaf-frame-pointer was designed for)
// 11) leaf omitted, non-leaf omitted
// Not all combinations of these are valid:
// * It's not useful to have leaf frame records without non-leaf ones.
// * It's not useful to have frame records without reserving the frame
// pointer.
//
// "omit" options taking precedence over "no-omit" options is the only way
// to make 3 valid states representable
llvm::opt::Arg *A =
Args.getLastArg(clang::driver::options::OPT_fomit_frame_pointer,
clang::driver::options::OPT_fno_omit_frame_pointer);

bool OmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fomit_frame_pointer);
bool NoOmitFP = A && A->getOption().matches(
clang::driver::options::OPT_fno_omit_frame_pointer);
bool OmitLeafFP =
Args.hasFlag(clang::driver::options::OPT_momit_leaf_frame_pointer,
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
(Triple.isAndroid() && Triple.isRISCV64()));
if (NoOmitFP || mustUseNonLeafFramePointerForTarget(Triple) ||
(!OmitFP && useFramePointerForTargetByDefault(Args, Triple))) {
if (OmitLeafFP)
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
return clang::CodeGenOptions::FramePointerKind::All;
}
// | Non-leaf | Leaf | Reserved |
// | N | N | N | FramePointerKind::None
// | N | N | Y | FramePointerKind::Reserved
// | N | Y | N | Invalid
// | N | Y | Y | Invalid
// | Y | N | N | Invalid
// | Y | N | Y | FramePointerKind::NonLeaf
// | Y | Y | N | Invalid
// | Y | Y | Y | FramePointerKind::All
//
// The FramePointerKind::Reserved case is currently only reachable for Arm,
// which has the -mframe-chain= option which can (in combination with
// -fno-omit-frame-pointer) specify that the frame chain must be valid,
// without requiring new frame records to be created.

bool DefaultFP = useFramePointerForTargetByDefault(Args, Triple);
bool EnableFP =
mustUseNonLeafFramePointerForTarget(Triple) ||
Args.hasFlag(clang::driver::options::OPT_fno_omit_frame_pointer,
clang::driver::options::OPT_fomit_frame_pointer, DefaultFP);

bool DefaultLeafFP =
useLeafFramePointerForTargetByDefault(Triple) ||
(EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
bool EnableLeafFP = Args.hasFlag(
clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
clang::driver::options::OPT_momit_leaf_frame_pointer, DefaultLeafFP);

bool FPRegReserved = EnableFP || mustMaintainValidFrameChain(Args, Triple);

if (EnableFP) {
if (EnableLeafFP)
return clang::CodeGenOptions::FramePointerKind::All;
return clang::CodeGenOptions::FramePointerKind::NonLeaf;
}
if (FPRegReserved)
return clang::CodeGenOptions::FramePointerKind::Reserved;
return clang::CodeGenOptions::FramePointerKind::None;
}

Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Flang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
case CodeGenOptions::FramePointerKind::None:
FPKeepKindStr = "-mframe-pointer=none";
break;
case CodeGenOptions::FramePointerKind::Reserved:
FPKeepKindStr = "-mframe-pointer=reserved";
break;
case CodeGenOptions::FramePointerKind::NonLeaf:
FPKeepKindStr = "-mframe-pointer=non-leaf";
break;
Expand Down
5 changes: 2 additions & 3 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
using namespace clang;
using namespace clang::extractapi;
using namespace llvm;
using namespace llvm::json;

namespace {

Expand Down Expand Up @@ -1036,9 +1035,9 @@ void SymbolGraphSerializer::serializeGraphToStream(
ExtendedModule &&EM) {
Object Root = serializeGraph(ModuleName, std::move(EM));
if (Options.Compact)
OS << formatv("{0}", Value(std::move(Root))) << "\n";
OS << formatv("{0}", json::Value(std::move(Root))) << "\n";
else
OS << formatv("{0:2}", Value(std::move(Root))) << "\n";
OS << formatv("{0:2}", json::Value(std::move(Root))) << "\n";
}

void SymbolGraphSerializer::serializeMainSymbolGraph(
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Format/ContinuationIndenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1712,7 +1712,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
(!Previous || Previous->isNot(tok::kw_return) ||
(Style.Language != FormatStyle::LK_Java && PrecedenceLevel > 0)) &&
(Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign ||
PrecedenceLevel != prec::Comma || Current.NestingLevel == 0) &&
PrecedenceLevel > prec::Comma || Current.NestingLevel == 0) &&
(!Style.isTableGen() ||
(Previous && Previous->isOneOf(TT_TableGenDAGArgListComma,
TT_TableGenDAGArgListCommaToBreak)))) {
Expand Down
9 changes: 2 additions & 7 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,8 @@ class AnnotatingParser {
Line.First->startsSequence(tok::kw_export, Keywords.kw_module) ||
Line.First->startsSequence(tok::kw_export, Keywords.kw_import)) {
Tok->setType(TT_ModulePartitionColon);
} else if (Line.First->is(tok::kw_asm)) {
Tok->setType(TT_InlineASMColon);
} else if (Contexts.back().ColonIsDictLiteral || Style.isProto()) {
Tok->setType(TT_DictLiteral);
if (Style.Language == FormatStyle::LK_TextProto) {
Expand Down Expand Up @@ -1425,13 +1427,6 @@ class AnnotatingParser {
// This handles a special macro in ObjC code where selectors including
// the colon are passed as macro arguments.
Tok->setType(TT_ObjCMethodExpr);
} else if (Contexts.back().ContextKind == tok::l_paren &&
!Line.InPragmaDirective) {
if (Style.isTableGen() && Contexts.back().IsTableGenDAGArg) {
Tok->setType(TT_TableGenDAGArgListColon);
break;
}
Tok->setType(TT_InlineASMColon);
}
break;
case tok::pipe:
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1181,10 +1181,10 @@ void UnwrappedLineParser::parsePPDefine() {
Line->InMacroBody = true;

if (Style.SkipMacroDefinitionBody) {
do {
while (!eof()) {
FormatTok->Finalized = true;
nextToken();
} while (!eof());
FormatTok = Tokens->getNextToken();
}
addUnwrappedLine();
return;
}
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Frontend/FrontendActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1169,8 +1169,8 @@ void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
llvm::SmallVector<dependency_directives_scan::Token, 16> Tokens;
llvm::SmallVector<dependency_directives_scan::Directive, 32> Directives;
if (scanSourceForDependencyDirectives(
FromFile.getBuffer(), Tokens, Directives, CI.getLangOpts(),
&CI.getDiagnostics(), SM.getLocForStartOfFile(SM.getMainFileID()))) {
FromFile.getBuffer(), Tokens, Directives, &CI.getDiagnostics(),
SM.getLocForStartOfFile(SM.getMainFileID()))) {
assert(CI.getDiagnostics().hasErrorOccurred() &&
"no errors reported for failure");

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
if (!ND)
continue;
// Check if we need to clean up the IdResolver chain.
if (ND->getDeclName().getFETokenInfo())
if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&
!D->getLangOpts().CPlusPlus)
getCI()->getSema().IdResolver.RemoveDecl(ND);
}
}
Expand Down
165 changes: 91 additions & 74 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"

#include <cstdarg>

using namespace clang;

// FIXME: Figure out how to unify with namespace init_convenience from
Expand Down Expand Up @@ -270,14 +273,10 @@ Interpreter::~Interpreter() {
// can't find the precise resource directory in unittests so we have to hard
// code them.
const char *const Runtimes = R"(
#define __CLANG_REPL__ 1
#ifdef __cplusplus
#define EXTERN_C extern "C"
void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, void*);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, float);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, double);
void __clang_Interpreter_SetValueNoAlloc(void*, void*, void*, long double);
void __clang_Interpreter_SetValueNoAlloc(void*,void*,void*,unsigned long long);
struct __clang_Interpreter_NewTag{} __ci_newtag;
void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept;
template <class T, class = T (*)() /*disable for arrays*/>
Expand All @@ -289,7 +288,11 @@ const char *const Runtimes = R"(
void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) {
__clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size);
}
#else
#define EXTERN_C extern
#endif // __cplusplus

EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...);
)";

llvm::Expected<std::unique_ptr<Interpreter>>
Expand Down Expand Up @@ -588,15 +591,17 @@ std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
if (!LookupInterface(ValuePrintingInfo[NoAlloc],
MagicRuntimeInterface[NoAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
if (Ctx.getLangOpts().CPlusPlus) {
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return nullptr;
}

return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
}
Expand Down Expand Up @@ -855,69 +860,81 @@ __clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal,
return VRef.getPtr();
}

// Pointers, lvalue struct that can take as a reference.
REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
void *Val) {
extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc(
void *This, void *OutVal, void *OpaqueType, ...) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setPtr(Val);
}
Interpreter *I = static_cast<Interpreter *>(This);
VRef = Value(I, OpaqueType);
if (VRef.isVoid())
return;

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal,
void *OpaqueType) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
}
va_list args;
va_start(args, /*last named param*/ OpaqueType);

static void SetValueDataBasedOnQualType(Value &V, unsigned long long Data) {
QualType QT = V.getType();
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();

switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
#define X(type, name) \
case BuiltinType::name: \
V.set##name(Data); \
break;
REPL_BUILTIN_TYPES
#undef X
QualType QT = VRef.getType();
if (VRef.getKind() == Value::K_PtrOrObj) {
VRef.setPtr(va_arg(args, void *));
} else {
if (const auto *ET = QT->getAs<EnumType>())
QT = ET->getDecl()->getIntegerType();
switch (QT->castAs<BuiltinType>()->getKind()) {
default:
llvm_unreachable("unknown type kind!");
break;
// Types shorter than int are resolved as int, else va_arg has UB.
case BuiltinType::Bool:
VRef.setBool(va_arg(args, int));
break;
case BuiltinType::Char_S:
VRef.setChar_S(va_arg(args, int));
break;
case BuiltinType::SChar:
VRef.setSChar(va_arg(args, int));
break;
case BuiltinType::Char_U:
VRef.setChar_U(va_arg(args, unsigned));
break;
case BuiltinType::UChar:
VRef.setUChar(va_arg(args, unsigned));
break;
case BuiltinType::Short:
VRef.setShort(va_arg(args, int));
break;
case BuiltinType::UShort:
VRef.setUShort(va_arg(args, unsigned));
break;
case BuiltinType::Int:
VRef.setInt(va_arg(args, int));
break;
case BuiltinType::UInt:
VRef.setUInt(va_arg(args, unsigned));
break;
case BuiltinType::Long:
VRef.setLong(va_arg(args, long));
break;
case BuiltinType::ULong:
VRef.setULong(va_arg(args, unsigned long));
break;
case BuiltinType::LongLong:
VRef.setLongLong(va_arg(args, long long));
break;
case BuiltinType::ULongLong:
VRef.setULongLong(va_arg(args, unsigned long long));
break;
// Types shorter than double are resolved as double, else va_arg has UB.
case BuiltinType::Float:
VRef.setFloat(va_arg(args, double));
break;
case BuiltinType::Double:
VRef.setDouble(va_arg(args, double));
break;
case BuiltinType::LongDouble:
VRef.setLongDouble(va_arg(args, long double));
break;
// See REPL_BUILTIN_TYPES.
}
}
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
unsigned long long Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
SetValueDataBasedOnQualType(VRef, Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
float Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setFloat(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setDouble(Val);
}

REPL_EXTERNAL_VISIBILITY void
__clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType,
long double Val) {
Value &VRef = *(Value *)OutVal;
VRef = Value(static_cast<Interpreter *>(This), OpaqueType);
VRef.setLongDouble(Val);
va_end(args);
}

// A trampoline to work around the fact that operator placement new cannot
Expand Down
22 changes: 9 additions & 13 deletions clang/lib/Lex/DependencyDirectivesScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,14 @@ struct DirectiveWithTokens {
struct Scanner {
Scanner(StringRef Input,
SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc,
const LangOptions &LangOpts)
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc)
: Input(Input), Tokens(Tokens), Diags(Diags),
InputSourceLoc(InputSourceLoc),
LangOpts(getLangOptsForDepScanning(LangOpts)),
TheLexer(InputSourceLoc, this->LangOpts, Input.begin(), Input.begin(),
InputSourceLoc(InputSourceLoc), LangOpts(getLangOptsForDepScanning()),
TheLexer(InputSourceLoc, LangOpts, Input.begin(), Input.begin(),
Input.end()) {}

static LangOptions
getLangOptsForDepScanning(const LangOptions &invocationLangOpts) {
LangOptions LangOpts(invocationLangOpts);
static LangOptions getLangOptsForDepScanning() {
LangOptions LangOpts;
// Set the lexer to use 'tok::at' for '@', instead of 'tok::unknown'.
LangOpts.ObjC = true;
LangOpts.LineComment = true;
Expand Down Expand Up @@ -703,7 +700,7 @@ bool Scanner::lex_Pragma(const char *&First, const char *const End) {
SmallVector<dependency_directives_scan::Token> DiscardTokens;
const char *Begin = Buffer.c_str();
Scanner PragmaScanner{StringRef(Begin, Buffer.size()), DiscardTokens, Diags,
InputSourceLoc, LangOptions()};
InputSourceLoc};

PragmaScanner.TheLexer.setParsingPreprocessorDirective(true);
if (PragmaScanner.lexPragma(Begin, Buffer.end()))
Expand Down Expand Up @@ -953,10 +950,9 @@ bool Scanner::scan(SmallVectorImpl<Directive> &Directives) {

bool clang::scanSourceForDependencyDirectives(
StringRef Input, SmallVectorImpl<dependency_directives_scan::Token> &Tokens,
SmallVectorImpl<Directive> &Directives, const LangOptions &LangOpts,
DiagnosticsEngine *Diags, SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc, LangOpts)
.scan(Directives);
SmallVectorImpl<Directive> &Directives, DiagnosticsEngine *Diags,
SourceLocation InputSourceLoc) {
return Scanner(Input, Tokens, Diags, InputSourceLoc).scan(Directives);
}

void clang::printDependencyDirectivesAsSource(
Expand Down
5 changes: 1 addition & 4 deletions clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,11 +571,8 @@ StmtResult Parser::ParseExprStatement(ParsedStmtContext StmtCtx) {
}

Token *CurTok = nullptr;
// If the semicolon is missing at the end of REPL input, consider if
// we want to do value printing. Note this is only enabled in C++ mode
// since part of the implementation requires C++ language features.
// Note we shouldn't eat the token since the callback needs it.
if (Tok.is(tok::annot_repl_input_end) && Actions.getLangOpts().CPlusPlus)
if (Tok.is(tok::annot_repl_input_end))
CurTok = &Tok;
else
// Otherwise, eat the semicolon.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,11 @@ void Scope::dumpImpl(raw_ostream &OS) const {
{CompoundStmtScope, "CompoundStmtScope"},
{ClassInheritanceScope, "ClassInheritanceScope"},
{CatchScope, "CatchScope"},
{ConditionVarScope, "ConditionVarScope"},
{OpenMPOrderClauseScope, "OpenMPOrderClauseScope"},
{LambdaScope, "LambdaScope"},
{OpenACCComputeConstructScope, "OpenACCComputeConstructScope"},
{TypeAliasScope, "TypeAliasScope"},
{FriendScope, "FriendScope"},
};

Expand Down
6 changes: 3 additions & 3 deletions clang/lib/Sema/SemaAMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ bool SemaAMDGPU::CheckAMDGCNBuiltinFunctionCall(unsigned BuiltinID,
constexpr const int SizeIdx = 2;
llvm::APSInt Size;
Expr *ArgExpr = TheCall->getArg(SizeIdx);
ExprResult R = SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
if (R.isInvalid())
return true;
[[maybe_unused]] ExprResult R =
SemaRef.VerifyIntegerConstantExpression(ArgExpr, &Size);
assert(!R.isInvalid());
switch (Size.getSExtValue()) {
case 1:
case 2:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -46,6 +47,10 @@ static const AvailabilityAttr *getAttrForPlatform(ASTContext &Context,
// Check each AvailabilityAttr to find the one for this platform.
// For multiple attributes with the same platform try to find one for this
// environment.
// The attribute is always on the FunctionDecl, not on the
// FunctionTemplateDecl.
if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
D = FTD->getTemplatedDecl();
for (const auto *A : D->attrs()) {
if (const auto *Avail = dyn_cast<AvailabilityAttr>(A)) {
// FIXME: this is copied from CheckAvailability. We should try to
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,8 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) {
// Partial translation units that are created in incremental processing must
// not clean up the IdResolver because PTUs should take into account the
// declarations that came from previous PTUs.
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC)
if (!PP.isIncrementalProcessingEnabled() || getLangOpts().ObjC ||
getLangOpts().CPlusPlus)
IdResolver.RemoveDecl(D);

// Warn on it if we are shadowing a declaration.
Expand Down
35 changes: 9 additions & 26 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5572,9 +5572,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
Res = Immediate.TransformInitializer(Param->getInit(),
/*NotCopy=*/false);
});
if (Res.isUsable())
Res = ConvertParamDefaultArgument(Param, Res.get(),
Res.get()->getBeginLoc());
if (Res.isInvalid())
return ExprError();
Res = ConvertParamDefaultArgument(Param, Res.get(),
Res.get()->getBeginLoc());
if (Res.isInvalid())
return ExprError();
Init = Res.get();
Expand Down Expand Up @@ -5608,10 +5609,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
InitializationContext.emplace(Loc, Field, CurContext);

Expr *Init = nullptr;
bool HasRewrittenInit = false;

bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();

EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);

Expand Down Expand Up @@ -5646,36 +5646,19 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
ImmediateCallVisitor V(getASTContext());
if (!NestedDefaultChecking)
V.TraverseDecl(Field);

// CWG1815
// Support lifetime extension of temporary created by aggregate
// initialization using a default member initializer. We should always rebuild
// the initializer if it contains any temporaries (if the initializer
// expression is an ExprWithCleanups). Then make sure the normal lifetime
// extension code recurses into the default initializer and does lifetime
// extension when warranted.
bool ContainsAnyTemporaries =
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
if (V.HasImmediateCalls || InLifetimeExtendingContext ||
ContainsAnyTemporaries) {
HasRewrittenInit = true;
if (V.HasImmediateCalls) {
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
CurContext};
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
NestedDefaultChecking;
// Pass down lifetime extending flag, and collect temporaries in
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
keepInLifetimeExtendingContext();

EnsureImmediateInvocationInDefaultArgs Immediate(*this);
ExprResult Res;

// Rebuild CXXDefaultInitExpr might cause diagnostics.
SFINAETrap Trap(*this);
runWithSufficientStackSpace(Loc, [&] {
Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
/*CXXDirectInit=*/false);
});
if (Res.isUsable())
if (!Res.isInvalid())
Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
if (Res.isInvalid()) {
Field->setInvalidDecl();
Expand All @@ -5702,7 +5685,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {

return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
Field, InitializationContext->Context,
HasRewrittenInit ? Init : nullptr);
Init);
}

// DR1351:
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,9 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
bool ListInitialization) {
QualType Ty = TInfo->getType();
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();

assert((!ListInitialization || Exprs.size() == 1) &&
"List initialization must have exactly one expression.");
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);

InitializedEntity Entity =
Expand Down Expand Up @@ -5126,6 +5129,7 @@ static bool CheckUnaryTypeTraitTypeCompleteness(Sema &S, TypeTrait UTT,
case UTT_IsStandardLayout:
case UTT_IsPOD:
case UTT_IsLiteral:
case UTT_IsBitwiseCloneable:
// By analogy, is_trivially_relocatable and is_trivially_equality_comparable
// impose the same constraints.
case UTT_IsTriviallyRelocatable:
Expand Down Expand Up @@ -5619,6 +5623,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
return C.hasUniqueObjectRepresentations(T);
case UTT_IsTriviallyRelocatable:
return T.isTriviallyRelocatableType(C);
case UTT_IsBitwiseCloneable:
return T.isBitwiseCloneableType(C);
case UTT_IsReferenceable:
return T.isReferenceable();
case UTT_CanPassInRegs:
Expand Down
19 changes: 18 additions & 1 deletion clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8063,6 +8063,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
enum PathLifetimeKind {
/// Lifetime-extend along this path.
Extend,
/// We should lifetime-extend, but we don't because (due to technical
/// limitations) we can't. This happens for default member initializers,
/// which we don't clone for every use, so we don't have a unique
/// MaterializeTemporaryExpr to update.
ShouldExtend,
/// Do not lifetime extend along this path.
NoExtend
};
Expand All @@ -8074,7 +8079,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
PathLifetimeKind Kind = PathLifetimeKind::Extend;
for (auto Elem : Path) {
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
Kind = PathLifetimeKind::Extend;
Kind = PathLifetimeKind::ShouldExtend;
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
return PathLifetimeKind::NoExtend;
}
Expand Down Expand Up @@ -8194,6 +8199,18 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
ExtendingEntity->allocateManglingNumber());
// Also visit the temporaries lifetime-extended by this initializer.
return true;

case PathLifetimeKind::ShouldExtend:
// We're supposed to lifetime-extend the temporary along this path (per
// the resolution of DR1815), but we don't support that yet.
//
// FIXME: Properly handle this situation. Perhaps the easiest approach
// would be to clone the initializer expression on each use that would
// lifetime extend its temporaries.
Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
<< RK << DiagRange;
break;

case PathLifetimeKind::NoExtend:
// If the path goes through the initialization of a variable or field,
// it can't possibly reach a temporary created in this full-expression.
Expand Down
1,193 changes: 656 additions & 537 deletions clang/lib/Sema/SemaOpenACC.cpp

Large diffs are not rendered by default.

12 changes: 4 additions & 8 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14172,13 +14172,6 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
&ArgumentChanged))
return ExprError();

if (E->isListInitialization() && !E->isStdInitListInitialization()) {
ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
if (Res.isInvalid())
return ExprError();
Args = {Res.get()};
}
}

if (!getDerived().AlwaysRebuild() &&
Expand All @@ -14190,9 +14183,12 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
return SemaRef.MaybeBindToTemporary(E);
}

// FIXME: We should just pass E->isListInitialization(), but we're not
// prepared to handle list-initialization without a child InitListExpr.
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
return getDerived().RebuildCXXTemporaryObjectExpr(
T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
T, LParenLoc, Args, E->getEndLoc(),
/*ListInitialization=*/LParenLoc.isInvalid());
}

template<typename Derived>
Expand Down
163 changes: 87 additions & 76 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,7 @@ bool ASTReader::ReadSLocEntry(int ID) {

unsigned NumFileDecls = Record[7];
if (NumFileDecls && ContextObj) {
const LocalDeclID *FirstDecl = F->FileSortedDecls + Record[6];
const unaligned_decl_id_t *FirstDecl = F->FileSortedDecls + Record[6];
assert(F->FileSortedDecls && "FILE_SORTED_DECLS not encountered yet ?");
FileDeclIDs[FID] =
FileDeclsInfo(F, llvm::ArrayRef(FirstDecl, NumFileDecls));
Expand Down Expand Up @@ -3377,26 +3377,11 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
"duplicate DECL_OFFSET record in AST file");
F.DeclOffsets = (const DeclOffset *)Blob.data();
F.LocalNumDecls = Record[0];
unsigned LocalBaseDeclID = Record[1];
F.BaseDeclID = getTotalNumDecls();

if (F.LocalNumDecls > 0) {
// Introduce the global -> local mapping for declarations within this
// module.
GlobalDeclMap.insert(std::make_pair(
GlobalDeclID(getTotalNumDecls() + NUM_PREDEF_DECL_IDS), &F));

// Introduce the local -> global mapping for declarations within this
// module.
F.DeclRemap.insertOrReplace(
std::make_pair(LocalBaseDeclID, F.BaseDeclID - LocalBaseDeclID));

// Introduce the global -> local mapping for declarations within this
// module.
F.GlobalToLocalDeclIDs[&F] = LocalBaseDeclID;
F.BaseDeclIndex = getTotalNumDecls();

if (F.LocalNumDecls > 0)
DeclsLoaded.resize(DeclsLoaded.size() + F.LocalNumDecls);
}

break;
}

Expand Down Expand Up @@ -3631,7 +3616,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
break;

case FILE_SORTED_DECLS:
F.FileSortedDecls = (const LocalDeclID *)Blob.data();
F.FileSortedDecls = (const unaligned_decl_id_t *)Blob.data();
F.NumFileSortedDecls = Record[0];
break;

Expand Down Expand Up @@ -4058,7 +4043,6 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
RemapBuilder PreprocessedEntityRemap(F.PreprocessedEntityRemap);
RemapBuilder SubmoduleRemap(F.SubmoduleRemap);
RemapBuilder SelectorRemap(F.SelectorRemap);
RemapBuilder DeclRemap(F.DeclRemap);
RemapBuilder TypeRemap(F.TypeRemap);

auto &ImportedModuleVector = F.TransitiveImports;
Expand Down Expand Up @@ -4097,8 +4081,6 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t SelectorIDOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t DeclIDOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint32_t TypeIndexOffset =
endian::readNext<uint32_t, llvm::endianness::little>(Data);

Expand All @@ -4116,11 +4098,7 @@ void ASTReader::ReadModuleOffsetMap(ModuleFile &F) const {
PreprocessedEntityRemap);
mapOffset(SubmoduleIDOffset, OM->BaseSubmoduleID, SubmoduleRemap);
mapOffset(SelectorIDOffset, OM->BaseSelectorID, SelectorRemap);
mapOffset(DeclIDOffset, OM->BaseDeclID, DeclRemap);
mapOffset(TypeIndexOffset, OM->BaseTypeIndex, TypeRemap);

// Global -> local mappings.
F.GlobalToLocalDeclIDs[OM] = DeclIDOffset;
}
}

Expand Down Expand Up @@ -4645,7 +4623,7 @@ ASTReader::ASTReadResult ASTReader::ReadAST(StringRef FileName, ModuleKind Type,
// that we load any additional categories.
if (ContextObj) {
for (unsigned I = 0, N = ObjCClassesLoaded.size(); I != N; ++I) {
loadObjCCategories(GlobalDeclID(ObjCClassesLoaded[I]->getGlobalID()),
loadObjCCategories(ObjCClassesLoaded[I]->getGlobalID(),
ObjCClassesLoaded[I], PreviousGeneration);
}
}
Expand Down Expand Up @@ -7644,50 +7622,59 @@ CXXBaseSpecifier *ASTReader::GetExternalCXXBaseSpecifiers(uint64_t Offset) {

GlobalDeclID ASTReader::getGlobalDeclID(ModuleFile &F,
LocalDeclID LocalID) const {
DeclID ID = LocalID.get();
if (ID < NUM_PREDEF_DECL_IDS)
return GlobalDeclID(ID);
if (LocalID.get() < NUM_PREDEF_DECL_IDS)
return GlobalDeclID(LocalID.get());

unsigned OwningModuleFileIndex = LocalID.getModuleFileIndex();
DeclID ID = LocalID.getLocalDeclIndex();

if (!F.ModuleOffsetMap.empty())
ReadModuleOffsetMap(F);

ContinuousRangeMap<DeclID, int, 2>::iterator I =
F.DeclRemap.find(ID - NUM_PREDEF_DECL_IDS);
assert(I != F.DeclRemap.end() && "Invalid index into decl index remap");
ModuleFile *OwningModuleFile =
OwningModuleFileIndex == 0
? &F
: F.TransitiveImports[OwningModuleFileIndex - 1];

if (OwningModuleFileIndex == 0)
ID -= NUM_PREDEF_DECL_IDS;

return GlobalDeclID(ID + I->second);
uint64_t NewModuleFileIndex = OwningModuleFile->Index + 1;
return GlobalDeclID(ID, NewModuleFileIndex);
}

bool ASTReader::isDeclIDFromModule(GlobalDeclID ID, ModuleFile &M) const {
// Predefined decls aren't from any module.
if (ID.get() < NUM_PREDEF_DECL_IDS)
return false;

return ID.get() - NUM_PREDEF_DECL_IDS >= M.BaseDeclID &&
ID.get() - NUM_PREDEF_DECL_IDS < M.BaseDeclID + M.LocalNumDecls;
unsigned ModuleFileIndex = ID.getModuleFileIndex();
return M.Index == ModuleFileIndex - 1;
}

ModuleFile *ASTReader::getOwningModuleFile(GlobalDeclID ID) const {
// Predefined decls aren't from any module.
if (ID.get() < NUM_PREDEF_DECL_IDS)
return nullptr;

uint64_t ModuleFileIndex = ID.getModuleFileIndex();
assert(ModuleFileIndex && "Untranslated Local Decl?");

return &getModuleManager()[ModuleFileIndex - 1];
}

ModuleFile *ASTReader::getOwningModuleFile(const Decl *D) {
ModuleFile *ASTReader::getOwningModuleFile(const Decl *D) const {
if (!D->isFromASTFile())
return nullptr;
GlobalDeclMapType::const_iterator I =
GlobalDeclMap.find(GlobalDeclID(D->getGlobalID()));
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
return I->second;

return getOwningModuleFile(D->getGlobalID());
}

SourceLocation ASTReader::getSourceLocationForDeclID(GlobalDeclID ID) {
if (ID.get() < NUM_PREDEF_DECL_IDS)
return SourceLocation();

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;

if (Index > DeclsLoaded.size()) {
Error("declaration ID out-of-range for AST file");
return SourceLocation();
}

if (Decl *D = DeclsLoaded[Index])
if (Decl *D = GetExistingDecl(ID))
return D->getLocation();

SourceLocation Loc;
Expand Down Expand Up @@ -7754,8 +7741,19 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
llvm_unreachable("PredefinedDeclIDs unknown enum value");
}

unsigned ASTReader::translateGlobalDeclIDToIndex(GlobalDeclID GlobalID) const {
ModuleFile *OwningModuleFile = getOwningModuleFile(GlobalID);
if (!OwningModuleFile) {
assert(GlobalID.get() < NUM_PREDEF_DECL_IDS && "Untransalted Global ID?");
return GlobalID.get();
}

return OwningModuleFile->BaseDeclIndex + GlobalID.getLocalDeclIndex();
}

Decl *ASTReader::GetExistingDecl(GlobalDeclID ID) {
assert(ContextObj && "reading decl with no AST context");

if (ID.get() < NUM_PREDEF_DECL_IDS) {
Decl *D = getPredefinedDecl(*ContextObj, (PredefinedDeclIDs)ID);
if (D) {
Expand All @@ -7768,7 +7766,7 @@ Decl *ASTReader::GetExistingDecl(GlobalDeclID ID) {
return D;
}

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
unsigned Index = translateGlobalDeclIDToIndex(ID);

if (Index >= DeclsLoaded.size()) {
assert(0 && "declaration ID out-of-range for AST file");
Expand All @@ -7783,7 +7781,7 @@ Decl *ASTReader::GetDecl(GlobalDeclID ID) {
if (ID.get() < NUM_PREDEF_DECL_IDS)
return GetExistingDecl(ID);

unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
unsigned Index = translateGlobalDeclIDToIndex(ID);

if (Index >= DeclsLoaded.size()) {
assert(0 && "declaration ID out-of-range for AST file");
Expand All @@ -7802,20 +7800,31 @@ Decl *ASTReader::GetDecl(GlobalDeclID ID) {

LocalDeclID ASTReader::mapGlobalIDToModuleFileGlobalID(ModuleFile &M,
GlobalDeclID GlobalID) {
DeclID ID = GlobalID.get();
if (ID < NUM_PREDEF_DECL_IDS)
if (GlobalID.get() < NUM_PREDEF_DECL_IDS)
return LocalDeclID(GlobalID.get());

if (!M.ModuleOffsetMap.empty())
ReadModuleOffsetMap(M);

ModuleFile *Owner = getOwningModuleFile(GlobalID);
DeclID ID = GlobalID.getLocalDeclIndex();

if (Owner == &M) {
ID += NUM_PREDEF_DECL_IDS;
return LocalDeclID(ID);
}

GlobalDeclMapType::const_iterator I = GlobalDeclMap.find(GlobalID);
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
ModuleFile *Owner = I->second;
uint64_t OrignalModuleFileIndex = 0;
for (unsigned I = 0; I < M.TransitiveImports.size(); I++)
if (M.TransitiveImports[I] == Owner) {
OrignalModuleFileIndex = I + 1;
break;
}

llvm::DenseMap<ModuleFile *, DeclID>::iterator Pos =
M.GlobalToLocalDeclIDs.find(Owner);
if (Pos == M.GlobalToLocalDeclIDs.end())
if (!OrignalModuleFileIndex)
return LocalDeclID();

return LocalDeclID(ID - Owner->BaseDeclID + Pos->second);
return LocalDeclID(ID, OrignalModuleFileIndex);
}

GlobalDeclID ASTReader::ReadDeclID(ModuleFile &F, const RecordData &Record,
Expand Down Expand Up @@ -7894,32 +7903,34 @@ void ASTReader::FindExternalLexicalDecls(

namespace {

class DeclIDComp {
class UnalignedDeclIDComp {
ASTReader &Reader;
ModuleFile &Mod;

public:
DeclIDComp(ASTReader &Reader, ModuleFile &M) : Reader(Reader), Mod(M) {}
UnalignedDeclIDComp(ASTReader &Reader, ModuleFile &M)
: Reader(Reader), Mod(M) {}

bool operator()(LocalDeclID L, LocalDeclID R) const {
bool operator()(unaligned_decl_id_t L, unaligned_decl_id_t R) const {
SourceLocation LHS = getLocation(L);
SourceLocation RHS = getLocation(R);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

bool operator()(SourceLocation LHS, LocalDeclID R) const {
bool operator()(SourceLocation LHS, unaligned_decl_id_t R) const {
SourceLocation RHS = getLocation(R);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

bool operator()(LocalDeclID L, SourceLocation RHS) const {
bool operator()(unaligned_decl_id_t L, SourceLocation RHS) const {
SourceLocation LHS = getLocation(L);
return Reader.getSourceManager().isBeforeInTranslationUnit(LHS, RHS);
}

SourceLocation getLocation(LocalDeclID ID) const {
SourceLocation getLocation(unaligned_decl_id_t ID) const {
return Reader.getSourceManager().getFileLoc(
Reader.getSourceLocationForDeclID(Reader.getGlobalDeclID(Mod, ID)));
Reader.getSourceLocationForDeclID(
Reader.getGlobalDeclID(Mod, (LocalDeclID)ID)));
}
};

Expand All @@ -7942,8 +7953,8 @@ void ASTReader::FindFileRegionDecls(FileID File,
BeginLoc = SM.getLocForStartOfFile(File).getLocWithOffset(Offset);
SourceLocation EndLoc = BeginLoc.getLocWithOffset(Length);

DeclIDComp DIDComp(*this, *DInfo.Mod);
ArrayRef<LocalDeclID>::iterator BeginIt =
UnalignedDeclIDComp DIDComp(*this, *DInfo.Mod);
ArrayRef<unaligned_decl_id_t>::iterator BeginIt =
llvm::lower_bound(DInfo.Decls, BeginLoc, DIDComp);
if (BeginIt != DInfo.Decls.begin())
--BeginIt;
Expand All @@ -7952,17 +7963,18 @@ void ASTReader::FindFileRegionDecls(FileID File,
// to backtrack until we find it otherwise we will fail to report that the
// region overlaps with an objc container.
while (BeginIt != DInfo.Decls.begin() &&
GetDecl(getGlobalDeclID(*DInfo.Mod, *BeginIt))
GetDecl(getGlobalDeclID(*DInfo.Mod, (LocalDeclID)(*BeginIt)))
->isTopLevelDeclInObjCContainer())
--BeginIt;

ArrayRef<LocalDeclID>::iterator EndIt =
ArrayRef<unaligned_decl_id_t>::iterator EndIt =
llvm::upper_bound(DInfo.Decls, EndLoc, DIDComp);
if (EndIt != DInfo.Decls.end())
++EndIt;

for (ArrayRef<LocalDeclID>::iterator DIt = BeginIt; DIt != EndIt; ++DIt)
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, *DIt)));
for (ArrayRef<unaligned_decl_id_t>::iterator DIt = BeginIt; DIt != EndIt;
++DIt)
Decls.push_back(GetDecl(getGlobalDeclID(*DInfo.Mod, (LocalDeclID)(*DIt))));
}

bool
Expand Down Expand Up @@ -8169,7 +8181,6 @@ LLVM_DUMP_METHOD void ASTReader::dump() {
dumpModuleIDMap("Global bit offset map", GlobalBitOffsetsMap);
dumpModuleIDMap("Global source location entry map", GlobalSLocEntryMap);
dumpModuleIDMap("Global type map", GlobalTypeMap);
dumpModuleIDMap("Global declaration map", GlobalDeclMap);
dumpModuleIDMap("Global identifier map", GlobalIdentifierMap);
dumpModuleIDMap("Global macro map", GlobalMacroMap);
dumpModuleIDMap("Global submodule map", GlobalSubmoduleMap);
Expand Down Expand Up @@ -9185,7 +9196,7 @@ void ASTRecordReader::readUnresolvedSet(LazyASTUnresolvedSet &Set) {
while (NumDecls--) {
GlobalDeclID ID = readDeclID();
AccessSpecifier AS = (AccessSpecifier) readInt();
Set.addLazyDecl(getContext(), ID.get(), AS);
Set.addLazyDecl(getContext(), ID, AS);
}
}

Expand Down
16 changes: 7 additions & 9 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2924,7 +2924,7 @@ void ASTDeclReader::mergeTemplatePattern(RedeclarableTemplateDecl *D,
auto *ExistingPattern = Existing->getTemplatedDecl();
RedeclarableResult Result(
/*MergeWith*/ ExistingPattern,
GlobalDeclID(DPattern->getCanonicalDecl()->getGlobalID()), IsKeyDecl);
DPattern->getCanonicalDecl()->getGlobalID(), IsKeyDecl);

if (auto *DClass = dyn_cast<CXXRecordDecl>(DPattern)) {
// Merge with any existing definition.
Expand Down Expand Up @@ -3245,11 +3245,10 @@ bool ASTReader::isConsumerInterestedIn(Decl *D) {
/// Get the correct cursor and offset for loading a declaration.
ASTReader::RecordLocation ASTReader::DeclCursorForID(GlobalDeclID ID,
SourceLocation &Loc) {
GlobalDeclMapType::iterator I = GlobalDeclMap.find(ID);
assert(I != GlobalDeclMap.end() && "Corrupted global declaration map");
ModuleFile *M = I->second;
const DeclOffset &DOffs =
M->DeclOffsets[ID.get() - M->BaseDeclID - NUM_PREDEF_DECL_IDS];
ModuleFile *M = getOwningModuleFile(ID);
assert(M);
unsigned LocalDeclIndex = ID.getLocalDeclIndex();
const DeclOffset &DOffs = M->DeclOffsets[LocalDeclIndex];
Loc = ReadSourceLocation(*M, DOffs.getRawLoc());
return RecordLocation(M, DOffs.getBitOffset(M->DeclsBlockStartOffset));
}
Expand Down Expand Up @@ -3792,7 +3791,6 @@ void ASTReader::markIncompleteDeclChain(Decl *D) {

/// Read the declaration at the given offset from the AST file.
Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
unsigned Index = ID.get() - NUM_PREDEF_DECL_IDS;
SourceLocation DeclLoc;
RecordLocation Loc = DeclCursorForID(ID, DeclLoc);
llvm::BitstreamCursor &DeclsCursor = Loc.F->DeclsCursor;
Expand Down Expand Up @@ -4123,7 +4121,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
}

assert(D && "Unknown declaration reading AST file");
LoadedDecl(Index, D);
LoadedDecl(translateGlobalDeclIDToIndex(ID), D);
// Set the DeclContext before doing any deserialization, to make sure internal
// calls to Decl::getASTContext() by Decl's methods will find the
// TranslationUnitDecl without crashing.
Expand Down Expand Up @@ -4449,7 +4447,7 @@ namespace {
M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap,
Compare);
if (Result == M.ObjCCategoriesMap + M.LocalNumObjCCategoriesInMap ||
Result->DefinitionID != LocalID) {
Result->getDefinitionID() != LocalID) {
// We didn't find anything. If the class definition is in this module
// file, then the module files it depends on cannot have any categories,
// so suppress further lookup.
Expand Down
7 changes: 1 addition & 6 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3357,12 +3357,10 @@ void ASTWriter::WriteTypeDeclOffsets() {
Abbrev = std::make_shared<BitCodeAbbrev>();
Abbrev->Add(BitCodeAbbrevOp(DECL_OFFSET));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // # of declarations
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // base decl ID
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // declarations block
unsigned DeclOffsetAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
{
RecordData::value_type Record[] = {DECL_OFFSET, DeclOffsets.size(),
FirstDeclID.get() - NUM_PREDEF_DECL_IDS};
RecordData::value_type Record[] = {DECL_OFFSET, DeclOffsets.size()};
Stream.EmitRecordWithBlob(DeclOffsetAbbrev, Record, bytes(DeclOffsets));
}
}
Expand Down Expand Up @@ -5423,7 +5421,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
M.NumPreprocessedEntities);
writeBaseIDOrNone(M.BaseSubmoduleID, M.LocalNumSubmodules);
writeBaseIDOrNone(M.BaseSelectorID, M.LocalNumSelectors);
writeBaseIDOrNone(M.BaseDeclID, M.LocalNumDecls);
writeBaseIDOrNone(M.BaseTypeIndex, M.LocalNumTypes);
}
}
Expand Down Expand Up @@ -6618,13 +6615,11 @@ void ASTWriter::ReaderInitialized(ASTReader *Reader) {

// Note, this will get called multiple times, once one the reader starts up
// and again each time it's done reading a PCH or module.
FirstDeclID = LocalDeclID(NUM_PREDEF_DECL_IDS + Chain->getTotalNumDecls());
FirstTypeID = NUM_PREDEF_TYPE_IDS + Chain->getTotalNumTypes();
FirstIdentID = NUM_PREDEF_IDENT_IDS + Chain->getTotalNumIdentifiers();
FirstMacroID = NUM_PREDEF_MACRO_IDS + Chain->getTotalNumMacros();
FirstSubmoduleID = NUM_PREDEF_SUBMODULE_IDS + Chain->getTotalNumSubmodules();
FirstSelectorID = NUM_PREDEF_SELECTOR_IDS + Chain->getTotalNumSelectors();
NextDeclID = FirstDeclID;
NextTypeID = FirstTypeID;
NextIdentID = FirstIdentID;
NextMacroID = FirstMacroID;
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Serialization/ModuleFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ LLVM_DUMP_METHOD void ModuleFile::dump() {
<< " Number of types: " << LocalNumTypes << '\n';
dumpLocalRemap("Type index local -> global map", TypeRemap);

llvm::errs() << " Base decl ID: " << BaseDeclID << '\n'
llvm::errs() << " Base decl index: " << BaseDeclIndex << '\n'
<< " Number of decls: " << LocalNumDecls << '\n';
dumpLocalRemap("Decl ID local -> global map", DeclRemap);
}
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ add_clang_library(clangStaticAnalyzerCheckers
NoReturnFunctionChecker.cpp
NonNullParamChecker.cpp
NonnullGlobalConstantsChecker.cpp
NoOwnershipChangeVisitor.cpp
NullabilityChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
Expand Down
147 changes: 18 additions & 129 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#include "AllocationState.h"
#include "InterCheckerAPI.h"
#include "NoOwnershipChangeVisitor.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
Expand Down Expand Up @@ -79,13 +80,11 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <functional>
#include <optional>
#include <utility>
Expand Down Expand Up @@ -414,7 +413,7 @@ class MallocChecker
bool isFreeingCall(const CallEvent &Call) const;
static bool isFreeingOwnershipAttrCall(const FunctionDecl *Func);

friend class NoOwnershipChangeVisitor;
friend class NoMemOwnershipChangeVisitor;

CallDescriptionMap<CheckFn> AllocatingMemFnMap{
{{CDM::CLibrary, {"alloca"}, 1}, &MallocChecker::checkAlloca},
Expand Down Expand Up @@ -765,61 +764,8 @@ class MallocChecker
//===----------------------------------------------------------------------===//

namespace {
class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
// The symbol whose (lack of) ownership change we are interested in.
SymbolRef Sym;
const MallocChecker &Checker;
using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;

// Collect which entities point to the allocated memory, and could be
// responsible for deallocating it.
class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
SymbolRef Sym;
OwnerSet &Owners;

public:
OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
: Sym(Sym), Owners(Owners) {}

bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
SVal Val) override {
if (Val.getAsSymbol() == Sym)
Owners.insert(Region);
return true;
}

LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
out << "Owners: {\n";
for (const MemRegion *Owner : Owners) {
out << " ";
Owner->dumpToStream(out);
out << ",\n";
}
out << "}\n";
}
};

class NoMemOwnershipChangeVisitor final : public NoOwnershipChangeVisitor {
protected:
OwnerSet getOwnersAtNode(const ExplodedNode *N) {
OwnerSet Ret;

ProgramStateRef State = N->getState();
OwnershipBindingsHandler Handler{Sym, Ret};
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Handler);
return Ret;
}

LLVM_DUMP_METHOD static std::string
getFunctionName(const ExplodedNode *CallEnterN) {
if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
if (const FunctionDecl *FD = CE->getDirectCallee())
return FD->getQualifiedNameAsString();
return "";
}

/// Syntactically checks whether the callee is a deallocating function. Since
/// we have no path-sensitive information on this call (we would need a
/// CallEvent instead of a CallExpr for that), its possible that a
Expand All @@ -828,8 +774,9 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
/// See namespace `memory_passed_to_fn_call_free_through_fn_ptr` in
/// clang/test/Analysis/NewDeleteLeaks.cpp.
bool isFreeingCallAsWritten(const CallExpr &Call) const {
if (Checker.FreeingMemFnMap.lookupAsWritten(Call) ||
Checker.ReallocatingMemFnMap.lookupAsWritten(Call))
const auto *MallocChk = static_cast<const MallocChecker *>(&Checker);
if (MallocChk->FreeingMemFnMap.lookupAsWritten(Call) ||
MallocChk->ReallocatingMemFnMap.lookupAsWritten(Call))
return true;

if (const auto *Func =
Expand All @@ -839,23 +786,21 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
return false;
}

bool hasResourceStateChanged(ProgramStateRef CallEnterState,
ProgramStateRef CallExitEndState) final {
return CallEnterState->get<RegionState>(Sym) !=
CallExitEndState->get<RegionState>(Sym);
}

/// Heuristically guess whether the callee intended to free memory. This is
/// done syntactically, because we are trying to argue about alternative
/// paths of execution, and as a consequence we don't have path-sensitive
/// information.
bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx) {
bool doesFnIntendToHandleOwnership(const Decl *Callee,
ASTContext &ACtx) final {
using namespace clang::ast_matchers;
const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);

// Given that the stack frame was entered, the body should always be
// theoretically obtainable. In case of body farms, the synthesized body
// is not attached to declaration, thus triggering the '!FD->hasBody()'
// branch. That said, would a synthesized body ever intend to handle
// ownership? As of today they don't. And if they did, how would we
// put notes inside it, given that it doesn't match any source locations?
if (!FD || !FD->hasBody())
return false;

auto Matches = match(findAll(stmt(anyOf(cxxDeleteExpr().bind("delete"),
callExpr().bind("call")))),
*FD->getBody(), ACtx);
Expand All @@ -873,30 +818,7 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
return false;
}

bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) override {
if (!doesFnIntendToHandleOwnership(
CallExitEndN->getFirstPred()->getLocationContext()->getDecl(),
CallExitEndN->getState()->getAnalysisManager().getASTContext()))
return true;

if (CallEnterN->getState()->get<RegionState>(Sym) !=
CallExitEndN->getState()->get<RegionState>(Sym))
return true;

OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);

// Owners in the current set may be purged from the analyzer later on.
// If a variable is dead (is not referenced directly or indirectly after
// some point), it will be removed from the Store before the end of its
// actual lifetime.
// This means that if the ownership status didn't change, CurrOwners
// must be a superset of, but not necessarily equal to ExitOwners.
return !llvm::set_is_subset(ExitOwners, CurrOwners);
}

static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) {
PathDiagnosticPieceRef emitNote(const ExplodedNode *N) final {
PathDiagnosticLocation L = PathDiagnosticLocation::create(
N->getLocation(),
N->getState()->getStateManager().getContext().getSourceManager());
Expand All @@ -905,42 +827,9 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
"later deallocation");
}

PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) override {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) override {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) override {
// TODO: Factor the logic of "what constitutes as an entity being passed
// into a function call" out by reusing the code in
// NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
// the printing technology in UninitializedObject's FieldChainInfo.
ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
SVal V = Call.getArgSVal(I);
if (V.getAsSymbol() == Sym)
return emitNote(N);
}
return nullptr;
}

public:
NoOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)
: NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym),
Checker(*Checker) {}
NoMemOwnershipChangeVisitor(SymbolRef Sym, const MallocChecker *Checker)
: NoOwnershipChangeVisitor(Sym, Checker) {}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
Expand Down Expand Up @@ -2949,7 +2838,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
R->markInteresting(Sym);
R->addVisitor<MallocBugVisitor>(Sym, true);
if (ShouldRegisterNoOwnershipChangeVisitor)
R->addVisitor<NoOwnershipChangeVisitor>(Sym, this);
R->addVisitor<NoMemOwnershipChangeVisitor>(Sym, this);
C.emitReport(std::move(R));
}

Expand Down
116 changes: 116 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/NoOwnershipChangeVisitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//===--------------------------------------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "NoOwnershipChangeVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/SetOperations.h"

using namespace clang;
using namespace ento;
using OwnerSet = NoOwnershipChangeVisitor::OwnerSet;

// Collect which entities point to the allocated memory, and could be
// responsible for deallocating it.
class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
SymbolRef Sym;
OwnerSet &Owners;

public:
OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
: Sym(Sym), Owners(Owners) {}

bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
SVal Val) override {
if (Val.getAsSymbol() == Sym)
Owners.insert(Region);
return true;
}

LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
out << "Owners: {\n";
for (const MemRegion *Owner : Owners) {
out << " ";
Owner->dumpToStream(out);
out << ",\n";
}
out << "}\n";
}
};

OwnerSet NoOwnershipChangeVisitor::getOwnersAtNode(const ExplodedNode *N) {
OwnerSet Ret;

ProgramStateRef State = N->getState();
OwnershipBindingsHandler Handler{Sym, Ret};
State->getStateManager().getStoreManager().iterBindings(State->getStore(),
Handler);
return Ret;
}

LLVM_DUMP_METHOD std::string
NoOwnershipChangeVisitor::getFunctionName(const ExplodedNode *CallEnterN) {
if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
if (const FunctionDecl *FD = CE->getDirectCallee())
return FD->getQualifiedNameAsString();
return "";
}

bool NoOwnershipChangeVisitor::wasModifiedInFunction(
const ExplodedNode *CallEnterN, const ExplodedNode *CallExitEndN) {
const Decl *Callee =
CallExitEndN->getFirstPred()->getLocationContext()->getDecl();
const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);

// Given that the stack frame was entered, the body should always be
// theoretically obtainable. In case of body farms, the synthesized body
// is not attached to declaration, thus triggering the '!FD->hasBody()'
// branch. That said, would a synthesized body ever intend to handle
// ownership? As of today they don't. And if they did, how would we
// put notes inside it, given that it doesn't match any source locations?
if (!FD || !FD->hasBody())
return false;
if (!doesFnIntendToHandleOwnership(
Callee,
CallExitEndN->getState()->getAnalysisManager().getASTContext()))
return true;

if (hasResourceStateChanged(CallEnterN->getState(), CallExitEndN->getState()))
return true;

OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);

// Owners in the current set may be purged from the analyzer later on.
// If a variable is dead (is not referenced directly or indirectly after
// some point), it will be removed from the Store before the end of its
// actual lifetime.
// This means that if the ownership status didn't change, CurrOwners
// must be a superset of, but not necessarily equal to ExitOwners.
return !llvm::set_is_subset(ExitOwners, CurrOwners);
}

PathDiagnosticPieceRef NoOwnershipChangeVisitor::maybeEmitNoteForParameters(
PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
// TODO: Factor the logic of "what constitutes as an entity being passed
// into a function call" out by reusing the code in
// NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
// the printing technology in UninitializedObject's FieldChainInfo.
ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
SVal V = Call.getArgSVal(I);
if (V.getAsSymbol() == Sym)
return emitNote(N);
}
return nullptr;
}
77 changes: 77 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/NoOwnershipChangeVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===--------------------------------------------------------------*- C++ -*--//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"

namespace clang {
namespace ento {

class NoOwnershipChangeVisitor : public NoStateChangeFuncVisitor {
protected:
// The symbol whose (lack of) ownership change we are interested in.
SymbolRef Sym;
const CheckerBase &Checker;

LLVM_DUMP_METHOD static std::string
getFunctionName(const ExplodedNode *CallEnterN);

/// Heuristically guess whether the callee intended to free the resource. This
/// is done syntactically, because we are trying to argue about alternative
/// paths of execution, and as a consequence we don't have path-sensitive
/// information.
virtual bool doesFnIntendToHandleOwnership(const Decl *Callee,
ASTContext &ACtx) = 0;

virtual bool hasResourceStateChanged(ProgramStateRef CallEnterState,
ProgramStateRef CallExitEndState) = 0;

bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
const ExplodedNode *CallExitEndN) final;

virtual PathDiagnosticPieceRef emitNote(const ExplodedNode *N) = 0;

PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) final {
// TODO: Implement.
return nullptr;
}

PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) final {
// TODO: Implement.
return nullptr;
}

// Set this to final, effectively dispatch to emitNote.
PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) final;

public:
using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;

private:
OwnerSet getOwnersAtNode(const ExplodedNode *N);

public:
NoOwnershipChangeVisitor(SymbolRef Sym, const CheckerBase *Checker)
: NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), Sym(Sym),
Checker(*Checker) {}

void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
ID.AddPointer(&Tag);
ID.AddPointer(Sym);
}
};
} // namespace ento
} // namespace clang
56 changes: 22 additions & 34 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1971,45 +1971,33 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);

bool HasRewrittenInit = false;
const Expr *ArgE = nullptr;
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) {
const Expr *ArgE;
if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
ArgE = DefE->getExpr();
HasRewrittenInit = DefE->hasRewrittenInit();
} else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) {
else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
ArgE = DefE->getExpr();
HasRewrittenInit = DefE->hasRewrittenInit();
} else
else
llvm_unreachable("unknown constant wrapper kind");

if (HasRewrittenInit) {
for (auto *N : PreVisit) {
ProgramStateRef state = N->getState();
const LocationContext *LCtx = N->getLocationContext();
state = state->BindExpr(S, LCtx, state->getSVal(ArgE, LCtx));
Bldr2.generateNode(S, N, state);
}
} else {
// If it's not rewritten, the contents of these expressions are not
// actually part of the current function, so we fall back to constant
// evaluation.
bool IsTemporary = false;
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
ArgE = MTE->getSubExpr();
IsTemporary = true;
}

std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
const LocationContext *LCtx = Pred->getLocationContext();
for (auto *I : PreVisit) {
ProgramStateRef State = I->getState();
State = State->BindExpr(S, LCtx, ConstantVal.value_or(UnknownVal()));
if (IsTemporary)
State = createTemporaryRegionIfNeeded(State, LCtx, cast<Expr>(S),
cast<Expr>(S));
bool IsTemporary = false;
if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
ArgE = MTE->getSubExpr();
IsTemporary = true;
}

Bldr2.generateNode(S, I, State);
}
std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
if (!ConstantVal)
ConstantVal = UnknownVal();

const LocationContext *LCtx = Pred->getLocationContext();
for (const auto I : PreVisit) {
ProgramStateRef State = I->getState();
State = State->BindExpr(S, LCtx, *ConstantVal);
if (IsTemporary)
State = createTemporaryRegionIfNeeded(State, LCtx,
cast<Expr>(S),
cast<Expr>(S));
Bldr2.generateNode(S, I, State);
}

getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ DependencyScanningWorkerFilesystem::readFile(StringRef Filename) {
}

bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
EntryRef Ref, const LangOptions &LangOpts) {
EntryRef Ref) {
auto &Entry = Ref.Entry;

if (Entry.isError() || Entry.isDirectory())
Expand All @@ -66,7 +66,7 @@ bool DependencyScanningWorkerFilesystem::ensureDirectiveTokensArePopulated(
// dependencies.
if (scanSourceForDependencyDirectives(Contents->Original->getBuffer(),
Contents->DepDirectiveTokens,
Directives, LangOpts)) {
Directives)) {
Contents->DepDirectiveTokens.clear();
// FIXME: Propagate the diagnostic if desired by the client.
Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,11 @@ class DependencyScanningAction : public tooling::ToolAction {
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS)
ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
[LocalDepFS = DepFS,
&LangOpts = ScanInstance.getLangOpts()](FileEntryRef File)
[LocalDepFS = DepFS](FileEntryRef File)
-> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry, LangOpts))
if (LocalDepFS->ensureDirectiveTokensArePopulated(*Entry))
return Entry->getDirectiveTokens();
return std::nullopt;
};
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Tooling/Syntax/Tokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,13 @@ llvm::ArrayRef<syntax::Token> TokenBuffer::spelledTokens(FileID FID) const {
return It->second.SpelledTokens;
}

const syntax::Token *TokenBuffer::spelledTokenAt(SourceLocation Loc) const {
const syntax::Token *
TokenBuffer::spelledTokenContaining(SourceLocation Loc) const {
assert(Loc.isFileID());
const auto *Tok = llvm::partition_point(
spelledTokens(SourceMgr->getFileID(Loc)),
[&](const syntax::Token &Tok) { return Tok.location() < Loc; });
if (!Tok || Tok->location() != Loc)
[&](const syntax::Token &Tok) { return Tok.endLocation() <= Loc; });
if (!Tok || Loc < Tok->location())
return nullptr;
return Tok;
}
Expand Down
14 changes: 14 additions & 0 deletions clang/test/AST/Interp/arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,3 +609,17 @@ namespace ArrayMemberAccess {
bool cond = a->x;
}
}

namespace OnePastEndSub {
struct A {};
constexpr A a[3][3];
constexpr int diff2 = &a[1][3] - &a[1][0]; /// Used to crash.
}

static int same_entity_2[3];
constexpr int *get2() {
// This is a redeclaration of the same entity, even though it doesn't
// inherit the type of the prior declaration.
extern int same_entity_2[];
return same_entity_2;
}
40 changes: 40 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,3 +774,43 @@ void overflowInSwitchCase(int n) {
break;
}
}

namespace APValues {
int g;
struct A { union { int n, m; }; int *p; int A::*q; char buffer[32]; };
template<A a> constexpr const A &get = a;
constexpr const A &v = get<A{}>;
constexpr const A &w = get<A{1, &g, &A::n, "hello"}>;
}

namespace self_referencing {
struct S {
S* ptr = nullptr;
constexpr S(int i) : ptr(this) {
if (this == ptr && i)
ptr = nullptr;
}
constexpr ~S() {}
};

void test() {
S s(1);
}
}

namespace GH64949 {
struct f {
int g; // both-note {{subobject declared here}}
constexpr ~f() {}
};

class h {
public:
consteval h(char *) {}
f i;
};

void test() { h{nullptr}; } // both-error {{call to consteval function 'GH64949::h::h' is not a constant expression}} \
// both-note {{subobject 'g' is not initialized}} \
// both-warning {{expression result unused}}
}
22 changes: 22 additions & 0 deletions clang/test/AST/Interp/cxx23.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,25 @@ namespace ExplicitLambdaThis {
};
static_assert(f());
}

namespace std {
struct strong_ordering {
int n;
constexpr operator int() const { return n; }
static const strong_ordering less, equal, greater;
};
constexpr strong_ordering strong_ordering::less = {-1};
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
}

namespace UndefinedThreeWay {
struct A {
friend constexpr std::strong_ordering operator<=>(const A&, const A&) = default; // all-note {{declared here}}
};

constexpr std::strong_ordering operator<=>(const A&, const A&) noexcept;
constexpr std::strong_ordering (*test_a_threeway)(const A&, const A&) = &operator<=>;
static_assert(!(*test_a_threeway)(A(), A())); // all-error {{static assertion expression is not an integral constant expression}} \
// all-note {{undefined function 'operator<=>' cannot be used in a constant expression}}
}
4 changes: 2 additions & 2 deletions clang/test/AST/Interp/eval-order.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ namespace EvalOrder {
// Rules 1 and 2 have no effect ('b' is not an expression).

// Rule 3: a->*b
// SEQ(A(ud).*B(&UserDefined::n)); FIXME
// SEQ(A(&ud)->*B(&UserDefined::n)); FIXME
SEQ(A(ud).*B(&UserDefined::n));
SEQ(A(&ud)->*B(&UserDefined::n));

// Rule 4: a(b1, b2, b3)
SEQ(A(f)(B(1), B(2), B(3))); // expected-error {{not an integral constant expression}} FIXME \
Expand Down
16 changes: 16 additions & 0 deletions clang/test/AST/Interp/lambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,19 @@ namespace CaptureDefaults {
};
static_assert(f2() == 3, "");
}

constexpr auto t4 = ([x=42]() consteval { return x; }());
static_assert(t4 == 42, "");

namespace InvalidCapture {

int &f(int *p);
char &f(...);
void g() {
int n = -1; // both-note {{declared here}}
[=] {
int arr[n]; // both-warning {{variable length arrays in C++ are a Clang extension}} \
both-note {{read of non-const variable 'n' is not allowed in a constant expression}}
} ();
}
}
9 changes: 7 additions & 2 deletions clang/test/AST/Interp/literals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ namespace ScalarTypes {
First = 0,
};
static_assert(getScalar<E>() == First, "");
/// FIXME: Member pointers.

struct S {
int v;
};
constexpr int S::* MemberPtr = &S::v;
static_assert(getScalar<decltype(MemberPtr)>() == nullptr, "");

#if __cplusplus >= 201402L
constexpr void Void(int n) {
Expand Down Expand Up @@ -1204,7 +1209,7 @@ namespace incdecbool {
constexpr int externvar1() { // both-error {{never produces a constant expression}}
extern char arr[]; // ref-note {{declared here}}
return arr[0]; // ref-note {{read of non-constexpr variable 'arr'}} \
// expected-note {{array-to-pointer decay of array member without known bound is not supported}}
// expected-note {{indexing of array without known bound}}
}
#endif

Expand Down
197 changes: 197 additions & 0 deletions clang/test/AST/Interp/memberpointers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// RUN: %clang_cc1 -std=c++14 -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++14 -verify=ref,both %s

namespace MemberPointers {
struct A {
constexpr A(int n) : n(n) {}
int n;
constexpr int f() const { return n + 3; }
};

constexpr A a(7);
static_assert(A(5).*&A::n == 5, "");
static_assert((&a)->*&A::n == 7, "");
static_assert((A(8).*&A::f)() == 11, "");
static_assert(((&a)->*&A::f)() == 10, "");

struct B : A {
constexpr B(int n, int m) : A(n), m(m) {}
int m;
constexpr int g() const { return n + m + 1; }
};
constexpr B b(9, 13);
static_assert(B(4, 11).*&A::n == 4, "");
static_assert(B(4, 11).*&B::m == 11, "");
static_assert(B(4, 11).m == 11, "");
static_assert(B(4, 11).*(int(A::*))&B::m == 11, "");
static_assert(B(4, 11).*&B::m == 11, "");
static_assert((&b)->*&A::n == 9, "");
static_assert((&b)->*&B::m == 13, "");
static_assert((&b)->*(int(A::*))&B::m == 13, "");
static_assert((B(4, 11).*&A::f)() == 7, "");
static_assert((B(4, 11).*&B::g)() == 16, "");

static_assert((B(4, 11).*(int(A::*)() const)&B::g)() == 16, "");

static_assert(((&b)->*&A::f)() == 12, "");
static_assert(((&b)->*&B::g)() == 23, "");
static_assert(((&b)->*(int(A::*)()const)&B::g)() == 23, "");


struct S {
constexpr S(int m, int n, int (S::*pf)() const, int S::*pn) :
m(m), n(n), pf(pf), pn(pn) {}
constexpr S() : m(), n(), pf(&S::f), pn(&S::n) {}

constexpr int f() const { return this->*pn; }
virtual int g() const;

int m, n;
int (S::*pf)() const;
int S::*pn;
};

constexpr int S::*pm = &S::m;
constexpr int S::*pn = &S::n;

constexpr int (S::*pf)() const = &S::f;
constexpr int (S::*pg)() const = &S::g;

constexpr S s(2, 5, &S::f, &S::m);

static_assert((s.*&S::f)() == 2, "");
static_assert((s.*s.pf)() == 2, "");

static_assert(pf == &S::f, "");

static_assert(pf == s.*&S::pf, "");

static_assert(pm == &S::m, "");
static_assert(pm != pn, "");
static_assert(s.pn != pn, "");
static_assert(s.pn == pm, "");
static_assert(pg != nullptr, "");
static_assert(pf != nullptr, "");
static_assert((int S::*)nullptr == nullptr, "");
static_assert(pg == pg, ""); // both-error {{constant expression}} \
// both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}
static_assert(pf != pg, ""); // both-error {{constant expression}} \
// both-note {{comparison of pointer to virtual member function 'g' has unspecified value}}

template<int n> struct T : T<n-1> { const int X = n;};
template<> struct T<0> { int n; char k;};
template<> struct T<30> : T<29> { int m; };

T<17> t17;
T<30> t30;

constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m;
constexpr int (T<10>::*deepn) = &T<0>::n;
constexpr char (T<10>::*deepk) = &T<0>::k;

static_assert(&(t17.*deepn) == &t17.n, "");
static_assert(&(t17.*deepk) == &t17.k, "");
static_assert(deepn == &T<2>::n, "");

constexpr int *pgood = &(t30.*deepm);
constexpr int *pbad = &(t17.*deepm); // both-error {{constant expression}}
static_assert(&(t30.*deepm) == &t30.m, "");

static_assert(deepm == &T<50>::m, "");
static_assert(deepm != deepn, "");

constexpr T<5> *p17_5 = &t17;
constexpr T<13> *p17_13 = (T<13>*)p17_5;
constexpr T<23> *p17_23 = (T<23>*)p17_13; // both-error {{constant expression}} \
// both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<23>'}}
constexpr T<18> *p17_18 = (T<18>*)p17_13; // both-error {{constant expression}} \
// both-note {{cannot cast object of dynamic type 'T<17>' to type 'T<18>'}}
static_assert(&(p17_5->*(int(T<0>::*))deepn) == &t17.n, "");
static_assert(&(p17_5->*(int(T<0>::*))deepn), "");


static_assert(&(p17_13->*deepn) == &t17.n, "");
constexpr int *pbad2 = &(p17_13->*(int(T<9>::*))deepm); // both-error {{constant expression}}

constexpr T<5> *p30_5 = &t30;
constexpr T<23> *p30_23 = (T<23>*)p30_5;
constexpr T<13> *p30_13 = p30_23;
static_assert(&(p30_13->*deepn) == &t30.n, "");
static_assert(&(p30_23->*deepn) == &t30.n, "");
static_assert(&(p30_5->*(int(T<3>::*))deepn) == &t30.n, "");

static_assert(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, "");
static_assert(&(((T<17>*)p30_13)->*deepm) == &t30.m, "");
static_assert(&(p30_23->*deepm) == &t30.m, "");


/// Added tests not from constant-expression-cxx11.cpp
static_assert(pm, "");
static_assert(!((int S::*)nullptr), "");
constexpr int S::*pk = nullptr;
static_assert(!pk, "");
}

namespace test3 {
struct nsCSSRect {
};
static int nsCSSRect::* sides;
nsCSSRect dimenX;
void ParseBoxCornerRadii(int y) {
switch (y) {
}
int& x = dimenX.*sides;
}
}

void foo() {
class X;
void (X::*d) ();
d = nullptr; /// This calls in the constant interpreter.
}

namespace {
struct A { int n; };
struct B { int n; };
struct C : A, B {};
struct D { double d; C c; };
const int &&u = static_cast<B&&>(0, ((D&&)D{}).*&D::c).n; // both-warning {{left operand of comma operator has no effect}}
}

/// From SemaTemplate/instantiate-member-pointers.cpp
namespace {
struct Y {
int x;
};

template<typename T, typename Class, T Class::*Ptr>
struct X3 {
X3<T, Class, Ptr> &operator=(const T& value) {
return *this;
}
};

typedef int Y::*IntMember;
template<IntMember Member>
struct X4 {
X3<int, Y, Member> member;
int &getMember(Y& y) { return y.*Member; }
};

int &get_X4(X4<&Y::x> x4, Y& y) {
return x4.getMember(y);
}
}

/// From test/CXX/basic/basic.def.odr/p2.cpp
namespace {
void use(int);
struct S { int x; int f() const; };
constexpr S *ps = nullptr;
S *const &psr = ps;

void test() {
use(ps->*&S::x);
use(psr->*&S::x);
}
}
6 changes: 3 additions & 3 deletions clang/test/AST/ast-dump-default-init-json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,10 @@ void test() {
// CHECK-NEXT: "valueCategory": "lvalue",
// CHECK-NEXT: "extendingDecl": {
// CHECK-NEXT: "id": "0x{{.*}}",
// CHECK-NEXT: "kind": "VarDecl",
// CHECK-NEXT: "name": "b",
// CHECK-NEXT: "kind": "FieldDecl",
// CHECK-NEXT: "name": "a",
// CHECK-NEXT: "type": {
// CHECK-NEXT: "qualType": "B"
// CHECK-NEXT: "qualType": "const A &"
// CHECK-NEXT: }
// CHECK-NEXT: },
// CHECK-NEXT: "storageDuration": "automatic",
Expand Down
2 changes: 1 addition & 1 deletion clang/test/AST/ast-dump-default-init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ void test() {
}
// CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init
// CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &'
// CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp>
// CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp>
// CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A'
Expand Down
12 changes: 6 additions & 6 deletions clang/test/Analysis/cxx-uninitialized-object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,27 +1114,27 @@ void fCXX11MemberInitTest1() {
CXX11MemberInitTest1();
}

#ifdef PEDANTIC
struct CXX11MemberInitTest2 {
struct RecordType {
int a; // expected-note {{uninitialized field 'this->a'}}
int b; // expected-note {{uninitialized field 'this->b'}}
// TODO: we'd expect the note: {{uninitialized field 'this->rec.a'}}
int a; // no-note
// TODO: we'd expect the note: {{uninitialized field 'this->rec.b'}}
int b; // no-note

RecordType(int) {}
};

RecordType rec = RecordType(int()); // expected-warning {{2 uninitialized fields}}
RecordType rec = RecordType(int());
int dontGetFilteredByNonPedanticMode = 0;

CXX11MemberInitTest2() {}
};

void fCXX11MemberInitTest2() {
// TODO: we'd expect the warning: {{2 uninitializeds field}}
CXX11MemberInitTest2(); // no-warning
}

#endif // PEDANTIC

//===----------------------------------------------------------------------===//
// "Esoteric" primitive type tests.
//===----------------------------------------------------------------------===//
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Analysis/lifetime-extended-regions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ void aggregateWithReferences() {
clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
// The lifetime lifetime of object bound to reference members of aggregates,
// that are created from default member initializer was extended.
RefAggregate defaultInitExtended{i};
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}

// clang does not currently implement extending lifetime of object bound to reference members of aggregates,
// that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`)
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
}

void lambda() {
Expand Down
2 changes: 2 additions & 0 deletions clang/test/CXX/drs/cwg16xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ namespace cwg1696 { // cwg1696: 7
const A &a = A(); // #cwg1696-D1-a
};
D1 d1 = {}; // #cwg1696-d1
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}}
// since-cxx14-note@#cwg1696-D1-a {{initializing field 'a' with default member initializer}}

struct D2 {
const A &a = A(); // #cwg1696-D2-a
Expand Down
19 changes: 5 additions & 14 deletions clang/test/CXX/drs/cwg18xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,28 +206,19 @@ namespace cwg1814 { // cwg1814: yes
#endif
}

namespace cwg1815 { // cwg1815: 19
namespace cwg1815 { // cwg1815: no
#if __cplusplus >= 201402L
struct A { int &&r = 0; };
// FIXME: needs codegen test
struct A { int &&r = 0; }; // #cwg1815-A
A a = {};
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME
// since-cxx14-note@#cwg1815-A {{initializing field 'r' with default member initializer}}

struct B { int &&r = 0; }; // #cwg1815-B
// since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
// since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
// since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
B b; // #cwg1815-b

#if __cplusplus >= 201703L
struct C { const int &r = 0; };
constexpr C c = {}; // OK, since cwg1815
static_assert(c.r == 0);

constexpr int f() {
A a = {}; // OK, since cwg1815
return a.r;
}
static_assert(f() == 0);
#endif
#endif
}

Expand Down
34 changes: 0 additions & 34 deletions clang/test/CXX/special/class.temporary/p6.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,40 +269,6 @@ void init_capture_init_list() {
// CHECK: }
}

void check_dr1815() { // dr1815: yes
#if __cplusplus >= 201402L

struct A {
int &&r = 0;
~A() {}
};

struct B {
A &&a = A{};
~B() {}
};
B a = {};

// CHECK: call {{.*}}block_scope_begin_function
extern void block_scope_begin_function();
extern void block_scope_end_function();
block_scope_begin_function();
{
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
B b = {};
}
// CHECK: call {{.*}}block_scope_end_function
block_scope_end_function();

// CHECK: call {{.*}}some_other_function
extern void some_other_function();
some_other_function();
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
#endif
}

namespace P2718R0 {
namespace basic {
template <typename E> using T2 = std::list<E>;
Expand Down
Loading