Skip to content

Commit

Permalink
When float_t and double_t types are used inside a scope with
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zahiraam committed Jun 23, 2023
1 parent 55d0411 commit 63b0b82
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 1 deletion.
7 changes: 7 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}

4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions clang/test/Sema/attr-only-in-default-eval.cpp
Original file line number Diff line number Diff line change
@@ -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));

102 changes: 102 additions & 0 deletions clang/test/Sema/fp-eval-pragma-with-float-double_t-1.c
Original file line number Diff line number Diff line change
@@ -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
100 changes: 100 additions & 0 deletions clang/test/Sema/fp-eval-pragma-with-float-double_t-2.c
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 63b0b82

Please sign in to comment.