diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -4661,6 +4661,13 @@ 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 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4193,3 +4193,10 @@ 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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11558,6 +11558,10 @@ "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 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -798,13 +798,15 @@ 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 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6777,6 +6777,10 @@ 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 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8369,6 +8369,12 @@ D->addAttr(FunctionReturnThunksAttr::Create(S.Context, Kind, AL)); } +static void handleAvailableOnlyInDefaultEvalMethod(Sema &S, Decl *D, + const ParsedAttr &AL) { + assert(isa(D) && "This attribute only applies to a typedef"); + handleSimpleAttribute(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(D); @@ -9250,6 +9256,10 @@ 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 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -374,6 +374,16 @@ diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); + if (D->hasAttr()) { + 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(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 --- 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 --- /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@+1{{'available_only_in_default_eval_method' attribute only applies to typedefs}} +class __attribute__((available_only_in_default_eval_method)) C1 { +}; +// expected-error@+1{{'available_only_in_default_eval_method' attribute only applies to typedefs}} +class [[clang::available_only_in_default_eval_method]] C2 { +}; + +// expected-error@+1{{'available_only_in_default_eval_method' attribute only applies to typedefs}} +struct [[clang::available_only_in_default_eval_method]] S1; +// expected-error@+1{{'available_only_in_default_eval_method' attribute only applies to typedefs}} +struct __attribute__((available_only_in_default_eval_method)) S2; + +// expected-error@+1{{'available_only_in_default_eval_method' attribute only applies to typedefs}} +void __attribute__((available_only_in_default_eval_method)) foo(); +// expected-error@+1{{'available_only_in_default_eval_method' attribute cannot be applied to types}} +void [[clang::available_only_in_default_eval_method]] goo(); +// expected-error@+1{{'available_only_in_default_eval_method' attribute cannot be applied to types}} +void bar() [[clang::available_only_in_default_eval_method]]; +// expected-error@+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 --- /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 --- /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 --- /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 +