add a user_substs table and store the annotations in there
This commit is contained in:
parent
1884fe35b6
commit
d7d4d7c8d5
6 changed files with 202 additions and 6 deletions
|
|
@ -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 @@ impl<'tcx> Decodable for Kind<'tcx> {
|
|||
/// 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 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx Substs<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
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::anon_types::AnonTypeDecl;
|
|||
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 std::ops::{self, Deref};
|
|||
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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, '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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
fcx,
|
||||
tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))),
|
||||
body,
|
||||
rustc_dump_user_substs,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -558,6 +566,22 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
|||
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 const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
|
|||
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 \
|
||||
|
|
|
|||
54
src/test/ui/nll/user-annotations/dump-fn-method.rs
Normal file
54
src/test/ui/nll/user-annotations/dump-fn-method.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// 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);
|
||||
}
|
||||
26
src/test/ui/nll/user-annotations/dump-fn-method.stderr
Normal file
26
src/test/ui/nll/user-annotations/dump-fn-method.stderr
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue