1 // Copyright 2012-2014 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.
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.
13 typeck.rs, an introduction
15 The type checker is responsible for:
17 1. Determining the type of each expression
18 2. Resolving methods and traits
19 3. Guaranteeing that most type rules are met ("most?", you say, "why most?"
20 Well, dear reader, read on)
22 The main entry point is `check_crate()`. Type checking operates in
25 1. The collect phase first passes over all items and determines their
26 type, without examining their "innards".
28 2. Variance inference then runs to compute the variance of each parameter
30 3. Coherence checks for overlapping or orphaned impls
32 4. Finally, the check phase then checks function bodies and so forth.
33 Within the check phase, we check each function body one at a time
34 (bodies of function expressions are checked as part of the
35 containing function). Inference is used to supply types wherever
36 they are unknown. The actual checking of a function itself has
37 several phases (check, regionck, writeback), as discussed in the
38 documentation for the `check` module.
40 The type checker is defined into various submodules which are documented
43 - astconv: converts the AST representation of types
44 into the `ty` representation
46 - collect: computes the types of each top-level item and enters them into
47 the `cx.tcache` table for later use
49 - coherence: enforces coherence rules, builds some tables
51 - variance: variance inference
53 - check: walks over function bodies and type checks them, inferring types for
54 local variables, type parameters, etc as necessary.
56 - infer: finds the types to use for each type variable such that
57 all subtyping and assignment constraints are met. In essence, the check
58 module specifies the constraints, and the infer module solves them.
62 #![allow(non_camel_case_types)]
69 use middle::subst::VecPerParamSpace;
71 use util::common::time;
72 use util::ppaux::Repr;
74 use util::nodemap::{DefIdMap, FnvHashMap};
76 use std::cell::RefCell;
77 use syntax::codemap::Span;
78 use syntax::print::pprust::*;
79 use syntax::{ast, ast_map, abi};
89 #[deriving(Clone, Encodable, Decodable, PartialEq, PartialOrd)]
90 pub struct param_index {
91 pub space: subst::ParamSpace,
95 #[deriving(Clone, Encodable, Decodable)]
96 pub enum MethodOrigin {
97 // fully statically resolved method
98 MethodStatic(ast::DefId),
100 // method invoked on a type parameter with a bounded trait
101 MethodParam(MethodParam),
103 // method invoked on a trait instance
104 MethodObject(MethodObject),
108 // details for a method invoked with a receiver whose type is a type parameter
109 // with a bounded trait.
110 #[deriving(Clone, Encodable, Decodable)]
111 pub struct MethodParam {
112 // the trait containing the method to be invoked
113 pub trait_id: ast::DefId,
115 // index of the method to be invoked amongst the trait's methods
116 pub method_num: uint,
118 // index of the type parameter (from those that are in scope) that is
119 // the type of the receiver
120 pub param_num: param_index,
122 // index of the bound for this type parameter which specifies the trait
126 // details for a method invoked with a receiver whose type is an object
127 #[deriving(Clone, Encodable, Decodable)]
128 pub struct MethodObject {
129 // the (super)trait containing the method to be invoked
130 pub trait_id: ast::DefId,
132 // the actual base trait id of the object
133 pub object_trait_id: ast::DefId,
135 // index of the method to be invoked amongst the trait's methods
136 pub method_num: uint,
138 // index into the actual runtime vtable.
139 // the vtable is formed by concatenating together the method lists of
140 // the base object trait and all supertraits; this is the index into
142 pub real_index: uint,
146 pub struct MethodCallee {
147 pub origin: MethodOrigin,
149 pub substs: subst::Substs
153 * With method calls, we store some extra information in
154 * side tables (i.e method_map, vtable_map). We use
155 * MethodCall as a key to index into these tables instead of
156 * just directly using the expression's NodeId. The reason
157 * for this being that we may apply adjustments (coercions)
158 * with the resulting expression also needing to use the
159 * side tables. The problem with this is that we don't
160 * assign a separate NodeId to this new expression
161 * and so it would clash with the base expression if both
162 * needed to add to the side tables. Thus to disambiguate
163 * we also keep track of whether there's an adjustment in
166 #[deriving(Clone, PartialEq, Eq, Hash, Show)]
167 pub struct MethodCall {
168 pub expr_id: ast::NodeId,
169 pub adjustment: ExprAdjustment
172 #[deriving(Clone, PartialEq, Eq, Hash, Show, Encodable, Decodable)]
173 pub enum ExprAdjustment {
179 pub struct TypeAndSubsts {
180 pub substs: subst::Substs,
185 pub fn expr(id: ast::NodeId) -> MethodCall {
188 adjustment: NoAdjustment
192 pub fn autoobject(id: ast::NodeId) -> MethodCall {
195 adjustment: AutoObject
199 pub fn autoderef(expr_id: ast::NodeId, autoderef: uint) -> MethodCall {
202 adjustment: AutoDeref(1 + autoderef)
207 // maps from an expression id that corresponds to a method call to the details
208 // of the method to be invoked
209 pub type MethodMap = RefCell<FnvHashMap<MethodCall, MethodCallee>>;
211 pub type vtable_param_res = Vec<vtable_origin>;
213 // Resolutions for bounds of all parameters, left to right, for a given path.
214 pub type vtable_res = VecPerParamSpace<vtable_param_res>;
217 pub enum vtable_origin {
219 Statically known vtable. def_id gives the class or impl item
220 from whence comes the vtable, and tys are the type substs.
221 vtable_res is the vtable itself
223 vtable_static(ast::DefId, subst::Substs, vtable_res),
226 Dynamic vtable, comes from a parameter that has a bound on it:
227 fn foo<T:quux,baz,bar>(a: T) -- a's vtable would have a
230 The first argument is the param index (identifying T in the example),
231 and the second is the bound number (identifying baz)
233 vtable_param(param_index, uint),
236 Asked to determine the vtable for ty_err. This is the value used
237 for the vtables of `Self` in a virtual call like `foo.bar()`
238 where `foo` is of object type. The same value is also used when
244 impl Repr for vtable_origin {
245 fn repr(&self, tcx: &ty::ctxt) -> String {
247 vtable_static(def_id, ref tys, ref vtable_res) => {
248 format!("vtable_static({:?}:{}, {}, {})",
250 ty::item_path_str(tcx, def_id),
252 vtable_res.repr(tcx))
255 vtable_param(x, y) => {
256 format!("vtable_param({:?}, {:?})", x, y)
260 format!("vtable_error")
266 pub type vtable_map = RefCell<FnvHashMap<MethodCall, vtable_res>>;
269 pub type impl_vtable_map = RefCell<DefIdMap<vtable_res>>;
271 pub struct CrateCtxt<'a> {
272 // A mapping from method call sites to traits that have that method.
273 trait_map: resolve::TraitMap,
277 // Functions that write types into the node type table
278 pub fn write_ty_to_tcx(tcx: &ty::ctxt, node_id: ast::NodeId, ty: ty::t) {
279 debug!("write_ty_to_tcx({}, {})", node_id, ppaux::ty_to_str(tcx, ty));
280 assert!(!ty::type_needs_infer(ty));
281 tcx.node_types.borrow_mut().insert(node_id as uint, ty);
283 pub fn write_substs_to_tcx(tcx: &ty::ctxt,
284 node_id: ast::NodeId,
285 item_substs: ty::ItemSubsts) {
286 if !item_substs.is_noop() {
287 debug!("write_substs_to_tcx({}, {})",
289 item_substs.repr(tcx));
291 assert!(item_substs.substs.types.all(|t| !ty::type_needs_infer(*t)));
293 tcx.item_substs.borrow_mut().insert(node_id, item_substs);
296 pub fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> def::Def {
297 match tcx.def_map.borrow().find(&id) {
300 tcx.sess.span_fatal(sp, "internal error looking up a definition")
305 pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId)
307 lookup_def_tcx(ccx.tcx, sp, id)
310 pub fn no_params(t: ty::t) -> ty::Polytype {
312 generics: ty::Generics {types: VecPerParamSpace::empty(),
313 regions: VecPerParamSpace::empty()},
318 pub fn require_same_types(tcx: &ty::ctxt,
319 maybe_infcx: Option<&infer::InferCtxt>,
320 t1_is_expected: bool,
326 let result = match maybe_infcx {
328 let infcx = infer::new_infer_ctxt(tcx);
329 infer::mk_eqty(&infcx, t1_is_expected, infer::Misc(span), t1, t2)
332 infer::mk_eqty(infcx, t1_is_expected, infer::Misc(span), t1, t2)
339 tcx.sess.span_err(span,
342 ty::type_err_to_str(tcx,
344 ty::note_and_explain_type_err(tcx, terr);
350 fn check_main_fn_ty(ccx: &CrateCtxt,
351 main_id: ast::NodeId,
354 let main_t = ty::node_id_to_type(tcx, main_id);
355 match ty::get(main_t).sty {
356 ty::ty_bare_fn(..) => {
357 match tcx.map.find(main_id) {
358 Some(ast_map::NodeItem(it)) => {
360 ast::ItemFn(_, _, _, ref ps, _)
361 if ps.is_parameterized() => {
364 "main function is not allowed to have type parameters");
372 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
373 fn_style: ast::NormalFn,
378 output: ty::mk_nil(),
383 require_same_types(tcx, None, false, main_span, main_t, se_ty,
385 format!("main function expects type: `{}`",
386 ppaux::ty_to_str(ccx.tcx, se_ty))
390 tcx.sess.span_bug(main_span,
391 format!("main has a non-function type: found \
393 ppaux::ty_to_str(tcx,
394 main_t)).as_slice());
399 fn check_start_fn_ty(ccx: &CrateCtxt,
400 start_id: ast::NodeId,
403 let start_t = ty::node_id_to_type(tcx, start_id);
404 match ty::get(start_t).sty {
405 ty::ty_bare_fn(_) => {
406 match tcx.map.find(start_id) {
407 Some(ast_map::NodeItem(it)) => {
409 ast::ItemFn(_,_,_,ref ps,_)
410 if ps.is_parameterized() => {
413 "start function is not allowed to have type parameters");
422 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
423 fn_style: ast::NormalFn,
429 ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
431 output: ty::mk_int(),
436 require_same_types(tcx, None, false, start_span, start_t, se_ty,
438 format!("start function expects type: `{}`",
439 ppaux::ty_to_str(ccx.tcx, se_ty))
444 tcx.sess.span_bug(start_span,
445 format!("start has a non-function type: found \
447 ppaux::ty_to_str(tcx,
448 start_t)).as_slice());
453 fn check_for_entry_fn(ccx: &CrateCtxt) {
455 match *tcx.sess.entry_fn.borrow() {
456 Some((id, sp)) => match tcx.sess.entry_type.get() {
457 Some(config::EntryMain) => check_main_fn_ty(ccx, id, sp),
458 Some(config::EntryStart) => check_start_fn_ty(ccx, id, sp),
459 Some(config::EntryNone) => {}
460 None => tcx.sess.bug("entry function without a type")
466 pub fn check_crate(tcx: &ty::ctxt,
467 trait_map: resolve::TraitMap,
468 krate: &ast::Crate) {
469 let time_passes = tcx.sess.time_passes();
470 let ccx = CrateCtxt {
471 trait_map: trait_map,
475 time(time_passes, "type collecting", (), |_|
476 collect::collect_item_types(&ccx, krate));
478 // this ensures that later parts of type checking can assume that items
479 // have valid types and not error
480 tcx.sess.abort_if_errors();
482 time(time_passes, "variance inference", (), |_|
483 variance::infer_variance(tcx, krate));
485 time(time_passes, "coherence checking", (), |_|
486 coherence::check_coherence(&ccx, krate));
488 time(time_passes, "type checking", (), |_|
489 check::check_item_types(&ccx, krate));
491 check_for_entry_fn(&ccx);
492 tcx.sess.abort_if_errors();