diff options
author | Timm Bäder <tbaeder@redhat.com> | 2024-02-21 16:38:01 +0100 |
---|---|---|
committer | Timm Bäder <tbaeder@redhat.com> | 2024-02-21 17:18:22 +0100 |
commit | ffcdf47bc443b36754c36bd6e1a77b4163657a00 (patch) | |
tree | 8ebbd1e59a49b550941f5cc5f85a62c324f52afc | |
parent | 3ee8c93769cd094ea0748b4a446a475160c0f51f (diff) | |
download | llvm-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.cpp | 11 | ||||
-rw-r--r-- | clang/test/AST/Interp/pointer-addition.c | 32 |
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}} +} |