Skip to content

Commit d7d4d7c

Browse files
committed
add a user_substs table and store the annotations in there
1 parent 1884fe3 commit d7d4d7c

File tree

6 files changed

+202
-6
lines changed

6 files changed

+202
-6
lines changed

src/librustc/ty/subst.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212

1313
use hir::def_id::DefId;
1414
use infer::canonical::Canonical;
15-
use ty::{self, Lift, List, Ty, TyCtxt};
15+
use ty::{self, CanonicalVar, Lift, List, Ty, TyCtxt};
1616
use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
1717

1818
use serialize::{self, Encodable, Encoder, Decodable, Decoder};
1919
use syntax_pos::{Span, DUMMY_SP};
2020
use rustc_data_structures::accumulate_vec::AccumulateVec;
2121
use rustc_data_structures::array_vec::ArrayVec;
22+
use rustc_data_structures::indexed_vec::Idx;
2223

2324
use core::intrinsics;
2425
use std::cmp::Ordering;
@@ -180,8 +181,6 @@ impl<'tcx> Decodable for Kind<'tcx> {
180181
/// A substitution mapping generic parameters to new values.
181182
pub type Substs<'tcx> = List<Kind<'tcx>>;
182183

183-
pub type CanonicalSubsts<'gcx> = Canonical<'gcx, &'gcx Substs<'gcx>>;
184-
185184
impl<'a, 'gcx, 'tcx> Substs<'tcx> {
186185
/// Creates a Substs that maps each generic parameter to itself.
187186
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> {
342341
}
343342
}
344343

344+
pub type CanonicalSubsts<'gcx> = Canonical<'gcx, &'gcx Substs<'gcx>>;
345+
346+
impl<'gcx> CanonicalSubsts<'gcx> {
347+
/// True if this represents a substitution like
348+
///
349+
/// ```ignore
350+
/// [?0, ?1, ?2]
351+
/// ```
352+
///
353+
/// i.e., each thing is mapped to a canonical variable with the same index.
354+
pub fn is_identity(&self) -> bool {
355+
self.value.iter().zip(CanonicalVar::new(0)..).all(|(kind, cvar)| {
356+
match kind.unpack() {
357+
UnpackedKind::Type(ty) => match ty.sty {
358+
ty::Infer(ty::CanonicalTy(cvar1)) => cvar == cvar1,
359+
_ => false,
360+
},
361+
362+
UnpackedKind::Lifetime(r) => match r {
363+
ty::ReCanonical(cvar1) => cvar == *cvar1,
364+
_ => false,
365+
},
366+
}
367+
})
368+
}
369+
}
370+
345371
impl<'tcx> serialize::UseSpecializedDecodable for &'tcx Substs<'tcx> {}
346372

347373
///////////////////////////////////////////////////////////////////////////

src/librustc_typeck/check/mod.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ use rustc::infer::anon_types::AnonTypeDecl;
9595
use rustc::infer::type_variable::{TypeVariableOrigin};
9696
use rustc::middle::region;
9797
use rustc::mir::interpret::{GlobalId};
98-
use rustc::ty::subst::{UnpackedKind, Subst, Substs};
98+
use rustc::ty::subst::{CanonicalSubsts, UnpackedKind, Subst, Substs};
9999
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
100100
use rustc::ty::{self, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate, RegionKind};
101101
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
@@ -122,6 +122,7 @@ use std::ops::{self, Deref};
122122
use rustc_target::spec::abi::Abi;
123123
use syntax::ast;
124124
use syntax::attr;
125+
use syntax::source_map::DUMMY_SP;
125126
use syntax::source_map::original_sp;
126127
use syntax::feature_gate::{GateIssue, emit_feature_err};
127128
use syntax::ptr::P;
@@ -2058,11 +2059,47 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
20582059
pub fn write_method_call(&self,
20592060
hir_id: hir::HirId,
20602061
method: MethodCallee<'tcx>) {
2062+
debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method);
20612063
self.tables
20622064
.borrow_mut()
20632065
.type_dependent_defs_mut()
20642066
.insert(hir_id, Def::Method(method.def_id));
2067+
20652068
self.write_substs(hir_id, method.substs);
2069+
2070+
// When the method is confirmed, the `method.substs` includes
2071+
// parameters from not just the method, but also the impl of
2072+
// the method -- in particular, the `Self` type will be fully
2073+
// resolved. However, those are not something that the "user
2074+
// specified" -- i.e., those types come from the inferred type
2075+
// of the receiver, not something the user wrote. So when we
2076+
// create the user-substs, we want to replace those earlier
2077+
// types with just the types that the user actually wrote --
2078+
// that is, those that appear on the *method itself*.
2079+
//
2080+
// As an example, if the user wrote something like
2081+
// `foo.bar::<u32>(...)` -- the `Self` type here will be the
2082+
// type of `foo` (possibly adjusted), but we don't want to
2083+
// include that. We want just the `[_, u32]` part.
2084+
if !method.substs.is_noop() {
2085+
let method_generics = self.tcx.generics_of(method.def_id);
2086+
if !method_generics.params.is_empty() {
2087+
let user_substs = self.infcx.probe(|_| {
2088+
let just_method_substs = Substs::for_item(self.tcx, method.def_id, |param, _| {
2089+
let i = param.index as usize;
2090+
if i < method_generics.parent_count {
2091+
self.infcx.var_for_def(DUMMY_SP, param)
2092+
} else {
2093+
method.substs[i]
2094+
}
2095+
});
2096+
self.infcx.canonicalize_response(&just_method_substs)
2097+
});
2098+
2099+
debug!("write_method_call: user_substs = {:?}", user_substs);
2100+
self.write_user_substs(hir_id, user_substs);
2101+
}
2102+
}
20662103
}
20672104

