Skip to content

Commit 3fbed98

Browse files
committed
implement Default for all arrays
1 parent 89ebad5 commit 3fbed98

File tree

8 files changed

+145
-21
lines changed

8 files changed

+145
-21
lines changed

compiler/rustc_hir/src/lang_items.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ language_item_table! {
248248
UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct;
249249
VaList, sym::va_list, va_list, Target::Struct;
250250

251+
DefaultFn, sym::default_fn, default_fn, Target::Method(MethodKind::Trait { body: false });
252+
251253
Deref, sym::deref, deref_trait, Target::Trait;
252254
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait;
253255
DerefTarget, sym::deref_target, deref_target, Target::AssocTy;
@@ -332,7 +334,7 @@ language_item_table! {
332334
ResultErr, sym::Err, result_err_variant, Target::Variant;
333335

334336
IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false });
335-
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false});
337+
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false });
336338

337339
PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent);
338340

@@ -343,4 +345,6 @@ language_item_table! {
343345
Range, sym::Range, range_struct, Target::Struct;
344346
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct;
345347
RangeTo, sym::RangeTo, range_to_struct, Target::Struct;
348+
349+
ArrayDefaultHack, sym::array_default_hack, array_default_hack, Target::Fn;
346350
}

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ symbols! {
276276
arm,
277277
arm_target_feature,
278278
array,
279+
array_default_hack,
279280
arrays,
280281
as_ptr,
281282
as_str,
@@ -454,6 +455,7 @@ symbols! {
454455
declare_lint_pass,
455456
decode,
456457
default_alloc_error_handler,
458+
default_fn,
457459
default_lib_allocator,
458460
default_type_parameter_fallback,
459461
default_type_params,

compiler/rustc_ty_utils/src/instance.rs

+31
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,37 @@ fn inner_resolve_instance<'tcx>(
8787
ty::InstanceDef::DropGlue(def_id, None)
8888
}
8989
}
90+
ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().array_default_hack() => {
91+
debug!(" => array default hack");
92+
if let Some(val) = substs.const_at(1).try_eval_usize(tcx, param_env) {
93+
if val == 0 {
94+
// For `N == 0` we return the lang item itself, which should just panic.
95+
debug!(" => zero length array");
96+
ty::InstanceDef::Item(def)
97+
} else {
98+
// For `N != 0` we use `<T as Default>::default` which should be well typed
99+
// according to the safety requirements of `array_default_hack`.
100+
debug!(" => with default");
101+
let def_id = match tcx.lang_items().require(rustc_hir::LangItem::DefaultFn)
102+
{
103+
Ok(id) => id,
104+
Err(s) => {
105+
tcx.sess.fatal(&format!("default for array_default_hack: {}", s));
106+
}
107+
};
108+
109+
return Instance::resolve(
110+
tcx,
111+
param_env,
112+
def_id,
113+
tcx.mk_substs(substs.iter().take(1)),
114+
);
115+
}
116+
} else {
117+
debug!(" => too generic");
118+
return Ok(None);
119+
}
120+
}
90121
_ => {
91122
debug!(" => free item");
92123
ty::InstanceDef::Item(def)

library/core/src/array/mod.rs

+71-20
Original file line numberDiff line numberDiff line change
@@ -366,29 +366,80 @@ impl<T: Ord, const N: usize> Ord for [T; N] {
366366
}
367367
}
368368

