1 // Copyright 2012 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 two major
23 phases: collect and check. The collect phase passes over all items and
24 determines their type, without examining their "innards". The check phase
25 then checks function bodies and so forth.
27 Within the check phase, we check each function body one at a time (bodies of
28 function expressions are checked as part of the containing function).
29 Inference is used to supply types wherever they are unknown. The actual
30 checking of a function itself has several phases (check, regionck, writeback),
31 as discussed in the documentation for the `check` module.
33 The type checker is defined into various submodules which are documented
36 - astconv: converts the AST representation of types
37 into the `ty` representation
39 - collect: computes the types of each top-level item and enters them into
40 the `cx.tcache` table for later use
42 - check: walks over function bodies and type checks them, inferring types for
43 local variables, type parameters, etc as necessary.
45 - infer: finds the types to use for each type variable such that
46 all subtyping and assignment constraints are met. In essence, the check
47 module specifies the constraints, and the infer module solves them.
55 use util::common::time;
56 use util::ppaux::Repr;
59 use core::hashmap::HashMap;
62 use syntax::codemap::span;
63 use syntax::print::pprust::*;
64 use syntax::{ast, ast_map, abi};
67 #[path = "check/mod.rs"]
71 #[path = "infer/mod.rs"]
78 pub enum method_origin {
79 // supertrait method invoked on "self" inside a default method
80 // first field is supertrait ID;
81 // second field is method index (relative to the *supertrait*
83 method_super(ast::def_id, uint),
85 // fully statically resolved method
86 method_static(ast::def_id),
88 // method invoked on a type parameter with a bounded trait
89 method_param(method_param),
91 // method invoked on a trait instance
92 method_trait(ast::def_id, uint, ty::TraitStore),
94 // method invoked on "self" inside a default method
95 method_self(ast::def_id, uint)
99 // details for a method invoked with a receiver whose type is a type parameter
100 // with a bounded trait.
103 pub struct method_param {
104 // the trait containing the method to be invoked
105 trait_id: ast::def_id,
107 // index of the method to be invoked amongst the trait's methods
110 // index of the type parameter (from those that are in scope) that is
111 // the type of the receiver
114 // index of the bound for this type parameter which specifies the trait
118 pub struct method_map_entry {
119 // the type of the self parameter, which is not reflected in the fn type
123 // the mode of `self`
124 self_mode: ty::SelfMode,
126 // the type of explicit self on the method
127 explicit_self: ast::self_ty_,
129 // method details being invoked
130 origin: method_origin,
133 // maps from an expression id that corresponds to a method call to the details
134 // of the method to be invoked
135 pub type method_map = @mut HashMap<ast::node_id, method_map_entry>;
137 // Resolutions for bounds of all parameters, left to right, for a given path.
138 pub type vtable_res = @~[vtable_origin];
140 pub enum vtable_origin {
142 Statically known vtable. def_id gives the class or impl item
143 from whence comes the vtable, and tys are the type substs.
144 vtable_res is the vtable itself
146 vtable_static(ast::def_id, ~[ty::t], vtable_res),
149 Dynamic vtable, comes from a parameter that has a bound on it:
150 fn foo<T:quux,baz,bar>(a: T) -- a's vtable would have a
153 The first uint is the param number (identifying T in the example),
154 and the second is the bound number (identifying baz)
156 vtable_param(uint, uint)
159 impl Repr for vtable_origin {
160 fn repr(&self, tcx: ty::ctxt) -> ~str {
162 vtable_static(def_id, ref tys, ref vtable_res) => {
163 fmt!("vtable_static(%?:%s, %s, %s)",
165 ty::item_path_str(tcx, def_id),
167 vtable_res.repr(tcx))
170 vtable_param(x, y) => {
171 fmt!("vtable_param(%?, %?)", x, y)
177 pub type vtable_map = @mut HashMap<ast::node_id, vtable_res>;
179 pub struct CrateCtxt {
180 // A mapping from method call sites to traits that have that method.
181 trait_map: resolve::TraitMap,
182 method_map: method_map,
183 vtable_map: vtable_map,
184 coherence_info: @coherence::CoherenceInfo,
188 // Functions that write types into the node type table
189 pub fn write_ty_to_tcx(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) {
190 debug!("write_ty_to_tcx(%d, %s)", node_id, ppaux::ty_to_str(tcx, ty));
191 assert!(!ty::type_needs_infer(ty));
192 tcx.node_types.insert(node_id as uint, ty);
194 pub fn write_substs_to_tcx(tcx: ty::ctxt,
195 node_id: ast::node_id,
197 if substs.len() > 0u {
198 debug!("write_substs_to_tcx(%d, %?)", node_id,
199 substs.map(|t| ppaux::ty_to_str(tcx, *t)));
200 assert!(substs.all(|t| !ty::type_needs_infer(*t)));
201 tcx.node_type_substs.insert(node_id, substs);
204 pub fn write_tpt_to_tcx(tcx: ty::ctxt,
205 node_id: ast::node_id,
206 tpt: &ty::ty_param_substs_and_ty) {
207 write_ty_to_tcx(tcx, node_id, tpt.ty);
208 if !tpt.substs.tps.is_empty() {
209 write_substs_to_tcx(tcx, node_id, copy tpt.substs.tps);
213 pub fn lookup_def_tcx(tcx: ty::ctxt, sp: span, id: ast::node_id) -> ast::def {
214 match tcx.def_map.find(&id) {
217 tcx.sess.span_fatal(sp, ~"internal error looking up a definition")
222 pub fn lookup_def_ccx(ccx: @mut CrateCtxt, sp: span, id: ast::node_id)
224 lookup_def_tcx(ccx.tcx, sp, id)
227 pub fn no_params(t: ty::t) -> ty::ty_param_bounds_and_ty {
228 ty::ty_param_bounds_and_ty {
229 generics: ty::Generics {type_param_defs: @~[],
235 pub fn require_same_types(
237 maybe_infcx: Option<@mut infer::InferCtxt>,
238 t1_is_expected: bool,
242 msg: &fn() -> ~str) -> bool {
248 l_infcx = infer::new_infer_ctxt(tcx);
256 match infer::mk_eqty(l_infcx, t1_is_expected, span, t1, t2) {
257 result::Ok(()) => true,
258 result::Err(ref terr) => {
259 l_tcx.sess.span_err(span, msg() + ~": " +
260 ty::type_err_to_str(l_tcx, terr));
261 ty::note_and_explain_type_err(l_tcx, terr);
267 // a list of mapping from in-scope-region-names ("isr") to the
268 // corresponding ty::Region
269 pub type isr_alist = @List<(ty::bound_region, ty::Region)>;
271 trait get_and_find_region {
272 fn get(&self, br: ty::bound_region) -> ty::Region;
273 fn find(&self, br: ty::bound_region) -> Option<ty::Region>;
276 impl get_and_find_region for isr_alist {
277 fn get(&self, br: ty::bound_region) -> ty::Region {
281 fn find(&self, br: ty::bound_region) -> Option<ty::Region> {
282 for list::each(*self) |isr| {
283 let (isr_br, isr_r) = *isr;
284 if isr_br == br { return Some(isr_r); }
290 fn check_main_fn_ty(ccx: @mut CrateCtxt,
291 main_id: ast::node_id,
294 let main_t = ty::node_id_to_type(tcx, main_id);
295 match ty::get(main_t).sty {
296 ty::ty_bare_fn(ref fn_ty) => {
297 match tcx.items.find(&main_id) {
298 Some(&ast_map::node_item(it,_)) => {
300 ast::item_fn(_, _, _, ref ps, _)
301 if ps.is_parameterized() => {
304 ~"main function is not allowed \
305 to have type parameters");
313 let mut ok = ty::type_is_nil(fn_ty.sig.output);
314 let num_args = vec::len(fn_ty.sig.inputs);
315 ok &= num_args == 0u;
319 fmt!("Wrong type in main function: found `%s`, \
320 expected `fn() -> ()`",
321 ppaux::ty_to_str(tcx, main_t)));
325 tcx.sess.span_bug(main_span,
326 ~"main has a non-function type: found `" +
327 ppaux::ty_to_str(tcx, main_t) + ~"`");
332 fn check_start_fn_ty(ccx: @mut CrateCtxt,
333 start_id: ast::node_id,
336 let start_t = ty::node_id_to_type(tcx, start_id);
337 match ty::get(start_t).sty {
338 ty::ty_bare_fn(_) => {
339 match tcx.items.find(&start_id) {
340 Some(&ast_map::node_item(it,_)) => {
342 ast::item_fn(_,_,_,ref ps,_)
343 if ps.is_parameterized() => {
346 ~"start function is not allowed to have type \
356 fn arg(ty: ty::t) -> ty::arg {
362 let se_ty = ty::mk_bare_fn(tcx, ty::BareFnTy {
363 purity: ast::impure_fn,
364 abis: abi::AbiSet::Rust(),
366 bound_lifetime_names: opt_vec::Empty,
369 arg(ty::mk_imm_ptr(tcx,
370 ty::mk_imm_ptr(tcx, ty::mk_u8()))),
371 arg(ty::mk_imm_ptr(tcx, ty::mk_u8()))
377 require_same_types(tcx, None, false, start_span, start_t, se_ty,
378 || fmt!("start function expects type: `%s`", ppaux::ty_to_str(ccx.tcx, se_ty)));
382 tcx.sess.span_bug(start_span,
383 ~"start has a non-function type: found `" +
384 ppaux::ty_to_str(tcx, start_t) + ~"`");
389 fn check_for_entry_fn(ccx: @mut CrateCtxt) {
391 if !*tcx.sess.building_library {
392 match *tcx.sess.entry_fn {
393 Some((id, sp)) => match *tcx.sess.entry_type {
394 Some(session::EntryMain) => check_main_fn_ty(ccx, id, sp),
395 Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp),
396 None => tcx.sess.bug(~"entry function without a type")
398 None => tcx.sess.err(~"entry function not found")
403 pub fn check_crate(tcx: ty::ctxt,
404 trait_map: resolve::TraitMap,
406 -> (method_map, vtable_map) {
407 let time_passes = tcx.sess.time_passes();
408 let ccx = @mut CrateCtxt {
409 trait_map: trait_map,
410 method_map: @mut HashMap::new(),
411 vtable_map: @mut HashMap::new(),
412 coherence_info: @coherence::CoherenceInfo(),
416 time(time_passes, ~"type collecting", ||
417 collect::collect_item_types(ccx, crate));
419 time(time_passes, ~"method resolution", ||
420 coherence::check_coherence(ccx, crate));
422 time(time_passes, ~"type checking", ||
423 check::check_item_types(ccx, crate));
425 check_for_entry_fn(ccx);
426 tcx.sess.abort_if_errors();
427 (ccx.method_map, ccx.vtable_map)
433 // indent-tabs-mode: nil
435 // buffer-file-coding-system: utf-8-unix