From 5aa2bc5b7d92acd8ae6350936fa3f8ae7789f93b Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Thu, 8 Oct 2020 02:48:42 +0100 Subject: [PATCH] Add built-in implementations of `Default` for function definition and zero-sized closure types. --- compiler/rustc_hir/src/lang_items.rs | 3 +- compiler/rustc_middle/src/mir/mono.rs | 3 +- compiler/rustc_middle/src/mir/visit.rs | 3 +- compiler/rustc_middle/src/ty/instance.rs | 16 ++++- compiler/rustc_middle/src/ty/mod.rs | 3 +- .../rustc_middle/src/ty/structural_impls.rs | 6 +- .../rustc_mir/src/interpret/terminator.rs | 1 + .../rustc_mir/src/monomorphize/collector.rs | 6 +- .../src/monomorphize/partitioning/default.rs | 6 +- compiler/rustc_mir/src/shim.rs | 38 +++++++++++ .../src/traits/select/candidate_assembly.rs | 5 ++ .../src/traits/select/confirmation.rs | 2 + .../src/traits/select/mod.rs | 68 +++++++++++++++++++ compiler/rustc_ty/src/instance.rs | 16 ++++- library/core/src/default.rs | 1 + src/test/ui/builtin-default.rs | 14 ++++ src/test/ui/not-default-closure.rs | 15 ++++ src/test/ui/not-default-closure.stderr | 17 +++++ 18 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 src/test/ui/builtin-default.rs create mode 100644 src/test/ui/not-default-closure.rs create mode 100644 src/test/ui/not-default-closure.stderr diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 3e4eb9eafd7f9..7107d44209aaa 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -157,7 +157,7 @@ where } language_item_table! { -// Variant name, Name, Method name, Target; +// Variant name, Name, Method name, Target; Bool, sym::bool, bool_impl, Target::Impl; Char, sym::char, char_impl, Target::Impl; Str, sym::str, str_impl, Target::Impl; @@ -196,6 +196,7 @@ language_item_table! { StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait; Copy, sym::copy, copy_trait, Target::Trait; Clone, sym::clone, clone_trait, Target::Trait; + Default, kw::Default, default_trait, Target::Trait; Sync, sym::sync, sync_trait, Target::Trait; DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait; // The associated item of `trait DiscriminantKind`. diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 79e2c5aac2385..bb49aef2470f5 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -344,7 +344,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceDef::Virtual(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) => None, + | InstanceDef::CloneShim(..) + | InstanceDef::DefaultShim(..) => None, } } MonoItem::Static(def_id) => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d8d639ab73451..7fa942f65f457 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -351,7 +351,8 @@ macro_rules! make_mir_visitor { ty::InstanceDef::FnPtrShim(_def_id, ty) | ty::InstanceDef::DropGlue(_def_id, Some(ty)) | - ty::InstanceDef::CloneShim(_def_id, ty) => { + ty::InstanceDef::CloneShim(_def_id, ty) | + ty::InstanceDef::DefaultShim(_def_id, ty) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty(ty, TyContext::Location(location)); } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 8b3fb87507061..5fff26918f5b3 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -93,6 +93,14 @@ pub enum InstanceDef<'tcx> { /// /// The `DefId` is for `Clone::clone`, the `Ty` is the type `T` with the builtin `Clone` impl. CloneShim(DefId, Ty<'tcx>), + + /// Compiler-generated `::default` implementation. + /// + /// Function definitions and closures without upvars have a compiler-generated `Default` + /// implementation. + /// + /// The `DefId` is for `Default::default`, the `Ty` is the type `T` with the builtin `Default` impl. + DefaultShim(DefId, Ty<'tcx>), } impl<'tcx> Instance<'tcx> { @@ -148,7 +156,8 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::Intrinsic(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) => def_id, + | InstanceDef::CloneShim(def_id, _) + | InstanceDef::DefaultShim(def_id, _) => def_id, } } @@ -163,7 +172,8 @@ impl<'tcx> InstanceDef<'tcx> { | InstanceDef::Intrinsic(def_id) | InstanceDef::ClosureOnceShim { call_once: def_id } | InstanceDef::DropGlue(def_id, _) - | InstanceDef::CloneShim(def_id, _) => ty::WithOptConstParam::unknown(def_id), + | InstanceDef::CloneShim(def_id, _) + | InstanceDef::DefaultShim(def_id, _) => ty::WithOptConstParam::unknown(def_id), } } @@ -242,6 +252,7 @@ impl<'tcx> InstanceDef<'tcx> { pub fn has_polymorphic_mir_body(&self) -> bool { match *self { InstanceDef::CloneShim(..) + | InstanceDef::DefaultShim(..) | InstanceDef::FnPtrShim(..) | InstanceDef::DropGlue(_, Some(_)) => false, InstanceDef::ClosureOnceShim { .. } @@ -275,6 +286,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"), InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({}))", ty), InstanceDef::CloneShim(_, ty) => write!(f, " - shim({})", ty), + InstanceDef::DefaultShim(_, ty) => write!(f, " - shim({})", ty), } } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 096c53ca9964c..9c7ce94c4413d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2949,7 +2949,8 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::DropGlue(..) - | ty::InstanceDef::CloneShim(..) => self.mir_shims(instance), + | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::DefaultShim(..) => self.mir_shims(instance), } } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 431225e276751..e585a76817179 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -707,6 +707,9 @@ impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { ty::InstanceDef::CloneShim(def_id, ty) => { Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) } + ty::InstanceDef::DefaultShim(def_id, ty) => { + Some(ty::InstanceDef::DefaultShim(def_id, tcx.lift(ty)?)) + } } } } @@ -888,6 +891,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { } DropGlue(did, ty) => DropGlue(did.fold_with(folder), ty.fold_with(folder)), CloneShim(did, ty) => CloneShim(did.fold_with(folder), ty.fold_with(folder)), + DefaultShim(did, ty) => DefaultShim(did.fold_with(folder), ty.fold_with(folder)), }, } } @@ -900,7 +904,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { did.visit_with(visitor) } - FnPtrShim(did, ty) | CloneShim(did, ty) => { + FnPtrShim(did, ty) | CloneShim(did, ty) | DefaultShim(did, ty) => { did.visit_with(visitor)?; ty.visit_with(visitor) } diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index bb11c2a23bd81..eeff059555103 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -262,6 +262,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::DefaultShim(..) | ty::InstanceDef::Item(_) => { // We need MIR for this fn let body = match M::find_mir_or_eval_fn(self, instance, args, ret, unwind)? { diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 417176564b92d..efc43ff89746e 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -782,7 +782,8 @@ fn visit_instance_use<'tcx>( | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::Item(..) | ty::InstanceDef::FnPtrShim(..) - | ty::InstanceDef::CloneShim(..) => { + | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::DefaultShim(..) => { output.push(create_fn_mono_item(tcx, instance, source)); } } @@ -802,7 +803,8 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> | ty::InstanceDef::FnPtrShim(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Intrinsic(_) - | ty::InstanceDef::CloneShim(..) => return true, + | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::DefaultShim(..) => return true, }; if tcx.is_foreign_item(def_id) { diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs index 5083a45b539ed..4fee771a56298 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs @@ -278,7 +278,8 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceDef::Intrinsic(..) | ty::InstanceDef::DropGlue(..) | ty::InstanceDef::Virtual(..) - | ty::InstanceDef::CloneShim(..) => return None, + | ty::InstanceDef::CloneShim(..) + | ty::InstanceDef::DefaultShim(..) => return None, }; // If this is a method, we want to put it into the same module as @@ -428,7 +429,8 @@ fn mono_item_visibility( | InstanceDef::Intrinsic(..) | InstanceDef::ClosureOnceShim { .. } | InstanceDef::DropGlue(..) - | InstanceDef::CloneShim(..) => return Visibility::Hidden, + | InstanceDef::CloneShim(..) + | InstanceDef::DefaultShim(..) => return Visibility::Hidden, }; // The `start_fn` lang item is actually a monomorphized instance of a diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index b2fa4b11f3658..502cd0c6788c9 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -66,6 +66,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' } ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty), ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), + ty::InstanceDef::DefaultShim(def_id, ty) => build_default_shim(tcx, def_id, ty), ty::InstanceDef::Virtual(..) => { bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance) } @@ -639,6 +640,43 @@ impl CloneShimBuilder<'tcx> { } } +/// Builds a `Default::default` shim for `self_ty`. Here, `def_id` is `Default::default`. +fn build_default_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> { + debug!("build_default_shim(def_id={:?})", def_id); + + let span = tcx.def_span(def_id); + let source_info = SourceInfo::outermost(span); + + let substs = tcx.mk_substs_trait(self_ty, &[]); + let sig = tcx.fn_sig(def_id).subst(tcx, substs); + let sig = tcx.erase_late_bound_regions(&sig); + let erased_ty = sig.inputs_and_output[0]; + let local_decls = local_decls_for_sig(&sig, span); + + let (fn_def_id, fn_substs) = match erased_ty.kind() { + ty::FnDef(fn_def_id, fn_substs) | ty::Closure(fn_def_id, fn_substs) => { + (*fn_def_id, *fn_substs) + } + _ => bug!("unexpected type for Debug shim {:?}", erased_ty), + }; + + let body = BasicBlockData { + statements: vec![Statement { + source_info, + kind: StatementKind::Assign(box ( + Place::return_place(), + Rvalue::Use(Operand::function_handle(tcx, fn_def_id, fn_substs, span)), + )), + }], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), + is_cleanup: false, + }; + + let source = MirSource::from_instance(ty::InstanceDef::DefaultShim(def_id, erased_ty)); + + new_body(source, IndexVec::from_elem_n(body, 1), local_decls, 0, span) +} + /// Builds a "call" shim for `instance`. The shim calls the function specified by `call_kind`, /// first adjusting its first argument according to `rcvr_adjustment`. fn build_call_shim<'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b0bfb4ad17371..97c9984917aa0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -281,6 +281,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // types have builtin support for `Clone`. let clone_conditions = self.copy_clone_conditions(obligation); self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?; + } else if lang_items.default_trait() == Some(def_id) { + // Function definition and closure types have built-in implementations of the + // `Default` trait. + let default_conditions = self.default_conditions(obligation); + self.assemble_builtin_bound_candidates(default_conditions, &mut candidates)?; } self.assemble_generator_candidates(obligation, &mut candidates)?; diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 872b8e85f563f..b2bbf868540bc 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -218,6 +218,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.copy_clone_conditions(obligation) } else if Some(trait_def) == lang_items.clone_trait() { self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.default_trait() { + self.default_conditions(obligation) } else { bug!("unexpected builtin trait {:?}", trait_def) }; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4cc4bc0acdab6..14c0316811431 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1630,6 +1630,74 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn default_conditions( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + match self_ty.kind() { + // Built-in implementations of `Default` for function definition and closure types + // with no upvars. + ty::FnDef(..) => Where(ty::Binder::dummy(Vec::new())), + ty::Closure(_, substs) => { + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else if substs.as_closure().upvar_tys().next().is_none() { + Where(ty::Binder::dummy(Vec::new())) + } else { + None + } + } + + ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::FnPtr(_) + | ty::Error(_) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, _) + | ty::Dynamic(..) + | ty::Str + | ty::Slice(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Foreign(..) + | ty::Array(_, _) + | ty::Tuple(_) + | ty::Adt(..) + | ty::Projection(..) + | ty::Param(..) + | ty::Opaque(..) => { + // Fallback to whatever user-defined impls exist in this case. + None + } + + ty::Infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ambiguous + } + + ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + /// For default impls, we need to break apart a type into its /// "constituent types" -- meaning, the types that it contains. /// diff --git a/compiler/rustc_ty/src/instance.rs b/compiler/rustc_ty/src/instance.rs index 220f4cec742f1..887eae5ab4042 100644 --- a/compiler/rustc_ty/src/instance.rs +++ b/compiler/rustc_ty/src/instance.rs @@ -3,7 +3,8 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, Instance, TyCtxt, TypeFoldable}; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::symbol::{kw, sym}; +use rustc_span::DUMMY_SP; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use traits::{translate_substs, Reveal}; @@ -267,6 +268,19 @@ fn resolve_associated_item<'tcx>( let substs = tcx.erase_regions(&rcvr_substs); Some(ty::Instance::new(def_id, substs)) } + } else if Some(trait_ref.def_id) == tcx.lang_items().default_trait() { + let name = tcx.item_name(def_id); + assert_eq!(name, kw::Default); + + let self_ty = trait_ref.self_ty(); + match self_ty.kind() { + ty::FnDef(..) | ty::Closure(..) => {} + _ => return Ok(None), + } + Some(Instance { + def: ty::InstanceDef::DefaultShim(def_id, self_ty), + substs: rcvr_substs, + }) } else { None } diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 9a8d65cd4e06b..747f3395bdaab 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -80,6 +80,7 @@ /// bar: f32, /// } /// ``` +#[cfg_attr(not(bootstrap), lang = "default")] #[stable(feature = "rust1", since = "1.0.0")] pub trait Default: Sized { /// Returns the "default value" for a type. diff --git a/src/test/ui/builtin-default.rs b/src/test/ui/builtin-default.rs new file mode 100644 index 0000000000000..85a782fd72764 --- /dev/null +++ b/src/test/ui/builtin-default.rs @@ -0,0 +1,14 @@ +// run-pass +// Test that `Default` is correctly implemented for builtin types. + +fn test_default(_: F) { + let f = F::default(); + f(); +} + +fn foo() { } + +fn main() { + test_default(foo); + test_default(|| ()); +} diff --git a/src/test/ui/not-default-closure.rs b/src/test/ui/not-default-closure.rs new file mode 100644 index 0000000000000..24107e1a34548 --- /dev/null +++ b/src/test/ui/not-default-closure.rs @@ -0,0 +1,15 @@ +// Check that closures do not implement `Default` if their environment is not empty. + +fn test_default(_: F) { + let f = F::default(); + f(); +} + +fn main() { + let a = "Bob"; + let hello = move || { + println!("Hello {}", a); + }; + + test_default(hello); //~ ERROR the trait bound +} diff --git a/src/test/ui/not-default-closure.stderr b/src/test/ui/not-default-closure.stderr new file mode 100644 index 0000000000000..eaa680910ca88 --- /dev/null +++ b/src/test/ui/not-default-closure.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `[closure@$DIR/not-default-closure.rs:10:17: 12:6]: Default` is not satisfied + --> $DIR/not-default-closure.rs:14:5 + | +LL | fn test_default(_: F) { + | ------- required by this bound in `test_default` +... +LL | let hello = move || { + | ------- consider calling this closure +... +LL | test_default(hello); + | ^^^^^^^^^^^^ the trait `Default` is not implemented for `[closure@$DIR/not-default-closure.rs:10:17: 12:6]` + | + = help: use parentheses to call the closure: `hello()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.