diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 498000db50f43..b1e5f7ca9293e 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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; @@ -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); @@ -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; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b2dac10c83fac..1ea3031f5c07d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -276,6 +276,7 @@ symbols! { arm, arm_target_feature, array, + array_default_hack, arrays, as_ptr, as_str, @@ -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, diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 874289d02938d..1538147ef90b3 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -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 `::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) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 8b56c9560aacf..b2dfa04e637c1 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -366,29 +366,69 @@ impl 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 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 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 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 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 ArrayDefault for [T; N] {} + #[unstable( + feature = "array_default_impl", + issue = "none", + reason = "internal implementation detail for `[T; N]: Default`" + )] + impl 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 { + unreachable!("array_default_hack used for array with length {}", N); + } + + #[stable(since = "1.4.0", feature = "array_default")] + impl 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::() }) + } + } +} #[lang = "array"] impl [T; N] { diff --git a/library/core/src/default.rs b/library/core/src/default.rs index fd7159d35fa7f..bdc8e7af52a0b 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -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; } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0e2c140c367a9..3849cc1aeb28d 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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)] diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index ce7480ce2ee89..5ff37b230b858 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -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; N] { + Default::default() + } + + #[test] + fn use_generic_default() { + assert_eq!(generic_default::(), [String::new(), String::new()]); + assert_eq!(generic_default::(), [0; 33]); + } + + #[test] + fn use_zero_default() { + struct NotDefault; + assert!(matches!(<[NotDefault; 0] as Default>::default(), [])); + } +} diff --git a/src/test/ui/const-generics/array-impls/default-not-length-1.rs b/src/test/ui/const-generics/array-impls/default-not-length-1.rs new file mode 100644 index 0000000000000..90e56619029a7 --- /dev/null +++ b/src/test/ui/const-generics/array-impls/default-not-length-1.rs @@ -0,0 +1,6 @@ +struct NotDefault; + +fn main() { + let _: [NotDefault; 1] = Default::default(); + //~^ ERROR the trait bound `NotDefault: Default` is not satisfied +} diff --git a/src/test/ui/const-generics/array-impls/default-not-length-1.stderr b/src/test/ui/const-generics/array-impls/default-not-length-1.stderr new file mode 100644 index 0000000000000..47eb87b7f79b3 --- /dev/null +++ b/src/test/ui/const-generics/array-impls/default-not-length-1.stderr @@ -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`.