Hi, this made clang segfault on some inputs seen in ANGLE. I filed PR42276 with a reduced repro and reverted this (and the two dependent changes) in r363352 for now.
Nico On Thu, Jun 13, 2019 at 2:56 PM Richard Smith via cfe-commits < cfe-commits@lists.llvm.org> wrote: > Author: rsmith > Date: Thu Jun 13 12:00:16 2019 > New Revision: 363295 > > URL: http://llvm.org/viewvc/llvm-project?rev=363295&view=rev > Log: > 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. > > Reviewers: rjmccall > > Subscribers: jdoerfert, cfe-commits > > Tags: #clang > > Differential Revision: https://reviews.llvm.org/D63157 > > Added: > cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp > cfe/trunk/test/CodeGenCXX/no-odr-use.cpp > Modified: > cfe/trunk/lib/CodeGen/CGDecl.cpp > cfe/trunk/lib/CodeGen/CGExpr.cpp > cfe/trunk/lib/CodeGen/CodeGenModule.h > cfe/trunk/lib/Sema/SemaExpr.cpp > cfe/trunk/test/CXX/drs/dr20xx.cpp > cfe/trunk/test/CXX/drs/dr21xx.cpp > cfe/trunk/test/CXX/drs/dr23xx.cpp > cfe/trunk/test/CXX/drs/dr6xx.cpp > cfe/trunk/test/CXX/drs/dr7xx.cpp > cfe/trunk/www/cxx_dr_status.html > > Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/lib/CodeGen/CGDecl.cpp (original) > +++ cfe/trunk/lib/CodeGen/CGDecl.cpp Thu Jun 13 12:00:16 2019 > @@ -1077,17 +1077,16 @@ static llvm::Constant *constWithPadding( > 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)) { > @@ -1099,22 +1098,39 @@ static Address createUnnamedGlobalFrom(C > } > }; > > - 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; > @@ -1197,10 +1213,10 @@ static void emitStoresForConstant(CodeGe > } > > // 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 +1779,10 @@ void CodeGenFunction::EmitAutoVarInit(co > 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"); > > Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/lib/CodeGen/CGExpr.cpp (original) > +++ cfe/trunk/lib/CodeGen/CGExpr.cpp Thu Jun 13 12:00:16 2019 > @@ -1422,10 +1422,11 @@ static ConstantEmissionKind checkVarType > } > > /// 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(co > 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. > @@ -2512,7 +2577,7 @@ LValue CodeGenFunction::EmitDeclRefLValu > // 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!"); > > > Modified: cfe/trunk/lib/CodeGen/CodeGenModule.h > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.h?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/lib/CodeGen/CodeGenModule.h (original) > +++ cfe/trunk/lib/CodeGen/CodeGenModule.h Thu Jun 13 12:00:16 2019 > @@ -362,6 +362,10 @@ private: > 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. > @@ -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; > > > Modified: cfe/trunk/lib/Sema/SemaExpr.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) > +++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu Jun 13 12:00:16 2019 > @@ -15806,6 +15806,32 @@ QualType Sema::getCapturedDeclRefType(Va > 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. > /// > @@ -15897,16 +15923,11 @@ static ExprResult rebuildPotentialResult > > // 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 rebuildPotentialResult > 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... > @@ -15988,7 +16064,7 @@ static ExprResult rebuildPotentialResult > // -- 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; > @@ -16016,7 +16092,7 @@ static ExprResult rebuildPotentialResult > // 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()) > @@ -16039,13 +16115,38 @@ static ExprResult rebuildPotentialResult > > // 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; > } > > Added: cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp?rev=363295&view=auto > > ============================================================================== > --- cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp (added) > +++ cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp Thu Jun 13 12:00:16 2019 > @@ -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); > +} > > Modified: cfe/trunk/test/CXX/drs/dr20xx.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr20xx.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/test/CXX/drs/dr20xx.cpp (original) > +++ cfe/trunk/test/CXX/drs/dr20xx.cpp Thu Jun 13 12:00:16 2019 > @@ -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; }; > > Modified: cfe/trunk/test/CXX/drs/dr21xx.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr21xx.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/test/CXX/drs/dr21xx.cpp (original) > +++ cfe/trunk/test/CXX/drs/dr21xx.cpp Thu Jun 13 12:00:16 2019 > @@ -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}} > > Modified: cfe/trunk/test/CXX/drs/dr23xx.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr23xx.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/test/CXX/drs/dr23xx.cpp (original) > +++ cfe/trunk/test/CXX/drs/dr23xx.cpp Thu Jun 13 12:00:16 2019 > @@ -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> int a = 0; > > Modified: cfe/trunk/test/CXX/drs/dr6xx.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr6xx.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/test/CXX/drs/dr6xx.cpp (original) > +++ cfe/trunk/test/CXX/drs/dr6xx.cpp Thu Jun 13 12:00:16 2019 > @@ -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 > + } > +} > > Modified: cfe/trunk/test/CXX/drs/dr7xx.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/drs/dr7xx.cpp?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/test/CXX/drs/dr7xx.cpp (original) > +++ cfe/trunk/test/CXX/drs/dr7xx.cpp Thu Jun 13 12:00:16 2019 > @@ -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<typename T> struct C; // expected-note 6{{here}} > > Added: cfe/trunk/test/CodeGenCXX/no-odr-use.cpp > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no-odr-use.cpp?rev=363295&view=auto > > ============================================================================== > --- cfe/trunk/test/CodeGenCXX/no-odr-use.cpp (added) > +++ cfe/trunk/test/CodeGenCXX/no-odr-use.cpp Thu Jun 13 12:00:16 2019 > @@ -0,0 +1,27 @@ > +// 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); > +} > > Modified: cfe/trunk/www/cxx_dr_status.html > URL: > http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_dr_status.html?rev=363295&r1=363294&r2=363295&view=diff > > ============================================================================== > --- cfe/trunk/www/cxx_dr_status.html (original) > +++ cfe/trunk/www/cxx_dr_status.html Thu Jun 13 12:00:16 2019 > @@ -4219,7 +4219,7 @@ and <I>POD class</I></td> > <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> > @@ -4315,7 +4315,7 @@ and <I>POD class</I></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> > @@ -12313,7 +12313,7 @@ and <I>POD class</I></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> > @@ -12433,7 +12433,7 @@ and <I>POD class</I></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> > @@ -12835,7 +12835,7 @@ and <I>POD class</I></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> > @@ -13933,7 +13933,7 @@ and <I>POD class</I></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> > > > _______________________________________________ > cfe-commits mailing list > cfe-commits@lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits