diff --git a/ices/78442.sh b/fixed/78442.sh similarity index 100% rename from ices/78442.sh rename to fixed/78442.sh diff --git a/ices/88468.rs b/ices/88468.rs new file mode 100644 index 00000000..83c0e4f4 --- /dev/null +++ b/ices/88468.rs @@ -0,0 +1,20 @@ +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +#![crate_type = "lib"] + +struct Assert; +trait IsTrue {} +impl IsTrue for Assert {} + +trait IsNotZst {} + +impl IsNotZst for T +where + Assert<{ std::mem::size_of::() > 0 }>: IsTrue, +{} + +fn assert_not_zero_sized(_: T) {} + +fn main() { + assert_not_zero_sized(vec![1, 2, 3]); +} diff --git a/ices/88599.sh b/ices/88599.rs similarity index 85% rename from ices/88599.sh rename to ices/88599.rs index 57671dea..fc2ff563 100644 --- a/ices/88599.sh +++ b/ices/88599.rs @@ -1,6 +1,4 @@ -#!/bin/bash - -rustc --crate-type lib - << EOF +#![crate_type = "lib"] pub trait First { const CONST: bool; @@ -18,5 +16,3 @@ pub trait Foo { impl <'a> Foo for () where &'a Self: Foo { const CONST: bool = <&Self>::CONST; } - -EOF diff --git a/ices/88643.sh b/ices/88643.rs similarity index 79% rename from ices/88643.sh rename to ices/88643.rs index 1b1f40d4..d432af34 100644 --- a/ices/88643.sh +++ b/ices/88643.rs @@ -1,6 +1,4 @@ -#!/bin/bash - -rustc --crate-type lib - << EOF +#![crate_type = "lib"] use std::collections::HashMap; @@ -13,7 +11,3 @@ pub trait WidgetExt { } static CALLBACKS: HashMap<*const dyn WidgetExt, dyn FnMut(&mut _) + 'static> = HashMap::new(); - -pub fn main() {} - -EOF diff --git a/ices/89022-1.rs b/ices/89022-1.rs new file mode 100644 index 00000000..3f87699d --- /dev/null +++ b/ices/89022-1.rs @@ -0,0 +1,256 @@ +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +#![feature(const_fn_trait_bound)] + +use core::marker::PhantomData; +use std::collections::HashMap; + +// const-evaluable equality for string slices +pub const fn str_eq(lhs: &str, rhs: &str) -> bool { + let lhs_bytes = lhs.as_bytes(); + let rhs_bytes = rhs.as_bytes(); + let mut i = 0; + let bytes = if lhs_bytes.len() == rhs_bytes.len() { + lhs_bytes.len() + } else { + return false; + }; + + while i < bytes { + if lhs_bytes[i] != rhs_bytes[i] { + return false; + } + i += 1; + } + return true; +} + +pub trait ContainsKey {} + +// trait used to compare two types that have type-encoded lists of keys (in this cast static strings) +pub trait KeySchema { + const KEYS: &'static [&'static str]; + const SIZE: usize; +} + +pub struct KeyNil; +impl KeySchema for KeyNil { + const KEYS: &'static [&'static str] = &[]; + const SIZE: usize = 0; +} + +pub struct KeyCons +where + Tail: KeySchema, +{ + _tail: PhantomData, +} + +pub const fn compute_successor_size() -> usize { + T::SIZE + 1 +} + +pub const fn construct_successor_array( + successor_key: &'static str, +) -> [&'static str; compute_successor_size::()] +where + [&'static str; compute_successor_size::()]: Sized, +{ + let mut keys = [""; compute_successor_size::()]; + let tail_keys = Tail::KEYS; + let mut i = 0; + let old_array_size: usize = compute_successor_size::() - 1; + while i < old_array_size { + keys[i] = tail_keys[i]; + i += 1; + } + keys[old_array_size] = successor_key; + keys +} + +pub const fn is_equivalent_except( + with_k: &[&'static str], + without_k: &[&'static str], +) -> bool { + let mut i = 0; + while i < with_k.len() { + if str_eq(with_k[i], K) { + i += 1; + continue; + } + let mut j = 0; + let mut match_found = false; + while j < without_k.len() { + if str_eq(with_k[i], without_k[j]) { + match_found = true; + break; + } + j += 1; + } + if !match_found { + return false; + } + i += 1; + } + return true; +} + +// Outputs a usize in order to make the array invalid by underflowing +// Alternatively this could use const_panic to produce good error messages +pub const fn check_valid_subset() -> usize +where + S1: ContainsKey, +{ + let with_k: &[&'static str] = &S1::KEYS; + let without_k: &[&'static str] = &S2::KEYS; + + if with_k.len() <= without_k.len() { + // panic because S1 isn't bigger + return (with_k.len() - 1) - without_k.len(); // panic using underflow + } + + if !is_equivalent_except::(with_k, without_k) { + // panic because S2 doesn't have the rest of S1's elements + return (without_k.len() - 1) - with_k.len(); // panic using underflow + } + + return 1; +} + +pub trait SubsetExcept: KeySchema +where + [(); Parent::SIZE - Self::SIZE]: Sized, + Parent: ContainsKey, +{ +} + +impl SubsetExcept for Schema +where + Schema: KeySchema, + PossibleParent: KeySchema, + PossibleParent: ContainsKey, + [(); PossibleParent::SIZE - Schema::SIZE]: Sized, + [(); check_valid_subset::()]: Sized, +{ +} + +impl KeySchema for KeyCons +where + Tail: KeySchema, + [(); compute_successor_size::()]: Sized, +{ + const KEYS: &'static [&'static str] = &construct_successor_array::(KEY_ID); + const SIZE: usize = compute_successor_size::(); +} + +// thanks to matt1992#5582 on the Rust Programming Language Community Discord for offering this strategy +// a const expression calls a function, which provides a "proof" that a given type should always use a given implementation +pub trait ContainsKeyHelper {} + +const fn contains_key_helper_helper() -> bool { + str_eq(KEY_ID, K) +} +impl ContainsKey + for KeyCons +where + Tail: KeySchema, + Self: ContainsKeyHelper<{ contains_key_helper_helper::() }, K>, +{ +} + +impl ContainsKeyHelper + for KeyCons +where + Tail: KeySchema + ContainsKey, +{ +} + +impl ContainsKeyHelper + for KeyCons +where + Tail: KeySchema, +{ +} + +pub struct RestrictedStringMap { + inner: HashMap<&'static str, Option>, + // schemas should be 0-sized, but I use a phantom data here just to emphasize that there's no data dependency + _schema: PhantomData, +} +impl ContainsKey for RestrictedStringMap where + S: ContainsKey +{ +} +impl RestrictedStringMap +where + [(); compute_successor_size::()]: Sized, +{ + pub fn empty_schema() -> RestrictedStringMap { + RestrictedStringMap::<_> { + inner: HashMap::new(), + // schemas should be 0-sized, but I use a phantom data here just to emphasize that there's no data dependency + _schema: PhantomData::<_>, + } + } + + pub fn new() -> Self { + let mut hm: HashMap<&'static str, Option> = HashMap::new(); + + for k in S::KEYS { + hm.insert(*k, None); + } + + hm.shrink_to_fit(); + + Self { + inner: hm, + _schema: PhantomData::<_>, + } + } + + /// Adds a possible &'static str to the HashMap. + /// This requires consuming the map since our type must change to reflect the new schema. + pub fn add_key(self) -> RestrictedStringMap> + where + // Proof asserting that one size larger is still a valid schema + // this should only be untrue if the number of keys exceeds usize::MAX + [(); compute_successor_size::()]: Sized, + { + let Self { mut inner, .. } = self; + inner.insert(K, None); + RestrictedStringMap::<_> { + inner: inner, + _schema: PhantomData::<_>, + } + } + + // I don't know of a way to remove the &'static str other than having the user provide their own new schema. + // This is because I can't use a dependently typed function to construct a return type. + // That's the only way I can think of to compute what the return type of such a function would look like without user input. + // + pub fn remove_key( + self, + ) -> RestrictedStringMap + where + Self: ContainsKey, + S: ContainsKey, + NewSchema: SubsetExcept, + [(); S::SIZE - NewSchema::SIZE]: Sized, + { + let Self { mut inner, .. } = self; + inner.remove(&K); + RestrictedStringMap::<_> { + inner: inner, + _schema: PhantomData::<_>, + } + } +} + +fn foo() { + let map: RestrictedStringMap = RestrictedStringMap::::empty_schema(); + let mut map: RestrictedStringMap, "k2">> = + map.add_key::<"k1">().add_key::<"k2">(); + let map: RestrictedStringMap> = map.remove_key::<_, "k2">(); +} + +fn main() {} diff --git a/ices/89022-2.rs b/ices/89022-2.rs new file mode 100644 index 00000000..99691be2 --- /dev/null +++ b/ices/89022-2.rs @@ -0,0 +1,242 @@ +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +#![feature(const_fn_trait_bound)] + +use core::marker::PhantomData; +use std::collections::HashMap; + +// const-evaluable equality for string slices +pub const fn str_eq(lhs: &str, rhs: &str) -> bool { + let lhs_bytes = lhs.as_bytes(); + let rhs_bytes = rhs.as_bytes(); + let mut i = 0; + let bytes = if lhs_bytes.len() == rhs_bytes.len() { + lhs_bytes.len() + } else { + return false; + }; + + while i < bytes { + if lhs_bytes[i] != rhs_bytes[i] { + return false; + } + i += 1; + } + return true; +} + +pub trait ContainsKey {} + +// trait used to compare two types that have type-encoded lists of keys (in this cast static strings) +pub trait KeySchema { + const KEYS: &'static [&'static str]; + const SIZE: usize; +} + +pub struct KeyNil; +impl KeySchema for KeyNil { + const KEYS: &'static [&'static str] = &[]; + const SIZE: usize = 0; +} + +pub struct KeyCons +where + Tail: KeySchema, +{ + _tail: PhantomData, +} + +pub const fn compute_successor_size() -> usize { + T::SIZE + 1 +} + +pub const fn construct_successor_array( + successor_key: &'static str, +) -> [&'static str; compute_successor_size::()] +where + [&'static str; compute_successor_size::()]: Sized, +{ + let mut keys = [""; compute_successor_size::()]; + let tail_keys = Tail::KEYS; + let mut i = 0; + let old_array_size: usize = compute_successor_size::() - 1; + while i < old_array_size { + keys[i] = tail_keys[i]; + i += 1; + } + keys[old_array_size] = successor_key; + keys +} + +pub const fn is_equivalent_except( + with_k: &[&'static str], + without_k: &[&'static str], +) -> bool { + let mut i = 0; + while i < with_k.len() { + if str_eq(with_k[i], K) { + i += 1; + continue; + } + let mut j = 0; + let mut match_found = false; + while j < without_k.len() { + if str_eq(with_k[i], without_k[j]) { + match_found = true; + break; + } + j += 1; + } + if !match_found { + return false; + } + i += 1; + } + return true; +} + +// Outputs a usize in order to make the array invalid by underflowing +// Alternatively this could use const_panic to produce good error messages +pub const fn check_valid_subset() -> usize +where + S1: ContainsKey, +{ + let with_k: &[&'static str] = &S1::KEYS; + let without_k: &[&'static str] = &S2::KEYS; + + if with_k.len() <= without_k.len() { + // panic because S1 isn't bigger + return (with_k.len() - 1) - without_k.len(); // panic using underflow + } + + if !is_equivalent_except::(with_k, without_k) { + // panic because S2 doesn't have the rest of S1's elements + return (without_k.len() - 1) - with_k.len(); // panic using underflow + } + + return 1; +} + +pub trait SubsetExcept: KeySchema +where + [(); Parent::SIZE - Self::SIZE]: Sized, + Parent: ContainsKey, +{ +} + +impl SubsetExcept for Schema +where + Schema: KeySchema, + PossibleParent: KeySchema, + PossibleParent: ContainsKey, + [(); PossibleParent::SIZE - Schema::SIZE]: Sized, + [(); check_valid_subset::()]: Sized, +{ +} + +impl KeySchema for KeyCons +where + Tail: KeySchema, + [(); compute_successor_size::()]: Sized, +{ + const KEYS: &'static [&'static str] = &construct_successor_array::(KEY_ID); + const SIZE: usize = compute_successor_size::(); +} + +// thanks to matt1992#5582 on the Rust Programming Language Community Discord for offering this strategy +// a const expression calls a function, which provides a "proof" that a given type should always use a given implementation +pub trait ContainsKeyHelper {} + +pub const fn contains_key_helper_helper() -> bool +{ + str_eq(KEY_ID, K) +} +impl ContainsKey + for KeyCons +where + Tail: KeySchema, + Self: ContainsKeyHelper<{ contains_key_helper_helper::() }, K>, +{ +} + +impl ContainsKeyHelper + for KeyCons +where + Tail: KeySchema + ContainsKey, +{ +} + +impl ContainsKeyHelper + for KeyCons +where + Tail: KeySchema, +{ +} + +pub struct RestrictedStringMap { + inner: HashMap<&'static str, Option>, + // schemas should be 0-sized, but I use a phantom data here just to emphasize that there's no data dependency + _schema: PhantomData, +} +impl ContainsKey for RestrictedStringMap where + S: ContainsKey +{ +} +impl RestrictedStringMap +where + [(); compute_successor_size::()]: Sized, +{ + pub fn empty_schema() -> RestrictedStringMap { + RestrictedStringMap::<_> { + inner: HashMap::new(), + // schemas should be 0-sized, but I use a phantom data here just to emphasize that there's no data dependency + _schema: PhantomData::<_>, + } + } + + /// Adds a possible &'static str to the HashMap. + /// This requires consuming the map since our type must change to reflect the new schema. + pub fn add_key(self) -> RestrictedStringMap> + where + // Proof asserting that one size larger is still a valid schema + // this should only be untrue if the number of keys exceeds usize::MAX + [(); compute_successor_size::()]: Sized, + { + let Self { mut inner, .. } = self; + inner.insert(K, None); + RestrictedStringMap::<_> { + inner: inner, + _schema: PhantomData::<_>, + } + } + + // I don't know of a way to remove the &'static str other than having the user provide their own new schema. + // This is because I can't use a dependently typed function to construct a return type. + // That's the only way I can think of to compute what the return type of such a function would look like without user input. + pub fn remove_key( + self, + ) -> RestrictedStringMap + where + Self: ContainsKey, + S: ContainsKey, + // the schema that the user provides must be a valid subset of the old schema + NewSchema: SubsetExcept, + [(); S::SIZE - NewSchema::SIZE]: Sized, + { + let Self { mut inner, .. } = self; + inner.remove(&K); + RestrictedStringMap::<_> { + inner: inner, + _schema: PhantomData::<_>, + } + } +} + +fn foo() { + let map: RestrictedStringMap = RestrictedStringMap::::empty_schema(); + let mut map: RestrictedStringMap, "k2">> = + map.add_key::<"k1">().add_key::<"k2">(); + let map: RestrictedStringMap> = map.remove_key::<_, "k2">(); +} + +fn main() {} diff --git a/ices/89189.rs b/ices/89189.rs new file mode 100644 index 00000000..7265f643 --- /dev/null +++ b/ices/89189.rs @@ -0,0 +1,9 @@ +#![feature(fn_traits, unboxed_closures)] +struct Func {} +impl Fn(&str) -> bool for Func { + extern "rust-call" fn call(&self, args: Args) -> Self::Output { + todo!() + } +} + +fn main() {} diff --git a/ices/89682-1.rs b/ices/89682-1.rs new file mode 100644 index 00000000..f3ae09f4 --- /dev/null +++ b/ices/89682-1.rs @@ -0,0 +1,20 @@ +#![feature(allocator_api)] + +use core::alloc::{AllocError, Allocator, Layout}; +use core::ptr::NonNull; + +struct AwfulAllocator([u64; N]); + +unsafe impl Allocator for AwfulAllocator { + fn allocate(&self, _layout: Layout) -> Result, AllocError> { + todo!() + } + unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { + todo!() + } +} + +fn main() { + let f = AwfulAllocator([0; 128]); + let _x = Box::>::new_in(43, f); +} diff --git a/ices/89682-2.rs b/ices/89682-2.rs new file mode 100644 index 00000000..6cfd5b35 --- /dev/null +++ b/ices/89682-2.rs @@ -0,0 +1,16 @@ +#![feature(allocator_api)] +use core::alloc::{AllocError, Allocator, Layout}; +use core::ptr::NonNull; + +struct ZST; +unsafe impl Allocator for &ZST { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + todo!() + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + todo!() + } +} +fn main() { + let _ = Box::::new_in(43, &ZST); +} diff --git a/ices/89864.rs b/ices/89864.rs new file mode 100644 index 00000000..45a914ac --- /dev/null +++ b/ices/89864.rs @@ -0,0 +1,37 @@ +#![crate_type = "lib"] + +use core::{fmt, marker::PhantomData}; + +pub trait WrapperTrait { + type Assoc; +} + +pub trait Trait { + type Assoc; +} + +pub struct Ice(< as WrapperTrait>::Assoc as Trait>::Assoc); + +impl fmt::Debug for Ice +where + < as WrapperTrait>::Assoc as Trait>::Assoc: fmt::Debug, +{ + fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { + format_args!("{:?}", self.0); + todo!() + } +} + +pub struct TraitImplementor; + +impl Trait for TraitImplementor { + type Assoc = ConcreteAssoc; +} + +pub struct ConcreteAssoc; + +impl WrapperTrait for Dynamic { + type Assoc = TraitImplementor; +} + +pub struct Dynamic(PhantomData); diff --git a/ices/90318.rs b/ices/90318.rs new file mode 100644 index 00000000..4ea8a78f --- /dev/null +++ b/ices/90318.rs @@ -0,0 +1,29 @@ +#![feature(const_type_id)] +#![feature(generic_const_exprs)] +#![feature(core_intrinsics)] +use core::intrinsics::type_id; + +struct If; +pub trait True {} +impl True for If {} + +fn consume(_val: T) +where + If<{ type_id::() != type_id::<()>() }>: True, +{ +} + +fn test() +where + If<{ type_id::() != type_id::<()>() }>: True, +{ +} + +fn main() { + let a = (); + consume(0i32); + consume(a); + // uncomenting these lines won't result in ICE + // test::(); + // test::<()>(); +} diff --git a/ices/90409-1.rs b/ices/90409-1.rs new file mode 100644 index 00000000..c4666dfe --- /dev/null +++ b/ices/90409-1.rs @@ -0,0 +1,16 @@ +#![feature(type_alias_impl_trait)] + +trait Bar { + fn bar(&self); +} + +type FooFn = impl FnOnce(); + +fn foo(bar: B) -> FooFn { + move || { bar.bar() } +} + +fn main() { + let boom: FooFn = unsafe { core::mem::zeroed() }; + boom(); +} diff --git a/ices/90409-2.rs b/ices/90409-2.rs new file mode 100644 index 00000000..95730f05 --- /dev/null +++ b/ices/90409-2.rs @@ -0,0 +1,19 @@ +#![feature(type_alias_impl_trait)] +#![crate_type = "lib"] + +use std::future::Future; + +trait Bar { + fn bar(&self); +} + +type FooFuture = impl Future; + +fn foo(bar: B) -> FooFuture { + async move { bar.bar() } +} + +pub fn mainish(ctx: &mut std::task::Context) { + let boom: FooFuture = unsafe { core::mem::zeroed() }; + Box::pin(boom).as_mut().poll(ctx); +}