提交 d7d4d7c8 编写于 作者: N Niko Matsakis

add a `user_substs` table and store the annotations in there

上级 1884fe35
......@@ -12,13 +12,14 @@
use hir::def_id::DefId;
use infer::canonical::Canonical;
use ty::{self, Lift, List, Ty, TyCtxt};
use ty::{self, CanonicalVar, Lift, List, Ty, TyCtxt};
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use serialize::{self, Encodable, Encoder, Decodable, Decoder};
use syntax_pos::{Span, DUMMY_SP};
use rustc_data_structures::accumulate_vec::AccumulateVec;
use rustc_data_structures::array_vec::ArrayVec;
use rustc_data_structures::indexed_vec::Idx;
use core::intrinsics;
use std::cmp::Ordering;
......@@ -180,8 +181,6 @@ fn decode<D: Decoder>(d: &mut D) -> Result<Kind<'tcx>, D::Error> {
/// A substitution mapping generic parameters to new values.
pub type Substs<'tcx> = List<Kind<'tcx>>;
pub type CanonicalSubsts<'gcx> = Canonical<'gcx, &'gcx Substs<'gcx>>;
impl<'a, 'gcx, 'tcx> Substs<'tcx> {
/// Creates a Substs that maps each generic parameter to itself.
pub fn identity_for_item(tcx: TyCtxt<'a, 'gcx, 'tcx>, def_id: DefId)
......@@ -342,6 +341,33 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
}
}
pub type CanonicalSubsts<'gcx> = Canonical<'gcx, &'gcx Substs<'gcx>>;
impl<'gcx> CanonicalSubsts<'gcx> {
/// True if this represents a substitution like
///
/// ```ignore
/// [?0, ?1, ?2]
/// ```
///
/// i.e., each thing is mapped to a canonical variable with the same index.
pub fn is_identity(&self) -> bool {
self.value.iter().zip(CanonicalVar::new(0)..).all(|(kind, cvar)| {
match kind.unpack() {
UnpackedKind::Type(ty) => match ty.sty {
ty::Infer(ty::CanonicalTy(cvar1)) => cvar == cvar1,
_ => false,
},
UnpackedKind::Lifetime(r) => match r {
ty::ReCanonical(cvar1) => cvar == *cvar1,
_ => false,
},
}
})
}
}
impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Substs<'tcx> {}
///////////////////////////////////////////////////////////////////////////
......
......@@ -95,7 +95,7 @@
use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region;
use rustc::mir::interpret::{GlobalId};
use rustc::ty::subst::{UnpackedKind, Subst, Substs};
use rustc::ty::subst::{CanonicalSubsts, UnpackedKind, Subst, Substs};
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, RegionKind};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
......@@ -122,6 +122,7 @@
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::attr;
use syntax::source_map::DUMMY_SP;
use syntax::source_map::original_sp;
use syntax::feature_gate::{GateIssue, emit_feature_err};
use syntax::ptr::P;
......@@ -2058,11 +2059,47 @@ pub fn write_field_index(&self, node_id: ast::NodeId, index: usize) {
pub fn write_method_call(&self,
hir_id: hir::HirId,
method: MethodCallee<'tcx>) {
debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
self.tables
.borrow_mut()
.type_dependent_defs_mut()
.insert(hir_id, Def::Method(method.def_id));
self.write_substs(hir_id, method.substs);
// When the method is confirmed, the `method.substs` includes
// parameters from not just the method, but also the impl of
// the method -- in particular, the `Self` type will be fully
// resolved. However, those are not something that the "user
// specified" -- i.e., those types come from the inferred type
// of the receiver, not something the user wrote. So when we
// create the user-substs, we want to replace those earlier
// types with just the types that the user actually wrote --
// that is, those that appear on the *method itself*.
//
// As an example, if the user wrote something like
// `foo.bar::<u32>(...)` -- the `Self` type here will be the
// type of `foo` (possibly adjusted), but we don't want to
// include that. We want just the `[_, u32]` part.
if !method.substs.is_noop() {
let method_generics = self.tcx.generics_of(method.def_id);
if !method_generics.params.is_empty() {
let user_substs = self.infcx.probe(|_| {
let just_method_substs = Substs::for_item(self.tcx, method.def_id, |param, _| {
let i = param.index as usize;
if i < method_generics.parent_count {
self.infcx.var_for_def(DUMMY_SP, param)
} else {
method.substs[i]
}
});
self.infcx.canonicalize_response(&just_method_substs)
});
debug!("write_method_call: user_substs = {:?}", user_substs);
self.write_user_substs(hir_id, user_substs);
}
}
}
pub fn write_substs(&self, node_id: hir::HirId, substs: &'tcx Substs<'tcx>) {
......@@ -2076,6 +2113,21 @@ pub fn write_substs(&self, node_id: hir::HirId, substs: &'tcx Substs<'tcx>) {
}
}
pub fn write_user_substs(&self, node_id: hir::HirId, substs: CanonicalSubsts<'tcx>) {
debug!(
"write_user_substs({:?}, {:?}) in fcx {}",
node_id,
substs,
self.tag(),
);
if !substs.is_identity() {
self.tables.borrow_mut().user_substs_mut().insert(node_id, substs);
} else {
debug!("write_user_substs: skipping identity substs");
}
}
pub fn apply_adjustments(&self, expr: &hir::Expr, adj: Vec<Adjustment<'tcx>>) {
debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
......@@ -5083,7 +5135,15 @@ pub fn instantiate_value_path(&self,
debug!("instantiate_value_path: type of {:?} is {:?}",
node_id,
ty_substituted);
self.write_substs(self.tcx.hir.node_to_hir_id(node_id), substs);
let hir_id = self.tcx.hir.node_to_hir_id(node_id);
self.write_substs(hir_id, substs);
if !substs.is_noop() {
let user_substs = self.infcx.canonicalize_response(&substs);
debug!("instantiate_value_path: user_substs = {:?}", user_substs);
self.write_user_substs(hir_id, user_substs);
}
ty_substituted
}
......
......@@ -35,7 +35,11 @@ pub fn resolve_type_vars_in_body(&self, body: &'gcx hir::Body) -> &'gcx ty::Type
let item_id = self.tcx.hir.body_owner(body.id());
let item_def_id = self.tcx.hir.local_def_id(item_id);
let mut wbcx = WritebackCx::new(self, body);
// This attribute causes us to dump some writeback information
// in the form of errors, which is used for unit tests.
let rustc_dump_user_substs = self.tcx.has_attr(item_def_id, "rustc_dump_user_substs");
let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs);
for arg in &body.arguments {
wbcx.visit_node_id(arg.pat.span, arg.hir_id);
}
......@@ -84,12 +88,15 @@ struct WritebackCx<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> {
tables: ty::TypeckTables<'gcx>,
body: &'gcx hir::Body,
rustc_dump_user_substs: bool,
}
impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
fn new(
fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>,
body: &'gcx hir::Body,
rustc_dump_user_substs: bool,
) -> WritebackCx<'cx, 'gcx, 'tcx> {
let owner = fcx.tcx.hir.definitions().node_to_hir_id(body.id().node_id);
......@@ -97,6 +104,7 @@ fn new(
fcx,
tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))),
body,
rustc_dump_user_substs,
}
}
......@@ -558,6 +566,22 @@ fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
assert!(!substs.needs_infer() && !substs.has_skol());
self.tables.node_substs_mut().insert(hir_id, substs);
}
// Copy over any user-substs
if let Some(user_substs) = self.fcx.tables.borrow().user_substs(hir_id) {
let user_substs = self.tcx().lift_to_global(&user_substs).unwrap();
self.tables.user_substs_mut().insert(hir_id, user_substs);
// Unit-testing mechanism:
if self.rustc_dump_user_substs {
let node_id = self.tcx().hir.hir_to_node_id(hir_id);
let span = self.tcx().hir.span(node_id);
self.tcx().sess.span_err(
span,
&format!("user substs: {:?}", user_substs),
);
}
}
}
fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) {
......
......@@ -865,6 +865,12 @@ pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
is just used for rustc unit tests \
and will never be stable",
cfg_fn!(rustc_attrs))),
("rustc_dump_user_substs", Whitelisted, Gated(Stability::Unstable,
"rustc_attrs",
"the `#[rustc_error]` attribute \
is just used for rustc unit tests \
and will never be stable",
cfg_fn!(rustc_attrs))),
("rustc_if_this_changed", Whitelisted, Gated(Stability::Unstable,
"rustc_attrs",
"the `#[rustc_if_this_changed]` attribute \
......
// Copyright 2017 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.
// Unit test for the "user substitutions" that are annotated on each
// node.
// compile-flags:-Zverbose
#![feature(nll)]
#![feature(rustc_attrs)]
trait Bazoom<T> {
fn method<U>(&self, arg: T, arg2: U) { }
}
impl<T, U> Bazoom<U> for T {
}
fn foo<'a, T>(_: T) { }
#[rustc_dump_user_substs]
fn main() {
// Here: nothing is given, so we don't have any annotation.
let x = foo;
x(22);
// Here: `u32` is given.
let x = foo::<u32>; //~ ERROR [u32]
x(22);
// Here: we only want the `T` to be given, the rest should be variables.
let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [?0, u32, ?1]
x(&22, 44, 66);
// Here: all are given
let x = <u8 as Bazoom<u16>>::method::<u32>; //~ ERROR [u8, u16, u32]
x(&22, 44, 66);
// Here: we want in particular that *only* the method `U`
// annotation is given, the rest are variables.
let y = 22_u32;
y.method::<u32>(44, 66); //~ ERROR [?0, ?1, u32]
// Here: nothing is given, so we don't have any annotation.
let y = 22_u32;
y.method(44, 66);
}
error: user substs: Canonical { variables: [], value: [u32] }
--> $DIR/dump-fn-method.rs:35:13
|
LL | let x = foo::<u32>; //~ ERROR [u32]
| ^^^^^^^^^^
error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: [?0, u32, ?1] }
--> $DIR/dump-fn-method.rs:39:13
|
LL | let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [?0, u32, ?1]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: user substs: Canonical { variables: [], value: [u8, u16, u32] }
--> $DIR/dump-fn-method.rs:43:13
|
LL | let x = <u8 as Bazoom<u16>>::method::<u32>; //~ ERROR [u8, u16, u32]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: [?0, ?1, u32] }
--> $DIR/dump-fn-method.rs:49:5
|
LL | y.method::<u32>(44, 66); //~ ERROR [?0, ?1, u32]
| ^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册