[clang] 63b0b82 - When float_t and double_t types are used inside a scope with
Zahira Ammarguellat via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 23 12:21:07 PDT 2023
Author: Zahira Ammarguellat
Date: 2023-06-23T15:12:51-04:00
New Revision: 63b0b82fd6bee672fa20078aa2fbe3c4ee2b8970
URL: https://github.com/llvm/llvm-project/commit/63b0b82fd6bee672fa20078aa2fbe3c4ee2b8970
DIFF: https://github.com/llvm/llvm-project/commit/63b0b82fd6bee672fa20078aa2fbe3c4ee2b8970.diff
LOG: When float_t and double_t types are used inside a scope with
a '#pragma clang fp eval_method, it can lead to ABI breakage.
See https://godbolt.org/z/56zG4Wo91
This patch prevents this.
Differential Revision: https://reviews.llvm.org/D153590
Added:
clang/test/Sema/attr-only-in-default-eval.cpp
clang/test/Sema/fp-eval-pragma-with-float-double_t-1.c
clang/test/Sema/fp-eval-pragma-with-float-double_t-2.c
clang/test/Sema/fp-eval-pragma-with-float-double_t-3.c
Modified:
clang/docs/LanguageExtensions.rst
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/TokenKinds.def
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaExpr.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index ab1fe885a3a75..bda55c007e6ae 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -4661,6 +4661,13 @@ The full syntax this pragma supports is
a = b[i] * c[i] + e;
}
+Note: ``math.h`` defines the typedefs ``float_t`` and ``double_t`` based on the active
+evaluation method at the point where the header is included, not where the
+typedefs are used. Because of this, it is unwise to combine these typedefs with
+``#pragma clang fp eval_method``. To catch obvious bugs, Clang will emit an
+error for any references to these typedefs within the scope of this pragma;
+however, this is not a fool-proof protection, and programmers must take care.
+
The ``#pragma float_control`` pragma allows precise floating-point
semantics and floating-point exception behavior to be specified
for a section of the source code. This pragma can only appear at file or
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 3fdd84d2b13b2..b9e701641fdf1 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4193,3 +4193,10 @@ def ReadOnlyPlacement : InheritableAttr {
let Subjects = SubjectList<[Record]>;
let Documentation = [ReadOnlyPlacementDocs];
}
+
+def AvailableOnlyInDefaultEvalMethod : InheritableAttr {
+ let Spellings = [Clang<"available_only_in_default_eval_method">];
+ let Subjects = SubjectList<[TypedefName], ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5f8853715a6e8..a215095d540ae 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11558,6 +11558,10 @@ def err_objc_type_args_wrong_arity : Error<
"too %select{many|few}0 type arguments for class %1 (have %2, expected %3)">;
}
+def err_type_available_only_in_default_eval_method : Error<
+ "cannot use type '%0' within '#pragma clang fp eval_method'; type is set "
+ "according to the default eval method for the translation unit">;
+
def err_objc_type_arg_not_id_compatible : Error<
"type argument %0 is neither an Objective-C object nor a block type">;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 8c0529fdb055b..ef0dad0f2dcd9 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -798,13 +798,15 @@ OBJC_AT_KEYWORD(import)
OBJC_AT_KEYWORD(available)
//===----------------------------------------------------------------------===//
-// Interesting idenitifiers.
+// Interesting identifiers.
//===----------------------------------------------------------------------===//
INTERESTING_IDENTIFIER(not_interesting)
INTERESTING_IDENTIFIER(FILE)
INTERESTING_IDENTIFIER(jmp_buf)
INTERESTING_IDENTIFIER(sigjmp_buf)
INTERESTING_IDENTIFIER(ucontext_t)
+INTERESTING_IDENTIFIER(float_t)
+INTERESTING_IDENTIFIER(double_t)
// TODO: What to do about context-sensitive keywords like:
// bycopy/byref/in/inout/oneway/out?
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f33542e05b983..698f57f0245b0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6777,6 +6777,10 @@ Sema::ActOnTypedefNameDecl(Scope *S, DeclContext *DC, TypedefNameDecl *NewTD,
case tok::InterestingIdentifierKind::ucontext_t:
Context.setucontext_tDecl(NewTD);
break;
+ case tok::InterestingIdentifierKind::float_t:
+ case tok::InterestingIdentifierKind::double_t:
+ NewTD->addAttr(AvailableOnlyInDefaultEvalMethodAttr::Create(Context));
+ break;
default:
break;
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index d9146a89d322e..834380ba10d5e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8369,6 +8369,12 @@ static void handleFunctionReturnThunksAttr(Sema &S, Decl *D,
D->addAttr(FunctionReturnThunksAttr::Create(S.Context, Kind, AL));
}
+static void handleAvailableOnlyInDefaultEvalMethod(Sema &S, Decl *D,
+ const ParsedAttr &AL) {
+ assert(isa<TypedefNameDecl>(D) && "This attribute only applies to a typedef");
+ handleSimpleAttribute<AvailableOnlyInDefaultEvalMethodAttr>(S, D, AL);
+}
+
static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// The 'sycl_kernel' attribute applies only to function templates.
const auto *FD = cast<FunctionDecl>(D);
@@ -9250,6 +9256,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleFunctionReturnThunksAttr(S, D, AL);
break;
+ case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod:
+ handleAvailableOnlyInDefaultEvalMethod(S, D, AL);
+ break;
+
// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
handleLayoutVersion(S, D, AL);
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3d9c2b13a243f..45056f0d56075 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -374,6 +374,16 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc);
+ if (D->hasAttr<AvailableOnlyInDefaultEvalMethodAttr>()) {
+ if (getLangOpts().getFPEvalMethod() !=
+ LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine &&
+ PP.getLastFPEvalPragmaLocation().isValid() &&
+ PP.getCurrentFPEvalMethod() != getLangOpts().getFPEvalMethod())
+ Diag(D->getLocation(),
+ diag::err_type_available_only_in_default_eval_method)
+ << D->getName();
+ }
+
if (auto *VD = dyn_cast<ValueDecl>(D))
checkTypeSupport(VD->getType(), Loc, VD);
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 8ec0550af6490..3691e827dfb1d 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -21,6 +21,7 @@
// CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Assumption (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
+// CHECK-NEXT: AvailableOnlyInDefaultEvalMethod (SubjectMatchRule_type_alias)
// CHECK-NEXT: BPFPreserveAccessIndex (SubjectMatchRule_record)
// CHECK-NEXT: BTFDeclTag (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record, SubjectMatchRule_field, SubjectMatchRule_type_alias)
// CHECK-NEXT: BuiltinAlias (SubjectMatchRule_function)
diff --git a/clang/test/Sema/attr-only-in-default-eval.cpp b/clang/test/Sema/attr-only-in-default-eval.cpp
new file mode 100644
index 0000000000000..510863cf2580a
--- /dev/null
+++ b/clang/test/Sema/attr-only-in-default-eval.cpp
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef float float_t [[clang::available_only_in_default_eval_method]];
+using double_t __attribute__((available_only_in_default_eval_method)) = double;
+
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+class __attribute__((available_only_in_default_eval_method)) C1 {
+};
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+class [[clang::available_only_in_default_eval_method]] C2 {
+};
+
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+struct [[clang::available_only_in_default_eval_method]] S1;
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+struct __attribute__((available_only_in_default_eval_method)) S2;
+
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+void __attribute__((available_only_in_default_eval_method)) foo();
+// expected-error at +1{{'available_only_in_default_eval_method' attribute cannot be applied to types}}
+void [[clang::available_only_in_default_eval_method]] goo();
+// expected-error at +1{{'available_only_in_default_eval_method' attribute cannot be applied to types}}
+void bar() [[clang::available_only_in_default_eval_method]];
+// expected-error at +1{{'available_only_in_default_eval_method' attribute only applies to typedefs}}
+void barz() __attribute__((available_only_in_default_eval_method));
+
diff --git a/clang/test/Sema/fp-eval-pragma-with-float-double_t-1.c b/clang/test/Sema/fp-eval-pragma-with-float-double_t-1.c
new file mode 100644
index 0000000000000..6fe048ae8740a
--- /dev/null
+++ b/clang/test/Sema/fp-eval-pragma-with-float-double_t-1.c
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=source \
+// RUN: -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=double %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=double %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=extended %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP \
+// RUN: -ffp-eval-method=extended %s
+
+
+#ifdef NOERROR
+// expected-no-diagnostics
+typedef float float_t;
+typedef double double_t;
+#else
+#ifdef CPP
+typedef float float_t; //expected-error 9 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 9 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#else
+typedef float float_t; //expected-error 7 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 7 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#endif
+#endif
+
+float foo1() {
+#pragma clang fp eval_method(source)
+ float a;
+ double b;
+ return a - b;
+}
+
+float foo2() {
+#pragma clang fp eval_method(source)
+ float_t a;
+ double_t b;
+ return a - b;
+}
+
+void foo3() {
+#pragma clang fp eval_method(source)
+ char buff[sizeof(float_t)];
+ char bufd[sizeof(double_t)];
+ buff[1] = bufd[2];
+}
+
+float foo4() {
+#pragma clang fp eval_method(source)
+ typedef float_t FT;
+ typedef double_t DT;
+ FT a;
+ DT b;
+ return a - b;
+}
+
+int foo5() {
+#pragma clang fp eval_method(source)
+ int t = _Generic( 1.0L, float_t:1, default:0);
+ int v = _Generic( 1.0L, double_t:1, default:0);
+ return t;
+}
+
+void foo6() {
+#pragma clang fp eval_method(source)
+ float f = (float_t)1;
+ double d = (double_t)2;
+}
+
+void foo7() {
+#pragma clang fp eval_method(source)
+ float c1 = (float_t)12;
+ double c2 = (double_t)13;
+}
+
+float foo8() {
+#pragma clang fp eval_method(source)
+ extern float_t f;
+ extern double_t g;
+ return f-g;
+}
+
+#ifdef CPP
+void foo9() {
+#pragma clang fp eval_method(source)
+ auto resf = [](float_t f) { return f; };
+ auto resd = [](double_t g) { return g; };
+}
+
+void foo10() {
+#pragma clang fp eval_method(source)
+ using Ft = float_t;
+ using Dt = double_t;
+ Ft a;
+ Dt b;
+}
+#endif
diff --git a/clang/test/Sema/fp-eval-pragma-with-float-double_t-2.c b/clang/test/Sema/fp-eval-pragma-with-float-double_t-2.c
new file mode 100644
index 0000000000000..9eccbb74aa28d
--- /dev/null
+++ b/clang/test/Sema/fp-eval-pragma-with-float-double_t-2.c
@@ -0,0 +1,100 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=double -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=double -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=source %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=extended %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=extended %s
+
+#ifdef NOERROR
+// expected-no-diagnostics
+typedef float float_t;
+typedef double double_t;
+#else
+#ifdef CPP
+typedef float float_t; //expected-error 9 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 9 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#else
+typedef float float_t; //expected-error 7 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 7 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#endif
+#endif
+
+float foo1() {
+#pragma clang fp eval_method(double)
+ float a;
+ double b;
+ return a - b;
+}
+
+float foo2() {
+#pragma clang fp eval_method(double)
+ float_t a;
+ double_t b;
+ return a - b;
+}
+
+void foo3() {
+#pragma clang fp eval_method(double)
+ char buff[sizeof(float_t)];
+ char bufd[sizeof(double_t)];
+ buff[1] = bufd[2];
+}
+
+float foo4() {
+#pragma clang fp eval_method(double)
+ typedef float_t FT;
+ typedef double_t DT;
+ FT a;
+ DT b;
+ return a - b;
+}
+
+int foo5() {
+#pragma clang fp eval_method(double)
+ int t = _Generic( 1.0L, float_t:1, default:0);
+ int v = _Generic( 1.0L, double_t:1, default:0);
+ return t;
+}
+
+void foo6() {
+#pragma clang fp eval_method(double)
+ float f = (float_t)1;
+ double d = (double_t)2;
+}
+
+void foo7() {
+#pragma clang fp eval_method(double)
+ float c1 = (float_t)12;
+ double c2 = (double_t)13;
+}
+
+float foo8() {
+#pragma clang fp eval_method(double)
+ extern float_t f;
+ extern double_t g;
+ return f-g;
+}
+
+#ifdef CPP
+void foo9() {
+#pragma clang fp eval_method(double)
+ auto resf = [](float_t f) { return f; };
+ auto resd = [](double_t g) { return g; };
+}
+
+void foo10() {
+#pragma clang fp eval_method(double)
+ using Ft = float_t;
+ using Dt = double_t;
+ Ft a;
+ Dt b;
+}
+#endif
+
diff --git a/clang/test/Sema/fp-eval-pragma-with-float-double_t-3.c b/clang/test/Sema/fp-eval-pragma-with-float-double_t-3.c
new file mode 100644
index 0000000000000..4aca13eddb7f2
--- /dev/null
+++ b/clang/test/Sema/fp-eval-pragma-with-float-double_t-3.c
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -verify -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=extended -DNOERROR %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP \
+// RUN: -ffp-eval-method=extended -DNOERROR %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=source %s
+
+// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=double %s
+// RUN: %clang_cc1 -verify -fsyntax-only -x c++ -DCPP -ffp-eval-method=double %s
+
+
+#ifdef NOERROR
+// expected-no-diagnostics
+typedef float float_t;
+typedef double double_t;
+#else
+#ifdef CPP
+typedef float float_t; //expected-error 9 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 9 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#else
+typedef float float_t; //expected-error 7 {{cannot use type 'float_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+
+typedef double double_t; //expected-error 7 {{cannot use type 'double_t' within '#pragma clang fp eval_method'; type is set according to the default eval method for the translation unit}}
+#endif
+#endif
+
+float foo1() {
+#pragma clang fp eval_method(extended)
+ float a;
+ double b;
+ return a - b;
+}
+
+float foo2() {
+#pragma clang fp eval_method(extended)
+ float_t a;
+ double_t b;
+ return a - b;
+}
+
+void foo3() {
+#pragma clang fp eval_method(extended)
+ char buff[sizeof(float_t)];
+ char bufd[sizeof(double_t)];
+ buff[1] = bufd[2];
+}
+
+float foo4() {
+#pragma clang fp eval_method(extended)
+ typedef float_t FT;
+ typedef double_t DT;
+ FT a;
+ DT b;
+ return a - b;
+}
+
+int foo5() {
+#pragma clang fp eval_method(extended)
+ int t = _Generic( 1.0L, float_t:1, default:0);
+ int v = _Generic( 1.0L, double_t:1, default:0);
+ return t;
+}
+
+void foo6() {
+#pragma clang fp eval_method(extended)
+ float f = (float_t)1;
+ double d = (double_t)2;
+}
+
+void foo7() {
+#pragma clang fp eval_method(extended)
+ float c1 = (float_t)12;
+ double c2 = (double_t)13;
+}
+
+float foo8() {
+#pragma clang fp eval_method(extended)
+ extern float_t f;
+ extern double_t g;
+ return f-g;
+}
+
+#ifdef CPP
+void foo9() {
+#pragma clang fp eval_method(extended)
+ auto resf = [](float_t f) { return f; };
+ auto resd = [](double_t g) { return g; };
+}
+
+void foo10() {
+#pragma clang fp eval_method(extended)
+ using Ft = float_t;
+ using Dt = double_t;
+ Ft a;
+ Dt b;
+}
+#endif
+
More information about the cfe-commits
mailing list