20682105
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> {
20762113
}
20772114
}
20782115

2116+
pub fn write_user_substs(&self, node_id: hir::HirId, substs: CanonicalSubsts<'tcx>) {
2117+
debug!(
2118+
"write_user_substs({:?}, {:?}) in fcx {}",
2119+
node_id,
2120+
substs,
2121+
self.tag(),
2122+
);
2123+
2124+
if !substs.is_identity() {
2125+
self.tables.borrow_mut().user_substs_mut().insert(node_id, substs);
2126+
} else {
2127+
debug!("write_user_substs: skipping identity substs");
2128+
}
2129+
}
2130+
20792131
pub fn apply_adjustments(&self, expr: &hir::Expr, adj: Vec<Adjustment<'tcx>>) {
20802132
debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj);
20812133

@@ -5083,7 +5135,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
50835135
debug!("instantiate_value_path: type of {:?} is {:?}",
50845136
node_id,
50855137
ty_substituted);
5086-
self.write_substs(self.tcx.hir.node_to_hir_id(node_id), substs);
5138+
let hir_id = self.tcx.hir.node_to_hir_id(node_id);
5139+
self.write_substs(hir_id, substs);
5140+
5141+
if !substs.is_noop() {
5142+
let user_substs = self.infcx.canonicalize_response(&substs);
5143+
debug!("instantiate_value_path: user_substs = {:?}", user_substs);
5144+
self.write_user_substs(hir_id, user_substs);
5145+
}
5146+
50875147
ty_substituted
50885148
}
50895149

src/librustc_typeck/check/writeback.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
3535
let item_id = self.tcx.hir.body_owner(body.id());
3636
let item_def_id = self.tcx.hir.local_def_id(item_id);
3737

38-
let mut wbcx = WritebackCx::new(self, body);
38+
// This attribute causes us to dump some writeback information
39+
// in the form of errors, which is used for unit tests.
40+
let rustc_dump_user_substs = self.tcx.has_attr(item_def_id, "rustc_dump_user_substs");
41+
42+
let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs);
3943
for arg in &body.arguments {
4044
wbcx.visit_node_id(arg.pat.span, arg.hir_id);
4145
}
@@ -84,19 +88,23 @@ struct WritebackCx<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> {
8488
tables: ty::TypeckTables<'gcx>,
8589

8690
body: &'gcx hir::Body,
91+
92+
rustc_dump_user_substs: bool,
8793
}
8894

8995
impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
9096
fn new(
9197
fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>,
9298
body: &'gcx hir::Body,
99+
rustc_dump_user_substs: bool,
93100
) -> WritebackCx<'cx, 'gcx, 'tcx> {
94101
let owner = fcx.tcx.hir.definitions().node_to_hir_id(body.id().node_id);
95102

96103
WritebackCx {
97104
fcx,
98105
tables: ty::TypeckTables::empty(Some(DefId::local(owner.owner))),
99106
body,
107+
rustc_dump_user_substs,
100108
}
101109
}
102110

