[c++14] Implement missed piece of N3323: use "converted constant" rules
authorRichard Smith <richard@metafoo.co.uk>
Wed, 19 Aug 2020 19:46:52 +0000 (12:46 -0700)
committerRichard Smith <richard@metafoo.co.uk>
Wed, 19 Aug 2020 22:45:51 +0000 (15:45 -0700)
for array bounds, not "integer constant" rules.

For an array bound of class type, this causes us to perform an implicit
conversion to size_t, instead of looking for a unique conversion to
integral or unscoped enumeration type. This affects which cases are
valid when a class has multiple implicit conversion functions to
different types.

clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaType.cpp
clang/test/SemaCXX/cxx1y-contextual-conversion-tweaks.cpp

index fb9f442..49ab94f 100644 (file)
@@ -3213,7 +3213,7 @@ public:
     CCEK_CaseValue,   ///< Expression in a case label.
     CCEK_Enumerator,  ///< Enumerator value with fixed underlying type.
     CCEK_TemplateArg, ///< Value of a non-type template parameter.
-    CCEK_NewExpr,     ///< Constant expression in a noptr-new-declarator.
+    CCEK_ArrayBound,  ///< Array bound in array declarator or new-expression.
     CCEK_ConstexprIf, ///< Condition in a constexpr if statement.
     CCEK_ExplicitBool ///< Condition in an explicit(bool) specifier.
   };
index 335ee95..132f5b0 100644 (file)
@@ -1760,12 +1760,10 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
             // C++1y [expr.new]p6: Every constant-expression in a noptr-new-declarator
             //   shall be a converted constant expression (5.19) of type std::size_t
             //   and shall evaluate to a strictly positive value.
-            unsigned IntWidth = Context.getTargetInfo().getIntWidth();
-            assert(IntWidth && "Builtin type of size 0?");
-            llvm::APSInt Value(IntWidth);
+            llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
             Array.NumElts
              = CheckConvertedConstantExpression(NumElts, Context.getSizeType(), Value,
-                                                CCEK_NewExpr)
+                                                CCEK_ArrayBound)
                  .get();
           } else {
             Array.NumElts
index bcf6aa7..dc00989 100644 (file)
@@ -5628,6 +5628,7 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
     return Result;
 
   // Check for a narrowing implicit conversion.
+  bool ReturnPreNarrowingValue = false;
   APValue PreNarrowingValue;
   QualType PreNarrowingType;
   switch (SCS->getNarrowingKind(S.Context, Result.get(), PreNarrowingValue,
@@ -5642,12 +5643,22 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
     break;
 
   case NK_Constant_Narrowing:
+    if (CCE == Sema::CCEK_ArrayBound &&
+        PreNarrowingType->isIntegralOrEnumerationType() &&
+        PreNarrowingValue.isInt()) {
+      // Don't diagnose array bound narrowing here; we produce more precise
+      // errors by allowing the un-narrowed value through.
+      ReturnPreNarrowingValue = true;
+      break;
+    }
     S.Diag(From->getBeginLoc(), diag::ext_cce_narrowing)
         << CCE << /*Constant*/ 1
         << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << T;
     break;
 
   case NK_Type_Narrowing:
+    // FIXME: It would be better to diagnose that the expression is not a
+    // constant expression.
     S.Diag(From->getBeginLoc(), diag::ext_cce_narrowing)
         << CCE << /*Constant*/ 0 << From->getType() << T;
     break;
@@ -5676,7 +5687,10 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,
 
     if (Notes.empty()) {
       // It's a constant expression.
-      return ConstantExpr::Create(S.Context, Result.get(), Value);
+      Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value);
+      if (ReturnPreNarrowingValue)
+        Value = std::move(PreNarrowingValue);
+      return E;
     }
   }
 
index fb62250..c08d442 100644 (file)
@@ -2229,6 +2229,21 @@ QualType Sema::BuildExtIntType(bool IsUnsigned, Expr *BitWidth,
 static ExprResult checkArraySize(Sema &S, Expr *&ArraySize,
                                  llvm::APSInt &SizeVal, unsigned VLADiag,
                                  bool VLAIsError) {
+  if (S.getLangOpts().CPlusPlus14 &&
+      (VLAIsError ||
+       !ArraySize->getType()->isIntegralOrUnscopedEnumerationType())) {
+    // C++14 [dcl.array]p1:
+    //   The constant-expression shall be a converted constant expression of
+    //   type std::size_t.
+    //
+    // Don't apply this rule if we might be forming a VLA: in that case, we
+    // allow non-constant expressions and constant-folding. We only need to use
+    // the converted constant expression rules (to properly convert the source)
+    // when the source expression is of class type.
+    return S.CheckConvertedConstantExpression(
+        ArraySize, S.Context.getSizeType(), SizeVal, Sema::CCEK_ArrayBound);
+  }
+
   // If the size is an ICE, it certainly isn't a VLA. If we're in a GNU mode
   // (like gnu99, but not c99) accept any evaluatable value as an extension.
   class VLADiagnoser : public Sema::VerifyICEDiagnoser {
index 7cb83c6..19ad70d 100644 (file)
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only %s
-// RUN: %clang_cc1 -std=c++1y %s -verify -DCXX1Y
+// RUN: %clang_cc1 -std=c++11 -verify=expected,cxx11 -fsyntax-only -pedantic-errors %s
+// RUN: %clang_cc1 -std=c++14 -verify=expected,cxx14 -fsyntax-only -pedantic-errors %s -DCXX1Y
 
 // Explicit member declarations behave as in C++11.
 
@@ -155,23 +155,41 @@ namespace extended_examples_cxx1y {
 #endif
 
 namespace extended_examples_array_bounds {
-  
+
   typedef decltype(sizeof(int)) size_t;
-  
-  struct Foo {
-    operator size_t();   // @162
-    operator unsigned short();  // @163
+
+  struct X {
+    constexpr operator size_t() const { return 1; } // cxx11-note 3{{conversion}}
+    constexpr operator unsigned short() const { return 0; } // cxx11-note 3{{conversion}}
   };
 
-  void bar() {
-    Foo x;
-    int *p = new int[x];        // @168
+  void f() {
+    X x;
+    int *p = new int[x]; // cxx11-error {{ambiguous}}
+
+    int arr[x]; // cxx11-error {{ambiguous}}
+    int (*q)[1] = new int[1][x]; // cxx11-error {{ambiguous}}
   }
-}
 
-#ifdef CXX1Y
-#else
-//expected-error@168 {{ambiguous conversion of array size expression of type 'extended_examples_array_bounds::Foo' to an integral or enumeration type}}
-//expected-note@162 {{conversion to integral type 'extended_examples_array_bounds::size_t'}}
-//expected-note@163 {{conversion to integral type 'unsigned short' declared here}}
-#endif
+  struct Y {
+    constexpr operator float() const { return 0.0f; } // cxx14-note 3{{candidate}}
+    constexpr operator int() const { return 1; } // cxx14-note 3{{candidate}}
+  };
+
+  void g() {
+    Y y;
+    int *p = new int[y]; // cxx14-error {{ambiguous}}
+
+    int arr[y]; // cxx14-error {{ambiguous}}
+    int (*q)[1] = new int[1][y]; // cxx14-error {{ambiguous}}
+  }
+
+  template<int N> struct Z {
+    constexpr operator int() const { return N; }
+  };
+  void h() {
+    int arrA[Z<1>()];
+    int arrB[Z<0>()]; // expected-error {{zero size array}}
+    int arrC[Z<-1>()]; // expected-error {{'arrC' declared as an array with a negative size}}
+  }
+}