ahatanak created this revision.
ahatanak added reviewers: rjmccall, arphaman.
ahatanak added a project: clang.
Herald added subscribers: jdoerfert, dexonsmith, jkorous.
clang currently emits an expression like `@("abc")` as a message send to
`stringWithUTF8String`. This patch makes changes so that a compile-time
constant is emitted instead when the expression inside the parentheses is a
string literal. I think it's possible to do the same optimization for constant
expressions that evaluate to zero-terminated strings (see the example below),
but I'm leaving that for future work.
constexpr const char *foo() { return "abc"; }
void test() {
NSString *s = @(foo());
}
The original motivation for the patch is to silence the
`-Wnullable-to-nonnull-conversion` warning that clang started emitting after
r317727:
The https://oleb.net/2018/@keypath/.
rdar://problem/42684601
Repository:
rC Clang
https://reviews.llvm.org/D58729
Files:
include/clang/AST/ExprObjC.h
lib/AST/ExprConstant.cpp
lib/CodeGen/CGExprConstant.cpp
lib/CodeGen/CGObjC.cpp
lib/Sema/SemaExprObjC.cpp
test/CodeGenObjC/boxing.m
test/SemaObjC/objc-literal-sig.m
test/SemaObjC/transfer-boxed-string-nullability.m
test/SemaObjCXX/literals.mm
Index: test/SemaObjCXX/literals.mm
===================================================================
--- test/SemaObjCXX/literals.mm
+++ test/SemaObjCXX/literals.mm
@@ -50,6 +50,9 @@
+ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id<NSCopying> [])keys count:(unsigned long)cnt;
@end
+@interface NSString
+@end
+
template<typename T>
struct ConvertibleTo {
operator T();
@@ -185,3 +188,8 @@
void test_dictionary_colon() {
id dict = @{ key : value };
}
+
+void testConstExpr() {
+ constexpr NSString *t0 = @"abc";
+ constexpr NSString *t1 = @("abc");
+}
Index: test/SemaObjC/transfer-boxed-string-nullability.m
===================================================================
--- test/SemaObjC/transfer-boxed-string-nullability.m
+++ test/SemaObjC/transfer-boxed-string-nullability.m
@@ -16,6 +16,10 @@
void takesNonNull(NSString * _Nonnull ptr);
void testBoxedString() {
+ // No diagnostic emitted as this doesn't need a stringWithUTF8String message
+ // send.
+ takesNonNull(@("hey"));
+
const char *str = "hey";
takesNonNull([NSString stringWithUTF8String:str]);
takesNonNull(@(str));
Index: test/SemaObjC/objc-literal-sig.m
===================================================================
--- test/SemaObjC/objc-literal-sig.m
+++ test/SemaObjC/objc-literal-sig.m
@@ -39,6 +39,8 @@
// All tests are doubled to make sure that a bad method is not saved
// and then used un-checked.
+const char *getStr(void);
+
void test_sig() {
(void)@__objc_yes; // expected-error{{literal construction method 'numberWithBool:' has incompatible signature}}
(void)@__objc_yes; // expected-error{{literal construction method 'numberWithBool:' has incompatible signature}}
@@ -46,6 +48,6 @@
id array2 = @[ @17 ]; // expected-error{{literal construction method 'arrayWithObjects:count:' has incompatible signature}}
id dict = @{ @"hello" : @17 }; // expected-error{{literal construction method 'dictionaryWithObjects:forKeys:count:' has incompatible signature}}
id dict2 = @{ @"hello" : @17 }; // expected-error{{literal construction method 'dictionaryWithObjects:forKeys:count:' has incompatible signature}}
- id str = @("hello"); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
- id str2 = @("hello"); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
+ id str = @(getStr()); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
+ id str2 = @(getStr()); // expected-error{{literal construction method 'stringWithUTF8String:' has incompatible signature}}
}
Index: test/CodeGenObjC/boxing.m
===================================================================
--- test/CodeGenObjC/boxing.m
+++ test/CodeGenObjC/boxing.m
@@ -53,6 +53,9 @@
+ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
@end
+// CHECK: [[V0:%.*]] = type opaque
+// CHECK: [[STRUCT_NSCONSTANT_STRING_TAG:%.*]] = type { i32*, i32, i8*, i64 }
+
// CHECK: [[WithIntMeth:@.*]] = private unnamed_addr constant [15 x i8] c"numberWithInt:\00"
// CHECK: [[WithIntSEL:@.*]] = private externally_initialized global i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[WithIntMeth]]
// CHECK: [[WithCharMeth:@.*]] = private unnamed_addr constant [16 x i8] c"numberWithChar:\00"
@@ -65,8 +68,12 @@
// CHECK: [[WithUnsignedIntegerSEL:@.*]] = private externally_initialized global i8* getelementptr inbounds ([27 x i8], [27 x i8]* [[WithUnsignedIntegerMeth]]
// CHECK: [[stringWithUTF8StringMeth:@.*]] = private unnamed_addr constant [22 x i8] c"stringWithUTF8String:\00"
// CHECK: [[stringWithUTF8StringSEL:@.*]] = private externally_initialized global i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[stringWithUTF8StringMeth]]
+// CHECK: [[STR0:.*]] = private unnamed_addr constant [4 x i8] c"abc\00", section "__TEXT,__cstring,cstring_literals", align 1
+// CHECK: [[UNNAMED_CFSTRING:.*]] = private global [[STRUCT_NSCONSTANT_STRING_TAG]] { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 1992, i8* getelementptr inbounds ([4 x i8], [4 x i8]* [[STR0]], i32 0, i32 0), i64 3 }, section "__DATA,__cfstring", align 8
int main() {
+ // CHECK: [[T:%.*]] = alloca [[V0]]*, align 8
+
// CHECK: load i8*, i8** [[WithIntSEL]]
int i; @(i);
// CHECK: load i8*, i8** [[WithCharSEL]]
@@ -92,4 +99,7 @@
Color col = Red;
// CHECK: load i8*, i8** [[WithIntegerSEL]]
@(col);
+
+ // CHECK: store [[V0]]* bitcast ([[STRUCT_NSCONSTANT_STRING_TAG]]* [[UNNAMED_CFSTRING]] to [[V0]]*), [[V0]]** [[T]], align 8
+ NSString *t = @("abc");
}
Index: lib/Sema/SemaExprObjC.cpp
===================================================================
--- lib/Sema/SemaExprObjC.cpp
+++ lib/Sema/SemaExprObjC.cpp
@@ -502,6 +502,7 @@
}
ObjCMethodDecl *BoxingMethod = nullptr;
QualType BoxedType;
+ Expr *ValueSubExpr = ValueExpr->IgnoreParens();
// Convert the expression to an RValue, so we can check for pointer types...
ExprResult RValue = DefaultFunctionArrayLvalueConversion(ValueExpr);
if (RValue.isInvalid()) {
@@ -524,6 +525,15 @@
NSStringPointer = Context.getObjCObjectPointerType(NSStringObject);
}
+ // The boxed expression can be emitted as a compile time constant if this
+ // is a string literal.
+ if (auto *SL = dyn_cast<StringLiteral>(ValueSubExpr)) {
+ BoxedType = Context.getAttributedType(
+ AttributedType::getNullabilityAttrKind(NullabilityKind::NonNull),
+ NSStringPointer, NSStringPointer);
+ return new (Context) ObjCBoxedExpr(SL, BoxedType, nullptr, SR);
+ }
+
if (!StringWithUTF8StringMethod) {
IdentifierInfo *II = &Context.Idents.get("stringWithUTF8String");
Selector stringWithUTF8String = Context.Selectors.getUnarySelector(II);
Index: lib/CodeGen/CGObjC.cpp
===================================================================
--- lib/CodeGen/CGObjC.cpp
+++ lib/CodeGen/CGObjC.cpp
@@ -14,6 +14,7 @@
#include "CGObjCRuntime.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
@@ -60,7 +61,12 @@
// Get the method.
const ObjCMethodDecl *BoxingMethod = E->getBoxingMethod();
const Expr *SubExpr = E->getSubExpr();
- assert(BoxingMethod && "BoxingMethod is null");
+
+ if (E->isExpressibleAsConstantInitializer()) {
+ ConstantEmitter ConstEmitter(CGM);
+ return ConstEmitter.tryEmitAbstract(E, E->getType());
+ }
+
assert(BoxingMethod->isClassMethod() && "BoxingMethod must be a class method");
Selector Sel = BoxingMethod->getSelector();
Index: lib/CodeGen/CGExprConstant.cpp
===================================================================
--- lib/CodeGen/CGExprConstant.cpp
+++ lib/CodeGen/CGExprConstant.cpp
@@ -1611,6 +1611,7 @@
ConstantLValue VisitConstantExpr(const ConstantExpr *E);
ConstantLValue VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
ConstantLValue VisitStringLiteral(const StringLiteral *E);
+ ConstantLValue VisitObjCBoxedExpr(const ObjCBoxedExpr *E);
ConstantLValue VisitObjCEncodeExpr(const ObjCEncodeExpr *E);
ConstantLValue VisitObjCStringLiteral(const ObjCStringLiteral *E);
ConstantLValue VisitPredefinedExpr(const PredefinedExpr *E);
@@ -1773,10 +1774,24 @@
return CGM.GetAddrOfConstantStringFromObjCEncode(E);
}
+static ConstantLValue emitConstantObjCStringLiteral(const StringLiteral *S,
+ QualType T,
+ CodeGenModule &CGM) {
+ auto C = CGM.getObjCRuntime().GenerateConstantString(S);
+ return C.getElementBitCast(CGM.getTypes().ConvertTypeForMem(T));
+}
+
ConstantLValue
ConstantLValueEmitter::VisitObjCStringLiteral(const ObjCStringLiteral *E) {
- auto C = CGM.getObjCRuntime().GenerateConstantString(E->getString());
- return C.getElementBitCast(CGM.getTypes().ConvertTypeForMem(E->getType()));
+ return emitConstantObjCStringLiteral(E->getString(), E->getType(), CGM);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitObjCBoxedExpr(const ObjCBoxedExpr *E) {
+ assert(E->isExpressibleAsConstantInitializer() &&
+ "this boxed expression can't be emitted as a compile-time constant");
+ return emitConstantObjCStringLiteral(cast<StringLiteral>(E->getSubExpr()),
+ E->getType(), CGM);
}
ConstantLValue
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -1743,6 +1743,8 @@
case Expr::CXXTypeidExprClass:
case Expr::CXXUuidofExprClass:
return true;
+ case Expr::ObjCBoxedExprClass:
+ return cast<ObjCBoxedExpr>(E)->isExpressibleAsConstantInitializer();
case Expr::CallExprClass:
return IsStringLiteralCall(cast<CallExpr>(E));
// For GCC compatibility, &&label has static storage duration.
@@ -5794,6 +5796,8 @@
bool VisitObjCStringLiteral(const ObjCStringLiteral *E)
{ return Success(E); }
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E) {
+ if (E->isExpressibleAsConstantInitializer())
+ return Success(E);
if (Info.noteFailure())
EvaluateIgnoredValue(Info, E->getSubExpr());
return Error(E);
Index: include/clang/AST/ExprObjC.h
===================================================================
--- include/clang/AST/ExprObjC.h
+++ include/clang/AST/ExprObjC.h
@@ -138,6 +138,12 @@
return BoxingMethod;
}
+ // Indicates whether this boxed expression can be emitted as a compile-time
+ // constant.
+ bool isExpressibleAsConstantInitializer() const {
+ return !BoxingMethod && SubExpr;
+ }
+
SourceLocation getAtLoc() const { return Range.getBegin(); }
SourceLocation getBeginLoc() const LLVM_READONLY { return Range.getBegin(); }
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits