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 {
180 pub fn expr(id: ast::NodeId) -> MethodCall {
183 adjustment: NoAdjustment
187 pub fn autoobject(id: ast::NodeId) -> MethodCall {
190 adjustment: AutoObject
194 pub fn autoderef(expr_id: ast::NodeId, autoderef: uint) -> MethodCall {
197 adjustment: AutoDeref(1 + autoderef)
202 // maps from an expression id that corresponds to a method call to the details
203 // of the method to be invoked
204 pub type MethodMap = RefCell<FnvHashMap<MethodCall, MethodCallee>>;
206 pub type vtable_param_res = Vec<vtable_origin>;
208 // Resolutions for bounds of all parameters, left to right, for a given path.
209 pub type vtable_res = VecPerParamSpace<vtable_param_res>;
212 pub enum vtable_origin {
214 Statically known vtable. def_id gives the class or impl item
215 from whence comes the vtable, and tys are the type substs.
216 vtable_res is the vtable itself
218 vtable_static(ast::DefId, subst::Substs, vtable_res),
221 Dynamic vtable, comes from a parameter that has a bound on it:
222 fn foo<T:quux,baz,bar>(a: T) -- a's vtable would have a
225 The first argument is the param index (identifying T in the example),
226 and the second is the bound number (identifying baz)
228 vtable_param(param_index, uint),
231 Asked to determine the vtable for ty_err. This is the value used
232 for the vtables of `Self` in a virtual call like `foo.bar()`
233 where `foo` is of object type. The same value is also used when
239 impl Repr for vtable_origin {
240 fn repr(&self, tcx: &ty::ctxt) -> String {
242 vtable_static(def_id, ref tys, ref vtable_res) => {
243 format!("vtable_static({:?}:{}, {}, {})",
245 ty::item_path_str(tcx, def_id),
247 vtable_res.repr(tcx))
250 vtable_param(x, y) => {
251 format!("vtable_param({:?}, {:?})", x, y)
255 format!("vtable_error")
261 pub type vtable_map = RefCell<FnvHashMap<MethodCall, vtable_res>>;
264 pub type impl_vtable_map = RefCell<DefIdMap<vtable_res>>;
266 pub struct CrateCtxt<'a> {
267 // A mapping from method call sites to traits that have that method.
268 trait_map: resolve::TraitMap,
272 // Functions that write types into the node type table
273 pub fn write_ty_to_tcx(tcx: &ty::ctxt, node_id: ast::NodeId, ty: ty::t) {
274 debug!("write_ty_to_tcx({}, {})", node_id, ppaux::ty_to_str(tcx, ty));
275 assert!(!ty::type_needs_infer(ty));
276 tcx.node_types.borrow_mut().insert(node_id as uint, ty);
278 pub fn write_substs_to_tcx(tcx: &ty::ctxt,
279 node_id: ast::NodeId,
280 item_substs: ty::ItemSubsts) {
281 if !item_substs.is_noop() {
282 debug!("write_substs_to_tcx({}, {})",
284 item_substs.repr(tcx));
286 assert!(item_substs.substs.types.all(|t| !ty::type_needs_infer(*t)));
288 tcx.item_substs.borrow_mut().insert(node_id, item_substs);
291 pub fn lookup_def_tcx(tcx:&ty::ctxt, sp: Span, id: ast::NodeId) -> def::Def {
292 match tcx.def_map.borrow().find(&id) {
295 tcx.sess.span_fatal(sp, "internal error looking up a definition")
300 pub fn lookup_def_ccx(ccx: &CrateCtxt, sp: Span, id: ast::NodeId)
302 lookup_def_tcx(ccx.tcx, sp, id)
305 pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
306 ty::ty_param_bounds_and_ty {
307 generics: ty::Generics {types: VecPerParamSpace::empty(),
308 regions: VecPerParamSpace::empty()},
313 pub fn require_same_types(tcx: &ty::ctxt,
314 maybe_infcx: Option<&infer::InferCtxt>,
315 t1_is_expected: bool,
321 let result = match maybe_infcx {
323 let infcx = infer::new_infer_ctxt(tcx);
324 infer::mk_eqty(&infcx, t1_is_expected, infer::Misc(span), t1, t2)
327 infer::mk_eqty(infcx, t1_is_expected, infer::Misc(span), t1, t2)
334 tcx.sess.span_err(span,
337 ty::type_err_to_str(tcx,
339 ty::note_and_explain_type_err(tcx, terr);
345 fn check_main_fn_ty(ccx: &CrateCtxt,
346 main_id: ast::NodeId,
349 let main_t = ty::node_id_to_type(tcx, main_id);
350 match ty::get(main_t).sty {
351 ty::ty_bare_fn(..) => {
352 match tcx.map.find(main_id) {
353 Some(ast_map::NodeItem(it)) => {
355 ast::ItemFn(_, _, _, ref ps, _)
356 if ps.is_parameterized() => {
359 "main function is not allowed to have type parameters");
367 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
368 fn_style: ast::NormalFn,
373 output: ty::mk_nil(),
378 require_same_types(tcx, None, false, main_span, main_t, se_ty,
380 format!("main function expects type: `{}`",
381 ppaux::ty_to_str(ccx.tcx, se_ty))
385 tcx.sess.span_bug(main_span,
386 format!("main has a non-function type: found \
388 ppaux::ty_to_str(tcx,
389 main_t)).as_slice());
394 fn check_start_fn_ty(ccx: &CrateCtxt,
395 start_id: ast::NodeId,
398 let start_t = ty::node_id_to_type(tcx, start_id);
399 match ty::get(start_t).sty {
400 ty::ty_bare_fn(_) => {
401 match tcx.map.find(start_id) {
402 Some(ast_map::NodeItem(it)) => {
404 ast::ItemFn(_,_,_,ref ps,_)
405 if ps.is_parameterized() => {
408 "start function is not allowed to have type parameters");
417 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
418 fn_style: ast::NormalFn,
424 ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
426 output: ty::mk_int(),
431 require_same_types(tcx, None, false, start_span, start_t, se_ty,
433 format!("start function expects type: `{}`",
434 ppaux::ty_to_str(ccx.tcx, se_ty))
439 tcx.sess.span_bug(start_span,
440 format!("start has a non-function type: found \
442 ppaux::ty_to_str(tcx,
443 start_t)).as_slice());
448 fn check_for_entry_fn(ccx: &CrateCtxt) {
450 match *tcx.sess.entry_fn.borrow() {
451 Some((id, sp)) => match tcx.sess.entry_type.get() {
452 Some(config::EntryMain) => check_main_fn_ty(ccx, id, sp),
453 Some(config::EntryStart) => check_start_fn_ty(ccx, id, sp),
454 Some(config::EntryNone) => {}
455 None => tcx.sess.bug("entry function without a type")
461 pub fn check_crate(tcx: &ty::ctxt,
462 trait_map: resolve::TraitMap,
463 krate: &ast::Crate) {
464 let time_passes = tcx.sess.time_passes();
465 let ccx = CrateCtxt {
466 trait_map: trait_map,
470 time(time_passes, "type collecting", (), |_|
471 collect::collect_item_types(&ccx, krate));
473 // this ensures that later parts of type checking can assume that items
474 // have valid types and not error
475 tcx.sess.abort_if_errors();
477 time(time_passes, "variance inference", (), |_|
478 variance::infer_variance(tcx, krate));
480 time(time_passes, "coherence checking", (), |_|
481 coherence::check_coherence(&ccx, krate));
483 time(time_passes, "type checking", (), |_|
484 check::check_item_types(&ccx, krate));
486 check_for_entry_fn(&ccx);
487 tcx.sess.abort_if_errors();