Skip to content

Commit

Permalink
normalize fn sig as part of reification
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed Dec 13, 2017
1 parent 0c26d8f commit d5cff07
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 58 deletions.
144 changes: 86 additions & 58 deletions src/librustc_mir/transform/type_check.rs
Expand Up @@ -17,7 +17,7 @@ use rustc::infer::region_constraints::RegionConstraintData;
use rustc::traits::{self, FulfillmentContext};
use rustc::ty::error::TypeError;
use rustc::ty::fold::TypeFoldable;
use rustc::ty::{self, Ty, TyCtxt, TypeVariants, ToPolyTraitRef};
use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, TypeVariants};
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::tcx::PlaceTy;
Expand Down Expand Up @@ -193,13 +193,11 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
assert_eq!(def_id, ty_def_id);
substs
}
_ => {
span_bug!(
self.last_span,
"unexpected type for constant function: {:?}",
value.ty
)
}
_ => span_bug!(
self.last_span,
"unexpected type for constant function: {:?}",
value.ty
),
};

let instantiated_predicates =
Expand Down Expand Up @@ -585,12 +583,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
span_mirbug!(self, "", "errors selecting obligation: {:?}", e);
}

self.infcx.process_registered_region_obligations(
&[],
None,
self.param_env,
self.body_id,
);
self.infcx
.process_registered_region_obligations(&[], None, self.param_env, self.body_id);

let data = self.infcx.take_and_reset_region_constraints();
if !data.is_empty() {
Expand Down Expand Up @@ -1164,18 +1158,16 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
self.check_aggregate_rvalue(mir, rvalue, ak, ops, location)
}

Rvalue::Repeat(operand, const_usize) => {
if const_usize.as_u64() > 1 {
let operand_ty = operand.ty(mir, tcx);
Rvalue::Repeat(operand, const_usize) => if const_usize.as_u64() > 1 {
let operand_ty = operand.ty(mir, tcx);

let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().copy_trait().unwrap(),
substs: tcx.mk_substs_trait(operand_ty, &[]),
};
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().copy_trait().unwrap(),
substs: tcx.mk_substs_trait(operand_ty, &[]),
};

self.prove_trait_ref(trait_ref, location);
}
}
self.prove_trait_ref(trait_ref, location);
},

Rvalue::NullaryOp(_, ty) => {
let trait_ref = ty::TraitRef {
Expand All @@ -1186,50 +1178,87 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
self.prove_trait_ref(trait_ref, location);
}

Rvalue::Cast(cast_kind, op, ty) => {
match cast_kind {
CastKind::ReifyFnPointer => {
let ty_fn_ptr_from = tcx.mk_fn_ptr(op.ty(mir, tcx).fn_sig(tcx));
Rvalue::Cast(cast_kind, op, ty) => match cast_kind {
CastKind::ReifyFnPointer => {
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);

if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(self, "", "casting {:?}", terr);
}
}
// The type that we see in the fcx is like
// `foo::<'a, 'b>`, where `foo` is the path to a
// function definition. When we extract the
// signature, it comes from the `fn_sig` query,
// and hence may contain unnormalized results.
let fn_sig = self.normalize(&fn_sig, location);

CastKind::ClosureFnPointer => {
let sig = match op.ty(mir, tcx).sty {
ty::TyClosure(def_id, substs) => {
substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
}
_ => bug!(),
};
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);
let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);

if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(self, "", "casting {:?}", terr);
}
if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}

CastKind::UnsafeFnPointer => {
let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(op.ty(mir, tcx).fn_sig(tcx));

if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(self, "", "casting {:?}", terr);
CastKind::ClosureFnPointer => {
let sig = match op.ty(mir, tcx).sty {
ty::TyClosure(def_id, substs) => {
substs.closure_sig_ty(def_id, tcx).fn_sig(tcx)
}
_ => bug!(),
};
let ty_fn_ptr_from = tcx.coerce_closure_fn_ty(sig);

if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}

CastKind::Unsize => {
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty]),
};
CastKind::UnsafeFnPointer => {
let fn_sig = op.ty(mir, tcx).fn_sig(tcx);

// The type that we see in the fcx is like
// `foo::<'a, 'b>`, where `foo` is the path to a
// function definition. When we extract the
// signature, it comes from the `fn_sig` query,
// and hence may contain unnormalized results.
let fn_sig = self.normalize(&fn_sig, location);

self.prove_trait_ref(trait_ref, location);
let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);

if let Err(terr) = self.eq_types(ty_fn_ptr_from, ty, location.at_self()) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
ty,
terr
);
}
}

CastKind::Misc => {}
CastKind::Unsize => {
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().coerce_unsized_trait().unwrap(),
substs: tcx.mk_substs_trait(op.ty(mir, tcx), &[ty]),
};

self.prove_trait_ref(trait_ref, location);
}
}

CastKind::Misc => {}
},

// FIXME: These other cases have to be implemented in future PRs
Rvalue::Use(..) |
Expand Down Expand Up @@ -1344,8 +1373,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
tcx.predicates_of(*def_id).instantiate(tcx, substs.substs)
}

AggregateKind::Array(_) |
AggregateKind::Tuple => ty::InstantiatedPredicates::empty(),
AggregateKind::Array(_) | AggregateKind::Tuple => ty::InstantiatedPredicates::empty(),
};

let predicates = self.normalize(&instantiated_predicates.predicates, location);
Expand Down
38 changes: 38 additions & 0 deletions src/test/run-pass/mir-typeck-normalize-fn-sig.rs
@@ -0,0 +1,38 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This code was creating an ICE in the MIR type checker. The reason
// is that we are reifying a reference to a function (`foo::<'x>`),
// which involves extracting its signature, but we were not
// normalizing the signature afterwards. As a result, we sometimes got
// errors around the `<u32 as Foo<'x>>::Value`, which can be
// normalized to `f64`.

#![allow(dead_code)]

trait Foo<'x> {
type Value;
}

impl<'x> Foo<'x> for u32 {
type Value = f64;
}

struct Providers<'x> {
foo: for<'y> fn(x: &'x u32, y: &'y u32) -> <u32 as Foo<'x>>::Value,
}

fn foo<'y, 'x: 'x>(x: &'x u32, y: &'y u32) -> <u32 as Foo<'x>>::Value {
*x as f64
}

fn main() {
Providers { foo };
}

0 comments on commit d5cff07

Please sign in to comment.