]> granicus.if.org Git - clang/commitdiff
PR12226: don't generate wrong code if a braced string literal is used to
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 15 Apr 2012 02:50:59 +0000 (02:50 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sun, 15 Apr 2012 02:50:59 +0000 (02:50 +0000)
initialize an array of unsigned char. Outside C++11 mode, this bug was benign,
and just resulted in us emitting a constant which was double the required
length, padded with 0s. In C++11, it resulted in us generating an array whose
first element was something like  i8 ptrtoint ([n x i8]* @str to i8).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154756 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Expr.h
lib/AST/Expr.cpp
lib/AST/ExprConstant.cpp
lib/CodeGen/CGExprAgg.cpp
lib/CodeGen/CGExprConstant.cpp
test/CodeGenCXX/const-init-cxx11.cpp
test/CodeGenCXX/static-init.cpp
test/SemaCXX/constant-expression-cxx11.cpp

index 558bd00ba978d2666b7a97cc58869ca2c3c53bde..a7822fab1cce2121f51afc7b9063e9ff83cb9f1e 100644 (file)
@@ -3608,6 +3608,10 @@ public:
     return LBraceLoc.isValid() && RBraceLoc.isValid();
   }
 
+  // Is this an initializer for an array of characters, initialized by a string
+  // literal or an @encode?
+  bool isStringLiteralInit() const;
+
   SourceLocation getLBraceLoc() const { return LBraceLoc; }
   void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; }
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
index 868109e3d56e00eef22077411deff90c20ac2bed..9556b1acb94f9b812f83b8c7fa6ca592efc5c0c5 100644 (file)
@@ -1590,6 +1590,16 @@ void InitListExpr::setArrayFiller(Expr *filler) {
       inits[i] = filler;
 }
 
+bool InitListExpr::isStringLiteralInit() const {
+  if (getNumInits() != 1)
+    return false;
+  const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(getType());
+  if (!CAT || !CAT->getElementType()->isIntegerType())
+    return false;
+  const Expr *Init = getInit(0)->IgnoreParenImpCasts();
+  return isa<StringLiteral>(Init) || isa<ObjCEncodeExpr>(Init);
+}
+
 SourceRange InitListExpr::getSourceRange() const {
   if (SyntacticForm)
     return SyntacticForm->getSourceRange();
index 01c9fe7cd846ae3dda5f49fa0c458cf6b2f8e07f..98fc0e5cf3c44274f976ecb057f386f3ebd82cfd 100644 (file)
@@ -1491,15 +1491,19 @@ static unsigned getBaseIndex(const CXXRecordDecl *Derived,
   llvm_unreachable("base class missing from derived class's bases list");
 }
 
-/// Extract the value of a character from a string literal.
+/// Extract the value of a character from a string literal. CharType is used to
+/// determine the expected signedness of the result -- a string literal used to
+/// initialize an array of 'signed char' or 'unsigned char' might contain chars
+/// of the wrong signedness.
 static APSInt ExtractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
-                                            uint64_t Index) {
+                                            uint64_t Index, QualType CharType) {
   // FIXME: Support PredefinedExpr, ObjCEncodeExpr, MakeStringConstant
   const StringLiteral *S = dyn_cast<StringLiteral>(Lit);
   assert(S && "unexpected string literal expression kind");
+  assert(CharType->isIntegerType() && "unexpected character type");
 
   APSInt Value(S->getCharByteWidth() * Info.Ctx.getCharWidth(),
-    Lit->getType()->getArrayElementTypeNoTypeQual()->isUnsignedIntegerType());
+               CharType->isUnsignedIntegerType());
   if (Index < S->getLength())
     Value = S->getCodeUnit(Index);
   return Value;
@@ -1546,7 +1550,7 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
         assert(I == N - 1 && "extracting subobject of character?");
         assert(!O->hasLValuePath() || O->getLValuePath().empty());
         Obj = APValue(ExtractStringLiteralCharacter(
-          Info, O->getLValueBase().get<const Expr*>(), Index));
+          Info, O->getLValueBase().get<const Expr*>(), Index, SubType));
         return true;
       } else if (O->getArrayInitializedElts() > Index)
         O = &O->getArrayInitializedElt(Index);
@@ -3849,8 +3853,7 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
 
   // C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...]
   // an appropriately-typed string literal enclosed in braces.
