[CUDA] Emit deferred diagnostics during Sema rather than during codegen.
Summary:
Emitting deferred diagnostics during codegen was a hack. It did work,
but usability was poor, both for us as compiler devs and for users. We
don't codegen if there are any sema errors, so for users this meant that
they wouldn't see deferred errors if there were any non-deferred errors.
For devs, this meant that we had to carefully split up our tests so that
when we tested deferred errors, we didn't emit any non-deferred errors.
This change moves checking for deferred errors into Sema. See the big
comment in SemaCUDA.cpp for an overview of the idea.
This checking adds overhead to compilation, because we have to maintain
a partial call graph. As a result, this change makes deferred errors a
CUDA-only concept (whereas before they were a general concept). If
anyone else wants to use this framework for something other than CUDA,
we can generalize at that time.
This patch makes the minimal set of test changes -- after this lands,
I'll go back through and do a cleanup of the tests that we no longer
have to split up.
Reviewers: rnk
Subscribers: cfe-commits, rsmith, tra
Differential Revision: https://reviews.llvm.org/D25541
llvm-svn: 284158
2016-10-14 04:52:12 +08:00
|
|
|
// RUN: %clang_cc1 %s --std=c++11 -triple nvptx-unknown-unknown -fcuda-is-device \
|
2016-10-20 05:15:01 +08:00
|
|
|
// RUN: -emit-llvm -o /dev/null -verify -verify-ignore-unexpected=note
|
2016-08-16 07:00:49 +08:00
|
|
|
|
|
|
|
// Note: This test won't work with -fsyntax-only, because some of these errors
|
|
|
|
// are emitted during codegen.
|
|
|
|
|
|
|
|
#include "Inputs/cuda.h"
|
|
|
|
|
|
|
|
extern "C" void host_fn() {}
|
2016-10-22 04:50:47 +08:00
|
|
|
// expected-note@-1 7 {{'host_fn' declared here}}
|
2016-10-08 09:07:11 +08:00
|
|
|
|
|
|
|
struct Dummy {};
|
2016-08-16 07:00:49 +08:00
|
|
|
|
|
|
|
struct S {
|
|
|
|
S() {}
|
2016-10-22 04:50:47 +08:00
|
|
|
// expected-note@-1 2 {{'S' declared here}}
|
2016-08-16 07:00:49 +08:00
|
|
|
~S() { host_fn(); }
|
2016-08-16 08:48:21 +08:00
|
|
|
// expected-note@-1 {{'~S' declared here}}
|
2016-08-16 07:00:49 +08:00
|
|
|
int x;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct T {
|
|
|
|
__host__ __device__ void hd() { host_fn(); }
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
|
|
|
|
|
|
|
// No error; this is (implicitly) inline and is never called, so isn't
|
|
|
|
// codegen'ed.
|
|
|
|
__host__ __device__ void hd2() { host_fn(); }
|
|
|
|
|
|
|
|
__host__ __device__ void hd3();
|
|
|
|
|
|
|
|
void h() {}
|
2016-08-16 08:48:21 +08:00
|
|
|
// expected-note@-1 {{'h' declared here}}
|
2016-10-08 09:07:11 +08:00
|
|
|
|
|
|
|
void operator+();
|
|
|
|
// expected-note@-1 {{'operator+' declared here}}
|
|
|
|
|
|
|
|
void operator-(const T&) {}
|
|
|
|
// expected-note@-1 {{'operator-' declared here}}
|
|
|
|
|
|
|
|
operator Dummy() { return Dummy(); }
|
|
|
|
// expected-note@-1 {{'operator Dummy' declared here}}
|
2016-10-11 08:21:10 +08:00
|
|
|
|
2018-09-22 01:29:33 +08:00
|
|
|
__host__ void operator delete(void *) { host_fn(); };
|
|
|
|
__device__ void operator delete(void*, __SIZE_TYPE__);
|
2016-10-11 08:21:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct U {
|
2018-09-22 01:29:33 +08:00
|
|
|
__device__ void operator delete(void*, __SIZE_TYPE__) = delete;
|
2016-10-11 08:21:10 +08:00
|
|
|
__host__ __device__ void operator delete(void*);
|
2016-08-16 07:00:49 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
__host__ __device__ void T::hd3() {
|
|
|
|
host_fn();
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T> __host__ __device__ void hd2() { host_fn(); }
|
2019-03-06 02:19:35 +08:00
|
|
|
// expected-error@-1 2 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
2016-08-16 07:00:49 +08:00
|
|
|
__global__ void kernel() { hd2<int>(); }
|
|
|
|
|
|
|
|
__host__ __device__ void hd() { host_fn(); }
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
|
|
|
|
|
|
|
template <typename T> __host__ __device__ void hd3() { host_fn(); }
|
2019-03-06 02:19:35 +08:00
|
|
|
// expected-error@-1 2 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
2016-08-16 07:00:49 +08:00
|
|
|
__device__ void device_fn() { hd3<int>(); }
|
|
|
|
|
|
|
|
// No error because this is never instantiated.
|
|
|
|
template <typename T> __host__ __device__ void hd4() { host_fn(); }
|
|
|
|
|
|
|
|
__host__ __device__ void local_var() {
|
|
|
|
S s;
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'S' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void placement_new(char *ptr) {
|
|
|
|
::new(ptr) S();
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'S' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void explicit_destructor(S *s) {
|
|
|
|
s->~S();
|
|
|
|
// expected-error@-1 {{reference to __host__ function '~S' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
2016-10-11 08:21:10 +08:00
|
|
|
__host__ __device__ void class_specific_delete(T *t, U *u) {
|
|
|
|
delete t; // ok, call sized device delete even though host has preferable non-sized version
|
|
|
|
delete u; // ok, call non-sized HD delete rather than sized D delete
|
|
|
|
}
|
|
|
|
|
2016-08-16 07:00:49 +08:00
|
|
|
__host__ __device__ void hd_member_fn() {
|
|
|
|
T t;
|
|
|
|
// Necessary to trigger an error on T::hd. It's (implicitly) inline, so
|
|
|
|
// isn't codegen'ed until we call it.
|
|
|
|
t.hd();
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void h_member_fn() {
|
|
|
|
T t;
|
|
|
|
t.h();
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'h' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void fn_ptr() {
|
|
|
|
auto* ptr = &host_fn;
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
__host__ __device__ void fn_ptr_template() {
|
|
|
|
auto* ptr = &host_fn; // Not an error because the template isn't instantiated.
|
|
|
|
}
|
2016-10-08 09:07:11 +08:00
|
|
|
|
|
|
|
__host__ __device__ void unaryOp() {
|
|
|
|
T t;
|
|
|
|
(void) +t; // expected-error {{reference to __host__ function 'operator+' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void binaryOp() {
|
|
|
|
T t;
|
|
|
|
(void) (t - t); // expected-error {{reference to __host__ function 'operator-' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
__host__ __device__ void implicitConversion() {
|
|
|
|
T t;
|
|
|
|
Dummy d = t; // expected-error {{reference to __host__ function 'operator Dummy' in __host__ __device__ function}}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct TmplStruct {
|
|
|
|
template <typename U> __host__ __device__ void fn() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
template <>
|
|
|
|
__host__ __device__ void TmplStruct<int>::fn<int>() { host_fn(); }
|
|
|
|
// expected-error@-1 {{reference to __host__ function 'host_fn' in __host__ __device__ function}}
|
|
|
|
|
|
|
|
__device__ void double_specialization() { TmplStruct<int>().fn<int>(); }
|