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( cx.zombie_with_span(
new_id, new_id,
span, span,
"Cannot create self-referential types, even through pointers", "cannot create self-referential types, even through pointers",
); );
Some(new_id) Some(new_id)
} }

View File

@ -171,7 +171,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if invalid_seq_cst { if invalid_seq_cst {
self.zombie( self.zombie(
semantics.def(self), 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 semantics
@ -352,11 +353,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
fn zombie_convert_ptr_to_u(&self, def: Word) { 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) { 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) { 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); .with_type(dest_ty);
if val_is_ptr || dest_is_ptr { if val_is_ptr || dest_is_ptr {
if self.is_system_crate(self.span()) {
self.zombie( self.zombie(
result.def(self), result.def(self),
&format!( &format!(
"Cannot cast between pointer and non-pointer types. From: {}. To: {}.", "cannot cast between pointer and non-pointer types\
\nfrom `{}`\
\n to `{}`",
self.debug_type(val.ty), self.debug_type(val.ty),
self.debug_type(dest_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();
}
} }
result result
@ -1884,7 +1880,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
empty(), empty(),
) )
.unwrap(); .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> { impl<'a, 'tcx> Builder<'a, 'tcx> {
fn load_err(&mut self, original_type: Word, invalid_type: Word) -> SpirvValue { fn load_err(&mut self, original_type: Word, invalid_type: Word) -> SpirvValue {
let mut err = self.struct_err(&format!( 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) self.debug_type(original_type)
)); ));
if original_type != invalid_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> { fn store_err(&mut self, original_type: Word, value: SpirvValue) -> Result<(), ErrorGuaranteed> {
let mut err = self.struct_err(&format!( 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) self.debug_type(original_type)
)); ));
if original_type != value.ty { if original_type != value.ty {

View File

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

View File

@ -140,9 +140,7 @@ impl SpirvValue {
IllegalConst::Indirect(cause) => cause.message(), IllegalConst::Indirect(cause) => cause.message(),
}; };
// HACK(eddyb) we don't know whether this constant originated cx.zombie_with_span(id, span, msg);
// in a system crate, so it's better to always zombie.
cx.zombie_even_in_user_code(id, span, msg);
id id
} }
@ -158,7 +156,6 @@ impl SpirvValue {
} }
SpirvValueKind::FnAddr { .. } => { SpirvValueKind::FnAddr { .. } => {
if cx.is_system_crate(span) {
cx.builder cx.builder
.const_to_id .const_to_id
.borrow() .borrow()
@ -168,15 +165,6 @@ impl SpirvValue {
}) })
.expect("FnAddr didn't go through proper undef registration") .expect("FnAddr didn't go through proper undef registration")
.val .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
}
} }
SpirvValueKind::LogicalPtrCast { SpirvValueKind::LogicalPtrCast {
@ -184,24 +172,17 @@ impl SpirvValue {
original_pointee_ty, original_pointee_ty,
zombie_target_undef, zombie_target_undef,
} => { } => {
if cx.is_system_crate(span) {
cx.zombie_with_span( cx.zombie_with_span(
zombie_target_undef, zombie_target_undef,
span, span,
&format!( &format!(
"Cannot cast between pointer types. From: {}. To: {}.", "cannot cast between pointer types\
\nfrom `*{}`\
\n to `{}`",
cx.debug_type(original_pointee_ty), cx.debug_type(original_pointee_ty),
cx.debug_type(self.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();
}
zombie_target_undef zombie_target_undef
} }

View File

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

View File

@ -70,7 +70,7 @@ impl<'tcx> CodegenCx<'tcx> {
} else { } else {
self.tcx self.tcx
.sess .sess
.span_err(span, format!("Cannot declare {name} as an entry point")); .span_err(span, format!("cannot declare {name} as an entry point"));
return; return;
}; };
let body = self 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_middle::ty::{Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_session::Session; use rustc_session::Session;
use rustc_span::def_id::DefId; 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_span::{SourceFile, Span, DUMMY_SP};
use rustc_target::abi::call::{FnAbi, PassMode}; use rustc_target::abi::call::{FnAbi, PassMode};
use rustc_target::abi::{HasDataLayout, TargetDataLayout}; use rustc_target::abi::{HasDataLayout, TargetDataLayout};
@ -164,28 +164,16 @@ impl<'tcx> CodegenCx<'tcx> {
} }
/// Zombie system: /// 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 /// If something unrepresentable is encountered, we don't want to fail
/// consumed by the linker, which continues to infect other values that reference it. /// 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 /// Errors will only be emitted (by `linker::zombies`) for reachable zombies.
/// something that isn't supported, and should be an error.
pub fn zombie_with_span(&self, word: Word, span: Span, reason: &str) { 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( self.zombie_decorations.borrow_mut().insert(
word, word,
( (
@ -198,31 +186,8 @@ impl<'tcx> CodegenCx<'tcx> {
), ),
); );
} }
pub fn zombie_no_span(&self, word: Word, reason: &str) {
/// Returns `true` if the originating crate of `span` (which could very well self.zombie_with_span(word, DUMMY_SP, reason);
/// 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 finalize_module(self) -> Module { pub fn finalize_module(self) -> Module {
@ -796,11 +761,9 @@ impl<'tcx> MiscMethods<'tcx> for CodegenCx<'tcx> {
} }
.def(span, self); .def(span, self);
if self.is_system_crate(span) { // Create these `OpUndef`s up front, instead of on-demand in `SpirvValue::def`,
// Create these undefs up front instead of on demand in SpirvValue::def because // because `SpirvValue::def` can't use `cx.emit()`.
// SpirvValue::def can't use cx.emit()
self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr); self.def_constant(ty, SpirvConst::ZombieUndefForFnAddr);
}
SpirvValue { SpirvValue {
kind: SpirvValueKind::FnAddr { kind: SpirvValueKind::FnAddr {

View File

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

View File

@ -1,29 +1,59 @@
error: Cannot cast between pointer types error: cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:23:5 from `*struct (usize, usize) { u32, u32 }`
| to `*struct B { }`
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
--> $DIR/zst_member_ref_arg-broken.rs:33:5 --> $DIR/zst_member_ref_arg-broken.rs:33:5
| |
33 | f(&s.y); 33 | f(&s.y);
| ^^^^^^^ | ^
| |
= note: from: *struct (usize, usize) { u32, u32 } note: used from within `zst_member_ref_arg_broken::main_scalar_scalar_pair_nested`
= note: to: *struct B { } --> $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 error: aborting due to 3 previous errors