Skip to content

Commit 3bf72d7

Browse files
committed
[Sema] Make string literal init an rvalue.
This allows substantially simplifying the expression evaluation code, because we don't have to special-case lvalues which are actually string literal initialization. This currently throws away an optimization where we would avoid creating an array APValue for string literal initialization. If we really want to optimize this case, we should fix APValue so it can store simple arrays more efficiently, like llvm::ConstantDataArray. This shouldn't affect the memory usage for other string literals. (Not sure if this is a blocker; I don't think string literal init is common enough for this to be a serious issue, but I could be wrong.) The change to test/CodeGenObjC/encode-test.m is a weird side-effect of these changes: we currently don't constant-evaluate arrays in C, so the strlen call shouldn't be folded, but lvalue string init managed to get around that check. I this this is fine. Fixes https://bugs.llvm.org/show_bug.cgi?id=40430 . llvm-svn: 353569
1 parent 57e60a5 commit 3bf72d7

File tree

6 files changed

+40
-59
lines changed

6 files changed

+40
-59
lines changed

clang/lib/AST/ExprConstant.cpp

+25-44
Original file line numberDiff line numberDiff line change
@@ -2688,9 +2688,11 @@ static APSInt extractStringLiteralCharacter(EvalInfo &Info, const Expr *Lit,
26882688
}
26892689

26902690
// Expand a string literal into an array of characters.
2691-
static void expandStringLiteral(EvalInfo &Info, const Expr *Lit,
2691+
//
2692+
// FIXME: This is inefficient; we should probably introduce something similar
2693+
// to the LLVM ConstantDataArray to make this cheaper.
2694+
static void expandStringLiteral(EvalInfo &Info, const StringLiteral *S,
26922695
APValue &Result) {
2693-
const StringLiteral *S = cast<StringLiteral>(Lit);
26942696
const ConstantArrayType *CAT =
26952697
Info.Ctx.getAsConstantArrayType(S->getType());
26962698
assert(CAT && "string literal isn't an array");
@@ -2884,18 +2886,6 @@ findSubobject(EvalInfo &Info, const Expr *E, const CompleteObject &Obj,
28842886

28852887
ObjType = CAT->getElementType();
28862888

2887-
// An array object is represented as either an Array APValue or as an
2888-
// LValue which refers to a string literal.
2889-
if (O->isLValue()) {
2890-
assert(I == N - 1 && "extracting subobject of character?");
2891-
assert(!O->hasLValuePath() || O->getLValuePath().empty());
2892-
if (handler.AccessKind != AK_Read)
2893-
expandStringLiteral(Info, O->getLValueBase().get<const Expr *>(),
2894-
*O);
2895-
else
2896-
return handler.foundString(*O, ObjType, Index);
2897-
}
2898-
28992889
if (O->getArrayInitializedElts() > Index)
29002890
O = &O->getArrayInitializedElt(Index);
29012891
else if (handler.AccessKind != AK_Read) {
@@ -3008,11 +2998,6 @@ struct ExtractSubobjectHandler {
30082998
Result = APValue(Value);
30092999
return true;
30103000
}
3011-
bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
3012-
Result = APValue(extractStringLiteralCharacter(
3013-
Info, Subobj.getLValueBase().get<const Expr *>(), Character));
3014-
return true;
3015-
}
30163001
};
30173002
} // end anonymous namespace
30183003

@@ -3070,9 +3055,6 @@ struct ModifySubobjectHandler {
30703055
Value = NewVal.getFloat();
30713056
return true;
30723057
}
3073-
bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
3074-
llvm_unreachable("shouldn't encounter string elements with ExpandArrays");
3075-
}
30763058
};
30773059
} // end anonymous namespace
30783060

