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

Add built-in implementations of Default for function definition and… #77688

Closed
wants to merge 1 commit 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
3 changes: 2 additions & 1 deletion compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`.
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/mir/mono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_middle/src/ty/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<T as Default>::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.
Copy link

@ghost ghost Nov 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The `DefId` is for `Default::default`, the `Ty` is the type `T` with the builtin `Default` impl.
/// The `DefId` is for `Default::default`, the `Ty` is the type `T` with the built-in `Default` impl.

there is inconsistency thoughout in the hyphenation of the(se) word(s) in this PR (and others), the surface area is limited to documentation so it maybe worth a simple rg and replace for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is just from rebasing on top of other PRs that changed these things, will address that before this get merged.

DefaultShim(DefId, Ty<'tcx>),
}

impl<'tcx> Instance<'tcx> {
Expand Down Expand Up @@ -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,
}
}

Expand All @@ -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),
}
}

Expand Down Expand Up @@ -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 { .. }
Expand Down Expand Up @@ -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),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}

Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_middle/src/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?))
}
}
}
}
Expand Down Expand Up @@ -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)),
},
}
}
Expand All @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/src/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)? {
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_mir/src/monomorphize/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
Expand All @@ -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) {
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_mir/src/monomorphize/partitioning/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
38 changes: 38 additions & 0 deletions compiler/rustc_mir/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
Expand Down
68 changes: 68 additions & 0 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_ty/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
}
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 @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/builtin-default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// run-pass
// Test that `Default` is correctly implemented for builtin types.

fn test_default<F: Default + Fn()>(_: F) {
let f = F::default();
f();
}

fn foo() { }

fn main() {
test_default(foo);
test_default(|| ());
}
15 changes: 15 additions & 0 deletions src/test/ui/not-default-closure.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Check that closures do not implement `Default` if their environment is not empty.

fn test_default<F: Default + Fn()>(_: F) {
let f = F::default();
f();
}

fn main() {
let a = "Bob";
let hello = move || {
println!("Hello {}", a);
};

test_default(hello); //~ ERROR the trait bound
}
Loading