369-
// The Default impls cannot be done with const generics because `[T; 0]` doesn't
370-
// require Default to be implemented, and having different impl blocks for
371-
// different numbers isn't supported yet.
372-
373-
macro_rules! array_impl_default {
374-
{$n:expr, $t:ident $($ts:ident)*} => {
375-
#[stable(since = "1.4.0", feature = "array_default")]
376-
impl<T> Default for [T; $n] where T: Default {
377-
fn default() -> [T; $n] {
378-
[$t::default(), $($ts::default()),*]
369+
#[cfg(bootstrap)]
370+
mod array_defaults {
371+
macro_rules! array_impl_default {
372+
{$n:expr, $t:ident $($ts:ident)*} => {
373+
#[stable(since = "1.4.0", feature = "array_default")]
374+
impl<T> Default for [T; $n] where T: Default {
375+
fn default() -> [T; $n] {
376+
[$t::default(), $($ts::default()),*]
377+
}
379378
}
380-
}
381-
array_impl_default!{($n - 1), $($ts)*}
382-
};
383-
{$n:expr,} => {
384-
#[stable(since = "1.4.0", feature = "array_default")]
385-
impl<T> Default for [T; $n] {
386-
fn default() -> [T; $n] { [] }
387-
}
388-
};
379+
array_impl_default!{($n - 1), $($ts)*}
380+
};
381+
{$n:expr,} => {
382+
#[stable(since = "1.4.0", feature = "array_default")]
383+
impl<T> Default for [T; $n] {
384+
fn default() -> [T; $n] { [] }
385+
}
386+
};
387+
}
388+
389+
array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
389390
}
390391

391-
array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T}
392+
#[cfg(not(bootstrap))]
393+
mod array_defaults {
394+
// We use auto traits to get overlapping impls without relying on nightly features.
395+
//
396+
// As the auto impl for `SendToDefault` is only considered if the manual impl does not apply,
397+
// we have to use the generic impl for `T: Default` as the impl for the `N = 0` case would
398+
// influence type inference in undesirable ways.
399+
//
400+
// While we are now able to implement `Default` exactly for the array types we want,
401+
// we're still not able to actually write the body of the `Default` function without
402+
// some further hacks.
403+
//
404+
// The idea here is that `array_default_hack` is resolved to itself only if `N = 0`
405+
// and is otherwise replaced with `T::default()`.
406+
//
407+
// This would cause issues if `T` doesn't actually implement default but as this function
408+
// is private and only used in the default impl itself this can not happen.
409+
410+
struct ZeroToSend<T, const N: usize>(*mut (), T);
411+
unsafe impl<T> Send for ZeroToSend<T, 0> {}
412+
413+
/// This struct implements `Send` either because of the manual impl for `N` is `0` or
414+
/// because all its fields implement `Send`, which is the case if `T` implements `Default`.
415+
#[unstable(
416+
feature = "array_default_impl",
417+
issue = "none",
418+
reason = "internal implementation detail for `[T; N]: Default`"
419+
)]
420+
#[allow(missing_debug_implementations)]
421+
pub struct SendToDefault<T, const N: usize>(ZeroToSend<T, N>);
422+
#[unstable(feature = "array_default_impl", issue = "none")]
423+
unsafe impl<T: Default, const N: usize> Send for SendToDefault<T, N> {}
424+
425+
// This function must not get called for `N != 0` if `T` does not implement `Default`.
426+
#[lang = "array_default_hack"]
427+
unsafe fn array_default_hack<T, const N: usize>() -> T {
428+
unreachable!("array_default_hack used for array with length {}", N);
429+
}
430+
431+
#[stable(since = "1.4.0", feature = "array_default")]
432+
impl<T, const N: usize> Default for [T; N]
433+
where
434+
SendToDefault<T, N>: Send,
435+
{
436+
fn default() -> [T; N] {
437+
// SAFETY: The only case where `T` does not implement `Default` is
438+
// when `N` is zero, in which case `array_default_hack` isn't called.
439+
[(); N].map(|()| unsafe { array_default_hack::<T, N>() })
440+
}
441+
}
442+
}
392443

393444
#[lang = "array"]
394445
impl<T, const N: usize> [T; N] {

library/core/src/default.rs

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub trait Default: Sized {
113113
/// }
114114
/// ```
115115
#[stable(feature = "rust1", since = "1.0.0")]
116+
#[cfg_attr(not(bootstrap), lang = "default_fn")]
116117
fn default() -> Self;
117118
}
118119

library/core/tests/array.rs

+16
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,19 @@ fn cell_allows_array_cycle() {
356356
b3.a[0].set(Some(&b1));
357357
b3.a[1].set(Some(&b2));
358358
}
359+
360+
fn generic_default<T: Default, const N: usize>() -> [T; N] {
361+
Default::default()
362+
}
363+
364+
#[test]
365+
fn use_generic_default() {
366+
assert_eq!(generic_default::<String, 2>(), [String::new(), String::new()]);
367+
assert_eq!(generic_default::<u8, 33>(), [0; 33]);
368+
}
369+
370+
#[test]
371+
fn use_zero_default() {
372+
struct NotDefault;
373+
assert!(matches!(<[NotDefault; 0] as Default>::default(), []));
374+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
struct NotDefault;
2+
3+
fn main() {
4+
let _: [NotDefault; 1] = Default::default();
5+
//~^ ERROR the trait bound `NotDefault: Default` is not satisfied
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0277]: the trait bound `NotDefault: Default` is not satisfied
2+
--> $DIR/default-not-length-1.rs:4:30
3+
|
4+
LL | let _: [NotDefault; 1] = Default::default();
5+
| ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `NotDefault`
6+
|
7+
= note: required because of the requirements on the impl of `Send` for `array::array_defaults::SendToDefault<NotDefault, 1_usize>`
8+
= note: required because of the requirements on the impl of `Default` for `[NotDefault; 1]`
9+
= note: required by `std::default::Default::default`
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)