@@ -3386,12 +3368,20 @@ static bool handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv,
33863368
CompleteObject LitObj(&Lit, Base->getType(), false);
33873369
return extractSubobject(Info, Conv, LitObj, LVal.Designator, RVal);
33883370
} else if (isa<StringLiteral>(Base) || isa<PredefinedExpr>(Base)) {
3389-
// We represent a string literal array as an lvalue pointing at the
3390-
// corresponding expression, rather than building an array of chars.
3391-
// FIXME: Support ObjCEncodeExpr, MakeStringConstant
3392-
APValue Str(Base, CharUnits::Zero(), APValue::NoLValuePath(), 0);
3393-
CompleteObject StrObj(&Str, Base->getType(), false);
3394-
return extractSubobject(Info, Conv, StrObj, LVal.Designator, RVal);
3371+
// Special-case character extraction so we don't have to construct an
3372+
// APValue for the whole string.
3373+
assert(LVal.Designator.Entries.size() == 1 &&
3374+
"Can only read characters from string literals");
3375+
if (LVal.Designator.isOnePastTheEnd()) {
3376+
if (Info.getLangOpts().CPlusPlus11)
3377+
Info.FFDiag(Conv, diag::note_constexpr_access_past_end) << AK_Read;
3378+
else
3379+
Info.FFDiag(Conv);
3380+
return false;
3381+
}
3382+
uint64_t CharIndex = LVal.Designator.Entries[0].ArrayIndex;
3383+
RVal = APValue(extractStringLiteralCharacter(Info, Base, CharIndex));
3384+
return true;
33953385
}
33963386
}
33973387

@@ -3517,9 +3507,6 @@ struct CompoundAssignSubobjectHandler {
35173507
LVal.moveInto(Subobj);
35183508
return true;
35193509
}
3520-
bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
3521-
llvm_unreachable("shouldn't encounter string elements here");
3522-
}
35233510
};
35243511
} // end anonymous namespace
35253512

@@ -3668,9 +3655,6 @@ struct IncDecSubobjectHandler {
36683655
LVal.moveInto(Subobj);
36693656
return true;
36703657
}
3671-
bool foundString(APValue &Subobj, QualType SubobjType, uint64_t Character) {
3672-
llvm_unreachable("shouldn't encounter string elements here");
3673-
}
36743658
};
36753659
} // end anonymous namespace
36763660

@@ -7150,8 +7134,7 @@ namespace {
71507134
: ExprEvaluatorBaseTy(Info), This(This), Result(Result) {}
71517135

71527136
bool Success(const APValue &V, const Expr *E) {
7153-
assert((V.isArray() || V.isLValue()) &&
7154-
"expected array or string literal");
7137+
assert(V.isArray() && "expected array");
71557138
Result = V;
71567139
return true;
71577140
}
@@ -7182,6 +7165,10 @@ namespace {
71827165
bool VisitCXXConstructExpr(const CXXConstructExpr *E,
71837166
const LValue &Subobject,
71847167
APValue *Value, QualType Type);
7168+
bool VisitStringLiteral(const StringLiteral *E) {
7169+
expandStringLiteral(Info, E, Result);
7170+
return true;
7171+
}
71857172
};
71867173
} // end anonymous namespace
71877174

@@ -7214,14 +7201,8 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
72147201

72157202
// C++11 [dcl.init.string]p1: A char array [...] can be initialized by [...]
72167203
// an appropriately-typed string literal enclosed in braces.
7217-
if (E->isStringLiteralInit()) {
7218-
LValue LV;
7219-
if (!EvaluateLValue(E->getInit(0), LV, Info))
7220-
return false;
7221-
APValue Val;
7222-
LV.moveInto(Val);
7223-
return Success(Val, E);
7224-
}
7204+
if (E->isStringLiteralInit())
7205+
return Visit(E->getInit(0));
72257206

72267207
bool Success = true;
72277208

