return constant;
}
-static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl &D,
- CGBuilderTy &Builder,
- llvm::Constant *Constant,
- CharUnits Align) {
+Address CodeGenModule::createUnnamedGlobalFrom(const VarDecl &D,
+ llvm::Constant *Constant,
+ CharUnits Align) {
auto FunctionName = [&](const DeclContext *DC) -> std::string {
if (const auto *FD = dyn_cast<FunctionDecl>(DC)) {
if (const auto *CC = dyn_cast<CXXConstructorDecl>(FD))
return CC->getNameAsString();
if (const auto *CD = dyn_cast<CXXDestructorDecl>(FD))
return CD->getNameAsString();
- return CGM.getMangledName(FD);
+ return getMangledName(FD);
} else if (const auto *OM = dyn_cast<ObjCMethodDecl>(DC)) {
return OM->getNameAsString();
} else if (isa<BlockDecl>(DC)) {
}
};
- auto *Ty = Constant->getType();
- bool isConstant = true;
- llvm::GlobalVariable *InsertBefore = nullptr;
- unsigned AS = CGM.getContext().getTargetAddressSpace(
- CGM.getStringLiteralAddressSpace());
- llvm::GlobalVariable *GV = new llvm::GlobalVariable(
- CGM.getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
- Constant,
- "__const." + FunctionName(D.getParentFunctionOrMethod()) + "." +
- D.getName(),
- InsertBefore, llvm::GlobalValue::NotThreadLocal, AS);
- GV->setAlignment(Align.getQuantity());
- GV->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
-
- Address SrcPtr = Address(GV, Align);
- llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(), AS);
+ // Form a simple per-variable cache of these values in case we find we
+ // want to reuse them.
+ llvm::GlobalVariable *&CacheEntry = InitializerConstants[&D];
+ if (!CacheEntry || CacheEntry->getInitializer() != Constant) {
+ auto *Ty = Constant->getType();
+ bool isConstant = true;
+ llvm::GlobalVariable *InsertBefore = nullptr;
+ unsigned AS =
+ getContext().getTargetAddressSpace(getStringLiteralAddressSpace());
+ llvm::GlobalVariable *GV = new llvm::GlobalVariable(
+ getModule(), Ty, isConstant, llvm::GlobalValue::PrivateLinkage,
+ Constant,
+ "__const." + FunctionName(D.getParentFunctionOrMethod()) + "." +
+ D.getName(),
+ 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());
+ }
+
+ return Address(CacheEntry, Align);
+}
+
+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)
SrcPtr = Builder.CreateBitCast(SrcPtr, BP);
return SrcPtr;
}
// Copy from a global.
- Builder.CreateMemCpy(
- Loc,
- createUnnamedGlobalFrom(CGM, D, Builder, constant, Loc.getAlignment()),
- SizeVal, isVolatile);
+ Builder.CreateMemCpy(Loc,
+ createUnnamedGlobalForMemcpyFrom(
+ CGM, D, Builder, constant, Loc.getAlignment()),
+ SizeVal, isVolatile);
}
static void emitStoresForZeroInit(CodeGenModule &CGM, const VarDecl &D,
llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2, "vla.cur");
Cur->addIncoming(Begin.getPointer(), OriginBB);
CharUnits CurAlign = Loc.getAlignment().alignmentOfArrayElement(EltSize);
- Builder.CreateMemCpy(
- Address(Cur, CurAlign),
- createUnnamedGlobalFrom(CGM, D, Builder, Constant, ConstantAlign),
- BaseSizeInChars, isVolatile);
+ Builder.CreateMemCpy(Address(Cur, CurAlign),
+ createUnnamedGlobalForMemcpyFrom(
+ CGM, D, Builder, Constant, ConstantAlign),
+ BaseSizeInChars, isVolatile);
llvm::Value *Next =
Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars, "vla.next");
llvm::Value *Done = Builder.CreateICmpEQ(Next, End, "vla-init.isdone");
}
/// 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
-/// produce an l-value for variables that we never actually captured
-/// in a block or lambda, which means const int variables or constexpr
-/// literals or similar.
+/// an l-value. This is just an optimization, but it avoids us needing
+/// to emit global copies of variables if they're named without triggering
+/// a formal use in a context where we can't emit a direct reference to them,
+/// 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::tryEmitAsConstant(DeclRefExpr *refExpr) {
ValueDecl *value = refExpr->getDecl();
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) {
const NamedDecl *ND = E->getDecl();
QualType T = E->getType();
+ assert(E->isNonOdrUse() != NOUR_Unevaluated &&
+ "should not emit an unevaluated operand");
+
if (const auto *VD = dyn_cast<VarDecl>(ND)) {
// Global Named registers access via intrinsics only
if (VD->getStorageClass() == SC_Register &&
VD->hasAttr<AsmLabelAttr>() && !VD->isLocalVarDecl())
return EmitGlobalNamedRegister(VD, CGM);
- // A DeclRefExpr for a reference initialized by a constant expression can
- // appear without being odr-used. Directly emit the constant initializer.
- VD->getAnyInitializer(VD);
- if (E->isNonOdrUse() == NOUR_Constant && VD->getType()->isReferenceType()) {
- llvm::Constant *Val =
- ConstantEmitter(*this).emitAbstract(E->getLocation(),
- *VD->evaluateValue(),
- VD->getType());
- assert(Val && "failed to emit reference constant expression");
- // FIXME: Eventually we will want to emit vector element references.
-
- // Should we be using the alignment of the constant pointer we emitted?
- CharUnits Alignment = getNaturalTypeAlignment(E->getType(),
- /* BaseInfo= */ nullptr,
- /* TBAAInfo= */ nullptr,
- /* forPointeeType= */ true);
- return MakeAddrLValue(Address(Val, Alignment), T, AlignmentSource::Decl);
+ // If this DeclRefExpr does not constitute an odr-use of the variable,
+ // we're not permitted to emit a reference to it in general, and it might
+ // not be captured if capture would be necessary for a use. Emit the
+ // constant value directly instead.
+ if (E->isNonOdrUse() == NOUR_Constant &&
+ (VD->getType()->isReferenceType() ||
+ !canEmitSpuriousReferenceToVariable(*this, E, VD, true))) {
+ VD->getAnyInitializer(VD);
+ llvm::Constant *Val = ConstantEmitter(*this).emitAbstract(
+ E->getLocation(), *VD->evaluateValue(), VD->getType());
+ assert(Val && "failed to emit constant expression");
+
+ Address Addr = Address::invalid();
+ if (!VD->getType()->isReferenceType()) {
+ // Spill the constant value to a global.
+ Addr = CGM.createUnnamedGlobalFrom(*VD, Val,
+ getContext().getDeclAlign(VD));
+ } 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: We should be able to assert this for FunctionDecls as well!
// FIXME: We should be able to assert this for all DeclRefExprs, not just
// 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()) &&
"Should not use decl without marking it used!");
llvm::SmallVector<std::pair<llvm::GlobalValue *, llvm::Constant *>, 8>
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.
/// Required to not issue a warning (on a mangling conflict) multiple times
/// for the same decl.
StaticLocalDeclGuardMap[D] = C;
}
+ Address createUnnamedGlobalFrom(const VarDecl &D, llvm::Constant *Constant,
+ CharUnits Align);
+
bool lookupRepresentativeDecl(StringRef MangledName,
GlobalDecl &Result) const;
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
/// non-odr-uses if they satisfy the side-conditions of the NonOdrUseReason.
///
// Rebuild as a non-odr-use DeclRefExpr.
MarkNotOdrUsed();
- TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
- if (DRE->hasExplicitTemplateArgs()) {
- DRE->copyTemplateArgumentsInto(TemplateArgStorage);
- TemplateArgs = &TemplateArgStorage;
- }
return DeclRefExpr::Create(
S.Context, DRE->getQualifierLoc(), DRE->getTemplateKeywordLoc(),
DRE->getDecl(), DRE->refersToEnclosingVariableOrCapture(),
DRE->getNameInfo(), DRE->getType(), DRE->getValueKind(),
- DRE->getFoundDecl(), TemplateArgs, NOUR);
+ DRE->getFoundDecl(), CopiedTemplateArgs(DRE), NOUR);
}
case Expr::FunctionParmPackExprClass: {
break;
}
- // FIXME: Implement these.
// -- If e is a subscripting operation with an array operand...
- // -- If e is a class member access expression [...] naming a non-static
- // data member...
+ case Expr::ArraySubscriptExprClass: {
+ 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: {
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())
- // FIXME: Recurse to the left-hand side.
break;
+ // -- If e is a class member access expression naming a static data member,
+ // ...
if (ME->isNonOdrUse() || IsPotentialResultOdrUsed(ME->getMemberDecl()))
break;
// Rebuild as a non-odr-use MemberExpr.
MarkNotOdrUsed();
- TemplateArgumentListInfo TemplateArgStorage, *TemplateArgs = nullptr;
- if (ME->hasExplicitTemplateArgs()) {
- ME->copyTemplateArgumentsInto(TemplateArgStorage);
- TemplateArgs = &TemplateArgStorage;
- }
return MemberExpr::Create(
S.Context, ME->getBase(), ME->isArrow(), ME->getOperatorLoc(),
ME->getQualifierLoc(), ME->getTemplateKeywordLoc(), ME->getMemberDecl(),
- ME->getFoundDecl(), ME->getMemberNameInfo(), TemplateArgs,
+ ME->getFoundDecl(), ME->getMemberNameInfo(), CopiedTemplateArgs(ME),
ME->getType(), ME->getValueKind(), ME->getObjectKind(), NOUR);
return ExprEmpty();
}
- // FIXME: Implement this.
- // -- If e is a pointer-to-member expression of the form e1 .* e2 ...
+ case Expr::BinaryOperatorClass: {
+ 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)...
case Expr::ParenExprClass: {
- auto *PE = dyn_cast<ParenExpr>(E);
+ auto *PE = cast<ParenExpr>(E);
ExprResult Sub = Rebuild(PE->getSubExpr());
if (!Sub.isUsable())
return Sub;
return S.ActOnParenExpr(PE->getLParen(), PE->getRParen(), Sub.get());
}
- // FIXME: Implement these.
// -- 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]
// -- If e has the form __extension__ e1...
// -- If e has the form _Generic(...), the set of potential results is the
// union of the sets of potential results of the associated expressions.
case Expr::GenericSelectionExprClass: {
- auto *GSE = dyn_cast<GenericSelectionExpr>(E);
+ auto *GSE = cast<GenericSelectionExpr>(E);
SmallVector<Expr *, 4> AssocExprs;
bool AnyChanged = false;
// results is the union of the sets of potential results of the
// second and third subexpressions.
case Expr::ChooseExprClass: {
- auto *CE = dyn_cast<ChooseExpr>(E);
+ auto *CE = cast<ChooseExpr>(E);
ExprResult LHS = Rebuild(CE->getLHS());
if (LHS.isInvalid())
// Step through non-syntactic nodes.
case Expr::ConstantExprClass: {
- auto *CE = dyn_cast<ConstantExpr>(E);
+ auto *CE = cast<ConstantExpr>(E);
ExprResult Sub = Rebuild(CE->getSubExpr());
if (!Sub.isUsable())
return Sub;
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:
break;
}
--- /dev/null
+// 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);
+}
// 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
-// expected-no-diagnostics
-
#if __cplusplus < 201103L
#define static_assert(...) _Static_assert(__VA_ARGS__)
#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
struct A { int n; };
struct B { volatile int n; };
#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
#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
struct A {};
struct B : A {};
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
class A {
A &operator=(const A &); // expected-note 0-2{{here}}
-// RUN: %clang_cc1 -std=c++98 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++11 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++2a %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 2>&1 | FileCheck %s
+// 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 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -std=c++2a %s -verify -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s
#if __cplusplus <= 201103L
// expected-no-diagnostics
#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
#if __cplusplus >= 201402L
template<int> int a = 0;
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
+ }
+}
}
}
+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
struct A {
template<typename T> struct C; // expected-note 6{{here}}
--- /dev/null
+// RUN: %clang_cc1 -emit-llvm -o - -triple x86_64-linux-gnu %s | FileCheck %s
+
+// CHECK: @__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] }
+
+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);
+}
<td><a href="http://wg21.link/cwg696">696</a></td>
<td>C++11</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 class="open" id="697">
<td><a href="http://wg21.link/cwg697">697</a></td>
<td><a href="http://wg21.link/cwg712">712</a></td>
<td>CD3</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 id="713">
<td><a href="http://wg21.link/cwg713">713</a></td>
<td><a href="http://wg21.link/cwg2083">2083</a></td>
<td>DR</td>
<td>Incorrect cases of odr-use</td>
- <td class="none" align="center">Unknown</td>
+ <td class="partial" align="center">Partial</td>
</tr>
<tr id="2084">
<td><a href="http://wg21.link/cwg2084">2084</a></td>
<td><a href="http://wg21.link/cwg2103">2103</a></td>
<td>DR</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 id="2104">
<td><a href="http://wg21.link/cwg2104">2104</a></td>
<td><a href="http://wg21.link/cwg2170">2170</a></td>
<td>DR</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 id="2171">
<td><a href="http://wg21.link/cwg2171">2171</a></td>
<td><a href="http://wg21.link/cwg2353">2353</a></td>
<td>DR</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 id="2354">
<td><a href="http://wg21.link/cwg2354">2354</a></td>