https://github.com/DataCorrupted updated https://github.com/llvm/llvm-project/pull/209688
>From 799be7a1beba3452e5a4d73a64ea515f84320f39 Mon Sep 17 00:00:00 2001 From: Peter Rong <[email protected]> Date: Tue, 14 Jul 2026 23:46:59 -0700 Subject: [PATCH 1/2] [clang][Sema] Fix ObjC file-scope literal diagnostic under -fobjc-arc --- clang/lib/Sema/SemaDecl.cpp | 11 ++++- ...c-constant-literal-dict-key-restrictions.m | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 clang/test/SemaObjC/objc-constant-literal-dict-key-restrictions.m 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}} >From 09cee40adc634e4152d4fa65112064126756b842 Mon Sep 17 00:00:00 2001 From: Peter Rong <[email protected]> Date: Tue, 14 Jul 2026 23:59:35 -0700 Subject: [PATCH 2/2] formatter --- clang/lib/Sema/SemaDecl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5dbc5b653ec42..885083c8bab9c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12919,7 +12919,7 @@ bool Sema::CheckForConstantInitializer(Expr *Init, unsigned DiagID) { // Under ARC, the initializer is wrapped in `ExprWithCleanups` + // `ImplicitCastExpr <ARCReclaimReturnedObject>`. Unwrap both - // here so the ObjC-specific path fires under ARC too. + // here so the ObjC-specific path fires under ARC too. Init = Init->IgnoreImpCasts(); if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) Init = EWC->getSubExpr()->IgnoreImpCasts(); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