clang/lib/CodeGen/CGExprConstant.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -1649,16 +1649,6 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter,
16491649
llvm::Constant *ConstantLValueEmitter::tryEmit() {
16501650
const APValue::LValueBase &base = Value.getLValueBase();
16511651

1652-
// Certain special array initializers are represented in APValue
1653-
// as l-values referring to the base expression which generates the
1654-
// array. This happens with e.g. string literals. These should
1655-
// probably just get their own representation kind in APValue.
1656-
if (DestType->isArrayType()) {
1657-
assert(!hasNonZeroOffset() && "offset on array initializer");
1658-
auto expr = const_cast<Expr*>(base.get<const Expr*>());
1659-
return ConstExprEmitter(Emitter).Visit(expr, DestType);
1660-
}
1661-
16621652
// Otherwise, the destination type should be a pointer or reference
16631653
// type, but it might also be a cast thereof.
16641654
//

clang/lib/Sema/SemaInit.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ static StringInitFailureKind IsStringInit(Expr *init, QualType declType,
144144
static void updateStringLiteralType(Expr *E, QualType Ty) {
145145
while (true) {
146146
E->setType(Ty);
147+
E->setValueKind(VK_RValue);
147148
if (isa<StringLiteral>(E) || isa<ObjCEncodeExpr>(E))
148149
break;
149150
else if (ParenExpr *PE = dyn_cast<ParenExpr>(E))

clang/test/AST/ast-dump-wchar.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// RUN: %clang_cc1 -std=c++11 -ast-dump %s -triple x86_64-linux-gnu | FileCheck %s
22

33
char c8[] = u8"test\0\\\"\a\b\f\n\r\t\v\234";
4-
// CHECK: StringLiteral {{.*}} lvalue u8"test\000\\\"\a\b\f\n\r\t\v\234"
4+
// CHECK: StringLiteral {{.*}} u8"test\000\\\"\a\b\f\n\r\t\v\234"
55

66
char16_t c16[] = u"test\0\\\"\t\a\b\234\u1234";
7-
// CHECK: StringLiteral {{.*}} lvalue u"test\000\\\"\t\a\b\234\u1234"
7+
// CHECK: StringLiteral {{.*}} u"test\000\\\"\t\a\b\234\u1234"
88

99
char32_t c32[] = U"test\0\\\"\t\a\b\234\u1234\U0010ffff"; // \
10-
// CHECK: StringLiteral {{.*}} lvalue U"test\000\\\"\t\a\b\234\u1234\U0010FFFF"
10+
// CHECK: StringLiteral {{.*}} U"test\000\\\"\t\a\b\234\u1234\U0010FFFF"
1111

1212
wchar_t wc[] = L"test\0\\\"\t\a\b\234\u1234\xffffffff"; // \
13-
// CHECK: StringLiteral {{.*}} lvalue L"test\000\\\"\t\a\b\234\x1234\xFFFFFFFF"
13+
// CHECK: StringLiteral {{.*}} L"test\000\\\"\t\a\b\234\x1234\xFFFFFFFF"

clang/test/CodeGenObjC/encode-test.m

+2-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,8 @@ @implementation Derived1X @end
186186

187187
// CHECK-LABEL: @test_strlen(
188188
// CHECK: %[[i:.*]] = alloca i32
189-
// CHECK: store i32 1, i32* %[[i]]
189+
// CHECK: %[[call:.*]] = call i32 @strlen
190+
// CHECK: store i32 %[[call]], i32* %[[i]]
190191
void test_strlen() {
191192
const char array[] = @encode(int);
192193
int i = strlen(array);

clang/test/SemaCXX/constant-expression-cxx11.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -2220,3 +2220,11 @@ namespace PointerArithmeticOverflow {
22202220
constexpr int *q = (&n + 1) - (unsigned __int128)-1; // expected-error {{constant expression}} expected-note {{cannot refer to element -3402}}
22212221
constexpr int *r = &(&n + 1)[(unsigned __int128)-1]; // expected-error {{constant expression}} expected-note {{cannot refer to element 3402}}
22222222
}
2223+
2224+
namespace PR40430 {
2225+
struct S {
2226+
char c[10] = "asdf";
2227+
constexpr char foo() const { return c[3]; }
2228+
};
2229+
static_assert(S().foo() == 'f', "");
2230+
}

0 commit comments

Comments
 (0)