aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimm Bäder <tbaeder@redhat.com>2024-02-21 16:38:01 +0100
committerTimm Bäder <tbaeder@redhat.com>2024-02-21 17:18:22 +0100
commitffcdf47bc443b36754c36bd6e1a77b4163657a00 (patch)
tree8ebbd1e59a49b550941f5cc5f85a62c324f52afc
parent3ee8c93769cd094ea0748b4a446a475160c0f51f (diff)
downloadllvm-ffcdf47bc443b36754c36bd6e1a77b4163657a00.zip
llvm-ffcdf47bc443b36754c36bd6e1a77b4163657a00.tar.gz
llvm-ffcdf47bc443b36754c36bd6e1a77b4163657a00.tar.bz2
[clang][Interp] Allow adding an offset to a function pointer
Pretty sure this isn't doing anything, but it fixes a test and is generally the right thing to do. Fixing the behavior will come later.
-rw-r--r--clang/lib/AST/Interp/ByteCodeExprGen.cpp11
-rw-r--r--clang/test/AST/Interp/pointer-addition.c32
2 files changed, 37 insertions, 6 deletions
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d11d05d..0b08309 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1403,12 +1403,11 @@ bool ByteCodeExprGen<Emitter>::VisitPointerCompoundAssignOperator(
if (!LT || !RT)
return false;
- assert(*LT == PT_Ptr);
if (!visit(LHS))
return false;
- if (!this->emitLoadPtr(LHS))
+ if (!this->emitLoad(*LT, LHS))
return false;
if (!visit(RHS))
@@ -2828,7 +2827,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (!this->visit(SubExpr))
return false;
- if (T == PT_Ptr) {
+ if (T == PT_Ptr || T == PT_FnPtr) {
if (!this->emitIncPtr(E))
return false;
@@ -2846,7 +2845,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (!this->visit(SubExpr))
return false;
- if (T == PT_Ptr) {
+ if (T == PT_Ptr || T == PT_FnPtr) {
if (!this->emitDecPtr(E))
return false;
@@ -2864,7 +2863,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (!this->visit(SubExpr))
return false;
- if (T == PT_Ptr) {
+ if (T == PT_Ptr || T == PT_FnPtr) {
if (!this->emitLoadPtr(E))
return false;
if (!this->emitConstUint8(1, E))
@@ -2903,7 +2902,7 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (!this->visit(SubExpr))
return false;
- if (T == PT_Ptr) {
+ if (T == PT_Ptr || T == PT_FnPtr) {
if (!this->emitLoadPtr(E))
return false;
if (!this->emitConstUint8(1, E))
diff --git a/clang/test/AST/Interp/pointer-addition.c b/clang/test/AST/Interp/pointer-addition.c
new file mode 100644
index 0000000..80ab670
--- /dev/null
+++ b/clang/test/AST/Interp/pointer-addition.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 %s -fsyntax-only -verify=gnu,expected -pedantic -Wextra -std=c11 -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 %s -fsyntax-only -triple i686-unknown-unknown -verify=gnu,expected -pedantic -Wextra -std=c11 -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 %s -fsyntax-only -triple x86_64-unknown-unknown -verify=gnu,expected -pedantic -Wextra -std=c11 -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 %s -fsyntax-only -verify -pedantic -Wextra -Wno-gnu -std=c11 -fexperimental-new-constant-interpreter
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef struct S S; // expected-note 4 {{forward declaration of 'struct S'}}
+extern _Atomic(S*) e;
+void a(S* b, void* c) {
+ void (*fp)(int) = 0;
+ b++; // expected-error {{arithmetic on a pointer to an incomplete type}}
+ b += 1; // expected-error {{arithmetic on a pointer to an incomplete type}}
+ c++; // gnu-warning {{arithmetic on a pointer to void is a GNU extension}}
+ c += 1; // gnu-warning {{arithmetic on a pointer to void is a GNU extension}}
+ c--; // gnu-warning {{arithmetic on a pointer to void is a GNU extension}}
+ c -= 1; // gnu-warning {{arithmetic on a pointer to void is a GNU extension}}
+ (void) c[1]; // gnu-warning {{subscript of a pointer to void is a GNU extension}}
+ b = 1+b; // expected-error {{arithmetic on a pointer to an incomplete type}}
+ /* The next couple tests are only pedantic warnings in gcc */
+ void (*d)(S*,void*) = a;
+ d += 1; // gnu-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' (aka 'void (struct S *, void *)') is a GNU extension}}
+ d++; // gnu-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' (aka 'void (struct S *, void *)') is a GNU extension}}
+ d--; // gnu-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' (aka 'void (struct S *, void *)') is a GNU extension}}
+ d -= 1; // gnu-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' (aka 'void (struct S *, void *)') is a GNU extension}}
+ (void)(1 + d); // gnu-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' (aka 'void (struct S *, void *)') is a GNU extension}}
+ e++; // expected-error {{arithmetic on a pointer to an incomplete type}}
+ intptr_t i = (intptr_t)b;
+ char *f = (char*)0 + i; // gnu-warning {{arithmetic on a null pointer treated as a cast from integer to pointer is a GNU extension}}
+ // Cases that don't match the GNU inttoptr idiom get a different warning.
+ f = (char*)0 - i; // expected-warning {{performing pointer arithmetic on a null pointer has undefined behavior}}
+ int *g = (int*)0 + i; // expected-warning {{performing pointer arithmetic on a null pointer has undefined behavior}}
+}