From 348faa0c994ec949fe99f6d25335f299a8f5c215 Mon Sep 17 00:00:00 2001 From: Kale1d0 Date: Tue, 22 Aug 2023 13:37:30 +0800 Subject: [PATCH] [clang] allow global initializers to use non-constant static storage variables This patch enhances the compatibility with GCC in Compound literals. Add a new BoolFOption that is true by default, namely allow-non-const-global-init. If this option is set to true, the check for 6.5.2.5p3 while compiling CPP files will be ignored, which is the default behavior of GCC, therefore allowing global initializers to use non-constant static storage variables. This is commonly harmless for single TU cases but might cause the static initialization order fiasco if variables in other source files are used. The test command in SemaCXX/compound-literal.cpp is changed, to ensure the original test can be passed. Also, add two test files for setting the option to both true and false. --- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 4 ++++ clang/lib/Sema/SemaExpr.cpp | 12 ++++++++--- clang/test/SemaCXX/compound-literal.cpp | 4 ++-- ...-constant-global-initializers-disabled.cpp | 9 +++++++++ .../non-constant-global-initializers.cpp | 20 +++++++++++++++++++ 6 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 clang/test/SemaCXX/non-constant-global-initializers-disabled.cpp create mode 100644 clang/test/SemaCXX/non-constant-global-initializers.cpp diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index ad366821f3cb..690acb72e076 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -156,6 +156,7 @@ LANGOPT(Coroutines , 1, 0, "C++20 coroutines") LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library features") +LANGOPT(NonConstantGlobalInitializers, 1, 1, "allow use of non-constant static storage variables in global initializers, to enhance compatibility with gcc.") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 3cab37b21aaf..9f7842647cde 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6129,6 +6129,10 @@ def fallow_half_arguments_and_returns : Flag<["-"], "fallow-half-arguments-and-r HelpText<"Allow function arguments and returns of type half">, MarshallingInfoFlag>, ImpliedByAnyOf<[fnative_half_arguments_and_returns.KeyPath]>; +defm allow_non_const_global_init : BoolFOption<"allow-non-const-global-init", + LangOpts<"NonConstantGlobalInitializers">, DefaultTrue, + PosFlag, NegFlag, + BothFlags<[], " use of non-constant static storage variables in global initializers.">>; def fdefault_calling_conv_EQ : Joined<["-"], "fdefault-calling-conv=">, HelpText<"Set default calling convention">, Values<"cdecl,fastcall,stdcall,vectorcall,regcall">, diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 83081bbf0aa0..e1178919705a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7290,9 +7290,15 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, if (isFileScope) { if (!LiteralExpr->isTypeDependent() && !LiteralExpr->isValueDependent() && - !literalType->isDependentType()) // C99 6.5.2.5p3 - if (CheckForConstantInitializer(LiteralExpr, literalType)) - return ExprError(); + !literalType->isDependentType()) { // C99 6.5.2.5p3 + // Try to behave like gcc and ignore this check by default + // while compiling CPP files. + bool ignoreCheck = getLangOpts().CPlusPlus && + getLangOpts().NonConstantGlobalInitializers; + if (!ignoreCheck && + CheckForConstantInitializer(LiteralExpr, literalType)) + return ExprError(); + } } else if (literalType.getAddressSpace() != LangAS::opencl_private && literalType.getAddressSpace() != LangAS::Default) { // Embedded-C extensions to C99 6.5.2.5: diff --git a/clang/test/SemaCXX/compound-literal.cpp b/clang/test/SemaCXX/compound-literal.cpp index 353be2cf48e4..9b1c2df77352 100644 --- a/clang/test/SemaCXX/compound-literal.cpp +++ b/clang/test/SemaCXX/compound-literal.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++03 -verify -ast-dump %s > %t-03 +// RUN: %clang_cc1 -fsyntax-only -fno-allow-non-const-global-init -std=c++03 -verify -ast-dump %s > %t-03 // RUN: FileCheck --input-file=%t-03 %s -// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -ast-dump %s > %t-11 +// RUN: %clang_cc1 -fsyntax-only -fno-allow-non-const-global-init -std=c++11 -verify -ast-dump %s > %t-11 // RUN: FileCheck --input-file=%t-11 %s // RUN: FileCheck --input-file=%t-11 %s --check-prefix=CHECK-CXX11 diff --git a/clang/test/SemaCXX/non-constant-global-initializers-disabled.cpp b/clang/test/SemaCXX/non-constant-global-initializers-disabled.cpp new file mode 100644 index 000000000000..3527f0922968 --- /dev/null +++ b/clang/test/SemaCXX/non-constant-global-initializers-disabled.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -x c -fsyntax-only -fno-allow-non-const-global-init %s -verify +// RUN: %clang_cc1 -x c++ -fsyntax-only -fno-allow-non-const-global-init %s -verify + +int* f(int *); +struct DT { int * el;}; +int * pa = 0; +struct DT va = (struct DT){pa}; // expected-error {{initializer element is not a compile-time constant}} +struct DT vb = (struct DT){pa + 1}; // expected-error {{initializer element is not a compile-time constant}} +struct DT vc = (struct DT){f(pa) + 1}; // expected-error {{initializer element is not a compile-time constant}} diff --git a/clang/test/SemaCXX/non-constant-global-initializers.cpp b/clang/test/SemaCXX/non-constant-global-initializers.cpp new file mode 100644 index 000000000000..cb35656e1ce1 --- /dev/null +++ b/clang/test/SemaCXX/non-constant-global-initializers.cpp @@ -0,0 +1,20 @@ +// The first and second runs check that nothing has been changed for C +// RUN: not %clang_cc1 -x c -fsyntax-only %s 2> %t +// RUN: FileCheck %s < %t +// RUN: not %clang_cc1 -x c -fsyntax-only -fallow-non-const-global-init %s 2> %t +// RUN: FileCheck %s < %t + +// The third and fourth runs check that it works for C++ both by default and by enabling the option +// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify +// RUN: %clang_cc1 -x c++ -fsyntax-only -fallow-non-const-global-init %s -verify +// expected-no-diagnostics + +int* f(int *); +struct DT { int * el;}; +int * pa = 0; +struct DT va = (struct DT){pa}; +// CHECK:[[@LINE-1]]:28: error: initializer element is not a compile-time constant +struct DT vb = (struct DT){pa + 1}; +// CHECK:[[@LINE-1]]:31: error: initializer element is not a compile-time constant +struct DT vc = (struct DT){f(pa) + 1}; +// CHECK:[[@LINE-1]]:34: error: initializer element is not a compile-time constant -- Gitee