Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -4363,6 +4363,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 @@ -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/Builtins.h =================================================================== --- clang/include/clang/Basic/Builtins.h +++ clang/include/clang/Basic/Builtins.h @@ -62,6 +62,7 @@ namespace Builtin { enum ID { +#define INTERESTING_IDENTIER(ID, TYPE, ATTRS) ID, NotBuiltin = 0, // This is not a builtin function. #define BUILTIN(ID, TYPE, ATTRS) BI##ID, #include "clang/Basic/Builtins.def" Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -116,6 +116,13 @@ # define LANGBUILTIN(ID, TYPE, ATTRS, BUILTIN_LANG) BUILTIN(ID, TYPE, ATTRS) #endif +#ifndef INTERESTING_IDENTIFIER +#define INTERESTING_IDENTIFIER(ID) +#endif + +INTERESTING_IDENTIFIER(float_t) +INTERESTING_IDENTIFIER(double_t) + // Standard libc/libm functions: BUILTIN(__builtin_atan2 , "ddd" , "Fne") BUILTIN(__builtin_atan2f, "fff" , "Fne") @@ -1743,3 +1750,4 @@ #undef BUILTIN #undef LIBBUILTIN #undef LANGBUILTIN +#undef INTERESTING_IDENTIFIER 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 @@ -75,6 +75,8 @@ enum { IdentifierInfoAlignment = 8 }; static constexpr int ObjCOrBuiltinIDBits = 16; +static constexpr int FirstInterestingIdentifierID = 1; +static constexpr int FirstBuiltinID = 3; /// One of these records is kept for each identifier that /// is lexed. This contains information about whether the token was \#define'd, @@ -301,15 +303,39 @@ /// /// 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 >= + (tok::NUM_OBJC_KEYWORDS + tok::NUM_INTERESTING_IDENTIFIERS)) + return ObjCOrBuiltinID - + (tok::NUM_OBJC_KEYWORDS + tok::NUM_INTERESTING_IDENTIFIERS); 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 = + ID + tok::NUM_OBJC_KEYWORDS + tok::NUM_INTERESTING_IDENTIFIERS; + assert(ObjCOrBuiltinID - unsigned(tok::NUM_OBJC_KEYWORDS + + tok::NUM_INTERESTING_IDENTIFIERS) == + ID && + "ID too large for field!"); + } + + unsigned getInterestingIdentifierID() { + unsigned LowerID = tok::NUM_INTERESTING_IDENTIFIERS + tok::NUM_OBJC_KEYWORDS; + unsigned UpperID = tok::NUM_INTERESTING_IDENTIFIERS + tok::NUM_OBJC_KEYWORDS + + (tok::NUM_INTERESTING_IDENTIFIERS - 1); + if (ObjCOrBuiltinID <= UpperID && ObjCOrBuiltinID > LowerID) + return ObjCOrBuiltinID - LowerID; + else + return tok::not_interesting; + } + + void setInterestingIdentifierID(unsigned ID) { + ObjCOrBuiltinID = + ID + tok::NUM_INTERESTING_IDENTIFIERS + tok::NUM_OBJC_KEYWORDS; + assert(ObjCOrBuiltinID - unsigned(tok::NUM_INTERESTING_IDENTIFIERS + + tok::NUM_OBJC_KEYWORDS) == + 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. @@ -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/lib/Basic/Builtins.cpp =================================================================== --- clang/lib/Basic/Builtins.cpp +++ clang/lib/Basic/Builtins.cpp @@ -30,7 +30,9 @@ } static constexpr Builtin::Info BuiltinInfo[] = { - {"not a builtin function", nullptr, nullptr, nullptr, HeaderDesc::NO_HEADER, +#define INTERESTING_IDENTIFIER(ID) \ + {#ID, nullptr, nullptr, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, +{"not a builtin function", nullptr, nullptr, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, #define BUILTIN(ID, TYPE, ATTRS) \ {#ID, TYPE, ATTRS, nullptr, HeaderDesc::NO_HEADER, ALL_LANGUAGES}, @@ -127,23 +129,29 @@ /// such. void Builtin::Context::initializeBuiltins(IdentifierTable &Table, const LangOptions& LangOpts) { - // Step #1: mark all target-independent builtins with their ID's. - for (unsigned i = Builtin::NotBuiltin+1; i != Builtin::FirstTSBuiltin; ++i) + // Step #1: Register interesting identifiers. + for (unsigned i = FirstInterestingIdentifierID; + i <= tok::NUM_INTERESTING_IDENTIFIERS - 1; ++i) + Table.get(BuiltinInfo[i].Name).setInterestingIdentifierID(i); + + // Step #2: mark all target-independent builtins with their ID's. + for (unsigned i = FirstBuiltinID; i != Builtin::FirstTSBuiltin; ++i) if (builtinIsSupported(BuiltinInfo[i], LangOpts)) { Table.get(BuiltinInfo[i].Name).setBuiltinID(i); } - // Step #2: Register target-specific builtins. + // Step #3: Register target-specific builtins. for (unsigned i = 0, e = TSRecords.size(); i != e; ++i) if (builtinIsSupported(TSRecords[i], LangOpts)) - Table.get(TSRecords[i].Name).setBuiltinID(i + Builtin::FirstTSBuiltin); + Table.get(TSRecords[i].Name) + .setBuiltinID(i + Builtin::FirstTSBuiltin); - // Step #3: Register target-specific builtins for AuxTarget. + // Step #4: Register target-specific builtins for AuxTarget. for (unsigned i = 0, e = AuxTSRecords.size(); i != e; ++i) Table.get(AuxTSRecords[i].Name) .setBuiltinID(i + Builtin::FirstTSBuiltin + TSRecords.size()); - // Step #4: Unregister any builtins specified by -fno-builtin-foo. + // Step #5: Unregister any builtins specified by -fno-builtin-foo. for (llvm::StringRef Name : LangOpts.NoBuiltinFuncs) { bool InStdNamespace = Name.consume_front("std-"); auto NameIt = Table.find(Name); 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,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 @@ -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/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,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)); +