@@ -558,6 +566,22 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
558566
assert!(!substs.needs_infer() && !substs.has_skol());
559567
self.tables.node_substs_mut().insert(hir_id, substs);
560568
}
569+
570+
// Copy over any user-substs
571+
if let Some(user_substs) = self.fcx.tables.borrow().user_substs(hir_id) {
572+
let user_substs = self.tcx().lift_to_global(&user_substs).unwrap();
573+
self.tables.user_substs_mut().insert(hir_id, user_substs);
574+
575+
// Unit-testing mechanism:
576+
if self.rustc_dump_user_substs {
577+
let node_id = self.tcx().hir.hir_to_node_id(hir_id);
578+
let span = self.tcx().hir.span(node_id);
579+
self.tcx().sess.span_err(
580+
span,
581+
&format!("user substs: {:?}", user_substs),
582+
);
583+
}
584+
}
561585
}
562586

563587
fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) {

src/libsyntax/feature_gate.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,12 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
865865
is just used for rustc unit tests \
866866
and will never be stable",
867867
cfg_fn!(rustc_attrs))),
868+
("rustc_dump_user_substs", Whitelisted, Gated(Stability::Unstable,
869+
"rustc_attrs",
870+
"the `#[rustc_error]` attribute \
871+
is just used for rustc unit tests \
872+
and will never be stable",
873+
cfg_fn!(rustc_attrs))),
868874
("rustc_if_this_changed", Whitelisted, Gated(Stability::Unstable,
869875
"rustc_attrs",
870876
"the `#[rustc_if_this_changed]` attribute \
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Unit test for the "user substitutions" that are annotated on each
12+
// node.
13+
14+
// compile-flags:-Zverbose
15+
16+
#![feature(nll)]
17+
#![feature(rustc_attrs)]
18+
19+
trait Bazoom<T> {
20+
fn method<U>(&self, arg: T, arg2: U) { }
21+
}
22+
23+
impl<T, U> Bazoom<U> for T {
24+
}
25+
26+
fn foo<'a, T>(_: T) { }
27+
28+
#[rustc_dump_user_substs]
29+
fn main() {
30+
// Here: nothing is given, so we don't have any annotation.
31+
let x = foo;
32+
x(22);
33+
34+
// Here: `u32` is given.
35+
let x = foo::<u32>; //~ ERROR [u32]
36+
x(22);
37+
38+
// Here: we only want the `T` to be given, the rest should be variables.
39+
let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [?0, u32, ?1]
40+
x(&22, 44, 66);
41+
42+
// Here: all are given
43+
let x = <u8 as Bazoom<u16>>::method::<u32>; //~ ERROR [u8, u16, u32]
44+
x(&22, 44, 66);
45+
46+
// Here: we want in particular that *only* the method `U`
47+
// annotation is given, the rest are variables.
48+
let y = 22_u32;
49+
y.method::<u32>(44, 66); //~ ERROR [?0, ?1, u32]
50+
51+
// Here: nothing is given, so we don't have any annotation.
52+
let y = 22_u32;
53+
y.method(44, 66);
54+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: user substs: Canonical { variables: [], value: [u32] }
2+
--> $DIR/dump-fn-method.rs:35:13
3+
|
4+
LL | let x = foo::<u32>; //~ ERROR [u32]
5+
| ^^^^^^^^^^
6+
7+
error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: [?0, u32, ?1] }
8+
--> $DIR/dump-fn-method.rs:39:13
9+
|
10+
LL | let x = <_ as Bazoom<u32>>::method::<_>; //~ ERROR [?0, u32, ?1]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: user substs: Canonical { variables: [], value: [u8, u16, u32] }
14+
--> $DIR/dump-fn-method.rs:43:13
15+
|
16+
LL | let x = <u8 as Bazoom<u16>>::method::<u32>; //~ ERROR [u8, u16, u32]
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: user substs: Canonical { variables: [CanonicalVarInfo { kind: Ty(General) }, CanonicalVarInfo { kind: Ty(General) }], value: [?0, ?1, u32] }
20+
--> $DIR/dump-fn-method.rs:49:5
21+
|
22+
LL | y.method::<u32>(44, 66); //~ ERROR [?0, ?1, u32]
23+
| ^^^^^^^^^^^^^^^^^^^^^^^
24+
25+
error: aborting due to 4 previous errors
26+

0 commit comments

Comments
 (0)