https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112475
--- Comment #2 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Slightly further cleaned up: #pragma omp declare target struct S { virtual double foo (); }; #pragma omp end declare target S s; int main () { #pragma omp target s.foo (); } The problem is that ubsan_expand_vptr_ifn assumes the 4th argument to .UBSAN_VPTR is is_gimple_min_invariant which can be put into static variable, but the data mapping violates this, turns the original &_ZTI1S into mapping of _ZTI1S variable and so the argument no longer is constant. Now, on the ubsan_expand_vptr_ifn side we could certainly work around that by not using a global const variable in that case, but automatic variable into which we'd fill all the details at runtime, at the expense of ubsan not being able to diagnose problems just the first time they are encountered, but all the time (ubsan stores there after reporting a problem small change which makes it ignore subsequent errors on the same spot) and slower runtime performance. But the more important question is what we actually want to do with the _ZTI*, _ZTS* and _ZTV* vars, if we want to map them, or pretend they are declare target, or something else. Consider also #include <typeinfo> __attribute__((noipa)) void bar (const std::type_info &x) { } void foo () { bar (typeid (long)); } int main () { #pragma omp target bar (typeid (int)); } etc. So, before working around this in ubsan, I think it would be good to discuss what we want. E.g. the _ZTI/_ZTS/_ZTV var references in declare target functions, target regions and declare target variable initializers could be auto-discovered during the auto declare target function discovery and marked, ... With _ZTI/_ZTS it is easier, with _ZTV which contains also the virtual method pointers the question is if we mark all methods declare target, or fill in NULLs or what else we do, I admit I haven't paid much attention what the latest OpenMP standard currently requires for virtual method calls.