Replace "system crate" vs "user code" distinction with zombies everywhere.

This commit is contained in:
Eduard-Mihai Burtescu 2023-04-19 04:55:51 +03:00 committed by Eduard-Mihai Burtescu
parent 633dff18bd
commit dcd2c74054
12 changed files with 153 additions and 192 deletions

View File

@ -210,7 +210,7 @@ impl<'tcx> RecursivePointeeCache<'tcx> {
cx.zombie_with_span(
new_id,
span,
"Cannot create self-referential types, even through pointers",
"cannot create self-referential types, even through pointers",
);
Some(new_id)
}

View File

@ -171,7 +171,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if invalid_seq_cst {
self.zombie(
semantics.def(self),
"Cannot use AtomicOrdering=SequentiallyConsistent on Vulkan memory model. Check if AcquireRelease fits your needs.",
"cannot use AtomicOrdering=SequentiallyConsistent on Vulkan memory model \
(check if AcquireRelease fits your needs)",
);
}
semantics
@ -352,11 +353,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
fn zombie_convert_ptr_to_u(&self, def: Word) {
self.zombie(def, "Cannot convert pointers to integers");
self.zombie(def, "cannot convert pointers to integers");
}
fn zombie_convert_u_to_ptr(&self, def: Word) {
self.zombie(def, "Cannot convert integers to pointers");
self.zombie(def, "cannot convert integers to pointers");
}
fn zombie_ptr_equal(&self, def: Word, inst: &str) {
@ -1453,21 +1454,16 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
.with_type(dest_ty);
if val_is_ptr || dest_is_ptr {
if self.is_system_crate(self.span()) {
self.zombie(
result.def(self),
&format!(
"Cannot cast between pointer and non-pointer types. From: {}. To: {}.",
self.debug_type(val.ty),
self.debug_type(dest_ty)
),
);
} else {
self.struct_err("Cannot cast between pointer and non-pointer types")
.note(&format!("from: {}", self.debug_type(val.ty)))
.note(&format!("to: {}", self.debug_type(dest_ty)))
.emit();
}
self.zombie(
result.def(self),
&format!(
"cannot cast between pointer and non-pointer types\
\nfrom `{}`\
\n to `{}`",
self.debug_type(val.ty),
self.debug_type(dest_ty)
),
);
}
result
@ -1884,7 +1880,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
empty(),
)
.unwrap();
self.zombie(dst.def(self), "Cannot memcpy dynamically sized data");
self.zombie(dst.def(self), "cannot memcpy dynamically sized data");
}
}

View File

