diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index db18ac49c6ec..233212c76bee 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1077,17 +1077,16 @@ static llvm::Constant *constWithPadding(CodeGenModule &CGM, IsPattern isPattern, 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(DC)) { if (const auto *CC = dyn_cast(FD)) return CC->getNameAsString(); if (const auto *CD = dyn_cast(FD)) return CD->getNameAsString(); - return CGM.getMangledName(FD); + return getMangledName(FD); } else if (const auto *OM = dyn_cast(DC)) { return OM->getNameAsString(); } else if (isa(DC)) { @@ -1095,26 +1094,47 @@ static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl &D, } else if (isa(DC)) { return ""; } else { - llvm::llvm_unreachable_internal("expected a function or method"); + llvm_unreachable("expected a function or method"); } }; - 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); + // 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()); + std::string Name; + if (D.hasGlobalStorage()) + Name = getMangledName(&D).str() + ".const"; + 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); - llvm::Type *BP = llvm::PointerType::getInt8PtrTy(CGM.getLLVMContext(), AS); + 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; @@ -1197,10 +1217,10 @@ static void emitStoresForConstant(CodeGenModule &CGM, const VarDecl &D, } // 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, @@ -1763,10 +1783,10 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { 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"); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index d5ce4870f18f..f0d8619faa30 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1422,10 +1422,11 @@ static ConstantEmissionKind checkVarTypeForConstantEmission(QualType type) { } /// 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(); @@ -2450,33 +2451,97 @@ static LValue EmitGlobalNamedRegister(const VarDecl *VD, CodeGenModule &CGM) { 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(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(ND)) { // Global Named registers access via intrinsics only if (VD->getStorageClass() == SC_Register && VD->hasAttr() && !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. + // 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"); - // 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); + 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. @@ -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 all DeclRefExprs, not just // those with a valid source location. - assert((ND->isUsed(false) || !isa(ND) || + assert((ND->isUsed(false) || !isa(ND) || E->isNonOdrUse() || !E->getLocation().isValid()) && "Should not use decl without marking it used!"); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 8c1bc0777dee..95964afed4ec 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -362,6 +362,10 @@ private: llvm::SmallVector, 8> GlobalValReplacements; + /// Variables for which we've emitted globals containing their constant + /// values along with the corresponding globals, for opportunistic reuse. + llvm::DenseMap 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. @@ -623,6 +627,9 @@ public: StaticLocalDeclGuardMap[D] = C; } + Address createUnnamedGlobalFrom(const VarDecl &D, llvm::Constant *Constant, + CharUnits Align); + bool lookupRepresentativeDecl(StringRef MangledName, GlobalDecl &Result) const; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a8f3ec0ba52b..7b3590aeb1a6 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15806,6 +15806,32 @@ QualType Sema::getCapturedDeclRefType(VarDecl *Var, SourceLocation Loc) { 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 + 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. /// @@ -15897,16 +15923,11 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, // 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: { @@ -15924,52 +15945,107 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, 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(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(E); + // -- If e is a class member access expression [...] naming a non-static + // data member... + if (isa(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(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(E); + auto *PE = cast(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(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... @@ -15988,7 +16064,7 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, // -- 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(E); + auto *GSE = cast(E); SmallVector AssocExprs; 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 // second and third subexpressions. case Expr::ChooseExprClass: { - auto *CE = dyn_cast(E); + auto *CE = cast(E); ExprResult LHS = Rebuild(CE->getLHS()); if (LHS.isInvalid()) @@ -16039,13 +16115,38 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, // Step through non-syntactic nodes. case Expr::ConstantExprClass: { - auto *CE = dyn_cast(E); + auto *CE = cast(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(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; } diff --git a/clang/test/CXX/basic/basic.def.odr/p2.cpp b/clang/test/CXX/basic/basic.def.odr/p2.cpp new file mode 100644 index 000000000000..0ffd08c924fb --- /dev/null +++ b/clang/test/CXX/basic/basic.def.odr/p2.cpp @@ -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); +} diff --git a/clang/test/CXX/drs/dr20xx.cpp b/clang/test/CXX/drs/dr20xx.cpp index 5819c319fd54..90ccd7c055a5 100644 --- a/clang/test/CXX/drs/dr20xx.cpp +++ b/clang/test/CXX/drs/dr20xx.cpp @@ -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++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; }; diff --git a/clang/test/CXX/drs/dr21xx.cpp b/clang/test/CXX/drs/dr21xx.cpp index 2522ff7dbdef..83c59d012028 100644 --- a/clang/test/CXX/drs/dr21xx.cpp +++ b/clang/test/CXX/drs/dr21xx.cpp @@ -8,6 +8,19 @@ #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 {}; @@ -19,6 +32,19 @@ namespace dr2120 { // dr2120: 7 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}} diff --git a/clang/test/CXX/drs/dr23xx.cpp b/clang/test/CXX/drs/dr23xx.cpp index 87db0d4c9b78..8e7a9a880bfb 100644 --- a/clang/test/CXX/drs/dr23xx.cpp +++ b/clang/test/CXX/drs/dr23xx.cpp @@ -1,13 +1,45 @@ -// 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 a = 0; diff --git a/clang/test/CXX/drs/dr6xx.cpp b/clang/test/CXX/drs/dr6xx.cpp index 31642dfbb0ba..530c88f86f88 100644 --- a/clang/test/CXX/drs/dr6xx.cpp +++ b/clang/test/CXX/drs/dr6xx.cpp @@ -1132,3 +1132,20 @@ namespace dr692 { // dr692: no 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 + } +} diff --git a/clang/test/CXX/drs/dr7xx.cpp b/clang/test/CXX/drs/dr7xx.cpp index 2d9d39601872..30cc4b9abe9b 100644 --- a/clang/test/CXX/drs/dr7xx.cpp +++ b/clang/test/CXX/drs/dr7xx.cpp @@ -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 struct A { template struct C; // expected-note 6{{here}} diff --git a/clang/test/CodeGenCXX/no-odr-use.cpp b/clang/test/CodeGenCXX/no-odr-use.cpp new file mode 100644 index 000000000000..ba310046ddc5 --- /dev/null +++ b/clang/test/CodeGenCXX/no-odr-use.cpp @@ -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])(); + } +} diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 663fe584aacc..cda1d2001db6 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -4219,7 +4219,7 @@ and POD class 696 C++11 Use of block-scope constants in local classes - Unknown + Yes 697 @@ -4315,7 +4315,7 @@ and POD class 712 CD3 Are integer constant operands of a conditional-expression “used?” - Unknown + Partial 713 @@ -12313,7 +12313,7 @@ and POD class 2083 DR Incorrect cases of odr-use - Unknown + Partial 2084 @@ -12433,7 +12433,7 @@ and POD class 2103 DR Lvalue-to-rvalue conversion is irrelevant in odr-use of a reference - Unknown + Yes 2104 @@ -12835,7 +12835,7 @@ and POD class 2170 DR Unclear definition of odr-use for arrays - Unknown + SVN 2171 @@ -13933,7 +13933,7 @@ and POD class 2353 DR Potential results of a member access expression for a static data member - Unknown + SVN 2354