Skip to content

Commit

Permalink
Auto merge of #42417 - eddyb:separate-fn-sig, r=nikomatsakis
Browse files Browse the repository at this point in the history
Don't drag function signatures along function item types.

This PR separates the signature of a function from the "function item type" (`TyFnDef`), leaving only the `DefId` and parameter `Substs`, making them even more like (captureless) closure types.

The motivation for this change is reducing typesystem complexity, and its consequences:
* operating on the signature instead of just the parameters was less efficient
  * specifically, signatures can easily add several levels of depth on top of the parameter types
  * and the signatured were always substituted and normalized, so typically even more complex
* it was *the only* type that was *both* nominal (identity) and structural (signature)
  * harder to model in Chalk than either a purely nominal or structural type
  * subtyping worked on the signature but parameters were always invariant
  * call type-checking was transforming signatures but keeping the nominal half intact
  * the signature could therefore get out of sync during type inference in several ways

That last point comes with a `[breaking-change]`, because functions with `'static` in their return types will now *not* be as usable as if they were using lifetime parameters instead:
```rust
// Will cause lifetime mismatch in main after this PR.
fn bar() -> &'static str { "bar" }
// Will continue to work fine, as every use can choose its own lifetime.
fn bar<'a>() -> &'a str { "bar" }

fn main() {
    let s = String::from("foo");
    Some(&s[..]).unwrap_or_else(bar);
}
```

r? @nikomatsakis
  • Loading branch information
bors committed Jun 28, 2017
2 parents f590a44 + 69076f3 commit 4079e61
Show file tree
Hide file tree
Showing 73 changed files with 777 additions and 879 deletions.
3 changes: 1 addition & 2 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,10 +524,9 @@ for ty::TypeVariants<'tcx>
region.hash_stable(hcx, hasher);
pointee_ty.hash_stable(hcx, hasher);
}
TyFnDef(def_id, substs, ref sig) => {
TyFnDef(def_id, substs) => {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
sig.hash_stable(hcx, hasher);
}
TyFnPtr(ref sig) => {
sig.hash_stable(hcx, hasher);
Expand Down
8 changes: 6 additions & 2 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
Some(self.tcx.closure_kind(def_id))
}

pub fn closure_type(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
/// Obtain the signature of a function or closure.
/// For closures, unlike `tcx.fn_sig(def_id)`, this method will
/// work during the type-checking of the enclosing function and
/// return the closure signature in its partially inferred state.
pub fn fn_sig(&self, def_id: DefId) -> ty::PolyFnSig<'tcx> {
if let Some(tables) = self.in_progress_tables {
if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
if let Some(&ty) = tables.borrow().closure_tys.get(&id) {
Expand All @@ -1378,7 +1382,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
}
}

self.tcx.closure_type(def_id)
self.tcx.fn_sig(def_id)
}
}

Expand Down
28 changes: 13 additions & 15 deletions src/librustc/middle/effect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! `unsafe`.
use self::RootUnsafeContext::*;

use ty::{self, Ty, TyCtxt};
use ty::{self, TyCtxt};
use lint;

use syntax::ast;
Expand Down Expand Up @@ -40,14 +40,6 @@ enum RootUnsafeContext {
UnsafeBlock(ast::NodeId),
}

fn type_is_unsafe_function(ty: Ty) -> bool {
match ty.sty {
ty::TyFnDef(.., f) |
ty::TyFnPtr(f) => f.unsafety() == hir::Unsafety::Unsafe,
_ => false,
}
}

