This revision was not accepted when it landed; it landed in state "Needs 
Review".
This revision was automatically updated to reflect the committed changes.
Closed by commit rL363295: C++ DR712 and others: handle non-odr-use resulting 
from an lvalue-to-rvalue… (authored by rsmith, committed by ).
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D63157?vs=204589&id=204599#toc

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D63157/new/

https://reviews.llvm.org/D63157

Files:
  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/basic/basic.def.odr/p2.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/test/CodeGenCXX/no-odr-use.cpp
  cfe/trunk/www/cxx_dr_status.html

Index: cfe/trunk/lib/CodeGen/CGDecl.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp
@@ -1077,17 +1077,16 @@
   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 @@
     }
   };
 
-  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 @@
   }
 
   // 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 @@
       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");
Index: cfe/trunk/lib/CodeGen/CGExpr.cpp
===================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp
@@ -1422,10 +1422,11 @@
 }
 
 /// 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 @@
   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 @@
   // 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!");
 
Index: cfe/trunk/lib/CodeGen/CodeGenModule.h
===================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.h
+++ cfe/trunk/lib/CodeGen/CodeGenModule.h
@@ -362,6 +362,10 @@
   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 @@
     StaticLocalDeclGuardMap[D] = C;
   }
 
+  Address createUnnamedGlobalFrom(const VarDecl &D, llvm::Constant *Constant,
+                                  CharUnits Align);
+
   bool lookupRepresentativeDecl(StringRef MangledName,
                                 GlobalDecl &Result) const;
 
Index: cfe/trunk/lib/Sema/SemaExpr.cpp
===================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp
+++ cfe/trunk/lib/Sema/SemaExpr.cpp
@@ -15806,6 +15806,32 @@
   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 @@
 
     // 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 @@
     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 @@
   //   -- 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 @@
   //      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 @@
 
   // 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;
   }
Index: cfe/trunk/www/cxx_dr_status.html
===================================================================
--- cfe/trunk/www/cxx_dr_status.html
+++ cfe/trunk/www/cxx_dr_status.html
@@ -4219,7 +4219,7 @@
     <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 @@
     <td><a href="http://wg21.link/cwg712";>712</a></td>
     <td>CD3</td>
     <td>Are integer constant operands of a <I>conditional-expression</I> &#8220;used?&#8221;</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 @@
     <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 @@
     <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 @@
     <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 @@
     <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>
Index: cfe/trunk/test/CXX/drs/dr6xx.cpp
===================================================================
--- cfe/trunk/test/CXX/drs/dr6xx.cpp
+++ cfe/trunk/test/CXX/drs/dr6xx.cpp
@@ -1132,3 +1132,20 @@
     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
+  }
+}
Index: cfe/trunk/test/CXX/drs/dr23xx.cpp
===================================================================
--- cfe/trunk/test/CXX/drs/dr23xx.cpp
+++ cfe/trunk/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> int a = 0;
Index: cfe/trunk/test/CXX/drs/dr21xx.cpp
===================================================================
--- cfe/trunk/test/CXX/drs/dr21xx.cpp
+++ cfe/trunk/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 @@
   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}}
Index: cfe/trunk/test/CXX/drs/dr20xx.cpp
===================================================================
--- cfe/trunk/test/CXX/drs/dr20xx.cpp
+++ cfe/trunk/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; };
Index: cfe/trunk/test/CXX/drs/dr7xx.cpp
===================================================================
--- cfe/trunk/test/CXX/drs/dr7xx.cpp
+++ cfe/trunk/test/CXX/drs/dr7xx.cpp
@@ -17,6 +17,42 @@
   }
 }
 
+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}}
Index: cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp
===================================================================
--- cfe/trunk/test/CXX/basic/basic.def.odr/p2.cpp
+++ cfe/trunk/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);
+}
Index: cfe/trunk/test/CodeGenCXX/no-odr-use.cpp
===================================================================
--- cfe/trunk/test/CodeGenCXX/no-odr-use.cpp
+++ cfe/trunk/test/CodeGenCXX/no-odr-use.cpp
@@ -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);
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D63157: C... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D631... John McCall via Phabricator via cfe-commits
    • [PATCH] D631... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D631... Richard Smith - zygoloid via Phabricator via cfe-commits
    • [PATCH] D631... John McCall via Phabricator via cfe-commits
    • [PATCH] D631... Richard Smith - zygoloid via Phabricator via cfe-commits

Reply via email to