Skip to content

Commit 7b2c1c2

Browse files
Don't use [!Default; 0]: Default impl (#150)
There is an effort to remove Rust's unconditional `[T; 0]: Default` impl for `T: !Default`, see rust-lang/rust#145457. foundations depends on this impl in its public API, so fixing this is a breaking change. However, after consideration we decided that we will break semver for this change. We don't expect any users of foundations to depend directly on `[T; 0]: Settings`. We also put a best-effort workaround in place for the `#[derive(Settings)]` use case inside foundations. Co-authored-by: Leo Blöcher <lblocher@cloudflare.com>
1 parent c8974db commit 7b2c1c2

File tree

2 files changed

+39
-9
lines changed

2 files changed

+39
-9
lines changed

foundations-macros/src/settings.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syn::parse::{Parse, ParseStream};
77
use syn::spanned::Spanned;
88
use syn::{
99
parse_macro_input, parse_quote, Attribute, Expr, ExprLit, Field, Fields, Ident, Item, ItemEnum,
10-
ItemStruct, Lit, LitStr, Meta, MetaNameValue, Path,
10+
ItemStruct, Lit, LitStr, Meta, MetaNameValue, Path, Type,
1111
};
1212

1313
const ERR_NOT_STRUCT_OR_ENUM: &str = "Settings should be either structure or enum.";
@@ -238,12 +238,19 @@ fn impl_settings_trait_for_field(
238238

239239
impl_for_field.append_all(quote_spanned! { span=>
240240
let mut key = parent_key.to_vec();
241-
242241
key.push(#name_str.into());
243-
244-
#crate_path::settings::Settings::add_docs(&self.#name, &key, docs);
245242
});
246243

244+
// foundations#150: `[T; 0]` used to impl Settings for `T: !Default`, but this
245+
// is not possible anymore. We thus can't call `Settings::add_docs` for such
246+
// fields, but it was a noop anyway.
247+
// TODO(lblocher): Remove this compatibility hack with the next major release
248+
if !is_array_zst(&field.ty) {
249+
impl_for_field.append_all(quote_spanned! { span =>
250+
#crate_path::settings::Settings::add_docs(&self.#name, &key, docs);
251+
});
252+
}
253+
247254
if !docs.is_empty() {
248255
impl_for_field.append_all(quote! {
249256
docs.insert(key, &[#(#docs,)*][..]);
@@ -286,6 +293,30 @@ fn extract_doc_comments(attrs: &[Attribute]) -> Vec<LitStr> {
286293
comments
287294
}
288295

296+
/// Returns whether `ty` is exactly `[T; 0]` (for some T)
297+
fn is_array_zst(ty: &Type) -> bool {
298+
let Type::Array(array) = ty else {
299+
return false;
300+
};
301+
302+
let mut expr = &array.len;
303+
while let Expr::Cast(cast) = expr {
304+
expr = &cast.expr;
305+
}
306+
307+
let Expr::Lit(ExprLit {
308+
lit: Lit::Int(len_lit),
309+
..
310+
}) = expr
311+
else {
312+
return false;
313+
};
314+
len_lit
315+
.base10_parse::<usize>()
316+
.map(|len| len == 0)
317+
.unwrap_or(false)
318+
}
319+
289320
fn impl_serde_aware_default(item: &ItemStruct) -> proc_macro2::TokenStream {
290321
let name = &item.ident;
291322
let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl();

foundations/src/settings/basic_impls.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ macro_rules! impl_noop {
3030
}
3131

3232
impl_noop!(<T> Settings for PhantomData<T> where T: 'static);
33-
impl_noop!(<T> Settings for [T; 0] where T: Debug + Clone + 'static);
3433
impl_noop!(<Idx> Settings for Range<Idx> where Idx: Debug + Serialize + DeserializeOwned + Clone + Default + 'static);
3534
impl_noop!(<T> Settings for Reverse<T> where T: Settings);
3635
impl_noop!(<T> Settings for Wrapping<T> where T: Settings);
@@ -119,10 +118,10 @@ macro_rules! impl_for_array {
119118
}
120119

121120
impl_for_array! {
122-
1 2 3 4 5 6 7 8 9 10
123-
11 12 13 14 15 16 17 18 19 20
124-
21 22 23 24 25 26 27 28 29 30
125-
31 32
121+
0 1 2 3 4 5 6 7 8 9
122+
10 11 12 13 14 15 16 17 18 19
123+
20 21 22 23 24 25 26 27 28 29
124+
30 31 32
126125
}
127126

128127
impl<T: Settings> Settings for Option<T> {

0 commit comments

Comments
 (0)