llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Peter Rong (DataCorrupted)

<details>
<summary>Changes</summary>

Under `-fobjc-arc`, an ObjC collection literal used as a file-scope initializer 
(e.g. `static NSDictionary *d = @{@<!-- -->5 : @<!-- -->1};`) drops the 
targeted `err_objc_literal_nonconstant_at_file_scope` message and falls back to 
the generic C `err_init_element_is_not_constant` — "initializer element is not 
a compile-time constant", pointed at the whole `@{...}`. This was very 
confusing to our developers as it is inaccurate: the initializer _is_ 
compiler-time constant in the eyes of developers.

We fix this by unwrap `ExprWithCleanups` and implicit cast both `Init` and 
`Culprit` at the top of the ObjC diagnostic block in 
`CheckForConstantInitializer`, so classification and element iteration find the 
underlying `ObjCObjectLiteral` regardless of ARC wrapping.
We also unwrap implicit casts before the per-key `isa&lt;ObjCStringLiteral&gt;` 
check in the dictionary literal branch. Bare `@"..."` keys are typically 
wrapped in `&lt;BitCast&gt;` to `id`; without the unwrap, legitimate 
string-literal keys failed the isa check and the diagnostic misreported the 
offender as the key when the actual failure was the value.

Added a `clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m` 
with two RUN lines (macOS without ARC + iOS simulator with ARC) covering 
non-string-literal keys under both paths.

[Assisted-by](https://t.ly/Dkjjk): Opus 4.8

---
Full diff: https://github.com/llvm/llvm-project/pull/209688.diff


2 Files Affected:

- (modified) clang/lib/Sema/SemaDecl.cpp (+9-2) 
- (added) clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m 
(+45) 


``````````diff
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7e1b23c971a9c..5dbc5b653ec42 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12917,6 +12917,14 @@ bool Sema::CheckForConstantInitializer(Expr *Init, 
unsigned DiagID) {
   if (Init->isConstantInitializer(Context, /*ForRef=*/false, &Culprit))
     return false;
 
+  // Under ARC, the initializer is wrapped in `ExprWithCleanups` +
+  // `ImplicitCastExpr <ARCReclaimReturnedObject>`. Unwrap both
+  // here so the ObjC-specific path fires under ARC too. 
+  Init = Init->IgnoreImpCasts();
+  if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
+    Init = EWC->getSubExpr()->IgnoreImpCasts();
+  Culprit = Culprit->IgnoreImpCasts();
+
   // Emit ObjC-specific diagnostics for non-constant literals at file scope.
   if (getLangOpts().ObjCConstantLiterals && isa<ObjCObjectLiteral>(Culprit)) {
 
@@ -12937,8 +12945,7 @@ bool Sema::CheckForConstantInitializer(Expr *Init, 
unsigned DiagID) {
       for (size_t I = 0, N = DLE->getNumElements(); I != N; ++I) {
         const ObjCDictionaryElement Elm = DLE->getKeyValueElement(I);
 
-        // Check that the key is a string literal and is constant.
-        if (!isa<ObjCStringLiteral>(Elm.Key) ||
+        if (!isa<ObjCStringLiteral>(Elm.Key->IgnoreImpCasts()) ||
             !Elm.Key->isConstantInitializer(Context)) {
           Diag(Elm.Key->getExprLoc(),
                diag::err_objc_literal_nonconstant_at_file_scope)
diff --git a/clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m 
b/clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m
new file mode 100644
index 0000000000000..14eb989eafb08
--- /dev/null
+++ b/clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-macosx11.0.0 
-fobjc-runtime=macosx-11.0.0 -fobjc-constant-literals 
-fconstant-nsnumber-literals -fconstant-nsarray-literals 
-fconstant-nsdictionary-literals -verify %s
+// RUN: %clang_cc1 -fsyntax-only -triple arm64-apple-ios15.1.0-simulator 
-fobjc-runtime=ios-15.1.0 -fobjc-arc -fobjc-constant-literals 
-fconstant-nsnumber-literals -fconstant-nsarray-literals 
-fconstant-nsdictionary-literals -verify %s
+
+#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 
|| NS_BUILD_32_LIKE_64
+typedef unsigned long NSUInteger;
+#else
+typedef unsigned int NSUInteger;
+#endif
+
+@class NSString;
+
+@interface NSNumber
++ (NSNumber *)numberWithInt:(int)value;
++ (NSNumber *)numberWithBool:(unsigned char)value;
+@end
+
+@interface NSArray
++ (id)arrayWithObjects:(const id[])objects count:(NSUInteger)cnt;
+@end
+
+@interface NSDictionary
++ (id)dictionaryWithObjects:(const id[])objects forKeys:(const id[])keys 
count:(NSUInteger)cnt;
+@end
+
+// ---- Accepted: NSString literal keys with constant literal values ---------
+
+static NSDictionary *const dASCII = @{@"a" : @1, @"m" : @2, @"z" : @3};
+static NSDictionary *const dEmpty = @{};
+
+// ---- Rejected: non-NSString-literal key -----------------------------------
+
+static NSDictionary *const dNumberKey = @{@5 : @1}; // expected-error {{a 
dictionary literal can only be used at file scope if its contents are all also 
constant literals and its keys are string literals}}
+static NSDictionary *const dBoxedKey = @{@(1 + 2) : @1}; // expected-error {{a 
dictionary literal can only be used at file scope if its contents are all also 
constant literals and its keys are string literals}}
+static NSDictionary *const dBoolKey = @{@__objc_yes : @1}; // expected-error 
{{a dictionary literal can only be used at file scope if its contents are all 
also constant literals and its keys are string literals}}
+
+// The error points at the specific offending key, not the whole `@{...}`.
+static NSDictionary *const dSecondKeyBad = @{@"ok" : @1, @2 : @2}; // 
expected-error {{a dictionary literal can only be used at file scope if its 
contents are all also constant literals and its keys are string literals}}
+
+// ---- Rejected: non-literal key (constant variable reference) --------------
+//
+// `someStringConstantVar` is an NSString, but it's a DeclRefExpr, not an
+// ObjCStringLiteral. Hits the same "not a string literal" branch as `@5`.
+
+static NSString *const someStringConstantVar = @"foo";
+static NSDictionary *const dConstVarKey = @{someStringConstantVar : @1}; // 
expected-error {{a dictionary literal can only be used at file scope if its 
contents are all also constant literals and its keys are string literals}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209688
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to