From a7ad98ff0919d6a24ea7c46634ea29bea551c1a0 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Mon, 11 Feb 2008 00:02:17 +0000 Subject: [PATCH] Fix PR1992 by computing the right type for string literals, which is an array type not a pointer type. This requires updating some diags that change and updating the code generator to handle the proper form of strings. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@46941 91177308-0d34-0410-b5e6-96231b3b80d8 --- CodeGen/CGExprConstant.cpp | 16 +++++----------- CodeGen/CodeGenModule.cpp | 9 +++------ CodeGen/CodeGenModule.h | 3 +++ Sema/SemaExpr.cpp | 24 ++++++++++++++---------- include/clang/AST/Expr.h | 3 ++- test/Sema/argument-checking.m | 2 +- test/Sema/array-init.c | 10 +++++----- test/Sema/compound-literal.c | 2 +- test/Sema/i-c-e2.c | 12 +++++++++++- 9 files changed, 45 insertions(+), 36 deletions(-) diff --git a/CodeGen/CGExprConstant.cpp b/CodeGen/CGExprConstant.cpp index 8c319a0eb3..569547549f 100644 --- a/CodeGen/CGExprConstant.cpp +++ b/CodeGen/CGExprConstant.cpp @@ -205,9 +205,7 @@ public: // Note that VLAs can't exist for global variables. // The only thing that can have array type like this is a // DeclRefExpr(FileVarDecl)? - const DeclRefExpr *DRE = cast(ICExpr->getSubExpr()); - const VarDecl *VD = cast(DRE->getDecl()); - llvm::Constant *C = CGM.GetAddrOfGlobalVar(VD, false); + llvm::Constant *C = EmitLValue(ICExpr->getSubExpr()); assert(isa(C->getType()) && isa(cast(C->getType()) ->getElementType())); @@ -232,11 +230,7 @@ public: llvm::Constant *VisitStringLiteral(StringLiteral *E) { const char *StrData = E->getStrData(); unsigned Len = E->getByteLength(); - - // If the string has a pointer type, emit it as a global and use the pointer - // to the global as its value. - if (E->getType()->isPointerType()) - return CGM.GetAddrOfConstantString(std::string(StrData, StrData + Len)); + assert(!E->getType()->isPointerType() && "Strings are always arrays"); // Otherwise this must be a string initializing an array in a static // initializer. Don't emit it as the address of the string, emit the string @@ -532,7 +526,7 @@ public: // to be the only use of the variable, so we just generate it here. CompoundLiteralExpr *CLE = cast(E); llvm::Constant* C = Visit(CLE->getInitializer()); - C = new llvm::GlobalVariable(C->getType(), E->getType().isConstQualified(), + C = new llvm::GlobalVariable(C->getType(),E->getType().isConstQualified(), llvm::GlobalValue::InternalLinkage, C, ".compoundliteral", &CGM.getModule()); return C; @@ -541,8 +535,8 @@ public: ValueDecl *Decl = cast(E)->getDecl(); if (const FunctionDecl *FD = dyn_cast(Decl)) return CGM.GetAddrOfFunctionDecl(FD, false); - if (const FileVarDecl* FVD = dyn_cast(Decl)) - return CGM.GetAddrOfGlobalVar(FVD, false); + if (const VarDecl* VD = dyn_cast(Decl)) + return CGM.GetAddrOfGlobalVar(VD, false); // We can end up here with static block-scope variables (and others?) // FIXME: How do we implement block-scope variables?! assert(0 && "Unimplemented Decl type"); diff --git a/CodeGen/CodeGenModule.cpp b/CodeGen/CodeGenModule.cpp index e1aec690e9..ef05586fcc 100644 --- a/CodeGen/CodeGenModule.cpp +++ b/CodeGen/CodeGenModule.cpp @@ -384,7 +384,7 @@ GetAddrOfConstantCFString(const std::string &str) { return GV; } -/// GenerateWritableString -- Creates storage for a string literal +/// GenerateWritableString -- Creates storage for a string literal. static llvm::Constant *GenerateStringLiteral(const std::string &str, bool constant, CodeGenModule &CGM) { @@ -395,14 +395,11 @@ static llvm::Constant *GenerateStringLiteral(const std::string &str, C = new llvm::GlobalVariable(C->getType(), constant, llvm::GlobalValue::InternalLinkage, C, ".str", &CGM.getModule()); - llvm::Constant *Zero = llvm::Constant::getNullValue(llvm::Type::Int32Ty); - llvm::Constant *Zeros[] = { Zero, Zero }; - C = llvm::ConstantExpr::getGetElementPtr(C, Zeros, 2); return C; } -/// CodeGenModule::GetAddrOfConstantString -- returns a pointer to the first -/// element of a character array containing the literal. +/// CodeGenModule::GetAddrOfConstantString -- returns a pointer to the character +/// array containing the literal. The result is pointer to array type. llvm::Constant *CodeGenModule::GetAddrOfConstantString(const std::string &str) { // Don't share any string literals if writable-strings is turned on. if (Features.WritableStrings) diff --git a/CodeGen/CodeGenModule.h b/CodeGen/CodeGenModule.h index 7c236e0ce8..92060887af 100644 --- a/CodeGen/CodeGenModule.h +++ b/CodeGen/CodeGenModule.h @@ -80,6 +80,9 @@ public: /// llvm::Function *getBuiltinLibFunction(unsigned BuiltinID); llvm::Constant *GetAddrOfConstantCFString(const std::string& str); + + /// GetAddrOfConstantString -- returns a pointer to the character + /// array containing the literal. The result is pointer to array type. llvm::Constant *GetAddrOfConstantString(const std::string& str); llvm::Function *getMemCpyFn(); llvm::Function *getIntrinsic(unsigned IID, const llvm::Type **Tys = 0, diff --git a/Sema/SemaExpr.cpp b/Sema/SemaExpr.cpp index 8764a5e829..0d48ad7aa5 100644 --- a/Sema/SemaExpr.cpp +++ b/Sema/SemaExpr.cpp @@ -42,23 +42,27 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks) { llvm::SmallVector StringTokLocs; for (unsigned i = 0; i != NumStringToks; ++i) StringTokLocs.push_back(StringToks[i].getLocation()); - - // FIXME: handle wchar_t - QualType t; - - if (Literal.Pascal) - t = Context.getPointerType(Context.UnsignedCharTy); - else - t = Context.getPointerType(Context.CharTy); - + + // Verify that pascal strings aren't too large. if (Literal.Pascal && Literal.GetStringLength() > 256) return Diag(StringToks[0].getLocation(), diag::err_pascal_string_too_long, SourceRange(StringToks[0].getLocation(), StringToks[NumStringToks-1].getLocation())); + QualType StrTy = Context.CharTy; + // FIXME: handle wchar_t + if (Literal.Pascal) StrTy = Context.UnsignedCharTy; + + // Get an array type for the string, according to C99 6.4.5. This includes + // the nul terminator character as well as the string length for pascal + // strings. + StrTy = Context.getConstantArrayType(StrTy, + llvm::APInt(32, Literal.GetStringLength()+1), + ArrayType::Normal, 0); + // Pass &StringTokLocs[0], StringTokLocs.size() to factory! return new StringLiteral(Literal.GetString(), Literal.GetStringLength(), - Literal.AnyWide, t, + Literal.AnyWide, StrTy, StringToks[0].getLocation(), StringToks[NumStringToks-1].getLocation()); } diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index c667a89ed9..ae1ae6c644 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -319,7 +319,8 @@ public: /// StringLiteral - This represents a string literal expression, e.g. "foo" /// or L"bar" (wide strings). The actual string is returned by getStrData() /// is NOT null-terminated, and the length of the string is determined by -/// calling getByteLength(). +/// calling getByteLength(). The C type for a string is always a +/// ConstantArrayType. class StringLiteral : public Expr { const char *StrData; unsigned ByteLength; diff --git a/test/Sema/argument-checking.m b/test/Sema/argument-checking.m index 45eba87804..820a2f09c7 100644 --- a/test/Sema/argument-checking.m +++ b/test/Sema/argument-checking.m @@ -17,7 +17,7 @@ void test() { struct S sInst; charStarFunc(1); // expected-warning {{incompatible integer to pointer conversion passing 'int', expected 'char *'}} - charFunc("abc"); // expected-warning {{incompatible pointer to integer conversion passing 'char *', expected 'char'}} + charFunc("abc"); // expected-warning {{incompatible pointer to integer conversion passing 'char [4]', expected 'char'}} [obj charStarMeth:1]; // expected-warning {{incompatible integer to pointer conversion sending 'int'}} [obj structMeth:1]; // expected-error {{incompatible type sending 'int'}} diff --git a/test/Sema/array-init.c b/test/Sema/array-init.c index cfbc2b926a..849737e49e 100644 --- a/test/Sema/array-init.c +++ b/test/Sema/array-init.c @@ -9,7 +9,7 @@ int ary2[] = { x, y, z }; // expected-error{{initializer element is not constant extern int fileScopeExtern[3] = { 1, 3, 5 }; // expected-warning{{'extern' variable has an initializer}} -static int ary3[] = { 1, "abc", 3, 4 }; // expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} +static int ary3[] = { 1, "abc", 3, 4 }; // expected-warning{{incompatible pointer to integer conversion initializing 'char [4]', expected 'int'}} void func() { int x = 1; @@ -48,7 +48,7 @@ void func() { extern int blockScopeExtern[3] = { 1, 3, 5 }; // expected-error{{'extern' variable cannot have an initializer}} - static int x2[3] = { 1.0, "abc" , 5.8 }; // expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} + static int x2[3] = { 1.0, "abc" , 5.8 }; // expected-warning{{incompatible pointer to integer conversion initializing 'char [4]', expected 'int'}} } void test() { @@ -152,10 +152,10 @@ void charArrays() char c[] = { "Hello" }; int l[sizeof(c) == 6 ? 1 : -1]; - int i[] = { "Hello "}; // expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} + int i[] = { "Hello "}; // expected-warning{{incompatible pointer to integer conversion initializing 'char [7]', expected 'int'}} char c2[] = { "Hello", "Good bye" }; //expected-error{{excess elements in char array initializer}} - int i2[1] = { "Hello" }; //expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} + int i2[1] = { "Hello" }; //expected-warning{{incompatible pointer to integer conversion initializing 'char [6]', expected 'int'}} char c3[5] = { "Hello" }; char c4[4] = { "Hello" }; //expected-warning{{initializer-string for char array is too long}} @@ -187,7 +187,7 @@ void autoStructTest() { struct s1 {char a; char b;} t1; struct s2 {struct s1 c;} t2 = { t1 }; // The following is a less than great diagnostic (though it's on par with EDG). -struct s1 t3[] = {t1, t1, "abc", 0}; //expected-warning{{incompatible pointer to integer conversion initializing 'char *', expected 'char'}} +struct s1 t3[] = {t1, t1, "abc", 0}; //expected-warning{{incompatible pointer to integer conversion initializing 'char [4]', expected 'char'}} int t4[sizeof t3 == 6 ? 1 : -1]; } struct foo { int z; } w; diff --git a/test/Sema/compound-literal.c b/test/Sema/compound-literal.c index 1a0394578a..83657245f5 100644 --- a/test/Sema/compound-literal.c +++ b/test/Sema/compound-literal.c @@ -9,7 +9,7 @@ static int *p = (int []){2,4}; static int x = (int){1}; // -expected-error {{initializer element is not constant}} -expected-warning{{braces around scalar initializer}} static int *p2 = (int []){2,x}; // -expected-error {{initializer element is not constant}} -static int *p3 = (int []){2,"x"}; // -expected-warning {{incompatible pointer to integer conversion initializing 'char *', expected 'int'}} +static int *p3 = (int []){2,"x"}; // -expected-warning {{incompatible pointer to integer conversion initializing 'char [2]', expected 'int'}} typedef struct Test {int a;int b;} Test; static Test* ll = &(Test) {0,0}; diff --git a/test/Sema/i-c-e2.c b/test/Sema/i-c-e2.c index 1fd4c21a0c..1afb7c90c2 100644 --- a/test/Sema/i-c-e2.c +++ b/test/Sema/i-c-e2.c @@ -1,6 +1,16 @@ -// RUN: clang %s -fsyntax-only +// RUN: clang %s -fsyntax-only -fpascal-strings char array[1024/(sizeof (long))]; int x['\xBb' == (char) 187 ? 1: -1]; +// PR1992 +void func(int x) +{ + switch (x) { + case sizeof("abc"): break; + case sizeof("loooong"): func(4); + case sizeof("\ploooong"): func(4); + } +} + -- 2.40.0