struct EffectCheckVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
Expand Down Expand Up @@ -174,10 +166,11 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
match expr.node {
hir::ExprMethodCall(..) => {
let def_id = self.tables.type_dependent_defs[&expr.id].def_id();
let base_type = self.tcx.type_of(def_id);
debug!("effect: method call case, base type is {:?}",
base_type);
if type_is_unsafe_function(base_type) {
let sig = self.tcx.fn_sig(def_id);
debug!("effect: method call case, signature is {:?}",
sig);

if sig.0.unsafety == hir::Unsafety::Unsafe {
self.require_unsafe(expr.span,
"invocation of unsafe method")
}
Expand All @@ -186,8 +179,13 @@ impl<'a, 'tcx> Visitor<'tcx> for EffectCheckVisitor<'a, 'tcx> {
let base_type = self.tables.expr_ty_adjusted(base);
debug!("effect: call case, base type is {:?}",
base_type);
if type_is_unsafe_function(base_type) {
self.require_unsafe(expr.span, "call to unsafe function")
match base_type.sty {
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
if base_type.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
self.require_unsafe(expr.span, "call to unsafe function")
}
}
_ => {}
}
}
hir::ExprUnary(hir::UnDeref, ref base) => {
Expand Down
27 changes: 8 additions & 19 deletions src/librustc/middle/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,8 @@ fn unpack_option_like<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,

impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
let intrinsic = match self.tcx.type_of(def_id).sty {
ty::TyFnDef(.., bfty) => bfty.abi() == RustIntrinsic,
_ => return false
};
intrinsic && self.tcx.item_name(def_id) == "transmute"
self.tcx.fn_sig(def_id).abi() == RustIntrinsic &&
self.tcx.item_name(def_id) == "transmute"
}

fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) {
Expand Down Expand Up @@ -153,22 +150,14 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
} else {
Def::Err
};
match def {
Def::Fn(did) if self.def_id_is_transmute(did) => {
if let Def::Fn(did) = def {
if self.def_id_is_transmute(did) {
let typ = self.tables.node_id_to_type(expr.id);
let typ = self.tcx.lift_to_global(&typ).unwrap();
match typ.sty {
ty::TyFnDef(.., sig) if sig.abi() == RustIntrinsic => {
let from = sig.inputs().skip_binder()[0];
let to = *sig.output().skip_binder();
self.check_transmute(expr.span, from, to);
}
_ => {
span_bug!(expr.span, "transmute wasn't a bare fn?!");
}
}
let sig = typ.fn_sig(self.tcx);
let from = sig.inputs().skip_binder()[0];
let to = *sig.output().skip_binder();
self.check_transmute(expr.span, from, to);
}
_ => {}
}

intravisit::walk_expr(self, expr);
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/traits/object_safety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

// The `Self` type is erased, so it should not appear in list of
// arguments or return type apart from the receiver.
let ref sig = self.type_of(method.def_id).fn_sig();
let ref sig = self.fn_sig(method.def_id);
for input_ty in &sig.skip_binder().inputs()[1..] {
if self.contains_illegal_self_type_reference(trait_def_id, input_ty) {
return Some(MethodViolationCode::ReferencesSelf);
Expand Down
14 changes: 12 additions & 2 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1137,9 +1137,19 @@ fn confirm_fn_pointer_candidate<'cx, 'gcx, 'tcx>(
-> Progress<'tcx>
{
let fn_type = selcx.infcx().shallow_resolve(fn_pointer_vtable.fn_ty);
let sig = fn_type.fn_sig();
let sig = fn_type.fn_sig(selcx.tcx());
let Normalized {
value: sig,
obligations
} = normalize_with_depth(selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth+1,
&sig);

confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
.with_addl_obligations(fn_pointer_vtable.nested)
.with_addl_obligations(obligations)
}

fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
Expand All @@ -1149,7 +1159,7 @@ fn confirm_closure_candidate<'cx, 'gcx, 'tcx>(
-> Progress<'tcx>
{
let closure_typer = selcx.closure_typer();
let closure_type = closure_typer.closure_type(vtable.closure_def_id)
let closure_type = closure_typer.fn_sig(vtable.closure_def_id)
.subst(selcx.tcx(), vtable.substs.substs);
let Normalized {
value: closure_type,
Expand Down
35 changes: 19 additions & 16 deletions src/librustc/traits/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1404,19 +1404,15 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
}

// provide an impl, but only for suitable `fn` pointers
ty::TyFnDef(.., ty::Binder(ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
variadic: false,
..
})) |
ty::TyFnPtr(ty::Binder(ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
variadic: false,
..
})) => {
candidates.vec.push(FnPointerCandidate);
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
if let ty::Binder(ty::FnSig {
unsafety: hir::Unsafety::Normal,
abi: Abi::Rust,
variadic: false,
..
}) = self_ty.fn_sig(self.tcx()) {
candidates.vec.push(FnPointerCandidate);
}
}

_ => { }
Expand Down Expand Up @@ -2348,19 +2344,26 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {

// ok to skip binder; it is reintroduced below
let self_ty = self.infcx.shallow_resolve(*obligation.self_ty().skip_binder());
let sig = self_ty.fn_sig();
let sig = self_ty.fn_sig(self.tcx());
let trait_ref =
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
self_ty,
sig,
util::TupleArgumentsFlag::Yes)
.map_bound(|(trait_ref, _)| trait_ref);

let Normalized { value: trait_ref, obligations } =
project::normalize_with_depth(self,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
&trait_ref);

self.confirm_poly_trait_refs(obligation.cause.clone(),
obligation.param_env,
obligation.predicate.to_poly_trait_ref(),
trait_ref)?;
Ok(VtableFnPointerData { fn_ty: self_ty, nested: vec![] })
Ok(VtableFnPointerData { fn_ty: self_ty, nested: obligations })
}

fn confirm_closure_candidate(&mut self,
Expand Down Expand Up @@ -2799,7 +2802,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
substs: ty::ClosureSubsts<'tcx>)
-> ty::PolyTraitRef<'tcx>
{
let closure_type = self.infcx.closure_type(closure_def_id)
let closure_type = self.infcx.fn_sig(closure_def_id)
.subst(self.tcx(), substs.substs);
let ty::Binder((trait_ref, _)) =
self.tcx().closure_trait_ref_and_return_type(obligation.predicate.def_id(),
Expand Down
5 changes: 2 additions & 3 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1378,9 +1378,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}

pub fn mk_fn_def(self, def_id: DefId,
substs: &'tcx Substs<'tcx>,
fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
self.mk_ty(TyFnDef(def_id, substs, fty))
substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
self.mk_ty(TyFnDef(def_id, substs))
}

pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/ty/fast_reject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// view of possibly unifying
simplify_type(tcx, mt.ty, can_simplify_params)
}
ty::TyFnDef(def_id, _) |
ty::TyClosure(def_id, _) => {
Some(ClosureSimplifiedType(def_id))
}
ty::TyNever => Some(NeverSimplifiedType),
ty::TyTuple(ref tys, _) => {
Some(TupleSimplifiedType(tys.len()))
}
ty::TyFnDef(.., ref f) | ty::TyFnPtr(ref f) => {
ty::TyFnPtr(ref f) => {
Some(FunctionSimplifiedType(f.skip_binder().inputs().len()))
}
ty::TyProjection(_) | ty::TyParam(_) => {
Expand Down
3 changes: 1 addition & 2 deletions src/librustc/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,8 @@ impl FlagComputation {
self.add_tys(&ts[..]);
}

&ty::TyFnDef(_, substs, f) => {
&ty::TyFnDef(_, substs) => {
self.add_substs(substs);
self.add_fn_sig(f);
}

&ty::TyFnPtr(f) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/item_path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> {
.filter_map(|ty| characteristic_def_id_of_type(ty))
.next(),

ty::TyFnDef(def_id, ..) |
ty::TyFnDef(def_id, _) |
ty::TyClosure(def_id, _) => Some(def_id),

ty::TyBool |
Expand Down
7 changes: 3 additions & 4 deletions src/librustc/ty/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,13 +875,12 @@ define_maps! { <'tcx>
/// for trans. This is also the only query that can fetch non-local MIR, at present.
[] optimized_mir: Mir(DefId) -> &'tcx mir::Mir<'tcx>,

/// Records the type of each closure. The def ID is the ID of the
/// Type of each closure. The def ID is the ID of the
/// expression defining the closure.
[] closure_kind: ItemSignature(DefId) -> ty::ClosureKind,

/// Records the type of each closure. The def ID is the ID of the
/// expression defining the closure.
[] closure_type: ItemSignature(DefId) -> ty::PolyFnSig<'tcx>,
/// The signature of functions and closures.
[] fn_sig: ItemSignature(DefId) -> ty::PolyFnSig<'tcx>,

/// Caches CoerceUnsized kinds for impls on custom types.
[] coerce_unsized_info: ItemSignature(DefId)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl AssociatedItem {
// late-bound regions, and we don't want method signatures to show up
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
format!("{}", tcx.type_of(self.def_id).fn_sig().skip_binder())
format!("{}", tcx.fn_sig(self.def_id).skip_binder())
}
ty::AssociatedKind::Type => format!("type {};", self.name.to_string()),
ty::AssociatedKind::Const => {
Expand Down
12 changes: 5 additions & 7 deletions src/librustc/ty/relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ impl<'tcx> Relate<'tcx> for ty::TraitRef<'tcx> {
if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else {
let substs = relation.relate_item_substs(a.def_id, a.substs, b.substs)?;
let substs = relate_substs(relation, None, a.substs, b.substs)?;
Ok(ty::TraitRef { def_id: a.def_id, substs: substs })
}
}
Expand All @@ -308,7 +308,7 @@ impl<'tcx> Relate<'tcx> for ty::ExistentialTraitRef<'tcx> {
if a.def_id != b.def_id {
Err(TypeError::Traits(expected_found(relation, &a.def_id, &b.def_id)))
} else {
let substs = relation.relate_item_substs(a.def_id, a.substs, b.substs)?;
let substs = relate_substs(relation, None, a.substs, b.substs)?;
Ok(ty::ExistentialTraitRef { def_id: a.def_id, substs: substs })
}
}
Expand Down Expand Up @@ -440,13 +440,11 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R,
}
}

(&ty::TyFnDef(a_def_id, a_substs, a_fty),
&ty::TyFnDef(b_def_id, b_substs, b_fty))
(&ty::TyFnDef(a_def_id, a_substs), &ty::TyFnDef(b_def_id, b_substs))
if a_def_id == b_def_id =>
{
let substs = relate_substs(relation, None, a_substs, b_substs)?;
let fty = relation.relate(&a_fty, &b_fty)?;
Ok(tcx.mk_fn_def(a_def_id, substs, fty))
let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?;
Ok(tcx.mk_fn_def(a_def_id, substs))
}

(&ty::TyFnPtr(a_fty), &ty::TyFnPtr(b_fty)) =>
Expand Down
10 changes: 3 additions & 7 deletions src/librustc/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,10 +531,8 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyDynamic(ref trait_ty, ref region) =>
ty::TyDynamic(trait_ty.fold_with(folder), region.fold_with(folder)),
ty::TyTuple(ts, defaulted) => ty::TyTuple(ts.fold_with(folder), defaulted),
ty::TyFnDef(def_id, substs, f) => {
ty::TyFnDef(def_id,
substs.fold_with(folder),
f.fold_with(folder))
ty::TyFnDef(def_id, substs) => {
ty::TyFnDef(def_id, substs.fold_with(folder))
}
ty::TyFnPtr(f) => ty::TyFnPtr(f.fold_with(folder)),
ty::TyRef(ref r, tm) => {
Expand Down Expand Up @@ -568,9 +566,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> {
ty::TyDynamic(ref trait_ty, ref reg) =>
trait_ty.visit_with(visitor) || reg.visit_with(visitor),
ty::TyTuple(ts, _) => ts.visit_with(visitor),
ty::TyFnDef(_, substs, ref f) => {
substs.visit_with(visitor) || f.visit_with(visitor)
}
ty::TyFnDef(_, substs) => substs.visit_with(visitor),
ty::TyFnPtr(ref f) => f.visit_with(visitor),
ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor),
ty::TyClosure(_did, ref substs) => substs.visit_with(visitor),
Expand Down
Loading

0 comments on commit 4079e61

Please sign in to comment.