-  if (E->getNumInits() == 1 && E->getInit(0)->isGLValue() &&
-      Info.Ctx.hasSameUnqualifiedType(E->getType(), E->getInit(0)->getType())) {
+  if (E->isStringLiteralInit()) {
     LValue LV;
     if (!EvaluateLValue(E->getInit(0), LV, Info))
       return false;
index b6efc1cafaaa64a0d1af68ee7d5b8b5e21ee34e4..975f572c0df32617ea56628705ff0c5a979fef08 100644 (file)
@@ -916,14 +916,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
 
   // Handle initialization of an array.
   if (E->getType()->isArrayType()) {
-    if (E->getNumInits() > 0) {
-      QualType T1 = E->getType();
-      QualType T2 = E->getInit(0)->getType();
-      if (CGF.getContext().hasSameUnqualifiedType(T1, T2)) {
-        EmitAggLoadOfLValue(E->getInit(0));
-        return;
-      }
-    }
+    if (E->isStringLiteralInit())
+      return Visit(E->getInit(0));
 
     QualType elementType =
         CGF.getContext().getAsArrayType(E->getType())->getElementType();
index d528e0c4b72aad496aee132a213159c50a0ea506..bc9f9ef07b28a020be68d837b9b40b79ad0fe08f 100644 (file)
@@ -758,17 +758,13 @@ public:
   }
 
   llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) {
-    unsigned NumInitElements = ILE->getNumInits();
-    if (NumInitElements == 1 &&
-        CGM.getContext().hasSameUnqualifiedType(ILE->getType(),
-                                                ILE->getInit(0)->getType()) &&
-        (isa<StringLiteral>(ILE->getInit(0)) ||
-         isa<ObjCEncodeExpr>(ILE->getInit(0))))
+    if (ILE->isStringLiteralInit())
       return Visit(ILE->getInit(0));
 
     llvm::ArrayType *AType =
         cast<llvm::ArrayType>(ConvertType(ILE->getType()));
     llvm::Type *ElemTy = AType->getElementType();
+    unsigned NumInitElements = ILE->getNumInits();
     unsigned NumElements = AType->getNumElements();
 
     // Initialising an array requires us to automatically
index c745deebf04b5ac093aad3d9e20a7fa338f70e77..62a345a495645f8c85a6d84de1ac04717903addc 100644 (file)
@@ -92,6 +92,9 @@ namespace Array {
   // CHECK: @_ZN5Array1cE = constant [6 x [4 x i8]] [{{.*}} c"foo\00", [4 x i8] c"a\00\00\00", [4 x i8] c"bar\00", [4 x i8] c"xyz\00", [4 x i8] c"b\00\00\00", [4 x i8] c"123\00"]
   extern constexpr char c[6][4] = { "foo", "a", { "bar" }, { 'x', 'y', 'z' }, { "b" }, '1', '2', '3' };
 
+  // CHECK: @_ZN5Array2ucE = constant [4 x i8] c"foo\00"
+  extern constexpr unsigned char uc[] = { "foo" };
+
   struct C { constexpr C() : n(5) {} int n, m = 3 * n + 1; };
   // CHECK: @_ZN5Array5ctorsE = constant [3 x {{.*}}] [{{.*}} { i32 5, i32 16 }, {{.*}} { i32 5, i32 16 }, {{.*}} { i32 5, i32 16 }]
   extern const C ctors[3];
index ed659de5e0624d625caed1306f64c4b04358f463..74278f7128c886bfb892b9c9961b87cb7ef21a9d 100644 (file)
@@ -2,6 +2,7 @@
 
 // CHECK: @_ZZ1hvE1i = internal global i32 0, align 4
 // CHECK: @base_req = global [4 x i8] c"foo\00", align 1
+// CHECK: @base_req_uchar = global [4 x i8] c"bar\00", align 1
 
 // CHECK: @_ZZN5test31BC1EvE1u = internal global { i8, [3 x i8] } { i8 97, [3 x i8] undef }, align 4
 // CHECK: @_ZZN5test1L6getvarEiE3var = internal constant [4 x i32] [i32 1, i32 0, i32 2, i32 4], align 16
@@ -64,6 +65,7 @@ namespace test1 {
 
 // Make sure we emit the initializer correctly for the following:
 char base_req[] = { "foo" };
+unsigned char base_req_uchar[] = { "bar" };
 
 namespace union_static_local {
   // CHECK: define internal void @_ZZN18union_static_local4testEvEN1c4mainEv
index 41d214a6364bcdef16ca0a404ea05422b0072ee0..9f80e7169bb143dcff13dd102be5afa3cddfe75c 100644 (file)
@@ -615,6 +615,10 @@ static_assert(agg1.arr[4] == 0, "");
 static_assert(agg1.arr[5] == 0, ""); // expected-error {{constant expression}} expected-note {{read of dereferenced one-past-the-end}}
 static_assert(agg1.p == nullptr, "");
 
+static constexpr const unsigned char uc[] = { "foo" };
+static_assert(uc[0] == 'f', "");
+static_assert(uc[3] == 0, "");
+
 namespace SimpleDerivedClass {
 
 struct B {