Skip to content
This repository has been archived by the owner on May 23, 2024. It is now read-only.

Add some ICEs #1007

Merged
merged 3 commits into from
Nov 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
20 changes: 20 additions & 0 deletions ices/88468.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![crate_type = "lib"]

struct Assert<const COND: bool>;
trait IsTrue {}
impl IsTrue for Assert<true> {}

trait IsNotZst {}

impl<T> IsNotZst for T
where
Assert<{ std::mem::size_of::<T>() > 0 }>: IsTrue,
{}

fn assert_not_zero_sized<T: IsNotZst>(_: T) {}

fn main() {
assert_not_zero_sized(vec![1, 2, 3]);
}
6 changes: 1 addition & 5 deletions ices/88599.sh → ices/88599.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#!/bin/bash

rustc --crate-type lib - << EOF
#![crate_type = "lib"]

pub trait First {
const CONST: bool;
Expand All @@ -18,5 +16,3 @@ pub trait Foo {
impl <'a> Foo for () where &'a Self: Foo {
const CONST: bool = <&Self>::CONST;
}

EOF
8 changes: 1 addition & 7 deletions ices/88643.sh → ices/88643.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#!/bin/bash

rustc --crate-type lib - << EOF
#![crate_type = "lib"]

use std::collections::HashMap;

Expand All @@ -13,7 +11,3 @@ pub trait WidgetExt {
}

static CALLBACKS: HashMap<*const dyn WidgetExt, dyn FnMut(&mut _) + 'static> = HashMap::new();

pub fn main() {}

EOF
256 changes: 256 additions & 0 deletions ices/89022-1.rs
Original file line number Diff line number Diff line change
@@ -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<const K: &'static str> {}

// 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<Tail, const KEY_ID: &'static str>
where
Tail: KeySchema,
{
_tail: PhantomData<Tail>,
}

pub const fn compute_successor_size<T: KeySchema>() -> usize {
T::SIZE + 1
}

pub const fn construct_successor_array<Tail: KeySchema>(
successor_key: &'static str,
) -> [&'static str; compute_successor_size::<Tail>()]
where
[&'static str; compute_successor_size::<Tail>()]: Sized,
{
let mut keys = [""; compute_successor_size::<Tail>()];
let tail_keys = Tail::KEYS;
let mut i = 0;
let old_array_size: usize = compute_successor_size::<Tail>() - 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<const K: &'static str>(
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<S1: KeySchema, S2: KeySchema, const K: &'static str>() -> usize
where
S1: ContainsKey<K>,
{
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::<K>(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<Parent: KeySchema, const K: &'static str>: KeySchema
where
[(); Parent::SIZE - Self::SIZE]: Sized,
Parent: ContainsKey<K>,
{
}

impl<Schema, PossibleParent, const K: &'static str> SubsetExcept<PossibleParent, K> for Schema
where
Schema: KeySchema,
PossibleParent: KeySchema,
PossibleParent: ContainsKey<K>,
[(); PossibleParent::SIZE - Schema::SIZE]: Sized,
[(); check_valid_subset::<PossibleParent, Schema, K>()]: Sized,
{
}

impl<Tail, const KEY_ID: &'static str> KeySchema for KeyCons<Tail, KEY_ID>
where
Tail: KeySchema,
[(); compute_successor_size::<Tail>()]: Sized,
{
const KEYS: &'static [&'static str] = &construct_successor_array::<Tail>(KEY_ID);
const SIZE: usize = compute_successor_size::<Tail>();
}

// 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 IS_EQUAL: bool, const K: &'static str> {}

const fn contains_key_helper_helper<const KEY_ID: &'static str, const K: &'static str>() -> bool {
str_eq(KEY_ID, K)
}
impl<Tail, const KEY_ID: &'static str, const K: &'static str> ContainsKey<K>
for KeyCons<Tail, KEY_ID>
where
Tail: KeySchema,
Self: ContainsKeyHelper<{ contains_key_helper_helper::<KEY_ID, K>() }, K>,
{
}

impl<Tail, const KEY_ID: &'static str, const K: &'static str> ContainsKeyHelper<false, K>
for KeyCons<Tail, KEY_ID>
where
Tail: KeySchema + ContainsKey<K>,
{
}

impl<Tail, const KEY_ID: &'static str, const K: &'static str> ContainsKeyHelper<true, K>
for KeyCons<Tail, KEY_ID>
where
Tail: KeySchema,
{
}

pub struct RestrictedStringMap<S: KeySchema> {
inner: HashMap<&'static str, Option<String>>,
// schemas should be 0-sized, but I use a phantom data here just to emphasize that there's no data dependency
_schema: PhantomData<S>,
}
impl<S: KeySchema, const K: &'static str> ContainsKey<K> for RestrictedStringMap<S> where
S: ContainsKey<K>
{
}
impl<S: KeySchema> RestrictedStringMap<S>
where
[(); compute_successor_size::<S>()]: Sized,
{
pub fn empty_schema() -> RestrictedStringMap<KeyNil> {
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<String>> = 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<const K: &'static str>(self) -> RestrictedStringMap<KeyCons<S, K>>
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::<S>()]: 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<NewSchema: KeySchema, const K: &'static str>(
self,
) -> RestrictedStringMap<NewSchema>
where
Self: ContainsKey<K>,
S: ContainsKey<K>,
NewSchema: SubsetExcept<S, K>,
[(); S::SIZE - NewSchema::SIZE]: Sized,
{
let Self { mut inner, .. } = self;
inner.remove(&K);
RestrictedStringMap::<_> {
inner: inner,
_schema: PhantomData::<_>,
}
}
}

fn foo() {
let map: RestrictedStringMap<KeyNil> = RestrictedStringMap::<KeyNil>::empty_schema();
let mut map: RestrictedStringMap<KeyCons<KeyCons<KeyNil, "k1">, "k2">> =
map.add_key::<"k1">().add_key::<"k2">();
let map: RestrictedStringMap<KeyCons<KeyNil, "k1">> = map.remove_key::<_, "k2">();
}

fn main() {}
Loading