forked from OSchip/llvm-project
C++ DR712 and others: handle non-odr-use resulting from an lvalue-to-rvalue conversion applied to a member access or similar not-quite-trivial lvalue expression.
Summary: When a variable is named in a context where we can't directly emit a reference to it (because we don't know for sure that it's going to be defined, or it's from an enclosing function and not captured, or the reference might not "work" for some reason), we emit a copy of the variable as a global and use that for the known-to-be-read-only access. This reinstates r363295, reverted in r363352, with a fix for PR42276: we now produce a proper name for a non-odr-use reference to a static constexpr data member. The name <mangled-name>.const is used in that case; such names are reserved to the implementation for cases such as this and should demangle nicely. Reviewers: rjmccall Subscribers: jdoerfert, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D63157 llvm-svn: 363428
This commit is contained in:
parent
dcdd12b68c
commit
24cdcadcc5
|
@ -1077,17 +1077,16 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern,
|
||||||
return constant;
|
return constant;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl &D,
|
Address CodeGenModule::createUnnamedGlobalFrom(const VarDecl &D,
|
||||||
CGBuilderTy &Builder,
|
llvm::Constant *Constant,
|
||||||
llvm::Constant *Constant,
|
CharUnits Align) {
|
||||||
CharUnits Align) {
|
|
||||||
auto FunctionName = [&](const DeclContext *DC) -> std::string {
|
auto FunctionName = [&](const DeclContext *DC) -> std::string {
|
||||||
if (const auto *FD = dyn_cast<FunctionDecl>(DC)) {
|
if (const auto *FD = dyn_cast<FunctionDecl>(DC)) {
|
||||||
if (const auto *CC = dyn_cast<CXXConstructorDecl>(FD))
|
if (const auto *CC = dyn_cast<CXXConstructorDecl>(FD))
|
||||||
return CC->getNameAsString();
|
return CC->getNameAsString();
|
||||||
if (const auto *CD = dyn_cast<CXXDestructorDecl>(FD))
|
if (const auto *CD = dyn_cast<CXXDestructorDecl>(FD))
|
||||||
return CD->getNameAsString();
|
return CD->getNameAsString();
|
||||||
return CGM.getMangledName(FD);
|
return getMangledName(FD);
|
||||||
} else if (const auto *OM = dyn_cast<ObjCMethodDecl>(DC)) {
|
} else if (const auto *OM = dyn_cast<ObjCMethodDecl>(DC)) {
|
||||||
return OM->getNameAsString();
|
return OM->getNameAsString();
|
||||||
} else if (isa<BlockDecl>(DC)) {
|
} else if (isa<BlockDecl>(DC)) {
|
||||||
|
@ -1095,26 +1094,47 @@ static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl &D,
|
||||||
} else if (isa<CapturedDecl>(DC)) {
|
} else if (isa<CapturedDecl>(DC)) {
|
||||||
return "<captured>";
|
return "<captured>";
|
||||||
} else {
|
} else {
|
||||||
llvm::llvm_unreachable_internal("expected a function or method");
|
llvm_unreachable("expected a function or method");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto *Ty = Constant->getType();
|
// Form a simple per-variable cache of these values in case we find we
|
||||||
bool isConstant = true;
|
// want to reuse them.
|
||||||
llvm::GlobalVariable *InsertBefore = nullptr;
|
llvm::GlobalVariable *&CacheEntry = InitializerConstants[&D];
|
||||||
unsigned AS = CGM.getContext().getTargetAddressSpace(
|
if (!CacheEntry || CacheEntry->getInitializer() != Constant) {
|
||||||
CGM.getStringLiteralAddressSpace());
|
auto *Ty = Constant->getType();
|
||||||
llvm::GlobalVariable *GV = new llvm::GlobalVariable(
|
bool isConstant = true;
|
||||||
CGM.getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
|
llvm::GlobalVariable *InsertBefore = nullptr;
|
||||||
Constant,
|
unsigned AS =
|
||||||
"__const." + FunctionName(D.getParentFunctionOrMethod()) + "." +
|
getContext().getTargetAddressSpace(getStringLiteralAddressSpace());
|
||||||
D.getName(),
|
std::string Name;
|
||||||
InsertBefore, llvm::GlobalValue::NotThreadLocal, AS);
|
if (D.hasGlobalStorage())
|
||||||
GV->setAlignment(Align.getQuantity());
|
Name = getMangledName(&D).str() + ".const";
|
||||||
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
else if (const DeclContext *DC = D.getParentFunctionOrMethod())
|
||||||
|
Name = ("__const." + FunctionName(DC) + "." + D.getName()).str();
|
||||||
|
else
|
||||||
|
llvm_unreachable("local variable has no parent function or method");
|
||||||
|
llvm::GlobalVariable *GV = new llvm::GlobalVariable(
|
||||||
|
getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
|
||||||
|
Constant, Name, InsertBefore, llvm::GlobalValue::NotThreadLocal, AS);
|
||||||
|
GV->setAlignment(Align.getQuantity());
|
||||||
|
GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
|
||||||
|
CacheEntry = GV;
|
||||||
|
} else if (CacheEntry->getAlignment() < Align.getQuantity()) {
|
||||||
|
CacheEntry->setAlignment(Align.getQuantity());
|
||||||
|
}
|
||||||
|
|
||||||
Address SrcPtr = Address(GV, Align);
|
return Address(CacheEntry, Align);
|
||||||
llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(), AS);
|
}
|
||||||
|
|
||||||
|
static Address createUnnamedGlobalForMemcpyFrom(CodeGenModule &CGM,
|
||||||
|
const VarDecl &D,
|
||||||
|
CGBuilderTy &Builder,
|
||||||
|
llvm::Constant *Constant,
|
||||||
|
CharUnits Align) {
|
||||||
|
Address SrcPtr = CGM.createUnnamedGlobalFrom(D, Constant, Align);
|
||||||
|
llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(),
|
||||||
|
SrcPtr.getAddressSpace());
|
||||||
if (SrcPtr.getType() != BP)
|
if (SrcPtr.getType() != BP)
|
||||||
SrcPtr = Builder.CreateBitCast(SrcPtr, BP);
|
SrcPtr = Builder.CreateBitCast(SrcPtr, BP);
|
||||||
return SrcPtr;
|
return SrcPtr;
|
||||||
|
@ -1197,10 +1217,10 @@ static void emitStoresForConstant(CodeGenModule &CGM, const VarDecl &D,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy from a global.
|
// Copy from a global.
|
||||||
Builder.CreateMemCpy(
|
Builder.CreateMemCpy(Loc,
|
||||||
Loc,
|
createUnnamedGlobalForMemcpyFrom(
|
||||||
createUnnamedGlobalFrom(CGM, D, Builder, constant, Loc.getAlignment()),
|
CGM, D, Builder, constant, Loc.getAlignment()),
|
||||||
SizeVal, isVolatile);
|
SizeVal, isVolatile);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void emitStoresForZeroInit(CodeGenModule &CGM, const VarDecl &D,
|
static void emitStoresForZeroInit(CodeGenModule &CGM, const VarDecl &D,
|
||||||
|
@ -1763,10 +1783,10 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
|
||||||
llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2, "vla.cur");
|
llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2, "vla.cur");
|
||||||
Cur->addIncoming(Begin.getPointer(), OriginBB);
|
Cur->addIncoming(Begin.getPointer(), OriginBB);
|
||||||
CharUnits CurAlign = Loc.getAlignment().alignmentOfArrayElement(EltSize);
|
CharUnits CurAlign = Loc.getAlignment().alignmentOfArrayElement(EltSize);
|
||||||
Builder.CreateMemCpy(
|
Builder.CreateMemCpy(Address(Cur, CurAlign),
|
||||||
Address(Cur, CurAlign),
|
createUnnamedGlobalForMemcpyFrom(
|
||||||
createUnnamedGlobalFrom(CGM, D, Builder, Constant, ConstantAlign),
|
CGM, D, Builder, Constant, ConstantAlign),
|
||||||
BaseSizeInChars, isVolatile);
|
BaseSizeInChars, isVolatile);
|
||||||
llvm::Value *Next =
|
llvm::Value *Next =
|
||||||
Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars, "vla.next");
|
Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars, "vla.next");
|
||||||
llvm::Value *Done = Builder.CreateICmpEQ(Next, End, "vla-init.isdone");
|
llvm::Value *Done = Builder.CreateICmpEQ(Next, End, "vla-init.isdone");
|
||||||
|
|
|
@ -1422,10 +1422,11 @@ static ConstantEmissionKind checkVarTypeForConstantEmission(QualType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to emit a reference to the given value without producing it as
|
/// Try to emit a reference to the given value without producing it as
|
||||||
/// an l-value. This is actually more than an optimization: we can't
|
/// an l-value. This is just an optimization, but it avoids us needing
|
||||||
/// produce an l-value for variables that we never actually captured
|
/// to emit global copies of variables if they're named without triggering
|
||||||
/// in a block or lambda, which means const int variables or constexpr
|
/// a formal use in a context where we can't emit a direct reference to them,
|
||||||
/// literals or similar.
|
/// for instance if a block or lambda or a member of a local class uses a
|
||||||
|
/// const int variable or constexpr variable from an enclosing function.
|
||||||
CodeGenFunction::ConstantEmission
|
CodeGenFunction::ConstantEmission
|
||||||
CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
|
CodeGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) {
|
||||||
ValueDecl *value = refExpr->getDecl();
|
ValueDecl *value = refExpr->getDecl();
|
||||||
|
@ -2450,33 +2451,97 @@ static LValue EmitGlobalNamedRegister(const VarDecl *VD, CodeGenModule &CGM) {
|
||||||
return LValue::MakeGlobalReg(Address(Ptr, Alignment), VD->getType());
|
return LValue::MakeGlobalReg(Address(Ptr, Alignment), VD->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine whether we can emit a reference to \p VD from the current
|
||||||
|
/// context, despite not necessarily having seen an odr-use of the variable in
|
||||||
|
/// this context.
|
||||||
|
static bool canEmitSpuriousReferenceToVariable(CodeGenFunction &CGF,
|
||||||
|
const DeclRefExpr *E,
|
||||||
|
const VarDecl *VD,
|
||||||
|
bool IsConstant) {
|
||||||
|
// For a variable declared in an enclosing scope, do not emit a spurious
|
||||||
|
// reference even if we have a capture, as that will emit an unwarranted
|
||||||
|
// reference to our capture state, and will likely generate worse code than
|
||||||
|
// emitting a local copy.
|
||||||
|
if (E->refersToEnclosingVariableOrCapture())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// For a local declaration declared in this function, we can always reference
|
||||||
|
// it even if we don't have an odr-use.
|
||||||
|
if (VD->hasLocalStorage()) {
|
||||||
|
return VD->getDeclContext() ==
|
||||||
|
dyn_cast_or_null<DeclContext>(CGF.CurCodeDecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a global declaration, we can emit a reference to it if we know
|
||||||
|
// for sure that we are able to emit a definition of it.
|
||||||
|
VD = VD->getDefinition(CGF.getContext());
|
||||||
|
if (!VD)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Don't emit a spurious reference if it might be to a variable that only
|
||||||
|
// exists on a different device / target.
|
||||||
|
// FIXME: This is unnecessarily broad. Check whether this would actually be a
|
||||||
|
// cross-target reference.
|
||||||
|
if (CGF.getLangOpts().OpenMP || CGF.getLangOpts().CUDA ||
|
||||||
|
CGF.getLangOpts().OpenCL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can emit a spurious reference only if the linkage implies that we'll
|
||||||
|
// be emitting a non-interposable symbol that will be retained until link
|
||||||
|
// time.
|
||||||
|
switch (CGF.CGM.getLLVMLinkageVarDefinition(VD, IsConstant)) {
|
||||||
|
case llvm::GlobalValue::ExternalLinkage:
|
||||||
|
case llvm::GlobalValue::LinkOnceODRLinkage:
|
||||||
|
case llvm::GlobalValue::WeakODRLinkage:
|
||||||
|
case llvm::GlobalValue::InternalLinkage:
|
||||||
|
case llvm::GlobalValue::PrivateLinkage:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
||||||
const NamedDecl *ND = E->getDecl();
|
const NamedDecl *ND = E->getDecl();
|
||||||
QualType T = E->getType();
|
QualType T = E->getType();
|
||||||
|
|
||||||
|
assert(E->isNonOdrUse() != NOUR_Unevaluated &&
|
||||||
|
"should not emit an unevaluated operand");
|
||||||
|
|
||||||
if (const auto *VD = dyn_cast<VarDecl>(ND)) {
|
if (const auto *VD = dyn_cast<VarDecl>(ND)) {
|
||||||
// Global Named registers access via intrinsics only
|
// Global Named registers access via intrinsics only
|
||||||
if (VD->getStorageClass() == SC_Register &&
|
if (VD->getStorageClass() == SC_Register &&
|
||||||
VD->hasAttr<AsmLabelAttr>() && !VD->isLocalVarDecl())
|
VD->hasAttr<AsmLabelAttr>() && !VD->isLocalVarDecl())
|
||||||
return EmitGlobalNamedRegister(VD, CGM);
|
return EmitGlobalNamedRegister(VD, CGM);
|
||||||
|
|
||||||
// A DeclRefExpr for a reference initialized by a constant expression can
|
// If this DeclRefExpr does not constitute an odr-use of the variable,
|
||||||
// appear without being odr-used. Directly emit the constant initializer.
|
// we're not permitted to emit a reference to it in general, and it might
|
||||||
VD->getAnyInitializer(VD);
|
// not be captured if capture would be necessary for a use. Emit the
|
||||||
if (E->isNonOdrUse() == NOUR_Constant && VD->getType()->isReferenceType()) {
|
// constant value directly instead.
|
||||||
llvm::Constant *Val =
|
if (E->isNonOdrUse() == NOUR_Constant &&
|
||||||
ConstantEmitter(*this).emitAbstract(E->getLocation(),
|
(VD->getType()->isReferenceType() ||
|
||||||
*VD->evaluateValue(),
|
!canEmitSpuriousReferenceToVariable(*this, E, VD, true))) {
|
||||||
VD->getType());
|
VD->getAnyInitializer(VD);
|
||||||
assert(Val && "failed to emit reference constant expression");
|
llvm::Constant *Val = ConstantEmitter(*this).emitAbstract(
|
||||||
// FIXME: Eventually we will want to emit vector element references.
|
E->getLocation(), *VD->evaluateValue(), VD->getType());
|
||||||
|
assert(Val && "failed to emit constant expression");
|
||||||
|
|
||||||
// Should we be using the alignment of the constant pointer we emitted?
|
Address Addr = Address::invalid();
|
||||||
CharUnits Alignment = getNaturalTypeAlignment(E->getType(),
|
if (!VD->getType()->isReferenceType()) {
|
||||||
/* BaseInfo= */ nullptr,
|
// Spill the constant value to a global.
|
||||||
/* TBAAInfo= */ nullptr,
|
Addr = CGM.createUnnamedGlobalFrom(*VD, Val,
|
||||||
/* forPointeeType= */ true);
|
getContext().getDeclAlign(VD));
|
||||||
return MakeAddrLValue(Address(Val, Alignment), T, AlignmentSource::Decl);
|
} else {
|
||||||
|
// Should we be using the alignment of the constant pointer we emitted?
|
||||||
|
CharUnits Alignment =
|
||||||
|
getNaturalTypeAlignment(E->getType(),
|
||||||
|
/* BaseInfo= */ nullptr,
|
||||||
|
/* TBAAInfo= */ nullptr,
|
||||||
|
/* forPointeeType= */ true);
|
||||||
|
Addr = Address(Val, Alignment);
|
||||||
|
}
|
||||||
|
return MakeAddrLValue(Addr, T, AlignmentSource::Decl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Handle other kinds of non-odr-use DeclRefExprs.
|
// FIXME: Handle other kinds of non-odr-use DeclRefExprs.
|
||||||
|
@ -2512,7 +2577,7 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) {
|
||||||
// FIXME: We should be able to assert this for FunctionDecls as well!
|
// FIXME: We should be able to assert this for FunctionDecls as well!
|
||||||
// FIXME: We should be able to assert this for all DeclRefExprs, not just
|
// FIXME: We should be able to assert this for all DeclRefExprs, not just
|
||||||
// those with a valid source location.
|
// those with a valid source location.
|
||||||
assert((ND->isUsed(false) || !isa<VarDecl>(ND) ||
|
assert((ND->isUsed(false) || !isa<VarDecl>(ND) || E->isNonOdrUse() ||
|
||||||
!E->getLocation().isValid()) &&
|
!E->getLocation().isValid()) &&
|
||||||
"Should not use decl without marking it used!");
|
"Should not use decl without marking it used!");
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,10 @@ private:
|
||||||
llvm::SmallVector<std::pair<llvm::GlobalValue *, llvm::Constant *>, 8>
|
llvm::SmallVector<std::pair<llvm::GlobalValue *, llvm::Constant *>, 8>
|
||||||
GlobalValReplacements;
|
GlobalValReplacements;
|
||||||
|
|
||||||
|
/// Variables for which we've emitted globals containing their constant
|
||||||
|
/// values along with the corresponding globals, for opportunistic reuse.
|
||||||
|
llvm::DenseMap<const VarDecl*, llvm::GlobalVariable*> InitializerConstants;
|
||||||
|
|
||||||
/// Set of global decls for which we already diagnosed mangled name conflict.
|
/// Set of global decls for which we already diagnosed mangled name conflict.
|
||||||
/// Required to not issue a warning (on a mangling conflict) multiple times
|
/// Required to not issue a warning (on a mangling conflict) multiple times
|
||||||
/// for the same decl.
|
/// for the same decl.
|
||||||
|
@ -623,6 +627,9 @@ public:
|
||||||
StaticLocalDeclGuardMap[D] = C;
|
StaticLocalDeclGuardMap[D] = C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address createUnnamedGlobalFrom(const VarDecl &D, llvm::Constant *Constant,
|
||||||
|
CharUnits Align);
|
||||||
|
|
||||||
bool lookupRepresentativeDecl(StringRef MangledName,
|
bool lookupRepresentativeDecl(StringRef MangledName,
|
||||||
GlobalDecl &Result) const;
|
GlobalDecl &Result) const;
|
||||||
|
|
||||||
|
|
|
@ -15806,6 +15806,32 @@ QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) {
|
||||||
return DeclRefType;
|
return DeclRefType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Helper to copy the template arguments from a DeclRefExpr or MemberExpr.
|
||||||
|
// The produced TemplateArgumentListInfo* points to data stored within this
|
||||||
|
// object, so should only be used in contexts where the pointer will not be
|
||||||
|
// used after the CopiedTemplateArgs object is destroyed.
|
||||||
|
class CopiedTemplateArgs {
|
||||||
|
bool HasArgs;
|
||||||
|
TemplateArgumentListInfo TemplateArgStorage;
|
||||||
|
public:
|
||||||
|
template<typename RefExpr>
|
||||||
|
CopiedTemplateArgs(RefExpr *E) : HasArgs(E->hasExplicitTemplateArgs()) {
|
||||||
|
if (HasArgs)
|
||||||
|
E->copyTemplateArgumentsInto(TemplateArgStorage);
|
||||||
|
}
|
||||||
|
operator TemplateArgumentListInfo*()
|
||||||
|
#ifdef __has_cpp_attribute
|
||||||
|
#if __has_cpp_attribute(clang::lifetimebound)
|
||||||
|
[[clang::lifetimebound]]
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return HasArgs ? &TemplateArgStorage : nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Walk the set of potential results of an expression and mark them all as
|
/// Walk the set of potential results of an expression and mark them all as
|
||||||
/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason.
|
/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason.
|
||||||
///
|
///
|
||||||
|
@ -15897,16 +15923,11 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
|
||||||
|
|
||||||
// Rebuild as a non-odr-use DeclRefExpr.
|
// Rebuild as a non-odr-use DeclRefExpr.
|
||||||
MarkNotOdrUsed();
|
MarkNotOdrUsed();
|
||||||
TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
|
|
||||||
if (DRE->hasExplicitTemplateArgs()) {
|
|
||||||
DRE->copyTemplateArgumentsInto(TemplateArgStorage);
|
|
||||||
TemplateArgs = &TemplateArgStorage;
|
|
||||||
}
|
|
||||||
return DeclRefExpr::Create(
|
return DeclRefExpr::Create(
|
||||||
S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
|
S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
|
||||||
DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
|
DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
|
||||||
DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
|
DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
|
||||||
DRE->getFoundDecl(), TemplateArgs, NOUR);
|
DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Expr::FunctionParmPackExprClass: {
|
case Expr::FunctionParmPackExprClass: {
|
||||||
|
@ -15924,52 +15945,107 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Implement these.
|
|
||||||
// -- If e is a subscripting operation with an array operand...
|
// -- If e is a subscripting operation with an array operand...
|
||||||
// -- If e is a class member access expression [...] naming a non-static
|
case Expr::ArraySubscriptExprClass: {
|
||||||
// data member...
|
auto *ASE = cast<ArraySubscriptExpr>(E);
|
||||||
|
Expr *OldBase = ASE->getBase()->IgnoreImplicit();
|
||||||
|
if (!OldBase->getType()->isArrayType())
|
||||||
|
break;
|
||||||
|
ExprResult Base = Rebuild(OldBase);
|
||||||
|
if (!Base.isUsable())
|
||||||
|
return Base;
|
||||||
|
Expr *LHS = ASE->getBase() == ASE->getLHS() ? Base.get() : ASE->getLHS();
|
||||||
|
Expr *RHS = ASE->getBase() == ASE->getRHS() ? Base.get() : ASE->getRHS();
|
||||||
|
SourceLocation LBracketLoc = ASE->getBeginLoc(); // FIXME: Not stored.
|
||||||
|
return S.ActOnArraySubscriptExpr(nullptr, LHS, LBracketLoc, RHS,
|
||||||
|
ASE->getRBracketLoc());
|
||||||
|
}
|
||||||
|
|
||||||
// -- If e is a class member access expression naming a static data member,
|
|
||||||
// ...
|
|
||||||
case Expr::MemberExprClass: {
|
case Expr::MemberExprClass: {
|
||||||
auto *ME = cast<MemberExpr>(E);
|
auto *ME = cast<MemberExpr>(E);
|
||||||
|
// -- If e is a class member access expression [...] naming a non-static
|
||||||
|
// data member...
|
||||||
|
if (isa<FieldDecl>(ME->getMemberDecl())) {
|
||||||
|
ExprResult Base = Rebuild(ME->getBase());
|
||||||
|
if (!Base.isUsable())
|
||||||
|
return Base;
|
||||||
|
return MemberExpr::Create(
|
||||||
|
S.Context, Base.get(), ME->isArrow(), ME->getOperatorLoc(),
|
||||||
|
ME->getQualifierLoc(), ME->getTemplateKeywordLoc(),
|
||||||
|
ME->getMemberDecl(), ME->getFoundDecl(), ME->getMemberNameInfo(),
|
||||||
|
CopiedTemplateArgs(ME), ME->getType(), ME->getValueKind(),
|
||||||
|
ME->getObjectKind(), ME->isNonOdrUse());
|
||||||
|
}
|
||||||
|
|
||||||
if (ME->getMemberDecl()->isCXXInstanceMember())
|
if (ME->getMemberDecl()->isCXXInstanceMember())
|
||||||
// FIXME: Recurse to the left-hand side.
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// -- If e is a class member access expression naming a static data member,
|
||||||
|
// ...
|
||||||
if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl()))
|
if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl()))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Rebuild as a non-odr-use MemberExpr.
|
// Rebuild as a non-odr-use MemberExpr.
|
||||||
MarkNotOdrUsed();
|
MarkNotOdrUsed();
|
||||||
TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
|
|
||||||
if (ME->hasExplicitTemplateArgs()) {
|
|
||||||
ME->copyTemplateArgumentsInto(TemplateArgStorage);
|
|
||||||
TemplateArgs = &TemplateArgStorage;
|
|
||||||
}
|
|
||||||
return MemberExpr::Create(
|
return MemberExpr::Create(
|
||||||
S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(),
|
S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(),
|
||||||
ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(),
|
ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(),
|
||||||
ME->getFoundDecl(), ME->getMemberNameInfo(), TemplateArgs,
|
ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME),
|
||||||
ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR);
|
ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR);
|
||||||
return ExprEmpty();
|
return ExprEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Implement this.
|
case Expr::BinaryOperatorClass: {
|
||||||
// -- If e is a pointer-to-member expression of the form e1 .* e2 ...
|
auto *BO = cast<BinaryOperator>(E);
|
||||||
|
Expr *LHS = BO->getLHS();
|
||||||
|
Expr *RHS = BO->getRHS();
|
||||||
|
// -- If e is a pointer-to-member expression of the form e1 .* e2 ...
|
||||||
|
if (BO->getOpcode() == BO_PtrMemD) {
|
||||||
|
ExprResult Sub = Rebuild(LHS);
|
||||||
|
if (!Sub.isUsable())
|
||||||
|
return Sub;
|
||||||
|
LHS = Sub.get();
|
||||||
|
// -- If e is a comma expression, ...
|
||||||
|
} else if (BO->getOpcode() == BO_Comma) {
|
||||||
|
ExprResult Sub = Rebuild(RHS);
|
||||||
|
if (!Sub.isUsable())
|
||||||
|
return Sub;
|
||||||
|
RHS = Sub.get();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return S.BuildBinOp(nullptr, BO->getOperatorLoc(), BO->getOpcode(),
|
||||||
|
LHS, RHS);
|
||||||
|
}
|
||||||
|
|
||||||
// -- If e has the form (e1)...
|
// -- If e has the form (e1)...
|
||||||
case Expr::ParenExprClass: {
|
case Expr::ParenExprClass: {
|
||||||
auto *PE = dyn_cast<ParenExpr>(E);
|
auto *PE = cast<ParenExpr>(E);
|
||||||
ExprResult Sub = Rebuild(PE->getSubExpr());
|
ExprResult Sub = Rebuild(PE->getSubExpr());
|
||||||
if (!Sub.isUsable())
|
if (!Sub.isUsable())
|
||||||
return Sub;
|
return Sub;
|
||||||
return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
|
return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Implement these.
|
|
||||||
// -- If e is a glvalue conditional expression, ...
|
// -- If e is a glvalue conditional expression, ...
|
||||||
// -- If e is a comma expression, ...
|
// We don't apply this to a binary conditional operator. FIXME: Should we?
|
||||||
|
case Expr::ConditionalOperatorClass: {
|
||||||
|
auto *CO = cast<ConditionalOperator>(E);
|
||||||
|
ExprResult LHS = Rebuild(CO->getLHS());
|
||||||
|
if (LHS.isInvalid())
|
||||||
|
return ExprError();
|
||||||
|
ExprResult RHS = Rebuild(CO->getRHS());
|
||||||
|
if (RHS.isInvalid())
|
||||||
|
return ExprError();
|
||||||
|
if (!LHS.isUsable() && !RHS.isUsable())
|
||||||
|
return ExprEmpty();
|
||||||
|
if (!LHS.isUsable())
|
||||||
|
LHS = CO->getLHS();
|
||||||
|
if (!RHS.isUsable())
|
||||||
|
RHS = CO->getRHS();
|
||||||
|
return S.ActOnConditionalOp(CO->getQuestionLoc(), CO->getColonLoc(),
|
||||||
|
CO->getCond(), LHS.get(), RHS.get());
|
||||||
|
}
|
||||||
|
|
||||||
// [Clang extension]
|
// [Clang extension]
|
||||||
// -- If e has the form __extension__ e1...
|
// -- If e has the form __extension__ e1...
|
||||||
|
@ -15988,7 +16064,7 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
|
||||||
// -- If e has the form _Generic(...), the set of potential results is the
|
// -- If e has the form _Generic(...), the set of potential results is the
|
||||||
// union of the sets of potential results of the associated expressions.
|
// union of the sets of potential results of the associated expressions.
|
||||||
case Expr::GenericSelectionExprClass: {
|
case Expr::GenericSelectionExprClass: {
|
||||||
auto *GSE = dyn_cast<GenericSelectionExpr>(E);
|
auto *GSE = cast<GenericSelectionExpr>(E);
|
||||||
|
|
||||||
SmallVector<Expr *, 4> AssocExprs;
|
SmallVector<Expr *, 4> AssocExprs;
|
||||||
bool AnyChanged = false;
|
bool AnyChanged = false;
|
||||||
|
@ -16016,7 +16092,7 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
|
||||||
// results is the union of the sets of potential results of the
|
// results is the union of the sets of potential results of the
|
||||||
// second and third subexpressions.
|
// second and third subexpressions.
|
||||||
case Expr::ChooseExprClass: {
|
case Expr::ChooseExprClass: {
|
||||||
auto *CE = dyn_cast<ChooseExpr>(E);
|
auto *CE = cast<ChooseExpr>(E);
|
||||||
|
|
||||||
ExprResult LHS = Rebuild(CE->getLHS());
|
ExprResult LHS = Rebuild(CE->getLHS());
|
||||||
if (LHS.isInvalid())
|
if (LHS.isInvalid())
|
||||||
|
@ -16039,13 +16115,38 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E,
|
||||||
|
|
||||||
// Step through non-syntactic nodes.
|
// Step through non-syntactic nodes.
|
||||||
case Expr::ConstantExprClass: {
|
case Expr::ConstantExprClass: {
|
||||||
auto *CE = dyn_cast<ConstantExpr>(E);
|
auto *CE = cast<ConstantExpr>(E);
|
||||||
ExprResult Sub = Rebuild(CE->getSubExpr());
|
ExprResult Sub = Rebuild(CE->getSubExpr());
|
||||||
if (!Sub.isUsable())
|
if (!Sub.isUsable())
|
||||||
return Sub;
|
return Sub;
|
||||||
return ConstantExpr::Create(S.Context, Sub.get());
|
return ConstantExpr::Create(S.Context, Sub.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We could mostly rely on the recursive rebuilding to rebuild implicit
|
||||||
|
// casts, but not at the top level, so rebuild them here.
|
||||||
|
case Expr::ImplicitCastExprClass: {
|
||||||
|
auto *ICE = cast<ImplicitCastExpr>(E);
|
||||||
|
// Only step through the narrow set of cast kinds we expect to encounter.
|
||||||
|
// Anything else suggests we've left the region in which potential results
|
||||||
|
// can be found.
|
||||||
|
switch (ICE->getCastKind()) {
|
||||||
|
case CK_NoOp:
|
||||||
|
case CK_DerivedToBase:
|
||||||
|
case CK_UncheckedDerivedToBase: {
|
||||||
|
ExprResult Sub = Rebuild(ICE->getSubExpr());
|
||||||
|
if (!Sub.isUsable())
|
||||||
|
return Sub;
|
||||||
|
CXXCastPath Path(ICE->path());
|
||||||
|
return S.ImpCastExprToType(Sub.get(), ICE->getType(), ICE->getCastKind(),
|
||||||
|
ICE->getValueKind(), &Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++98 %s -Wno-unused -verify
|
||||||
|
// RUN: %clang_cc1 -std=c++11 %s -Wno-unused -verify
|
||||||
|
// RUN: %clang_cc1 -std=c++2a %s -Wno-unused -verify
|
||||||
|
|
||||||
|
void use(int);
|
||||||
|
|
||||||
|
void f() {
|
||||||
|
const int a = 1; // expected-note {{here}}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
constexpr int arr[3] = {1, 2, 3}; // expected-note 2{{here}}
|
||||||
|
|
||||||
|
struct S { int x; int f() const; };
|
||||||
|
constexpr S s = {0}; // expected-note 3{{here}}
|
||||||
|
constexpr S *ps = nullptr;
|
||||||
|
S *const &psr = ps; // expected-note 2{{here}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
void test(int i) {
|
||||||
|
// id-expression
|
||||||
|
use(a);
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
// subscripting operation with an array operand
|
||||||
|
use(arr[i]);
|
||||||
|
use(i[arr]);
|
||||||
|
use((+arr)[i]); // expected-error {{reference to local variable}}
|
||||||
|
use(i[+arr]); // expected-error {{reference to local variable}}
|
||||||
|
|
||||||
|
// class member access naming non-static data member
|
||||||
|
use(s.x);
|
||||||
|
use(s.f()); // expected-error {{reference to local variable}}
|
||||||
|
use((&s)->x); // expected-error {{reference to local variable}}
|
||||||
|
use(ps->x); // ok (lvalue-to-rvalue conversion applied to id-expression)
|
||||||
|
use(psr->x); // expected-error {{reference to local variable}}
|
||||||
|
|
||||||
|
// class member access naming a static data member
|
||||||
|
// FIXME: How to test this?
|
||||||
|
|
||||||
|
// pointer-to-member expression
|
||||||
|
use(s.*&S::x);
|
||||||
|
use((s.*&S::f)()); // expected-error {{reference to local variable}}
|
||||||
|
use(ps->*&S::x); // ok (lvalue-to-rvalue conversion applied to id-expression)
|
||||||
|
use(psr->*&S::x); // expected-error {{reference to local variable}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// parentheses
|
||||||
|
use((a));
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
use((s.x));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// glvalue conditional expression
|
||||||
|
use(i ? a : a);
|
||||||
|
use(i ? i : a);
|
||||||
|
|
||||||
|
// comma expression
|
||||||
|
use((i, a));
|
||||||
|
// FIXME: This is not an odr-use because it is a discarded-value
|
||||||
|
// expression applied to an expression whose potential result is 'a'.
|
||||||
|
use((a, a)); // expected-error {{reference to local variable}}
|
||||||
|
|
||||||
|
// (and combinations thereof)
|
||||||
|
use(a ? (i, a) : a);
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
use(a ? (i, a) : arr[a ? s.x : arr[a]]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Test that this behaves properly.
|
||||||
|
namespace std_example {
|
||||||
|
struct S { static const int x = 0, y = 0; };
|
||||||
|
const int &f(const int &r);
|
||||||
|
bool b;
|
||||||
|
int n = b ? (1, S::x)
|
||||||
|
: f(S::y);
|
||||||
|
}
|
|
@ -4,12 +4,205 @@
|
||||||
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||||
|
|
||||||
// expected-no-diagnostics
|
|
||||||
|
|
||||||
#if __cplusplus < 201103L
|
#if __cplusplus < 201103L
|
||||||
#define static_assert(...) _Static_assert(__VA_ARGS__)
|
#define static_assert(...) _Static_assert(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace dr2083 { // dr2083: partial
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
void non_const_mem_ptr() {
|
||||||
|
struct A {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
constexpr A a = {1, 2};
|
||||||
|
struct B {
|
||||||
|
int A::*p;
|
||||||
|
constexpr int g() const {
|
||||||
|
// OK, not an odr-use of 'a'.
|
||||||
|
return a.*p;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(B{&A::x}.g() == 1, "");
|
||||||
|
static_assert(B{&A::y}.g() == 2, "");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int a = 1;
|
||||||
|
int b;
|
||||||
|
// Note, references only get special odr-use / constant initializxer
|
||||||
|
// treatment in C++11 onwards. We continue to apply that even after DR2083.
|
||||||
|
void ref_to_non_const() {
|
||||||
|
int c;
|
||||||
|
const int &ra = a; // expected-note 0-1{{here}}
|
||||||
|
int &rb = b; // expected-note 0-1{{here}}
|
||||||
|
int &rc = c; // expected-note {{here}}
|
||||||
|
struct A {
|
||||||
|
int f() {
|
||||||
|
int a = ra;
|
||||||
|
int b = rb;
|
||||||
|
#if __cplusplus < 201103L
|
||||||
|
// expected-error@-3 {{in enclosing function}}
|
||||||
|
// expected-error@-3 {{in enclosing function}}
|
||||||
|
#endif
|
||||||
|
int c = rc; // expected-error {{in enclosing function}}
|
||||||
|
return a + b + c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
struct NoMut1 { int a, b; };
|
||||||
|
struct NoMut2 { NoMut1 m; };
|
||||||
|
struct NoMut3 : NoMut1 {
|
||||||
|
constexpr NoMut3(int a, int b) : NoMut1{a, b} {}
|
||||||
|
};
|
||||||
|
struct Mut1 {
|
||||||
|
int a;
|
||||||
|
mutable int b;
|
||||||
|
};
|
||||||
|
struct Mut2 { Mut1 m; };
|
||||||
|
struct Mut3 : Mut1 {
|
||||||
|
constexpr Mut3(int a, int b) : Mut1{a, b} {}
|
||||||
|
};
|
||||||
|
void mutable_subobjects() {
|
||||||
|
constexpr NoMut1 nm1 = {1, 2};
|
||||||
|
constexpr NoMut2 nm2 = {1, 2};
|
||||||
|
constexpr NoMut3 nm3 = {1, 2};
|
||||||
|
constexpr Mut1 m1 = {1, 2}; // expected-note {{declared here}}
|
||||||
|
constexpr Mut2 m2 = {1, 2}; // expected-note {{declared here}}
|
||||||
|
constexpr Mut3 m3 = {1, 2}; // expected-note {{declared here}}
|
||||||
|
struct A {
|
||||||
|
void f() {
|
||||||
|
static_assert(nm1.a == 1, "");
|
||||||
|
static_assert(nm2.m.a == 1, "");
|
||||||
|
static_assert(nm3.a == 1, "");
|
||||||
|
// Can't even access a non-mutable member of a variable containing mutable fields.
|
||||||
|
static_assert(m1.a == 1, ""); // expected-error {{enclosing function}}
|
||||||
|
static_assert(m2.m.a == 1, ""); // expected-error {{enclosing function}}
|
||||||
|
static_assert(m3.a == 1, ""); // expected-error {{enclosing function}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ellipsis() {
|
||||||
|
void ellipsis(...);
|
||||||
|
struct A {};
|
||||||
|
const int n = 0;
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
constexpr
|
||||||
|
#endif
|
||||||
|
A a = {}; // expected-note {{here}}
|
||||||
|
struct B {
|
||||||
|
void f() {
|
||||||
|
ellipsis(n);
|
||||||
|
// Even though this is technically modelled as an lvalue-to-rvalue
|
||||||
|
// conversion, it calls a constructor and binds 'a' to a reference, so
|
||||||
|
// it results in an odr-use.
|
||||||
|
ellipsis(a); // expected-error {{enclosing function}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
void volatile_lval() {
|
||||||
|
struct A { int n; };
|
||||||
|
constexpr A a = {0}; // expected-note {{here}}
|
||||||
|
struct B {
|
||||||
|
void f() {
|
||||||
|
// An lvalue-to-rvalue conversion of a volatile lvalue always results
|
||||||
|
// in odr-use.
|
||||||
|
int A::*p = &A::n;
|
||||||
|
int x = a.*p;
|
||||||
|
volatile int A::*q = p;
|
||||||
|
int y = a.*q; // expected-error {{enclosing function}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void discarded_lval() {
|
||||||
|
struct A { int x; mutable int y; volatile int z; };
|
||||||
|
A a; // expected-note 1+{{here}}
|
||||||
|
int &r = a.x; // expected-note {{here}}
|
||||||
|
struct B {
|
||||||
|
void f() {
|
||||||
|
a.x; // expected-warning {{unused}}
|
||||||
|
a.*&A::x; // expected-warning {{unused}}
|
||||||
|
true ? a.x : a.y; // expected-warning {{unused}}
|
||||||
|
(void)a.x;
|
||||||
|
a.x, discarded_lval(); // expected-warning {{unused}}
|
||||||
|
#if 1 // FIXME: These errors are all incorrect; the above code is valid.
|
||||||
|
// expected-error@-6 {{enclosing function}}
|
||||||
|
// expected-error@-6 {{enclosing function}}
|
||||||
|
// expected-error@-6 2{{enclosing function}}
|
||||||
|
// expected-error@-6 {{enclosing function}}
|
||||||
|
// expected-error@-6 {{enclosing function}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 'volatile' qualifier triggers an lvalue-to-rvalue conversion.
|
||||||
|
a.z; // expected-error {{enclosing function}}
|
||||||
|
#if __cplusplus < 201103L
|
||||||
|
// expected-warning@-2 {{assign into a variable}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// References always get "loaded" to determine what they reference,
|
||||||
|
// even if the result is discarded.
|
||||||
|
r; // expected-error {{enclosing function}} expected-warning {{unused}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dr_example_1 {
|
||||||
|
extern int globx;
|
||||||
|
int main() {
|
||||||
|
const int &x = globx;
|
||||||
|
struct A {
|
||||||
|
#if __cplusplus < 201103L
|
||||||
|
// expected-error@+2 {{enclosing function}} expected-note@-3 {{here}}
|
||||||
|
#endif
|
||||||
|
const int *foo() { return &x; }
|
||||||
|
} a;
|
||||||
|
return *a.foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
namespace dr_example_2 {
|
||||||
|
struct A {
|
||||||
|
int q;
|
||||||
|
constexpr A(int q) : q(q) {}
|
||||||
|
constexpr A(const A &a) : q(a.q * 2) {} // (note, not called)
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
constexpr A a(42);
|
||||||
|
constexpr int aq = a.q;
|
||||||
|
struct Q {
|
||||||
|
int foo() { return a.q; }
|
||||||
|
} q;
|
||||||
|
return q.foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking odr-use does not invent an lvalue-to-rvalue conversion (and
|
||||||
|
// hence copy construction) on the potential result variable.
|
||||||
|
struct B {
|
||||||
|
int b = 42;
|
||||||
|
constexpr B() {}
|
||||||
|
constexpr B(const B&) = delete;
|
||||||
|
};
|
||||||
|
void f() {
|
||||||
|
constexpr B b;
|
||||||
|
struct Q {
|
||||||
|
constexpr int foo() const { return b.b; }
|
||||||
|
};
|
||||||
|
static_assert(Q().foo() == 42, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace dr2094 { // dr2094: 5
|
namespace dr2094 { // dr2094: 5
|
||||||
struct A { int n; };
|
struct A { int n; };
|
||||||
struct B { volatile int n; };
|
struct B { volatile int n; };
|
||||||
|
|
|
@ -8,6 +8,19 @@
|
||||||
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
|
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace dr2103 { // dr2103: yes
|
||||||
|
void f() {
|
||||||
|
int a;
|
||||||
|
int &r = a; // expected-note {{here}}
|
||||||
|
struct Inner {
|
||||||
|
void f() {
|
||||||
|
int &s = r; // expected-error {{enclosing function}}
|
||||||
|
(void)s;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace dr2120 { // dr2120: 7
|
namespace dr2120 { // dr2120: 7
|
||||||
struct A {};
|
struct A {};
|
||||||
struct B : A {};
|
struct B : A {};
|
||||||
|
@ -19,6 +32,19 @@ namespace dr2120 { // dr2120: 7
|
||||||
static_assert(!__is_standard_layout(E), "");
|
static_assert(!__is_standard_layout(E), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace dr2170 { // dr2170: 9
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
void f() {
|
||||||
|
constexpr int arr[3] = {1, 2, 3}; // expected-note {{here}}
|
||||||
|
struct S {
|
||||||
|
int get(int n) { return arr[n]; }
|
||||||
|
const int &get_ref(int n) { return arr[n]; } // expected-error {{enclosing function}}
|
||||||
|
// FIXME: expected-warning@-1 {{reference to stack}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace dr2180 { // dr2180: yes
|
namespace dr2180 { // dr2180: yes
|
||||||
class A {
|
class A {
|
||||||
A &operator=(const A &); // expected-note 0-2{{here}}
|
A &operator=(const A &); // expected-note 0-2{{here}}
|
||||||
|
|
|
@ -1,13 +1,45 @@
|
||||||
// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
|
||||||
// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
|
||||||
// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
|
||||||
// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
|
||||||
// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
|
||||||
|
|
||||||
#if __cplusplus <= 201103L
|
#if __cplusplus <= 201103L
|
||||||
// expected-no-diagnostics
|
// expected-no-diagnostics
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace dr2353 { // dr2353: 9
|
||||||
|
struct X {
|
||||||
|
static const int n = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CHECK: FunctionDecl {{.*}} use
|
||||||
|
int use(X x) {
|
||||||
|
// CHECK: MemberExpr {{.*}} .n
|
||||||
|
// CHECK-NOT: non_odr_use
|
||||||
|
// CHECK: DeclRefExpr {{.*}} 'x'
|
||||||
|
// CHECK-NOT: non_odr_use
|
||||||
|
return *&x.n;
|
||||||
|
}
|
||||||
|
#pragma clang __debug dump use
|
||||||
|
|
||||||
|
// CHECK: FunctionDecl {{.*}} not_use
|
||||||
|
int not_use(X x) {
|
||||||
|
// CHECK: MemberExpr {{.*}} .n {{.*}} non_odr_use_constant
|
||||||
|
// CHECK: DeclRefExpr {{.*}} 'x'
|
||||||
|
return x.n;
|
||||||
|
}
|
||||||
|
#pragma clang __debug dump not_use
|
||||||
|
|
||||||
|
// CHECK: FunctionDecl {{.*}} not_use_2
|
||||||
|
int not_use_2(X *x) {
|
||||||
|
// CHECK: MemberExpr {{.*}} ->n {{.*}} non_odr_use_constant
|
||||||
|
// CHECK: DeclRefExpr {{.*}} 'x'
|
||||||
|
return x->n;
|
||||||
|
}
|
||||||
|
#pragma clang __debug dump not_use_2
|
||||||
|
}
|
||||||
|
|
||||||
namespace dr2387 { // dr2387: 9
|
namespace dr2387 { // dr2387: 9
|
||||||
#if __cplusplus >= 201402L
|
#if __cplusplus >= 201402L
|
||||||
template<int> int a = 0;
|
template<int> int a = 0;
|
||||||
|
|
|
@ -1132,3 +1132,20 @@ namespace dr692 { // dr692: no
|
||||||
template void f(int*); // expected-error {{ambiguous}}
|
template void f(int*); // expected-error {{ambiguous}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace dr696 { // dr696: yes
|
||||||
|
void f(const int*);
|
||||||
|
void g() {
|
||||||
|
const int N = 10; // expected-note 1+{{here}}
|
||||||
|
struct A {
|
||||||
|
void h() {
|
||||||
|
int arr[N]; (void)arr;
|
||||||
|
f(&N); // expected-error {{declared in enclosing}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
(void) [] { int arr[N]; (void)arr; };
|
||||||
|
(void) [] { f(&N); }; // expected-error {{cannot be implicitly captured}} expected-note {{here}}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,42 @@ namespace dr705 { // dr705: yes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace dr712 { // dr712: partial
|
||||||
|
void use(int);
|
||||||
|
void f() {
|
||||||
|
const int a = 0; // expected-note 5{{here}}
|
||||||
|
struct X {
|
||||||
|
void g(bool cond) {
|
||||||
|
use(a);
|
||||||
|
use((a));
|
||||||
|
use(cond ? a : a);
|
||||||
|
use((cond, a)); // expected-warning 2{{unused}} FIXME: should only warn once
|
||||||
|
|
||||||
|
(void)a; // FIXME: expected-error {{declared in enclosing}}
|
||||||
|
(void)(a); // FIXME: expected-error {{declared in enclosing}}
|
||||||
|
(void)(cond ? a : a); // FIXME: expected-error 2{{declared in enclosing}}
|
||||||
|
(void)(cond, a); // FIXME: expected-error {{declared in enclosing}} expected-warning {{unused}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201103L
|
||||||
|
void g() {
|
||||||
|
struct A { int n; };
|
||||||
|
constexpr A a = {0}; // expected-note 2{{here}}
|
||||||
|
struct X {
|
||||||
|
void g(bool cond) {
|
||||||
|
use(a.n);
|
||||||
|
use(a.*&A::n);
|
||||||
|
|
||||||
|
(void)a.n; // FIXME: expected-error {{declared in enclosing}}
|
||||||
|
(void)(a.*&A::n); // FIXME: expected-error {{declared in enclosing}}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace dr727 { // dr727: partial
|
namespace dr727 { // dr727: partial
|
||||||
struct A {
|
struct A {
|
||||||
template<typename T> struct C; // expected-note 6{{here}}
|
template<typename T> struct C; // expected-note 6{{here}}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// RUN: %clang_cc1 -std=c++11 -emit-llvm -o - -triple x86_64-linux-gnu %s | FileCheck %s --check-prefixes=CHECK,CHECK-CXX11
|
||||||
|
// RUN: %clang_cc1 -std=c++2a -emit-llvm -o - -triple x86_64-linux-gnu %s | FileCheck %s --check-prefixes=CHECK,CHECK-CXX2A
|
||||||
|
|
||||||
|
// CHECK-DAG: @__const._Z1fi.a = private unnamed_addr constant {{.*}} { i32 1, [2 x i32] [i32 2, i32 3], [3 x i32] [i32 4, i32 5, i32 6] }
|
||||||
|
// CHECK-CXX11-DAG: @_ZN7PR422765State1mE.const = private unnamed_addr constant [2 x { i64, i64 }] [{ {{.*}} @_ZN7PR422765State2f1Ev {{.*}}, i64 0 }, { {{.*}} @_ZN7PR422765State2f2Ev {{.*}}, i64 0 }]
|
||||||
|
// CHECK-CXX2A-DAG: @_ZN7PR422765State1mE = linkonce_odr constant [2 x { i64, i64 }] [{ {{.*}} @_ZN7PR422765State2f1Ev {{.*}}, i64 0 }, { {{.*}} @_ZN7PR422765State2f2Ev {{.*}}, i64 0 }], comdat
|
||||||
|
|
||||||
|
struct A { int x, y[2]; int arr[3]; };
|
||||||
|
// CHECK-LABEL: define i32 @_Z1fi(
|
||||||
|
int f(int i) {
|
||||||
|
// CHECK: call void {{.*}}memcpy{{.*}}({{.*}}, {{.*}} @__const._Z1fi.a
|
||||||
|
constexpr A a = {1, 2, 3, 4, 5, 6};
|
||||||
|
|
||||||
|
// CHECK-LABEL: define {{.*}}@"_ZZ1fiENK3$_0clEiM1Ai"(
|
||||||
|
return [] (int n, int A::*p) {
|
||||||
|
// CHECK: br i1
|
||||||
|
return (n >= 0
|
||||||
|
// CHECK: getelementptr inbounds [3 x i32], [3 x i32]* getelementptr inbounds ({{.*}} @__const._Z1fi.a, i32 0, i32 2), i64 0, i64 %
|
||||||
|
? a.arr[n]
|
||||||
|
// CHECK: br i1
|
||||||
|
: (n == -1
|
||||||
|
// CHECK: getelementptr inbounds i8, i8* bitcast ({{.*}} @__const._Z1fi.a to i8*), i64 %
|
||||||
|
// CHECK: bitcast i8* %{{.*}} to i32*
|
||||||
|
// CHECK: load i32
|
||||||
|
? a.*p
|
||||||
|
// CHECK: getelementptr inbounds [2 x i32], [2 x i32]* getelementptr inbounds ({{.*}} @__const._Z1fi.a, i32 0, i32 1), i64 0, i64 %
|
||||||
|
// CHECK: load i32
|
||||||
|
: a.y[2 - n]));
|
||||||
|
}(i, &A::x);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace PR42276 {
|
||||||
|
class State {
|
||||||
|
void syncDirtyObjects();
|
||||||
|
void f1(), f2();
|
||||||
|
using l = void (State::*)();
|
||||||
|
static constexpr l m[]{&State::f1, &State::f2};
|
||||||
|
};
|
||||||
|
// CHECK-LABEL: define void @_ZN7PR422765State16syncDirtyObjectsEv(
|
||||||
|
void State::syncDirtyObjects() {
|
||||||
|
for (int i = 0; i < sizeof(m) / sizeof(m[0]); ++i)
|
||||||
|
// CHECK-CXX11: getelementptr inbounds [2 x { i64, i64 }], [2 x { i64, i64 }]* @_ZN7PR422765State1mE.const, i64 0, i64 %
|
||||||
|
// CHECK-CXX2A: getelementptr inbounds [2 x { i64, i64 }], [2 x { i64, i64 }]* @_ZN7PR422765State1mE, i64 0, i64 %
|
||||||
|
(this->*m[i])();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4219,7 +4219,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg696">696</a></td>
|
<td><a href="http://wg21.link/cwg696">696</a></td>
|
||||||
<td>C++11</td>
|
<td>C++11</td>
|
||||||
<td>Use of block-scope constants in local classes</td>
|
<td>Use of block-scope constants in local classes</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="full" align="center">Yes</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="open" id="697">
|
<tr class="open" id="697">
|
||||||
<td><a href="http://wg21.link/cwg697">697</a></td>
|
<td><a href="http://wg21.link/cwg697">697</a></td>
|
||||||
|
@ -4315,7 +4315,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg712">712</a></td>
|
<td><a href="http://wg21.link/cwg712">712</a></td>
|
||||||
<td>CD3</td>
|
<td>CD3</td>
|
||||||
<td>Are integer constant operands of a <I>conditional-expression</I> “used?”</td>
|
<td>Are integer constant operands of a <I>conditional-expression</I> “used?”</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="partial" align="center">Partial</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="713">
|
<tr id="713">
|
||||||
<td><a href="http://wg21.link/cwg713">713</a></td>
|
<td><a href="http://wg21.link/cwg713">713</a></td>
|
||||||
|
@ -12313,7 +12313,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg2083">2083</a></td>
|
<td><a href="http://wg21.link/cwg2083">2083</a></td>
|
||||||
<td>DR</td>
|
<td>DR</td>
|
||||||
<td>Incorrect cases of odr-use</td>
|
<td>Incorrect cases of odr-use</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="partial" align="center">Partial</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="2084">
|
<tr id="2084">
|
||||||
<td><a href="http://wg21.link/cwg2084">2084</a></td>
|
<td><a href="http://wg21.link/cwg2084">2084</a></td>
|
||||||
|
@ -12433,7 +12433,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg2103">2103</a></td>
|
<td><a href="http://wg21.link/cwg2103">2103</a></td>
|
||||||
<td>DR</td>
|
<td>DR</td>
|
||||||
<td>Lvalue-to-rvalue conversion is irrelevant in odr-use of a reference</td>
|
<td>Lvalue-to-rvalue conversion is irrelevant in odr-use of a reference</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="full" align="center">Yes</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="2104">
|
<tr id="2104">
|
||||||
<td><a href="http://wg21.link/cwg2104">2104</a></td>
|
<td><a href="http://wg21.link/cwg2104">2104</a></td>
|
||||||
|
@ -12835,7 +12835,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg2170">2170</a></td>
|
<td><a href="http://wg21.link/cwg2170">2170</a></td>
|
||||||
<td>DR</td>
|
<td>DR</td>
|
||||||
<td>Unclear definition of odr-use for arrays</td>
|
<td>Unclear definition of odr-use for arrays</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="svn" align="center">SVN</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="2171">
|
<tr id="2171">
|
||||||
<td><a href="http://wg21.link/cwg2171">2171</a></td>
|
<td><a href="http://wg21.link/cwg2171">2171</a></td>
|
||||||
|
@ -13933,7 +13933,7 @@ and <I>POD class</I></td>
|
||||||
<td><a href="http://wg21.link/cwg2353">2353</a></td>
|
<td><a href="http://wg21.link/cwg2353">2353</a></td>
|
||||||
<td>DR</td>
|
<td>DR</td>
|
||||||
<td>Potential results of a member access expression for a static data member</td>
|
<td>Potential results of a member access expression for a static data member</td>
|
||||||
<td class="none" align="center">Unknown</td>
|
<td class="svn" align="center">SVN</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="2354">
|
<tr id="2354">
|
||||||
<td><a href="http://wg21.link/cwg2354">2354</a></td>
|
<td><a href="http://wg21.link/cwg2354">2354</a></td>
|
||||||
|
|
Loading…
Reference in New Issue