@ -11,7 +11,7 @@ use rustc_target::abi::{Align, Size};
impl<'a, 'tcx> Builder<'a, 'tcx> {
fn load_err(&mut self, original_type: Word, invalid_type: Word) -> SpirvValue {
let mut err = self.struct_err(&format!(
"Cannot load type {} in an untyped buffer load",
"cannot load type {} in an untyped buffer load",
self.debug_type(original_type)
));
if original_type != invalid_type {
@ -206,7 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn store_err(&mut self, original_type: Word, value: SpirvValue) -> Result<(), ErrorGuaranteed> {
let mut err = self.struct_err(&format!(
"Cannot store type {} in an untyped buffer store",
"cannot store type {} in an untyped buffer store",
self.debug_type(original_type)
));
if original_type != value.ty {

View File

@ -208,7 +208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.with_type(result_type);
self.zombie(
result.def(self),
"Cannot offset a pointer to an arbitrary element",
"cannot offset a pointer to an arbitrary element",
);
result
}
@ -219,7 +219,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let width = match self.lookup_type(shift.ty) {
SpirvType::Integer(width, _) => width,
other => self.fatal(&format!(
"Cannot rotate non-integer type: {}",
"cannot rotate non-integer type: {}",
other.debug(shift.ty, self)
)),
};

View File

@ -140,9 +140,7 @@ impl SpirvValue {
IllegalConst::Indirect(cause) => cause.message(),
};
// HACK(eddyb) we don't know whether this constant originated
// in a system crate, so it's better to always zombie.
cx.zombie_even_in_user_code(id, span, msg);
cx.zombie_with_span(id, span, msg);
id
}
@ -158,25 +156,15 @@ impl SpirvValue {
}
SpirvValueKind::FnAddr { .. } => {
if cx.is_system_crate(span) {
cx.builder
.const_to_id
.borrow()
.get(&WithType {
ty: self.ty,
val: SpirvConst::ZombieUndefForFnAddr,
})
.expect("FnAddr didn't go through proper undef registration")
.val
} else {
cx.tcx.sess.span_err(
span,
"Cannot use this function pointer for anything other than calls",
);
// Because we never get beyond compilation (into e.g. linking),
// emitting an invalid ID reference here is OK.
0
}
cx.builder
.const_to_id
.borrow()
.get(&WithType {
ty: self.ty,
val: SpirvConst::ZombieUndefForFnAddr,
})
.expect("FnAddr didn't go through proper undef registration")
.val
}
SpirvValueKind::LogicalPtrCast {
@ -184,24 +172,17 @@ impl SpirvValue {
original_pointee_ty,
zombie_target_undef,
} => {
if cx.is_system_crate(span) {
cx.zombie_with_span(
zombie_target_undef,
span,
&format!(
"Cannot cast between pointer types. From: {}. To: {}.",
cx.debug_type(original_pointee_ty),
cx.debug_type(self.ty)
),
);
} else {
cx.tcx
.sess
.struct_span_err(span, "Cannot cast between pointer types")
.note(&format!("from: *{}", cx.debug_type(original_pointee_ty)))
.note(&format!("to: {}", cx.debug_type(self.ty)))
.emit();
}
cx.zombie_with_span(
zombie_target_undef,
span,
&format!(
"cannot cast between pointer types\
\nfrom `*{}`\
\n to `{}`",
cx.debug_type(original_pointee_ty),
cx.debug_type(self.ty)
),
);
zombie_target_undef
}

View File

@ -255,11 +255,8 @@ impl<'tcx> ConstMethods<'tcx> for CodegenCx<'tcx> {
self.constant_null(ty)
} else {
let result = self.undef(ty);
// HACK(eddyb) we don't know whether this constant originated
// in a system crate, so it's better to always zombie.
self.zombie_even_in_user_code(
self.zombie_no_span(
result.def_cx(self),
DUMMY_SP,
"pointer has non-null integer address",
);
result
@ -408,7 +405,7 @@ impl<'tcx> CodegenCx<'tcx> {
SpirvType::Void => self
.tcx
.sess
.fatal("Cannot create const alloc of type void"),
.fatal("cannot create const alloc of type void"),
SpirvType::Bool
| SpirvType::Integer(..)
| SpirvType::Float(_)
@ -552,37 +549,36 @@ impl<'tcx> CodegenCx<'tcx> {
*data = *c + asdf->y[*c];
}
*/
// HACK(eddyb) we don't know whether this constant originated
// in a system crate, so it's better to always zombie.
self.zombie_even_in_user_code(
result.def_cx(self),
DUMMY_SP,
"constant runtime array value",
);
// NOTE(eddyb) the above description is a bit outdated, it's now
// clear `OpTypeRuntimeArray` does not belong in user code, and
// is only for dynamically-sized SSBOs and descriptor indexing,
// and a general solution looks similar to `union` handling, but
// for the length of a fixed-length array.
self.zombie_no_span(result.def_cx(self), "constant `OpTypeRuntimeArray` value");
result
}
SpirvType::Function { .. } => self
.tcx
.sess
.fatal("TODO: SpirvType::Function not supported yet in create_const_alloc"),
SpirvType::Image { .. } => self.tcx.sess.fatal("Cannot create a constant image value"),
SpirvType::Image { .. } => self.tcx.sess.fatal("cannot create a constant image value"),
SpirvType::Sampler => self
.tcx
.sess
.fatal("Cannot create a constant sampler value"),
.fatal("cannot create a constant sampler value"),
SpirvType::SampledImage { .. } => self
.tcx
.sess
.fatal("Cannot create a constant sampled image value"),
.fatal("cannot create a constant sampled image value"),
SpirvType::InterfaceBlock { .. } => self
.tcx
.sess
.fatal("Cannot create a constant interface block value"),
.fatal("cannot create a constant interface block value"),
SpirvType::AccelerationStructureKhr => self
.tcx
.sess
.fatal("Cannot create a constant acceleration structure"),
SpirvType::RayQueryKhr => self.tcx.sess.fatal("Cannot create a constant ray query"),
.fatal("cannot create a constant acceleration structure"),
SpirvType::RayQueryKhr => self.tcx.sess.fatal("cannot create a constant ray query"),
}
}
}

View File

@ -217,7 +217,8 @@ impl<'tcx> CodegenCx<'tcx> {
.variable(ptr_ty, None, StorageClass::Private, None)
.with_type(ptr_ty);
// TODO: These should be StorageClass::Private, so just zombie for now.
self.zombie_with_span(result.def_cx(self), span, "Globals are not supported yet");
// FIXME(eddyb) why zombie? this looks like it should just work nowadays.
self.zombie_with_span(result.def_cx(self), span, "globals are not supported yet");
result
}
}

View File

@ -70,7 +70,7 @@ impl<'tcx> CodegenCx<'tcx> {
} else {
self.tcx
.sess
.span_err(span, format!("Cannot declare {name} as an entry point"));
.span_err(span, format!("cannot declare {name} as an entry point"));
return;
};
let body = self

View File

@ -25,7 +25,7 @@ use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt};
use rustc_middle::ty::{Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_session::Session;
use rustc_span::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::symbol::Symbol;
use rustc_span::{SourceFile, Span, DUMMY_SP};
use rustc_target::abi::call::{FnAbi, PassMode};
use rustc_target::abi::{HasDataLayout, TargetDataLayout};
@ -164,28 +164,16 @@ impl<'tcx> CodegenCx<'tcx> {
}
/// Zombie system:
/// When compiling libcore and other system libraries, if something unrepresentable is
/// encountered, we don't want to fail the compilation. Instead, we emit something bogus
/// (usually it's fairly faithful, though, e.g. u128 emits `OpTypeInt 128`), and then mark the
/// resulting ID as a "zombie". We continue compiling the rest of the crate, then, at the very
/// end, anything that transtively references a zombie value is stripped from the binary.
///
/// If an exported function is stripped, then we emit a special "zombie export" item, which is
/// consumed by the linker, which continues to infect other values that reference it.
/// If something unrepresentable is encountered, we don't want to fail
/// the compilation. Instead, we emit something bogus (usually it's fairly
/// faithful, though, e.g. `u128` emits `OpTypeInt 128 0`), and then mark the
/// resulting ID as a "zombie". We continue compiling the rest of the crate,
/// then, at the very end, anything that transitively references a zombie value
/// is stripped from the binary.
///
/// Finally, if *user* code is marked as zombie, then this means that the user tried to do
/// something that isn't supported, and should be an error.
/// Errors will only be emitted (by `linker::zombies`) for reachable zombies.
pub fn zombie_with_span(&self, word: Word, span: Span, reason: &str) {
if self.is_system_crate(span) {
self.zombie_even_in_user_code(word, span, reason);
} else {
self.tcx.sess.span_err(span, reason);
}
}
pub fn zombie_no_span(&self, word: Word, reason: &str) {
self.zombie_with_span(word, DUMMY_SP, reason);
}
pub fn zombie_even_in_user_code(&self, word: Word, span: Span, reason: &str) {
self.zombie_decorations.borrow_mut().insert(
word,
(
@ -198,31 +186,8 @@ impl<'tcx> CodegenCx<'tcx> {
),
);
}
/// Returns `true` if the originating crate of `span` (which could very well
/// be a different crate, e.g. a generic/`#[inline]` function, or a macro),
/// is a "system crate", and therefore allowed to have some errors deferred
/// as "zombies" (see `zombie_with_span`'s docs above for more details).
pub fn is_system_crate(&self, span: Span) -> bool {
// HACK(eddyb) this ignores `.lo` vs `.hi` potentially resulting in
// different `SourceFile`s (which is likely a bug anyway).
let cnum = self
.tcx
.sess
.source_map()
.lookup_source_file(span.data().lo)
.cnum;
self.tcx
.get_attr(cnum.as_def_id(), sym::compiler_builtins)
.is_some()
|| [
sym::core,
self.sym.spirv_std,
self.sym.libm,
self.sym.num_traits,
]
.contains(&self.tcx.crate_name(cnum))
pub fn zombie_no_span(&self, word: Word, reason: &str) {
self.zombie_with_span(word, DUMMY_SP, reason);
}
pub fn finalize_module(self) -> Module {
@ -796,11 +761,9 @@ impl<'tcx> MiscMethods<'tcx> for CodegenCx<'tcx> {
}
.def(span, self);
if self.is_system_crate(span) {
// Create these undefs up front instead of on demand in SpirvValue::def because
// SpirvValue::def can't use cx.emit()
self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr);
}
// Create these `OpUndef`s up front, instead of on-demand in `SpirvValue::def`,
// because `SpirvValue::def` can't use `cx.emit()`.
self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr);
SpirvValue {
kind: SpirvValueKind::FnAddr {

View File

@ -103,27 +103,28 @@ impl SpirvType<'_> {
Self::Bool => cx.emit_global().type_bool_id(id),
Self::Integer(width, signedness) => {
let result = cx.emit_global().type_int_id(id, width, signedness as u32);
let u_or_i = if signedness { "i" } else { "u" };
match width {
8 if !cx.builder.has_capability(Capability::Int8) => cx
.zombie_even_in_user_code(result, def_span, "u8 without OpCapability Int8"),
16 if !cx.builder.has_capability(Capability::Int16) => cx
.zombie_even_in_user_code(
result,
def_span,
"u16 without OpCapability Int16",
),
64 if !cx.builder.has_capability(Capability::Int64) => cx
.zombie_even_in_user_code(
result,
def_span,
"u64 without OpCapability Int64",
),
8 | 16 | 32 | 64 => (),
128 => cx.zombie_with_span(result, def_span, "u128"),
other => cx.zombie_with_span(
8 if !cx.builder.has_capability(Capability::Int8) => cx.zombie_with_span(
result,
def_span,
&format!("Integer width {other} invalid for spir-v"),
&format!("`{u_or_i}8` without `OpCapability Int8`"),
),
16 if !cx.builder.has_capability(Capability::Int16) => cx.zombie_with_span(
result,
def_span,
&format!("`{u_or_i}16` without `OpCapability Int16`"),
),
64 if !cx.builder.has_capability(Capability::Int64) => cx.zombie_with_span(
result,
def_span,
&format!("`{u_or_i}64` without `OpCapability Int64`"),
),
8 | 16 | 32 | 64 => {}
w => cx.zombie_with_span(
result,
def_span,
&format!("`{u_or_i}{w}` unsupported in SPIR-V"),
),
};
result
@ -131,17 +132,16 @@ impl SpirvType<'_> {
Self::Float(width) => {
let result = cx.emit_global().type_float_id(id, width);
match width {
64 if !cx.builder.has_capability(Capability::Float64) => cx
.zombie_even_in_user_code(
result,
def_span,
"f64 without OpCapability Float64",
),
64 if !cx.builder.has_capability(Capability::Float64) => cx.zombie_with_span(
result,
def_span,
"`f64` without `OpCapability Float64`",
),
32 | 64 => (),
other => cx.zombie_with_span(
result,
def_span,
&format!("Float width {other} invalid for spir-v"),
&format!("`f{other}` unsupported in SPIR-V"),
),
};
result
@ -217,11 +217,8 @@ impl SpirvType<'_> {
.type_pointer(id, StorageClass::Generic, pointee);
// no pointers to functions
if let SpirvType::Function { .. } = cx.lookup_type(pointee) {
cx.zombie_even_in_user_code(
result,
def_span,
"function pointer types are not allowed",
);
// FIXME(eddyb) use the `SPV_INTEL_function_pointers` extension.
cx.zombie_with_span(result, def_span, "function pointer types are not allowed");
}
result
}
@ -293,11 +290,8 @@ impl SpirvType<'_> {
.type_pointer(Some(id), StorageClass::Generic, pointee);
// no pointers to functions
if let SpirvType::Function { .. } = cx.lookup_type(pointee) {
cx.zombie_even_in_user_code(
result,
def_span,
"function pointer types are not allowed",
);
// FIXME(eddyb) use the `SPV_INTEL_function_pointers` extension.
cx.zombie_with_span(result, def_span, "function pointer types are not allowed");
}
result
}

View File

@ -1,4 +1,4 @@
error: Cannot memcpy dynamically sized data
error: cannot memcpy dynamically sized data
--> $CORE_SRC/intrinsics.rs:2460:9
|
2460 | copy(src, dst, count)

View File

@ -1,29 +1,59 @@
error: Cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:23:5
|
23 | f(&s.y);
| ^^^^^^^
|
= note: from: *u32
= note: to: *struct B { }
error: Cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:28:5
|
28 | f(&s.y);
| ^^^^^^^
|
= note: from: *struct S<usize, usize> { u32, u32 }
= note: to: *struct B { }
error: Cannot cast between pointer types
error: cannot cast between pointer types
from `*struct (usize, usize) { u32, u32 }`
to `*struct B { }`
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^^^^^^^
| ^
|
= note: from: *struct (usize, usize) { u32, u32 }
= note: to: *struct B { }
note: used from within `zst_member_ref_arg_broken::main_scalar_scalar_pair_nested`
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^
note: called by `main_scalar_scalar_pair_nested`
--> $DIR/zst_member_ref_arg-broken.rs:31:1
|
31 | #[spirv(fragment)]
| ^
error: cannot cast between pointer types
from `*struct (usize, usize) { u32, u32 }`
to `*struct B { }`
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^
|
note: used from within `zst_member_ref_arg_broken::main_scalar`
--> $DIR/zst_member_ref_arg-broken.rs:23:5
|
23 | f(&s.y);
| ^
note: called by `main_scalar`
--> $DIR/zst_member_ref_arg-broken.rs:21:1
|
21 | #[spirv(fragment)]
| ^
error: cannot cast between pointer types
from `*struct (usize, usize) { u32, u32 }`
to `*struct B { }`
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^
|
note: used from within `zst_member_ref_arg_broken::main_scalar_pair`
--> $DIR/zst_member_ref_arg-broken.rs:28:5
|
28 | f(&s.y);
| ^
note: called by `main_scalar_pair`
--> $DIR/zst_member_ref_arg-broken.rs:26:1
|
26 | #[spirv(fragment)]
| ^
error: aborting due to 3 previous errors