about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-08-08 18:10:19 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-08-24 13:27:38 -0400
commitd7d4d7c8d5620b7b120304cc5eecf25b499e394a (patch)
treea1ba9d1df697e80e03259426083d26072f1d67af
parent1884fe35b6b30e02a65bf13a542f0798f220724f (diff)
downloadrust-d7d4d7c8d5620b7b120304cc5eecf25b499e394a.tar.gz
rust-d7d4d7c8d5620b7b120304cc5eecf25b499e394a.zip
add a `user_substs` table and store the annotations in there
-rw-r--r--src/librustc/ty/subst.rs32
-rw-r--r--src/librustc_typeck/check/mod.rs64
-rw-r--r--src/librustc_typeck/check/writeback.rs26
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/test/ui/nll/user-annotations/dump-fn-method.rs54
-rw-r--r--src/test/ui/nll/user-annotations/dump-fn-method.stderr26
6 files changed, 202 insertions, 6 deletions
diff --git a/src/librustc/ty/subst.rs b/src/librustc/ty/subst.rs
index 26e8b72946b..f786547e6e3 100644
--- a/src/librustc/ty/subst.rs
+++ b/src/librustc/ty/subst.rs
@@ -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> {}
 
 ///////////////////////////////////////////////////////////////////////////
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index db4dda0da5b..16894478df0 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -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
     }
 
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 319de51713b..0d8401c1c86 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -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) {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 4ed96d26906..28d27836034 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -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 \
diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.rs b/src/test/ui/nll/user-annotations/dump-fn-method.rs
new file mode 100644
index 00000000000..fa14009da5a
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/dump-fn-method.rs
@@ -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);
+}
diff --git a/src/test/ui/nll/user-annotations/dump-fn-method.stderr b/src/test/ui/nll/user-annotations/dump-fn-method.stderr
new file mode 100644
index 00000000000..8f3a0498e24
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/dump-fn-method.stderr
@@ -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
+