Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -4363,6 +4363,13 @@ a = b[i] * c[i] + e; } +Note: the types ``float_t`` and ``double_t`` are defined in the math header file. +Their definition changes with the eval method. If they are used inside a scope +containing a ``#pragma clang fp eval_method`` their definition end up being +different than what the user might have intended. This can potentially generate +incorrect code, leading to an ABI mismatch. This case is prevented by emitting a +diagnostic. + 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 Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -4168,3 +4168,9 @@ 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]; +} Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11516,6 +11516,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< Index: clang/include/clang/Basic/IdentifierTable.h =================================================================== --- clang/include/clang/Basic/IdentifierTable.h +++ clang/include/clang/Basic/IdentifierTable.h @@ -93,6 +93,8 @@ // the remaining values are for builtins. unsigned ObjCOrBuiltinID : ObjCOrBuiltinIDBits; + unsigned InterestingIdentifierID : 5; + // True if there is a #define for this. unsigned HasMacro : 1; @@ -297,6 +299,16 @@ } void setObjCKeywordID(tok::ObjCKeywordKind ID) { ObjCOrBuiltinID = ID; } + void setInterestingIdentifierID(tok::InterestingIdentifierKind ID) { + InterestingIdentifierID = ID; + } + tok::InterestingIdentifierKind getInterestingIdentifierID() { + if (InterestingIdentifierID < tok::NUM_INTERESTING_IDENTIFIERS) + return tok::InterestingIdentifierKind(InterestingIdentifierID); + else + return tok::not_interesting; + } + /// Return a value indicating whether this is a builtin function. /// /// 0 is not-built-in. 1+ are specific builtin functions. Index: clang/include/clang/Basic/TokenKinds.h =================================================================== --- clang/include/clang/Basic/TokenKinds.h +++ clang/include/clang/Basic/TokenKinds.h @@ -44,6 +44,14 @@ NUM_OBJC_KEYWORDS }; +/// Provides a namespace for interesting identifers such as float_t and +/// double_t. +enum InterestingIdentifierKind { +#define Interesting_Identifier(X) X, +#include "clang/Basic/TokenKinds.def" + NUM_INTERESTING_IDENTIFIERS +}; + /// Defines the possible values of an on-off-switch (C99 6.10.6p2). enum OnOffSwitch { OOS_ON, OOS_OFF, OOS_DEFAULT Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -85,6 +85,9 @@ #ifndef PRAGMA_ANNOTATION #define PRAGMA_ANNOTATION(X) ANNOTATION(X) #endif +#ifndef Interesting_Identifier +#define Interesting_Identifier(X) +#endif //===----------------------------------------------------------------------===// // Preprocessor keywords. @@ -787,6 +790,13 @@ OBJC_AT_KEYWORD(import) OBJC_AT_KEYWORD(available) +//===----------------------------------------------------------------------===// +// Interesting idenitifiers. +//===----------------------------------------------------------------------===// +Interesting_Identifier(not_interesting) +Interesting_Identifier(float_t) +Interesting_Identifier(double_t) + // TODO: What to do about context-sensitive keywords like: // bycopy/byref/in/inout/oneway/out? @@ -964,3 +974,4 @@ #undef TOK #undef C99_KEYWORD #undef C2X_KEYWORD +#undef Interesting_Identifier Index: clang/include/clang/Lex/Token.h =================================================================== --- clang/include/clang/Lex/Token.h +++ clang/include/clang/Lex/Token.h @@ -283,6 +283,9 @@ /// Return the ObjC keyword kind. tok::ObjCKeywordKind getObjCKeywordID() const; + bool isInterestingTypeKeywordID(tok::InterestingIdentifierKind BTkey) const; + tok::InterestingIdentifierKind getInterestingIdentifierID() const; + /// Return true if this token has trigraphs or escaped newlines in it. bool needsCleaning() const { return getFlag(NeedsCleaning); } Index: clang/lib/Basic/IdentifierTable.cpp =================================================================== --- clang/lib/Basic/IdentifierTable.cpp +++ clang/lib/Basic/IdentifierTable.cpp @@ -279,6 +279,13 @@ Table.get(Name).setObjCKeywordID(ObjCID); } +static void AddInterestingIdentifier(StringRef Name, + tok::InterestingIdentifierKind BTID, + IdentifierTable &Table) { + IdentifierInfo &Info = Table.get(Name, tok::identifier); + Info.setInterestingIdentifierID(BTID); +} + /// AddKeywords - Add all keywords to the symbol table. /// void IdentifierTable::AddKeywords(const LangOptions &LangOpts) { @@ -295,6 +302,8 @@ #define OBJC_AT_KEYWORD(NAME) \ if (LangOpts.ObjC) \ AddObjCKeyword(StringRef(#NAME), tok::objc_##NAME, *this); +#define Interesting_Identifier(NAME) \ + AddInterestingIdentifier(StringRef(#NAME), tok::##NAME, *this); #define TESTING_KEYWORD(NAME, FLAGS) #include "clang/Basic/TokenKinds.def" Index: clang/lib/Lex/Lexer.cpp =================================================================== --- clang/lib/Lex/Lexer.cpp +++ clang/lib/Lex/Lexer.cpp @@ -70,6 +70,22 @@ return specId ? specId->getObjCKeywordID() : tok::objc_not_keyword; } +bool Token::isInterestingTypeKeywordID( + tok::InterestingIdentifierKind BTkey) const { + if (isAnnotation()) + return false; + if (IdentifierInfo *II = getIdentifierInfo()) + return II->getInterestingIdentifierID() == BTkey; + return false; +} + +tok::InterestingIdentifierKind Token::getInterestingIdentifierID() const { + if (isAnnotation()) + return tok::not_interesting; + IdentifierInfo *specId = getIdentifierInfo(); + return specId ? specId->getInterestingIdentifierID() : tok::not_interesting; +} + //===----------------------------------------------------------------------===// // Lexer Class Implementation //===----------------------------------------------------------------------===// Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -6485,6 +6485,12 @@ } } + if (Tok.getInterestingIdentifierID()) { + D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation()); + D.SetRangeEnd(Tok.getLocation()); + ConsumeToken(); + goto PastIdentifier; + } if (Tok.is(tok::l_paren)) { // If this might be an abstract-declarator followed by a direct-initializer, // check whether this is a valid declarator chunk. If it can't be, assume Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -6436,6 +6436,15 @@ if (!New) return nullptr; + if (New->hasAttr()) { + if (getLangOpts().getFPEvalMethod() != + LangOptions::FPEvalMethodKind::FEM_UnsetOnCommandLine && + PP.getLastFPEvalPragmaLocation().isValid() && + PP.getCurrentFPEvalMethod() != getLangOpts().getFPEvalMethod()) + Diag(New->getLocation(), + diag::err_type_available_only_in_default_eval_method) + << New->getName(); + } // If this has an identifier and is not a function template specialization, // add it to the scope stack. if (New->getDeclName() && AddToScope) @@ -6766,6 +6775,9 @@ Context.setsigjmp_bufDecl(NewTD); else if (II->isStr("ucontext_t")) Context.setucontext_tDecl(NewTD); + if (II->getInterestingIdentifierID() != tok::not_interesting) { + NewTD->addAttr(AvailableOnlyInDefaultEvalMethodAttr::Create(Context)); + } } return NewTD; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -8258,6 +8258,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); @@ -9136,6 +9142,9 @@ case ParsedAttr::AT_FunctionReturnThunks: handleFunctionReturnThunksAttr(S, D, AL); break; + case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod: + handleAvailableOnlyInDefaultEvalMethod(S, D, AL); + break; // Microsoft attributes: case ParsedAttr::AT_LayoutVersion: Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -375,6 +375,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); Index: clang/test/Sema/abi-check-1.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-1.cpp @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DNOERROR %s +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source -DNOERROR %s + +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=double %s +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=extended %s + +#ifdef NOERROR +// expected-no-diagnostics +typedef float float_t; +typedef double double_t; +#else +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}} +#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) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; +} + +void foo7() { +#pragma clang fp eval_method(source) + float f = (float_t)1; + double d = (double_t)2; +} + +void foo8() { +#pragma clang fp eval_method(source) + using Ft = float_t; + using Dt = double_t; + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(source) + float c1 = (float_t)12; + double c2 = (double_t)13; +} + +float foo10() { +#pragma clang fp eval_method(source) + extern float_t f; + extern double_t g; + return f-g; +} Index: clang/test/Sema/abi-check-2.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-2.cpp @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DNOERROR %s +// RUN: %clang_cc1 -fsyntax-only -verify -ffp-eval-method=double -DNOERROR %s + +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source %s +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=extended %s + +#ifdef NOERROR +// expected-no-diagnostics +typedef float float_t; +typedef double double_t; +#else +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}} +#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) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; +} + +void foo7() { +#pragma clang fp eval_method(double) + float f = (float_t)1; + double d = (double_t)2; +} + +void foo8() { +#pragma clang fp eval_method(double) + using Ft = float_t; + using Dt = double_t; + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(double) + float c1 = (float_t)12; + double c2 = (double_t)13; +} + +float foo10() { +#pragma clang fp eval_method(double) + extern float_t f; + extern double_t g; + return f-g; +} Index: clang/test/Sema/abi-check-3.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-3.cpp @@ -0,0 +1,86 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -DNOERROR %s +// RUN: %clang_cc1 -fsyntax-only -verify -ffp-eval-method=extended -DNOERROR %s + +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=source %s +// RUN: %clang_cc1 -verify -fsyntax-only -ffp-eval-method=double %s + + +#ifdef NOERROR +// expected-no-diagnostics +typedef float float_t; +typedef double double_t; +#else +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}} +#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) + auto resf = [](float_t f) { return f; }; + auto resd = [](double_t g) { return g; }; +} + +void foo7() { +#pragma clang fp eval_method(extended) + float f = (float_t)1; + double d = (double_t)2; +} + +void foo8() { +#pragma clang fp eval_method(extended) + using Ft = float_t; + using Dt = double_t; + Ft a; + Dt b; +} + +void foo9() { +#pragma clang fp eval_method(extended) + float c1 = (float_t)12; + double c2 = (double_t)13; +} + +float foo10() { +#pragma clang fp eval_method(extended) + extern float_t f; + extern double_t g; + return f-g; +} Index: clang/test/Sema/attr-only-in-default-eval.cpp =================================================================== --- /dev/null +++ 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)); +