Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -4594,6 +4594,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 Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ 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]; +} Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ 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">; Index: clang/include/clang/Basic/IdentifierTable.h =================================================================== --- clang/include/clang/Basic/IdentifierTable.h +++ clang/include/clang/Basic/IdentifierTable.h @@ -76,6 +76,19 @@ static constexpr int ObjCOrBuiltinIDBits = 16; +/// The "layout" of ObjCOrBuiltinID is: +/// - The first value (0) represents "not a special identifier". +/// - The next (NUM_OBJC_KEYWORDS - 1) values represent ObjCKeywordKinds (not +/// including objc_not_keyword). +/// - The next (NUM_INTERESTING_IDENTIFIERS - 1) values represent InterestingIdentifierKinds +/// (including not_interesting). +/// - The rest of the values represent builtin IDs (including not_builtin). +static constexpr int FirstObjCKeywordID = 1; +static constexpr int LastObjCKeywordID = FirstObjCKeywordID + tok::NUM_OBJC_KEYWORDS - 2; +static constexpr int FirstInterestingIdentifierID = LastObjCKeywordID + 1; +static constexpr int LastInterestingIdentifierID = LastObjCKeywordID + tok::NUM_INTERESTING_IDENTIFIERS; +static constexpr int FirstBuiltinID = LastInterestingIdentifierID + 1; + /// One of these records is kept for each identifier that /// is lexed. This contains information about whether the token was \#define'd, /// is a language keyword, or if it is a front-end token of some sort (e.g. a @@ -290,7 +303,9 @@ /// /// For example, 'class' will return tok::objc_class if ObjC is enabled. tok::ObjCKeywordKind getObjCKeywordID() const { - if (ObjCOrBuiltinID < tok::NUM_OBJC_KEYWORDS) + static_assert(FirstObjCKeywordID == 1, + "hard-coding this assumption to simplify code"); + if (ObjCOrBuiltinID <= LastObjCKeywordID) return tok::ObjCKeywordKind(ObjCOrBuiltinID); else return tok::objc_not_keyword; @@ -301,15 +316,27 @@ /// /// 0 is not-built-in. 1+ are specific builtin functions. unsigned getBuiltinID() const { - if (ObjCOrBuiltinID >= tok::NUM_OBJC_KEYWORDS) - return ObjCOrBuiltinID - tok::NUM_OBJC_KEYWORDS; + if (ObjCOrBuiltinID >= FirstBuiltinID) + return 1 + (ObjCOrBuiltinID - FirstBuiltinID); else return 0; } void setBuiltinID(unsigned ID) { - ObjCOrBuiltinID = ID + tok::NUM_OBJC_KEYWORDS; - assert(ObjCOrBuiltinID - unsigned(tok::NUM_OBJC_KEYWORDS) == ID - && "ID too large for field!"); + ObjCOrBuiltinID = FirstBuiltinID + (ID - 1); + assert(getBuiltinID() == ID && "ID too large for field!"); + } + + tok::InterestingIdentifierKind getInterestingIdentifierID() const { + if (ObjCOrBuiltinID >= FirstInterestingIdentifierID && + ObjCOrBuiltinID <= LastInterestingIdentifierID) + return tok::InterestingIdentifierKind(ObjCOrBuiltinID - + FirstInterestingIdentifierID); + else + return tok::not_interesting; + } + void setInterestingIdentifierID(unsigned ID) { + ObjCOrBuiltinID = FirstInterestingIdentifierID + ID; + assert(getInterestingIdentifierID() == ID && "ID too large for field!"); } unsigned getObjCOrBuiltinID() const { return ObjCOrBuiltinID; } 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. @@ -794,6 +797,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? @@ -974,3 +984,4 @@ #undef TOK #undef C99_KEYWORD #undef C2X_KEYWORD +#undef INTERESTING_IDENTIFIER 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,9 @@ #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/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -6441,6 +6441,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) @@ -6771,6 +6780,8 @@ Context.setsigjmp_bufDecl(NewTD); else if (II->isStr("ucontext_t")) Context.setucontext_tDecl(NewTD); + if (II->getInterestingIdentifierID() != 0) + NewTD->addAttr(AvailableOnlyInDefaultEvalMethodAttr::Create(Context)); } return NewTD; Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ 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); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ 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); Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -320,9 +320,10 @@ // Compiler builtins are always visible, regardless of where they end // up being declared. if (IdentifierInfo *Id = NameInfo.getName().getAsIdentifierInfo()) { - if (unsigned BuiltinID = Id->getBuiltinID()) { - if (!getSema().Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) - AllowHidden = true; + if (!Id->getInterestingIdentifierID()) + if (unsigned BuiltinID = Id->getBuiltinID()) { + if (!getSema().Context.BuiltinInfo.isPredefinedLibFunction(BuiltinID)) + AllowHidden = true; } } } @@ -943,6 +944,10 @@ // If this is a builtin on this (or all) targets, create the decl. if (unsigned BuiltinID = II->getBuiltinID()) { + if (unsigned ID = II->getInterestingIdentifierID()) { + // Don't think we should LazyCreate the interesting identifier? + return true; + } // In C++ and OpenCL (spec v1.2 s6.9.f), we don't have any predefined // library functions like 'malloc'. Instead, we'll just error. if ((getLangOpts().CPlusPlus || getLangOpts().OpenCL) && Index: clang/test/Sema/abi-check-1.cpp =================================================================== --- /dev/null +++ clang/test/Sema/abi-check-1.cpp @@ -0,0 +1,86 @@ +// 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)); +