Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement Default for all arrays #84838

Closed
wants to merge 3 commits into from
Closed
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
6 changes: 5 additions & 1 deletion compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ language_item_table! {
UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct;
VaList, sym::va_list, va_list, Target::Struct;

DefaultFn, sym::default_fn, default_fn, Target::Method(MethodKind::Trait { body: false });

Deref, sym::deref, deref_trait, Target::Trait;
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait;
DerefTarget, sym::deref_target, deref_target, Target::AssocTy;
Expand Down Expand Up @@ -332,7 +334,7 @@ language_item_table! {
ResultErr, sym::Err, result_err_variant, Target::Variant;

IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false });
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false});
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false });

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

Expand All @@ -343,4 +345,6 @@ language_item_table! {
Range, sym::Range, range_struct, Target::Struct;
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct;

ArrayDefaultHack, sym::array_default_hack, array_default_hack, Target::Fn;
}
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ symbols! {
arm,
arm_target_feature,
array,
array_default_hack,
arrays,
as_ptr,
as_str,
Expand Down Expand Up @@ -454,6 +455,7 @@ symbols! {
declare_lint_pass,
decode,
default_alloc_error_handler,
default_fn,
default_lib_allocator,
default_type_parameter_fallback,
default_type_params,
Expand Down
31 changes: 31 additions & 0 deletions compiler/rustc_ty_utils/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,37 @@ fn inner_resolve_instance<'tcx>(
ty::InstanceDef::DropGlue(def_id, None)
}
}
ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().array_default_hack() => {
debug!(" => array default hack");
if let Some(val) = substs.const_at(1).try_eval_usize(tcx, param_env) {
if val == 0 {
// For `N == 0` we return the lang item itself, which should just panic.
debug!(" => zero length array");
ty::InstanceDef::Item(def)
} else {
// For `N != 0` we use `<T as Default>::default` which should be well typed
// according to the safety requirements of `array_default_hack`.
debug!(" => with default");
let def_id = match tcx.lang_items().require(rustc_hir::LangItem::DefaultFn)
{
Ok(id) => id,
Err(s) => {
tcx.sess.fatal(&format!("default for array_default_hack: {}", s));
}
};

return Instance::resolve(
tcx,
param_env,
def_id,
tcx.mk_substs(substs.iter().take(1)),
);
}
} else {
debug!(" => too generic");
return Ok(None);
}
}
_ => {
debug!(" => free item");
ty::InstanceDef::Item(def)
Expand Down
80 changes: 60 additions & 20 deletions library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,29 +366,69 @@ impl<T: Ord, const N: usize> Ord for [T; N] {
}
}

// The Default impls cannot be done with const generics because `[T; 0]` doesn't
// require Default to be implemented, and having different impl blocks for
// different numbers isn't supported yet.

macro_rules! array_impl_default {
{$n:expr, $t:ident $($ts:ident)*} => {
#[stable(since = "1.4.0", feature = "array_default")]
impl<T> Default for [T; $n] where T: Default {
fn default() -> [T; $n] {
[$t::default(), $($ts::default()),*]
#[cfg(bootstrap)]
mod array_defaults {
macro_rules! array_impl_default {
{$n:expr, $t:ident $($ts:ident)*} => {
#[stable(since = "1.4.0", feature = "array_default")]
impl<T> Default for [T; $n] where T: Default {
fn default() -> [T; $n] {
[$t::default(), $($ts::default()),*]
}
}
}
array_impl_default!{($n - 1), $($ts)*}
};
{$n:expr,} => {
#[stable(since = "1.4.0", feature = "array_default")]
impl<T> Default for [T; $n] {
fn default() -> [T; $n] { [] }
}
};
array_impl_default!{($n - 1), $($ts)*}
};
{$n:expr,} => {
#[stable(since = "1.4.0", feature = "array_default")]
impl<T> Default for [T; $n] {
fn default() -> [T; $n] { [] }
}
};
}

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}
}

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}
#[cfg(not(bootstrap))]
mod array_defaults {
#[marker]
#[unstable(
feature = "array_default_impl",
issue = "none",
reason = "internal implementation detail for `[T; N]: Default`"
)]
pub trait ArrayDefault {}
#[unstable(
feature = "array_default_impl",
issue = "none",
reason = "internal implementation detail for `[T; N]: Default`"
)]
impl<T: Default, const N: usize> ArrayDefault for [T; N] {}
#[unstable(
feature = "array_default_impl",
issue = "none",
reason = "internal implementation detail for `[T; N]: Default`"
)]
impl<T> ArrayDefault for [T; 0] {}

// This function must not get monomorphized for `N != 0` if `T` does not implement `Default`.
#[lang = "array_default_hack"]
unsafe fn array_default_hack<T, const N: usize>() -> T {
unreachable!("array_default_hack used for array with length {}", N);
}

#[stable(since = "1.4.0", feature = "array_default")]
impl<T, const N: usize> Default for [T; N]
where
[T; N]: ArrayDefault,
{
fn default() -> [T; N] {
// SAFETY: The only case where `T` does not implement `Default` is
// when `N` is zero, in which case `array_default_hack` isn't called.
[(); N].map(|()| unsafe { array_default_hack::<T, N>() })
}
}
}

#[lang = "array"]
impl<T, const N: usize> [T; N] {
Expand Down
1 change: 1 addition & 0 deletions library/core/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub trait Default: Sized {
/// }
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(bootstrap), lang = "default_fn")]
fn default() -> Self;
}

Expand Down
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
#![feature(rustc_attrs)]
#![feature(simd_ffi)]
#![feature(min_specialization)]
#![feature(marker_trait_attr)]
#![feature(staged_api)]
#![feature(std_internals)]
#![feature(stmt_expr_attributes)]
Expand Down
19 changes: 19 additions & 0 deletions library/core/tests/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,22 @@ fn cell_allows_array_cycle() {
b3.a[0].set(Some(&b1));
b3.a[1].set(Some(&b2));
}

#[cfg(not(bootstrap))]
mod array_defaults {
fn generic_default<T: Default, const N: usize>() -> [T; N] {
Default::default()
}

#[test]
fn use_generic_default() {
assert_eq!(generic_default::<String, 2>(), [String::new(), String::new()]);
assert_eq!(generic_default::<u8, 33>(), [0; 33]);
}

#[test]
fn use_zero_default() {
struct NotDefault;
assert!(matches!(<[NotDefault; 0] as Default>::default(), []));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
struct NotDefault;

fn main() {
let _: [NotDefault; 1] = Default::default();
//~^ ERROR the trait bound `NotDefault: Default` is not satisfied
}
13 changes: 13 additions & 0 deletions src/test/ui/const-generics/array-impls/default-not-length-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error[E0277]: the trait bound `NotDefault: Default` is not satisfied
--> $DIR/default-not-length-1.rs:4:30
|
LL | let _: [NotDefault; 1] = Default::default();
| ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `NotDefault`
|
= note: required because of the requirements on the impl of `array::array_defaults::ArrayDefault` for `[NotDefault; 1]`
= note: required because of the requirements on the impl of `Default` for `[NotDefault; 1]`
= note: required by `std::default::Default::default`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.