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
This commit is contained in:
Zahira Ammarguellat 2023-06-22 17:28:22 -04:00
parent 55d0411968
commit 63b0b82fd6
12 changed files with 376 additions and 1 deletions

View File

@ -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

View File

@ -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];
}

View File

@ -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">;

View File

@ -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?

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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