diff options
| author | Graydon Hoare <graydon@mozilla.com> | 2012-02-29 11:46:23 -0800 |
|---|---|---|
| committer | Graydon Hoare <graydon@mozilla.com> | 2012-03-02 18:46:13 -0800 |
| commit | 87c14f1e3d85751bffffda0b1920be5e726172c4 (patch) | |
| tree | 371d86e9a7c65b06df5c8f5e6d499cf4730324fc /src/rustc | |
| parent | 9228947fe15af96593abf4745d91802b56c205e8 (diff) | |
| download | rust-87c14f1e3d85751bffffda0b1920be5e726172c4.tar.gz rust-87c14f1e3d85751bffffda0b1920be5e726172c4.zip | |
Move src/comp to src/rustc
Diffstat (limited to 'src/rustc')
91 files changed, 54922 insertions, 0 deletions
diff --git a/src/rustc/README.txt b/src/rustc/README.txt new file mode 100644 index 00000000000..9ee501c4c3b --- /dev/null +++ b/src/rustc/README.txt @@ -0,0 +1,77 @@ +An informal guide to reading and working on the rustc compiler. +================================================================== + +If you wish to expand on this document, or have one of the +slightly-more-familiar authors add anything else to it, please get in +touch or file a bug. Your concerns are probably the same as someone +else's. + + +High-level concepts +=================== + +Rustc consists of the following subdirectories: + +syntax/ - pure syntax concerns: lexer, parser, AST. +front/ - front-end: attributes, conditional compilation +middle/ - middle-end: resolving, typechecking, translating +back/ - back-end: linking and ABI +driver/ - command-line processing, main() entrypoint +util/ - ubiquitous types and helper functions +lib/ - bindings to LLVM +pretty/ - pretty-printing + +The entry-point for the compiler is main() in driver/rustc.rs, and +this file sequences the various parts together. + + +The 3 central data structures: +------------------------------ + +#1: syntax/ast.rs defines the AST. The AST is treated as immutable + after parsing despite containing some mutable types (hashtables + and such). There are three interesting details to know about this + structure: + + - Many -- though not all -- nodes within this data structure are + wrapped in the type spanned<T>, meaning that the front-end has + marked the input coordinates of that node. The member .node is + the data itself, the member .span is the input location (file, + line, column; both low and high). + + - Many other nodes within this data structure carry a + def_id. These nodes represent the 'target' of some name + reference elsewhere in the tree. When the AST is resolved, by + middle/resolve.rs, all names wind up acquiring a def that they + point to. So anything that can be pointed-to by a name winds + up with a def_id. + +#2: middle/ty.rs defines the datatype sty. This is the type that + represents types after they have been resolved and normalized by + the middle-end. The typeck phase converts every ast type to a + ty::sty, and the latter is used to drive later phases of + compilation. Most variants in the ast::ty tag have a + corresponding variant in the ty::sty tag. + +#3: lib/llvm.rs defines the exported types ValueRef, TypeRef, + BasicBlockRef, and several others. Each of these is an opaque + pointer to an LLVM type, manipulated through the lib.llvm + interface. + + +Control and information flow within the compiler: +------------------------------------------------- + +- main() in driver/rustc.rs assumes control on startup. Options are + parsed, platform is detected, etc. + +- front/parser.rs is driven over the input files. + +- Multiple middle-end passes (middle/resolve.rs, middle/typeck.rs) are + run over the resulting AST. Each pass generates new information + about the AST which is stored in various side data structures. + +- Finally middle/trans.rs is applied to the AST, which performs a + type-directed translation to LLVM-ese. When it's finished + synthesizing LLVM values, rustc asks LLVM to write them out in some + form (.bc, .o) and possibly run the system linker. diff --git a/src/rustc/back/abi.rs b/src/rustc/back/abi.rs new file mode 100644 index 00000000000..c02960deb00 --- /dev/null +++ b/src/rustc/back/abi.rs @@ -0,0 +1,93 @@ + + + +// FIXME: Most of these should be uints. +const rc_base_field_refcnt: int = 0; + +const task_field_refcnt: int = 0; + +const task_field_stk: int = 2; + +const task_field_runtime_sp: int = 3; + +const task_field_rust_sp: int = 4; + +const task_field_gc_alloc_chain: int = 5; + +const task_field_dom: int = 6; + +const n_visible_task_fields: int = 7; + +const dom_field_interrupt_flag: int = 1; + +const frame_glue_fns_field_mark: int = 0; + +const frame_glue_fns_field_drop: int = 1; + +const frame_glue_fns_field_reloc: int = 2; + +const box_field_refcnt: int = 0; +const box_field_tydesc: int = 1; +const box_field_prev: int = 2; +const box_field_next: int = 3; +const box_field_body: int = 4; + +const general_code_alignment: int = 16; + +const tydesc_field_first_param: int = 0; +const tydesc_field_size: int = 1; +const tydesc_field_align: int = 2; +const tydesc_field_take_glue: int = 3; +const tydesc_field_drop_glue: int = 4; +const tydesc_field_free_glue: int = 5; +const tydesc_field_unused: int = 6; +const tydesc_field_sever_glue: int = 7; +const tydesc_field_mark_glue: int = 8; +const tydesc_field_unused2: int = 9; +const tydesc_field_unused_2: int = 10; +const tydesc_field_shape: int = 11; +const tydesc_field_shape_tables: int = 12; +const tydesc_field_n_params: int = 13; +const tydesc_field_obj_params: int = 14; // FIXME unused +const n_tydesc_fields: int = 15; + +const cmp_glue_op_eq: uint = 0u; + +const cmp_glue_op_lt: uint = 1u; + +const cmp_glue_op_le: uint = 2u; + +// The two halves of a closure: code and environment. +const fn_field_code: int = 0; +const fn_field_box: int = 1; + +// closures, see trans_closure.rs +const closure_body_ty_params: int = 0; +const closure_body_bindings: int = 1; + +const vec_elt_fill: int = 0; + +const vec_elt_alloc: int = 1; + +const vec_elt_elems: int = 2; + +const worst_case_glue_call_args: int = 7; + +const abi_version: uint = 1u; + +fn memcpy_glue_name() -> str { ret "rust_memcpy_glue"; } + +fn bzero_glue_name() -> str { ret "rust_bzero_glue"; } + +fn yield_glue_name() -> str { ret "rust_yield_glue"; } + +fn no_op_type_glue_name() -> str { ret "rust_no_op_type_glue"; } +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs new file mode 100644 index 00000000000..c2cc106b3e2 --- /dev/null +++ b/src/rustc/back/link.rs @@ -0,0 +1,718 @@ +import ctypes::{c_int, c_uint}; +import driver::session; +import session::session; +import lib::llvm::llvm; +import front::attr; +import middle::ty; +import metadata::{encoder, cstore}; +import middle::trans::common::crate_ctxt; +import std::fs; +import std::run; +import std::sha1::sha1; +import syntax::ast; +import syntax::print::pprust; +import lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False}; +import util::filesearch; +import middle::ast_map::{path, path_mod, path_name}; + +enum output_type { + output_type_none, + output_type_bitcode, + output_type_assembly, + output_type_llvm_assembly, + output_type_object, + output_type_exe, +} + +fn llvm_err(sess: session, msg: str) unsafe { + let buf = llvm::LLVMRustGetLastError(); + if buf == ptr::null() { + sess.fatal(msg); + } else { sess.fatal(msg + ": " + str::from_cstr(buf)); } +} + +fn load_intrinsics_bc(sess: session) -> option<ModuleRef> { + let path = alt filesearch::search( + sess.filesearch, + bind filesearch::pick_file("intrinsics.bc", _)) { + option::some(path) { path } + option::none { + sess.warn("couldn't find intrinsics.bc"); + ret option::none; + } + }; + let membuf = str::as_buf(path, {|buf| + llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) + }); + if membuf as uint == 0u { + llvm_err(sess, "installation problem: couldn't open " + path); + fail; + } + let llintrinsicsmod = llvm::LLVMRustParseBitcode(membuf); + llvm::LLVMDisposeMemoryBuffer(membuf); + if llintrinsicsmod as uint == 0u { + sess.warn("couldn't parse intrinsics.bc"); + ret option::none; + } + + ret option::some(llintrinsicsmod); +} + +fn load_intrinsics_ll(sess: session) -> ModuleRef { + let path = alt filesearch::search( + sess.filesearch, + bind filesearch::pick_file("intrinsics.ll", _)) { + option::some(path) { path } + option::none { sess.fatal("couldn't find intrinsics.ll") } + }; + let llintrinsicsmod = str::as_buf(path, { |buf| + llvm::LLVMRustParseAssemblyFile(buf) + }); + if llintrinsicsmod as uint == 0u { + llvm_err(sess, "couldn't parse intrinsics.ll"); + fail; + } + ret llintrinsicsmod; +} + +fn link_intrinsics(sess: session, llmod: ModuleRef) { + let llintrinsicsmod = { + alt load_intrinsics_bc(sess) { + option::some(m) { m } + option::none { + // When the bitcode format changes we can't parse a .bc + // file produced with a newer LLVM (as happens when stage0 + // is trying to build against a new LLVM revision), in + // that case we'll try to parse the assembly. + sess.warn("couldn't parse intrinsics.bc, trying intrinsics.ll"); + load_intrinsics_ll(sess) + } + } + }; + let linkres = llvm::LLVMLinkModules(llmod, llintrinsicsmod); + llvm::LLVMDisposeModule(llintrinsicsmod); + if linkres == False { + llvm_err(sess, "couldn't link the module with the intrinsics"); + fail; + } +} + +mod write { + fn is_object_or_assembly_or_exe(ot: output_type) -> bool { + if ot == output_type_assembly || ot == output_type_object || + ot == output_type_exe { + ret true; + } + ret false; + } + + // Decides what to call an intermediate file, given the name of the output + // and the extension to use. + fn mk_intermediate_name(output_path: str, extension: str) -> str unsafe { + let stem = alt str::find_char(output_path, '.') { + some(dot_pos) { str::slice(output_path, 0u, dot_pos) } + none { output_path } + }; + ret stem + "." + extension; + } + + fn run_passes(sess: session, llmod: ModuleRef, output: str) { + let opts = sess.opts; + if opts.time_llvm_passes { llvm::LLVMRustEnableTimePasses(); } + link_intrinsics(sess, llmod); + let pm = mk_pass_manager(); + let td = mk_target_data( + sess.targ_cfg.target_strs.data_layout); + llvm::LLVMAddTargetData(td.lltd, pm.llpm); + // TODO: run the linter here also, once there are llvm-c bindings for + // it. + + // Generate a pre-optimization intermediate file if -save-temps was + // specified. + + if opts.save_temps { + alt opts.output_type { + output_type_bitcode { + if opts.optimize != 0u { + let filename = mk_intermediate_name(output, "no-opt.bc"); + str::as_buf(filename, + {|buf| + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + _ { + let filename = mk_intermediate_name(output, "bc"); + str::as_buf(filename, + {|buf| + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + } + if opts.verify { llvm::LLVMAddVerifierPass(pm.llpm); } + // FIXME: This is mostly a copy of the bits of opt's -O2 that are + // available in the C api. + // FIXME2: We might want to add optimization levels like -O1, -O2, + // -Os, etc + // FIXME3: Should we expose and use the pass lists used by the opt + // tool? + + if opts.optimize != 0u { + let fpm = mk_pass_manager(); + llvm::LLVMAddTargetData(td.lltd, fpm.llpm); + + let FPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(FPMB, 2u as c_uint); + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(FPMB, + fpm.llpm); + llvm::LLVMPassManagerBuilderDispose(FPMB); + + llvm::LLVMRunPassManager(fpm.llpm, llmod); + let threshold = 225u; + if opts.optimize == 3u { threshold = 275u; } + + let MPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(MPMB, + opts.optimize as c_uint); + llvm::LLVMPassManagerBuilderSetSizeLevel(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnitAtATime(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableSimplifyLibCalls(MPMB, + False); + + if threshold != 0u { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold + (MPMB, threshold as c_uint); + } + llvm::LLVMPassManagerBuilderPopulateModulePassManager(MPMB, + pm.llpm); + + llvm::LLVMPassManagerBuilderDispose(MPMB); + } + if opts.verify { llvm::LLVMAddVerifierPass(pm.llpm); } + if is_object_or_assembly_or_exe(opts.output_type) { + let LLVMAssemblyFile = 0 as c_int; + let LLVMObjectFile = 1 as c_int; + let LLVMOptNone = 0 as c_int; // -O0 + let LLVMOptLess = 1 as c_int; // -O1 + let LLVMOptDefault = 2 as c_int; // -O2, -Os + let LLVMOptAggressive = 3 as c_int; // -O3 + + let CodeGenOptLevel; + alt check opts.optimize { + 0u { CodeGenOptLevel = LLVMOptNone; } + 1u { CodeGenOptLevel = LLVMOptLess; } + 2u { CodeGenOptLevel = LLVMOptDefault; } + 3u { CodeGenOptLevel = LLVMOptAggressive; } + } + + let FileType; + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + FileType = LLVMObjectFile; + } else { FileType = LLVMAssemblyFile; } + // Write optimized bitcode if --save-temps was on. + + if opts.save_temps { + // Always output the bitcode file with --save-temps + + let filename = mk_intermediate_name(output, "opt.bc"); + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_buf(filename, + {|buf| + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + pm = mk_pass_manager(); + // Save the assembly file if -S is used + + if opts.output_type == output_type_assembly { + let _: () = str::as_buf( + sess.targ_cfg.target_strs.target_triple, + {|buf_t| + str::as_buf(output, {|buf_o| + llvm::LLVMRustWriteOutputFile( + pm.llpm, + llmod, + buf_t, + buf_o, + LLVMAssemblyFile, + CodeGenOptLevel, + true)})}); + } + + + // Save the object file for -c or --save-temps alone + // This .o is needed when an exe is built + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + let _: () = + str::as_buf( + sess.targ_cfg.target_strs.target_triple, + {|buf_t| + str::as_buf(output, {|buf_o| + llvm::LLVMRustWriteOutputFile( + pm.llpm, + llmod, + buf_t, + buf_o, + LLVMObjectFile, + CodeGenOptLevel, + true)})}); + } + } else { + // If we aren't saving temps then just output the file + // type corresponding to the '-c' or '-S' flag used + + let _: () = + str::as_buf( + sess.targ_cfg.target_strs.target_triple, + {|buf_t| + str::as_buf(output, {|buf_o| + llvm::LLVMRustWriteOutputFile( + pm.llpm, + llmod, + buf_t, + buf_o, + FileType, + CodeGenOptLevel, + true)})}); + } + // Clean up and return + + llvm::LLVMDisposeModule(llmod); + if opts.time_llvm_passes { llvm::LLVMRustPrintPassTimings(); } + ret; + } + + if opts.output_type == output_type_llvm_assembly { + // Given options "-S --emit-llvm": output LLVM assembly + str::as_buf(output, {|buf_o| + llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o)}); + } else { + // If only a bitcode file is asked for by using the '--emit-llvm' + // flag, then output it here + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_buf(output, + {|buf| llvm::LLVMWriteBitcodeToFile(llmod, buf) }); + } + + llvm::LLVMDisposeModule(llmod); + if opts.time_llvm_passes { llvm::LLVMRustPrintPassTimings(); } + } +} + + +/* + * Name mangling and its relationship to metadata. This is complex. Read + * carefully. + * + * The semantic model of Rust linkage is, broadly, that "there's no global + * namespace" between crates. Our aim is to preserve the illusion of this + * model despite the fact that it's not *quite* possible to implement on + * modern linkers. We initially didn't use system linkers at all, but have + * been convinced of their utility. + * + * There are a few issues to handle: + * + * - Linkers operate on a flat namespace, so we have to flatten names. + * We do this using the C++ namespace-mangling technique. Foo::bar + * symbols and such. + * + * - Symbols with the same name but different types need to get different + * linkage-names. We do this by hashing a string-encoding of the type into + * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: + * we use SHA1) to "prevent collisions". This is not airtight but 16 hex + * digits on uniform probability means you're going to need 2**32 same-name + * symbols in the same process before you're even hitting birthday-paradox + * collision probability. + * + * - Symbols in different crates but with same names "within" the crate need + * to get different linkage-names. + * + * So here is what we do: + * + * - Separate the meta tags into two sets: exported and local. Only work with + * the exported ones when considering linkage. + * + * - Consider two exported tags as special (and mandatory): name and vers. + * Every crate gets them; if it doesn't name them explicitly we infer them + * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS. + * + * - Define CMETA as all the non-name, non-vers exported meta tags in the + * crate (in sorted order). + * + * - Define CMH as hash(CMETA + hashes of dependent crates). + * + * - Compile our crate to lib CNAME-CMH-CVERS.so + * + * - Define STH(sym) as hash(CNAME, CMH, type_str(sym)) + * + * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the + * name, non-name metadata, and type sense, and versioned in the way + * system linkers understand. + * + */ + +type link_meta = {name: str, vers: str, extras_hash: str}; + +fn build_link_meta(sess: session, c: ast::crate, output: str, + sha: sha1) -> link_meta { + + type provided_metas = + {name: option<str>, + vers: option<str>, + cmh_items: [@ast::meta_item]}; + + fn provided_link_metas(sess: session, c: ast::crate) -> + provided_metas { + let name: option<str> = none; + let vers: option<str> = none; + let cmh_items: [@ast::meta_item] = []; + let linkage_metas = attr::find_linkage_metas(c.node.attrs); + attr::require_unique_names(sess, linkage_metas); + for meta: @ast::meta_item in linkage_metas { + if attr::get_meta_item_name(meta) == "name" { + alt attr::get_meta_item_value_str(meta) { + some(v) { name = some(v); } + none { cmh_items += [meta]; } + } + } else if attr::get_meta_item_name(meta) == "vers" { + alt attr::get_meta_item_value_str(meta) { + some(v) { vers = some(v); } + none { cmh_items += [meta]; } + } + } else { cmh_items += [meta]; } + } + ret {name: name, vers: vers, cmh_items: cmh_items}; + } + + // This calculates CMH as defined above + fn crate_meta_extras_hash(sha: sha1, _crate: ast::crate, + metas: provided_metas, + dep_hashes: [str]) -> str { + fn len_and_str(s: str) -> str { + ret #fmt["%u_%s", str::len(s), s]; + } + + fn len_and_str_lit(l: ast::lit) -> str { + ret len_and_str(pprust::lit_to_str(@l)); + } + + let cmh_items = attr::sort_meta_items(metas.cmh_items); + + sha.reset(); + for m_: @ast::meta_item in cmh_items { + let m = m_; + alt m.node { + ast::meta_name_value(key, value) { + sha.input_str(len_and_str(key)); + sha.input_str(len_and_str_lit(value)); + } + ast::meta_word(name) { sha.input_str(len_and_str(name)); } + ast::meta_list(_, _) { + // FIXME (#607): Implement this + fail "unimplemented meta_item variant"; + } + } + } + + for dh in dep_hashes { + sha.input_str(len_and_str(dh)); + } + + ret truncated_sha1_result(sha); + } + + fn warn_missing(sess: session, name: str, default: str) { + if !sess.building_library { ret; } + sess.warn(#fmt["missing crate link meta '%s', using '%s' as default", + name, default]); + } + + fn crate_meta_name(sess: session, _crate: ast::crate, + output: str, metas: provided_metas) -> str { + ret alt metas.name { + some(v) { v } + none { + let name = + { + let os = str::split_char(fs::basename(output), '.'); + if (vec::len(os) < 2u) { + sess.fatal(#fmt("Output file name %s doesn't\ + appear to have an extension", output)); + } + vec::pop(os); + str::connect(os, ".") + }; + warn_missing(sess, "name", name); + name + } + }; + } + + fn crate_meta_vers(sess: session, _crate: ast::crate, + metas: provided_metas) -> str { + ret alt metas.vers { + some(v) { v } + none { + let vers = "0.0"; + warn_missing(sess, "vers", vers); + vers + } + }; + } + + let provided_metas = provided_link_metas(sess, c); + let name = crate_meta_name(sess, c, output, provided_metas); + let vers = crate_meta_vers(sess, c, provided_metas); + let dep_hashes = cstore::get_dep_hashes(sess.cstore); + let extras_hash = + crate_meta_extras_hash(sha, c, provided_metas, dep_hashes); + + ret {name: name, vers: vers, extras_hash: extras_hash}; +} + +fn truncated_sha1_result(sha: sha1) -> str unsafe { + ret str::slice(sha.result_str(), 0u, 16u); +} + + +// This calculates STH for a symbol, as defined above +fn symbol_hash(tcx: ty::ctxt, sha: sha1, t: ty::t, link_meta: link_meta) -> + str { + // NB: do *not* use abbrevs here as we want the symbol names + // to be independent of one another in the crate. + + sha.reset(); + sha.input_str(link_meta.name); + sha.input_str("-"); + // FIXME: This wants to be link_meta.meta_hash + sha.input_str(link_meta.name); + sha.input_str("-"); + sha.input_str(encoder::encoded_ty(tcx, t)); + let hash = truncated_sha1_result(sha); + // Prefix with _ so that it never blends into adjacent digits + + ret "_" + hash; +} + +fn get_symbol_hash(ccx: crate_ctxt, t: ty::t) -> str { + let hash = ""; + alt ccx.type_sha1s.find(t) { + some(h) { hash = h; } + none { + hash = symbol_hash(ccx.tcx, ccx.sha, t, ccx.link_meta); + ccx.type_sha1s.insert(t, hash); + } + } + ret hash; +} + +fn mangle(ss: path) -> str { + // Follow C++ namespace-mangling style + + let n = "_ZN"; // Begin name-sequence. + + for s in ss { + alt s { path_name(s) | path_mod(s) { + n += #fmt["%u%s", str::len(s), s]; + } } + } + n += "E"; // End name-sequence. + n +} + +fn exported_name(path: path, hash: str, _vers: str) -> str { + // FIXME: versioning isn't working yet + ret mangle(path + [path_name(hash)]); // + "@" + vers; + +} + +fn mangle_exported_name(ccx: crate_ctxt, path: path, t: ty::t) -> str { + let hash = get_symbol_hash(ccx, t); + ret exported_name(path, hash, ccx.link_meta.vers); +} + +fn mangle_internal_name_by_type_only(ccx: crate_ctxt, t: ty::t, name: str) -> + str { + let s = util::ppaux::ty_to_short_str(ccx.tcx, t); + let hash = get_symbol_hash(ccx, t); + ret mangle([path_name(name), path_name(s), path_name(hash)]); +} + +fn mangle_internal_name_by_path_and_seq(ccx: crate_ctxt, path: path, + flav: str) -> str { + ret mangle(path + [path_name(ccx.names(flav))]); +} + +fn mangle_internal_name_by_path(_ccx: crate_ctxt, path: path) -> str { + ret mangle(path); +} + +fn mangle_internal_name_by_seq(ccx: crate_ctxt, flav: str) -> str { + ret ccx.names(flav); +} + +// If the user wants an exe generated we need to invoke +// cc to link the object file with some libs +fn link_binary(sess: session, + obj_filename: str, + out_filename: str, + lm: link_meta) { + // Converts a library file name into a cc -l argument + fn unlib(config: @session::config, filename: str) -> str unsafe { + let rmlib = fn@(filename: str) -> str { + let found = str::find_str(filename, "lib"); + if config.os == session::os_macos || + (config.os == session::os_linux || + config.os == session::os_freebsd) && + option::is_some(found) && option::get(found) == 0u { + ret str::slice(filename, 3u, str::len(filename)); + } else { ret filename; } + }; + fn rmext(filename: str) -> str { + let parts = str::split_char(filename, '.'); + vec::pop(parts); + ret str::connect(parts, "."); + } + ret alt config.os { + session::os_macos { rmext(rmlib(filename)) } + session::os_linux { rmext(rmlib(filename)) } + session::os_freebsd { rmext(rmlib(filename)) } + _ { rmext(filename) } + }; + } + + let output = if sess.building_library { + let long_libname = + std::os::dylib_filename(#fmt("%s-%s-%s", + lm.name, lm.extras_hash, lm.vers)); + #debug("link_meta.name: %s", lm.name); + #debug("long_libname: %s", long_libname); + #debug("out_filename: %s", out_filename); + #debug("dirname(out_filename): %s", fs::dirname(out_filename)); + + fs::connect(fs::dirname(out_filename), long_libname) + } else { out_filename }; + + log(debug, "output: " + output); + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let stage: str = "-L" + sess.filesearch.get_target_lib_path(); + + // In the future, FreeBSD will use clang as default compiler. + // It would be flexible to use cc (system's default C compiler) + // instead of hard-coded gcc. + // For win32, there is no cc command, + // so we add a condition to make it use gcc. + let cc_prog: str = + if sess.targ_cfg.os == session::os_win32 { "gcc" } else { "cc" }; + // The invocations of cc share some flags across platforms + + let cc_args = + [stage] + sess.targ_cfg.target_strs.cc_args + + ["-o", output, obj_filename]; + + let lib_cmd; + let os = sess.targ_cfg.os; + if os == session::os_macos { + lib_cmd = "-dynamiclib"; + } else { lib_cmd = "-shared"; } + + let cstore = sess.cstore; + for cratepath: str in cstore::get_used_crate_files(cstore) { + if str::ends_with(cratepath, ".rlib") { + cc_args += [cratepath]; + cont; + } + let cratepath = cratepath; + let dir = fs::dirname(cratepath); + if dir != "" { cc_args += ["-L" + dir]; } + let libarg = unlib(sess.targ_cfg, fs::basename(cratepath)); + cc_args += ["-l" + libarg]; + } + + let ula = cstore::get_used_link_args(cstore); + for arg: str in ula { cc_args += [arg]; } + + let used_libs = cstore::get_used_libraries(cstore); + for l: str in used_libs { cc_args += ["-l" + l]; } + + if sess.building_library { + cc_args += [lib_cmd]; + + // On mac we need to tell the linker to let this library + // be rpathed + if sess.targ_cfg.os == session::os_macos { + cc_args += ["-Wl,-install_name,@rpath/" + + fs::basename(output)]; + } + } else { + // FIXME: why do we hardcode -lm? + cc_args += ["-lm"]; + } + + // Always want the runtime linked in + cc_args += ["-lrustrt"]; + + // On linux librt and libdl are an indirect dependencies via rustrt, + // and binutils 2.22+ won't add them automatically + if sess.targ_cfg.os == session::os_linux { + cc_args += ["-lrt", "-ldl"]; + } + + if sess.targ_cfg.os == session::os_freebsd { + cc_args += ["-lrt", "-L/usr/local/lib", "-lexecinfo", + "-L/usr/local/lib/gcc46", + "-L/usr/local/lib/gcc44", "-lstdc++", + "-Wl,-z,origin", + "-Wl,-rpath,/usr/local/lib/gcc46", + "-Wl,-rpath,/usr/local/lib/gcc44"]; + } + + // OS X 10.6 introduced 'compact unwind info', which is produced by the + // linker from the dwarf unwind info. Unfortunately, it does not seem to + // understand how to unwind our __morestack frame, so we have to turn it + // off. This has impacted some other projects like GHC. + if sess.targ_cfg.os == session::os_macos { + cc_args += ["-Wl,-no_compact_unwind"]; + } + + // Stack growth requires statically linking a __morestack function + cc_args += ["-lmorestack"]; + + cc_args += rpath::get_rpath_flags(sess, output); + + #debug("%s link args: %s", cc_prog, str::connect(cc_args, " ")); + // We run 'cc' here + let prog = run::program_output(cc_prog, cc_args); + if 0 != prog.status { + sess.err(#fmt["linking with %s failed with code %d", + cc_prog, prog.status]); + sess.note(#fmt["%s arguments: %s", + cc_prog, str::connect(cc_args, " ")]); + sess.note(prog.err + prog.out); + sess.abort_if_errors(); + } + + // Clean up on Darwin + if sess.targ_cfg.os == session::os_macos { + run::run_program("dsymutil", [output]); + } + + // Remove the temporary object file if we aren't saving temps + if !sess.opts.save_temps { + run::run_program("rm", [obj_filename]); + } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/back/rpath.rs b/src/rustc/back/rpath.rs new file mode 100644 index 00000000000..cc74efa82cf --- /dev/null +++ b/src/rustc/back/rpath.rs @@ -0,0 +1,345 @@ +import std::{os, fs, os_fs, map}; +import metadata::cstore; +import driver::session; +import util::filesearch; + +export get_rpath_flags; + +pure fn not_win32(os: session::os) -> bool { + alt os { + session::os_win32 { false } + _ { true } + } +} + +fn get_rpath_flags(sess: session::session, out_filename: str) -> [str] { + let os = sess.targ_cfg.os; + + // No rpath on windows + if os == session::os_win32 { + ret []; + } + + #debug("preparing the RPATH!"); + + let cwd = os::getcwd(); + let sysroot = sess.filesearch.sysroot(); + let output = out_filename; + let libs = cstore::get_used_crate_files(sess.cstore); + // We don't currently rpath native libraries, but we know + // where rustrt is and we know every rust program needs it + let libs = libs + [get_sysroot_absolute_rt_lib(sess)]; + + let target_triple = sess.opts.target_triple; + let rpaths = get_rpaths(os, cwd, sysroot, output, libs, target_triple); + rpaths_to_flags(rpaths) +} + +fn get_sysroot_absolute_rt_lib(sess: session::session) -> fs::path { + let path = [sess.filesearch.sysroot()] + + filesearch::relative_target_lib_path( + sess.opts.target_triple) + + [os::dylib_filename("rustrt")]; + fs::connect_many(path) +} + +fn rpaths_to_flags(rpaths: [str]) -> [str] { + vec::map(rpaths, { |rpath| #fmt("-Wl,-rpath,%s",rpath)}) +} + +fn get_rpaths(os: session::os, cwd: fs::path, sysroot: fs::path, + output: fs::path, libs: [fs::path], + target_triple: str) -> [str] { + #debug("cwd: %s", cwd); + #debug("sysroot: %s", sysroot); + #debug("output: %s", output); + #debug("libs:"); + for libpath in libs { + #debug(" %s", libpath); + } + #debug("target_triple: %s", target_triple); + + // Use relative paths to the libraries. Binaries can be moved + // as long as they maintain the relative relationship to the + // crates they depend on. + let rel_rpaths = get_rpaths_relative_to_output(os, cwd, output, libs); + + // Make backup absolute paths to the libraries. Binaries can + // be moved as long as the crates they link against don't move. + let abs_rpaths = get_absolute_rpaths(cwd, libs); + + // And a final backup rpath to the global library location. + let fallback_rpaths = [get_install_prefix_rpath(cwd, target_triple)]; + + fn log_rpaths(desc: str, rpaths: [str]) { + #debug("%s rpaths:", desc); + for rpath in rpaths { + #debug(" %s", rpath); + } + } + + log_rpaths("relative", rel_rpaths); + log_rpaths("absolute", abs_rpaths); + log_rpaths("fallback", fallback_rpaths); + + let rpaths = rel_rpaths + abs_rpaths + fallback_rpaths; + + // Remove duplicates + let rpaths = minimize_rpaths(rpaths); + ret rpaths; +} + +fn get_rpaths_relative_to_output(os: session::os, + cwd: fs::path, + output: fs::path, + libs: [fs::path]) -> [str] { + vec::map(libs, bind get_rpath_relative_to_output(os, cwd, output, _)) +} + +fn get_rpath_relative_to_output(os: session::os, + cwd: fs::path, + output: fs::path, + &&lib: fs::path) : not_win32(os) -> str { + // Mac doesn't appear to support $ORIGIN + let prefix = alt os { + session::os_linux { "$ORIGIN" + fs::path_sep() } + session::os_freebsd { "$ORIGIN" + fs::path_sep() } + session::os_macos { "@executable_path" + fs::path_sep() } + session::os_win32 { std::util::unreachable(); } + }; + + prefix + get_relative_to( + get_absolute(cwd, output), + get_absolute(cwd, lib)) +} + +// Find the relative path from one file to another +fn get_relative_to(abs1: fs::path, abs2: fs::path) -> fs::path { + assert fs::path_is_absolute(abs1); + assert fs::path_is_absolute(abs2); + #debug("finding relative path from %s to %s", + abs1, abs2); + let normal1 = fs::normalize(abs1); + let normal2 = fs::normalize(abs2); + let split1 = str::split_char(normal1, os_fs::path_sep); + let split2 = str::split_char(normal2, os_fs::path_sep); + let len1 = vec::len(split1); + let len2 = vec::len(split2); + assert len1 > 0u; + assert len2 > 0u; + + let max_common_path = math::min(len1, len2) - 1u; + let start_idx = 0u; + while start_idx < max_common_path + && split1[start_idx] == split2[start_idx] { + start_idx += 1u; + } + + let path = []; + + uint::range(start_idx, len1 - 1u) {|_i| path += [".."]; }; + + path += vec::slice(split2, start_idx, len2 - 1u); + + if check vec::is_not_empty(path) { + ret fs::connect_many(path); + } else { + ret "."; + } +} + +fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] { + vec::map(libs, bind get_absolute_rpath(cwd, _)) +} + +fn get_absolute_rpath(cwd: fs::path, &&lib: fs::path) -> str { + fs::dirname(get_absolute(cwd, lib)) +} + +fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path { + if fs::path_is_absolute(lib) { + lib + } else { + fs::connect(cwd, lib) + } +} + +fn get_install_prefix_rpath(cwd: fs::path, target_triple: str) -> str { + let install_prefix = #env("CFG_PREFIX"); + + if install_prefix == "" { + fail "rustc compiled without CFG_PREFIX environment variable"; + } + + let path = [install_prefix] + + filesearch::relative_target_lib_path(target_triple); + get_absolute(cwd, fs::connect_many(path)) +} + +fn minimize_rpaths(rpaths: [str]) -> [str] { + let set = map::new_str_hash::<()>(); + let minimized = []; + for rpath in rpaths { + if !set.contains_key(rpath) { + minimized += [rpath]; + set.insert(rpath, ()); + } + } + ret minimized; +} + +#[cfg(target_os = "linux")] +#[cfg(target_os = "macos")] +#[cfg(target_os = "freebsd")] +mod test { + #[test] + fn test_rpaths_to_flags() { + let flags = rpaths_to_flags(["path1", "path2"]); + assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]; + } + + #[test] + fn test_get_absolute1() { + let cwd = "/dir"; + let lib = "some/path/lib"; + let res = get_absolute(cwd, lib); + assert res == "/dir/some/path/lib"; + } + + #[test] + fn test_get_absolute2() { + let cwd = "/dir"; + let lib = "/some/path/lib"; + let res = get_absolute(cwd, lib); + assert res == "/some/path/lib"; + } + + #[test] + fn test_prefix_rpath() { + let res = get_install_prefix_rpath("/usr/lib", "triple"); + let d = fs::connect(#env("CFG_PREFIX"), "/lib/rustc/triple/lib"); + assert str::ends_with(res, d); + } + + #[test] + fn test_prefix_rpath_abs() { + let res = get_install_prefix_rpath("/usr/lib", "triple"); + assert fs::path_is_absolute(res); + } + + #[test] + fn test_minimize1() { + let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]); + assert res == ["rpath1", "rpath2"]; + } + + #[test] + fn test_minimize2() { + let res = minimize_rpaths(["1a", "2", "2", "1a", "4a", + "1a", "2", "3", "4a", "3"]); + assert res == ["1a", "2", "4a", "3"]; + } + + #[test] + fn test_relative_to1() { + let p1 = "/usr/bin/rustc"; + let p2 = "/usr/lib/mylib"; + let res = get_relative_to(p1, p2); + assert res == "../lib"; + } + + #[test] + fn test_relative_to2() { + let p1 = "/usr/bin/rustc"; + let p2 = "/usr/bin/../lib/mylib"; + let res = get_relative_to(p1, p2); + assert res == "../lib"; + } + + #[test] + fn test_relative_to3() { + let p1 = "/usr/bin/whatever/rustc"; + let p2 = "/usr/lib/whatever/mylib"; + let res = get_relative_to(p1, p2); + assert res == "../../lib/whatever"; + } + + #[test] + fn test_relative_to4() { + let p1 = "/usr/bin/whatever/../rustc"; + let p2 = "/usr/lib/whatever/mylib"; + let res = get_relative_to(p1, p2); + assert res == "../lib/whatever"; + } + + #[test] + fn test_relative_to5() { + let p1 = "/usr/bin/whatever/../rustc"; + let p2 = "/usr/lib/whatever/../mylib"; + let res = get_relative_to(p1, p2); + assert res == "../lib"; + } + + #[test] + fn test_relative_to6() { + let p1 = "/1"; + let p2 = "/2/3"; + let res = get_relative_to(p1, p2); + assert res == "2"; + } + + #[test] + fn test_relative_to7() { + let p1 = "/1/2"; + let p2 = "/3"; + let res = get_relative_to(p1, p2); + assert res == ".."; + } + + #[test] + fn test_relative_to8() { + let p1 = "/home/brian/Dev/rust/build/" + + "stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so"; + let p2 = "/home/brian/Dev/rust/build/stage2/bin/.." + + "/lib/rustc/i686-unknown-linux-gnu/lib/libstd.so"; + let res = get_relative_to(p1, p2); + assert res == "."; + } + + #[test] + #[cfg(target_os = "linux")] + fn test_rpath_relative() { + let o = session::os_linux; + check not_win32(o); + let res = get_rpath_relative_to_output(o, + "/usr", "bin/rustc", "lib/libstd.so"); + assert res == "$ORIGIN/../lib"; + } + + #[test] + #[cfg(target_os = "freebsd")] + fn test_rpath_relative() { + let o = session::os_freebsd; + check not_win32(o); + let res = get_rpath_relative_to_output(o, + "/usr", "bin/rustc", "lib/libstd.so"); + assert res == "$ORIGIN/../lib"; + } + + #[test] + #[cfg(target_os = "macos")] + fn test_rpath_relative() { + // this is why refinements would be nice + let o = session::os_macos; + check not_win32(o); + let res = get_rpath_relative_to_output(o, "/usr", "bin/rustc", + "lib/libstd.so"); + assert res == "@executable_path/../lib"; + } + + #[test] + fn test_get_absolute_rpath() { + let res = get_absolute_rpath("/usr", "lib/libstd.so"); + assert res == "/usr/lib"; + } +} diff --git a/src/rustc/back/target_strs.rs b/src/rustc/back/target_strs.rs new file mode 100644 index 00000000000..cc40746b71c --- /dev/null +++ b/src/rustc/back/target_strs.rs @@ -0,0 +1,7 @@ +type t = { + module_asm: str, + meta_sect_name: str, + data_layout: str, + target_triple: str, + cc_args: [str] +}; diff --git a/src/rustc/back/upcall.rs b/src/rustc/back/upcall.rs new file mode 100644 index 00000000000..31ee139c524 --- /dev/null +++ b/src/rustc/back/upcall.rs @@ -0,0 +1,130 @@ + +import driver::session; +import middle::trans::base; +import middle::trans::common::{T_fn, T_i1, T_i8, T_i32, + T_int, T_nil, T_dict, + T_opaque_vec, T_ptr, + T_size_t, T_void}; +import lib::llvm::{type_names, ModuleRef, ValueRef, TypeRef}; + +type upcalls = + {_fail: ValueRef, + malloc: ValueRef, + free: ValueRef, + validate_box: ValueRef, + shared_malloc: ValueRef, + shared_free: ValueRef, + shared_realloc: ValueRef, + mark: ValueRef, + create_shared_type_desc: ValueRef, + free_shared_type_desc: ValueRef, + get_type_desc: ValueRef, + intern_dict: ValueRef, + vec_grow: ValueRef, + vec_push: ValueRef, + cmp_type: ValueRef, + log_type: ValueRef, + dynastack_mark: ValueRef, + dynastack_alloc: ValueRef, + dynastack_free: ValueRef, + alloc_c_stack: ValueRef, + call_shim_on_c_stack: ValueRef, + call_shim_on_rust_stack: ValueRef, + rust_personality: ValueRef, + reset_stack_limit: ValueRef}; + +fn declare_upcalls(targ_cfg: @session::config, + _tn: type_names, + tydesc_type: TypeRef, + llmod: ModuleRef) -> @upcalls { + fn decl(llmod: ModuleRef, prefix: str, name: str, + tys: [TypeRef], rv: TypeRef) -> + ValueRef { + let arg_tys: [TypeRef] = []; + for t: TypeRef in tys { arg_tys += [t]; } + let fn_ty = T_fn(arg_tys, rv); + ret base::decl_cdecl_fn(llmod, prefix + name, fn_ty); + } + let d = bind decl(llmod, "upcall_", _, _, _); + let dv = bind decl(llmod, "upcall_", _, _, T_void()); + let dvi = bind decl(llmod, "upcall_intrinsic_", _, _, T_void()); + + let int_t = T_int(targ_cfg); + let size_t = T_size_t(targ_cfg); + let opaque_vec_t = T_opaque_vec(targ_cfg); + + ret @{_fail: dv("fail", [T_ptr(T_i8()), + T_ptr(T_i8()), + size_t]), + malloc: + d("malloc", [T_ptr(tydesc_type)], T_ptr(T_i8())), + free: + dv("free", [T_ptr(T_i8())]), + validate_box: + dv("validate_box", [T_ptr(T_i8())]), + shared_malloc: + d("shared_malloc", [size_t], T_ptr(T_i8())), + shared_free: + dv("shared_free", [T_ptr(T_i8())]), + shared_realloc: + d("shared_realloc", [T_ptr(T_i8()), size_t], T_ptr(T_i8())), + mark: + d("mark", [T_ptr(T_i8())], int_t), + create_shared_type_desc: + d("create_shared_type_desc", [T_ptr(tydesc_type)], + T_ptr(tydesc_type)), + free_shared_type_desc: + dv("free_shared_type_desc", [T_ptr(tydesc_type)]), + get_type_desc: + d("get_type_desc", + [T_ptr(T_nil()), size_t, + size_t, size_t, + T_ptr(T_ptr(tydesc_type)), int_t], + T_ptr(tydesc_type)), + intern_dict: + d("intern_dict", [size_t, T_ptr(T_dict())], T_ptr(T_dict())), + vec_grow: + dv("vec_grow", [T_ptr(T_ptr(opaque_vec_t)), int_t]), + vec_push: + dvi("vec_push", + [T_ptr(T_ptr(opaque_vec_t)), T_ptr(tydesc_type), + T_ptr(T_i8())]), + cmp_type: + dv("cmp_type", + [T_ptr(T_i1()), T_ptr(tydesc_type), + T_ptr(T_ptr(tydesc_type)), T_ptr(T_i8()), T_ptr(T_i8()), + T_i8()]), + log_type: + dv("log_type", [T_ptr(tydesc_type), T_ptr(T_i8()), T_i32()]), + dynastack_mark: + d("dynastack_mark", [], T_ptr(T_i8())), + dynastack_alloc: + d("dynastack_alloc_2", [size_t, T_ptr(tydesc_type)], + T_ptr(T_i8())), + dynastack_free: + dv("dynastack_free", [T_ptr(T_i8())]), + alloc_c_stack: + d("alloc_c_stack", [size_t], T_ptr(T_i8())), + call_shim_on_c_stack: + d("call_shim_on_c_stack", + // arguments: void *args, void *fn_ptr + [T_ptr(T_i8()), T_ptr(T_i8())], + int_t), + call_shim_on_rust_stack: + d("call_shim_on_rust_stack", + [T_ptr(T_i8()), T_ptr(T_i8())], int_t), + rust_personality: + d("rust_personality", [], T_i32()), + reset_stack_limit: + dv("reset_stack_limit", []) + }; +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/back/x86.rs b/src/rustc/back/x86.rs new file mode 100644 index 00000000000..3db3d1db50a --- /dev/null +++ b/src/rustc/back/x86.rs @@ -0,0 +1,53 @@ +import driver::session; + +fn get_target_strs(target_os: session::os) -> target_strs::t { + ret { + module_asm: "", + + meta_sect_name: alt target_os { + session::os_macos { "__DATA,__note.rustc" } + session::os_win32 { ".note.rustc" } + session::os_linux { ".note.rustc" } + session::os_freebsd { ".note.rustc" } + }, + + data_layout: alt target_os { + session::os_macos { + "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16" + "-i32:32:32-i64:32:64" + + "-f32:32:32-f64:32:64-v64:64:64" + + "-v128:128:128-a0:0:64-f80:128:128" + "-n8:16:32" + } + + session::os_win32 { + "e-p:32:32-f64:64:64-i64:64:64-f80:32:32-n8:16:32" + } + + session::os_linux { + "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" + } + + session::os_freebsd { + "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32" + } + }, + + target_triple: alt target_os { + session::os_macos { "i686-apple-darwin" } + session::os_win32 { "i686-pc-mingw32" } + session::os_linux { "i686-unknown-linux-gnu" } + session::os_freebsd { "i686-unknown-freebsd" } + }, + + cc_args: ["-m32"] + }; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/back/x86_64.rs b/src/rustc/back/x86_64.rs new file mode 100644 index 00000000000..284152c23c6 --- /dev/null +++ b/src/rustc/back/x86_64.rs @@ -0,0 +1,60 @@ +import driver::session; + +fn get_target_strs(target_os: session::os) -> target_strs::t { + ret { + module_asm: "", + + meta_sect_name: alt target_os { + session::os_macos { "__DATA,__note.rustc" } + session::os_win32 { ".note.rustc" } + session::os_linux { ".note.rustc" } + session::os_freebsd { ".note.rustc" } + }, + + data_layout: alt target_os { + session::os_macos { + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + "s0:64:64-f80:128:128-n8:16:32:64" + } + + session::os_win32 { + // FIXME: Test this. Copied from linux + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + "s0:64:64-f80:128:128-n8:16:32:64-S128" + } + + session::os_linux { + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + "s0:64:64-f80:128:128-n8:16:32:64-S128" + } + + session::os_freebsd { + "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-"+ + "f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-"+ + "s0:64:64-f80:128:128-n8:16:32:64-S128" + } + }, + + target_triple: alt target_os { + session::os_macos { "x86_64-apple-darwin" } + session::os_win32 { "x86_64-pc-mingw32" } + session::os_linux { "x86_64-unknown-linux-gnu" } + session::os_freebsd { "x86_64-unknown-freebsd" } + }, + + cc_args: ["-m64"] + }; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/driver/diagnostic.rs b/src/rustc/driver/diagnostic.rs new file mode 100644 index 00000000000..8f0570a4349 --- /dev/null +++ b/src/rustc/driver/diagnostic.rs @@ -0,0 +1,256 @@ +import std::{io, term}; +import io::writer_util; +import syntax::codemap; +import codemap::span; + +export emitter, emit; +export level, fatal, error, warning, note; +export span_handler, handler, mk_span_handler, mk_handler; +export codemap_span_handler, codemap_handler; +export ice_msg; + +type emitter = fn@(cmsp: option<(codemap::codemap, span)>, + msg: str, lvl: level); + + +iface span_handler { + fn span_fatal(sp: span, msg: str) -> !; + fn span_err(sp: span, msg: str); + fn span_warn(sp: span, msg: str); + fn span_note(sp: span, msg: str); + fn span_bug(sp: span, msg: str) -> !; + fn span_unimpl(sp: span, msg: str) -> !; + fn handler() -> handler; +} + +iface handler { + fn fatal(msg: str) -> !; + fn err(msg: str); + fn bump_err_count(); + fn has_errors() -> bool; + fn abort_if_errors(); + fn warn(msg: str); + fn note(msg: str); + fn bug(msg: str) -> !; + fn unimpl(msg: str) -> !; + fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level); +} + +type handler_t = @{ + mutable err_count: uint, + _emit: emitter +}; + +type codemap_t = @{ + handler: handler, + cm: codemap::codemap +}; + +impl codemap_span_handler of span_handler for codemap_t { + fn span_fatal(sp: span, msg: str) -> ! { + self.handler.emit(some((self.cm, sp)), msg, fatal); + fail; + } + fn span_err(sp: span, msg: str) { + self.handler.emit(some((self.cm, sp)), msg, error); + self.handler.bump_err_count(); + } + fn span_warn(sp: span, msg: str) { + self.handler.emit(some((self.cm, sp)), msg, warning); + } + fn span_note(sp: span, msg: str) { + self.handler.emit(some((self.cm, sp)), msg, note); + } + fn span_bug(sp: span, msg: str) -> ! { + self.span_fatal(sp, ice_msg(msg)); + } + fn span_unimpl(sp: span, msg: str) -> ! { + self.span_bug(sp, "unimplemented " + msg); + } + fn handler() -> handler { + self.handler + } +} + +impl codemap_handler of handler for handler_t { + fn fatal(msg: str) -> ! { + self._emit(none, msg, fatal); + fail; + } + fn err(msg: str) { + self._emit(none, msg, error); + self.bump_err_count(); + } + fn bump_err_count() { + self.err_count += 1u; + } + fn has_errors() -> bool { self.err_count > 0u } + fn abort_if_errors() { + if self.err_count > 0u { + self.fatal("aborting due to previous errors"); + } + } + fn warn(msg: str) { + self._emit(none, msg, warning); + } + fn note(msg: str) { + self._emit(none, msg, note); + } + fn bug(msg: str) -> ! { + self.fatal(ice_msg(msg)); + } + fn unimpl(msg: str) -> ! { self.bug("unimplemented " + msg); } + fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level) { + self._emit(cmsp, msg, lvl); + } +} + +fn ice_msg(msg: str) -> str { + #fmt["internal compiler error %s", msg] +} + +fn mk_span_handler(handler: handler, cm: codemap::codemap) -> span_handler { + @{ handler: handler, cm: cm } as span_handler +} + +fn mk_handler(emitter: option<emitter>) -> handler { + + let emit = alt emitter { + some(e) { e } + none { + let f = fn@(cmsp: option<(codemap::codemap, span)>, + msg: str, t: level) { + emit(cmsp, msg, t); + }; + f + } + }; + + @{ + mutable err_count: 0u, + _emit: emit + } as handler +} + +enum level { + fatal, + error, + warning, + note, +} + +fn diagnosticstr(lvl: level) -> str { + alt lvl { + fatal { "error" } + error { "error" } + warning { "warning" } + note { "note" } + } +} + +fn diagnosticcolor(lvl: level) -> u8 { + alt lvl { + fatal { term::color_bright_red } + error { term::color_bright_red } + warning { term::color_bright_yellow } + note { term::color_bright_green } + } +} + +fn print_diagnostic(topic: str, lvl: level, msg: str) { + if str::is_not_empty(topic) { + io::stderr().write_str(#fmt["%s ", topic]); + } + if term::color_supported() { + term::fg(io::stderr(), diagnosticcolor(lvl)); + } + io::stderr().write_str(#fmt["%s:", diagnosticstr(lvl)]); + if term::color_supported() { + term::reset(io::stderr()); + } + io::stderr().write_str(#fmt[" %s\n", msg]); +} + +fn emit(cmsp: option<(codemap::codemap, span)>, + msg: str, lvl: level) { + alt cmsp { + some((cm, sp)) { + let sp = codemap::adjust_span(cm,sp); + let ss = codemap::span_to_str(sp, cm); + let lines = codemap::span_to_lines(sp, cm); + print_diagnostic(ss, lvl, msg); + highlight_lines(cm, sp, lines); + print_macro_backtrace(cm, sp); + } + none { + print_diagnostic("", lvl, msg); + } + } +} + +fn highlight_lines(cm: codemap::codemap, sp: span, + lines: @codemap::file_lines) { + + let fm = lines.file; + + // arbitrarily only print up to six lines of the error + let max_lines = 6u; + let elided = false; + let display_lines = lines.lines; + if vec::len(display_lines) > max_lines { + display_lines = vec::slice(display_lines, 0u, max_lines); + elided = true; + } + // Print the offending lines + for line: uint in display_lines { + io::stderr().write_str(#fmt["%s:%u ", fm.name, line + 1u]); + let s = codemap::get_line(fm, line as int) + "\n"; + io::stderr().write_str(s); + } + if elided { + let last_line = display_lines[vec::len(display_lines) - 1u]; + let s = #fmt["%s:%u ", fm.name, last_line + 1u]; + let indent = str::len(s); + let out = ""; + while indent > 0u { out += " "; indent -= 1u; } + out += "...\n"; + io::stderr().write_str(out); + } + + + // If there's one line at fault we can easily point to the problem + if vec::len(lines.lines) == 1u { + let lo = codemap::lookup_char_pos(cm, sp.lo); + let digits = 0u; + let num = (lines.lines[0] + 1u) / 10u; + + // how many digits must be indent past? + while num > 0u { num /= 10u; digits += 1u; } + + // indent past |name:## | and the 0-offset column location + let left = str::len(fm.name) + digits + lo.col + 3u; + let s = ""; + while left > 0u { str::push_char(s, ' '); left -= 1u; } + + s += "^"; + let hi = codemap::lookup_char_pos(cm, sp.hi); + if hi.col != lo.col { + // the ^ already takes up one space + let width = hi.col - lo.col - 1u; + while width > 0u { str::push_char(s, '~'); width -= 1u; } + } + io::stderr().write_str(s + "\n"); + } +} + +fn print_macro_backtrace(cm: codemap::codemap, sp: span) { + option::may (sp.expn_info) {|ei| + let ss = option::maybe("", ei.callie.span, + bind codemap::span_to_str(_, cm)); + print_diagnostic(ss, note, + #fmt("in expansion of #%s", ei.callie.name)); + let ss = codemap::span_to_str(ei.call_site, cm); + print_diagnostic(ss, note, "expansion site"); + print_macro_backtrace(cm, ei.call_site); + } +} diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs new file mode 100644 index 00000000000..4aaabc667ba --- /dev/null +++ b/src/rustc/driver/driver.rs @@ -0,0 +1,667 @@ + +// -*- rust -*- +import metadata::{creader, cstore}; +import session::session; +import syntax::parse::{parser}; +import syntax::{ast, codemap}; +import front::attr; +import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage, + last_use, lint, inline}; +import syntax::print::{pp, pprust}; +import util::{ppaux, filesearch}; +import back::link; +import result::{ok, err}; +import std::{fs, io, getopts}; +import io::{reader_util, writer_util}; +import getopts::{optopt, optmulti, optflag, optflagopt, opt_present}; +import back::{x86, x86_64}; + +enum pp_mode { ppm_normal, ppm_expanded, ppm_typed, ppm_identified, } + +fn default_configuration(sess: session, argv0: str, input: str) -> + ast::crate_cfg { + let libc = alt sess.targ_cfg.os { + session::os_win32 { "msvcrt.dll" } + session::os_macos { "libc.dylib" } + session::os_linux { "libc.so.6" } + session::os_freebsd { "libc.so.7" } + _ { "libc.so" } + }; + + let mk = attr::mk_name_value_item_str; + + let arch = alt sess.targ_cfg.arch { + session::arch_x86 { "x86" } + session::arch_x86_64 { "x86_64" } + session::arch_arm { "arm" } + }; + + ret [ // Target bindings. + mk("target_os", std::os::target_os()), + mk("target_arch", arch), + mk("target_libc", libc), + // Build bindings. + mk("build_compiler", argv0), + mk("build_input", input)]; +} + +fn build_configuration(sess: session, argv0: str, input: str) -> + ast::crate_cfg { + // Combine the configuration requested by the session (command line) with + // some default and generated configuration items + let default_cfg = default_configuration(sess, argv0, input); + let user_cfg = sess.opts.cfg; + // If the user wants a test runner, then add the test cfg + let gen_cfg = + { + if sess.opts.test && !attr::contains_name(user_cfg, "test") + { + [attr::mk_word_item("test")] + } else { [] } + }; + ret user_cfg + gen_cfg + default_cfg; +} + +// Convert strings provided as --cfg [cfgspec] into a crate_cfg +fn parse_cfgspecs(cfgspecs: [str]) -> ast::crate_cfg { + // FIXME: It would be nice to use the parser to parse all varieties of + // meta_item here. At the moment we just support the meta_word variant. + let words = []; + for s: str in cfgspecs { words += [attr::mk_word_item(s)]; } + ret words; +} + +fn input_is_stdin(filename: str) -> bool { filename == "-" } + +fn parse_input(sess: session, cfg: ast::crate_cfg, input: str) + -> @ast::crate { + if !input_is_stdin(input) { + parser::parse_crate_from_file(input, cfg, sess.parse_sess) + } else { + let src = @str::from_bytes(io::stdin().read_whole_stream()); + parser::parse_crate_from_source_str(input, src, cfg, sess.parse_sess) + } +} + +fn time<T>(do_it: bool, what: str, thunk: fn@() -> T) -> T { + if !do_it { ret thunk(); } + let start = std::time::precise_time_s(); + let rv = thunk(); + let end = std::time::precise_time_s(); + io::stdout().write_str(#fmt("time: %3.3f s\t%s\n", + end - start, what)); + ret rv; +} + +enum compile_upto { + cu_parse, + cu_expand, + cu_typeck, + cu_no_trans, + cu_everything, +} + +fn compile_upto(sess: session, cfg: ast::crate_cfg, + input: str, upto: compile_upto, + outputs: option<output_filenames>) + -> {crate: @ast::crate, tcx: option<ty::ctxt>} { + let time_passes = sess.opts.time_passes; + let crate = time(time_passes, "parsing", + bind parse_input(sess, cfg, input)); + if upto == cu_parse { ret {crate: crate, tcx: none}; } + + sess.building_library = session::building_library( + sess.opts.crate_type, crate, sess.opts.test); + + crate = + time(time_passes, "configuration", + bind front::config::strip_unconfigured_items(crate)); + crate = + time(time_passes, "maybe building test harness", + bind front::test::modify_for_testing(sess, crate)); + crate = + time(time_passes, "expansion", + bind syntax::ext::expand::expand_crate(sess, crate)); + + if upto == cu_expand { ret {crate: crate, tcx: none}; } + + crate = + time(time_passes, "core injection", + bind front::core_inject::maybe_inject_libcore_ref(sess, crate)); + + let ast_map = + time(time_passes, "ast indexing", + bind middle::ast_map::map_crate(*crate)); + time(time_passes, "external crate/lib resolution", + bind creader::read_crates(sess, *crate)); + let {def_map, exp_map, impl_map} = + time(time_passes, "resolution", + bind resolve::resolve_crate(sess, ast_map, crate)); + let freevars = + time(time_passes, "freevar finding", + bind freevars::annotate_freevars(def_map, crate)); + let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars); + let (method_map, dict_map) = + time(time_passes, "typechecking", + bind typeck::check_crate(ty_cx, impl_map, crate)); + time(time_passes, "const checking", + bind middle::check_const::check_crate(sess, crate, method_map)); + + if upto == cu_typeck { ret {crate: crate, tcx: some(ty_cx)}; } + + time(time_passes, "block-use checking", + bind middle::block_use::check_crate(ty_cx, crate)); + time(time_passes, "function usage", + bind fn_usage::check_crate_fn_usage(ty_cx, crate)); + time(time_passes, "alt checking", + bind middle::check_alt::check_crate(ty_cx, crate)); + time(time_passes, "typestate checking", + bind middle::tstate::ck::check_crate(ty_cx, crate)); + let mutbl_map = + time(time_passes, "mutability checking", + bind middle::mutbl::check_crate(ty_cx, crate)); + let (copy_map, ref_map) = + time(time_passes, "alias checking", + bind middle::alias::check_crate(ty_cx, crate)); + let last_uses = time(time_passes, "last use finding", + bind last_use::find_last_uses(crate, def_map, ref_map, ty_cx)); + time(time_passes, "kind checking", + bind kind::check_crate(ty_cx, method_map, last_uses, crate)); + + lint::check_crate(ty_cx, crate, sess.opts.lint_opts, time_passes); + + if upto == cu_no_trans { ret {crate: crate, tcx: some(ty_cx)}; } + let outputs = option::get(outputs); + + let maps = {mutbl_map: mutbl_map, copy_map: copy_map, + last_uses: last_uses, impl_map: impl_map, + method_map: method_map, dict_map: dict_map}; + + let ienbld = sess.opts.inline; + let inline_map = + time(time_passes, "inline", + bind inline::instantiate_inlines(ienbld, ty_cx, maps, crate)); + + let (llmod, link_meta) = + time(time_passes, "translation", + bind trans::base::trans_crate( + sess, crate, ty_cx, outputs.obj_filename, + exp_map, maps, inline_map)); + time(time_passes, "LLVM passes", + bind link::write::run_passes(sess, llmod, outputs.obj_filename)); + + let stop_after_codegen = + sess.opts.output_type != link::output_type_exe || + sess.opts.static && sess.building_library; + + if stop_after_codegen { ret {crate: crate, tcx: some(ty_cx)}; } + + time(time_passes, "Linking", + bind link::link_binary(sess, outputs.obj_filename, + outputs.out_filename, link_meta)); + ret {crate: crate, tcx: some(ty_cx)}; +} + +fn compile_input(sess: session, cfg: ast::crate_cfg, input: str, + outdir: option<str>, output: option<str>) { + + let upto = if sess.opts.parse_only { cu_parse } + else if sess.opts.no_trans { cu_no_trans } + else { cu_everything }; + let outputs = build_output_filenames(input, outdir, output, sess); + compile_upto(sess, cfg, input, upto, some(outputs)); +} + +fn pretty_print_input(sess: session, cfg: ast::crate_cfg, input: str, + ppm: pp_mode) { + fn ann_paren_for_expr(node: pprust::ann_node) { + alt node { pprust::node_expr(s, expr) { pprust::popen(s); } _ { } } + } + fn ann_typed_post(tcx: ty::ctxt, node: pprust::ann_node) { + alt node { + pprust::node_expr(s, expr) { + pp::space(s.s); + pp::word(s.s, "as"); + pp::space(s.s); + pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))); + pprust::pclose(s); + } + _ { } + } + } + fn ann_identified_post(node: pprust::ann_node) { + alt node { + pprust::node_item(s, item) { + pp::space(s.s); + pprust::synth_comment(s, int::to_str(item.id, 10u)); + } + pprust::node_block(s, blk) { + pp::space(s.s); + pprust::synth_comment(s, + "block " + int::to_str(blk.node.id, 10u)); + } + pprust::node_expr(s, expr) { + pp::space(s.s); + pprust::synth_comment(s, int::to_str(expr.id, 10u)); + pprust::pclose(s); + } + _ { } + } + } + + // Because the pretty printer needs to make a pass over the source + // to collect comments and literals, and we need to support reading + // from stdin, we're going to just suck the source into a string + // so both the parser and pretty-printer can use it. + let upto = alt ppm { + ppm_expanded { cu_expand } + ppm_typed { cu_typeck } + _ { cu_parse } + }; + let {crate, tcx} = compile_upto(sess, cfg, input, upto, none); + + let ann: pprust::pp_ann = pprust::no_ann(); + alt ppm { + ppm_typed { + ann = {pre: ann_paren_for_expr, + post: bind ann_typed_post(option::get(tcx), _)}; + } + ppm_identified { + ann = {pre: ann_paren_for_expr, post: ann_identified_post}; + } + ppm_expanded | ppm_normal {} + } + let src = codemap::get_filemap(sess.codemap, input).src; + pprust::print_crate(sess.codemap, sess.span_diagnostic, crate, input, + io::string_reader(*src), io::stdout(), ann); +} + +fn get_os(triple: str) -> option<session::os> { + ret if str::contains(triple, "win32") || + str::contains(triple, "mingw32") { + some(session::os_win32) + } else if str::contains(triple, "darwin") { + some(session::os_macos) + } else if str::contains(triple, "linux") { + some(session::os_linux) + } else if str::contains(triple, "freebsd") { + some(session::os_freebsd) + } else { none }; +} + +fn get_arch(triple: str) -> option<session::arch> { + ret if str::contains(triple, "i386") || str::contains(triple, "i486") || + str::contains(triple, "i586") || + str::contains(triple, "i686") || + str::contains(triple, "i786") { + some(session::arch_x86) + } else if str::contains(triple, "x86_64") { + some(session::arch_x86_64) + } else if str::contains(triple, "arm") || + str::contains(triple, "xscale") { + some(session::arch_arm) + } else { none }; +} + +fn build_target_config(sopts: @session::options, + demitter: diagnostic::emitter) -> @session::config { + let os = alt get_os(sopts.target_triple) { + some(os) { os } + none { early_error(demitter, "Unknown operating system!") } + }; + let arch = alt get_arch(sopts.target_triple) { + some(arch) { arch } + none { early_error(demitter, + "Unknown architecture! " + sopts.target_triple) } + }; + let (int_type, uint_type, float_type) = alt arch { + session::arch_x86 {(ast::ty_i32, ast::ty_u32, ast::ty_f64)} + session::arch_x86_64 {(ast::ty_i64, ast::ty_u64, ast::ty_f64)} + session::arch_arm {(ast::ty_i32, ast::ty_u32, ast::ty_f64)} + }; + let target_strs = alt arch { + session::arch_x86 {x86::get_target_strs(os)} + session::arch_x86_64 {x86_64::get_target_strs(os)} + session::arch_arm {x86::get_target_strs(os)} + }; + let target_cfg: @session::config = + @{os: os, arch: arch, target_strs: target_strs, int_type: int_type, + uint_type: uint_type, float_type: float_type}; + ret target_cfg; +} + +fn host_triple() -> str { + // Get the host triple out of the build environment. This ensures that our + // idea of the host triple is the same as for the set of libraries we've + // actually built. We can't just take LLVM's host triple because they + // normalize all ix86 architectures to i386. + // FIXME: Instead of grabbing the host triple we really should be + // grabbing (at compile time) the target triple that this rustc is + // built with and calling that (at runtime) the host triple. + let ht = #env("CFG_HOST_TRIPLE"); + ret if ht != "" { + ht + } else { + fail "rustc built without CFG_HOST_TRIPLE" + }; +} + +fn build_session_options(match: getopts::match, + demitter: diagnostic::emitter) -> @session::options { + let crate_type = if opt_present(match, "lib") { + session::lib_crate + } else if opt_present(match, "bin") { + session::bin_crate + } else { + session::unknown_crate + }; + let static = opt_present(match, "static"); + + let parse_only = opt_present(match, "parse-only"); + let no_trans = opt_present(match, "no-trans"); + let lint_opts = []; + if opt_present(match, "no-lint-ctypes") { + lint_opts += [(lint::ctypes, false)]; + } + let monomorphize = opt_present(match, "monomorphize"); + let inline = opt_present(match, "inline"); + + let output_type = + if parse_only || no_trans { + link::output_type_none + } else if opt_present(match, "S") && opt_present(match, "emit-llvm") { + link::output_type_llvm_assembly + } else if opt_present(match, "S") { + link::output_type_assembly + } else if opt_present(match, "c") { + link::output_type_object + } else if opt_present(match, "emit-llvm") { + link::output_type_bitcode + } else { link::output_type_exe }; + let verify = !opt_present(match, "no-verify"); + let save_temps = opt_present(match, "save-temps"); + let extra_debuginfo = opt_present(match, "xg"); + let debuginfo = opt_present(match, "g") || extra_debuginfo; + let stats = opt_present(match, "stats"); + let time_passes = opt_present(match, "time-passes"); + let time_llvm_passes = opt_present(match, "time-llvm-passes"); + let sysroot_opt = getopts::opt_maybe_str(match, "sysroot"); + let target_opt = getopts::opt_maybe_str(match, "target"); + let no_asm_comments = getopts::opt_present(match, "no-asm-comments"); + alt output_type { + // unless we're emitting huamn-readable assembly, omit comments. + link::output_type_llvm_assembly | link::output_type_assembly {} + _ { no_asm_comments = true; } + } + let opt_level: uint = + if opt_present(match, "O") { + if opt_present(match, "opt-level") { + early_error(demitter, "-O and --opt-level both provided"); + } + 2u + } else if opt_present(match, "opt-level") { + alt getopts::opt_str(match, "opt-level") { + "0" { 0u } + "1" { 1u } + "2" { 2u } + "3" { 3u } + _ { + early_error(demitter, "optimization level needs " + + "to be between 0-3") + } + } + } else { 0u }; + let target = + alt target_opt { + none { host_triple() } + some(s) { s } + }; + + let addl_lib_search_paths = getopts::opt_strs(match, "L"); + let cfg = parse_cfgspecs(getopts::opt_strs(match, "cfg")); + let test = opt_present(match, "test"); + let warn_unused_imports = opt_present(match, "warn-unused-imports"); + let enforce_mut_vars = opt_present(match, "enforce-mut-vars"); + let sopts: @session::options = + @{crate_type: crate_type, + static: static, + optimize: opt_level, + debuginfo: debuginfo, + extra_debuginfo: extra_debuginfo, + verify: verify, + lint_opts: lint_opts, + save_temps: save_temps, + stats: stats, + time_passes: time_passes, + time_llvm_passes: time_llvm_passes, + output_type: output_type, + addl_lib_search_paths: addl_lib_search_paths, + maybe_sysroot: sysroot_opt, + target_triple: target, + cfg: cfg, + test: test, + parse_only: parse_only, + no_trans: no_trans, + no_asm_comments: no_asm_comments, + monomorphize: monomorphize, + inline: inline, + warn_unused_imports: warn_unused_imports, + enforce_mut_vars: enforce_mut_vars}; + ret sopts; +} + +fn build_session(sopts: @session::options, input: str, + demitter: diagnostic::emitter) -> session { + let codemap = codemap::new_codemap(); + let diagnostic_handler = + diagnostic::mk_handler(some(demitter)); + let span_diagnostic_handler = + diagnostic::mk_span_handler(diagnostic_handler, codemap); + build_session_(sopts, input, codemap, demitter, + span_diagnostic_handler) +} + +fn build_session_( + sopts: @session::options, input: str, + codemap: codemap::codemap, + demitter: diagnostic::emitter, + span_diagnostic_handler: diagnostic::span_handler +) -> session { + let target_cfg = build_target_config(sopts, demitter); + let cstore = cstore::mk_cstore(); + let filesearch = filesearch::mk_filesearch( + sopts.maybe_sysroot, + sopts.target_triple, + sopts.addl_lib_search_paths); + @{targ_cfg: target_cfg, + opts: sopts, + cstore: cstore, + parse_sess: @{ + cm: codemap, + mutable next_id: 1, + span_diagnostic: span_diagnostic_handler, + mutable chpos: 0u, + mutable byte_pos: 0u + }, + codemap: codemap, + // For a library crate, this is always none + mutable main_fn: none, + span_diagnostic: span_diagnostic_handler, + filesearch: filesearch, + mutable building_library: false, + working_dir: fs::dirname(input)} +} + +fn parse_pretty(sess: session, &&name: str) -> pp_mode { + if str::eq(name, "normal") { + ret ppm_normal; + } else if str::eq(name, "expanded") { + ret ppm_expanded; + } else if str::eq(name, "typed") { + ret ppm_typed; + } else if str::eq(name, "identified") { ret ppm_identified; } + sess.fatal("argument to `pretty` must be one of `normal`, `typed`, or " + + "`identified`"); +} + +fn opts() -> [getopts::opt] { + ret [optflag("h"), optflag("help"), optflag("v"), optflag("version"), + optflag("emit-llvm"), optflagopt("pretty"), + optflag("ls"), optflag("parse-only"), optflag("no-trans"), + optflag("O"), optopt("opt-level"), optmulti("L"), optflag("S"), + optopt("o"), optopt("out-dir"), optflag("xg"), + optflag("c"), optflag("g"), optflag("save-temps"), + optopt("sysroot"), optopt("target"), optflag("stats"), + optflag("time-passes"), optflag("time-llvm-passes"), + optflag("no-verify"), + optflag("no-lint-ctypes"), + optflag("monomorphize"), + optflag("inline"), + optmulti("cfg"), optflag("test"), + optflag("lib"), optflag("bin"), optflag("static"), optflag("gc"), + optflag("no-asm-comments"), + optflag("warn-unused-imports"), + optflag("enforce-mut-vars")]; +} + +type output_filenames = @{out_filename: str, obj_filename:str}; + +fn build_output_filenames(ifile: str, + odir: option<str>, + ofile: option<str>, + sess: session) + -> output_filenames { + let obj_path = ""; + let out_path: str = ""; + let sopts = sess.opts; + let stop_after_codegen = + sopts.output_type != link::output_type_exe || + sopts.static && sess.building_library; + + + let obj_suffix = + alt sopts.output_type { + link::output_type_none { "none" } + link::output_type_bitcode { "bc" } + link::output_type_assembly { "s" } + link::output_type_llvm_assembly { "ll" } + // Object and exe output both use the '.o' extension here + link::output_type_object | link::output_type_exe { + "o" + } + }; + + alt ofile { + none { + // "-" as input file will cause the parser to read from stdin so we + // have to make up a name + // We want to toss everything after the final '.' + let dirname = alt odir { + some(d) { d } + none { + if input_is_stdin(ifile) { + std::os::getcwd() + } else { + fs::dirname(ifile) + } + } + }; + + let base_filename = if !input_is_stdin(ifile) { + let (path, _) = fs::splitext(ifile); + fs::basename(path) + } else { + "rust_out" + }; + let base_path = fs::connect(dirname, base_filename); + + + if sess.building_library { + let basename = fs::basename(base_path); + let dylibname = std::os::dylib_filename(basename); + out_path = fs::connect(dirname, dylibname); + obj_path = fs::connect(dirname, basename + "." + obj_suffix); + } else { + out_path = base_path; + obj_path = base_path + "." + obj_suffix; + } + } + + some(out_file) { + out_path = out_file; + obj_path = if stop_after_codegen { + out_file + } else { + let (base, _) = fs::splitext(out_file); + let modified = base + "." + obj_suffix; + modified + }; + + if sess.building_library { + // FIXME: We might want to warn here; we're actually not going to + // respect the user's choice of library name when it comes time to + // link, we'll be linking to lib<basename>-<hash>-<version>.so no + // matter what. + } + + if odir != none { + sess.warn("Ignoring --out-dir flag due to -o flag."); + } + } + } + ret @{out_filename: out_path, + obj_filename: obj_path}; +} + +fn early_error(emitter: diagnostic::emitter, msg: str) -> ! { + emitter(none, msg, diagnostic::fatal); + fail; +} + +fn list_metadata(sess: session, path: str, out: io::writer) { + metadata::creader::list_file_metadata(sess, path, out); +} + +#[cfg(test)] +mod test { + + // When the user supplies --test we should implicitly supply --cfg test + #[test] + fn test_switch_implies_cfg_test() { + let match = + alt getopts::getopts(["--test"], opts()) { + ok(m) { m } + err(f) { fail "test_switch_implies_cfg_test: " + + getopts::fail_str(f); } + }; + let sessopts = build_session_options(match, diagnostic::emit); + let sess = build_session(sessopts, "", diagnostic::emit); + let cfg = build_configuration(sess, "whatever", "whatever"); + assert (attr::contains_name(cfg, "test")); + } + + // When the user supplies --test and --cfg test, don't implicitly add + // another --cfg test + #[test] + fn test_switch_implies_cfg_test_unless_cfg_test() { + let match = + alt getopts::getopts(["--test", "--cfg=test"], opts()) { + ok(m) { m } + err(f) { fail "test_switch_implies_cfg_test_unless_cfg_test: " + + getopts::fail_str(f); } + }; + let sessopts = build_session_options(match, diagnostic::emit); + let sess = build_session(sessopts, "", diagnostic::emit); + let cfg = build_configuration(sess, "whatever", "whatever"); + let test_items = attr::find_meta_items_by_name(cfg, "test"); + assert (vec::len(test_items) == 1u); + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/driver/rustc.rs b/src/rustc/driver/rustc.rs new file mode 100644 index 00000000000..8d0b8549062 --- /dev/null +++ b/src/rustc/driver/rustc.rs @@ -0,0 +1,197 @@ +use std; +use rustc; + +// -*- rust -*- +import result::{ok, err}; +import std::{io, getopts}; +import io::writer_util; +import getopts::{opt_present}; +import rustc::driver::driver::*; +import rustc::syntax::codemap; +import rustc::driver::diagnostic; + +fn version(argv0: str) { + let vers = "unknown version"; + let env_vers = #env["CFG_VERSION"]; + if str::len(env_vers) != 0u { vers = env_vers; } + io::stdout().write_str(#fmt["%s %s\n", argv0, vers]); + io::stdout().write_str(#fmt["host: %s\n", host_triple()]); +} + +fn usage(argv0: str) { + io::stdout().write_str(#fmt["Usage: %s [options] <input>\n", argv0] + + " +Options: + + --bin Compile an executable crate (default) + -c Compile and assemble, but do not link + --cfg <cfgspec> Configure the compilation environment + --emit-llvm Produce an LLVM bitcode file + -g Produce debug info + --gc Garbage collect shared data (experimental/temporary) + -h --help Display this message + -L <path> Add a directory to the library search path + --lib Compile a library crate + --ls List the symbols defined by a compiled library crate + --no-asm-comments Do not add comments into the assembly source + --no-lint-ctypes Suppress warnings for possibly incorrect ctype usage + --no-trans Run all passes except translation; no output + --no-verify Suppress LLVM verification step (slight speedup) + (see http://llvm.org/docs/Passes.html for detail) + -O Equivalent to --opt-level=2 + -o <filename> Write output to <filename> + --opt-level <lvl> Optimize with possible levels 0-3 + --out-dir <dir> Write output to compiler-chosen filename in <dir> + --parse-only Parse only; do not compile, assemble, or link + --pretty [type] Pretty-print the input instead of compiling; + valid types are: normal (un-annotated source), + expanded (crates expanded), typed (crates expanded, + with type annotations), or identified (fully + parenthesized, AST nodes and blocks with IDs) + -S Compile only; do not assemble or link + --save-temps Write intermediate files (.bc, .opt.bc, .o) + in addition to normal output + --static Use or produce static libraries or binaries + --stats Print compilation statistics + --sysroot <path> Override the system root + --test Build a test harness + --target <triple> Target cpu-manufacturer-kernel[-os] to compile for + (default: host triple) + (see http://sources.redhat.com/autobook/autobook/ + autobook_17.html for detail) + + --time-passes Time the individual phases of the compiler + --time-llvm-passes Time the individual phases of the LLVM backend + -v --version Print version info and exit + --warn-unused-imports + Warn about unnecessary imports + +"); +} + +fn run_compiler(args: [str], demitter: diagnostic::emitter) { + // Don't display log spew by default. Can override with RUST_LOG. + logging::console_off(); + + let args = args, binary = vec::shift(args); + + if vec::len(args) == 0u { usage(binary); ret; } + + let match = + alt getopts::getopts(args, opts()) { + ok(m) { m } + err(f) { + early_error(demitter, getopts::fail_str(f)) + } + }; + if opt_present(match, "h") || opt_present(match, "help") { + usage(binary); + ret; + } + if opt_present(match, "v") || opt_present(match, "version") { + version(binary); + ret; + } + let ifile = alt vec::len(match.free) { + 0u { early_error(demitter, "No input filename given.") } + 1u { match.free[0] } + _ { early_error(demitter, "Multiple input filenames provided.") } + }; + + let sopts = build_session_options(match, demitter); + let sess = build_session(sopts, ifile, demitter); + let odir = getopts::opt_maybe_str(match, "out-dir"); + let ofile = getopts::opt_maybe_str(match, "o"); + let cfg = build_configuration(sess, binary, ifile); + let pretty = + option::map(getopts::opt_default(match, "pretty", + "normal"), + bind parse_pretty(sess, _)); + alt pretty { + some::<pp_mode>(ppm) { pretty_print_input(sess, cfg, ifile, ppm); ret; } + none::<pp_mode> {/* continue */ } + } + let ls = opt_present(match, "ls"); + if ls { + list_metadata(sess, ifile, io::stdout()); + ret; + } + + compile_input(sess, cfg, ifile, odir, ofile); +} + +/* +This is a sanity check that any failure of the compiler is performed +through the diagnostic module and reported properly - we shouldn't be calling +plain-old-fail on any execution path that might be taken. Since we have +console logging off by default, hitting a plain fail statement would make the +compiler silently exit, which would be terrible. + +This method wraps the compiler in a subtask and injects a function into the +diagnostic emitter which records when we hit a fatal error. If the task +fails without recording a fatal error then we've encountered a compiler +bug and need to present an error. +*/ +fn monitor(f: fn~(diagnostic::emitter)) { + enum monitor_msg { + fatal, + done, + }; + + let p = comm::port(); + let ch = comm::chan(p); + + alt task::try {|| + + // The 'diagnostics emitter'. Every error, warning, etc. should + // go through this function. + let demitter = fn@(cmsp: option<(codemap::codemap, codemap::span)>, + msg: str, lvl: diagnostic::level) { + if lvl == diagnostic::fatal { + comm::send(ch, fatal); + } + diagnostic::emit(cmsp, msg, lvl); + }; + + resource finally(ch: comm::chan<monitor_msg>) { + comm::send(ch, done); + } + + let _finally = finally(ch); + + f(demitter) + } { + result::ok(_) { /* fallthrough */ } + result::err(_) { + // Task failed without emitting a fatal diagnostic + if comm::recv(p) == done { + diagnostic::emit( + none, + diagnostic::ice_msg("unexpected failure"), + diagnostic::error); + let note = "The compiler hit an unexpected failure path. \ + This is a bug. Try running with \ + RUST_LOG=rustc=0,::rt::backtrace \ + to get further details and report the results \ + to github.com/mozilla/rust/issues"; + diagnostic::emit(none, note, diagnostic::note); + } + // Fail so the process returns a failure code + fail; + } + } +} + +fn main(args: [str]) { + monitor {|demitter| + run_compiler(args, demitter); + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs new file mode 100644 index 00000000000..ec2182d2929 --- /dev/null +++ b/src/rustc/driver/session.rs @@ -0,0 +1,210 @@ + +import syntax::{ast, codemap}; +import syntax::ast::node_id; +import codemap::span; +import syntax::ast::{int_ty, uint_ty, float_ty}; +import syntax::parse::parser::parse_sess; +import util::filesearch; +import back::target_strs; +import middle::lint; + +enum os { os_win32, os_macos, os_linux, os_freebsd, } + +enum arch { arch_x86, arch_x86_64, arch_arm, } + +enum crate_type { bin_crate, lib_crate, unknown_crate, } + +type config = + {os: os, + arch: arch, + target_strs: target_strs::t, + int_type: int_ty, + uint_type: uint_ty, + float_type: float_ty}; + +type options = + // The crate config requested for the session, which may be combined + // with additional crate configurations during the compile process + {crate_type: crate_type, + static: bool, + optimize: uint, + debuginfo: bool, + extra_debuginfo: bool, + verify: bool, + lint_opts: [(lint::option, bool)], + save_temps: bool, + stats: bool, + time_passes: bool, + time_llvm_passes: bool, + output_type: back::link::output_type, + addl_lib_search_paths: [str], + maybe_sysroot: option<str>, + target_triple: str, + cfg: ast::crate_cfg, + test: bool, + parse_only: bool, + no_trans: bool, + no_asm_comments: bool, + monomorphize: bool, + inline: bool, + warn_unused_imports: bool, + enforce_mut_vars: bool}; + +type crate_metadata = {name: str, data: [u8]}; + +type session = @{targ_cfg: @config, + opts: @options, + cstore: metadata::cstore::cstore, + parse_sess: parse_sess, + codemap: codemap::codemap, + // For a library crate, this is always none + mutable main_fn: option<(node_id, codemap::span)>, + span_diagnostic: diagnostic::span_handler, + filesearch: filesearch::filesearch, + mutable building_library: bool, + working_dir: str}; + +impl session for session { + fn span_fatal(sp: span, msg: str) -> ! { + self.span_diagnostic.span_fatal(sp, msg) + } + fn fatal(msg: str) -> ! { + self.span_diagnostic.handler().fatal(msg) + } + fn span_err(sp: span, msg: str) { + self.span_diagnostic.span_err(sp, msg) + } + fn err(msg: str) { + self.span_diagnostic.handler().err(msg) + } + fn has_errors() -> bool { + self.span_diagnostic.handler().has_errors() + } + fn abort_if_errors() { + self.span_diagnostic.handler().abort_if_errors() + } + fn span_warn(sp: span, msg: str) { + self.span_diagnostic.span_warn(sp, msg) + } + fn warn(msg: str) { + self.span_diagnostic.handler().warn(msg) + } + fn span_note(sp: span, msg: str) { + self.span_diagnostic.span_note(sp, msg) + } + fn note(msg: str) { + self.span_diagnostic.handler().note(msg) + } + fn span_bug(sp: span, msg: str) -> ! { + self.span_diagnostic.span_bug(sp, msg) + } + fn bug(msg: str) -> ! { + self.span_diagnostic.handler().bug(msg) + } + fn span_unimpl(sp: span, msg: str) -> ! { + self.span_diagnostic.span_unimpl(sp, msg) + } + fn unimpl(msg: str) -> ! { + self.span_diagnostic.handler().unimpl(msg) + } + fn next_node_id() -> ast::node_id { + ret syntax::parse::parser::next_node_id(self.parse_sess); + } +} + +fn building_library(req_crate_type: crate_type, crate: @ast::crate, + testing: bool) -> bool { + alt req_crate_type { + bin_crate { false } + lib_crate { true } + unknown_crate { + if testing { + false + } else { + alt front::attr::get_meta_item_value_str_by_name( + crate.node.attrs, + "crate_type") { + option::some("lib") { true } + _ { false } + } + } + } + } +} + +#[cfg(test)] +mod test { + import syntax::ast_util; + + fn make_crate_type_attr(t: str) -> ast::attribute { + ast_util::respan(ast_util::dummy_sp(), { + style: ast::attr_outer, + value: ast_util::respan(ast_util::dummy_sp(), + ast::meta_name_value( + "crate_type", + ast_util::respan(ast_util::dummy_sp(), + ast::lit_str(t)))) + }) + } + + fn make_crate(with_bin: bool, with_lib: bool) -> @ast::crate { + let attrs = []; + if with_bin { attrs += [make_crate_type_attr("bin")]; } + if with_lib { attrs += [make_crate_type_attr("lib")]; } + @ast_util::respan(ast_util::dummy_sp(), { + directives: [], + module: {view_items: [], items: []}, + attrs: attrs, + config: [] + }) + } + + #[test] + fn bin_crate_type_attr_results_in_bin_output() { + let crate = make_crate(true, false); + assert !building_library(unknown_crate, crate, false); + } + + #[test] + fn lib_crate_type_attr_results_in_lib_output() { + let crate = make_crate(false, true); + assert building_library(unknown_crate, crate, false); + } + + #[test] + fn bin_option_overrides_lib_crate_type() { + let crate = make_crate(false, true); + assert !building_library(bin_crate, crate, false); + } + + #[test] + fn lib_option_overrides_bin_crate_type() { + let crate = make_crate(true, false); + assert building_library(lib_crate, crate, false); + } + + #[test] + fn bin_crate_type_is_default() { + let crate = make_crate(false, false); + assert !building_library(unknown_crate, crate, false); + } + + #[test] + fn test_option_overrides_lib_crate_type() { + let crate = make_crate(false, true); + assert !building_library(unknown_crate, crate, true); + } + + #[test] + fn test_option_does_not_override_requested_lib_type() { + let crate = make_crate(false, false); + assert building_library(lib_crate, crate, true); + } +} + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/front/attr.rs b/src/rustc/front/attr.rs new file mode 100644 index 00000000000..9a2aea62570 --- /dev/null +++ b/src/rustc/front/attr.rs @@ -0,0 +1,308 @@ +// Functions dealing with attributes and meta_items + +import std::map; +import syntax::{ast, ast_util}; +import driver::session::session; + +export attr_meta; +export attr_metas; +export find_linkage_metas; +export find_attrs_by_name; +export attrs_contains_name; +export find_meta_items_by_name; +export contains; +export contains_name; +export sort_meta_items; +export remove_meta_items_by_name; +export require_unique_names; +export get_attr_name; +export get_meta_item_name; +export get_meta_item_value_str; +export get_meta_item_value_str_by_name; +export get_meta_item_list; +export meta_item_value_from_list; +export meta_item_list_from_list; +export name_value_str_pair; +export mk_name_value_item_str; +export mk_name_value_item; +export mk_list_item; +export mk_word_item; +export mk_attr; +export native_abi; + +// From a list of crate attributes get only the meta_items that impact crate +// linkage +fn find_linkage_metas(attrs: [ast::attribute]) -> [@ast::meta_item] { + let metas: [@ast::meta_item] = []; + for attr: ast::attribute in find_attrs_by_name(attrs, "link") { + alt attr.node.value.node { + ast::meta_list(_, items) { metas += items; } + _ { #debug("ignoring link attribute that has incorrect type"); } + } + } + ret metas; +} + +// Search a list of attributes and return only those with a specific name +fn find_attrs_by_name(attrs: [ast::attribute], name: ast::ident) -> + [ast::attribute] { + let filter = ( + fn@(a: ast::attribute) -> option<ast::attribute> { + if get_attr_name(a) == name { + option::some(a) + } else { option::none } + } + ); + ret vec::filter_map(attrs, filter); +} + +fn attrs_contains_name(attrs: [ast::attribute], name: ast::ident) -> bool { + vec::is_not_empty(find_attrs_by_name(attrs, name)) +} + +fn get_attr_name(attr: ast::attribute) -> ast::ident { + get_meta_item_name(@attr.node.value) +} + +fn find_meta_items_by_name(metas: [@ast::meta_item], name: ast::ident) -> + [@ast::meta_item] { + let filter = fn@(&&m: @ast::meta_item) -> option<@ast::meta_item> { + if get_meta_item_name(m) == name { + option::some(m) + } else { option::none } + }; + ret vec::filter_map(metas, filter); +} + +fn get_meta_item_name(meta: @ast::meta_item) -> ast::ident { + alt meta.node { + ast::meta_word(n) { n } + ast::meta_name_value(n, _) { n } + ast::meta_list(n, _) { n } + } +} + +// Gets the string value if the meta_item is a meta_name_value variant +// containing a string, otherwise none +fn get_meta_item_value_str(meta: @ast::meta_item) -> option<str> { + alt meta.node { + ast::meta_name_value(_, v) { + alt v.node { ast::lit_str(s) { option::some(s) } _ { option::none } } + } + _ { option::none } + } +} + +fn get_meta_item_value_str_by_name(attrs: [ast::attribute], name: ast::ident) + -> option<str> { + let mattrs = find_attrs_by_name(attrs, name); + if vec::len(mattrs) > 0u { + ret get_meta_item_value_str(attr_meta(mattrs[0])); + } + ret option::none; +} + +fn get_meta_item_list(meta: @ast::meta_item) -> option<[@ast::meta_item]> { + alt meta.node { + ast::meta_list(_, l) { option::some(l) } + _ { option::none } + } +} + +fn attr_meta(attr: ast::attribute) -> @ast::meta_item { @attr.node.value } + +// Get the meta_items from inside a vector of attributes +fn attr_metas(attrs: [ast::attribute]) -> [@ast::meta_item] { + let mitems = []; + for a: ast::attribute in attrs { mitems += [attr_meta(a)]; } + ret mitems; +} + +fn eq(a: @ast::meta_item, b: @ast::meta_item) -> bool { + ret alt a.node { + ast::meta_word(na) { + alt b.node { ast::meta_word(nb) { na == nb } _ { false } } + } + ast::meta_name_value(na, va) { + alt b.node { + ast::meta_name_value(nb, vb) { na == nb && va.node == vb.node } + _ { false } + } + } + ast::meta_list(na, la) { + + // FIXME (#607): Needs implementing + // This involves probably sorting the list by name and + // meta_item variant + fail "unimplemented meta_item variant" + } + } +} + +fn contains(haystack: [@ast::meta_item], needle: @ast::meta_item) -> bool { + #debug("looking for %s", + syntax::print::pprust::meta_item_to_str(*needle)); + for item: @ast::meta_item in haystack { + #debug("looking in %s", + syntax::print::pprust::meta_item_to_str(*item)); + if eq(item, needle) { #debug("found it!"); ret true; } + } + #debug("found it not :("); + ret false; +} + +fn contains_name(metas: [@ast::meta_item], name: ast::ident) -> bool { + let matches = find_meta_items_by_name(metas, name); + ret vec::len(matches) > 0u; +} + +// FIXME: This needs to sort by meta_item variant in addition to the item name +fn sort_meta_items(items: [@ast::meta_item]) -> [@ast::meta_item] { + fn lteq(&&ma: @ast::meta_item, &&mb: @ast::meta_item) -> bool { + fn key(m: @ast::meta_item) -> ast::ident { + alt m.node { + ast::meta_word(name) { name } + ast::meta_name_value(name, _) { name } + ast::meta_list(name, _) { name } + } + } + ret key(ma) <= key(mb); + } + + // This is sort of stupid here, converting to a vec of mutables and back + let v: [mutable @ast::meta_item] = [mutable]; + for mi: @ast::meta_item in items { v += [mutable mi]; } + + std::sort::quick_sort(lteq, v); + + let v2: [@ast::meta_item] = []; + for mi: @ast::meta_item in v { v2 += [mi]; } + ret v2; +} + +fn remove_meta_items_by_name(items: [@ast::meta_item], name: str) -> + [@ast::meta_item] { + + let filter = fn@(&&item: @ast::meta_item) -> option<@ast::meta_item> { + if get_meta_item_name(item) != name { + option::some(item) + } else { option::none } + }; + + ret vec::filter_map(items, filter); +} + +fn require_unique_names(sess: session, metas: [@ast::meta_item]) { + let map = map::new_str_hash(); + for meta: @ast::meta_item in metas { + let name = get_meta_item_name(meta); + if map.contains_key(name) { + sess.span_fatal(meta.span, + #fmt["duplicate meta item `%s`", name]); + } + map.insert(name, ()); + } +} + +fn native_abi(attrs: [ast::attribute]) -> either::t<str, ast::native_abi> { + ret alt attr::get_meta_item_value_str_by_name(attrs, "abi") { + option::none { + either::right(ast::native_abi_cdecl) + } + option::some("rust-intrinsic") { + either::right(ast::native_abi_rust_intrinsic) + } + option::some("cdecl") { + either::right(ast::native_abi_cdecl) + } + option::some("stdcall") { + either::right(ast::native_abi_stdcall) + } + option::some(t) { + either::left("unsupported abi: " + t) + } + }; +} + +fn meta_item_from_list( + items: [@ast::meta_item], + name: str +) -> option<@ast::meta_item> { + let items = attr::find_meta_items_by_name(items, name); + vec::last(items) +} + +fn meta_item_value_from_list( + items: [@ast::meta_item], + name: str +) -> option<str> { + alt meta_item_from_list(items, name) { + some(item) { + alt attr::get_meta_item_value_str(item) { + some(value) { some(value) } + none { none } + } + } + none { none } + } +} + +fn meta_item_list_from_list( + items: [@ast::meta_item], + name: str +) -> option<[@ast::meta_item]> { + alt meta_item_from_list(items, name) { + some(item) { + attr::get_meta_item_list(item) + } + none { none } + } +} + +fn name_value_str_pair( + item: @ast::meta_item +) -> option<(str, str)> { + alt attr::get_meta_item_value_str(item) { + some(value) { + let name = attr::get_meta_item_name(item); + some((name, value)) + } + none { none } + } +} + +fn span<T: copy>(item: T) -> ast::spanned<T> { + ret {node: item, span: ast_util::dummy_sp()}; +} + +fn mk_name_value_item_str(name: ast::ident, value: str) -> @ast::meta_item { + let value_lit = span(ast::lit_str(value)); + ret mk_name_value_item(name, value_lit); +} + +fn mk_name_value_item(name: ast::ident, value: ast::lit) -> @ast::meta_item { + ret @span(ast::meta_name_value(name, value)); +} + +fn mk_list_item(name: ast::ident, items: [@ast::meta_item]) -> + @ast::meta_item { + ret @span(ast::meta_list(name, items)); +} + +fn mk_word_item(name: ast::ident) -> @ast::meta_item { + ret @span(ast::meta_word(name)); +} + +fn mk_attr(item: @ast::meta_item) -> ast::attribute { + ret span({style: ast::attr_inner, value: *item}); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/front/config.rs b/src/rustc/front/config.rs new file mode 100644 index 00000000000..44c88739069 --- /dev/null +++ b/src/rustc/front/config.rs @@ -0,0 +1,134 @@ +import syntax::{ast, fold}; + +export strip_unconfigured_items; +export metas_in_cfg; +export strip_items; + +type in_cfg_pred = fn@([ast::attribute]) -> bool; + +type ctxt = @{ + in_cfg: in_cfg_pred +}; + +// Support conditional compilation by transforming the AST, stripping out +// any items that do not belong in the current configuration +fn strip_unconfigured_items(crate: @ast::crate) -> @ast::crate { + strip_items(crate) {|attrs| + in_cfg(crate.node.config, attrs) + } +} + +fn strip_items(crate: @ast::crate, in_cfg: in_cfg_pred) + -> @ast::crate { + + let ctxt = @{in_cfg: in_cfg}; + + let precursor = + {fold_mod: bind fold_mod(ctxt, _, _), + fold_block: fold::wrap(bind fold_block(ctxt, _, _)), + fold_native_mod: bind fold_native_mod(ctxt, _, _) + with *fold::default_ast_fold()}; + + let fold = fold::make_fold(precursor); + let res = @fold.fold_crate(*crate); + ret res; +} + +fn filter_item(cx: ctxt, &&item: @ast::item) -> + option<@ast::item> { + if item_in_cfg(cx, item) { option::some(item) } else { option::none } +} + +fn fold_mod(cx: ctxt, m: ast::_mod, fld: fold::ast_fold) -> + ast::_mod { + let filter = bind filter_item(cx, _); + let filtered_items = vec::filter_map(m.items, filter); + ret {view_items: vec::map(m.view_items, fld.fold_view_item), + items: vec::map(filtered_items, fld.fold_item)}; +} + +fn filter_native_item(cx: ctxt, &&item: @ast::native_item) -> + option<@ast::native_item> { + if native_item_in_cfg(cx, item) { + option::some(item) + } else { option::none } +} + +fn fold_native_mod(cx: ctxt, nm: ast::native_mod, + fld: fold::ast_fold) -> ast::native_mod { + let filter = bind filter_native_item(cx, _); + let filtered_items = vec::filter_map(nm.items, filter); + ret {view_items: vec::map(nm.view_items, fld.fold_view_item), + items: filtered_items}; +} + +fn filter_stmt(cx: ctxt, &&stmt: @ast::stmt) -> + option<@ast::stmt> { + alt stmt.node { + ast::stmt_decl(decl, _) { + alt decl.node { + ast::decl_item(item) { + if item_in_cfg(cx, item) { + option::some(stmt) + } else { option::none } + } + _ { option::some(stmt) } + } + } + _ { option::some(stmt) } + } +} + +fn fold_block(cx: ctxt, b: ast::blk_, fld: fold::ast_fold) -> + ast::blk_ { + let filter = bind filter_stmt(cx, _); + let filtered_stmts = vec::filter_map(b.stmts, filter); + ret {view_items: b.view_items, + stmts: vec::map(filtered_stmts, fld.fold_stmt), + expr: option::map(b.expr, fld.fold_expr), + id: b.id, + rules: b.rules}; +} + +fn item_in_cfg(cx: ctxt, item: @ast::item) -> bool { + ret cx.in_cfg(item.attrs); +} + +fn native_item_in_cfg(cx: ctxt, item: @ast::native_item) -> bool { + ret cx.in_cfg(item.attrs); +} + +// Determine if an item should be translated in the current crate +// configuration based on the item's attributes +fn in_cfg(cfg: ast::crate_cfg, attrs: [ast::attribute]) -> bool { + metas_in_cfg(cfg, attr::attr_metas(attrs)) +} + +fn metas_in_cfg(cfg: ast::crate_cfg, metas: [@ast::meta_item]) -> bool { + + // The "cfg" attributes on the item + let cfg_metas = attr::find_meta_items_by_name(metas, "cfg"); + + // Pull the inner meta_items from the #[cfg(meta_item, ...)] attributes, + // so we can match against them. This is the list of configurations for + // which the item is valid + let cfg_metas = vec::concat(vec::filter_map(cfg_metas, + {|&&i| attr::get_meta_item_list(i)})); + + let has_cfg_metas = vec::len(cfg_metas) > 0u; + if !has_cfg_metas { ret true; } + + for cfg_mi: @ast::meta_item in cfg_metas { + if attr::contains(cfg, cfg_mi) { ret true; } + } + + ret false; +} + + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/front/core_inject.rs b/src/rustc/front/core_inject.rs new file mode 100644 index 00000000000..b21967721c5 --- /dev/null +++ b/src/rustc/front/core_inject.rs @@ -0,0 +1,41 @@ +import driver::session::session; +import syntax::codemap; +import syntax::ast; +import front::attr; + +export maybe_inject_libcore_ref; + +fn maybe_inject_libcore_ref(sess: session, + crate: @ast::crate) -> @ast::crate { + if use_core(crate) { + inject_libcore_ref(sess, crate) + } else { + crate + } +} + +fn use_core(crate: @ast::crate) -> bool { + !attr::attrs_contains_name(crate.node.attrs, "no_core") +} + +fn inject_libcore_ref(sess: session, + crate: @ast::crate) -> @ast::crate { + + fn spanned<T: copy>(x: T) -> @ast::spanned<T> { + ret @{node: x, + span: {lo: 0u, hi: 0u, + expn_info: option::none}}; + } + + let n1 = sess.next_node_id(); + let n2 = sess.next_node_id(); + + let vi1 = spanned(ast::view_item_use("core", [], n1)); + let vp = spanned(ast::view_path_glob(@["core"], n2)); + let vi2 = spanned(ast::view_item_import([vp])); + + let vis = [vi1, vi2] + crate.node.module.view_items; + + ret @{node: {module: { view_items: vis with crate.node.module } + with crate.node} with *crate } +} diff --git a/src/rustc/front/test.rs b/src/rustc/front/test.rs new file mode 100644 index 00000000000..47cd3328755 --- /dev/null +++ b/src/rustc/front/test.rs @@ -0,0 +1,470 @@ +// Code that generates a test runner to run all the tests in a crate + +import syntax::{ast, ast_util}; +import syntax::ast_util::*; +//import syntax::ast_util::dummy_sp; +import syntax::fold; +import syntax::print::pprust; +import syntax::codemap::span; +import driver::session; +import session::session; +import front::attr; + +export modify_for_testing; + +type node_id_gen = fn@() -> ast::node_id; + +type test = {span: span, path: [ast::ident], ignore: bool, should_fail: bool}; + +type test_ctxt = + @{sess: session::session, + crate: @ast::crate, + mutable path: [ast::ident], + mutable testfns: [test]}; + +// Traverse the crate, collecting all the test functions, eliding any +// existing main functions, and synthesizing a main test harness +fn modify_for_testing(sess: session::session, + crate: @ast::crate) -> @ast::crate { + + if sess.opts.test { + generate_test_harness(sess, crate) + } else { + strip_test_functions(crate) + } +} + +fn generate_test_harness(sess: session::session, + crate: @ast::crate) -> @ast::crate { + let cx: test_ctxt = + @{sess: sess, + crate: crate, + mutable path: [], + mutable testfns: []}; + + let precursor = + {fold_crate: fold::wrap(bind fold_crate(cx, _, _)), + fold_item: bind fold_item(cx, _, _), + fold_mod: bind fold_mod(cx, _, _) with *fold::default_ast_fold()}; + + let fold = fold::make_fold(precursor); + let res = @fold.fold_crate(*crate); + ret res; +} + +fn strip_test_functions(crate: @ast::crate) -> @ast::crate { + // When not compiling with --test we should not compile the + // #[test] functions + config::strip_items(crate) {|attrs| + !attr::contains_name(attr::attr_metas(attrs), "test") + } +} + +fn fold_mod(_cx: test_ctxt, m: ast::_mod, fld: fold::ast_fold) -> ast::_mod { + + // Remove any defined main function from the AST so it doesn't clash with + // the one we're going to add. FIXME: This is sloppy. Instead we should + // have some mechanism to indicate to the translation pass which function + // we want to be main. + fn nomain(&&item: @ast::item) -> option<@ast::item> { + alt item.node { + ast::item_fn(_, _, _) { + if item.ident == "main" { + option::none + } else { option::some(item) } + } + _ { option::some(item) } + } + } + + let mod_nomain = + {view_items: m.view_items, items: vec::filter_map(m.items, nomain)}; + ret fold::noop_fold_mod(mod_nomain, fld); +} + +fn fold_crate(cx: test_ctxt, c: ast::crate_, fld: fold::ast_fold) -> + ast::crate_ { + let folded = fold::noop_fold_crate(c, fld); + + // Add a special __test module to the crate that will contain code + // generated for the test harness + ret {module: add_test_module(cx, folded.module) with folded}; +} + + +fn fold_item(cx: test_ctxt, &&i: @ast::item, fld: fold::ast_fold) -> + @ast::item { + + cx.path += [i.ident]; + #debug("current path: %s", ast_util::path_name_i(cx.path)); + + if is_test_fn(i) { + alt i.node { + ast::item_fn(decl, _, _) if decl.purity == ast::unsafe_fn { + cx.sess.span_fatal( + i.span, + "unsafe functions cannot be used for tests"); + } + _ { + #debug("this is a test function"); + let test = {span: i.span, + path: cx.path, ignore: is_ignored(cx, i), + should_fail: should_fail(i)}; + cx.testfns += [test]; + #debug("have %u test functions", vec::len(cx.testfns)); + } + } + } + + let res = fold::noop_fold_item(i, fld); + vec::pop(cx.path); + ret res; +} + +fn is_test_fn(i: @ast::item) -> bool { + let has_test_attr = + vec::len(attr::find_attrs_by_name(i.attrs, "test")) > 0u; + + fn has_test_signature(i: @ast::item) -> bool { + alt i.node { + ast::item_fn(decl, tps, _) { + let input_cnt = vec::len(decl.inputs); + let no_output = decl.output.node == ast::ty_nil; + let tparm_cnt = vec::len(tps); + input_cnt == 0u && no_output && tparm_cnt == 0u + } + _ { false } + } + } + + ret has_test_attr && has_test_signature(i); +} + +fn is_ignored(cx: test_ctxt, i: @ast::item) -> bool { + let ignoreattrs = attr::find_attrs_by_name(i.attrs, "ignore"); + let ignoreitems = attr::attr_metas(ignoreattrs); + let cfg_metas = vec::concat(vec::filter_map(ignoreitems, + {|&&i| attr::get_meta_item_list(i)})); + ret if vec::is_not_empty(ignoreitems) { + config::metas_in_cfg(cx.crate.node.config, cfg_metas) + } else { + false + } +} + +fn should_fail(i: @ast::item) -> bool { + vec::len(attr::find_attrs_by_name(i.attrs, "should_fail")) > 0u +} + +fn add_test_module(cx: test_ctxt, m: ast::_mod) -> ast::_mod { + let testmod = mk_test_module(cx); + ret {items: m.items + [testmod] with m}; +} + +/* + +We're going to be building a module that looks more or less like: + +mod __test { + + fn main(args: [str]) -> int { + std::test::test_main(args, tests()) + } + + fn tests() -> [std::test::test_desc] { + ... the list of tests in the crate ... + } +} + +*/ + +fn mk_test_module(cx: test_ctxt) -> @ast::item { + // A function that generates a vector of test descriptors to feed to the + // test runner + let testsfn = mk_tests(cx); + // The synthesized main function which will call the console test runner + // with our list of tests + let mainfn = mk_main(cx); + let testmod: ast::_mod = {view_items: [], items: [mainfn, testsfn]}; + let item_ = ast::item_mod(testmod); + // This attribute tells resolve to let us call unexported functions + let resolve_unexported_attr = + attr::mk_attr(attr::mk_word_item("!resolve_unexported")); + let item: ast::item = + {ident: "__test", + attrs: [resolve_unexported_attr], + id: cx.sess.next_node_id(), + node: item_, + span: dummy_sp()}; + + #debug("Synthetic test module:\n%s\n", pprust::item_to_str(@item)); + + ret @item; +} + +fn nospan<T: copy>(t: T) -> ast::spanned<T> { + ret {node: t, span: dummy_sp()}; +} + +fn mk_tests(cx: test_ctxt) -> @ast::item { + let ret_ty = mk_test_desc_vec_ty(cx); + + let decl: ast::fn_decl = + {inputs: [], + output: ret_ty, + purity: ast::impure_fn, + cf: ast::return_val, + constraints: []}; + + // The vector of test_descs for this crate + let test_descs = mk_test_desc_vec(cx); + + let body_: ast::blk_ = + default_block([], option::some(test_descs), cx.sess.next_node_id()); + let body = nospan(body_); + + let item_ = ast::item_fn(decl, [], body); + let item: ast::item = + {ident: "tests", + attrs: [], + id: cx.sess.next_node_id(), + node: item_, + span: dummy_sp()}; + ret @item; +} + +fn mk_path(cx: test_ctxt, path: [ast::ident]) -> [ast::ident] { + // For tests that are inside of std we don't want to prefix + // the paths with std:: + let is_std = { + let items = attr::find_linkage_metas(cx.crate.node.attrs); + alt attr::meta_item_value_from_list(items, "name") { + some("std") { true } + _ { false } + } + }; + (if is_std { [] } else { ["std"] }) + path +} + +// The ast::ty of [std::test::test_desc] +fn mk_test_desc_vec_ty(cx: test_ctxt) -> @ast::ty { + let test_desc_ty_path = + @nospan({global: false, + idents: mk_path(cx, ["test", "test_desc"]), + types: []}); + + let test_desc_ty: ast::ty = + nospan(ast::ty_path(test_desc_ty_path, cx.sess.next_node_id())); + + let vec_mt: ast::mt = {ty: @test_desc_ty, mutbl: ast::m_imm}; + + ret @nospan(ast::ty_vec(vec_mt)); +} + +fn mk_test_desc_vec(cx: test_ctxt) -> @ast::expr { + #debug("building test vector from %u tests", vec::len(cx.testfns)); + let descs = []; + for test: test in cx.testfns { + let test_ = test; // Satisfy alias analysis + descs += [mk_test_desc_rec(cx, test_)]; + } + + ret @{id: cx.sess.next_node_id(), + node: ast::expr_vec(descs, ast::m_imm), + span: dummy_sp()}; +} + +fn mk_test_desc_rec(cx: test_ctxt, test: test) -> @ast::expr { + let span = test.span; + let path = test.path; + + #debug("encoding %s", ast_util::path_name_i(path)); + + let name_lit: ast::lit = + nospan(ast::lit_str(ast_util::path_name_i(path))); + let name_expr: ast::expr = + {id: cx.sess.next_node_id(), + node: ast::expr_lit(@name_lit), + span: span}; + + let name_field: ast::field = + nospan({mutbl: ast::m_imm, ident: "name", expr: @name_expr}); + + let fn_path = @nospan({global: false, idents: path, types: []}); + + let fn_expr: ast::expr = + {id: cx.sess.next_node_id(), + node: ast::expr_path(fn_path), + span: span}; + + let fn_wrapper_expr = mk_test_wrapper(cx, fn_expr, span); + + let fn_field: ast::field = + nospan({mutbl: ast::m_imm, ident: "fn", expr: fn_wrapper_expr}); + + let ignore_lit: ast::lit = nospan(ast::lit_bool(test.ignore)); + + let ignore_expr: ast::expr = + {id: cx.sess.next_node_id(), + node: ast::expr_lit(@ignore_lit), + span: span}; + + let ignore_field: ast::field = + nospan({mutbl: ast::m_imm, ident: "ignore", expr: @ignore_expr}); + + let fail_lit: ast::lit = nospan(ast::lit_bool(test.should_fail)); + + let fail_expr: ast::expr = + {id: cx.sess.next_node_id(), + node: ast::expr_lit(@fail_lit), + span: span}; + + let fail_field: ast::field = + nospan({mutbl: ast::m_imm, ident: "should_fail", expr: @fail_expr}); + + let desc_rec_: ast::expr_ = + ast::expr_rec([name_field, fn_field, ignore_field, fail_field], + option::none); + let desc_rec: ast::expr = + {id: cx.sess.next_node_id(), node: desc_rec_, span: span}; + ret @desc_rec; +} + +// Produces a bare function that wraps the test function +// FIXME: This can go away once fn is the type of bare function +fn mk_test_wrapper(cx: test_ctxt, + fn_path_expr: ast::expr, + span: span) -> @ast::expr { + let call_expr: ast::expr = { + id: cx.sess.next_node_id(), + node: ast::expr_call(@fn_path_expr, [], false), + span: span + }; + + let call_stmt: ast::stmt = nospan( + ast::stmt_semi(@call_expr, cx.sess.next_node_id())); + + let wrapper_decl: ast::fn_decl = { + inputs: [], + output: @nospan(ast::ty_nil), + purity: ast::impure_fn, + cf: ast::return_val, + constraints: [] + }; + + let wrapper_body: ast::blk = nospan({ + view_items: [], + stmts: [@call_stmt], + expr: option::none, + id: cx.sess.next_node_id(), + rules: ast::default_blk + }); + + let wrapper_capture: @ast::capture_clause = @{ + copies: [], + moves: [] + }; + + let wrapper_expr: ast::expr = { + id: cx.sess.next_node_id(), + node: ast::expr_fn(ast::proto_bare, wrapper_decl, + wrapper_body, wrapper_capture), + span: span + }; + + ret @wrapper_expr; +} + +fn mk_main(cx: test_ctxt) -> @ast::item { + let str_pt = @nospan({global: false, idents: ["str"], types: []}); + let str_ty = @nospan(ast::ty_path(str_pt, cx.sess.next_node_id())); + let args_mt: ast::mt = {ty: str_ty, mutbl: ast::m_imm}; + let args_ty: ast::ty = nospan(ast::ty_vec(args_mt)); + + let args_arg: ast::arg = + {mode: ast::expl(ast::by_val), + ty: @args_ty, + ident: "args", + id: cx.sess.next_node_id()}; + + let ret_ty = nospan(ast::ty_nil); + + let decl: ast::fn_decl = + {inputs: [args_arg], + output: @ret_ty, + purity: ast::impure_fn, + cf: ast::return_val, + constraints: []}; + + let test_main_call_expr = mk_test_main_call(cx); + + let body_: ast::blk_ = + default_block([], option::some(test_main_call_expr), + cx.sess.next_node_id()); + let body = {node: body_, span: dummy_sp()}; + + let item_ = ast::item_fn(decl, [], body); + let item: ast::item = + {ident: "main", + attrs: [], + id: cx.sess.next_node_id(), + node: item_, + span: dummy_sp()}; + ret @item; +} + +fn mk_test_main_call(cx: test_ctxt) -> @ast::expr { + + // Get the args passed to main so we can pass the to test_main + let args_path = + @nospan({global: false, idents: ["args"], types: []}); + + let args_path_expr_: ast::expr_ = ast::expr_path(args_path); + + let args_path_expr: ast::expr = + {id: cx.sess.next_node_id(), node: args_path_expr_, span: dummy_sp()}; + + // Call __test::test to generate the vector of test_descs + let test_path = + @nospan({global: false, idents: ["tests"], types: []}); + + let test_path_expr_: ast::expr_ = ast::expr_path(test_path); + + let test_path_expr: ast::expr = + {id: cx.sess.next_node_id(), node: test_path_expr_, span: dummy_sp()}; + + let test_call_expr_ = ast::expr_call(@test_path_expr, [], false); + + let test_call_expr: ast::expr = + {id: cx.sess.next_node_id(), node: test_call_expr_, span: dummy_sp()}; + + // Call std::test::test_main + let test_main_path = + @nospan({global: false, + idents: mk_path(cx, ["test", "test_main"]), + types: []}); + + let test_main_path_expr_: ast::expr_ = ast::expr_path(test_main_path); + + let test_main_path_expr: ast::expr = + {id: cx.sess.next_node_id(), node: test_main_path_expr_, + span: dummy_sp()}; + + let test_main_call_expr_: ast::expr_ = + ast::expr_call(@test_main_path_expr, + [@args_path_expr, @test_call_expr], false); + + let test_main_call_expr: ast::expr = + {id: cx.sess.next_node_id(), node: test_main_call_expr_, + span: dummy_sp()}; + + ret @test_main_call_expr; +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/lib/llvm.rs b/src/rustc/lib/llvm.rs new file mode 100644 index 00000000000..2dc2945d8de --- /dev/null +++ b/src/rustc/lib/llvm.rs @@ -0,0 +1,1113 @@ +import str::sbuf; + +import ctypes::{c_int, c_uint, unsigned, longlong, ulonglong}; + +type Opcode = u32; +type Bool = unsigned; +const True: Bool = 1u32; +const False: Bool = 0u32; + +// Consts for the LLVM CallConv type, pre-cast to uint. + +enum CallConv { + CCallConv = 0, + FastCallConv = 8, + ColdCallConv = 9, + X86StdcallCallConv = 64, + X86FastcallCallConv = 65, +} + +enum Visibility { + LLVMDefaultVisibility = 0, + HiddenVisibility = 1, + ProtectedVisibility = 2, +} + +enum Linkage { + ExternalLinkage = 0, + AvailableExternallyLinkage = 1, + LinkOnceAnyLinkage = 2, + LinkOnceODRLinkage = 3, + WeakAnyLinkage = 4, + WeakODRLinkage = 5, + AppendingLinkage = 6, + InternalLinkage = 7, + PrivateLinkage = 8, + DLLImportLinkage = 9, + DLLExportLinkage = 10, + ExternalWeakLinkage = 11, + GhostLinkage = 12, + CommonLinkage = 13, + LinkerPrivateLinkage = 14, + LinkerPrivateWeakLinkage = 15, + LinkerPrivateWeakDefAutoLinkage = 16, +} + +enum Attribute { + ZExtAttribute = 1, + SExtAttribute = 2, + NoReturnAttribute = 4, + InRegAttribute = 8, + StructRetAttribute = 16, + NoUnwindAttribute = 32, + NoAliasAttribute = 64, + ByValAttribute = 128, + NestAttribute = 256, + ReadNoneAttribute = 512, + ReadOnlyAttribute = 1024, + NoInlineAttribute = 2048, + AlwaysInlineAttribute = 4096, + OptimizeForSizeAttribute = 8192, + StackProtectAttribute = 16384, + StackProtectReqAttribute = 32768, + // 31 << 16 + AlignmentAttribute = 2031616, + NoCaptureAttribute = 2097152, + NoRedZoneAttribute = 4194304, + NoImplicitFloatAttribute = 8388608, + NakedAttribute = 16777216, + InlineHintAttribute = 33554432, + // 7 << 26 + StackAttribute = 469762048, + ReturnsTwiceAttribute = 536870912, + // 1 << 30 + UWTableAttribute = 1073741824, + NonLazyBindAttribute = 2147483648, +} + +// Consts for the LLVM IntPredicate type, pre-cast to uint. +// FIXME: as above. + +enum IntPredicate { + IntEQ = 32, + IntNE = 33, + IntUGT = 34, + IntUGE = 35, + IntULT = 36, + IntULE = 37, + IntSGT = 38, + IntSGE = 39, + IntSLT = 40, + IntSLE = 41, +} + +// Consts for the LLVM RealPredicate type, pre-case to uint. +// FIXME: as above. + +enum RealPredicate { + RealOEQ = 1, + RealOGT = 2, + RealOGE = 3, + RealOLT = 4, + RealOLE = 5, + RealONE = 6, + RealORD = 7, + RealUNO = 8, + RealUEQ = 9, + RealUGT = 10, + RealUGE = 11, + RealULT = 12, + RealULE = 13, + RealUNE = 14, +} + +// Opaque pointer types +enum Module_opaque {} +type ModuleRef = *Module_opaque; +enum Context_opaque {} +type ContextRef = *Context_opaque; +enum Type_opaque {} +type TypeRef = *Type_opaque; +enum Value_opaque {} +type ValueRef = *Value_opaque; +enum BasicBlock_opaque {} +type BasicBlockRef = *BasicBlock_opaque; +enum Builder_opaque {} +type BuilderRef = *Builder_opaque; +enum MemoryBuffer_opaque {} +type MemoryBufferRef = *MemoryBuffer_opaque; +enum PassManager_opaque {} +type PassManagerRef = *PassManager_opaque; +enum PassManagerBuilder_opaque {} +type PassManagerBuilderRef = *PassManagerBuilder_opaque; +enum Use_opaque {} +type UseRef = *Use_opaque; +enum TargetData_opaque {} +type TargetDataRef = *TargetData_opaque; +enum ObjectFile_opaque {} +type ObjectFileRef = *ObjectFile_opaque; +enum SectionIterator_opaque {} +type SectionIteratorRef = *SectionIterator_opaque; + +#[link_args = "-Lrustllvm"] +#[link_name = "rustllvm"] +#[abi = "cdecl"] +native mod llvm { + /* Create and destroy contexts. */ + fn LLVMContextCreate() -> ContextRef; + fn LLVMGetGlobalContext() -> ContextRef; + fn LLVMContextDispose(C: ContextRef); + fn LLVMGetMDKindIDInContext(C: ContextRef, Name: sbuf, SLen: unsigned) -> + unsigned; + fn LLVMGetMDKindID(Name: sbuf, SLen: unsigned) -> unsigned; + + /* Create and destroy modules. */ + fn LLVMModuleCreateWithNameInContext(ModuleID: sbuf, C: ContextRef) -> + ModuleRef; + fn LLVMDisposeModule(M: ModuleRef); + + /** Data layout. See Module::getDataLayout. */ + fn LLVMGetDataLayout(M: ModuleRef) -> sbuf; + fn LLVMSetDataLayout(M: ModuleRef, Triple: sbuf); + + /** Target triple. See Module::getTargetTriple. */ + fn LLVMGetTarget(M: ModuleRef) -> sbuf; + fn LLVMSetTarget(M: ModuleRef, Triple: sbuf); + + /** See Module::dump. */ + fn LLVMDumpModule(M: ModuleRef); + + /** See Module::setModuleInlineAsm. */ + fn LLVMSetModuleInlineAsm(M: ModuleRef, Asm: sbuf); + + /** See llvm::LLVMTypeKind::getTypeID. */ + + // FIXME: returning int rather than TypeKind because + // we directly inspect the values, and casting from + // a native doesn't work yet (only *to* a native). + + fn LLVMGetTypeKind(Ty: TypeRef) -> c_int; + + /** See llvm::LLVMType::getContext. */ + fn LLVMGetTypeContext(Ty: TypeRef) -> ContextRef; + + /* Operations on integer types */ + fn LLVMInt1TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMInt8TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMInt16TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMInt32TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMInt64TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMIntTypeInContext(C: ContextRef, NumBits: unsigned) -> TypeRef; + + fn LLVMInt1Type() -> TypeRef; + fn LLVMInt8Type() -> TypeRef; + fn LLVMInt16Type() -> TypeRef; + fn LLVMInt32Type() -> TypeRef; + fn LLVMInt64Type() -> TypeRef; + fn LLVMIntType(NumBits: unsigned) -> TypeRef; + fn LLVMGetIntTypeWidth(IntegerTy: TypeRef) -> unsigned; + + /* Operations on real types */ + fn LLVMFloatTypeInContext(C: ContextRef) -> TypeRef; + fn LLVMDoubleTypeInContext(C: ContextRef) -> TypeRef; + fn LLVMX86FP80TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMFP128TypeInContext(C: ContextRef) -> TypeRef; + fn LLVMPPCFP128TypeInContext(C: ContextRef) -> TypeRef; + + fn LLVMFloatType() -> TypeRef; + fn LLVMDoubleType() -> TypeRef; + fn LLVMX86FP80Type() -> TypeRef; + fn LLVMFP128Type() -> TypeRef; + fn LLVMPPCFP128Type() -> TypeRef; + + /* Operations on function types */ + fn LLVMFunctionType(ReturnType: TypeRef, ParamTypes: *TypeRef, + ParamCount: unsigned, IsVarArg: Bool) -> TypeRef; + fn LLVMIsFunctionVarArg(FunctionTy: TypeRef) -> Bool; + fn LLVMGetReturnType(FunctionTy: TypeRef) -> TypeRef; + fn LLVMCountParamTypes(FunctionTy: TypeRef) -> unsigned; + fn LLVMGetParamTypes(FunctionTy: TypeRef, Dest: *TypeRef); + + /* Operations on struct types */ + fn LLVMStructTypeInContext(C: ContextRef, ElementTypes: *TypeRef, + ElementCount: unsigned, + Packed: Bool) -> TypeRef; + fn LLVMStructType(ElementTypes: *TypeRef, ElementCount: unsigned, + Packed: Bool) -> TypeRef; + fn LLVMCountStructElementTypes(StructTy: TypeRef) -> unsigned; + fn LLVMGetStructElementTypes(StructTy: TypeRef, Dest: *TypeRef); + fn LLVMIsPackedStruct(StructTy: TypeRef) -> Bool; + + /* Operations on array, pointer, and vector types (sequence types) */ + fn LLVMArrayType(ElementType: TypeRef, + ElementCount: unsigned) -> TypeRef; + fn LLVMPointerType(ElementType: TypeRef, + AddressSpace: unsigned) -> TypeRef; + fn LLVMVectorType(ElementType: TypeRef, + ElementCount: unsigned) -> TypeRef; + + fn LLVMGetElementType(Ty: TypeRef) -> TypeRef; + fn LLVMGetArrayLength(ArrayTy: TypeRef) -> unsigned; + fn LLVMGetPointerAddressSpace(PointerTy: TypeRef) -> unsigned; + fn LLVMGetVectorSize(VectorTy: TypeRef) -> unsigned; + + /* Operations on other types */ + fn LLVMVoidTypeInContext(C: ContextRef) -> TypeRef; + fn LLVMLabelTypeInContext(C: ContextRef) -> TypeRef; + fn LLVMMetadataTypeInContext(C: ContextRef) -> TypeRef; + + fn LLVMVoidType() -> TypeRef; + fn LLVMLabelType() -> TypeRef; + fn LLVMMetadataType() -> TypeRef; + + /* Operations on all values */ + fn LLVMTypeOf(Val: ValueRef) -> TypeRef; + fn LLVMGetValueName(Val: ValueRef) -> sbuf; + fn LLVMSetValueName(Val: ValueRef, Name: sbuf); + fn LLVMDumpValue(Val: ValueRef); + fn LLVMReplaceAllUsesWith(OldVal: ValueRef, NewVal: ValueRef); + fn LLVMHasMetadata(Val: ValueRef) -> c_int; + fn LLVMGetMetadata(Val: ValueRef, KindID: unsigned) -> ValueRef; + fn LLVMSetMetadata(Val: ValueRef, KindID: unsigned, Node: ValueRef); + + /* Operations on Uses */ + fn LLVMGetFirstUse(Val: ValueRef) -> UseRef; + fn LLVMGetNextUse(U: UseRef) -> UseRef; + fn LLVMGetUser(U: UseRef) -> ValueRef; + fn LLVMGetUsedValue(U: UseRef) -> ValueRef; + + /* Operations on Users */ + fn LLVMGetOperand(Val: ValueRef, Index: unsigned) -> ValueRef; + fn LLVMSetOperand(Val: ValueRef, Index: unsigned, Op: ValueRef); + + /* Operations on constants of any type */ + fn LLVMConstNull(Ty: TypeRef) -> ValueRef; + /* all zeroes */ + fn LLVMConstAllOnes(Ty: TypeRef) -> ValueRef; + /* only for int/vector */ + fn LLVMGetUndef(Ty: TypeRef) -> ValueRef; + fn LLVMIsConstant(Val: ValueRef) -> Bool; + fn LLVMIsNull(Val: ValueRef) -> Bool; + fn LLVMIsUndef(Val: ValueRef) -> Bool; + fn LLVMConstPointerNull(Ty: TypeRef) -> ValueRef; + + /* Operations on metadata */ + fn LLVMMDStringInContext(C: ContextRef, Str: sbuf, SLen: unsigned) -> + ValueRef; + fn LLVMMDString(Str: sbuf, SLen: unsigned) -> ValueRef; + fn LLVMMDNodeInContext(C: ContextRef, Vals: *ValueRef, Count: unsigned) -> + ValueRef; + fn LLVMMDNode(Vals: *ValueRef, Count: unsigned) -> ValueRef; + fn LLVMAddNamedMetadataOperand(M: ModuleRef, Str: sbuf, + Val: ValueRef); + + /* Operations on scalar constants */ + fn LLVMConstInt(IntTy: TypeRef, N: ulonglong, SignExtend: Bool) -> + ValueRef; + // FIXME: radix is actually u8, but our native layer can't handle this + // yet. lucky for us we're little-endian. Small miracles. + fn LLVMConstIntOfString(IntTy: TypeRef, Text: sbuf, Radix: c_int) -> + ValueRef; + fn LLVMConstIntOfStringAndSize(IntTy: TypeRef, Text: sbuf, SLen: unsigned, + Radix: u8) -> ValueRef; + fn LLVMConstReal(RealTy: TypeRef, N: f64) -> ValueRef; + fn LLVMConstRealOfString(RealTy: TypeRef, Text: sbuf) -> ValueRef; + fn LLVMConstRealOfStringAndSize(RealTy: TypeRef, Text: sbuf, + SLen: unsigned) -> ValueRef; + fn LLVMConstIntGetZExtValue(ConstantVal: ValueRef) -> ulonglong; + fn LLVMConstIntGetSExtValue(ConstantVal: ValueRef) -> longlong; + + + /* Operations on composite constants */ + fn LLVMConstStringInContext(C: ContextRef, Str: sbuf, Length: unsigned, + DontNullTerminate: Bool) -> ValueRef; + fn LLVMConstStructInContext(C: ContextRef, ConstantVals: *ValueRef, + Count: unsigned, Packed: Bool) -> ValueRef; + + fn LLVMConstString(Str: sbuf, Length: unsigned, + DontNullTerminate: Bool) -> ValueRef; + fn LLVMConstArray(ElementTy: TypeRef, ConstantVals: *ValueRef, + Length: unsigned) -> ValueRef; + fn LLVMConstStruct(ConstantVals: *ValueRef, + Count: unsigned, Packed: Bool) -> ValueRef; + fn LLVMConstVector(ScalarConstantVals: *ValueRef, + Size: unsigned) -> ValueRef; + + /* Constant expressions */ + fn LLVMAlignOf(Ty: TypeRef) -> ValueRef; + fn LLVMSizeOf(Ty: TypeRef) -> ValueRef; + fn LLVMConstNeg(ConstantVal: ValueRef) -> ValueRef; + fn LLVMConstNSWNeg(ConstantVal: ValueRef) -> ValueRef; + fn LLVMConstNUWNeg(ConstantVal: ValueRef) -> ValueRef; + fn LLVMConstFNeg(ConstantVal: ValueRef) -> ValueRef; + fn LLVMConstNot(ConstantVal: ValueRef) -> ValueRef; + fn LLVMConstAdd(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstNSWAdd(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstNUWAdd(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstFAdd(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstSub(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstNSWSub(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstNUWSub(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstFSub(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstMul(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstNSWMul(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstNUWMul(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstFMul(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstUDiv(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstSDiv(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstExactSDiv(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstFDiv(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstURem(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstSRem(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstFRem(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstAnd(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstOr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstXor(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstShl(LHSConstant: ValueRef, RHSConstant: ValueRef) -> ValueRef; + fn LLVMConstLShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstAShr(LHSConstant: ValueRef, RHSConstant: ValueRef) -> + ValueRef; + fn LLVMConstGEP(ConstantVal: ValueRef, ConstantIndices: *uint, + NumIndices: unsigned) -> ValueRef; + fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: *uint, + NumIndices: unsigned) -> ValueRef; + fn LLVMConstTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstSExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstZExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstFPTrunc(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstFPExt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstUIToFP(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstSIToFP(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstFPToUI(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstFPToSI(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstPtrToInt(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstIntToPtr(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstBitCast(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstZExtOrBitCast(ConstantVal: ValueRef, ToType: TypeRef) -> + ValueRef; + fn LLVMConstSExtOrBitCast(ConstantVal: ValueRef, ToType: TypeRef) -> + ValueRef; + fn LLVMConstTruncOrBitCast(ConstantVal: ValueRef, ToType: TypeRef) -> + ValueRef; + fn LLVMConstPointerCast(ConstantVal: ValueRef, ToType: TypeRef) -> + ValueRef; + fn LLVMConstIntCast(ConstantVal: ValueRef, ToType: TypeRef, + isSigned: Bool) -> ValueRef; + fn LLVMConstFPCast(ConstantVal: ValueRef, ToType: TypeRef) -> ValueRef; + fn LLVMConstSelect(ConstantCondition: ValueRef, ConstantIfTrue: ValueRef, + ConstantIfFalse: ValueRef) -> ValueRef; + fn LLVMConstExtractElement(VectorConstant: ValueRef, + IndexConstant: ValueRef) -> ValueRef; + fn LLVMConstInsertElement(VectorConstant: ValueRef, + ElementValueConstant: ValueRef, + IndexConstant: ValueRef) -> ValueRef; + fn LLVMConstShuffleVector(VectorAConstant: ValueRef, + VectorBConstant: ValueRef, + MaskConstant: ValueRef) -> ValueRef; + fn LLVMConstExtractValue(AggConstant: ValueRef, IdxList: *uint, + NumIdx: unsigned) -> ValueRef; + fn LLVMConstInsertValue(AggConstant: ValueRef, + ElementValueConstant: ValueRef, IdxList: *uint, + NumIdx: unsigned) -> ValueRef; + fn LLVMConstInlineAsm(Ty: TypeRef, AsmString: sbuf, Constraints: sbuf, + HasSideEffects: Bool, IsAlignStack: Bool) -> + ValueRef; + fn LLVMBlockAddress(F: ValueRef, BB: BasicBlockRef) -> ValueRef; + + + + /* Operations on global variables, functions, and aliases (globals) */ + fn LLVMGetGlobalParent(Global: ValueRef) -> ModuleRef; + fn LLVMIsDeclaration(Global: ValueRef) -> Bool; + fn LLVMGetLinkage(Global: ValueRef) -> unsigned; + fn LLVMSetLinkage(Global: ValueRef, Link: unsigned); + fn LLVMGetSection(Global: ValueRef) -> sbuf; + fn LLVMSetSection(Global: ValueRef, Section: sbuf); + fn LLVMGetVisibility(Global: ValueRef) -> unsigned; + fn LLVMSetVisibility(Global: ValueRef, Viz: unsigned); + fn LLVMGetAlignment(Global: ValueRef) -> unsigned; + fn LLVMSetAlignment(Global: ValueRef, Bytes: unsigned); + + + /* Operations on global variables */ + fn LLVMAddGlobal(M: ModuleRef, Ty: TypeRef, Name: sbuf) -> ValueRef; + fn LLVMAddGlobalInAddressSpace(M: ModuleRef, Ty: TypeRef, Name: sbuf, + AddressSpace: unsigned) -> ValueRef; + fn LLVMGetNamedGlobal(M: ModuleRef, Name: sbuf) -> ValueRef; + fn LLVMGetFirstGlobal(M: ModuleRef) -> ValueRef; + fn LLVMGetLastGlobal(M: ModuleRef) -> ValueRef; + fn LLVMGetNextGlobal(GlobalVar: ValueRef) -> ValueRef; + fn LLVMGetPreviousGlobal(GlobalVar: ValueRef) -> ValueRef; + fn LLVMDeleteGlobal(GlobalVar: ValueRef); + fn LLVMGetInitializer(GlobalVar: ValueRef) -> ValueRef; + fn LLVMSetInitializer(GlobalVar: ValueRef, ConstantVal: ValueRef); + fn LLVMIsThreadLocal(GlobalVar: ValueRef) -> Bool; + fn LLVMSetThreadLocal(GlobalVar: ValueRef, IsThreadLocal: Bool); + fn LLVMIsGlobalConstant(GlobalVar: ValueRef) -> Bool; + fn LLVMSetGlobalConstant(GlobalVar: ValueRef, IsConstant: Bool); + + /* Operations on aliases */ + fn LLVMAddAlias(M: ModuleRef, Ty: TypeRef, Aliasee: ValueRef, Name: sbuf) + -> ValueRef; + + /* Operations on functions */ + fn LLVMAddFunction(M: ModuleRef, Name: sbuf, FunctionTy: TypeRef) -> + ValueRef; + fn LLVMGetNamedFunction(M: ModuleRef, Name: sbuf) -> ValueRef; + fn LLVMGetFirstFunction(M: ModuleRef) -> ValueRef; + fn LLVMGetLastFunction(M: ModuleRef) -> ValueRef; + fn LLVMGetNextFunction(Fn: ValueRef) -> ValueRef; + fn LLVMGetPreviousFunction(Fn: ValueRef) -> ValueRef; + fn LLVMDeleteFunction(Fn: ValueRef); + fn LLVMGetOrInsertFunction(M: ModuleRef, Name: sbuf, FunctionTy: TypeRef) + -> ValueRef; + fn LLVMGetIntrinsicID(Fn: ValueRef) -> unsigned; + fn LLVMGetFunctionCallConv(Fn: ValueRef) -> unsigned; + fn LLVMSetFunctionCallConv(Fn: ValueRef, CC: unsigned); + fn LLVMGetGC(Fn: ValueRef) -> sbuf; + fn LLVMSetGC(Fn: ValueRef, Name: sbuf); + fn LLVMAddFunctionAttr(Fn: ValueRef, PA: unsigned, HighPA: unsigned); + fn LLVMGetFunctionAttr(Fn: ValueRef) -> unsigned; + fn LLVMRemoveFunctionAttr(Fn: ValueRef, PA: unsigned, HighPA: unsigned); + + /* Operations on parameters */ + fn LLVMCountParams(Fn: ValueRef) -> unsigned; + fn LLVMGetParams(Fn: ValueRef, Params: *ValueRef); + fn LLVMGetParam(Fn: ValueRef, Index: unsigned) -> ValueRef; + fn LLVMGetParamParent(Inst: ValueRef) -> ValueRef; + fn LLVMGetFirstParam(Fn: ValueRef) -> ValueRef; + fn LLVMGetLastParam(Fn: ValueRef) -> ValueRef; + fn LLVMGetNextParam(Arg: ValueRef) -> ValueRef; + fn LLVMGetPreviousParam(Arg: ValueRef) -> ValueRef; + fn LLVMAddAttribute(Arg: ValueRef, PA: unsigned); + fn LLVMRemoveAttribute(Arg: ValueRef, PA: unsigned); + fn LLVMGetAttribute(Arg: ValueRef) -> unsigned; + fn LLVMSetParamAlignment(Arg: ValueRef, align: unsigned); + + /* Operations on basic blocks */ + fn LLVMBasicBlockAsValue(BB: BasicBlockRef) -> ValueRef; + fn LLVMValueIsBasicBlock(Val: ValueRef) -> Bool; + fn LLVMValueAsBasicBlock(Val: ValueRef) -> BasicBlockRef; + fn LLVMGetBasicBlockParent(BB: BasicBlockRef) -> ValueRef; + fn LLVMCountBasicBlocks(Fn: ValueRef) -> unsigned; + fn LLVMGetBasicBlocks(Fn: ValueRef, BasicBlocks: *ValueRef); + fn LLVMGetFirstBasicBlock(Fn: ValueRef) -> BasicBlockRef; + fn LLVMGetLastBasicBlock(Fn: ValueRef) -> BasicBlockRef; + fn LLVMGetNextBasicBlock(BB: BasicBlockRef) -> BasicBlockRef; + fn LLVMGetPreviousBasicBlock(BB: BasicBlockRef) -> BasicBlockRef; + fn LLVMGetEntryBasicBlock(Fn: ValueRef) -> BasicBlockRef; + + fn LLVMAppendBasicBlockInContext(C: ContextRef, Fn: ValueRef, Name: sbuf) + -> BasicBlockRef; + fn LLVMInsertBasicBlockInContext(C: ContextRef, BB: BasicBlockRef, + Name: sbuf) -> BasicBlockRef; + + fn LLVMAppendBasicBlock(Fn: ValueRef, Name: sbuf) -> BasicBlockRef; + fn LLVMInsertBasicBlock(InsertBeforeBB: BasicBlockRef, Name: sbuf) -> + BasicBlockRef; + fn LLVMDeleteBasicBlock(BB: BasicBlockRef); + + /* Operations on instructions */ + fn LLVMGetInstructionParent(Inst: ValueRef) -> BasicBlockRef; + fn LLVMGetFirstInstruction(BB: BasicBlockRef) -> ValueRef; + fn LLVMGetLastInstruction(BB: BasicBlockRef) -> ValueRef; + fn LLVMGetNextInstruction(Inst: ValueRef) -> ValueRef; + fn LLVMGetPreviousInstruction(Inst: ValueRef) -> ValueRef; + + /* Operations on call sites */ + fn LLVMSetInstructionCallConv(Instr: ValueRef, CC: unsigned); + fn LLVMGetInstructionCallConv(Instr: ValueRef) -> unsigned; + fn LLVMAddInstrAttribute(Instr: ValueRef, index: unsigned, IA: unsigned); + fn LLVMRemoveInstrAttribute(Instr: ValueRef, index: unsigned, + IA: unsigned); + fn LLVMSetInstrParamAlignment(Instr: ValueRef, index: unsigned, + align: unsigned); + + /* Operations on call instructions (only) */ + fn LLVMIsTailCall(CallInst: ValueRef) -> Bool; + fn LLVMSetTailCall(CallInst: ValueRef, IsTailCall: Bool); + + /* Operations on phi nodes */ + fn LLVMAddIncoming(PhiNode: ValueRef, IncomingValues: *ValueRef, + IncomingBlocks: *BasicBlockRef, Count: unsigned); + fn LLVMCountIncoming(PhiNode: ValueRef) -> unsigned; + fn LLVMGetIncomingValue(PhiNode: ValueRef, Index: unsigned) -> ValueRef; + fn LLVMGetIncomingBlock(PhiNode: ValueRef, + Index: unsigned) -> BasicBlockRef; + + /* Instruction builders */ + fn LLVMCreateBuilderInContext(C: ContextRef) -> BuilderRef; + fn LLVMCreateBuilder() -> BuilderRef; + fn LLVMPositionBuilder(Builder: BuilderRef, Block: BasicBlockRef, + Instr: ValueRef); + fn LLVMPositionBuilderBefore(Builder: BuilderRef, Instr: ValueRef); + fn LLVMPositionBuilderAtEnd(Builder: BuilderRef, Block: BasicBlockRef); + fn LLVMGetInsertBlock(Builder: BuilderRef) -> BasicBlockRef; + fn LLVMClearInsertionPosition(Builder: BuilderRef); + fn LLVMInsertIntoBuilder(Builder: BuilderRef, Instr: ValueRef); + fn LLVMInsertIntoBuilderWithName(Builder: BuilderRef, Instr: ValueRef, + Name: sbuf); + fn LLVMDisposeBuilder(Builder: BuilderRef); + + /* Metadata */ + fn LLVMSetCurrentDebugLocation(Builder: BuilderRef, L: ValueRef); + fn LLVMGetCurrentDebugLocation(Builder: BuilderRef) -> ValueRef; + fn LLVMSetInstDebugLocation(Builder: BuilderRef, Inst: ValueRef); + + /* Terminators */ + fn LLVMBuildRetVoid(B: BuilderRef) -> ValueRef; + fn LLVMBuildRet(B: BuilderRef, V: ValueRef) -> ValueRef; + fn LLVMBuildAggregateRet(B: BuilderRef, RetVals: *ValueRef, + N: unsigned) -> ValueRef; + fn LLVMBuildBr(B: BuilderRef, Dest: BasicBlockRef) -> ValueRef; + fn LLVMBuildCondBr(B: BuilderRef, If: ValueRef, Then: BasicBlockRef, + Else: BasicBlockRef) -> ValueRef; + fn LLVMBuildSwitch(B: BuilderRef, V: ValueRef, Else: BasicBlockRef, + NumCases: unsigned) -> ValueRef; + fn LLVMBuildIndirectBr(B: BuilderRef, Addr: ValueRef, + NumDests: unsigned) -> ValueRef; + fn LLVMBuildInvoke(B: BuilderRef, Fn: ValueRef, Args: *ValueRef, + NumArgs: unsigned, Then: BasicBlockRef, + Catch: BasicBlockRef, Name: sbuf) -> ValueRef; + fn LLVMBuildLandingPad(B: BuilderRef, Ty: TypeRef, PersFn: ValueRef, + NumClauses: unsigned, Name: sbuf) -> ValueRef; + fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef; + fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef; + + /* Add a case to the switch instruction */ + fn LLVMAddCase(Switch: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef); + + /* Add a destination to the indirectbr instruction */ + fn LLVMAddDestination(IndirectBr: ValueRef, Dest: BasicBlockRef); + + /* Add a clause to the landing pad instruction */ + fn LLVMAddClause(LandingPad: ValueRef, ClauseVal: ValueRef); + + /* Set the cleanup on a landing pad instruction */ + fn LLVMSetCleanup(LandingPad: ValueRef, Val: Bool); + + /* Arithmetic */ + fn LLVMBuildAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildNSWAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildNUWAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildSub(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildNSWSub(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildNUWSub(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFSub(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildMul(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildNSWMul(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildNUWMul(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFMul(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildUDiv(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildSDiv(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildExactSDiv(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFDiv(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildURem(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildSRem(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildFRem(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildShl(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildLShr(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildAShr(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildAnd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildOr(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) -> + ValueRef; + fn LLVMBuildXor(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildBinOp(B: BuilderRef, Op: Opcode, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildNeg(B: BuilderRef, V: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildNSWNeg(B: BuilderRef, V: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildNUWNeg(B: BuilderRef, V: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildFNeg(B: BuilderRef, V: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildNot(B: BuilderRef, V: ValueRef, Name: sbuf) -> ValueRef; + + /* Memory */ + fn LLVMBuildMalloc(B: BuilderRef, Ty: TypeRef, Name: sbuf) -> ValueRef; + fn LLVMBuildArrayMalloc(B: BuilderRef, Ty: TypeRef, Val: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildAlloca(B: BuilderRef, Ty: TypeRef, Name: sbuf) -> ValueRef; + fn LLVMBuildArrayAlloca(B: BuilderRef, Ty: TypeRef, Val: ValueRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFree(B: BuilderRef, PointerVal: ValueRef) -> ValueRef; + fn LLVMBuildLoad(B: BuilderRef, PointerVal: ValueRef, Name: sbuf) -> + ValueRef; + fn LLVMBuildStore(B: BuilderRef, Val: ValueRef, Ptr: ValueRef) -> + ValueRef; + fn LLVMBuildGEP(B: BuilderRef, Pointer: ValueRef, Indices: *ValueRef, + NumIndices: unsigned, Name: sbuf) -> ValueRef; + fn LLVMBuildInBoundsGEP(B: BuilderRef, Pointer: ValueRef, + Indices: *ValueRef, NumIndices: unsigned, + Name: sbuf) + -> ValueRef; + fn LLVMBuildStructGEP(B: BuilderRef, Pointer: ValueRef, Idx: unsigned, + Name: sbuf) -> ValueRef; + fn LLVMBuildGlobalString(B: BuilderRef, Str: sbuf, Name: sbuf) -> + ValueRef; + fn LLVMBuildGlobalStringPtr(B: BuilderRef, Str: sbuf, Name: sbuf) -> + ValueRef; + + /* Casts */ + fn LLVMBuildTrunc(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildZExt(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildSExt(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFPToUI(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFPToSI(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildUIToFP(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildSIToFP(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFPTrunc(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFPExt(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildPtrToInt(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildIntToPtr(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildBitCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildZExtOrBitCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildSExtOrBitCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildTruncOrBitCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildCast(B: BuilderRef, Op: Opcode, Val: ValueRef, + DestTy: TypeRef, Name: sbuf) -> ValueRef; + fn LLVMBuildPointerCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildIntCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + fn LLVMBuildFPCast(B: BuilderRef, Val: ValueRef, DestTy: TypeRef, + Name: sbuf) -> ValueRef; + + /* Comparisons */ + fn LLVMBuildICmp(B: BuilderRef, Op: unsigned, LHS: ValueRef, + RHS: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildFCmp(B: BuilderRef, Op: unsigned, LHS: ValueRef, + RHS: ValueRef, Name: sbuf) -> ValueRef; + + /* Miscellaneous instructions */ + fn LLVMBuildPhi(B: BuilderRef, Ty: TypeRef, Name: sbuf) -> ValueRef; + fn LLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: *ValueRef, + NumArgs: unsigned, Name: sbuf) -> ValueRef; + fn LLVMBuildSelect(B: BuilderRef, If: ValueRef, Then: ValueRef, + Else: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildVAArg(B: BuilderRef, list: ValueRef, Ty: TypeRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildExtractElement(B: BuilderRef, VecVal: ValueRef, + Index: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildInsertElement(B: BuilderRef, VecVal: ValueRef, + EltVal: ValueRef, Index: ValueRef, Name: sbuf) + -> ValueRef; + fn LLVMBuildShuffleVector(B: BuilderRef, V1: ValueRef, V2: ValueRef, + Mask: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildExtractValue(B: BuilderRef, AggVal: ValueRef, Index: unsigned, + Name: sbuf) -> ValueRef; + fn LLVMBuildInsertValue(B: BuilderRef, AggVal: ValueRef, EltVal: ValueRef, + Index: unsigned, Name: sbuf) -> ValueRef; + + fn LLVMBuildIsNull(B: BuilderRef, Val: ValueRef, Name: sbuf) -> ValueRef; + fn LLVMBuildIsNotNull(B: BuilderRef, Val: ValueRef, Name: sbuf) -> + ValueRef; + fn LLVMBuildPtrDiff(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, + Name: sbuf) -> ValueRef; + + /* Selected entries from the downcasts. */ + fn LLVMIsATerminatorInst(Inst: ValueRef) -> ValueRef; + + /** Writes a module to the specified path. Returns 0 on success. */ + fn LLVMWriteBitcodeToFile(M: ModuleRef, Path: sbuf) -> c_int; + + /** Creates target data from a target layout string. */ + fn LLVMCreateTargetData(StringRep: sbuf) -> TargetDataRef; + /** Adds the target data to the given pass manager. The pass manager + references the target data only weakly. */ + fn LLVMAddTargetData(TD: TargetDataRef, PM: PassManagerRef); + /** Returns the size of a type. FIXME: rv is actually a ULongLong! */ + fn LLVMStoreSizeOfType(TD: TargetDataRef, Ty: TypeRef) -> unsigned; + /** Returns the alignment of a type. */ + fn LLVMPreferredAlignmentOfType(TD: TargetDataRef, + Ty: TypeRef) -> unsigned; + /** Disposes target data. */ + fn LLVMDisposeTargetData(TD: TargetDataRef); + + /** Creates a pass manager. */ + fn LLVMCreatePassManager() -> PassManagerRef; + /** Disposes a pass manager. */ + fn LLVMDisposePassManager(PM: PassManagerRef); + /** Runs a pass manager on a module. */ + fn LLVMRunPassManager(PM: PassManagerRef, M: ModuleRef) -> Bool; + + /** Adds a verification pass. */ + fn LLVMAddVerifierPass(PM: PassManagerRef); + + fn LLVMAddGlobalOptimizerPass(PM: PassManagerRef); + fn LLVMAddIPSCCPPass(PM: PassManagerRef); + fn LLVMAddDeadArgEliminationPass(PM: PassManagerRef); + fn LLVMAddInstructionCombiningPass(PM: PassManagerRef); + fn LLVMAddCFGSimplificationPass(PM: PassManagerRef); + fn LLVMAddFunctionInliningPass(PM: PassManagerRef); + fn LLVMAddFunctionAttrsPass(PM: PassManagerRef); + fn LLVMAddScalarReplAggregatesPass(PM: PassManagerRef); + fn LLVMAddScalarReplAggregatesPassSSA(PM: PassManagerRef); + fn LLVMAddJumpThreadingPass(PM: PassManagerRef); + fn LLVMAddConstantPropagationPass(PM: PassManagerRef); + fn LLVMAddReassociatePass(PM: PassManagerRef); + fn LLVMAddLoopRotatePass(PM: PassManagerRef); + fn LLVMAddLICMPass(PM: PassManagerRef); + fn LLVMAddLoopUnswitchPass(PM: PassManagerRef); + fn LLVMAddLoopDeletionPass(PM: PassManagerRef); + fn LLVMAddLoopUnrollPass(PM: PassManagerRef); + fn LLVMAddGVNPass(PM: PassManagerRef); + fn LLVMAddMemCpyOptPass(PM: PassManagerRef); + fn LLVMAddSCCPPass(PM: PassManagerRef); + fn LLVMAddDeadStoreEliminationPass(PM: PassManagerRef); + fn LLVMAddStripDeadPrototypesPass(PM: PassManagerRef); + fn LLVMAddConstantMergePass(PM: PassManagerRef); + fn LLVMAddArgumentPromotionPass(PM: PassManagerRef); + fn LLVMAddTailCallEliminationPass(PM: PassManagerRef); + fn LLVMAddIndVarSimplifyPass(PM: PassManagerRef); + fn LLVMAddAggressiveDCEPass(PM: PassManagerRef); + fn LLVMAddGlobalDCEPass(PM: PassManagerRef); + fn LLVMAddCorrelatedValuePropagationPass(PM: PassManagerRef); + fn LLVMAddPruneEHPass(PM: PassManagerRef); + fn LLVMAddSimplifyLibCallsPass(PM: PassManagerRef); + fn LLVMAddLoopIdiomPass(PM: PassManagerRef); + fn LLVMAddEarlyCSEPass(PM: PassManagerRef); + fn LLVMAddTypeBasedAliasAnalysisPass(PM: PassManagerRef); + fn LLVMAddBasicAliasAnalysisPass(PM: PassManagerRef); + + fn LLVMPassManagerBuilderCreate() -> PassManagerBuilderRef; + fn LLVMPassManagerBuilderDispose(PMB: PassManagerBuilderRef); + fn LLVMPassManagerBuilderSetOptLevel(PMB: PassManagerBuilderRef, + OptimizationLevel: unsigned); + fn LLVMPassManagerBuilderSetSizeLevel(PMB: PassManagerBuilderRef, + Value: Bool); + fn LLVMPassManagerBuilderSetDisableUnitAtATime(PMB: PassManagerBuilderRef, + Value: Bool); + fn LLVMPassManagerBuilderSetDisableUnrollLoops(PMB: PassManagerBuilderRef, + Value: Bool); + fn LLVMPassManagerBuilderSetDisableSimplifyLibCalls + (PMB: PassManagerBuilderRef, Value: Bool); + fn LLVMPassManagerBuilderUseInlinerWithThreshold + (PMB: PassManagerBuilderRef, threshold: unsigned); + fn LLVMPassManagerBuilderPopulateModulePassManager + (PMB: PassManagerBuilderRef, PM: PassManagerRef); + + fn LLVMPassManagerBuilderPopulateFunctionPassManager + (PMB: PassManagerBuilderRef, PM: PassManagerRef); + + /** Destroys a memory buffer. */ + fn LLVMDisposeMemoryBuffer(MemBuf: MemoryBufferRef); + + + /* Stuff that's in rustllvm/ because it's not upstream yet. */ + + /** Opens an object file. */ + fn LLVMCreateObjectFile(MemBuf: MemoryBufferRef) -> ObjectFileRef; + /** Closes an object file. */ + fn LLVMDisposeObjectFile(ObjectFile: ObjectFileRef); + + /** Enumerates the sections in an object file. */ + fn LLVMGetSections(ObjectFile: ObjectFileRef) -> SectionIteratorRef; + /** Destroys a section iterator. */ + fn LLVMDisposeSectionIterator(SI: SectionIteratorRef); + /** Returns true if the section iterator is at the end of the section + list: */ + fn LLVMIsSectionIteratorAtEnd(ObjectFile: ObjectFileRef, + SI: SectionIteratorRef) -> Bool; + /** Moves the section iterator to point to the next section. */ + fn LLVMMoveToNextSection(SI: SectionIteratorRef); + /** Returns the current section name. */ + fn LLVMGetSectionName(SI: SectionIteratorRef) -> sbuf; + /** Returns the current section size. */ + fn LLVMGetSectionSize(SI: SectionIteratorRef) -> ulonglong; + /** Returns the current section contents as a string buffer. */ + fn LLVMGetSectionContents(SI: SectionIteratorRef) -> sbuf; + + /** Reads the given file and returns it as a memory buffer. Use + LLVMDisposeMemoryBuffer() to get rid of it. */ + fn LLVMRustCreateMemoryBufferWithContentsOfFile(Path: sbuf) -> + MemoryBufferRef; + + /* FIXME: The FileType is an enum.*/ + fn LLVMRustWriteOutputFile(PM: PassManagerRef, M: ModuleRef, Triple: sbuf, + Output: sbuf, FileType: c_int, OptLevel: c_int, + EnableSegmentedStacks: bool); + + /** Returns a string describing the last error caused by an LLVMRust* + call. */ + fn LLVMRustGetLastError() -> sbuf; + + /** Parses the bitcode in the given memory buffer. */ + fn LLVMRustParseBitcode(MemBuf: MemoryBufferRef) -> ModuleRef; + + /** Parses LLVM asm in the given file */ + fn LLVMRustParseAssemblyFile(Filename: sbuf) -> ModuleRef; + + /** FiXME: Hacky adaptor for lack of ULongLong in FFI: */ + fn LLVMRustConstInt(IntTy: TypeRef, N_hi: unsigned, N_lo: unsigned, + SignExtend: Bool) -> ValueRef; + + fn LLVMRustAddPrintModulePass(PM: PassManagerRef, M: ModuleRef, + Output: sbuf); + + /** Turn on LLVM pass-timing. */ + fn LLVMRustEnableTimePasses(); + + /** Print the pass timings since static dtors aren't picking them up. */ + fn LLVMRustPrintPassTimings(); + + fn LLVMStructCreateNamed(C: ContextRef, Name: sbuf) -> TypeRef; + + fn LLVMStructSetBody(StructTy: TypeRef, ElementTypes: *TypeRef, + ElementCount: unsigned, Packed: Bool); + + fn LLVMConstNamedStruct(S: TypeRef, ConstantVals: *ValueRef, + Count: unsigned) -> ValueRef; + + /** Links LLVM modules together. `Src` is destroyed by this call and + must never be referenced again. */ + fn LLVMLinkModules(Dest: ModuleRef, Src: ModuleRef) -> Bool; +} + +fn SetInstructionCallConv(Instr: ValueRef, CC: CallConv) { + llvm::LLVMSetInstructionCallConv(Instr, CC as unsigned); +} +fn SetFunctionCallConv(Fn: ValueRef, CC: CallConv) { + llvm::LLVMSetFunctionCallConv(Fn, CC as unsigned); +} +fn SetLinkage(Global: ValueRef, Link: Linkage) { + llvm::LLVMSetLinkage(Global, Link as unsigned); +} + +/* Memory-managed object interface to type handles. */ + +type type_names = @{type_names: std::map::hashmap<TypeRef, str>, + named_types: std::map::hashmap<str, TypeRef>}; + +fn associate_type(tn: type_names, s: str, t: TypeRef) { + assert tn.type_names.insert(t, s); + assert tn.named_types.insert(s, t); +} + +fn type_has_name(tn: type_names, t: TypeRef) -> option<str> { + ret tn.type_names.find(t); +} + +fn name_has_type(tn: type_names, s: str) -> option<TypeRef> { + ret tn.named_types.find(s); +} + +fn mk_type_names() -> type_names { + fn hash(&&t: TypeRef) -> uint { ret t as uint; } + fn eq(&&a: TypeRef, &&b: TypeRef) -> bool { ret a as uint == b as uint; } + @{type_names: std::map::mk_hashmap(hash, eq), + named_types: std::map::new_str_hash()} +} + +fn type_to_str(names: type_names, ty: TypeRef) -> str { + ret type_to_str_inner(names, [], ty); +} + +fn type_to_str_inner(names: type_names, outer0: [TypeRef], ty: TypeRef) -> + str { + alt type_has_name(names, ty) { + option::some(n) { ret n; } + _ {} + } + + let outer = outer0 + [ty]; + + let kind: int = llvm::LLVMGetTypeKind(ty) as int; + + fn tys_str(names: type_names, outer: [TypeRef], tys: [TypeRef]) -> str { + let s: str = ""; + let first: bool = true; + for t: TypeRef in tys { + if first { first = false; } else { s += ", "; } + s += type_to_str_inner(names, outer, t); + } + ret s; + } + + alt kind { + // FIXME: more enum-as-int constants determined from Core::h; + // horrible, horrible. Complete as needed. + 0 { ret "Void"; } + 1 { ret "Half"; } + 2 { ret "Float"; } + 3 { ret "Double"; } + 4 { ret "X86_FP80"; } + 5 { ret "FP128"; } + 6 { ret "PPC_FP128"; } + 7 { ret "Label"; } + 8 { + ret "i" + int::str(llvm::LLVMGetIntTypeWidth(ty) as int); + } + 9 { + let s = "fn("; + let out_ty: TypeRef = llvm::LLVMGetReturnType(ty); + let n_args = llvm::LLVMCountParamTypes(ty) as uint; + let args: [TypeRef] = vec::init_elt::<TypeRef>(n_args, 0 as TypeRef); + unsafe { + llvm::LLVMGetParamTypes(ty, vec::unsafe::to_ptr(args)); + } + s += tys_str(names, outer, args); + s += ") -> "; + s += type_to_str_inner(names, outer, out_ty); + ret s; + } + 10 { + let s: str = "{"; + let n_elts = llvm::LLVMCountStructElementTypes(ty) as uint; + let elts: [TypeRef] = vec::init_elt::<TypeRef>(n_elts, 0 as TypeRef); + unsafe { + llvm::LLVMGetStructElementTypes(ty, vec::unsafe::to_ptr(elts)); + } + s += tys_str(names, outer, elts); + s += "}"; + ret s; + } + 11 { + let el_ty = llvm::LLVMGetElementType(ty); + ret "[" + type_to_str_inner(names, outer, el_ty) + " x " + + uint::str(llvm::LLVMGetArrayLength(ty) as uint) + "]"; + } + 12 { + let i: uint = 0u; + for tout: TypeRef in outer0 { + i += 1u; + if tout as int == ty as int { + let n: uint = vec::len::<TypeRef>(outer0) - i; + ret "*\\" + int::str(n as int); + } + } + ret "*" + + type_to_str_inner(names, outer, llvm::LLVMGetElementType(ty)); + } + 13 { ret "Vector"; } + 14 { ret "Metadata"; } + 15 { ret "X86_MMAX"; } + _ { #error("unknown TypeKind %d", kind as int); fail; } + } +} + +fn float_width(llt: TypeRef) -> uint { + ret alt llvm::LLVMGetTypeKind(llt) as int { + 1 { 32u } + 2 { 64u } + 3 { 80u } + 4 | 5 { 128u } + _ { fail "llvm_float_width called on a non-float type" } + }; +} + +fn fn_ty_param_tys(fn_ty: TypeRef) -> [TypeRef] unsafe { + let args = vec::init_elt(llvm::LLVMCountParamTypes(fn_ty) as uint, + 0 as TypeRef); + llvm::LLVMGetParamTypes(fn_ty, vec::unsafe::to_ptr(args)); + ret args; +} + + +/* Memory-managed interface to target data. */ + +resource target_data_res(TD: TargetDataRef) { + llvm::LLVMDisposeTargetData(TD); +} + +type target_data = {lltd: TargetDataRef, dtor: @target_data_res}; + +fn mk_target_data(string_rep: str) -> target_data { + let lltd = + str::as_buf(string_rep, {|buf| llvm::LLVMCreateTargetData(buf) }); + ret {lltd: lltd, dtor: @target_data_res(lltd)}; +} + +/* Memory-managed interface to pass managers. */ + +resource pass_manager_res(PM: PassManagerRef) { + llvm::LLVMDisposePassManager(PM); +} + +type pass_manager = {llpm: PassManagerRef, dtor: @pass_manager_res}; + +fn mk_pass_manager() -> pass_manager { + let llpm = llvm::LLVMCreatePassManager(); + ret {llpm: llpm, dtor: @pass_manager_res(llpm)}; +} + +/* Memory-managed interface to object files. */ + +resource object_file_res(ObjectFile: ObjectFileRef) { + llvm::LLVMDisposeObjectFile(ObjectFile); +} + +type object_file = {llof: ObjectFileRef, dtor: @object_file_res}; + +fn mk_object_file(llmb: MemoryBufferRef) -> option<object_file> { + let llof = llvm::LLVMCreateObjectFile(llmb); + if llof as int == 0 { ret option::none::<object_file>; } + ret option::some({llof: llof, dtor: @object_file_res(llof)}); +} + +/* Memory-managed interface to section iterators. */ + +resource section_iter_res(SI: SectionIteratorRef) { + llvm::LLVMDisposeSectionIterator(SI); +} + +type section_iter = {llsi: SectionIteratorRef, dtor: @section_iter_res}; + +fn mk_section_iter(llof: ObjectFileRef) -> section_iter { + let llsi = llvm::LLVMGetSections(llof); + ret {llsi: llsi, dtor: @section_iter_res(llsi)}; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/metadata/astencode.rs b/src/rustc/metadata/astencode.rs new file mode 100644 index 00000000000..bd1309c7c86 --- /dev/null +++ b/src/rustc/metadata/astencode.rs @@ -0,0 +1,925 @@ +import syntax::ast; +import syntax::fold; +import syntax::visit; +import syntax::ast_util; +import syntax::ast_util::inlined_item_methods; +import syntax::codemap::span; +import std::map::map; +import std::smallintmap::map; +import std::ebml; +import std::ebml::writer; +import std::serialization; +import std::serialization::serializer; +import std::serialization::deserializer; +import std::serialization::serializer_helpers; +import std::serialization::deserializer_helpers; +import middle::trans::common::maps; +import middle::{ty, typeck, last_use, ast_map}; +import middle::typeck::method_origin; +import middle::typeck::dict_res; +import middle::typeck::dict_origin; +import driver::session::session; +import middle::freevars::freevar_entry; +import c = common; +import e = encoder; + +// used in testing: +import std::io; +import driver::diagnostic; +import syntax::codemap; +import syntax::parse::parser; +import syntax::print::pprust; + +export encode_inlined_item; +export decode_inlined_item; + +type decode_ctxt = @{ + cdata: cstore::crate_metadata, + tcx: ty::ctxt, + maps: maps +}; + +type extended_decode_ctxt = @{ + dcx: decode_ctxt, + from_id_range: id_range, + to_id_range: id_range +}; + +iface tr { + fn tr(xcx: extended_decode_ctxt) -> self; +} + +// ______________________________________________________________________ +// Top-level methods. + +fn encode_inlined_item(ecx: @e::encode_ctxt, + ebml_w: ebml::writer, + path: ast_map::path, + ii: ast::inlined_item) { + #debug["> Encoding inlined item: %s::%s (%u)", + ast_map::path_to_str(path), ii.ident(), + ebml_w.writer.tell()]; + + let id_range = compute_id_range(ii); + ebml_w.wr_tag(c::tag_ast as uint) {|| + encode_id_range(ebml_w, id_range); + encode_ast(ebml_w, ii); + encode_side_tables_for_ii(ecx, ebml_w, ii); + } + + #debug["< Encoded inlined fn: %s::%s (%u)", + ast_map::path_to_str(path), ii.ident(), + ebml_w.writer.tell()]; +} + +fn decode_inlined_item(cdata: cstore::crate_metadata, + tcx: ty::ctxt, + maps: maps, + path: ast_map::path, + par_doc: ebml::doc) -> option<ast::inlined_item> { + let dcx = @{cdata: cdata, tcx: tcx, maps: maps}; + alt par_doc.opt_child(c::tag_ast) { + none { none } + some(ast_doc) { + #debug["> Decoding inlined fn: %s::?", ast_map::path_to_str(path)]; + let from_id_range = decode_id_range(ast_doc); + let to_id_range = reserve_id_range(dcx.tcx.sess, from_id_range); + let xcx = @{dcx: dcx, + from_id_range: from_id_range, + to_id_range: to_id_range}; + let raw_ii = decode_ast(ast_doc); + let ii = renumber_ast(xcx, raw_ii); + ast_map::map_decoded_item(dcx.tcx.items, path, ii); + #debug["Fn named: %s", ii.ident()]; + decode_side_tables(xcx, ast_doc); + #debug["< Decoded inlined fn: %s::%s", + ast_map::path_to_str(path), ii.ident()]; + some(ii) + } + } +} + +// ______________________________________________________________________ +// Enumerating the IDs which appear in an AST + +type id_range = {min: ast::node_id, max: ast::node_id}; + +fn empty(range: id_range) -> bool { + range.min >= range.max +} + +fn visit_ids(item: ast::inlined_item, vfn: fn@(ast::node_id)) { + let visitor = visit::mk_simple_visitor(@{ + visit_mod: fn@(_m: ast::_mod, _sp: span, id: ast::node_id) { + vfn(id) + }, + + visit_view_item: fn@(vi: @ast::view_item) { + alt vi.node { + ast::view_item_use(_, _, id) { vfn(id) } + ast::view_item_import(vps) | ast::view_item_export(vps) { + vec::iter(vps) {|vp| + alt vp.node { + ast::view_path_simple(_, _, id) { vfn(id) } + ast::view_path_glob(_, id) { vfn(id) } + ast::view_path_list(_, _, id) { vfn(id) } + } + } + } + } + }, + + visit_native_item: fn@(ni: @ast::native_item) { + vfn(ni.id) + }, + + visit_item: fn@(i: @ast::item) { + vfn(i.id); + }, + + visit_local: fn@(l: @ast::local) { + vfn(l.node.id); + }, + + visit_block: fn@(b: ast::blk) { + vfn(b.node.id); + }, + + visit_stmt: fn@(s: @ast::stmt) { + vfn(ast_util::stmt_id(*s)); + }, + + visit_arm: fn@(_a: ast::arm) { }, + + visit_pat: fn@(p: @ast::pat) { + vfn(p.id) + }, + + visit_decl: fn@(_d: @ast::decl) { + }, + + visit_expr: fn@(e: @ast::expr) { + vfn(e.id); + alt e.node { + ast::expr_unary(_, _) | ast::expr_binary(_, _, _) { + vfn(ast_util::op_expr_callee_id(e)); + } + _ { /* fallthrough */ } + } + }, + + visit_ty: fn@(t: @ast::ty) { + alt t.node { + ast::ty_path(_, id) { + vfn(id) + } + _ { /* fall through */ } + } + }, + + visit_ty_params: fn@(ps: [ast::ty_param]) { + vec::iter(ps) {|p| vfn(p.id) } + }, + + visit_constr: fn@(_p: @ast::path, _sp: span, id: ast::node_id) { + vfn(id); + }, + + visit_fn: fn@(fk: visit::fn_kind, d: ast::fn_decl, + _b: ast::blk, _sp: span, id: ast::node_id) { + vfn(id); + + alt fk { + visit::fk_item_fn(_, tps) | + visit::fk_method(_, tps) | + visit::fk_res(_, tps) { + vec::iter(tps) {|tp| vfn(tp.id)} + } + visit::fk_anon(_) | + visit::fk_fn_block { + } + } + + vec::iter(d.inputs) {|arg| + vfn(arg.id) + } + }, + + visit_class_item: fn@(_s: span, _p: ast::privacy, + c: ast::class_member) { + alt c { + ast::instance_var(_, _, _, id) { + vfn(id) + } + ast::class_method(_) { + } + } + } + }); + + item.accept((), visitor) +} + +fn compute_id_range(item: ast::inlined_item) -> id_range { + let min = @mutable int::max_value; + let max = @mutable int::min_value; + visit_ids(item) {|id| + *min = int::min(*min, id); + *max = int::max(*max, id + 1); + } + ret {min:*min, max:*max}; +} + +fn encode_id_range(ebml_w: ebml::writer, id_range: id_range) { + ebml_w.wr_tag(c::tag_id_range as uint) {|| + ebml_w.emit_tup(2u) {|| + ebml_w.emit_tup_elt(0u) {|| ebml_w.emit_int(id_range.min) } + ebml_w.emit_tup_elt(1u) {|| ebml_w.emit_int(id_range.max) } + } + } +} + +fn decode_id_range(par_doc: ebml::doc) -> id_range { + let range_doc = par_doc[c::tag_id_range]; + let dsr = serialization::mk_ebml_deserializer(range_doc); + dsr.read_tup(2u) {|| + {min: dsr.read_tup_elt(0u) {|| dsr.read_int() }, + max: dsr.read_tup_elt(1u) {|| dsr.read_int() }} + } +} + +fn reserve_id_range(sess: session, + from_id_range: id_range) -> id_range { + // Handle the case of an empty range: + if empty(from_id_range) { ret from_id_range; } + let cnt = from_id_range.max - from_id_range.min; + let to_id_min = sess.parse_sess.next_id; + let to_id_max = sess.parse_sess.next_id + cnt; + sess.parse_sess.next_id = to_id_max; + ret {min: to_id_min, max: to_id_min}; +} + +impl translation_routines for extended_decode_ctxt { + fn tr_id(id: ast::node_id) -> ast::node_id { + // from_id_range should be non-empty + assert !empty(self.from_id_range); + (id - self.from_id_range.min + self.to_id_range.min) + } + fn tr_def_id(did: ast::def_id) -> ast::def_id { + decoder::translate_def_id(self.dcx.cdata, did) + } + fn tr_intern_def_id(did: ast::def_id) -> ast::def_id { + assert did.crate == ast::local_crate; + {crate: ast::local_crate, node: self.tr_id(did.node)} + } + fn tr_span(_span: span) -> span { + ast_util::dummy_sp() // TODO... + } +} + +impl of tr for ast::def_id { + fn tr(xcx: extended_decode_ctxt) -> ast::def_id { + xcx.tr_def_id(self) + } + fn tr_intern(xcx: extended_decode_ctxt) -> ast::def_id { + xcx.tr_intern_def_id(self) + } +} + +impl of tr for span { + fn tr(xcx: extended_decode_ctxt) -> span { + xcx.tr_span(self) + } +} + +impl serializer_helpers<S: serialization::serializer> for S { + fn emit_def_id(did: ast::def_id) { + astencode_gen::serialize_syntax_ast_def_id(self, did) + } +} + +impl deserializer_helpers<D: serialization::deserializer> for D { + fn read_def_id(xcx: extended_decode_ctxt) -> ast::def_id { + let did = astencode_gen::deserialize_syntax_ast_def_id(self); + did.tr(xcx) + } +} + +// ______________________________________________________________________ +// Encoding and decoding the AST itself +// +// The hard work is done by an autogenerated module astencode_gen. To +// regenerate astencode_gen, run src/etc/gen-astencode. It will +// replace astencode_gen with a dummy file and regenerate its +// contents. If you get compile errors, the dummy file +// remains---resolve the errors and then rerun astencode_gen. +// Annoying, I know, but hopefully only temporary. +// +// When decoding, we have to renumber the AST so that the node ids that +// appear within are disjoint from the node ids in our existing ASTs. +// We also have to adjust the spans: for now we just insert a dummy span, +// but eventually we should add entries to the local codemap as required. + +fn encode_ast(ebml_w: ebml::writer, item: ast::inlined_item) { + ebml_w.wr_tag(c::tag_tree as uint) {|| + astencode_gen::serialize_syntax_ast_inlined_item(ebml_w, item) + } +} + +fn decode_ast(par_doc: ebml::doc) -> ast::inlined_item { + let chi_doc = par_doc[c::tag_tree]; + let d = serialization::mk_ebml_deserializer(chi_doc); + astencode_gen::deserialize_syntax_ast_inlined_item(d) +} + +fn renumber_ast(xcx: extended_decode_ctxt, ii: ast::inlined_item) + -> ast::inlined_item { + let fld = fold::make_fold({ + new_id: xcx.tr_id(_), + new_span: xcx.tr_span(_) + with *fold::default_ast_fold() + }); + + alt ii { + ast::ii_item(i) { + ast::ii_item(fld.fold_item(i)) + } + ast::ii_method(d, m) { + ast::ii_method(xcx.tr_def_id(d), fld.fold_method(m)) + } + } +} + +// ______________________________________________________________________ +// Encoding and decoding of ast::def + +fn encode_def(ebml_w: ebml::writer, def: ast::def) { + astencode_gen::serialize_syntax_ast_def(ebml_w, def) +} + +fn decode_def(xcx: extended_decode_ctxt, doc: ebml::doc) -> ast::def { + let dsr = serialization::mk_ebml_deserializer(doc); + let def = astencode_gen::deserialize_syntax_ast_def(dsr); + def.tr(xcx) +} + +impl of tr for ast::def { + fn tr(xcx: extended_decode_ctxt) -> ast::def { + alt self { + ast::def_fn(did, p) { ast::def_fn(did.tr(xcx), p) } + ast::def_self(nid) { ast::def_self(xcx.tr_id(nid)) } + ast::def_mod(did) { ast::def_mod(did.tr(xcx)) } + ast::def_native_mod(did) { ast::def_native_mod(did.tr(xcx)) } + ast::def_const(did) { ast::def_const(did.tr(xcx)) } + ast::def_arg(nid, m) { ast::def_arg(xcx.tr_id(nid), m) } + ast::def_local(nid, b) { ast::def_local(xcx.tr_id(nid), b) } + ast::def_variant(e_did, v_did) { + ast::def_variant(e_did.tr(xcx), v_did.tr(xcx)) + } + ast::def_ty(did) { ast::def_ty(did.tr(xcx)) } + ast::def_prim_ty(p) { ast::def_prim_ty(p) } + ast::def_ty_param(did, v) { ast::def_ty_param(did.tr(xcx), v) } + ast::def_binding(nid) { ast::def_binding(xcx.tr_id(nid)) } + ast::def_use(did) { ast::def_use(did.tr(xcx)) } + ast::def_upvar(nid1, def, nid2) { + ast::def_upvar(xcx.tr_id(nid1), @(*def).tr(xcx), xcx.tr_id(nid2)) + } + ast::def_class(did) { + ast::def_class(did.tr(xcx)) + } + ast::def_class_field(did0, did1) { + ast::def_class_field(did0.tr(xcx), did1.tr(xcx)) + } + ast::def_class_method(did0, did1) { + ast::def_class_method(did0.tr(xcx), did1.tr(xcx)) + } + } + } +} + +// ______________________________________________________________________ +// Encoding and decoding of freevar information + +fn encode_freevar_entry(ebml_w: ebml::writer, fv: freevar_entry) { + astencode_gen::serialize_middle_freevars_freevar_entry(ebml_w, fv) +} + +impl helper for serialization::ebml_deserializer { + fn read_freevar_entry(xcx: extended_decode_ctxt) -> freevar_entry { + let fv = + astencode_gen::deserialize_middle_freevars_freevar_entry(self); + fv.tr(xcx) + } +} + +impl of tr for freevar_entry { + fn tr(xcx: extended_decode_ctxt) -> freevar_entry { + {def: self.def.tr(xcx), span: self.span.tr(xcx)} + } +} + +// ______________________________________________________________________ +// Encoding and decoding of method_origin + +fn encode_method_origin(ebml_w: ebml::writer, mo: method_origin) { + astencode_gen::serialize_middle_typeck_method_origin(ebml_w, mo) +} + +impl helper for serialization::ebml_deserializer { + fn read_method_origin(xcx: extended_decode_ctxt) -> method_origin { + let fv = astencode_gen::deserialize_middle_typeck_method_origin(self); + fv.tr(xcx) + } +} + +impl of tr for method_origin { + fn tr(xcx: extended_decode_ctxt) -> method_origin { + alt self { + typeck::method_static(did) { + typeck::method_static(did.tr(xcx)) + } + typeck::method_param(did, m, p, b) { + typeck::method_param(did.tr(xcx), m, p, b) + } + typeck::method_iface(did, m) { + typeck::method_iface(did.tr(xcx), m) + } + } + } +} + +// ______________________________________________________________________ +// Encoding and decoding dict_res + +fn encode_dict_res(ecx: @e::encode_ctxt, + ebml_w: ebml::writer, + dr: typeck::dict_res) { + // can't autogenerate this code because automatic serialization of + // ty::t doesn't work, and there is no way (atm) to have + // hand-written serialization routines combine with auto-generated + // ones. perhaps we should fix this. + ebml_w.emit_from_vec(*dr) {|dict_origin| + encode_dict_origin(ecx, ebml_w, dict_origin) + } +} + +fn encode_dict_origin(ecx: @e::encode_ctxt, + ebml_w: ebml::writer, + dict_origin: typeck::dict_origin) { + ebml_w.emit_enum("dict_origin") {|| + alt dict_origin { + typeck::dict_static(def_id, tys, dict_res) { + ebml_w.emit_enum_variant("dict_static", 0u, 3u) {|| + ebml_w.emit_enum_variant_arg(0u) {|| + ebml_w.emit_def_id(def_id) + } + ebml_w.emit_enum_variant_arg(1u) {|| + ebml_w.emit_tys(ecx, tys); + } + ebml_w.emit_enum_variant_arg(2u) {|| + encode_dict_res(ecx, ebml_w, dict_res); + } + } + } + typeck::dict_param(pn, bn) { + ebml_w.emit_enum_variant("dict_param", 1u, 2u) {|| + ebml_w.emit_enum_variant_arg(0u) {|| + ebml_w.emit_uint(pn); + } + ebml_w.emit_enum_variant_arg(1u) {|| + ebml_w.emit_uint(bn); + } + } + } + typeck::dict_iface(def_id) { + ebml_w.emit_enum_variant("dict_iface", 1u, 3u) {|| + ebml_w.emit_enum_variant_arg(0u) {|| + ebml_w.emit_def_id(def_id) + } + } + } + } + } + +} + +impl helpers for serialization::ebml_deserializer { + fn read_dict_res(xcx: extended_decode_ctxt) -> typeck::dict_res { + @self.read_to_vec {|| self.read_dict_origin(xcx) } + } + + fn read_dict_origin(xcx: extended_decode_ctxt) -> typeck::dict_origin { + self.read_enum("dict_origin") {|| + self.read_enum_variant {|i| + alt check i { + 0u { + typeck::dict_static( + self.read_enum_variant_arg(0u) {|| + self.read_def_id(xcx) + }, + self.read_enum_variant_arg(1u) {|| + self.read_tys(xcx) + }, + self.read_enum_variant_arg(2u) {|| + self.read_dict_res(xcx) + } + ) + } + 1u { + typeck::dict_param( + self.read_enum_variant_arg(0u) {|| + self.read_uint() + }, + self.read_enum_variant_arg(1u) {|| + self.read_uint() + } + ) + } + 2u { + typeck::dict_iface( + self.read_enum_variant_arg(0u) {|| + self.read_def_id(xcx) + } + ) + } + } + } + } + } +} + +// ______________________________________________________________________ +// Encoding and decoding the side tables + +impl helpers for @e::encode_ctxt { + fn ty_str_ctxt() -> @tyencode::ctxt { + @{ds: e::def_to_str, + tcx: self.ccx.tcx, + abbrevs: tyencode::ac_use_abbrevs(self.type_abbrevs)} + } +} + +impl helpers for ebml::writer { + fn emit_ty(ecx: @e::encode_ctxt, ty: ty::t) { + e::write_type(ecx, self, ty) + } + + fn emit_tys(ecx: @e::encode_ctxt, tys: [ty::t]) { + self.emit_from_vec(tys) {|ty| + e::write_type(ecx, self, ty) + } + } + + fn emit_bounds(ecx: @e::encode_ctxt, bs: ty::param_bounds) { + tyencode::enc_bounds(self.writer, ecx.ty_str_ctxt(), bs) + } + + fn emit_tpbt(ecx: @e::encode_ctxt, tpbt: ty::ty_param_bounds_and_ty) { + self.emit_rec {|| + self.emit_rec_field("bounds", 0u) {|| + self.emit_from_vec(*tpbt.bounds) {|bs| + self.emit_bounds(ecx, bs) + } + } + self.emit_rec_field("ty", 0u) {|| + self.emit_ty(ecx, tpbt.ty); + } + } + } +} + +impl writer for ebml::writer { + fn tag(tag_id: c::astencode_tag, f: fn()) { + self.wr_tag(tag_id as uint) {|| f() } + } + + fn id(id: ast::node_id) { + self.wr_tagged_u64(c::tag_table_id as uint, id as u64) + } +} + +fn encode_side_tables_for_ii(ecx: @e::encode_ctxt, + ebml_w: ebml::writer, + ii: ast::inlined_item) { + ebml_w.wr_tag(c::tag_table as uint) {|| + visit_ids(ii, fn@(id: ast::node_id) { + // Note: this will cause a copy of ebml_w, which is bad as + // it has mutable fields. But I believe it's harmless since + // we generate balanced EBML. + encode_side_tables_for_id(ecx, ebml_w, id) + }); + } +} + +fn encode_side_tables_for_id(ecx: @e::encode_ctxt, + ebml_w: ebml::writer, + id: ast::node_id) { + + let ccx = ecx.ccx; + let tcx = ccx.tcx; + + #debug["Encoding side tables for id %d", id]; + + option::may(tcx.def_map.find(id)) {|def| + ebml_w.tag(c::tag_table_def) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + astencode_gen::serialize_syntax_ast_def(ebml_w, def) + } + } + } + option::may((*tcx.node_types).find(id as uint)) {|ty| + ebml_w.tag(c::tag_table_node_type) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + e::write_type(ecx, ebml_w, ty) + } + } + } + + option::may(tcx.node_type_substs.find(id)) {|tys| + ebml_w.tag(c::tag_table_node_type_subst) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + ebml_w.emit_tys(ecx, tys) + } + } + } + + option::may(tcx.freevars.find(id)) {|fv| + ebml_w.tag(c::tag_table_freevars) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + ebml_w.emit_from_vec(*fv) {|fv_entry| + encode_freevar_entry(ebml_w, *fv_entry) + } + } + } + } + + let lid = {crate: ast::local_crate, node: id}; + option::may(tcx.tcache.find(lid)) {|tpbt| + ebml_w.tag(c::tag_table_tcache) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + ebml_w.emit_tpbt(ecx, tpbt); + } + } + } + + option::may(tcx.ty_param_bounds.find(id)) {|pbs| + ebml_w.tag(c::tag_table_param_bounds) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + ebml_w.emit_bounds(ecx, pbs) + } + } + } + + // I believe it is not necessary to encode this information. The + // ids will appear in the AST but in the *type* information, which + // is what we actually use in trans, all modes will have been + // resolved. + // + //option::may(tcx.inferred_modes.find(id)) {|m| + // ebml_w.tag(c::tag_table_inferred_modes) {|| + // ebml_w.id(id); + // ebml_w.tag(c::tag_table_val) {|| + // tyencode::enc_mode(ebml_w.writer, ty_str_ctxt(), m); + // } + // } + //} + + option::may(ccx.maps.mutbl_map.find(id)) {|_m| + ebml_w.tag(c::tag_table_mutbl) {|| + ebml_w.id(id); + } + } + + option::may(ccx.maps.copy_map.find(id)) {|_m| + ebml_w.tag(c::tag_table_copy) {|| + ebml_w.id(id); + } + } + + option::may(ccx.maps.last_uses.find(id)) {|_m| + ebml_w.tag(c::tag_table_last_use) {|| + ebml_w.id(id); + } + } + + // impl_map is not used except when emitting metadata, + // don't need to keep it. + + option::may(ccx.maps.method_map.find(id)) {|mo| + ebml_w.tag(c::tag_table_method_map) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + astencode_gen:: + serialize_middle_typeck_method_origin(ebml_w, mo) + } + } + } + + option::may(ccx.maps.dict_map.find(id)) {|dr| + ebml_w.tag(c::tag_table_dict_map) {|| + ebml_w.id(id); + ebml_w.tag(c::tag_table_val) {|| + encode_dict_res(ecx, ebml_w, dr); + } + } + } +} + +impl decoder for ebml::doc { + fn as_int() -> int { ebml::doc_as_u64(self) as int } + fn [](tag: c::astencode_tag) -> ebml::doc { + ebml::get_doc(self, tag as uint) + } + fn opt_child(tag: c::astencode_tag) -> option<ebml::doc> { + ebml::maybe_get_doc(self, tag as uint) + } +} + +impl decoder for serialization::ebml_deserializer { + fn read_ty(xcx: extended_decode_ctxt) -> ty::t { + tydecode::parse_ty_data( + self.parent.data, xcx.dcx.cdata.cnum, self.pos, xcx.dcx.tcx, + xcx.tr_def_id(_)) + } + + fn read_tys(xcx: extended_decode_ctxt) -> [ty::t] { + self.read_to_vec {|| self.read_ty(xcx) } + } + + fn read_bounds(xcx: extended_decode_ctxt) -> @[ty::param_bound] { + tydecode::parse_bounds_data( + self.parent.data, self.pos, xcx.dcx.cdata.cnum, xcx.dcx.tcx, + xcx.tr_def_id(_)) + } + + fn read_ty_param_bounds_and_ty(xcx: extended_decode_ctxt) + -> ty::ty_param_bounds_and_ty { + self.read_rec {|| + { + bounds: self.read_rec_field("bounds", 0u) {|| + @self.read_to_vec {|| self.read_bounds(xcx) } + }, + ty: self.read_rec_field("ty", 1u) {|| + self.read_ty(xcx) + } + } + } + } +} + +fn decode_side_tables(xcx: extended_decode_ctxt, + ast_doc: ebml::doc) { + let dcx = xcx.dcx; + let tbl_doc = ast_doc[c::tag_table]; + ebml::docs(tbl_doc) {|tag, entry_doc| + let id0 = entry_doc[c::tag_table_id].as_int(); + let id = xcx.tr_id(id0); + + #debug[">> Side table document with tag 0x%x \ + found for id %d (orig %d)", + tag, id, id0]; + + if tag == (c::tag_table_mutbl as uint) { + dcx.maps.mutbl_map.insert(id, ()); + } else if tag == (c::tag_table_copy as uint) { + dcx.maps.copy_map.insert(id, ()); + } else if tag == (c::tag_table_last_use as uint) { + dcx.maps.last_uses.insert(id, last_use::is_last_use); + } else { + let val_doc = entry_doc[c::tag_table_val]; + let val_dsr = serialization::mk_ebml_deserializer(val_doc); + if tag == (c::tag_table_def as uint) { + let def = decode_def(xcx, val_doc); + dcx.tcx.def_map.insert(id, def); + } else if tag == (c::tag_table_node_type as uint) { + let ty = val_dsr.read_ty(xcx); + (*dcx.tcx.node_types).insert(id as uint, ty); + } else if tag == (c::tag_table_node_type_subst as uint) { + let tys = val_dsr.read_tys(xcx); + dcx.tcx.node_type_substs.insert(id, tys); + } else if tag == (c::tag_table_freevars as uint) { + let fv_info = @val_dsr.read_to_vec {|| + @val_dsr.read_freevar_entry(xcx) + }; + dcx.tcx.freevars.insert(id, fv_info); + } else if tag == (c::tag_table_tcache as uint) { + let tpbt = val_dsr.read_ty_param_bounds_and_ty(xcx); + let lid = {crate: ast::local_crate, node: id}; + dcx.tcx.tcache.insert(lid, tpbt); + } else if tag == (c::tag_table_param_bounds as uint) { + let bounds = val_dsr.read_bounds(xcx); + dcx.tcx.ty_param_bounds.insert(id, bounds); + } else if tag == (c::tag_table_method_map as uint) { + dcx.maps.method_map.insert(id, + val_dsr.read_method_origin(xcx)); + } else if tag == (c::tag_table_dict_map as uint) { + dcx.maps.dict_map.insert(id, + val_dsr.read_dict_res(xcx)); + } else { + xcx.dcx.tcx.sess.bug( + #fmt["Unknown tag found in side tables: %x", tag]); + } + } + + #debug[">< Side table doc loaded"]; + } +} + +// ______________________________________________________________________ +// Testing of astencode_gen + +#[cfg(test)] +fn encode_item_ast(ebml_w: ebml::writer, item: @ast::item) { + ebml_w.wr_tag(c::tag_tree as uint) {|| + astencode_gen::serialize_syntax_ast_item(ebml_w, *item); + } +} + +#[cfg(test)] +fn decode_item_ast(par_doc: ebml::doc) -> @ast::item { + let chi_doc = par_doc[c::tag_tree]; + let d = serialization::mk_ebml_deserializer(chi_doc); + @astencode_gen::deserialize_syntax_ast_item(d) +} + +#[cfg(test)] +fn new_parse_sess() -> parser::parse_sess { + let cm = codemap::new_codemap(); + let handler = diagnostic::mk_handler(option::none); + let sess = @{ + cm: cm, + mutable next_id: 1, + span_diagnostic: diagnostic::mk_span_handler(handler, cm), + mutable chpos: 0u, + mutable byte_pos: 0u + }; + ret sess; +} + +#[cfg(test)] +iface fake_ext_ctxt { + fn session() -> fake_session; +} + +#[cfg(test)] +type fake_options = {cfg: ast::crate_cfg}; + +#[cfg(test)] +type fake_session = {opts: @fake_options, + parse_sess: parser::parse_sess}; + +#[cfg(test)] +impl of fake_ext_ctxt for fake_session { + fn session() -> fake_session {self} +} + +#[cfg(test)] +fn mk_ctxt() -> fake_ext_ctxt { + let opts : fake_options = {cfg: []}; + {opts: @opts, parse_sess: new_parse_sess()} as fake_ext_ctxt +} + +#[cfg(test)] +fn roundtrip(in_item: @ast::item) { + #debug["in_item = %s", pprust::item_to_str(in_item)]; + let mbuf = io::mk_mem_buffer(); + let ebml_w = ebml::mk_writer(io::mem_buffer_writer(mbuf)); + encode_item_ast(ebml_w, in_item); + let ebml_doc = ebml::new_doc(@io::mem_buffer_buf(mbuf)); + let out_item = decode_item_ast(ebml_doc); + #debug["out_item = %s", pprust::item_to_str(out_item)]; + assert in_item == out_item; +} + +#[test] +fn test_basic() { + let ext_cx = mk_ctxt(); + roundtrip(#ast(item){ + fn foo() {} + }); +} + +#[test] +fn test_smalltalk() { + let ext_cx = mk_ctxt(); + roundtrip(#ast(item){ + fn foo() -> int { 3 + 4 } // first smalltalk program ever executed. + }); +} + +#[test] +fn test_more() { + let ext_cx = mk_ctxt(); + roundtrip(#ast(item){ + fn foo(x: uint, y: uint) -> uint { + let z = x + y; + ret z; + } + }); +} \ No newline at end of file diff --git a/src/rustc/metadata/astencode_gen.rs b/src/rustc/metadata/astencode_gen.rs new file mode 100644 index 00000000000..1c413de261c --- /dev/null +++ b/src/rustc/metadata/astencode_gen.rs @@ -0,0 +1,8831 @@ +/*syntax::ast::ident*/ +fn serialize_1<S: std::serialization::serializer>(s: S, + v: syntax::ast::ident) { + + s.emit_str(v); +} +/*syntax::ast::attr_style*/ +fn serialize_5<S: std::serialization::serializer>(s: S, + v: + syntax::ast::attr_style) { + s.emit_enum("syntax::ast::attr_style", + + {|| + alt v { + syntax::ast::attr_outer { + s.emit_enum_variant("syntax::ast::attr_outer", 0u, 0u, + {|| }) + } + syntax::ast::attr_inner { + s.emit_enum_variant("syntax::ast::attr_inner", 1u, 0u, + {|| }) + } + } + }); +} +/*@syntax::ast::meta_item*/ +fn serialize_9<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::meta_item) { + s.emit_box(/*syntax::ast::meta_item*/{|| serialize_6(s, *v) }); +} +/*[@syntax::ast::meta_item]*/ +fn serialize_8<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::meta_item]) { + s.emit_vec(vec::len(v), /*@syntax::ast::meta_item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_9(s, e) }) + }) + }); +} +/*str*/ +fn serialize_12<S: std::serialization::serializer>(s: S, v: str) { + + s.emit_str(v); +} +/*i64*/ +fn serialize_13<S: std::serialization::serializer>(s: S, v: i64) { + + s.emit_i64(v); +} +/*syntax::ast::int_ty*/ +fn serialize_14<S: std::serialization::serializer>(s: S, + v: syntax::ast::int_ty) { + + s.emit_enum("syntax::ast::int_ty", + + + + + + {|| + alt v { + syntax::ast::ty_i { + s.emit_enum_variant("syntax::ast::ty_i", 0u, 0u, + {|| }) + } + syntax::ast::ty_char { + s.emit_enum_variant("syntax::ast::ty_char", 1u, 0u, + {|| }) + } + syntax::ast::ty_i8 { + s.emit_enum_variant("syntax::ast::ty_i8", 2u, 0u, + {|| }) + } + syntax::ast::ty_i16 { + s.emit_enum_variant("syntax::ast::ty_i16", 3u, 0u, + {|| }) + } + syntax::ast::ty_i32 { + s.emit_enum_variant("syntax::ast::ty_i32", 4u, 0u, + {|| }) + } + syntax::ast::ty_i64 { + s.emit_enum_variant("syntax::ast::ty_i64", 5u, 0u, + {|| }) + } + } + }); +} +/*u64*/ +fn serialize_15<S: std::serialization::serializer>(s: S, v: u64) { + + s.emit_u64(v); +} +/*syntax::ast::uint_ty*/ +fn serialize_16<S: std::serialization::serializer>(s: S, + v: syntax::ast::uint_ty) { + + s.emit_enum("syntax::ast::uint_ty", + + + + + {|| + alt v { + syntax::ast::ty_u { + s.emit_enum_variant("syntax::ast::ty_u", 0u, 0u, + {|| }) + } + syntax::ast::ty_u8 { + s.emit_enum_variant("syntax::ast::ty_u8", 1u, 0u, + {|| }) + } + syntax::ast::ty_u16 { + s.emit_enum_variant("syntax::ast::ty_u16", 2u, 0u, + {|| }) + } + syntax::ast::ty_u32 { + s.emit_enum_variant("syntax::ast::ty_u32", 3u, 0u, + {|| }) + } + syntax::ast::ty_u64 { + s.emit_enum_variant("syntax::ast::ty_u64", 4u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::float_ty*/ +fn serialize_17<S: std::serialization::serializer>(s: S, + v: syntax::ast::float_ty) { + + s.emit_enum("syntax::ast::float_ty", + + + {|| + alt v { + syntax::ast::ty_f { + s.emit_enum_variant("syntax::ast::ty_f", 0u, 0u, + {|| }) + } + syntax::ast::ty_f32 { + s.emit_enum_variant("syntax::ast::ty_f32", 1u, 0u, + {|| }) + } + syntax::ast::ty_f64 { + s.emit_enum_variant("syntax::ast::ty_f64", 2u, 0u, + {|| }) + } + } + }); +} +/*bool*/ +fn serialize_18<S: std::serialization::serializer>(s: S, v: bool) { + + s.emit_bool(v); +} +/*syntax::ast::lit_*/ +fn serialize_11<S: std::serialization::serializer>(s: S, + v: syntax::ast::lit_) { + + s.emit_enum("syntax::ast::lit_", + /*str*/ + /*i64*//*syntax::ast::int_ty*/ + /*u64*//*syntax::ast::uint_ty*/ + /*str*//*syntax::ast::float_ty*/ + + /*bool*/ + {|| + alt v { + syntax::ast::lit_str(v0) { + s.emit_enum_variant("syntax::ast::lit_str", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_12(s, + v0) + }) + } + }) + } + syntax::ast::lit_int(v0, v1) { + s.emit_enum_variant("syntax::ast::lit_int", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_13(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_14(s, + v1) + }) + } + }) + } + syntax::ast::lit_uint(v0, v1) { + s.emit_enum_variant("syntax::ast::lit_uint", 2u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_15(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_16(s, + v1) + }) + } + }) + } + syntax::ast::lit_float(v0, v1) { + s.emit_enum_variant("syntax::ast::lit_float", 3u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_12(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_17(s, + v1) + }) + } + }) + } + syntax::ast::lit_nil { + s.emit_enum_variant("syntax::ast::lit_nil", 4u, 0u, + {|| }) + } + syntax::ast::lit_bool(v0) { + s.emit_enum_variant("syntax::ast::lit_bool", 5u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_18(s, + v0) + }) + } + }) + } + } + }); +} +/*uint*/ +fn serialize_20<S: std::serialization::serializer>(s: S, v: uint) { + + s.emit_uint(v); +} +/*core::option::t<syntax::codemap::span>*/ +fn serialize_26<S: std::serialization::serializer>(s: S, + v: + core::option::t<syntax::codemap::span>) { + s.emit_enum("core::option::t", + + /*syntax::codemap::span*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_19(s, + v0) + }) + } + }) + } + } + }); +} +/*{name: str,span: core::option::t<syntax::codemap::span>}*/ +fn serialize_25<S: std::serialization::serializer>(s: S, + v: + {name: str, + span: + core::option::t<syntax::codemap::span>,}) { + s.emit_rec(/*str*//*core::option::t<syntax::codemap::span>*/ + {|| + { + s.emit_rec_field("name", 0u, + {|| serialize_12(s, v.name) }); + s.emit_rec_field("span", 1u, + {|| serialize_26(s, v.span) }) + } + }); +} + +/*{call_site: syntax::codemap::span,callie: {name: str,span: core::option::t<syntax::codemap::span>}}*/ +fn serialize_24<S: std::serialization::serializer>(s: S, + v: + {call_site: + syntax::codemap::span, + callie: + {name: str, + span: + core::option::t<syntax::codemap::span>,},}) { + s.emit_rec(/*syntax::codemap::span*/ + /*{name: str,span: core::option::t<syntax::codemap::span>}*/ + {|| + { + s.emit_rec_field("call_site", 0u, + {|| serialize_19(s, v.call_site) }); + s.emit_rec_field("callie", 1u, + {|| serialize_25(s, v.callie) }) + } + }); +} +/*syntax::codemap::expn_info_*/ +fn serialize_23<S: std::serialization::serializer>(s: S, + v: + syntax::codemap::expn_info_) { + s.emit_enum("syntax::codemap::expn_info_", + + /*{call_site: syntax::codemap::span,callie: {name: str,span: core::option::t<syntax::codemap::span>}}*/ + {|| + alt v { + syntax::codemap::expanded_from(v0) { + s.emit_enum_variant("syntax::codemap::expanded_from", + 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_24(s, + v0) + }) + } + }) + } + } + }); +} +/*@syntax::codemap::expn_info_*/ +fn serialize_22<S: std::serialization::serializer>(s: S, + v: + @syntax::codemap::expn_info_) { + s.emit_box(/*syntax::codemap::expn_info_*/{|| serialize_23(s, *v) }); +} +/*syntax::codemap::expn_info<@syntax::codemap::expn_info_>*/ +fn serialize_21<S: std::serialization::serializer>(s: S, + v: + syntax::codemap::expn_info<@syntax::codemap::expn_info_>) { + s.emit_enum("core::option::t", + + /*@syntax::codemap::expn_info_*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_22(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::codemap::span*/ +fn serialize_19<S: std::serialization::serializer>(s: S, + v: syntax::codemap::span) { + + s.emit_rec(/*uint*//*uint*/ + /*syntax::codemap::expn_info<@syntax::codemap::expn_info_>*/ + {|| + { + s.emit_rec_field("lo", 0u, + {|| serialize_20(s, v.lo) }); + s.emit_rec_field("hi", 1u, + {|| serialize_20(s, v.hi) }); + s.emit_rec_field("expn_info", 2u, + {|| serialize_21(s, v.expn_info) }) + } + }); +} +/*syntax::ast::lit*/ +fn serialize_10<S: std::serialization::serializer>(s: S, + v: syntax::ast::lit) { + + s.emit_rec(/*syntax::ast::lit_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_11(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*syntax::ast::meta_item_*/ +fn serialize_7<S: std::serialization::serializer>(s: S, + v: + syntax::ast::meta_item_) { + s.emit_enum("syntax::ast::meta_item_", + /*syntax::ast::ident*/ + /*syntax::ast::ident*//*[@syntax::ast::meta_item]*/ + /*syntax::ast::ident*//*syntax::ast::lit*/ + {|| + alt v { + syntax::ast::meta_word(v0) { + s.emit_enum_variant("syntax::ast::meta_word", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }) + } + }) + } + syntax::ast::meta_list(v0, v1) { + s.emit_enum_variant("syntax::ast::meta_list", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_8(s, + v1) + }) + } + }) + } + syntax::ast::meta_name_value(v0, v1) { + s.emit_enum_variant("syntax::ast::meta_name_value", + 2u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_10(s, + v1) + }) + } + }) + } + } + }); +} +/*syntax::ast::meta_item*/ +fn serialize_6<S: std::serialization::serializer>(s: S, + v: syntax::ast::meta_item) { + + s.emit_rec(/*syntax::ast::meta_item_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_7(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*syntax::ast::attribute_*/ +fn serialize_4<S: std::serialization::serializer>(s: S, + v: + syntax::ast::attribute_) { + s.emit_rec(/*syntax::ast::attr_style*//*syntax::ast::meta_item*/ + {|| + { + s.emit_rec_field("style", 0u, + {|| serialize_5(s, v.style) }); + s.emit_rec_field("value", 1u, + {|| serialize_6(s, v.value) }) + } + }); +} +/*syntax::ast::attribute*/ +fn serialize_3<S: std::serialization::serializer>(s: S, + v: syntax::ast::attribute) { + + s.emit_rec(/*syntax::ast::attribute_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_4(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::attribute]*/ +fn serialize_2<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::attribute]) { + s.emit_vec(vec::len(v), /*syntax::ast::attribute*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_3(s, e) }) + }) + }); +} +/*syntax::ast::node_id*/ +fn serialize_27<S: std::serialization::serializer>(s: S, + v: syntax::ast::node_id) { + + s.emit_int(v); +} +/*syntax::ast::mutability*/ +fn serialize_33<S: std::serialization::serializer>(s: S, + v: + syntax::ast::mutability) { + s.emit_enum("syntax::ast::mutability", + + + {|| + alt v { + syntax::ast::m_mutbl { + s.emit_enum_variant("syntax::ast::m_mutbl", 0u, 0u, + {|| }) + } + syntax::ast::m_imm { + s.emit_enum_variant("syntax::ast::m_imm", 1u, 0u, + {|| }) + } + syntax::ast::m_const { + s.emit_enum_variant("syntax::ast::m_const", 2u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::mt*/ +fn serialize_32<S: std::serialization::serializer>(s: S, v: syntax::ast::mt) { + + s.emit_rec(/*@syntax::ast::ty*//*syntax::ast::mutability*/ + {|| + { + s.emit_rec_field("ty", 0u, + {|| serialize_29(s, v.ty) }); + s.emit_rec_field("mutbl", 1u, + {|| serialize_33(s, v.mutbl) }) + } + }); +} +/*syntax::ast::ty_field_*/ +fn serialize_36<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_field_) { + s.emit_rec(/*syntax::ast::ident*//*syntax::ast::mt*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("mt", 1u, {|| serialize_32(s, v.mt) }) + } + }); +} +/*syntax::ast::ty_field*/ +fn serialize_35<S: std::serialization::serializer>(s: S, + v: syntax::ast::ty_field) { + + s.emit_rec(/*syntax::ast::ty_field_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_36(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::ty_field]*/ +fn serialize_34<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::ty_field]) { + s.emit_vec(vec::len(v), /*syntax::ast::ty_field*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_35(s, e) }) + }) + }); +} +/*syntax::ast::proto*/ +fn serialize_37<S: std::serialization::serializer>(s: S, + v: syntax::ast::proto) { + + s.emit_enum("syntax::ast::proto", + + + + + {|| + alt v { + syntax::ast::proto_bare { + s.emit_enum_variant("syntax::ast::proto_bare", 0u, 0u, + {|| }) + } + syntax::ast::proto_any { + s.emit_enum_variant("syntax::ast::proto_any", 1u, 0u, + {|| }) + } + syntax::ast::proto_uniq { + s.emit_enum_variant("syntax::ast::proto_uniq", 2u, 0u, + {|| }) + } + syntax::ast::proto_box { + s.emit_enum_variant("syntax::ast::proto_box", 3u, 0u, + {|| }) + } + syntax::ast::proto_block { + s.emit_enum_variant("syntax::ast::proto_block", 4u, + 0u, {|| }) + } + } + }); +} +/*syntax::ast::rmode*/ +fn serialize_42<S: std::serialization::serializer>(s: S, + v: syntax::ast::rmode) { + + s.emit_enum("syntax::ast::rmode", + + + + + {|| + alt v { + syntax::ast::by_ref { + s.emit_enum_variant("syntax::ast::by_ref", 0u, 0u, + {|| }) + } + syntax::ast::by_val { + s.emit_enum_variant("syntax::ast::by_val", 1u, 0u, + {|| }) + } + syntax::ast::by_mutbl_ref { + s.emit_enum_variant("syntax::ast::by_mutbl_ref", 2u, + 0u, {|| }) + } + syntax::ast::by_move { + s.emit_enum_variant("syntax::ast::by_move", 3u, 0u, + {|| }) + } + syntax::ast::by_copy { + s.emit_enum_variant("syntax::ast::by_copy", 4u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::mode<syntax::ast::rmode>*/ +fn serialize_41<S: std::serialization::serializer>(s: S, + v: + syntax::ast::mode<syntax::ast::rmode>) { + s.emit_enum("syntax::ast::inferable", + /*syntax::ast::rmode*/ + /*syntax::ast::node_id*/ + {|| + alt v { + syntax::ast::expl(v0) { + s.emit_enum_variant("syntax::ast::expl", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_42(s, + v0) + }) + } + }) + } + syntax::ast::infer(v0) { + s.emit_enum_variant("syntax::ast::infer", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::arg*/ +fn serialize_40<S: std::serialization::serializer>(s: S, + v: syntax::ast::arg) { + + s.emit_rec(/*syntax::ast::mode<syntax::ast::rmode>*//*@syntax::ast::ty*/ + /*syntax::ast::ident*//*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("mode", 0u, + {|| serialize_41(s, v.mode) }); + s.emit_rec_field("ty", 1u, + {|| serialize_29(s, v.ty) }); + s.emit_rec_field("ident", 2u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("id", 3u, {|| serialize_27(s, v.id) }) + } + }); +} +/*[syntax::ast::arg]*/ +fn serialize_39<S: std::serialization::serializer>(s: S, + v: [syntax::ast::arg]) { + + s.emit_vec(vec::len(v), /*syntax::ast::arg*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_40(s, e) }) + }) + }); +} +/*syntax::ast::purity*/ +fn serialize_43<S: std::serialization::serializer>(s: S, + v: syntax::ast::purity) { + + s.emit_enum("syntax::ast::purity", + + + + {|| + alt v { + syntax::ast::pure_fn { + s.emit_enum_variant("syntax::ast::pure_fn", 0u, 0u, + {|| }) + } + syntax::ast::unsafe_fn { + s.emit_enum_variant("syntax::ast::unsafe_fn", 1u, 0u, + {|| }) + } + syntax::ast::impure_fn { + s.emit_enum_variant("syntax::ast::impure_fn", 2u, 0u, + {|| }) + } + syntax::ast::crust_fn { + s.emit_enum_variant("syntax::ast::crust_fn", 3u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::ret_style*/ +fn serialize_44<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ret_style) { + s.emit_enum("syntax::ast::ret_style", + + {|| + alt v { + syntax::ast::noreturn { + s.emit_enum_variant("syntax::ast::noreturn", 0u, 0u, + {|| }) + } + syntax::ast::return_val { + s.emit_enum_variant("syntax::ast::return_val", 1u, 0u, + {|| }) + } + } + }); +} +/*[syntax::ast::ident]*/ +fn serialize_52<S: std::serialization::serializer>(s: S, + v: [syntax::ast::ident]) { + + s.emit_vec(vec::len(v), /*syntax::ast::ident*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_1(s, e) }) + }) + }); +} +/*[@syntax::ast::ty]*/ +fn serialize_53<S: std::serialization::serializer>(s: S, + v: [@syntax::ast::ty]) { + + s.emit_vec(vec::len(v), /*@syntax::ast::ty*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_29(s, e) }) + }) + }); +} +/*syntax::ast::path_*/ +fn serialize_51<S: std::serialization::serializer>(s: S, + v: syntax::ast::path_) { + + s.emit_rec(/*bool*//*[syntax::ast::ident]*//*[@syntax::ast::ty]*/ + {|| + { + s.emit_rec_field("global", 0u, + {|| serialize_18(s, v.global) }); + s.emit_rec_field("idents", 1u, + {|| serialize_52(s, v.idents) }); + s.emit_rec_field("types", 2u, + {|| serialize_53(s, v.types) }) + } + }); +} +/*syntax::ast::path*/ +fn serialize_50<S: std::serialization::serializer>(s: S, + v: syntax::ast::path) { + + s.emit_rec(/*syntax::ast::path_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_51(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::path*/ +fn serialize_49<S: std::serialization::serializer>(s: S, + v: @syntax::ast::path) { + + s.emit_box(/*syntax::ast::path*/{|| serialize_50(s, *v) }); +} +/*@syntax::ast::lit*/ +fn serialize_58<S: std::serialization::serializer>(s: S, + v: @syntax::ast::lit) { + + s.emit_box(/*syntax::ast::lit*/{|| serialize_10(s, *v) }); +} +/*syntax::ast::constr_arg_general_<uint>*/ +fn serialize_57<S: std::serialization::serializer>(s: S, + v: + syntax::ast::constr_arg_general_<uint>) { + s.emit_enum("syntax::ast::constr_arg_general_", + + /*uint*/ + /*@syntax::ast::lit*/ + {|| + alt v { + syntax::ast::carg_base { + s.emit_enum_variant("syntax::ast::carg_base", 0u, 0u, + {|| }) + } + syntax::ast::carg_ident(v0) { + s.emit_enum_variant("syntax::ast::carg_ident", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_20(s, + v0) + }) + } + }) + } + syntax::ast::carg_lit(v0) { + s.emit_enum_variant("syntax::ast::carg_lit", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_58(s, + v0) + }) + } + }) + } + } + }); +} +/*{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ +fn serialize_56<S: std::serialization::serializer>(s: S, + v: + {node: + syntax::ast::constr_arg_general_<uint>, + span: + syntax::codemap::span,}) { + s.emit_rec(/*syntax::ast::constr_arg_general_<uint>*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_57(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} + +/*@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ +fn serialize_55<S: std::serialization::serializer>(s: S, + v: + @{node: + syntax::ast::constr_arg_general_<uint>, + span: + syntax::codemap::span,}) { + s.emit_box( + /*{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ + {|| serialize_56(s, *v) }); +} + +/*[@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}]*/ +fn serialize_54<S: std::serialization::serializer>(s: S, + v: + [@{node: + syntax::ast::constr_arg_general_<uint>, + span: + syntax::codemap::span,}]) { + s.emit_vec(vec::len(v), + /*@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_55(s, e) }) + }) + }); +} + +/*{path: @syntax::ast::path,args: [@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}],id: syntax::ast::node_id}*/ +fn serialize_48<S: std::serialization::serializer>(s: S, + v: + {path: + @syntax::ast::path, + args: + [@{node: + syntax::ast::constr_arg_general_<uint>, + span: + syntax::codemap::span,}], + id: + syntax::ast::node_id,}) { + s.emit_rec(/*@syntax::ast::path*/ + /*[@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}]*/ + /*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("path", 0u, + {|| serialize_49(s, v.path) }); + s.emit_rec_field("args", 1u, + {|| serialize_54(s, v.args) }); + s.emit_rec_field("id", 2u, {|| serialize_27(s, v.id) }) + } + }); +} +/*syntax::ast::constr*/ +fn serialize_47<S: std::serialization::serializer>(s: S, + v: syntax::ast::constr) { + + s.emit_rec( + /*{path: @syntax::ast::path,args: [@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}],id: syntax::ast::node_id}*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_48(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::constr*/ +fn serialize_46<S: std::serialization::serializer>(s: S, + v: @syntax::ast::constr) { + + s.emit_box(/*syntax::ast::constr*/{|| serialize_47(s, *v) }); +} +/*[@syntax::ast::constr]*/ +fn serialize_45<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::constr]) { + s.emit_vec(vec::len(v), /*@syntax::ast::constr*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_46(s, e) }) + }) + }); +} +/*syntax::ast::fn_decl*/ +fn serialize_38<S: std::serialization::serializer>(s: S, + v: syntax::ast::fn_decl) { + + s.emit_rec(/*[syntax::ast::arg]*//*@syntax::ast::ty*/ + /*syntax::ast::purity*//*syntax::ast::ret_style*/ + /*[@syntax::ast::constr]*/ + {|| + { + s.emit_rec_field("inputs", 0u, + {|| serialize_39(s, v.inputs) }); + s.emit_rec_field("output", 1u, + {|| serialize_29(s, v.output) }); + s.emit_rec_field("purity", 2u, + {|| serialize_43(s, v.purity) }); + s.emit_rec_field("cf", 3u, + {|| serialize_44(s, v.cf) }); + s.emit_rec_field("constraints", 4u, + {|| serialize_45(s, v.constraints) }) + } + }); +} +/*syntax::ast::constr_arg_general_<@syntax::ast::path>*/ +fn serialize_66<S: std::serialization::serializer>(s: S, + v: + syntax::ast::constr_arg_general_<@syntax::ast::path>) { + s.emit_enum("syntax::ast::constr_arg_general_", + + /*@syntax::ast::path*/ + /*@syntax::ast::lit*/ + {|| + alt v { + syntax::ast::carg_base { + s.emit_enum_variant("syntax::ast::carg_base", 0u, 0u, + {|| }) + } + syntax::ast::carg_ident(v0) { + s.emit_enum_variant("syntax::ast::carg_ident", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }) + } + }) + } + syntax::ast::carg_lit(v0) { + s.emit_enum_variant("syntax::ast::carg_lit", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_58(s, + v0) + }) + } + }) + } + } + }); +} + +/*{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ +fn serialize_65<S: std::serialization::serializer>(s: S, + v: + {node: + syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: + syntax::codemap::span,}) { + s.emit_rec(/*syntax::ast::constr_arg_general_<@syntax::ast::path>*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_66(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} + +/*@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ +fn serialize_64<S: std::serialization::serializer>(s: S, + v: + @{node: + syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: + syntax::codemap::span,}) { + s.emit_box( + /*{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ + {|| serialize_65(s, *v) }); +} + +/*[@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}]*/ +fn serialize_63<S: std::serialization::serializer>(s: S, + v: + [@{node: + syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: + syntax::codemap::span,}]) { + s.emit_vec(vec::len(v), + /*@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_64(s, e) }) + }) + }); +} +/*syntax::ast::ty_constr_*/ +fn serialize_62<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_constr_) { + s.emit_rec(/*@syntax::ast::path*/ + /*[@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}]*/ + /*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("path", 0u, + {|| serialize_49(s, v.path) }); + s.emit_rec_field("args", 1u, + {|| serialize_63(s, v.args) }); + s.emit_rec_field("id", 2u, {|| serialize_27(s, v.id) }) + } + }); +} +/*syntax::ast::ty_constr*/ +fn serialize_61<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_constr) { + s.emit_rec(/*syntax::ast::ty_constr_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_62(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::ty_constr*/ +fn serialize_60<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::ty_constr) { + s.emit_box(/*syntax::ast::ty_constr*/{|| serialize_61(s, *v) }); +} +/*[@syntax::ast::ty_constr]*/ +fn serialize_59<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::ty_constr]) { + s.emit_vec(vec::len(v), /*@syntax::ast::ty_constr*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_60(s, e) }) + }) + }); +} +/*[@syntax::ast::expr]*/ +fn serialize_73<S: std::serialization::serializer>(s: S, + v: [@syntax::ast::expr]) { + + s.emit_vec(vec::len(v), /*@syntax::ast::expr*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_70(s, e) }) + }) + }); +} +/*syntax::ast::field_*/ +fn serialize_76<S: std::serialization::serializer>(s: S, + v: syntax::ast::field_) { + + s.emit_rec(/*syntax::ast::mutability*//*syntax::ast::ident*/ + /*@syntax::ast::expr*/ + {|| + { + s.emit_rec_field("mutbl", 0u, + {|| serialize_33(s, v.mutbl) }); + s.emit_rec_field("ident", 1u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("expr", 2u, + {|| serialize_70(s, v.expr) }) + } + }); +} +/*syntax::ast::field*/ +fn serialize_75<S: std::serialization::serializer>(s: S, + v: syntax::ast::field) { + + s.emit_rec(/*syntax::ast::field_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_76(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::field]*/ +fn serialize_74<S: std::serialization::serializer>(s: S, + v: [syntax::ast::field]) { + + s.emit_vec(vec::len(v), /*syntax::ast::field*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_75(s, e) }) + }) + }); +} +/*core::option::t<@syntax::ast::expr>*/ +fn serialize_77<S: std::serialization::serializer>(s: S, + v: + core::option::t<@syntax::ast::expr>) { + s.emit_enum("core::option::t", + + /*@syntax::ast::expr*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + } + }); +} +/*[core::option::t<@syntax::ast::expr>]*/ +fn serialize_78<S: std::serialization::serializer>(s: S, + v: + [core::option::t<@syntax::ast::expr>]) { + s.emit_vec(vec::len(v), /*core::option::t<@syntax::ast::expr>*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_77(s, e) }) + }) + }); +} +/*syntax::ast::binop*/ +fn serialize_79<S: std::serialization::serializer>(s: S, + v: syntax::ast::binop) { + + s.emit_enum("syntax::ast::binop", + + + + + + + + + + + + + + + + + + + {|| + alt v { + syntax::ast::add { + s.emit_enum_variant("syntax::ast::add", 0u, 0u, {|| }) + } + syntax::ast::subtract { + s.emit_enum_variant("syntax::ast::subtract", 1u, 0u, + {|| }) + } + syntax::ast::mul { + s.emit_enum_variant("syntax::ast::mul", 2u, 0u, {|| }) + } + syntax::ast::div { + s.emit_enum_variant("syntax::ast::div", 3u, 0u, {|| }) + } + syntax::ast::rem { + s.emit_enum_variant("syntax::ast::rem", 4u, 0u, {|| }) + } + syntax::ast::and { + s.emit_enum_variant("syntax::ast::and", 5u, 0u, {|| }) + } + syntax::ast::or { + s.emit_enum_variant("syntax::ast::or", 6u, 0u, {|| }) + } + syntax::ast::bitxor { + s.emit_enum_variant("syntax::ast::bitxor", 7u, 0u, + {|| }) + } + syntax::ast::bitand { + s.emit_enum_variant("syntax::ast::bitand", 8u, 0u, + {|| }) + } + syntax::ast::bitor { + s.emit_enum_variant("syntax::ast::bitor", 9u, 0u, + {|| }) + } + syntax::ast::lsl { + s.emit_enum_variant("syntax::ast::lsl", 10u, 0u, + {|| }) + } + syntax::ast::lsr { + s.emit_enum_variant("syntax::ast::lsr", 11u, 0u, + {|| }) + } + syntax::ast::asr { + s.emit_enum_variant("syntax::ast::asr", 12u, 0u, + {|| }) + } + syntax::ast::eq { + s.emit_enum_variant("syntax::ast::eq", 13u, 0u, {|| }) + } + syntax::ast::lt { + s.emit_enum_variant("syntax::ast::lt", 14u, 0u, {|| }) + } + syntax::ast::le { + s.emit_enum_variant("syntax::ast::le", 15u, 0u, {|| }) + } + syntax::ast::ne { + s.emit_enum_variant("syntax::ast::ne", 16u, 0u, {|| }) + } + syntax::ast::ge { + s.emit_enum_variant("syntax::ast::ge", 17u, 0u, {|| }) + } + syntax::ast::gt { + s.emit_enum_variant("syntax::ast::gt", 18u, 0u, {|| }) + } + } + }); +} +/*syntax::ast::unop*/ +fn serialize_80<S: std::serialization::serializer>(s: S, + v: syntax::ast::unop) { + + s.emit_enum("syntax::ast::unop", + /*syntax::ast::mutability*/ + /*syntax::ast::mutability*/ + + + {|| + alt v { + syntax::ast::box(v0) { + s.emit_enum_variant("syntax::ast::box", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_33(s, + v0) + }) + } + }) + } + syntax::ast::uniq(v0) { + s.emit_enum_variant("syntax::ast::uniq", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_33(s, + v0) + }) + } + }) + } + syntax::ast::deref { + s.emit_enum_variant("syntax::ast::deref", 2u, 0u, + {|| }) + } + syntax::ast::not { + s.emit_enum_variant("syntax::ast::not", 3u, 0u, {|| }) + } + syntax::ast::neg { + s.emit_enum_variant("syntax::ast::neg", 4u, 0u, {|| }) + } + } + }); +} +/*syntax::ast::simple_path*/ +fn serialize_92<S: std::serialization::serializer>(s: S, + v: + syntax::ast::simple_path) { + s.emit_vec(vec::len(v), /*syntax::ast::ident*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_1(s, e) }) + }) + }); +} +/*@syntax::ast::simple_path*/ +fn serialize_91<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::simple_path) { + s.emit_box(/*syntax::ast::simple_path*/{|| serialize_92(s, *v) }); +} +/*syntax::ast::path_list_ident_*/ +fn serialize_95<S: std::serialization::serializer>(s: S, + v: + syntax::ast::path_list_ident_) { + s.emit_rec(/*syntax::ast::ident*//*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("name", 0u, + {|| serialize_1(s, v.name) }); + s.emit_rec_field("id", 1u, {|| serialize_27(s, v.id) }) + } + }); +} +/*syntax::ast::path_list_ident*/ +fn serialize_94<S: std::serialization::serializer>(s: S, + v: + syntax::ast::path_list_ident) { + s.emit_rec(/*syntax::ast::path_list_ident_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_95(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::path_list_ident]*/ +fn serialize_93<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::path_list_ident]) { + s.emit_vec(vec::len(v), /*syntax::ast::path_list_ident*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_94(s, e) }) + }) + }); +} +/*syntax::ast::view_path_*/ +fn serialize_90<S: std::serialization::serializer>(s: S, + v: + syntax::ast::view_path_) { + s.emit_enum("syntax::ast::view_path_", + /*syntax::ast::ident*//*@syntax::ast::simple_path*/ + /*syntax::ast::node_id*/ + /*@syntax::ast::simple_path*//*syntax::ast::node_id*/ + /*@syntax::ast::simple_path*/ + /*[syntax::ast::path_list_ident]*//*syntax::ast::node_id*/ + {|| + alt v { + syntax::ast::view_path_simple(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::view_path_simple", + 0u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_91(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_27(s, + v2) + }) + } + }) + } + syntax::ast::view_path_glob(v0, v1) { + s.emit_enum_variant("syntax::ast::view_path_glob", 1u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_91(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_27(s, + v1) + }) + } + }) + } + syntax::ast::view_path_list(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::view_path_list", 2u, + 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_91(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_93(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_27(s, + v2) + }) + } + }) + } + } + }); +} +/*syntax::ast::view_path*/ +fn serialize_89<S: std::serialization::serializer>(s: S, + v: + syntax::ast::view_path) { + s.emit_rec(/*syntax::ast::view_path_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_90(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::view_path*/ +fn serialize_88<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::view_path) { + s.emit_box(/*syntax::ast::view_path*/{|| serialize_89(s, *v) }); +} +/*[@syntax::ast::view_path]*/ +fn serialize_87<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::view_path]) { + s.emit_vec(vec::len(v), /*@syntax::ast::view_path*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_88(s, e) }) + }) + }); +} +/*syntax::ast::view_item_*/ +fn serialize_86<S: std::serialization::serializer>(s: S, + v: + syntax::ast::view_item_) { + s.emit_enum("syntax::ast::view_item_", + /*syntax::ast::ident*//*[@syntax::ast::meta_item]*/ + /*syntax::ast::node_id*/ + /*[@syntax::ast::view_path]*/ + /*[@syntax::ast::view_path]*/ + {|| + alt v { + syntax::ast::view_item_use(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::view_item_use", 0u, + 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_8(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_27(s, + v2) + }) + } + }) + } + syntax::ast::view_item_import(v0) { + s.emit_enum_variant("syntax::ast::view_item_import", + 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_87(s, + v0) + }) + } + }) + } + syntax::ast::view_item_export(v0) { + s.emit_enum_variant("syntax::ast::view_item_export", + 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_87(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::view_item*/ +fn serialize_85<S: std::serialization::serializer>(s: S, + v: + syntax::ast::view_item) { + s.emit_rec(/*syntax::ast::view_item_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_86(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::view_item*/ +fn serialize_84<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::view_item) { + s.emit_box(/*syntax::ast::view_item*/{|| serialize_85(s, *v) }); +} +/*[@syntax::ast::view_item]*/ +fn serialize_83<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::view_item]) { + s.emit_vec(vec::len(v), /*@syntax::ast::view_item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_84(s, e) }) + }) + }); +} +/*core::option::t<@syntax::ast::pat>*/ +fn serialize_110<S: std::serialization::serializer>(s: S, + v: + core::option::t<@syntax::ast::pat>) { + s.emit_enum("core::option::t", + + /*@syntax::ast::pat*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_107(s, + v0) + }) + } + }) + } + } + }); +} +/*[@syntax::ast::pat]*/ +fn serialize_111<S: std::serialization::serializer>(s: S, + v: [@syntax::ast::pat]) { + + s.emit_vec(vec::len(v), /*@syntax::ast::pat*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_107(s, e) }) + }) + }); +} +/*syntax::ast::field_pat*/ +fn serialize_113<S: std::serialization::serializer>(s: S, + v: + syntax::ast::field_pat) { + s.emit_rec(/*syntax::ast::ident*//*@syntax::ast::pat*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("pat", 1u, + {|| serialize_107(s, v.pat) }) + } + }); +} +/*[syntax::ast::field_pat]*/ +fn serialize_112<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::field_pat]) { + s.emit_vec(vec::len(v), /*syntax::ast::field_pat*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_113(s, e) }) + }) + }); +} +/*syntax::ast::pat_*/ +fn serialize_109<S: std::serialization::serializer>(s: S, + v: syntax::ast::pat_) { + + s.emit_enum("syntax::ast::pat_", + + /*@syntax::ast::path*//*core::option::t<@syntax::ast::pat>*/ + /*@syntax::ast::path*//*[@syntax::ast::pat]*/ + /*[syntax::ast::field_pat]*//*bool*/ + /*[@syntax::ast::pat]*/ + /*@syntax::ast::pat*/ + /*@syntax::ast::pat*/ + /*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + {|| + alt v { + syntax::ast::pat_wild { + s.emit_enum_variant("syntax::ast::pat_wild", 0u, 0u, + {|| }) + } + syntax::ast::pat_ident(v0, v1) { + s.emit_enum_variant("syntax::ast::pat_ident", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_110(s, + v1) + }) + } + }) + } + syntax::ast::pat_enum(v0, v1) { + s.emit_enum_variant("syntax::ast::pat_enum", 2u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_111(s, + v1) + }) + } + }) + } + syntax::ast::pat_rec(v0, v1) { + s.emit_enum_variant("syntax::ast::pat_rec", 3u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_112(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_18(s, + v1) + }) + } + }) + } + syntax::ast::pat_tup(v0) { + s.emit_enum_variant("syntax::ast::pat_tup", 4u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_111(s, + v0) + }) + } + }) + } + syntax::ast::pat_box(v0) { + s.emit_enum_variant("syntax::ast::pat_box", 5u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_107(s, + v0) + }) + } + }) + } + syntax::ast::pat_uniq(v0) { + s.emit_enum_variant("syntax::ast::pat_uniq", 6u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_107(s, + v0) + }) + } + }) + } + syntax::ast::pat_lit(v0) { + s.emit_enum_variant("syntax::ast::pat_lit", 7u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + syntax::ast::pat_range(v0, v1) { + s.emit_enum_variant("syntax::ast::pat_range", 8u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + } + }); +} +/*syntax::ast::pat*/ +fn serialize_108<S: std::serialization::serializer>(s: S, + v: syntax::ast::pat) { + + s.emit_rec(/*syntax::ast::node_id*//*syntax::ast::pat_*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("id", 0u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("node", 1u, + {|| serialize_109(s, v.node) }); + s.emit_rec_field("span", 2u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::pat*/ +fn serialize_107<S: std::serialization::serializer>(s: S, + v: @syntax::ast::pat) { + + s.emit_box(/*syntax::ast::pat*/{|| serialize_108(s, *v) }); +} +/*syntax::ast::init_op*/ +fn serialize_116<S: std::serialization::serializer>(s: S, + v: syntax::ast::init_op) { + + s.emit_enum("syntax::ast::init_op", + + {|| + alt v { + syntax::ast::init_assign { + s.emit_enum_variant("syntax::ast::init_assign", 0u, + 0u, {|| }) + } + syntax::ast::init_move { + s.emit_enum_variant("syntax::ast::init_move", 1u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::initializer*/ +fn serialize_115<S: std::serialization::serializer>(s: S, + v: + syntax::ast::initializer) { + s.emit_rec(/*syntax::ast::init_op*//*@syntax::ast::expr*/ + {|| + { + s.emit_rec_field("op", 0u, + {|| serialize_116(s, v.op) }); + s.emit_rec_field("expr", 1u, + {|| serialize_70(s, v.expr) }) + } + }); +} +/*core::option::t<syntax::ast::initializer>*/ +fn serialize_114<S: std::serialization::serializer>(s: S, + v: + core::option::t<syntax::ast::initializer>) { + s.emit_enum("core::option::t", + + /*syntax::ast::initializer*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_115(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::local_*/ +fn serialize_106<S: std::serialization::serializer>(s: S, + v: syntax::ast::local_) { + + s.emit_rec(/*bool*//*@syntax::ast::ty*//*@syntax::ast::pat*/ + /*core::option::t<syntax::ast::initializer>*/ + /*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("is_mutbl", 0u, + {|| serialize_18(s, v.is_mutbl) }); + s.emit_rec_field("ty", 1u, + {|| serialize_29(s, v.ty) }); + s.emit_rec_field("pat", 2u, + {|| serialize_107(s, v.pat) }); + s.emit_rec_field("init", 3u, + {|| serialize_114(s, v.init) }); + s.emit_rec_field("id", 4u, {|| serialize_27(s, v.id) }) + } + }); +} +/*syntax::ast::local*/ +fn serialize_105<S: std::serialization::serializer>(s: S, + v: syntax::ast::local) { + + s.emit_rec(/*syntax::ast::local_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_106(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::local*/ +fn serialize_104<S: std::serialization::serializer>(s: S, + v: @syntax::ast::local) { + + s.emit_box(/*syntax::ast::local*/{|| serialize_105(s, *v) }); +} +/*[@syntax::ast::local]*/ +fn serialize_103<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::local]) { + s.emit_vec(vec::len(v), /*@syntax::ast::local*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_104(s, e) }) + }) + }); +} +/*@syntax::ast::item*/ +fn serialize_117<S: std::serialization::serializer>(s: S, + v: @syntax::ast::item) { + + s.emit_box(/*syntax::ast::item*/{|| serialize_0(s, *v) }); +} +/*syntax::ast::decl_*/ +fn serialize_102<S: std::serialization::serializer>(s: S, + v: syntax::ast::decl_) { + + s.emit_enum("syntax::ast::decl_", + /*[@syntax::ast::local]*/ + /*@syntax::ast::item*/ + {|| + alt v { + syntax::ast::decl_local(v0) { + s.emit_enum_variant("syntax::ast::decl_local", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_103(s, + v0) + }) + } + }) + } + syntax::ast::decl_item(v0) { + s.emit_enum_variant("syntax::ast::decl_item", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_117(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::decl*/ +fn serialize_101<S: std::serialization::serializer>(s: S, + v: syntax::ast::decl) { + + s.emit_rec(/*syntax::ast::decl_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_102(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::decl*/ +fn serialize_100<S: std::serialization::serializer>(s: S, + v: @syntax::ast::decl) { + + s.emit_box(/*syntax::ast::decl*/{|| serialize_101(s, *v) }); +} +/*syntax::ast::stmt_*/ +fn serialize_99<S: std::serialization::serializer>(s: S, + v: syntax::ast::stmt_) { + + s.emit_enum("syntax::ast::stmt_", + /*@syntax::ast::decl*//*syntax::ast::node_id*/ + /*@syntax::ast::expr*//*syntax::ast::node_id*/ + /*@syntax::ast::expr*//*syntax::ast::node_id*/ + {|| + alt v { + syntax::ast::stmt_decl(v0, v1) { + s.emit_enum_variant("syntax::ast::stmt_decl", 0u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_100(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_27(s, + v1) + }) + } + }) + } + syntax::ast::stmt_expr(v0, v1) { + s.emit_enum_variant("syntax::ast::stmt_expr", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_27(s, + v1) + }) + } + }) + } + syntax::ast::stmt_semi(v0, v1) { + s.emit_enum_variant("syntax::ast::stmt_semi", 2u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_27(s, + v1) + }) + } + }) + } + } + }); +} +/*syntax::ast::stmt*/ +fn serialize_98<S: std::serialization::serializer>(s: S, + v: syntax::ast::stmt) { + + s.emit_rec(/*syntax::ast::stmt_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_99(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::stmt*/ +fn serialize_97<S: std::serialization::serializer>(s: S, + v: @syntax::ast::stmt) { + + s.emit_box(/*syntax::ast::stmt*/{|| serialize_98(s, *v) }); +} +/*[@syntax::ast::stmt]*/ +fn serialize_96<S: std::serialization::serializer>(s: S, + v: [@syntax::ast::stmt]) { + + s.emit_vec(vec::len(v), /*@syntax::ast::stmt*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_97(s, e) }) + }) + }); +} +/*syntax::ast::blk_check_mode*/ +fn serialize_118<S: std::serialization::serializer>(s: S, + v: + syntax::ast::blk_check_mode) { + s.emit_enum("syntax::ast::blk_check_mode", + + + {|| + alt v { + syntax::ast::default_blk { + s.emit_enum_variant("syntax::ast::default_blk", 0u, + 0u, {|| }) + } + syntax::ast::unchecked_blk { + s.emit_enum_variant("syntax::ast::unchecked_blk", 1u, + 0u, {|| }) + } + syntax::ast::unsafe_blk { + s.emit_enum_variant("syntax::ast::unsafe_blk", 2u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::blk_*/ +fn serialize_82<S: std::serialization::serializer>(s: S, + v: syntax::ast::blk_) { + + s.emit_rec(/*[@syntax::ast::view_item]*//*[@syntax::ast::stmt]*/ + /*core::option::t<@syntax::ast::expr>*//*syntax::ast::node_id*/ + /*syntax::ast::blk_check_mode*/ + {|| + { + s.emit_rec_field("view_items", 0u, + {|| serialize_83(s, v.view_items) }); + s.emit_rec_field("stmts", 1u, + {|| serialize_96(s, v.stmts) }); + s.emit_rec_field("expr", 2u, + {|| serialize_77(s, v.expr) }); + s.emit_rec_field("id", 3u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("rules", 4u, + {|| serialize_118(s, v.rules) }) + } + }); +} +/*syntax::ast::blk*/ +fn serialize_81<S: std::serialization::serializer>(s: S, + v: syntax::ast::blk) { + + s.emit_rec(/*syntax::ast::blk_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_82(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*syntax::ast::arm*/ +fn serialize_120<S: std::serialization::serializer>(s: S, + v: syntax::ast::arm) { + + s.emit_rec(/*[@syntax::ast::pat]*//*core::option::t<@syntax::ast::expr>*/ + /*syntax::ast::blk*/ + {|| + { + s.emit_rec_field("pats", 0u, + {|| serialize_111(s, v.pats) }); + s.emit_rec_field("guard", 1u, + {|| serialize_77(s, v.guard) }); + s.emit_rec_field("body", 2u, + {|| serialize_81(s, v.body) }) + } + }); +} +/*[syntax::ast::arm]*/ +fn serialize_119<S: std::serialization::serializer>(s: S, + v: [syntax::ast::arm]) { + + s.emit_vec(vec::len(v), /*syntax::ast::arm*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_120(s, e) }) + }) + }); +} +/*syntax::ast::alt_mode*/ +fn serialize_121<S: std::serialization::serializer>(s: S, + v: + syntax::ast::alt_mode) { + s.emit_enum("syntax::ast::alt_mode", + + {|| + alt v { + syntax::ast::alt_check { + s.emit_enum_variant("syntax::ast::alt_check", 0u, 0u, + {|| }) + } + syntax::ast::alt_exhaustive { + s.emit_enum_variant("syntax::ast::alt_exhaustive", 1u, + 0u, {|| }) + } + } + }); +} +/*int*/ +fn serialize_127<S: std::serialization::serializer>(s: S, v: int) { + + s.emit_int(v); +} +/*syntax::ast::capture_item*/ +fn serialize_126<S: std::serialization::serializer>(s: S, + v: + syntax::ast::capture_item) { + s.emit_rec(/*int*//*syntax::ast::ident*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("id", 0u, + {|| serialize_127(s, v.id) }); + s.emit_rec_field("name", 1u, + {|| serialize_1(s, v.name) }); + s.emit_rec_field("span", 2u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::capture_item*/ +fn serialize_125<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::capture_item) { + s.emit_box(/*syntax::ast::capture_item*/{|| serialize_126(s, *v) }); +} +/*[@syntax::ast::capture_item]*/ +fn serialize_124<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::capture_item]) { + s.emit_vec(vec::len(v), /*@syntax::ast::capture_item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_125(s, e) }) + }) + }); +} +/*syntax::ast::capture_clause*/ +fn serialize_123<S: std::serialization::serializer>(s: S, + v: + syntax::ast::capture_clause) { + s.emit_rec(/*[@syntax::ast::capture_item]*/ + /*[@syntax::ast::capture_item]*/ + {|| + { + s.emit_rec_field("copies", 0u, + {|| serialize_124(s, v.copies) }); + s.emit_rec_field("moves", 1u, + {|| serialize_124(s, v.moves) }) + } + }); +} +/*@syntax::ast::capture_clause*/ +fn serialize_122<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::capture_clause) { + s.emit_box(/*syntax::ast::capture_clause*/{|| serialize_123(s, *v) }); +} +/*syntax::ast::expr_check_mode*/ +fn serialize_128<S: std::serialization::serializer>(s: S, + v: + syntax::ast::expr_check_mode) { + s.emit_enum("syntax::ast::expr_check_mode", + + {|| + alt v { + syntax::ast::claimed_expr { + s.emit_enum_variant("syntax::ast::claimed_expr", 0u, + 0u, {|| }) + } + syntax::ast::checked_expr { + s.emit_enum_variant("syntax::ast::checked_expr", 1u, + 0u, {|| }) + } + } + }); +} +/*syntax::ast::expr_*/ +fn serialize_72<S: std::serialization::serializer>(s: S, + v: syntax::ast::expr_) { + + s.emit_enum("syntax::ast::expr_", + /*[@syntax::ast::expr]*//*syntax::ast::mutability*/ + /*[syntax::ast::field]*/ + /*core::option::t<@syntax::ast::expr>*/ + /*@syntax::ast::expr*//*[@syntax::ast::expr]*//*bool*/ + /*[@syntax::ast::expr]*/ + /*@syntax::ast::expr*/ + /*[core::option::t<@syntax::ast::expr>]*/ + /*syntax::ast::binop*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*/ + /*syntax::ast::unop*//*@syntax::ast::expr*/ + /*@syntax::ast::lit*/ + /*@syntax::ast::expr*//*@syntax::ast::ty*/ + /*@syntax::ast::expr*//*syntax::ast::blk*/ + /*core::option::t<@syntax::ast::expr>*/ + /*@syntax::ast::expr*//*syntax::ast::blk*/ + /*@syntax::ast::local*//*@syntax::ast::expr*/ + /*syntax::ast::blk*/ + /*syntax::ast::blk*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*[syntax::ast::arm]*/ + /*syntax::ast::alt_mode*/ + /*syntax::ast::proto*//*syntax::ast::fn_decl*/ + /*syntax::ast::blk*//*@syntax::ast::capture_clause*/ + /*syntax::ast::fn_decl*//*syntax::ast::blk*/ + /*syntax::ast::blk*/ + /*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + /*syntax::ast::binop*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*syntax::ast::ident*/ + /*[@syntax::ast::ty]*/ + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + /*@syntax::ast::path*/ + /*core::option::t<@syntax::ast::expr>*/ + + + /*core::option::t<@syntax::ast::expr>*/ + /*@syntax::ast::expr*/ + /*int*//*@syntax::ast::expr*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*/ + /*syntax::ast::expr_check_mode*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*//*syntax::ast::blk*/ + /*core::option::t<@syntax::ast::expr>*/ + /*syntax::ast::mac*/ + {|| + alt v { + syntax::ast::expr_vec(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_vec", 0u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_73(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_33(s, + v1) + }) + } + }) + } + syntax::ast::expr_rec(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_rec", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_74(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_77(s, + v1) + }) + } + }) + } + syntax::ast::expr_call(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_call", 2u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_73(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_18(s, + v2) + }) + } + }) + } + syntax::ast::expr_tup(v0) { + s.emit_enum_variant("syntax::ast::expr_tup", 3u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_73(s, + v0) + }) + } + }) + } + syntax::ast::expr_bind(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_bind", 4u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_78(s, + v1) + }) + } + }) + } + syntax::ast::expr_binary(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_binary", 5u, + 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_79(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_70(s, + v2) + }) + } + }) + } + syntax::ast::expr_unary(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_unary", 6u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_80(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_lit(v0) { + s.emit_enum_variant("syntax::ast::expr_lit", 7u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_58(s, + v0) + }) + } + }) + } + syntax::ast::expr_cast(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_cast", 8u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_29(s, + v1) + }) + } + }) + } + syntax::ast::expr_if(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_if", 9u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_81(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_77(s, + v2) + }) + } + }) + } + syntax::ast::expr_while(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_while", 10u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_81(s, + v1) + }) + } + }) + } + syntax::ast::expr_for(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_for", 11u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_104(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_81(s, + v2) + }) + } + }) + } + syntax::ast::expr_do_while(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_do_while", 12u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_81(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_alt(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_alt", 13u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_119(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_121(s, + v2) + }) + } + }) + } + syntax::ast::expr_fn(v0, v1, v2, v3) { + s.emit_enum_variant("syntax::ast::expr_fn", 14u, 4u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_37(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_38(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_81(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_122(s, + v3) + }) + } + }) + } + syntax::ast::expr_fn_block(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_fn_block", 15u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_38(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_81(s, + v1) + }) + } + }) + } + syntax::ast::expr_block(v0) { + s.emit_enum_variant("syntax::ast::expr_block", 16u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_81(s, + v0) + }) + } + }) + } + syntax::ast::expr_copy(v0) { + s.emit_enum_variant("syntax::ast::expr_copy", 17u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + syntax::ast::expr_move(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_move", 18u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_assign(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_assign", 19u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_swap(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_swap", 20u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_assign_op(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_assign_op", + 21u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_79(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_70(s, + v2) + }) + } + }) + } + syntax::ast::expr_field(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_field", 22u, + 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_1(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_53(s, + v2) + }) + } + }) + } + syntax::ast::expr_index(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_index", 23u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_path(v0) { + s.emit_enum_variant("syntax::ast::expr_path", 24u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }) + } + }) + } + syntax::ast::expr_fail(v0) { + s.emit_enum_variant("syntax::ast::expr_fail", 25u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_77(s, + v0) + }) + } + }) + } + syntax::ast::expr_break { + s.emit_enum_variant("syntax::ast::expr_break", 26u, + 0u, {|| }) + } + syntax::ast::expr_cont { + s.emit_enum_variant("syntax::ast::expr_cont", 27u, 0u, + {|| }) + } + syntax::ast::expr_ret(v0) { + s.emit_enum_variant("syntax::ast::expr_ret", 28u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_77(s, + v0) + }) + } + }) + } + syntax::ast::expr_be(v0) { + s.emit_enum_variant("syntax::ast::expr_be", 29u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + syntax::ast::expr_log(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_log", 30u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_127(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_70(s, + v2) + }) + } + }) + } + syntax::ast::expr_assert(v0) { + s.emit_enum_variant("syntax::ast::expr_assert", 31u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + syntax::ast::expr_check(v0, v1) { + s.emit_enum_variant("syntax::ast::expr_check", 32u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_128(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::expr_if_check(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::expr_if_check", 33u, + 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_81(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_77(s, + v2) + }) + } + }) + } + syntax::ast::expr_mac(v0) { + s.emit_enum_variant("syntax::ast::expr_mac", 34u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_67(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::expr*/ +fn serialize_71<S: std::serialization::serializer>(s: S, + v: syntax::ast::expr) { + + s.emit_rec(/*syntax::ast::node_id*//*syntax::ast::expr_*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("id", 0u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("node", 1u, + {|| serialize_72(s, v.node) }); + s.emit_rec_field("span", 2u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::expr*/ +fn serialize_70<S: std::serialization::serializer>(s: S, + v: @syntax::ast::expr) { + + s.emit_box(/*syntax::ast::expr*/{|| serialize_71(s, *v) }); +} +/*syntax::ast::mac_arg<@syntax::ast::expr>*/ +fn serialize_69<S: std::serialization::serializer>(s: S, + v: + syntax::ast::mac_arg<@syntax::ast::expr>) { + s.emit_enum("core::option::t", + + /*@syntax::ast::expr*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_70(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::mac_body_*/ +fn serialize_130<S: std::serialization::serializer>(s: S, + v: + syntax::ast::mac_body_) { + s.emit_rec(/*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("span", 0u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*syntax::ast::mac_body<syntax::ast::mac_body_>*/ +fn serialize_129<S: std::serialization::serializer>(s: S, + v: + syntax::ast::mac_body<syntax::ast::mac_body_>) { + s.emit_enum("core::option::t", + + /*syntax::ast::mac_body_*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_130(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::mac_*/ +fn serialize_68<S: std::serialization::serializer>(s: S, + v: syntax::ast::mac_) { + + s.emit_enum("syntax::ast::mac_", + /*@syntax::ast::path*/ + /*syntax::ast::mac_arg<@syntax::ast::expr>*/ + /*syntax::ast::mac_body<syntax::ast::mac_body_>*/ + /*@syntax::ast::ty*/ + /*syntax::ast::blk*/ + + /*syntax::codemap::span*//*@syntax::ast::expr*/ + /*uint*/ + {|| + alt v { + syntax::ast::mac_invoc(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::mac_invoc", 0u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_69(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_129(s, + v2) + }) + } + }) + } + syntax::ast::mac_embed_type(v0) { + s.emit_enum_variant("syntax::ast::mac_embed_type", 1u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }) + } + }) + } + syntax::ast::mac_embed_block(v0) { + s.emit_enum_variant("syntax::ast::mac_embed_block", + 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_81(s, + v0) + }) + } + }) + } + syntax::ast::mac_ellipsis { + s.emit_enum_variant("syntax::ast::mac_ellipsis", 3u, + 0u, {|| }) + } + syntax::ast::mac_aq(v0, v1) { + s.emit_enum_variant("syntax::ast::mac_aq", 4u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_19(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::mac_var(v0) { + s.emit_enum_variant("syntax::ast::mac_var", 5u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_20(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::mac*/ +fn serialize_67<S: std::serialization::serializer>(s: S, + v: syntax::ast::mac) { + + s.emit_rec(/*syntax::ast::mac_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_68(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*syntax::ast::ty_*/ +fn serialize_31<S: std::serialization::serializer>(s: S, + v: syntax::ast::ty_) { + + s.emit_enum("syntax::ast::ty_", + + + /*syntax::ast::mt*/ + /*syntax::ast::mt*/ + /*syntax::ast::mt*/ + /*syntax::ast::mt*/ + /*[syntax::ast::ty_field]*/ + /*syntax::ast::proto*//*syntax::ast::fn_decl*/ + /*[@syntax::ast::ty]*/ + /*@syntax::ast::path*//*syntax::ast::node_id*/ + /*@syntax::ast::ty*//*[@syntax::ast::ty_constr]*/ + /*syntax::ast::mac*/ + {|| + alt v { + syntax::ast::ty_nil { + s.emit_enum_variant("syntax::ast::ty_nil", 0u, 0u, + {|| }) + } + syntax::ast::ty_bot { + s.emit_enum_variant("syntax::ast::ty_bot", 1u, 0u, + {|| }) + } + syntax::ast::ty_box(v0) { + s.emit_enum_variant("syntax::ast::ty_box", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_32(s, + v0) + }) + } + }) + } + syntax::ast::ty_uniq(v0) { + s.emit_enum_variant("syntax::ast::ty_uniq", 3u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_32(s, + v0) + }) + } + }) + } + syntax::ast::ty_vec(v0) { + s.emit_enum_variant("syntax::ast::ty_vec", 4u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_32(s, + v0) + }) + } + }) + } + syntax::ast::ty_ptr(v0) { + s.emit_enum_variant("syntax::ast::ty_ptr", 5u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_32(s, + v0) + }) + } + }) + } + syntax::ast::ty_rec(v0) { + s.emit_enum_variant("syntax::ast::ty_rec", 6u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_34(s, + v0) + }) + } + }) + } + syntax::ast::ty_fn(v0, v1) { + s.emit_enum_variant("syntax::ast::ty_fn", 7u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_37(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_38(s, + v1) + }) + } + }) + } + syntax::ast::ty_tup(v0) { + s.emit_enum_variant("syntax::ast::ty_tup", 8u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_53(s, + v0) + }) + } + }) + } + syntax::ast::ty_path(v0, v1) { + s.emit_enum_variant("syntax::ast::ty_path", 9u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_49(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_27(s, + v1) + }) + } + }) + } + syntax::ast::ty_constr(v0, v1) { + s.emit_enum_variant("syntax::ast::ty_constr", 10u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_59(s, + v1) + }) + } + }) + } + syntax::ast::ty_mac(v0) { + s.emit_enum_variant("syntax::ast::ty_mac", 11u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_67(s, + v0) + }) + } + }) + } + syntax::ast::ty_infer { + s.emit_enum_variant("syntax::ast::ty_infer", 12u, 0u, + {|| }) + } + } + }); +} +/*syntax::ast::ty*/ +fn serialize_30<S: std::serialization::serializer>(s: S, v: syntax::ast::ty) { + + s.emit_rec(/*syntax::ast::ty_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_31(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::ty*/ +fn serialize_29<S: std::serialization::serializer>(s: S, + v: @syntax::ast::ty) { + + s.emit_box(/*syntax::ast::ty*/{|| serialize_30(s, *v) }); +} +/*syntax::ast::ty_param_bound*/ +fn serialize_135<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_param_bound) { + s.emit_enum("syntax::ast::ty_param_bound", + + + /*@syntax::ast::ty*/ + {|| + alt v { + syntax::ast::bound_copy { + s.emit_enum_variant("syntax::ast::bound_copy", 0u, 0u, + {|| }) + } + syntax::ast::bound_send { + s.emit_enum_variant("syntax::ast::bound_send", 1u, 0u, + {|| }) + } + syntax::ast::bound_iface(v0) { + s.emit_enum_variant("syntax::ast::bound_iface", 2u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }) + } + }) + } + } + }); +} +/*[syntax::ast::ty_param_bound]*/ +fn serialize_134<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::ty_param_bound]) { + s.emit_vec(vec::len(v), /*syntax::ast::ty_param_bound*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_135(s, e) }) + }) + }); +} +/*@[syntax::ast::ty_param_bound]*/ +fn serialize_133<S: std::serialization::serializer>(s: S, + v: + @[syntax::ast::ty_param_bound]) { + s.emit_box(/*[syntax::ast::ty_param_bound]*/{|| serialize_134(s, *v) }); +} +/*syntax::ast::ty_param*/ +fn serialize_132<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_param) { + s.emit_rec(/*syntax::ast::ident*//*syntax::ast::node_id*/ + /*@[syntax::ast::ty_param_bound]*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("id", 1u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("bounds", 2u, + {|| serialize_133(s, v.bounds) }) + } + }); +} +/*[syntax::ast::ty_param]*/ +fn serialize_131<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::ty_param]) { + s.emit_vec(vec::len(v), /*syntax::ast::ty_param*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_132(s, e) }) + }) + }); +} +/*[@syntax::ast::item]*/ +fn serialize_137<S: std::serialization::serializer>(s: S, + v: [@syntax::ast::item]) { + + s.emit_vec(vec::len(v), /*@syntax::ast::item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_117(s, e) }) + }) + }); +} +/*syntax::ast::_mod*/ +fn serialize_136<S: std::serialization::serializer>(s: S, + v: syntax::ast::_mod) { + + s.emit_rec(/*[@syntax::ast::view_item]*//*[@syntax::ast::item]*/ + {|| + { + s.emit_rec_field("view_items", 0u, + {|| serialize_83(s, v.view_items) }); + s.emit_rec_field("items", 1u, + {|| serialize_137(s, v.items) }) + } + }); +} +/*syntax::ast::native_item_*/ +fn serialize_142<S: std::serialization::serializer>(s: S, + v: + syntax::ast::native_item_) { + s.emit_enum("syntax::ast::native_item_", + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + {|| + alt v { + syntax::ast::native_item_fn(v0, v1) { + s.emit_enum_variant("syntax::ast::native_item_fn", 0u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_38(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_131(s, + v1) + }) + } + }) + } + } + }); +} +/*syntax::ast::native_item*/ +fn serialize_141<S: std::serialization::serializer>(s: S, + v: + syntax::ast::native_item) { + s.emit_rec(/*syntax::ast::ident*//*[syntax::ast::attribute]*/ + /*syntax::ast::native_item_*//*syntax::ast::node_id*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("attrs", 1u, + {|| serialize_2(s, v.attrs) }); + s.emit_rec_field("node", 2u, + {|| serialize_142(s, v.node) }); + s.emit_rec_field("id", 3u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("span", 4u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::native_item*/ +fn serialize_140<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::native_item) { + s.emit_box(/*syntax::ast::native_item*/{|| serialize_141(s, *v) }); +} +/*[@syntax::ast::native_item]*/ +fn serialize_139<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::native_item]) { + s.emit_vec(vec::len(v), /*@syntax::ast::native_item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_140(s, e) }) + }) + }); +} +/*syntax::ast::native_mod*/ +fn serialize_138<S: std::serialization::serializer>(s: S, + v: + syntax::ast::native_mod) { + s.emit_rec(/*[@syntax::ast::view_item]*//*[@syntax::ast::native_item]*/ + {|| + { + s.emit_rec_field("view_items", 0u, + {|| serialize_83(s, v.view_items) }); + s.emit_rec_field("items", 1u, + {|| serialize_139(s, v.items) }) + } + }); +} +/*syntax::ast::variant_arg*/ +fn serialize_147<S: std::serialization::serializer>(s: S, + v: + syntax::ast::variant_arg) { + s.emit_rec(/*@syntax::ast::ty*//*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("ty", 0u, + {|| serialize_29(s, v.ty) }); + s.emit_rec_field("id", 1u, {|| serialize_27(s, v.id) }) + } + }); +} +/*[syntax::ast::variant_arg]*/ +fn serialize_146<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::variant_arg]) { + s.emit_vec(vec::len(v), /*syntax::ast::variant_arg*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_147(s, e) }) + }) + }); +} +/*syntax::ast::variant_*/ +fn serialize_145<S: std::serialization::serializer>(s: S, + v: + syntax::ast::variant_) { + s.emit_rec(/*syntax::ast::ident*//*[syntax::ast::attribute]*/ + /*[syntax::ast::variant_arg]*//*syntax::ast::node_id*/ + /*core::option::t<@syntax::ast::expr>*/ + {|| + { + s.emit_rec_field("name", 0u, + {|| serialize_1(s, v.name) }); + s.emit_rec_field("attrs", 1u, + {|| serialize_2(s, v.attrs) }); + s.emit_rec_field("args", 2u, + {|| serialize_146(s, v.args) }); + s.emit_rec_field("id", 3u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("disr_expr", 4u, + {|| serialize_77(s, v.disr_expr) }) + } + }); +} +/*syntax::ast::variant*/ +fn serialize_144<S: std::serialization::serializer>(s: S, + v: syntax::ast::variant) { + + s.emit_rec(/*syntax::ast::variant_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_145(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::variant]*/ +fn serialize_143<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::variant]) { + s.emit_vec(vec::len(v), /*syntax::ast::variant*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_144(s, e) }) + }) + }); +} +/*syntax::ast::privacy*/ +fn serialize_152<S: std::serialization::serializer>(s: S, + v: syntax::ast::privacy) { + + s.emit_enum("syntax::ast::privacy", + + {|| + alt v { + syntax::ast::priv { + s.emit_enum_variant("syntax::ast::priv", 0u, 0u, + {|| }) + } + syntax::ast::pub { + s.emit_enum_variant("syntax::ast::pub", 1u, 0u, {|| }) + } + } + }); +} +/*syntax::ast::class_mutability*/ +fn serialize_154<S: std::serialization::serializer>(s: S, + v: + syntax::ast::class_mutability) { + s.emit_enum("syntax::ast::class_mutability", + + {|| + alt v { + syntax::ast::class_mutable { + s.emit_enum_variant("syntax::ast::class_mutable", 0u, + 0u, {|| }) + } + syntax::ast::class_immutable { + s.emit_enum_variant("syntax::ast::class_immutable", + 1u, 0u, {|| }) + } + } + }); +} +/*syntax::ast::class_member*/ +fn serialize_153<S: std::serialization::serializer>(s: S, + v: + syntax::ast::class_member) { + s.emit_enum("syntax::ast::class_member", + /*syntax::ast::ident*//*@syntax::ast::ty*/ + /*syntax::ast::class_mutability*//*syntax::ast::node_id*/ + /*@syntax::ast::item*/ + {|| + alt v { + syntax::ast::instance_var(v0, v1, v2, v3) { + s.emit_enum_variant("syntax::ast::instance_var", 0u, + 4u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_1(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_29(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_154(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_27(s, + v3) + }) + } + }) + } + syntax::ast::class_method(v0) { + s.emit_enum_variant("syntax::ast::class_method", 1u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_117(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::class_item_*/ +fn serialize_151<S: std::serialization::serializer>(s: S, + v: + syntax::ast::class_item_) { + s.emit_rec(/*syntax::ast::privacy*//*syntax::ast::class_member*/ + {|| + { + s.emit_rec_field("privacy", 0u, + {|| serialize_152(s, v.privacy) }); + s.emit_rec_field("decl", 1u, + {|| serialize_153(s, v.decl) }) + } + }); +} +/*syntax::ast::class_item*/ +fn serialize_150<S: std::serialization::serializer>(s: S, + v: + syntax::ast::class_item) { + s.emit_rec(/*syntax::ast::class_item_*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("node", 0u, + {|| serialize_151(s, v.node) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::class_item*/ +fn serialize_149<S: std::serialization::serializer>(s: S, + v: + @syntax::ast::class_item) { + s.emit_box(/*syntax::ast::class_item*/{|| serialize_150(s, *v) }); +} +/*[@syntax::ast::class_item]*/ +fn serialize_148<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::class_item]) { + s.emit_vec(vec::len(v), /*@syntax::ast::class_item*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_149(s, e) }) + }) + }); +} +/*syntax::ast::ty_method*/ +fn serialize_156<S: std::serialization::serializer>(s: S, + v: + syntax::ast::ty_method) { + s.emit_rec(/*syntax::ast::ident*//*[syntax::ast::attribute]*/ + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("attrs", 1u, + {|| serialize_2(s, v.attrs) }); + s.emit_rec_field("decl", 2u, + {|| serialize_38(s, v.decl) }); + s.emit_rec_field("tps", 3u, + {|| serialize_131(s, v.tps) }); + s.emit_rec_field("span", 4u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*[syntax::ast::ty_method]*/ +fn serialize_155<S: std::serialization::serializer>(s: S, + v: + [syntax::ast::ty_method]) { + s.emit_vec(vec::len(v), /*syntax::ast::ty_method*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_156(s, e) }) + }) + }); +} +/*core::option::t<@syntax::ast::ty>*/ +fn serialize_157<S: std::serialization::serializer>(s: S, + v: + core::option::t<@syntax::ast::ty>) { + s.emit_enum("core::option::t", + + /*@syntax::ast::ty*/ + {|| + alt v { + core::option::none { + s.emit_enum_variant("core::option::none", 0u, 0u, + {|| }) + } + core::option::some(v0) { + s.emit_enum_variant("core::option::some", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }) + } + }) + } + } + }); +} +/*syntax::ast::method*/ +fn serialize_160<S: std::serialization::serializer>(s: S, + v: syntax::ast::method) { + + s.emit_rec(/*syntax::ast::ident*//*[syntax::ast::attribute]*/ + /*[syntax::ast::ty_param]*//*syntax::ast::fn_decl*/ + /*syntax::ast::blk*//*syntax::ast::node_id*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("attrs", 1u, + {|| serialize_2(s, v.attrs) }); + s.emit_rec_field("tps", 2u, + {|| serialize_131(s, v.tps) }); + s.emit_rec_field("decl", 3u, + {|| serialize_38(s, v.decl) }); + s.emit_rec_field("body", 4u, + {|| serialize_81(s, v.body) }); + s.emit_rec_field("id", 5u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("span", 6u, + {|| serialize_19(s, v.span) }) + } + }); +} +/*@syntax::ast::method*/ +fn serialize_159<S: std::serialization::serializer>(s: S, + v: @syntax::ast::method) { + + s.emit_box(/*syntax::ast::method*/{|| serialize_160(s, *v) }); +} +/*[@syntax::ast::method]*/ +fn serialize_158<S: std::serialization::serializer>(s: S, + v: + [@syntax::ast::method]) { + s.emit_vec(vec::len(v), /*@syntax::ast::method*/ + {|| + vec::iteri(v, + {|i, e| + s.emit_vec_elt(i, {|| serialize_159(s, e) }) + }) + }); +} +/*syntax::ast::item_*/ +fn serialize_28<S: std::serialization::serializer>(s: S, + v: syntax::ast::item_) { + + s.emit_enum("syntax::ast::item_", + /*@syntax::ast::ty*//*@syntax::ast::expr*/ + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + /*syntax::ast::blk*/ + /*syntax::ast::_mod*/ + /*syntax::ast::native_mod*/ + /*@syntax::ast::ty*//*[syntax::ast::ty_param]*/ + /*[syntax::ast::variant]*//*[syntax::ast::ty_param]*/ + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + /*syntax::ast::blk*//*syntax::ast::node_id*/ + /*syntax::ast::node_id*/ + /*[syntax::ast::ty_param]*//*[@syntax::ast::class_item]*/ + /*syntax::ast::node_id*//*syntax::ast::fn_decl*/ + /*syntax::ast::blk*/ + /*[syntax::ast::ty_param]*//*[syntax::ast::ty_method]*/ + /*[syntax::ast::ty_param]*/ + /*core::option::t<@syntax::ast::ty>*//*@syntax::ast::ty*/ + /*[@syntax::ast::method]*/ + {|| + alt v { + syntax::ast::item_const(v0, v1) { + s.emit_enum_variant("syntax::ast::item_const", 0u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_70(s, + v1) + }) + } + }) + } + syntax::ast::item_fn(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::item_fn", 1u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_38(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_131(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_81(s, + v2) + }) + } + }) + } + syntax::ast::item_mod(v0) { + s.emit_enum_variant("syntax::ast::item_mod", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_136(s, + v0) + }) + } + }) + } + syntax::ast::item_native_mod(v0) { + s.emit_enum_variant("syntax::ast::item_native_mod", + 3u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_138(s, + v0) + }) + } + }) + } + syntax::ast::item_ty(v0, v1) { + s.emit_enum_variant("syntax::ast::item_ty", 4u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_29(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_131(s, + v1) + }) + } + }) + } + syntax::ast::item_enum(v0, v1) { + s.emit_enum_variant("syntax::ast::item_enum", 5u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_143(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_131(s, + v1) + }) + } + }) + } + syntax::ast::item_res(v0, v1, v2, v3, v4) { + s.emit_enum_variant("syntax::ast::item_res", 6u, 5u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_38(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_131(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_81(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_27(s, + v3) + }); + s.emit_enum_variant_arg(4u, + {|| + serialize_27(s, + v4) + }) + } + }) + } + syntax::ast::item_class(v0, v1, v2, v3, v4) { + s.emit_enum_variant("syntax::ast::item_class", 7u, 5u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_131(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_148(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_27(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_38(s, + v3) + }); + s.emit_enum_variant_arg(4u, + {|| + serialize_81(s, + v4) + }) + } + }) + } + syntax::ast::item_iface(v0, v1) { + s.emit_enum_variant("syntax::ast::item_iface", 8u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_131(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_155(s, + v1) + }) + } + }) + } + syntax::ast::item_impl(v0, v1, v2, v3) { + s.emit_enum_variant("syntax::ast::item_impl", 9u, 4u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_131(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_157(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_29(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_158(s, + v3) + }) + } + }) + } + } + }); +} +/*syntax::ast::item*/ +fn serialize_0<S: std::serialization::serializer>(s: S, + v: syntax::ast::item) { + + s.emit_rec(/*syntax::ast::ident*//*[syntax::ast::attribute]*/ + /*syntax::ast::node_id*//*syntax::ast::item_*/ + /*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("ident", 0u, + {|| serialize_1(s, v.ident) }); + s.emit_rec_field("attrs", 1u, + {|| serialize_2(s, v.attrs) }); + s.emit_rec_field("id", 2u, + {|| serialize_27(s, v.id) }); + s.emit_rec_field("node", 3u, + {|| serialize_28(s, v.node) }); + s.emit_rec_field("span", 4u, + {|| serialize_19(s, v.span) }) + } + }); +} +fn serialize_syntax_ast_item<S: std::serialization::serializer>(s: S, + v: + syntax::ast::item) { + serialize_0(s, v); +} +/*syntax::ast::ident*/ +fn deserialize_1<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ident { + s.read_str() +} +/*syntax::ast::attr_style*/ +fn deserialize_5<S: std::serialization::deserializer>(s: S) -> + syntax::ast::attr_style { + s.read_enum("syntax::ast::attr_style", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::attr_outer } + 1u { syntax::ast::attr_inner } + } + }) + }) +} +/*@syntax::ast::meta_item*/ +fn deserialize_9<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::meta_item { + + s.read_box(/*syntax::ast::meta_item*/{|| @deserialize_6(s) }) + +} +/*[@syntax::ast::meta_item]*/ +fn deserialize_8<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::meta_item] { + s.read_vec( + + /*@syntax::ast::meta_item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_9(s) }) + }) + }) +} +/*str*/ +fn deserialize_12<S: std::serialization::deserializer>(s: S) -> str { + s.read_str() +} +/*i64*/ +fn deserialize_13<S: std::serialization::deserializer>(s: S) -> i64 { + s.read_i64() +} +/*syntax::ast::int_ty*/ +fn deserialize_14<S: std::serialization::deserializer>(s: S) -> + syntax::ast::int_ty { + s.read_enum("syntax::ast::int_ty", + + + + + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::ty_i } + 1u { syntax::ast::ty_char } + 2u { syntax::ast::ty_i8 } + 3u { syntax::ast::ty_i16 } + 4u { syntax::ast::ty_i32 } + 5u { syntax::ast::ty_i64 } + } + }) + }) +} +/*u64*/ +fn deserialize_15<S: std::serialization::deserializer>(s: S) -> u64 { + s.read_u64() +} +/*syntax::ast::uint_ty*/ +fn deserialize_16<S: std::serialization::deserializer>(s: S) -> + syntax::ast::uint_ty { + s.read_enum("syntax::ast::uint_ty", + + + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::ty_u } + 1u { syntax::ast::ty_u8 } + 2u { syntax::ast::ty_u16 } + 3u { syntax::ast::ty_u32 } + 4u { syntax::ast::ty_u64 } + } + }) + }) +} +/*syntax::ast::float_ty*/ +fn deserialize_17<S: std::serialization::deserializer>(s: S) -> + syntax::ast::float_ty { + s.read_enum("syntax::ast::float_ty", + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::ty_f } + 1u { syntax::ast::ty_f32 } + 2u { syntax::ast::ty_f64 } + } + }) + }) +} +/*bool*/ +fn deserialize_18<S: std::serialization::deserializer>(s: S) -> bool { + s.read_bool() +} +/*syntax::ast::lit_*/ +fn deserialize_11<S: std::serialization::deserializer>(s: S) -> + syntax::ast::lit_ { + s.read_enum("syntax::ast::lit_", + /*str*/ + + /*i64*//*syntax::ast::int_ty*/ + + /*u64*//*syntax::ast::uint_ty*/ + + /*str*//*syntax::ast::float_ty*/ + + + + /*bool*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::lit_str(s.read_enum_variant_arg(0u, + {|| + deserialize_12(s) + })) + } + 1u { + syntax::ast::lit_int(s.read_enum_variant_arg(0u, + {|| + deserialize_13(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_14(s) + })) + } + 2u { + syntax::ast::lit_uint(s.read_enum_variant_arg(0u, + {|| + deserialize_15(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_16(s) + })) + } + 3u { + syntax::ast::lit_float(s.read_enum_variant_arg(0u, + {|| + deserialize_12(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_17(s) + })) + } + 4u { syntax::ast::lit_nil } + 5u { + syntax::ast::lit_bool(s.read_enum_variant_arg(0u, + {|| + deserialize_18(s) + })) + } + } + }) + }) +} +/*uint*/ +fn deserialize_20<S: std::serialization::deserializer>(s: S) -> uint { + s.read_uint() +} +/*core::option::t<syntax::codemap::span>*/ +fn deserialize_26<S: std::serialization::deserializer>(s: S) -> + core::option::t<syntax::codemap::span> { + s.read_enum("core::option::t", + + + /*syntax::codemap::span*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_19(s) + })) + } + } + }) + }) +} +/*{name: str,span: core::option::t<syntax::codemap::span>}*/ +fn deserialize_25<S: std::serialization::deserializer>(s: S) -> + {name: str, span: core::option::t<syntax::codemap::span>,} { + + s.read_rec( + + + /*str*/ + + /*core::option::t<syntax::codemap::span>*/ + + {|| + {name: + s.read_rec_field("name", 0u, {|| deserialize_12(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_26(s) }),} + }) + +} +/*{call_site: syntax::codemap::span,callie: {name: str,span: core::option::t<syntax::codemap::span>}}*/ +fn deserialize_24<S: std::serialization::deserializer>(s: S) -> + {call_site: syntax::codemap::span, + callie: {name: str, span: core::option::t<syntax::codemap::span>,},} { + + s.read_rec( + + + /*syntax::codemap::span*/ + + /*{name: str,span: core::option::t<syntax::codemap::span>}*/ + + {|| + {call_site: + s.read_rec_field("call_site", 0u, + {|| deserialize_19(s) }), + callie: + s.read_rec_field("callie", 1u, + {|| deserialize_25(s) }),} + }) + +} +/*syntax::codemap::expn_info_*/ +fn deserialize_23<S: std::serialization::deserializer>(s: S) -> + syntax::codemap::expn_info_ { + s.read_enum("syntax::codemap::expn_info_", + + /*{call_site: syntax::codemap::span,callie: {name: str,span: core::option::t<syntax::codemap::span>}}*/ + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::codemap::expanded_from(s.read_enum_variant_arg(0u, + {|| + deserialize_24(s) + })) + } + } + }) + }) +} +/*@syntax::codemap::expn_info_*/ +fn deserialize_22<S: std::serialization::deserializer>(s: S) -> + @syntax::codemap::expn_info_ { + + s.read_box(/*syntax::codemap::expn_info_*/{|| @deserialize_23(s) }) + +} +/*syntax::codemap::expn_info<@syntax::codemap::expn_info_>*/ +fn deserialize_21<S: std::serialization::deserializer>(s: S) -> + syntax::codemap::expn_info<@syntax::codemap::expn_info_> { + s.read_enum("core::option::t", + + + /*@syntax::codemap::expn_info_*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_22(s) + })) + } + } + }) + }) +} +/*syntax::codemap::span*/ +fn deserialize_19<S: std::serialization::deserializer>(s: S) -> + syntax::codemap::span { + + s.read_rec( + + + /*uint*/ + + /*uint*/ + + /*syntax::codemap::expn_info<@syntax::codemap::expn_info_>*/ + + {|| + {lo: s.read_rec_field("lo", 0u, {|| deserialize_20(s) }), + hi: s.read_rec_field("hi", 1u, {|| deserialize_20(s) }), + expn_info: + s.read_rec_field("expn_info", 2u, + {|| deserialize_21(s) }),} + }) + +} +/*syntax::ast::lit*/ +fn deserialize_10<S: std::serialization::deserializer>(s: S) -> + syntax::ast::lit { + + s.read_rec( + + + /*syntax::ast::lit_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_11(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*syntax::ast::meta_item_*/ +fn deserialize_7<S: std::serialization::deserializer>(s: S) -> + syntax::ast::meta_item_ { + s.read_enum("syntax::ast::meta_item_", + /*syntax::ast::ident*/ + + /*syntax::ast::ident*//*[@syntax::ast::meta_item]*/ + + /*syntax::ast::ident*//*syntax::ast::lit*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::meta_word(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + })) + } + 1u { + syntax::ast::meta_list(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_8(s) + })) + } + 2u { + syntax::ast::meta_name_value(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_10(s) + })) + } + } + }) + }) +} +/*syntax::ast::meta_item*/ +fn deserialize_6<S: std::serialization::deserializer>(s: S) -> + syntax::ast::meta_item { + + s.read_rec( + + + /*syntax::ast::meta_item_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_7(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*syntax::ast::attribute_*/ +fn deserialize_4<S: std::serialization::deserializer>(s: S) -> + syntax::ast::attribute_ { + + s.read_rec( + + + /*syntax::ast::attr_style*/ + + /*syntax::ast::meta_item*/ + + {|| + {style: + s.read_rec_field("style", 0u, {|| deserialize_5(s) }), + value: + s.read_rec_field("value", 1u, + {|| deserialize_6(s) }),} + }) + +} +/*syntax::ast::attribute*/ +fn deserialize_3<S: std::serialization::deserializer>(s: S) -> + syntax::ast::attribute { + + s.read_rec( + + + /*syntax::ast::attribute_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_4(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::attribute]*/ +fn deserialize_2<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::attribute] { + s.read_vec( + + /*syntax::ast::attribute*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_3(s) }) + }) + }) +} +/*syntax::ast::node_id*/ +fn deserialize_27<S: std::serialization::deserializer>(s: S) -> + syntax::ast::node_id { + s.read_int() +} +/*syntax::ast::mutability*/ +fn deserialize_33<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mutability { + s.read_enum("syntax::ast::mutability", + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::m_mutbl } + 1u { syntax::ast::m_imm } + 2u { syntax::ast::m_const } + } + }) + }) +} +/*syntax::ast::mt*/ +fn deserialize_32<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mt { + + s.read_rec( + + + /*@syntax::ast::ty*/ + + /*syntax::ast::mutability*/ + + {|| + {ty: s.read_rec_field("ty", 0u, {|| deserialize_29(s) }), + mutbl: + s.read_rec_field("mutbl", 1u, + {|| deserialize_33(s) }),} + }) + +} +/*syntax::ast::ty_field_*/ +fn deserialize_36<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_field_ { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*syntax::ast::mt*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + mt: s.read_rec_field("mt", 1u, {|| deserialize_32(s) }),} + }) +} +/*syntax::ast::ty_field*/ +fn deserialize_35<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_field { + + s.read_rec( + + + /*syntax::ast::ty_field_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_36(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::ty_field]*/ +fn deserialize_34<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::ty_field] { + s.read_vec( + + /*syntax::ast::ty_field*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_35(s) }) + }) + }) +} +/*syntax::ast::proto*/ +fn deserialize_37<S: std::serialization::deserializer>(s: S) -> + syntax::ast::proto { + s.read_enum("syntax::ast::proto", + + + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::proto_bare } + 1u { syntax::ast::proto_any } + 2u { syntax::ast::proto_uniq } + 3u { syntax::ast::proto_box } + 4u { syntax::ast::proto_block } + } + }) + }) +} +/*syntax::ast::rmode*/ +fn deserialize_42<S: std::serialization::deserializer>(s: S) -> + syntax::ast::rmode { + s.read_enum("syntax::ast::rmode", + + + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::by_ref } + 1u { syntax::ast::by_val } + 2u { syntax::ast::by_mutbl_ref } + 3u { syntax::ast::by_move } + 4u { syntax::ast::by_copy } + } + }) + }) +} +/*syntax::ast::mode<syntax::ast::rmode>*/ +fn deserialize_41<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mode<syntax::ast::rmode> { + s.read_enum("syntax::ast::inferable", + /*syntax::ast::rmode*/ + + /*syntax::ast::node_id*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::expl(s.read_enum_variant_arg(0u, + {|| + deserialize_42(s) + })) + } + 1u { + syntax::ast::infer(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + })) + } + } + }) + }) +} +/*syntax::ast::arg*/ +fn deserialize_40<S: std::serialization::deserializer>(s: S) -> + syntax::ast::arg { + + s.read_rec( + + + /*syntax::ast::mode<syntax::ast::rmode>*/ + + /*@syntax::ast::ty*/ + + /*syntax::ast::ident*/ + + /*syntax::ast::node_id*/ + + {|| + {mode: + s.read_rec_field("mode", 0u, {|| deserialize_41(s) }), + ty: s.read_rec_field("ty", 1u, {|| deserialize_29(s) }), + ident: + s.read_rec_field("ident", 2u, {|| deserialize_1(s) }), + id: s.read_rec_field("id", 3u, {|| deserialize_27(s) }),} + }) +} +/*[syntax::ast::arg]*/ +fn deserialize_39<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::arg] { + s.read_vec( + + /*syntax::ast::arg*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_40(s) }) + }) + }) +} +/*syntax::ast::purity*/ +fn deserialize_43<S: std::serialization::deserializer>(s: S) -> + syntax::ast::purity { + s.read_enum("syntax::ast::purity", + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::pure_fn } + 1u { syntax::ast::unsafe_fn } + 2u { syntax::ast::impure_fn } + 3u { syntax::ast::crust_fn } + } + }) + }) +} +/*syntax::ast::ret_style*/ +fn deserialize_44<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ret_style { + s.read_enum("syntax::ast::ret_style", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::noreturn } + 1u { syntax::ast::return_val } + } + }) + }) +} +/*[syntax::ast::ident]*/ +fn deserialize_52<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::ident] { + s.read_vec( + + /*syntax::ast::ident*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_1(s) }) + }) + }) +} +/*[@syntax::ast::ty]*/ +fn deserialize_53<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::ty] { + s.read_vec( + + /*@syntax::ast::ty*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_29(s) }) + }) + }) +} +/*syntax::ast::path_*/ +fn deserialize_51<S: std::serialization::deserializer>(s: S) -> + syntax::ast::path_ { + + s.read_rec( + + + /*bool*/ + + /*[syntax::ast::ident]*/ + + /*[@syntax::ast::ty]*/ + + {|| + {global: + s.read_rec_field("global", 0u, + {|| deserialize_18(s) }), + idents: + s.read_rec_field("idents", 1u, + {|| deserialize_52(s) }), + types: + s.read_rec_field("types", 2u, + {|| deserialize_53(s) }),} + }) + +} +/*syntax::ast::path*/ +fn deserialize_50<S: std::serialization::deserializer>(s: S) -> + syntax::ast::path { + + s.read_rec( + + + /*syntax::ast::path_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_51(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::path*/ +fn deserialize_49<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::path { + + s.read_box(/*syntax::ast::path*/{|| @deserialize_50(s) }) + +} +/*@syntax::ast::lit*/ +fn deserialize_58<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::lit { + + s.read_box(/*syntax::ast::lit*/{|| @deserialize_10(s) }) + +} +/*syntax::ast::constr_arg_general_<uint>*/ +fn deserialize_57<S: std::serialization::deserializer>(s: S) -> + syntax::ast::constr_arg_general_<uint> { + s.read_enum("syntax::ast::constr_arg_general_", + + + /*uint*/ + + /*@syntax::ast::lit*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::carg_base } + 1u { + syntax::ast::carg_ident(s.read_enum_variant_arg(0u, + {|| + deserialize_20(s) + })) + } + 2u { + syntax::ast::carg_lit(s.read_enum_variant_arg(0u, + {|| + deserialize_58(s) + })) + } + } + }) + }) +} +/*{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ +fn deserialize_56<S: std::serialization::deserializer>(s: S) -> + {node: syntax::ast::constr_arg_general_<uint>, + span: syntax::codemap::span,} { + + s.read_rec( + + + /*syntax::ast::constr_arg_general_<uint>*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_57(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ +fn deserialize_55<S: std::serialization::deserializer>(s: S) -> + @{node: syntax::ast::constr_arg_general_<uint>, + span: syntax::codemap::span,} { + + s.read_box( + /*{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ + {|| @deserialize_56(s) }) + +} +/*[@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}]*/ +fn deserialize_54<S: std::serialization::deserializer>(s: S) -> + [@{node: syntax::ast::constr_arg_general_<uint>, + span: syntax::codemap::span,}] { + s.read_vec( + + + /*@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}*/ + + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_55(s) }) + }) + }) +} +/*{path: @syntax::ast::path,args: [@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}],id: syntax::ast::node_id}*/ +fn deserialize_48<S: std::serialization::deserializer>(s: S) -> + {path: @syntax::ast::path, + args: + [@{node: syntax::ast::constr_arg_general_<uint>, + span: syntax::codemap::span,}], + id: syntax::ast::node_id,} { + + s.read_rec( + + + /*@syntax::ast::path*/ + + + /*[@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}]*/ + + + /*syntax::ast::node_id*/ + + {|| + {path: + s.read_rec_field("path", 0u, {|| deserialize_49(s) }), + args: + s.read_rec_field("args", 1u, {|| deserialize_54(s) }), + id: s.read_rec_field("id", 2u, {|| deserialize_27(s) }),} + }) +} +/*syntax::ast::constr*/ +fn deserialize_47<S: std::serialization::deserializer>(s: S) -> + syntax::ast::constr { + + s.read_rec( + + + + /*{path: @syntax::ast::path,args: [@{node: syntax::ast::constr_arg_general_<uint>,span: syntax::codemap::span}],id: syntax::ast::node_id}*/ + + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_48(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::constr*/ +fn deserialize_46<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::constr { + + s.read_box(/*syntax::ast::constr*/{|| @deserialize_47(s) }) + +} +/*[@syntax::ast::constr]*/ +fn deserialize_45<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::constr] { + s.read_vec( + + /*@syntax::ast::constr*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_46(s) }) + }) + }) +} +/*syntax::ast::fn_decl*/ +fn deserialize_38<S: std::serialization::deserializer>(s: S) -> + syntax::ast::fn_decl { + + s.read_rec( + + + /*[syntax::ast::arg]*/ + + /*@syntax::ast::ty*/ + + /*syntax::ast::purity*/ + + /*syntax::ast::ret_style*/ + + /*[@syntax::ast::constr]*/ + + {|| + {inputs: + s.read_rec_field("inputs", 0u, + {|| deserialize_39(s) }), + output: + s.read_rec_field("output", 1u, + {|| deserialize_29(s) }), + purity: + s.read_rec_field("purity", 2u, + {|| deserialize_43(s) }), + cf: s.read_rec_field("cf", 3u, {|| deserialize_44(s) }), + constraints: + s.read_rec_field("constraints", 4u, + {|| deserialize_45(s) }),} + }) + +} +/*syntax::ast::constr_arg_general_<@syntax::ast::path>*/ +fn deserialize_66<S: std::serialization::deserializer>(s: S) -> + syntax::ast::constr_arg_general_<@syntax::ast::path> { + s.read_enum("syntax::ast::constr_arg_general_", + + + /*@syntax::ast::path*/ + + /*@syntax::ast::lit*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::carg_base } + 1u { + syntax::ast::carg_ident(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + })) + } + 2u { + syntax::ast::carg_lit(s.read_enum_variant_arg(0u, + {|| + deserialize_58(s) + })) + } + } + }) + }) +} +/*{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ +fn deserialize_65<S: std::serialization::deserializer>(s: S) -> + {node: syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: syntax::codemap::span,} { + + s.read_rec( + + + /*syntax::ast::constr_arg_general_<@syntax::ast::path>*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_66(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ +fn deserialize_64<S: std::serialization::deserializer>(s: S) -> + @{node: syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: syntax::codemap::span,} { + + s.read_box( + /*{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ + {|| @deserialize_65(s) }) + +} +/*[@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}]*/ +fn deserialize_63<S: std::serialization::deserializer>(s: S) -> + [@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>, + span: syntax::codemap::span,}] { + s.read_vec( + + + /*@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}*/ + + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_64(s) }) + }) + }) +} +/*syntax::ast::ty_constr_*/ +fn deserialize_62<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_constr_ { + + s.read_rec( + + + /*@syntax::ast::path*/ + + + /*[@{node: syntax::ast::constr_arg_general_<@syntax::ast::path>,span: syntax::codemap::span}]*/ + + + /*syntax::ast::node_id*/ + + {|| + {path: + s.read_rec_field("path", 0u, {|| deserialize_49(s) }), + args: + s.read_rec_field("args", 1u, {|| deserialize_63(s) }), + id: s.read_rec_field("id", 2u, {|| deserialize_27(s) }),} + }) +} +/*syntax::ast::ty_constr*/ +fn deserialize_61<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_constr { + + s.read_rec( + + + /*syntax::ast::ty_constr_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_62(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::ty_constr*/ +fn deserialize_60<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::ty_constr { + + s.read_box(/*syntax::ast::ty_constr*/{|| @deserialize_61(s) }) + +} +/*[@syntax::ast::ty_constr]*/ +fn deserialize_59<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::ty_constr] { + s.read_vec( + + /*@syntax::ast::ty_constr*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_60(s) }) + }) + }) +} +/*[@syntax::ast::expr]*/ +fn deserialize_73<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::expr] { + s.read_vec( + + /*@syntax::ast::expr*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_70(s) }) + }) + }) +} +/*syntax::ast::field_*/ +fn deserialize_76<S: std::serialization::deserializer>(s: S) -> + syntax::ast::field_ { + + s.read_rec( + + + /*syntax::ast::mutability*/ + + /*syntax::ast::ident*/ + + /*@syntax::ast::expr*/ + + {|| + {mutbl: + s.read_rec_field("mutbl", 0u, + {|| deserialize_33(s) }), + ident: + s.read_rec_field("ident", 1u, {|| deserialize_1(s) }), + expr: + s.read_rec_field("expr", 2u, + {|| deserialize_70(s) }),} + }) + +} +/*syntax::ast::field*/ +fn deserialize_75<S: std::serialization::deserializer>(s: S) -> + syntax::ast::field { + + s.read_rec( + + + /*syntax::ast::field_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_76(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::field]*/ +fn deserialize_74<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::field] { + s.read_vec( + + /*syntax::ast::field*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_75(s) }) + }) + }) +} +/*core::option::t<@syntax::ast::expr>*/ +fn deserialize_77<S: std::serialization::deserializer>(s: S) -> + core::option::t<@syntax::ast::expr> { + s.read_enum("core::option::t", + + + /*@syntax::ast::expr*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + } + }) + }) +} +/*[core::option::t<@syntax::ast::expr>]*/ +fn deserialize_78<S: std::serialization::deserializer>(s: S) -> + [core::option::t<@syntax::ast::expr>] { + s.read_vec( + + /*core::option::t<@syntax::ast::expr>*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_77(s) }) + }) + }) +} +/*syntax::ast::binop*/ +fn deserialize_79<S: std::serialization::deserializer>(s: S) -> + syntax::ast::binop { + s.read_enum("syntax::ast::binop", + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::add } + 1u { syntax::ast::subtract } + 2u { syntax::ast::mul } + 3u { syntax::ast::div } + 4u { syntax::ast::rem } + 5u { syntax::ast::and } + 6u { syntax::ast::or } + 7u { syntax::ast::bitxor } + 8u { syntax::ast::bitand } + 9u { syntax::ast::bitor } + 10u { syntax::ast::lsl } + 11u { syntax::ast::lsr } + 12u { syntax::ast::asr } + 13u { syntax::ast::eq } + 14u { syntax::ast::lt } + 15u { syntax::ast::le } + 16u { syntax::ast::ne } + 17u { syntax::ast::ge } + 18u { syntax::ast::gt } + } + }) + }) +} +/*syntax::ast::unop*/ +fn deserialize_80<S: std::serialization::deserializer>(s: S) -> + syntax::ast::unop { + s.read_enum("syntax::ast::unop", + /*syntax::ast::mutability*/ + + /*syntax::ast::mutability*/ + + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::box(s.read_enum_variant_arg(0u, + {|| + deserialize_33(s) + })) + } + 1u { + syntax::ast::uniq(s.read_enum_variant_arg(0u, + {|| + deserialize_33(s) + })) + } + 2u { syntax::ast::deref } + 3u { syntax::ast::not } + 4u { syntax::ast::neg } + } + }) + }) +} +/*syntax::ast::simple_path*/ +fn deserialize_92<S: std::serialization::deserializer>(s: S) -> + syntax::ast::simple_path { + s.read_vec( + + /*syntax::ast::ident*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_1(s) }) + }) + }) +} +/*@syntax::ast::simple_path*/ +fn deserialize_91<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::simple_path { + + s.read_box(/*syntax::ast::simple_path*/{|| @deserialize_92(s) }) + +} +/*syntax::ast::path_list_ident_*/ +fn deserialize_95<S: std::serialization::deserializer>(s: S) -> + syntax::ast::path_list_ident_ { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*syntax::ast::node_id*/ + + {|| + {name: + s.read_rec_field("name", 0u, {|| deserialize_1(s) }), + id: s.read_rec_field("id", 1u, {|| deserialize_27(s) }),} + }) +} +/*syntax::ast::path_list_ident*/ +fn deserialize_94<S: std::serialization::deserializer>(s: S) -> + syntax::ast::path_list_ident { + + s.read_rec( + + + /*syntax::ast::path_list_ident_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_95(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::path_list_ident]*/ +fn deserialize_93<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::path_list_ident] { + s.read_vec( + + /*syntax::ast::path_list_ident*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_94(s) }) + }) + }) +} +/*syntax::ast::view_path_*/ +fn deserialize_90<S: std::serialization::deserializer>(s: S) -> + syntax::ast::view_path_ { + s.read_enum("syntax::ast::view_path_", + /*syntax::ast::ident*//*@syntax::ast::simple_path*/ + /*syntax::ast::node_id*/ + + /*@syntax::ast::simple_path*//*syntax::ast::node_id*/ + + /*@syntax::ast::simple_path*/ + /*[syntax::ast::path_list_ident]*//*syntax::ast::node_id*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::view_path_simple(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_91(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_27(s) + })) + } + 1u { + syntax::ast::view_path_glob(s.read_enum_variant_arg(0u, + {|| + deserialize_91(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_27(s) + })) + } + 2u { + syntax::ast::view_path_list(s.read_enum_variant_arg(0u, + {|| + deserialize_91(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_93(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_27(s) + })) + } + } + }) + }) +} +/*syntax::ast::view_path*/ +fn deserialize_89<S: std::serialization::deserializer>(s: S) -> + syntax::ast::view_path { + + s.read_rec( + + + /*syntax::ast::view_path_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_90(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::view_path*/ +fn deserialize_88<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::view_path { + + s.read_box(/*syntax::ast::view_path*/{|| @deserialize_89(s) }) + +} +/*[@syntax::ast::view_path]*/ +fn deserialize_87<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::view_path] { + s.read_vec( + + /*@syntax::ast::view_path*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_88(s) }) + }) + }) +} +/*syntax::ast::view_item_*/ +fn deserialize_86<S: std::serialization::deserializer>(s: S) -> + syntax::ast::view_item_ { + s.read_enum("syntax::ast::view_item_", + /*syntax::ast::ident*//*[@syntax::ast::meta_item]*/ + /*syntax::ast::node_id*/ + + /*[@syntax::ast::view_path]*/ + + /*[@syntax::ast::view_path]*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::view_item_use(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_8(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_27(s) + })) + } + 1u { + syntax::ast::view_item_import(s.read_enum_variant_arg(0u, + {|| + deserialize_87(s) + })) + } + 2u { + syntax::ast::view_item_export(s.read_enum_variant_arg(0u, + {|| + deserialize_87(s) + })) + } + } + }) + }) +} +/*syntax::ast::view_item*/ +fn deserialize_85<S: std::serialization::deserializer>(s: S) -> + syntax::ast::view_item { + + s.read_rec( + + + /*syntax::ast::view_item_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_86(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::view_item*/ +fn deserialize_84<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::view_item { + + s.read_box(/*syntax::ast::view_item*/{|| @deserialize_85(s) }) + +} +/*[@syntax::ast::view_item]*/ +fn deserialize_83<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::view_item] { + s.read_vec( + + /*@syntax::ast::view_item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_84(s) }) + }) + }) +} +/*core::option::t<@syntax::ast::pat>*/ +fn deserialize_110<S: std::serialization::deserializer>(s: S) -> + core::option::t<@syntax::ast::pat> { + s.read_enum("core::option::t", + + + /*@syntax::ast::pat*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_107(s) + })) + } + } + }) + }) +} +/*[@syntax::ast::pat]*/ +fn deserialize_111<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::pat] { + s.read_vec( + + /*@syntax::ast::pat*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_107(s) }) + }) + }) +} +/*syntax::ast::field_pat*/ +fn deserialize_113<S: std::serialization::deserializer>(s: S) -> + syntax::ast::field_pat { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*@syntax::ast::pat*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + pat: + s.read_rec_field("pat", 1u, + {|| deserialize_107(s) }),} + }) + +} +/*[syntax::ast::field_pat]*/ +fn deserialize_112<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::field_pat] { + s.read_vec( + + /*syntax::ast::field_pat*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_113(s) }) + }) + }) +} +/*syntax::ast::pat_*/ +fn deserialize_109<S: std::serialization::deserializer>(s: S) -> + syntax::ast::pat_ { + s.read_enum("syntax::ast::pat_", + + + /*@syntax::ast::path*//*core::option::t<@syntax::ast::pat>*/ + + /*@syntax::ast::path*//*[@syntax::ast::pat]*/ + + /*[syntax::ast::field_pat]*//*bool*/ + + /*[@syntax::ast::pat]*/ + + /*@syntax::ast::pat*/ + + /*@syntax::ast::pat*/ + + /*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::pat_wild } + 1u { + syntax::ast::pat_ident(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_110(s) + })) + } + 2u { + syntax::ast::pat_enum(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_111(s) + })) + } + 3u { + syntax::ast::pat_rec(s.read_enum_variant_arg(0u, + {|| + deserialize_112(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_18(s) + })) + } + 4u { + syntax::ast::pat_tup(s.read_enum_variant_arg(0u, + {|| + deserialize_111(s) + })) + } + 5u { + syntax::ast::pat_box(s.read_enum_variant_arg(0u, + {|| + deserialize_107(s) + })) + } + 6u { + syntax::ast::pat_uniq(s.read_enum_variant_arg(0u, + {|| + deserialize_107(s) + })) + } + 7u { + syntax::ast::pat_lit(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + 8u { + syntax::ast::pat_range(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + } + }) + }) +} +/*syntax::ast::pat*/ +fn deserialize_108<S: std::serialization::deserializer>(s: S) -> + syntax::ast::pat { + + s.read_rec( + + + /*syntax::ast::node_id*/ + + /*syntax::ast::pat_*/ + + /*syntax::codemap::span*/ + + {|| + {id: s.read_rec_field("id", 0u, {|| deserialize_27(s) }), + node: + s.read_rec_field("node", 1u, + {|| deserialize_109(s) }), + span: + s.read_rec_field("span", 2u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::pat*/ +fn deserialize_107<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::pat { + + s.read_box(/*syntax::ast::pat*/{|| @deserialize_108(s) }) + +} +/*syntax::ast::init_op*/ +fn deserialize_116<S: std::serialization::deserializer>(s: S) -> + syntax::ast::init_op { + s.read_enum("syntax::ast::init_op", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::init_assign } + 1u { syntax::ast::init_move } + } + }) + }) +} +/*syntax::ast::initializer*/ +fn deserialize_115<S: std::serialization::deserializer>(s: S) -> + syntax::ast::initializer { + + s.read_rec( + + + /*syntax::ast::init_op*/ + + /*@syntax::ast::expr*/ + + {|| + {op: s.read_rec_field("op", 0u, {|| deserialize_116(s) }), + expr: + s.read_rec_field("expr", 1u, + {|| deserialize_70(s) }),} + }) + +} +/*core::option::t<syntax::ast::initializer>*/ +fn deserialize_114<S: std::serialization::deserializer>(s: S) -> + core::option::t<syntax::ast::initializer> { + s.read_enum("core::option::t", + + + /*syntax::ast::initializer*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_115(s) + })) + } + } + }) + }) +} +/*syntax::ast::local_*/ +fn deserialize_106<S: std::serialization::deserializer>(s: S) -> + syntax::ast::local_ { + + s.read_rec( + + + /*bool*/ + + /*@syntax::ast::ty*/ + + /*@syntax::ast::pat*/ + + /*core::option::t<syntax::ast::initializer>*/ + + /*syntax::ast::node_id*/ + + {|| + {is_mutbl: + s.read_rec_field("is_mutbl", 0u, + {|| deserialize_18(s) }), + ty: s.read_rec_field("ty", 1u, {|| deserialize_29(s) }), + pat: + s.read_rec_field("pat", 2u, {|| deserialize_107(s) }), + init: + s.read_rec_field("init", 3u, + {|| deserialize_114(s) }), + id: s.read_rec_field("id", 4u, {|| deserialize_27(s) }),} + }) +} +/*syntax::ast::local*/ +fn deserialize_105<S: std::serialization::deserializer>(s: S) -> + syntax::ast::local { + + s.read_rec( + + + /*syntax::ast::local_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, + {|| deserialize_106(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::local*/ +fn deserialize_104<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::local { + + s.read_box(/*syntax::ast::local*/{|| @deserialize_105(s) }) + +} +/*[@syntax::ast::local]*/ +fn deserialize_103<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::local] { + s.read_vec( + + /*@syntax::ast::local*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_104(s) }) + }) + }) +} +/*@syntax::ast::item*/ +fn deserialize_117<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::item { + + s.read_box(/*syntax::ast::item*/{|| @deserialize_0(s) }) + +} +/*syntax::ast::decl_*/ +fn deserialize_102<S: std::serialization::deserializer>(s: S) -> + syntax::ast::decl_ { + s.read_enum("syntax::ast::decl_", + /*[@syntax::ast::local]*/ + + /*@syntax::ast::item*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::decl_local(s.read_enum_variant_arg(0u, + {|| + deserialize_103(s) + })) + } + 1u { + syntax::ast::decl_item(s.read_enum_variant_arg(0u, + {|| + deserialize_117(s) + })) + } + } + }) + }) +} +/*syntax::ast::decl*/ +fn deserialize_101<S: std::serialization::deserializer>(s: S) -> + syntax::ast::decl { + + s.read_rec( + + + /*syntax::ast::decl_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, + {|| deserialize_102(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::decl*/ +fn deserialize_100<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::decl { + + s.read_box(/*syntax::ast::decl*/{|| @deserialize_101(s) }) + +} +/*syntax::ast::stmt_*/ +fn deserialize_99<S: std::serialization::deserializer>(s: S) -> + syntax::ast::stmt_ { + s.read_enum("syntax::ast::stmt_", + /*@syntax::ast::decl*//*syntax::ast::node_id*/ + + /*@syntax::ast::expr*//*syntax::ast::node_id*/ + + /*@syntax::ast::expr*//*syntax::ast::node_id*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::stmt_decl(s.read_enum_variant_arg(0u, + {|| + deserialize_100(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_27(s) + })) + } + 1u { + syntax::ast::stmt_expr(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_27(s) + })) + } + 2u { + syntax::ast::stmt_semi(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_27(s) + })) + } + } + }) + }) +} +/*syntax::ast::stmt*/ +fn deserialize_98<S: std::serialization::deserializer>(s: S) -> + syntax::ast::stmt { + + s.read_rec( + + + /*syntax::ast::stmt_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_99(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::stmt*/ +fn deserialize_97<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::stmt { + + s.read_box(/*syntax::ast::stmt*/{|| @deserialize_98(s) }) + +} +/*[@syntax::ast::stmt]*/ +fn deserialize_96<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::stmt] { + s.read_vec( + + /*@syntax::ast::stmt*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, {|| deserialize_97(s) }) + }) + }) +} +/*syntax::ast::blk_check_mode*/ +fn deserialize_118<S: std::serialization::deserializer>(s: S) -> + syntax::ast::blk_check_mode { + s.read_enum("syntax::ast::blk_check_mode", + + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::default_blk } + 1u { + syntax::ast::unchecked_blk + } + 2u { syntax::ast::unsafe_blk } + } + }) + }) +} +/*syntax::ast::blk_*/ +fn deserialize_82<S: std::serialization::deserializer>(s: S) -> + syntax::ast::blk_ { + + s.read_rec( + + + /*[@syntax::ast::view_item]*/ + + /*[@syntax::ast::stmt]*/ + + /*core::option::t<@syntax::ast::expr>*/ + + /*syntax::ast::node_id*/ + + /*syntax::ast::blk_check_mode*/ + + {|| + {view_items: + s.read_rec_field("view_items", 0u, + {|| deserialize_83(s) }), + stmts: + s.read_rec_field("stmts", 1u, + {|| deserialize_96(s) }), + expr: + s.read_rec_field("expr", 2u, {|| deserialize_77(s) }), + id: s.read_rec_field("id", 3u, {|| deserialize_27(s) }), + rules: + s.read_rec_field("rules", 4u, + {|| deserialize_118(s) }),} + }) + +} +/*syntax::ast::blk*/ +fn deserialize_81<S: std::serialization::deserializer>(s: S) -> + syntax::ast::blk { + + s.read_rec( + + + /*syntax::ast::blk_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_82(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*syntax::ast::arm*/ +fn deserialize_120<S: std::serialization::deserializer>(s: S) -> + syntax::ast::arm { + + s.read_rec( + + + /*[@syntax::ast::pat]*/ + + /*core::option::t<@syntax::ast::expr>*/ + + /*syntax::ast::blk*/ + + {|| + {pats: + s.read_rec_field("pats", 0u, + {|| deserialize_111(s) }), + guard: + s.read_rec_field("guard", 1u, + {|| deserialize_77(s) }), + body: + s.read_rec_field("body", 2u, + {|| deserialize_81(s) }),} + }) + +} +/*[syntax::ast::arm]*/ +fn deserialize_119<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::arm] { + s.read_vec( + + /*syntax::ast::arm*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_120(s) }) + }) + }) +} +/*syntax::ast::alt_mode*/ +fn deserialize_121<S: std::serialization::deserializer>(s: S) -> + syntax::ast::alt_mode { + s.read_enum("syntax::ast::alt_mode", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::alt_check } + 1u { + syntax::ast::alt_exhaustive + } + } + }) + }) +} +/*int*/ +fn deserialize_127<S: std::serialization::deserializer>(s: S) -> int { + s.read_int() +} +/*syntax::ast::capture_item*/ +fn deserialize_126<S: std::serialization::deserializer>(s: S) -> + syntax::ast::capture_item { + + s.read_rec( + + + /*int*/ + + /*syntax::ast::ident*/ + + /*syntax::codemap::span*/ + + {|| + {id: s.read_rec_field("id", 0u, {|| deserialize_127(s) }), + name: + s.read_rec_field("name", 1u, {|| deserialize_1(s) }), + span: + s.read_rec_field("span", 2u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::capture_item*/ +fn deserialize_125<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::capture_item { + + s.read_box(/*syntax::ast::capture_item*/{|| @deserialize_126(s) }) + +} +/*[@syntax::ast::capture_item]*/ +fn deserialize_124<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::capture_item] { + s.read_vec( + + /*@syntax::ast::capture_item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_125(s) }) + }) + }) +} +/*syntax::ast::capture_clause*/ +fn deserialize_123<S: std::serialization::deserializer>(s: S) -> + syntax::ast::capture_clause { + + s.read_rec( + + + /*[@syntax::ast::capture_item]*/ + + /*[@syntax::ast::capture_item]*/ + + {|| + {copies: + s.read_rec_field("copies", 0u, + {|| deserialize_124(s) }), + moves: + s.read_rec_field("moves", 1u, + {|| deserialize_124(s) }),} + }) + +} +/*@syntax::ast::capture_clause*/ +fn deserialize_122<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::capture_clause { + + s.read_box(/*syntax::ast::capture_clause*/{|| @deserialize_123(s) }) + +} +/*syntax::ast::expr_check_mode*/ +fn deserialize_128<S: std::serialization::deserializer>(s: S) -> + syntax::ast::expr_check_mode { + s.read_enum("syntax::ast::expr_check_mode", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::claimed_expr } + 1u { syntax::ast::checked_expr } + } + }) + }) +} +/*syntax::ast::expr_*/ +fn deserialize_72<S: std::serialization::deserializer>(s: S) -> + syntax::ast::expr_ { + s.read_enum("syntax::ast::expr_", + /*[@syntax::ast::expr]*//*syntax::ast::mutability*/ + + /*[syntax::ast::field]*/ + /*core::option::t<@syntax::ast::expr>*/ + + /*@syntax::ast::expr*//*[@syntax::ast::expr]*//*bool*/ + + /*[@syntax::ast::expr]*/ + + /*@syntax::ast::expr*/ + /*[core::option::t<@syntax::ast::expr>]*/ + + /*syntax::ast::binop*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*/ + + /*syntax::ast::unop*//*@syntax::ast::expr*/ + + /*@syntax::ast::lit*/ + + /*@syntax::ast::expr*//*@syntax::ast::ty*/ + + /*@syntax::ast::expr*//*syntax::ast::blk*/ + /*core::option::t<@syntax::ast::expr>*/ + + /*@syntax::ast::expr*//*syntax::ast::blk*/ + + /*@syntax::ast::local*//*@syntax::ast::expr*/ + /*syntax::ast::blk*/ + + /*syntax::ast::blk*//*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*[syntax::ast::arm]*/ + /*syntax::ast::alt_mode*/ + + /*syntax::ast::proto*//*syntax::ast::fn_decl*/ + /*syntax::ast::blk*//*@syntax::ast::capture_clause*/ + + /*syntax::ast::fn_decl*//*syntax::ast::blk*/ + + /*syntax::ast::blk*/ + + /*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + + /*syntax::ast::binop*//*@syntax::ast::expr*/ + /*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*syntax::ast::ident*/ + /*[@syntax::ast::ty]*/ + + /*@syntax::ast::expr*//*@syntax::ast::expr*/ + + /*@syntax::ast::path*/ + + /*core::option::t<@syntax::ast::expr>*/ + + + + + + /*core::option::t<@syntax::ast::expr>*/ + + /*@syntax::ast::expr*/ + + /*int*//*@syntax::ast::expr*//*@syntax::ast::expr*/ + + /*@syntax::ast::expr*/ + + /*syntax::ast::expr_check_mode*//*@syntax::ast::expr*/ + + /*@syntax::ast::expr*//*syntax::ast::blk*/ + /*core::option::t<@syntax::ast::expr>*/ + + /*syntax::ast::mac*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::expr_vec(s.read_enum_variant_arg(0u, + {|| + deserialize_73(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_33(s) + })) + } + 1u { + syntax::ast::expr_rec(s.read_enum_variant_arg(0u, + {|| + deserialize_74(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_77(s) + })) + } + 2u { + syntax::ast::expr_call(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_73(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_18(s) + })) + } + 3u { + syntax::ast::expr_tup(s.read_enum_variant_arg(0u, + {|| + deserialize_73(s) + })) + } + 4u { + syntax::ast::expr_bind(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_78(s) + })) + } + 5u { + syntax::ast::expr_binary(s.read_enum_variant_arg(0u, + {|| + deserialize_79(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_70(s) + })) + } + 6u { + syntax::ast::expr_unary(s.read_enum_variant_arg(0u, + {|| + deserialize_80(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 7u { + syntax::ast::expr_lit(s.read_enum_variant_arg(0u, + {|| + deserialize_58(s) + })) + } + 8u { + syntax::ast::expr_cast(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_29(s) + })) + } + 9u { + syntax::ast::expr_if(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_81(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_77(s) + })) + } + 10u { + syntax::ast::expr_while(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_81(s) + })) + } + 11u { + syntax::ast::expr_for(s.read_enum_variant_arg(0u, + {|| + deserialize_104(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_81(s) + })) + } + 12u { + syntax::ast::expr_do_while(s.read_enum_variant_arg(0u, + {|| + deserialize_81(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 13u { + syntax::ast::expr_alt(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_119(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_121(s) + })) + } + 14u { + syntax::ast::expr_fn(s.read_enum_variant_arg(0u, + {|| + deserialize_37(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_81(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_122(s) + })) + } + 15u { + syntax::ast::expr_fn_block(s.read_enum_variant_arg(0u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_81(s) + })) + } + 16u { + syntax::ast::expr_block(s.read_enum_variant_arg(0u, + {|| + deserialize_81(s) + })) + } + 17u { + syntax::ast::expr_copy(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + 18u { + syntax::ast::expr_move(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 19u { + syntax::ast::expr_assign(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 20u { + syntax::ast::expr_swap(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 21u { + syntax::ast::expr_assign_op(s.read_enum_variant_arg(0u, + {|| + deserialize_79(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_70(s) + })) + } + 22u { + syntax::ast::expr_field(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_53(s) + })) + } + 23u { + syntax::ast::expr_index(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 24u { + syntax::ast::expr_path(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + })) + } + 25u { + syntax::ast::expr_fail(s.read_enum_variant_arg(0u, + {|| + deserialize_77(s) + })) + } + 26u { syntax::ast::expr_break } + 27u { syntax::ast::expr_cont } + 28u { + syntax::ast::expr_ret(s.read_enum_variant_arg(0u, + {|| + deserialize_77(s) + })) + } + 29u { + syntax::ast::expr_be(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + 30u { + syntax::ast::expr_log(s.read_enum_variant_arg(0u, + {|| + deserialize_127(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_70(s) + })) + } + 31u { + syntax::ast::expr_assert(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + 32u { + syntax::ast::expr_check(s.read_enum_variant_arg(0u, + {|| + deserialize_128(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 33u { + syntax::ast::expr_if_check(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_81(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_77(s) + })) + } + 34u { + syntax::ast::expr_mac(s.read_enum_variant_arg(0u, + {|| + deserialize_67(s) + })) + } + } + }) + }) +} +/*syntax::ast::expr*/ +fn deserialize_71<S: std::serialization::deserializer>(s: S) -> + syntax::ast::expr { + + s.read_rec( + + + /*syntax::ast::node_id*/ + + /*syntax::ast::expr_*/ + + /*syntax::codemap::span*/ + + {|| + {id: s.read_rec_field("id", 0u, {|| deserialize_27(s) }), + node: + s.read_rec_field("node", 1u, {|| deserialize_72(s) }), + span: + s.read_rec_field("span", 2u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::expr*/ +fn deserialize_70<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::expr { + + s.read_box(/*syntax::ast::expr*/{|| @deserialize_71(s) }) + +} +/*syntax::ast::mac_arg<@syntax::ast::expr>*/ +fn deserialize_69<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mac_arg<@syntax::ast::expr> { + s.read_enum("core::option::t", + + + /*@syntax::ast::expr*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_70(s) + })) + } + } + }) + }) +} +/*syntax::ast::mac_body_*/ +fn deserialize_130<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mac_body_ { + + s.read_rec( + + + /*syntax::codemap::span*/ + + {|| + {span: + s.read_rec_field("span", 0u, + {|| deserialize_19(s) }),} + }) + +} +/*syntax::ast::mac_body<syntax::ast::mac_body_>*/ +fn deserialize_129<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mac_body<syntax::ast::mac_body_> { + s.read_enum("core::option::t", + + + /*syntax::ast::mac_body_*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_130(s) + })) + } + } + }) + }) +} +/*syntax::ast::mac_*/ +fn deserialize_68<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mac_ { + s.read_enum("syntax::ast::mac_", + /*@syntax::ast::path*/ + /*syntax::ast::mac_arg<@syntax::ast::expr>*/ + /*syntax::ast::mac_body<syntax::ast::mac_body_>*/ + + /*@syntax::ast::ty*/ + + /*syntax::ast::blk*/ + + + + /*syntax::codemap::span*//*@syntax::ast::expr*/ + + /*uint*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::mac_invoc(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_69(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_129(s) + })) + } + 1u { + syntax::ast::mac_embed_type(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + })) + } + 2u { + syntax::ast::mac_embed_block(s.read_enum_variant_arg(0u, + {|| + deserialize_81(s) + })) + } + 3u { syntax::ast::mac_ellipsis } + 4u { + syntax::ast::mac_aq(s.read_enum_variant_arg(0u, + {|| + deserialize_19(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 5u { + syntax::ast::mac_var(s.read_enum_variant_arg(0u, + {|| + deserialize_20(s) + })) + } + } + }) + }) +} +/*syntax::ast::mac*/ +fn deserialize_67<S: std::serialization::deserializer>(s: S) -> + syntax::ast::mac { + + s.read_rec( + + + /*syntax::ast::mac_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_68(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*syntax::ast::ty_*/ +fn deserialize_31<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_ { + s.read_enum("syntax::ast::ty_", + + + + + /*syntax::ast::mt*/ + + /*syntax::ast::mt*/ + + /*syntax::ast::mt*/ + + /*syntax::ast::mt*/ + + /*[syntax::ast::ty_field]*/ + + /*syntax::ast::proto*//*syntax::ast::fn_decl*/ + + /*[@syntax::ast::ty]*/ + + /*@syntax::ast::path*//*syntax::ast::node_id*/ + + /*@syntax::ast::ty*//*[@syntax::ast::ty_constr]*/ + + /*syntax::ast::mac*/ + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::ty_nil } + 1u { syntax::ast::ty_bot } + 2u { + syntax::ast::ty_box(s.read_enum_variant_arg(0u, + {|| + deserialize_32(s) + })) + } + 3u { + syntax::ast::ty_uniq(s.read_enum_variant_arg(0u, + {|| + deserialize_32(s) + })) + } + 4u { + syntax::ast::ty_vec(s.read_enum_variant_arg(0u, + {|| + deserialize_32(s) + })) + } + 5u { + syntax::ast::ty_ptr(s.read_enum_variant_arg(0u, + {|| + deserialize_32(s) + })) + } + 6u { + syntax::ast::ty_rec(s.read_enum_variant_arg(0u, + {|| + deserialize_34(s) + })) + } + 7u { + syntax::ast::ty_fn(s.read_enum_variant_arg(0u, + {|| + deserialize_37(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_38(s) + })) + } + 8u { + syntax::ast::ty_tup(s.read_enum_variant_arg(0u, + {|| + deserialize_53(s) + })) + } + 9u { + syntax::ast::ty_path(s.read_enum_variant_arg(0u, + {|| + deserialize_49(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_27(s) + })) + } + 10u { + syntax::ast::ty_constr(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_59(s) + })) + } + 11u { + syntax::ast::ty_mac(s.read_enum_variant_arg(0u, + {|| + deserialize_67(s) + })) + } + 12u { syntax::ast::ty_infer } + } + }) + }) +} +/*syntax::ast::ty*/ +fn deserialize_30<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty { + + s.read_rec( + + + /*syntax::ast::ty_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, {|| deserialize_31(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::ty*/ +fn deserialize_29<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::ty { + + s.read_box(/*syntax::ast::ty*/{|| @deserialize_30(s) }) + +} +/*syntax::ast::ty_param_bound*/ +fn deserialize_135<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_param_bound { + s.read_enum("syntax::ast::ty_param_bound", + + + + + /*@syntax::ast::ty*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::bound_copy } + 1u { syntax::ast::bound_send } + 2u { + syntax::ast::bound_iface(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + })) + } + } + }) + }) +} +/*[syntax::ast::ty_param_bound]*/ +fn deserialize_134<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::ty_param_bound] { + s.read_vec( + + /*syntax::ast::ty_param_bound*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_135(s) }) + }) + }) +} +/*@[syntax::ast::ty_param_bound]*/ +fn deserialize_133<S: std::serialization::deserializer>(s: S) -> + @[syntax::ast::ty_param_bound] { + + s.read_box(/*[syntax::ast::ty_param_bound]*/{|| @deserialize_134(s) }) + +} +/*syntax::ast::ty_param*/ +fn deserialize_132<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_param { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*syntax::ast::node_id*/ + + /*@[syntax::ast::ty_param_bound]*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + id: s.read_rec_field("id", 1u, {|| deserialize_27(s) }), + bounds: + s.read_rec_field("bounds", 2u, + {|| deserialize_133(s) }),} + }) + +} +/*[syntax::ast::ty_param]*/ +fn deserialize_131<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::ty_param] { + s.read_vec( + + /*syntax::ast::ty_param*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_132(s) }) + }) + }) +} +/*[@syntax::ast::item]*/ +fn deserialize_137<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::item] { + s.read_vec( + + /*@syntax::ast::item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_117(s) }) + }) + }) +} +/*syntax::ast::_mod*/ +fn deserialize_136<S: std::serialization::deserializer>(s: S) -> + syntax::ast::_mod { + + s.read_rec( + + + /*[@syntax::ast::view_item]*/ + + /*[@syntax::ast::item]*/ + + {|| + {view_items: + s.read_rec_field("view_items", 0u, + {|| deserialize_83(s) }), + items: + s.read_rec_field("items", 1u, + {|| deserialize_137(s) }),} + }) + +} +/*syntax::ast::native_item_*/ +fn deserialize_142<S: std::serialization::deserializer>(s: S) -> + syntax::ast::native_item_ { + s.read_enum("syntax::ast::native_item_", + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::native_item_fn(s.read_enum_variant_arg(0u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_131(s) + })) + } + } + }) + }) +} +/*syntax::ast::native_item*/ +fn deserialize_141<S: std::serialization::deserializer>(s: S) -> + syntax::ast::native_item { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*[syntax::ast::attribute]*/ + + /*syntax::ast::native_item_*/ + + /*syntax::ast::node_id*/ + + /*syntax::codemap::span*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + attrs: + s.read_rec_field("attrs", 1u, {|| deserialize_2(s) }), + node: + s.read_rec_field("node", 2u, + {|| deserialize_142(s) }), + id: s.read_rec_field("id", 3u, {|| deserialize_27(s) }), + span: + s.read_rec_field("span", 4u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::native_item*/ +fn deserialize_140<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::native_item { + + s.read_box(/*syntax::ast::native_item*/{|| @deserialize_141(s) }) + +} +/*[@syntax::ast::native_item]*/ +fn deserialize_139<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::native_item] { + s.read_vec( + + /*@syntax::ast::native_item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_140(s) }) + }) + }) +} +/*syntax::ast::native_mod*/ +fn deserialize_138<S: std::serialization::deserializer>(s: S) -> + syntax::ast::native_mod { + + s.read_rec( + + + /*[@syntax::ast::view_item]*/ + + /*[@syntax::ast::native_item]*/ + + {|| + {view_items: + s.read_rec_field("view_items", 0u, + {|| deserialize_83(s) }), + items: + s.read_rec_field("items", 1u, + {|| deserialize_139(s) }),} + }) + +} +/*syntax::ast::variant_arg*/ +fn deserialize_147<S: std::serialization::deserializer>(s: S) -> + syntax::ast::variant_arg { + + s.read_rec( + + + /*@syntax::ast::ty*/ + + /*syntax::ast::node_id*/ + + {|| + {ty: s.read_rec_field("ty", 0u, {|| deserialize_29(s) }), + id: s.read_rec_field("id", 1u, {|| deserialize_27(s) }),} + }) +} +/*[syntax::ast::variant_arg]*/ +fn deserialize_146<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::variant_arg] { + s.read_vec( + + /*syntax::ast::variant_arg*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_147(s) }) + }) + }) +} +/*syntax::ast::variant_*/ +fn deserialize_145<S: std::serialization::deserializer>(s: S) -> + syntax::ast::variant_ { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*[syntax::ast::attribute]*/ + + /*[syntax::ast::variant_arg]*/ + + /*syntax::ast::node_id*/ + + /*core::option::t<@syntax::ast::expr>*/ + + {|| + {name: + s.read_rec_field("name", 0u, {|| deserialize_1(s) }), + attrs: + s.read_rec_field("attrs", 1u, {|| deserialize_2(s) }), + args: + s.read_rec_field("args", 2u, + {|| deserialize_146(s) }), + id: s.read_rec_field("id", 3u, {|| deserialize_27(s) }), + disr_expr: + s.read_rec_field("disr_expr", 4u, + {|| deserialize_77(s) }),} + }) + +} +/*syntax::ast::variant*/ +fn deserialize_144<S: std::serialization::deserializer>(s: S) -> + syntax::ast::variant { + + s.read_rec( + + + /*syntax::ast::variant_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, + {|| deserialize_145(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::variant]*/ +fn deserialize_143<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::variant] { + s.read_vec( + + /*syntax::ast::variant*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_144(s) }) + }) + }) +} +/*syntax::ast::privacy*/ +fn deserialize_152<S: std::serialization::deserializer>(s: S) -> + syntax::ast::privacy { + s.read_enum("syntax::ast::privacy", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { syntax::ast::priv } + 1u { syntax::ast::pub } + } + }) + }) +} +/*syntax::ast::class_mutability*/ +fn deserialize_154<S: std::serialization::deserializer>(s: S) -> + syntax::ast::class_mutability { + s.read_enum("syntax::ast::class_mutability", + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::class_mutable + } + 1u { + syntax::ast::class_immutable + } + } + }) + }) +} +/*syntax::ast::class_member*/ +fn deserialize_153<S: std::serialization::deserializer>(s: S) -> + syntax::ast::class_member { + s.read_enum("syntax::ast::class_member", + /*syntax::ast::ident*//*@syntax::ast::ty*/ + /*syntax::ast::class_mutability*//*syntax::ast::node_id*/ + + /*@syntax::ast::item*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::instance_var(s.read_enum_variant_arg(0u, + {|| + deserialize_1(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_29(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_154(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_27(s) + })) + } + 1u { + syntax::ast::class_method(s.read_enum_variant_arg(0u, + {|| + deserialize_117(s) + })) + } + } + }) + }) +} +/*syntax::ast::class_item_*/ +fn deserialize_151<S: std::serialization::deserializer>(s: S) -> + syntax::ast::class_item_ { + + s.read_rec( + + + /*syntax::ast::privacy*/ + + /*syntax::ast::class_member*/ + + {|| + {privacy: + s.read_rec_field("privacy", 0u, + {|| deserialize_152(s) }), + decl: + s.read_rec_field("decl", 1u, + {|| deserialize_153(s) }),} + }) + +} +/*syntax::ast::class_item*/ +fn deserialize_150<S: std::serialization::deserializer>(s: S) -> + syntax::ast::class_item { + + s.read_rec( + + + /*syntax::ast::class_item_*/ + + /*syntax::codemap::span*/ + + {|| + {node: + s.read_rec_field("node", 0u, + {|| deserialize_151(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::class_item*/ +fn deserialize_149<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::class_item { + + s.read_box(/*syntax::ast::class_item*/{|| @deserialize_150(s) }) + +} +/*[@syntax::ast::class_item]*/ +fn deserialize_148<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::class_item] { + s.read_vec( + + /*@syntax::ast::class_item*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_149(s) }) + }) + }) +} +/*syntax::ast::ty_method*/ +fn deserialize_156<S: std::serialization::deserializer>(s: S) -> + syntax::ast::ty_method { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*[syntax::ast::attribute]*/ + + /*syntax::ast::fn_decl*/ + + /*[syntax::ast::ty_param]*/ + + /*syntax::codemap::span*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + attrs: + s.read_rec_field("attrs", 1u, {|| deserialize_2(s) }), + decl: + s.read_rec_field("decl", 2u, {|| deserialize_38(s) }), + tps: + s.read_rec_field("tps", 3u, {|| deserialize_131(s) }), + span: + s.read_rec_field("span", 4u, + {|| deserialize_19(s) }),} + }) + +} +/*[syntax::ast::ty_method]*/ +fn deserialize_155<S: std::serialization::deserializer>(s: S) -> + [syntax::ast::ty_method] { + s.read_vec( + + /*syntax::ast::ty_method*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_156(s) }) + }) + }) +} +/*core::option::t<@syntax::ast::ty>*/ +fn deserialize_157<S: std::serialization::deserializer>(s: S) -> + core::option::t<@syntax::ast::ty> { + s.read_enum("core::option::t", + + + /*@syntax::ast::ty*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { core::option::none } + 1u { + core::option::some(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + })) + } + } + }) + }) +} +/*syntax::ast::method*/ +fn deserialize_160<S: std::serialization::deserializer>(s: S) -> + syntax::ast::method { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*[syntax::ast::attribute]*/ + + /*[syntax::ast::ty_param]*/ + + /*syntax::ast::fn_decl*/ + + /*syntax::ast::blk*/ + + /*syntax::ast::node_id*/ + + /*syntax::codemap::span*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + attrs: + s.read_rec_field("attrs", 1u, {|| deserialize_2(s) }), + tps: + s.read_rec_field("tps", 2u, {|| deserialize_131(s) }), + decl: + s.read_rec_field("decl", 3u, {|| deserialize_38(s) }), + body: + s.read_rec_field("body", 4u, {|| deserialize_81(s) }), + id: s.read_rec_field("id", 5u, {|| deserialize_27(s) }), + span: + s.read_rec_field("span", 6u, + {|| deserialize_19(s) }),} + }) + +} +/*@syntax::ast::method*/ +fn deserialize_159<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::method { + + s.read_box(/*syntax::ast::method*/{|| @deserialize_160(s) }) + +} +/*[@syntax::ast::method]*/ +fn deserialize_158<S: std::serialization::deserializer>(s: S) -> + [@syntax::ast::method] { + s.read_vec( + + /*@syntax::ast::method*/ + {|len| + vec::init_fn(len, + {|i| + s.read_vec_elt(i, + {|| deserialize_159(s) }) + }) + }) +} +/*syntax::ast::item_*/ +fn deserialize_28<S: std::serialization::deserializer>(s: S) -> + syntax::ast::item_ { + s.read_enum("syntax::ast::item_", + /*@syntax::ast::ty*//*@syntax::ast::expr*/ + + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + /*syntax::ast::blk*/ + + /*syntax::ast::_mod*/ + + /*syntax::ast::native_mod*/ + + /*@syntax::ast::ty*//*[syntax::ast::ty_param]*/ + + /*[syntax::ast::variant]*//*[syntax::ast::ty_param]*/ + + /*syntax::ast::fn_decl*//*[syntax::ast::ty_param]*/ + /*syntax::ast::blk*//*syntax::ast::node_id*/ + /*syntax::ast::node_id*/ + + /*[syntax::ast::ty_param]*//*[@syntax::ast::class_item]*/ + /*syntax::ast::node_id*//*syntax::ast::fn_decl*/ + /*syntax::ast::blk*/ + + /*[syntax::ast::ty_param]*//*[syntax::ast::ty_method]*/ + + /*[syntax::ast::ty_param]*/ + /*core::option::t<@syntax::ast::ty>*//*@syntax::ast::ty*/ + /*[@syntax::ast::method]*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::item_const(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_70(s) + })) + } + 1u { + syntax::ast::item_fn(s.read_enum_variant_arg(0u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_131(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_81(s) + })) + } + 2u { + syntax::ast::item_mod(s.read_enum_variant_arg(0u, + {|| + deserialize_136(s) + })) + } + 3u { + syntax::ast::item_native_mod(s.read_enum_variant_arg(0u, + {|| + deserialize_138(s) + })) + } + 4u { + syntax::ast::item_ty(s.read_enum_variant_arg(0u, + {|| + deserialize_29(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_131(s) + })) + } + 5u { + syntax::ast::item_enum(s.read_enum_variant_arg(0u, + {|| + deserialize_143(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_131(s) + })) + } + 6u { + syntax::ast::item_res(s.read_enum_variant_arg(0u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_131(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_81(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_27(s) + }), + s.read_enum_variant_arg(4u, + {|| + deserialize_27(s) + })) + } + 7u { + syntax::ast::item_class(s.read_enum_variant_arg(0u, + {|| + deserialize_131(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_148(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_27(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_38(s) + }), + s.read_enum_variant_arg(4u, + {|| + deserialize_81(s) + })) + } + 8u { + syntax::ast::item_iface(s.read_enum_variant_arg(0u, + {|| + deserialize_131(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_155(s) + })) + } + 9u { + syntax::ast::item_impl(s.read_enum_variant_arg(0u, + {|| + deserialize_131(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_157(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_29(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_158(s) + })) + } + } + }) + }) +} +/*syntax::ast::item*/ +fn deserialize_0<S: std::serialization::deserializer>(s: S) -> + syntax::ast::item { + + s.read_rec( + + + /*syntax::ast::ident*/ + + /*[syntax::ast::attribute]*/ + + /*syntax::ast::node_id*/ + + /*syntax::ast::item_*/ + + /*syntax::codemap::span*/ + + {|| + {ident: + s.read_rec_field("ident", 0u, {|| deserialize_1(s) }), + attrs: + s.read_rec_field("attrs", 1u, {|| deserialize_2(s) }), + id: s.read_rec_field("id", 2u, {|| deserialize_27(s) }), + node: + s.read_rec_field("node", 3u, {|| deserialize_28(s) }), + span: + s.read_rec_field("span", 4u, + {|| deserialize_19(s) }),} + }) + +} +fn deserialize_syntax_ast_item<S: std::serialization::deserializer>(s: S) -> + syntax::ast::item { + deserialize_0(s) +} +/*syntax::ast::crate_num*/ +fn serialize_163<S: std::serialization::serializer>(s: S, + v: + syntax::ast::crate_num) { + s.emit_int(v); +} +/*syntax::ast::def_id*/ +fn serialize_162<S: std::serialization::serializer>(s: S, + v: syntax::ast::def_id) { + + s.emit_rec(/*syntax::ast::crate_num*//*syntax::ast::node_id*/ + {|| + { + s.emit_rec_field("crate", 0u, + {|| serialize_163(s, v.crate) }); + s.emit_rec_field("node", 1u, + {|| serialize_27(s, v.node) }) + } + }); +} +/*syntax::ast::prim_ty*/ +fn serialize_164<S: std::serialization::serializer>(s: S, + v: syntax::ast::prim_ty) { + + s.emit_enum("syntax::ast::prim_ty", + /*syntax::ast::int_ty*/ + /*syntax::ast::uint_ty*/ + /*syntax::ast::float_ty*/ + + {|| + alt v { + syntax::ast::ty_int(v0) { + s.emit_enum_variant("syntax::ast::ty_int", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_14(s, + v0) + }) + } + }) + } + syntax::ast::ty_uint(v0) { + s.emit_enum_variant("syntax::ast::ty_uint", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_16(s, + v0) + }) + } + }) + } + syntax::ast::ty_float(v0) { + s.emit_enum_variant("syntax::ast::ty_float", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_17(s, + v0) + }) + } + }) + } + syntax::ast::ty_str { + s.emit_enum_variant("syntax::ast::ty_str", 3u, 0u, + {|| }) + } + syntax::ast::ty_bool { + s.emit_enum_variant("syntax::ast::ty_bool", 4u, 0u, + {|| }) + } + } + }); +} +/*@syntax::ast::def*/ +fn serialize_165<S: std::serialization::serializer>(s: S, + v: @syntax::ast::def) { + + s.emit_box(/*syntax::ast::def*/{|| serialize_161(s, *v) }); +} +/*syntax::ast::def*/ +fn serialize_161<S: std::serialization::serializer>(s: S, + v: syntax::ast::def) { + + s.emit_enum("syntax::ast::def", + /*syntax::ast::def_id*//*syntax::ast::purity*/ + /*syntax::ast::node_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::node_id*/ + /*syntax::ast::mode<syntax::ast::rmode>*/ + /*syntax::ast::node_id*//*bool*/ + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::prim_ty*/ + /*syntax::ast::def_id*//*uint*/ + /*syntax::ast::node_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::node_id*//*@syntax::ast::def*/ + /*syntax::ast::node_id*/ + /*syntax::ast::def_id*/ + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + {|| + alt v { + syntax::ast::def_fn(v0, v1) { + s.emit_enum_variant("syntax::ast::def_fn", 0u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_43(s, + v1) + }) + } + }) + } + syntax::ast::def_self(v0) { + s.emit_enum_variant("syntax::ast::def_self", 1u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }) + } + }) + } + syntax::ast::def_mod(v0) { + s.emit_enum_variant("syntax::ast::def_mod", 2u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_native_mod(v0) { + s.emit_enum_variant("syntax::ast::def_native_mod", 3u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_const(v0) { + s.emit_enum_variant("syntax::ast::def_const", 4u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_arg(v0, v1) { + s.emit_enum_variant("syntax::ast::def_arg", 5u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_41(s, + v1) + }) + } + }) + } + syntax::ast::def_local(v0, v1) { + s.emit_enum_variant("syntax::ast::def_local", 6u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_18(s, + v1) + }) + } + }) + } + syntax::ast::def_variant(v0, v1) { + s.emit_enum_variant("syntax::ast::def_variant", 7u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_162(s, + v1) + }) + } + }) + } + syntax::ast::def_ty(v0) { + s.emit_enum_variant("syntax::ast::def_ty", 8u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_prim_ty(v0) { + s.emit_enum_variant("syntax::ast::def_prim_ty", 9u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_164(s, + v0) + }) + } + }) + } + syntax::ast::def_ty_param(v0, v1) { + s.emit_enum_variant("syntax::ast::def_ty_param", 10u, + 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_20(s, + v1) + }) + } + }) + } + syntax::ast::def_binding(v0) { + s.emit_enum_variant("syntax::ast::def_binding", 11u, + 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }) + } + }) + } + syntax::ast::def_use(v0) { + s.emit_enum_variant("syntax::ast::def_use", 12u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_upvar(v0, v1, v2) { + s.emit_enum_variant("syntax::ast::def_upvar", 13u, 3u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_27(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_165(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_27(s, + v2) + }) + } + }) + } + syntax::ast::def_class(v0) { + s.emit_enum_variant("syntax::ast::def_class", 14u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + syntax::ast::def_class_field(v0, v1) { + s.emit_enum_variant("syntax::ast::def_class_field", + 15u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_162(s, + v1) + }) + } + }) + } + syntax::ast::def_class_method(v0, v1) { + s.emit_enum_variant("syntax::ast::def_class_method", + 16u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_162(s, + v1) + }) + } + }) + } + } + }); +} +fn serialize_syntax_ast_def<S: std::serialization::serializer>(s: S, + v: + syntax::ast::def) { + serialize_161(s, v); +} +/*syntax::ast::crate_num*/ +fn deserialize_163<S: std::serialization::deserializer>(s: S) -> + syntax::ast::crate_num { + s.read_int() +} +/*syntax::ast::def_id*/ +fn deserialize_162<S: std::serialization::deserializer>(s: S) -> + syntax::ast::def_id { + + s.read_rec( + + + /*syntax::ast::crate_num*/ + + /*syntax::ast::node_id*/ + + {|| + {crate: + s.read_rec_field("crate", 0u, + {|| deserialize_163(s) }), + node: + s.read_rec_field("node", 1u, + {|| deserialize_27(s) }),} + }) + +} +/*syntax::ast::prim_ty*/ +fn deserialize_164<S: std::serialization::deserializer>(s: S) -> + syntax::ast::prim_ty { + s.read_enum("syntax::ast::prim_ty", + /*syntax::ast::int_ty*/ + + /*syntax::ast::uint_ty*/ + + /*syntax::ast::float_ty*/ + + + + + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::ty_int(s.read_enum_variant_arg(0u, + {|| + deserialize_14(s) + })) + } + 1u { + syntax::ast::ty_uint(s.read_enum_variant_arg(0u, + {|| + deserialize_16(s) + })) + } + 2u { + syntax::ast::ty_float(s.read_enum_variant_arg(0u, + {|| + deserialize_17(s) + })) + } + 3u { syntax::ast::ty_str } + 4u { syntax::ast::ty_bool } + } + }) + }) +} +/*@syntax::ast::def*/ +fn deserialize_165<S: std::serialization::deserializer>(s: S) -> + @syntax::ast::def { + + s.read_box(/*syntax::ast::def*/{|| @deserialize_161(s) }) + +} +/*syntax::ast::def*/ +fn deserialize_161<S: std::serialization::deserializer>(s: S) -> + syntax::ast::def { + s.read_enum("syntax::ast::def", + /*syntax::ast::def_id*//*syntax::ast::purity*/ + + /*syntax::ast::node_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::node_id*/ + /*syntax::ast::mode<syntax::ast::rmode>*/ + + /*syntax::ast::node_id*//*bool*/ + + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::prim_ty*/ + + /*syntax::ast::def_id*//*uint*/ + + /*syntax::ast::node_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::node_id*//*@syntax::ast::def*/ + /*syntax::ast::node_id*/ + + /*syntax::ast::def_id*/ + + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + + /*syntax::ast::def_id*//*syntax::ast::def_id*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::def_fn(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_43(s) + })) + } + 1u { + syntax::ast::def_self(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + })) + } + 2u { + syntax::ast::def_mod(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 3u { + syntax::ast::def_native_mod(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 4u { + syntax::ast::def_const(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 5u { + syntax::ast::def_arg(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_41(s) + })) + } + 6u { + syntax::ast::def_local(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_18(s) + })) + } + 7u { + syntax::ast::def_variant(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_162(s) + })) + } + 8u { + syntax::ast::def_ty(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 9u { + syntax::ast::def_prim_ty(s.read_enum_variant_arg(0u, + {|| + deserialize_164(s) + })) + } + 10u { + syntax::ast::def_ty_param(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_20(s) + })) + } + 11u { + syntax::ast::def_binding(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + })) + } + 12u { + syntax::ast::def_use(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 13u { + syntax::ast::def_upvar(s.read_enum_variant_arg(0u, + {|| + deserialize_27(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_165(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_27(s) + })) + } + 14u { + syntax::ast::def_class(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 15u { + syntax::ast::def_class_field(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_162(s) + })) + } + 16u { + syntax::ast::def_class_method(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_162(s) + })) + } + } + }) + }) +} +fn deserialize_syntax_ast_def<S: std::serialization::deserializer>(s: S) -> + syntax::ast::def { + deserialize_161(s) +} +/*middle::typeck::method_origin*/ +fn serialize_166<S: std::serialization::serializer>(s: S, + v: + middle::typeck::method_origin) { + s.emit_enum("middle::typeck::method_origin", + /*syntax::ast::def_id*/ + /*syntax::ast::def_id*//*uint*//*uint*//*uint*/ + /*syntax::ast::def_id*//*uint*/ + {|| + alt v { + middle::typeck::method_static(v0) { + s.emit_enum_variant("middle::typeck::method_static", + 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }) + } + }) + } + middle::typeck::method_param(v0, v1, v2, v3) { + s.emit_enum_variant("middle::typeck::method_param", + 1u, 4u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_20(s, + v1) + }); + s.emit_enum_variant_arg(2u, + {|| + serialize_20(s, + v2) + }); + s.emit_enum_variant_arg(3u, + {|| + serialize_20(s, + v3) + }) + } + }) + } + middle::typeck::method_iface(v0, v1) { + s.emit_enum_variant("middle::typeck::method_iface", + 2u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_20(s, + v1) + }) + } + }) + } + } + }); +} +fn serialize_middle_typeck_method_origin<S: std::serialization::serializer>(s: + S, + v: + middle::typeck::method_origin) { + serialize_166(s, v); +} +/*middle::typeck::method_origin*/ +fn deserialize_166<S: std::serialization::deserializer>(s: S) -> + middle::typeck::method_origin { + s.read_enum("middle::typeck::method_origin", + /*syntax::ast::def_id*/ + + /*syntax::ast::def_id*//*uint*//*uint*//*uint*/ + + /*syntax::ast::def_id*//*uint*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + middle::typeck::method_static(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + })) + } + 1u { + middle::typeck::method_param(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_20(s) + }), + s.read_enum_variant_arg(2u, + {|| + deserialize_20(s) + }), + s.read_enum_variant_arg(3u, + {|| + deserialize_20(s) + })) + } + 2u { + middle::typeck::method_iface(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_20(s) + })) + } + } + }) + }) +} +fn deserialize_middle_typeck_method_origin<S: std::serialization::deserializer>(s: + S) + -> middle::typeck::method_origin { + deserialize_166(s) +} +/*middle::freevars::freevar_entry*/ +fn serialize_167<S: std::serialization::serializer>(s: S, + v: + middle::freevars::freevar_entry) { + s.emit_rec(/*syntax::ast::def*//*syntax::codemap::span*/ + {|| + { + s.emit_rec_field("def", 0u, + {|| serialize_161(s, v.def) }); + s.emit_rec_field("span", 1u, + {|| serialize_19(s, v.span) }) + } + }); +} +fn serialize_middle_freevars_freevar_entry<S: std::serialization::serializer>(s: + S, + v: + middle::freevars::freevar_entry) { + serialize_167(s, v); +} +/*middle::freevars::freevar_entry*/ +fn deserialize_167<S: std::serialization::deserializer>(s: S) -> + middle::freevars::freevar_entry { + + s.read_rec( + + + /*syntax::ast::def*/ + + /*syntax::codemap::span*/ + + {|| + {def: + s.read_rec_field("def", 0u, {|| deserialize_161(s) }), + span: + s.read_rec_field("span", 1u, + {|| deserialize_19(s) }),} + }) + +} +fn deserialize_middle_freevars_freevar_entry<S: std::serialization::deserializer>(s: + S) + -> middle::freevars::freevar_entry { + deserialize_167(s) +} +fn serialize_syntax_ast_def_id<S: std::serialization::serializer>(s: S, + v: + syntax::ast::def_id) { + serialize_162(s, v); +} +fn deserialize_syntax_ast_def_id<S: std::serialization::deserializer>(s: S) -> + syntax::ast::def_id { + deserialize_162(s) +} +/*syntax::ast::inlined_item*/ +fn serialize_168<S: std::serialization::serializer>(s: S, + v: + syntax::ast::inlined_item) { + s.emit_enum("syntax::ast::inlined_item", + /*@syntax::ast::item*/ + /*syntax::ast::def_id*//*@syntax::ast::method*/ + {|| + alt v { + syntax::ast::ii_item(v0) { + s.emit_enum_variant("syntax::ast::ii_item", 0u, 1u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_117(s, + v0) + }) + } + }) + } + syntax::ast::ii_method(v0, v1) { + s.emit_enum_variant("syntax::ast::ii_method", 1u, 2u, + {|| + { + s.emit_enum_variant_arg(0u, + {|| + serialize_162(s, + v0) + }); + s.emit_enum_variant_arg(1u, + {|| + serialize_159(s, + v1) + }) + } + }) + } + } + }); +} +fn serialize_syntax_ast_inlined_item<S: std::serialization::serializer>(s: S, + v: + syntax::ast::inlined_item) { + serialize_168(s, v); +} +/*syntax::ast::inlined_item*/ +fn deserialize_168<S: std::serialization::deserializer>(s: S) -> + syntax::ast::inlined_item { + s.read_enum("syntax::ast::inlined_item", + /*@syntax::ast::item*/ + + /*syntax::ast::def_id*//*@syntax::ast::method*/ + {|| + s.read_enum_variant({|v_id| + alt check v_id { + 0u { + syntax::ast::ii_item(s.read_enum_variant_arg(0u, + {|| + deserialize_117(s) + })) + } + 1u { + syntax::ast::ii_method(s.read_enum_variant_arg(0u, + {|| + deserialize_162(s) + }), + s.read_enum_variant_arg(1u, + {|| + deserialize_159(s) + })) + } + } + }) + }) +} +fn deserialize_syntax_ast_inlined_item<S: std::serialization::deserializer>(s: + S) + -> syntax::ast::inlined_item { + deserialize_168(s) +} diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs new file mode 100644 index 00000000000..4ccb3405560 --- /dev/null +++ b/src/rustc/metadata/common.rs @@ -0,0 +1,112 @@ +// EBML enum definitions and utils shared by the encoder and decoder + +const tag_paths: uint = 0x01u; + +const tag_items: uint = 0x02u; + +const tag_paths_data: uint = 0x03u; + +const tag_paths_data_name: uint = 0x04u; + +const tag_paths_data_item: uint = 0x05u; + +const tag_paths_data_mod: uint = 0x06u; + +const tag_def_id: uint = 0x07u; + +const tag_items_data: uint = 0x08u; + +const tag_items_data_item: uint = 0x09u; + +const tag_items_data_item_family: uint = 0x0au; + +const tag_items_data_item_ty_param_bounds: uint = 0x0bu; + +const tag_items_data_item_type: uint = 0x0cu; + +const tag_items_data_item_symbol: uint = 0x0du; + +const tag_items_data_item_variant: uint = 0x0eu; + +const tag_items_data_item_enum_id: uint = 0x0fu; + +const tag_index: uint = 0x11u; + +const tag_index_buckets: uint = 0x12u; + +const tag_index_buckets_bucket: uint = 0x13u; + +const tag_index_buckets_bucket_elt: uint = 0x14u; + +const tag_index_table: uint = 0x15u; + +const tag_meta_item_name_value: uint = 0x18u; + +const tag_meta_item_name: uint = 0x19u; + +const tag_meta_item_value: uint = 0x20u; + +const tag_attributes: uint = 0x21u; + +const tag_attribute: uint = 0x22u; + +const tag_meta_item_word: uint = 0x23u; + +const tag_meta_item_list: uint = 0x24u; + +// The list of crates that this crate depends on +const tag_crate_deps: uint = 0x25u; + +// A single crate dependency +const tag_crate_dep: uint = 0x26u; + +const tag_crate_hash: uint = 0x28u; + +const tag_mod_impl: uint = 0x30u; + +const tag_item_method: uint = 0x31u; +const tag_impl_iface: uint = 0x32u; + +// discriminator value for variants +const tag_disr_val: uint = 0x34u; + +// used to encode ast_map::path and ast_map::path_elt +const tag_path: uint = 0x40u; +const tag_path_len: uint = 0x41u; +const tag_path_elt_mod: uint = 0x42u; +const tag_path_elt_name: uint = 0x43u; + +// used to encode crate_ctxt side tables +enum astencode_tag { // Reserves 0x50 -- 0x6f + tag_ast = 0x50, + + tag_tree = 0x51, + + tag_id_range = 0x52, + + tag_table = 0x53, + tag_table_id = 0x54, + tag_table_val = 0x55, + tag_table_def = 0x56, + tag_table_node_type = 0x57, + tag_table_node_type_subst = 0x58, + tag_table_freevars = 0x59, + tag_table_tcache, + tag_table_param_bounds, + tag_table_inferred_modes, + tag_table_mutbl, + tag_table_copy, + tag_table_last_use, + tag_table_method_map, + tag_table_dict_map +} + +// djb's cdb hashes. +fn hash_node_id(&&node_id: int) -> uint { ret 177573u ^ (node_id as uint); } + +fn hash_path(&&s: str) -> uint { + let h = 5381u; + for ch: u8 in str::bytes(s) { h = (h << 5u) + h ^ (ch as uint); } + ret h; +} + diff --git a/src/rustc/metadata/creader.rs b/src/rustc/metadata/creader.rs new file mode 100644 index 00000000000..6222fc91e2c --- /dev/null +++ b/src/rustc/metadata/creader.rs @@ -0,0 +1,307 @@ +// Extracting metadata from crate files + +import driver::session; +import session::session; +import syntax::{ast, ast_util}; +import lib::llvm::{False, llvm, mk_object_file, mk_section_iter}; +import front::attr; +import syntax::visit; +import syntax::codemap::span; +import util::{filesearch}; +import std::{io, fs}; +import io::writer_util; +import std::map::{hashmap, new_int_hash}; +import syntax::print::pprust; +import common::*; + +export read_crates; +export list_file_metadata; + +// Traverses an AST, reading all the information about use'd crates and native +// libraries necessary for later resolving, typechecking, linking, etc. +fn read_crates(sess: session::session, crate: ast::crate) { + let e = @{sess: sess, + crate_cache: std::map::new_str_hash::<int>(), + mutable next_crate_num: 1}; + let v = + visit::mk_simple_visitor(@{visit_view_item: + bind visit_view_item(e, _), + visit_item: bind visit_item(e, _) + with *visit::default_simple_visitor()}); + visit::visit_crate(crate, (), v); +} + +type env = @{sess: session::session, + crate_cache: hashmap<str, int>, + mutable next_crate_num: ast::crate_num}; + +fn visit_view_item(e: env, i: @ast::view_item) { + alt i.node { + ast::view_item_use(ident, meta_items, id) { + let cnum = resolve_crate(e, ident, meta_items, i.span); + cstore::add_use_stmt_cnum(e.sess.cstore, id, cnum); + } + _ { } + } +} + +fn visit_item(e: env, i: @ast::item) { + alt i.node { + ast::item_native_mod(m) { + alt attr::native_abi(i.attrs) { + either::right(abi) { + if abi != ast::native_abi_cdecl && + abi != ast::native_abi_stdcall { ret; } + } + either::left(msg) { e.sess.span_fatal(i.span, msg); } + } + + let cstore = e.sess.cstore; + let native_name = + alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") { + some(nn) { + if nn == "" { + e.sess.span_fatal( + i.span, + "empty #[link_name] not allowed; use #[nolink]."); + } + nn + } + none { i.ident } + }; + let already_added = false; + if vec::len(attr::find_attrs_by_name(i.attrs, "nolink")) == 0u { + already_added = !cstore::add_used_library(cstore, native_name); + } + let link_args = attr::find_attrs_by_name(i.attrs, "link_args"); + if vec::len(link_args) > 0u && already_added { + e.sess.span_fatal(i.span, "library '" + native_name + + "' already added: can't specify link_args."); + } + for a: ast::attribute in link_args { + alt attr::get_meta_item_value_str(attr::attr_meta(a)) { + some(linkarg) { + cstore::add_used_link_args(cstore, linkarg); + } + none {/* fallthrough */ } + } + } + } + _ { } + } +} + +// A diagnostic function for dumping crate metadata to an output stream +fn list_file_metadata(sess: session::session, path: str, out: io::writer) { + alt get_metadata_section(sess, path) { + option::some(bytes) { decoder::list_crate_metadata(bytes, out); } + option::none { + out.write_str("Could not find metadata in " + path + ".\n"); + } + } +} + +fn metadata_matches(crate_data: @[u8], metas: [@ast::meta_item]) -> bool { + let attrs = decoder::get_crate_attributes(crate_data); + let linkage_metas = attr::find_linkage_metas(attrs); + + #debug("matching %u metadata requirements against %u items", + vec::len(metas), vec::len(linkage_metas)); + + #debug("crate metadata:"); + for have: @ast::meta_item in linkage_metas { + #debug(" %s", pprust::meta_item_to_str(*have)); + } + + for needed: @ast::meta_item in metas { + #debug("looking for %s", pprust::meta_item_to_str(*needed)); + if !attr::contains(linkage_metas, needed) { + #debug("missing %s", pprust::meta_item_to_str(*needed)); + ret false; + } + } + ret true; +} + +fn default_native_lib_naming(sess: session::session, static: bool) -> + {prefix: str, suffix: str} { + if static { ret {prefix: "lib", suffix: ".rlib"}; } + alt sess.targ_cfg.os { + session::os_win32 { ret {prefix: "", suffix: ".dll"}; } + session::os_macos { ret {prefix: "lib", suffix: ".dylib"}; } + session::os_linux { ret {prefix: "lib", suffix: ".so"}; } + session::os_freebsd { ret {prefix: "lib", suffix: ".so"}; } + } +} + +fn find_library_crate(sess: session::session, ident: ast::ident, + metas: [@ast::meta_item]) + -> option<{ident: str, data: @[u8]}> { + + attr::require_unique_names(sess, metas); + let metas = metas; + + let crate_name = + { + let name_items = attr::find_meta_items_by_name(metas, "name"); + alt vec::last(name_items) { + some(i) { + alt attr::get_meta_item_value_str(i) { + some(n) { n } + // FIXME: Probably want a warning here since the user + // is using the wrong type of meta item + _ { ident } + } + } + none { ident } + } + }; + + let nn = default_native_lib_naming(sess, sess.opts.static); + let x = + find_library_crate_aux(sess, nn, crate_name, + metas, sess.filesearch); + if x != none || sess.opts.static { ret x; } + let nn2 = default_native_lib_naming(sess, true); + ret find_library_crate_aux(sess, nn2, crate_name, metas, + sess.filesearch); +} + +fn find_library_crate_aux(sess: session::session, + nn: {prefix: str, suffix: str}, + crate_name: str, + metas: [@ast::meta_item], + filesearch: filesearch::filesearch) -> + option<{ident: str, data: @[u8]}> { + let prefix: str = nn.prefix + crate_name + "-"; + let suffix: str = nn.suffix; + + ret filesearch::search(filesearch, { |path| + #debug("inspecting file %s", path); + let f: str = fs::basename(path); + if !(str::starts_with(f, prefix) && str::ends_with(f, suffix)) { + #debug("skipping %s, doesn't look like %s*%s", path, prefix, + suffix); + option::none + } else { + #debug("%s is a candidate", path); + alt get_metadata_section(sess, path) { + option::some(cvec) { + if !metadata_matches(cvec, metas) { + #debug("skipping %s, metadata doesn't match", path); + option::none + } else { + #debug("found %s with matching metadata", path); + option::some({ident: path, data: cvec}) + } + } + _ { + #debug("could not load metadata for %s", path); + option::none + } + } + } + }); +} + +fn get_metadata_section(sess: session::session, + filename: str) -> option<@[u8]> unsafe { + let mb = str::as_buf(filename, {|buf| + llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf) + }); + if mb as int == 0 { ret option::none::<@[u8]>; } + let of = alt mk_object_file(mb) { + option::some(of) { of } + _ { ret option::none::<@[u8]>; } + }; + let si = mk_section_iter(of.llof); + while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { + let name_buf = llvm::LLVMGetSectionName(si.llsi); + let name = unsafe { str::from_cstr(name_buf) }; + if str::eq(name, sess.targ_cfg.target_strs.meta_sect_name) { + let cbuf = llvm::LLVMGetSectionContents(si.llsi); + let csz = llvm::LLVMGetSectionSize(si.llsi) as uint; + unsafe { + let cvbuf: *u8 = unsafe::reinterpret_cast(cbuf); + ret option::some::<@[u8]>(@vec::unsafe::from_buf(cvbuf, csz)); + } + } + llvm::LLVMMoveToNextSection(si.llsi); + } + ret option::none::<@[u8]>; +} + +fn load_library_crate(sess: session::session, span: span, ident: ast::ident, + metas: [@ast::meta_item]) + -> {ident: str, data: @[u8]} { + + + alt find_library_crate(sess, ident, metas) { + some(t) { ret t; } + none { + sess.span_fatal(span, #fmt["can't find crate for '%s'", ident]); + } + } +} + +fn resolve_crate(e: env, ident: ast::ident, metas: [@ast::meta_item], + span: span) -> ast::crate_num { + if !e.crate_cache.contains_key(ident) { + let cinfo = + load_library_crate(e.sess, span, ident, metas); + + let cfilename = cinfo.ident; + let cdata = cinfo.data; + + // Claim this crate number and cache it + let cnum = e.next_crate_num; + e.crate_cache.insert(ident, cnum); + e.next_crate_num += 1; + + // Now resolve the crates referenced by this crate + let cnum_map = resolve_crate_deps(e, cdata); + + let cmeta = @{name: ident, data: cdata, + cnum_map: cnum_map, cnum: cnum}; + + let cstore = e.sess.cstore; + cstore::set_crate_data(cstore, cnum, cmeta); + cstore::add_used_crate_file(cstore, cfilename); + ret cnum; + } else { ret e.crate_cache.get(ident); } +} + +// Go through the crate metadata and load any crates that it references +fn resolve_crate_deps(e: env, cdata: @[u8]) -> cstore::cnum_map { + #debug("resolving deps of external crate"); + // The map from crate numbers in the crate we're resolving to local crate + // numbers + let cnum_map = new_int_hash::<ast::crate_num>(); + for dep: decoder::crate_dep in decoder::get_crate_deps(cdata) { + let extrn_cnum = dep.cnum; + let cname = dep.ident; + #debug("resolving dep %s", cname); + if e.crate_cache.contains_key(cname) { + #debug("already have it"); + // We've already seen this crate + let local_cnum = e.crate_cache.get(cname); + cnum_map.insert(extrn_cnum, local_cnum); + } else { + #debug("need to load it"); + // This is a new one so we've got to load it + // FIXME: Need better error reporting than just a bogus span + let fake_span = ast_util::dummy_sp(); + let local_cnum = resolve_crate(e, cname, [], fake_span); + cnum_map.insert(extrn_cnum, local_cnum); + } + } + ret cnum_map; +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs new file mode 100644 index 00000000000..7dbf8dde75a --- /dev/null +++ b/src/rustc/metadata/csearch.rs @@ -0,0 +1,126 @@ +// Searching for information from the cstore + +import syntax::ast; +import syntax::ast_util; +import middle::{ty, ast_map}; +import option::{some, none}; +import driver::session; +import middle::trans::common::maps; + +export get_symbol; +export get_type_param_count; +export lookup_defs; +export lookup_method_purity; +export get_enum_variants; +export get_impls_for_mod; +export get_iface_methods; +export get_type; +export get_impl_iface; +export get_item_path; +export maybe_get_item_ast; + +fn get_symbol(cstore: cstore::cstore, def: ast::def_id) -> str { + let cdata = cstore::get_crate_data(cstore, def.crate).data; + ret decoder::get_symbol(cdata, def.node); +} + +fn get_type_param_count(cstore: cstore::cstore, def: ast::def_id) -> uint { + let cdata = cstore::get_crate_data(cstore, def.crate).data; + ret decoder::get_type_param_count(cdata, def.node); +} + +fn lookup_defs(cstore: cstore::cstore, cnum: ast::crate_num, + path: [ast::ident]) -> [ast::def] { + let result = []; + for (c, data, def) in resolve_path(cstore, cnum, path) { + result += [decoder::lookup_def(c, data, def)]; + } + ret result; +} + +fn lookup_method_purity(cstore: cstore::cstore, did: ast::def_id) + -> ast::purity { + let cdata = cstore::get_crate_data(cstore, did.crate).data; + alt check decoder::lookup_def(did.crate, cdata, did) { + ast::def_fn(_, p) { p } + } +} + +fn resolve_path(cstore: cstore::cstore, cnum: ast::crate_num, + path: [ast::ident]) -> + [(ast::crate_num, @[u8], ast::def_id)] { + let cm = cstore::get_crate_data(cstore, cnum); + #debug("resolve_path %s in crates[%d]:%s", + str::connect(path, "::"), cnum, cm.name); + let result = []; + for def in decoder::resolve_path(path, cm.data) { + if def.crate == ast::local_crate { + result += [(cnum, cm.data, def)]; + } else { + if cm.cnum_map.contains_key(def.crate) { + // This reexport is itself a reexport from anther crate + let next_cnum = cm.cnum_map.get(def.crate); + let next_cm_data = cstore::get_crate_data(cstore, next_cnum); + result += [(next_cnum, next_cm_data.data, def)]; + } + } + } + ret result; +} + +fn get_item_path(tcx: ty::ctxt, def: ast::def_id) -> ast_map::path { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + let path = decoder::get_item_path(cdata, def.node); + [ast_map::path_mod(cdata.name)] + path +} + +// Finds the AST for this item in the crate metadata, if any. If the item was +// not marked for inlining, then the AST will not be present and hence none +// will be returned. +fn maybe_get_item_ast(tcx: ty::ctxt, maps: maps, def: ast::def_id) + -> option<ast::inlined_item> { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::maybe_get_item_ast(cdata, tcx, maps, def.node) +} + +fn get_enum_variants(tcx: ty::ctxt, def: ast::def_id) -> [ty::variant_info] { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + ret decoder::get_enum_variants(cdata, def.node, tcx) +} + +fn get_impls_for_mod(cstore: cstore::cstore, def: ast::def_id, + name: option<ast::ident>) + -> @[@middle::resolve::_impl] { + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::get_impls_for_mod(cdata, def.node, name) +} + +fn get_iface_methods(tcx: ty::ctxt, def: ast::def_id) -> @[ty::method] { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::get_iface_methods(cdata, def.node, tcx) +} + +fn get_type(tcx: ty::ctxt, def: ast::def_id) -> ty::ty_param_bounds_and_ty { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::get_type(cdata, def.node, tcx) +} + +fn get_impl_iface(tcx: ty::ctxt, def: ast::def_id) + -> option<ty::t> { + let cstore = tcx.sess.cstore; + let cdata = cstore::get_crate_data(cstore, def.crate); + decoder::get_impl_iface(cdata, def.node, tcx) +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/metadata/cstore.rs b/src/rustc/metadata/cstore.rs new file mode 100644 index 00000000000..e9dd1a010c5 --- /dev/null +++ b/src/rustc/metadata/cstore.rs @@ -0,0 +1,175 @@ +// The crate store - a central repo for information collected about external +// crates and libraries + +import std::map; +import syntax::ast; +import util::common::*; + +export cstore::{}; +export cnum_map; +export crate_metadata; +export mk_cstore; +export get_crate_data; +export set_crate_data; +export have_crate_data; +export iter_crate_data; +export add_used_crate_file; +export get_used_crate_files; +export add_used_library; +export get_used_libraries; +export add_used_link_args; +export get_used_link_args; +export add_use_stmt_cnum; +export find_use_stmt_cnum; +export get_dep_hashes; +export get_path; + + +// A map from external crate numbers (as decoded from some crate file) to +// local crate numbers (as generated during this session). Each external +// crate may refer to types in other external crates, and each has their +// own crate numbers. +type cnum_map = map::hashmap<ast::crate_num, ast::crate_num>; + +// Multiple items may have the same def_id in crate metadata. They may be +// renamed imports or reexports. This map keeps the "real" module path +// and def_id. +type mod_path_map = map::hashmap<ast::def_id, str>; + +type crate_metadata = @{name: str, + data: @[u8], + cnum_map: cnum_map, + cnum: ast::crate_num}; + +// This is a bit of an experiment at encapsulating the data in cstore. By +// keeping all the data in a non-exported enum variant, it's impossible for +// other modules to access the cstore's private data. This could also be +// achieved with an obj, but at the expense of a vtable. Not sure if this is a +// good pattern or not. +enum cstore { private(cstore_private), } + +type cstore_private = + @{metas: map::hashmap<ast::crate_num, crate_metadata>, + use_crate_map: use_crate_map, + mod_path_map: mod_path_map, + mutable used_crate_files: [str], + mutable used_libraries: [str], + mutable used_link_args: [str]}; + +// Map from node_id's of local use statements to crate numbers +type use_crate_map = map::hashmap<ast::node_id, ast::crate_num>; + +// Internal method to retrieve the data from the cstore +fn p(cstore: cstore) -> cstore_private { alt cstore { private(p) { p } } } + +fn mk_cstore() -> cstore { + let meta_cache = map::new_int_hash::<crate_metadata>(); + let crate_map = map::new_int_hash::<ast::crate_num>(); + let mod_path_map = new_def_hash(); + ret private(@{metas: meta_cache, + use_crate_map: crate_map, + mod_path_map: mod_path_map, + mutable used_crate_files: [], + mutable used_libraries: [], + mutable used_link_args: []}); +} + +fn get_crate_data(cstore: cstore, cnum: ast::crate_num) -> crate_metadata { + ret p(cstore).metas.get(cnum); +} + +fn set_crate_data(cstore: cstore, cnum: ast::crate_num, + data: crate_metadata) { + p(cstore).metas.insert(cnum, data); + vec::iter(decoder::get_crate_module_paths(data.data)) {|dp| + let (did, path) = dp; + let d = {crate: cnum, node: did.node}; + p(cstore).mod_path_map.insert(d, path); + } +} + +fn have_crate_data(cstore: cstore, cnum: ast::crate_num) -> bool { + ret p(cstore).metas.contains_key(cnum); +} + +fn iter_crate_data(cstore: cstore, i: fn(ast::crate_num, crate_metadata)) { + p(cstore).metas.items {|k,v| i(k, v);}; +} + +fn add_used_crate_file(cstore: cstore, lib: str) { + if !vec::contains(p(cstore).used_crate_files, lib) { + p(cstore).used_crate_files += [lib]; + } +} + +fn get_used_crate_files(cstore: cstore) -> [str] { + ret p(cstore).used_crate_files; +} + +fn add_used_library(cstore: cstore, lib: str) -> bool { + assert lib != ""; + + if vec::contains(p(cstore).used_libraries, lib) { ret false; } + p(cstore).used_libraries += [lib]; + ret true; +} + +fn get_used_libraries(cstore: cstore) -> [str] { + ret p(cstore).used_libraries; +} + +fn add_used_link_args(cstore: cstore, args: str) { + p(cstore).used_link_args += str::split_char(args, ' '); +} + +fn get_used_link_args(cstore: cstore) -> [str] { + ret p(cstore).used_link_args; +} + +fn add_use_stmt_cnum(cstore: cstore, use_id: ast::node_id, + cnum: ast::crate_num) { + p(cstore).use_crate_map.insert(use_id, cnum); +} + +fn find_use_stmt_cnum(cstore: cstore, + use_id: ast::node_id) -> option<ast::crate_num> { + p(cstore).use_crate_map.find(use_id) +} + +// returns hashes of crates directly used by this crate. Hashes are +// sorted by crate name. +fn get_dep_hashes(cstore: cstore) -> [str] { + type crate_hash = {name: str, hash: str}; + let result = []; + + p(cstore).use_crate_map.values {|cnum| + let cdata = cstore::get_crate_data(cstore, cnum); + let hash = decoder::get_crate_hash(cdata.data); + #debug("Add hash[%s]: %s", cdata.name, hash); + result += [{name: cdata.name, hash: hash}]; + }; + fn lteq(a: crate_hash, b: crate_hash) -> bool { + ret a.name <= b.name; + } + let sorted = std::sort::merge_sort(lteq, result); + #debug("sorted:"); + for x in sorted { + #debug(" hash[%s]: %s", x.name, x.hash); + } + fn mapper(ch: crate_hash) -> str { ret ch.hash; } + ret vec::map(sorted, mapper); +} + +fn get_path(cstore: cstore, d: ast::def_id) -> [str] { + alt p(cstore).mod_path_map.find(d) { + option::some(ds) { str::split_str(ds, "::") } + option::none { [] } + } +} +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs new file mode 100644 index 00000000000..196789ee58c --- /dev/null +++ b/src/rustc/metadata/decoder.rs @@ -0,0 +1,577 @@ +// Decoding metadata from a single crate's metadata + +import std::{ebml, map, io}; +import io::writer_util; +import syntax::{ast, ast_util}; +import driver::session::session; +import front::attr; +import middle::ty; +import middle::ast_map; +import common::*; +import tydecode::{parse_ty_data, parse_def_id, parse_bounds_data}; +import syntax::print::pprust; +import cmd=cstore::crate_metadata; +import middle::trans::common::maps; + +export get_symbol; +export get_enum_variants; +export get_type; +export get_type_param_count; +export get_impl_iface; +export lookup_def; +export lookup_item_name; +export get_impl_iface; +export resolve_path; +export get_crate_attributes; +export list_crate_metadata; +export crate_dep; +export get_crate_deps; +export get_crate_hash; +export get_impls_for_mod; +export get_iface_methods; +export get_crate_module_paths; +export get_item_path; +export maybe_get_item_ast; + +// Used internally by astencode: +export translate_def_id; + +// A function that takes a def_id relative to the crate being searched and +// returns a def_id relative to the compilation environment, i.e. if we hit a +// def_id for an item defined in another crate, somebody needs to figure out +// what crate that's in and give us a def_id that makes sense for the current +// build. + +fn lookup_hash(d: ebml::doc, eq_fn: fn@([u8]) -> bool, hash: uint) -> + [ebml::doc] { + let index = ebml::get_doc(d, tag_index); + let table = ebml::get_doc(index, tag_index_table); + let hash_pos = table.start + hash % 256u * 4u; + let pos = io::u64_from_be_bytes(*d.data, hash_pos, 4u) as uint; + let {tag:_, doc:bucket} = ebml::doc_at(d.data, pos); + // Awkward logic because we can't ret from foreach yet + + let result: [ebml::doc] = []; + let belt = tag_index_buckets_bucket_elt; + ebml::tagged_docs(bucket, belt) {|elt| + let pos = io::u64_from_be_bytes(*elt.data, elt.start, 4u) as uint; + if eq_fn(vec::slice::<u8>(*elt.data, elt.start + 4u, elt.end)) { + result += [ebml::doc_at(d.data, pos).doc]; + } + }; + ret result; +} + +fn maybe_find_item(item_id: int, items: ebml::doc) -> option<ebml::doc> { + fn eq_item(bytes: [u8], item_id: int) -> bool { + ret io::u64_from_be_bytes(bytes, 0u, 4u) as int == item_id; + } + let eqer = bind eq_item(_, item_id); + let found = lookup_hash(items, eqer, hash_node_id(item_id)); + if vec::len(found) == 0u { + ret option::none::<ebml::doc>; + } else { ret option::some::<ebml::doc>(found[0]); } +} + +fn find_item(item_id: int, items: ebml::doc) -> ebml::doc { + ret option::get(maybe_find_item(item_id, items)); +} + +// Looks up an item in the given metadata and returns an ebml doc pointing +// to the item data. +fn lookup_item(item_id: int, data: @[u8]) -> ebml::doc { + let items = ebml::get_doc(ebml::new_doc(data), tag_items); + ret find_item(item_id, items); +} + +fn item_family(item: ebml::doc) -> char { + let fam = ebml::get_doc(item, tag_items_data_item_family); + ebml::doc_as_u8(fam) as char +} + +fn item_symbol(item: ebml::doc) -> str { + let sym = ebml::get_doc(item, tag_items_data_item_symbol); + ret str::from_bytes(ebml::doc_data(sym)); +} + +fn variant_enum_id(d: ebml::doc) -> ast::def_id { + let tagdoc = ebml::get_doc(d, tag_items_data_item_enum_id); + ret parse_def_id(ebml::doc_data(tagdoc)); +} + +fn variant_disr_val(d: ebml::doc) -> option<int> { + option::chain(ebml::maybe_get_doc(d, tag_disr_val)) {|val_doc| + int::parse_buf(ebml::doc_data(val_doc), 10u) + } +} + +fn doc_type(doc: ebml::doc, tcx: ty::ctxt, cdata: cmd) -> ty::t { + let tp = ebml::get_doc(doc, tag_items_data_item_type); + parse_ty_data(tp.data, cdata.cnum, tp.start, tcx, {|did| + translate_def_id(cdata, did) + }) +} + +fn item_type(item_id: ast::def_id, item: ebml::doc, + tcx: ty::ctxt, cdata: cmd) -> ty::t { + let t = doc_type(item, tcx, cdata); + if family_names_type(item_family(item)) { + ty::mk_with_id(tcx, t, item_id) + } else { t } +} + +fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) + -> option<ty::t> { + let result = none; + ebml::tagged_docs(item, tag_impl_iface) {|ity| + let t = parse_ty_data(ity.data, cdata.cnum, ity.start, tcx, {|did| + translate_def_id(cdata, did) + }); + result = some(t); + } + result +} + +fn item_ty_param_bounds(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) + -> @[ty::param_bounds] { + let bounds = []; + ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p| + let bd = parse_bounds_data(p.data, p.start, cdata.cnum, tcx, {|did| + translate_def_id(cdata, did) + }); + bounds += [bd]; + } + @bounds +} + +fn item_ty_param_count(item: ebml::doc) -> uint { + let n = 0u; + ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds, + {|_p| n += 1u; }); + n +} + +fn enum_variant_ids(item: ebml::doc, cdata: cmd) -> [ast::def_id] { + let ids: [ast::def_id] = []; + let v = tag_items_data_item_variant; + ebml::tagged_docs(item, v) {|p| + let ext = parse_def_id(ebml::doc_data(p)); + ids += [{crate: cdata.cnum, node: ext.node}]; + }; + ret ids; +} + +// Given a path and serialized crate metadata, returns the ID of the +// definition the path refers to. +fn resolve_path(path: [ast::ident], data: @[u8]) -> [ast::def_id] { + fn eq_item(data: [u8], s: str) -> bool { + ret str::eq(str::from_bytes(data), s); + } + let s = str::connect(path, "::"); + let md = ebml::new_doc(data); + let paths = ebml::get_doc(md, tag_paths); + let eqer = bind eq_item(_, s); + let result: [ast::def_id] = []; + for doc: ebml::doc in lookup_hash(paths, eqer, hash_path(s)) { + let did_doc = ebml::get_doc(doc, tag_def_id); + result += [parse_def_id(ebml::doc_data(did_doc))]; + } + ret result; +} + +fn item_path(item_doc: ebml::doc) -> ast_map::path { + let path_doc = ebml::get_doc(item_doc, tag_path); + + let len_doc = ebml::get_doc(path_doc, tag_path_len); + let len = ebml::doc_as_u32(len_doc) as uint; + + let result = []; + vec::reserve(result, len); + + ebml::docs(path_doc) {|tag, elt_doc| + if tag == tag_path_elt_mod { + let str = ebml::doc_as_str(elt_doc); + result += [ast_map::path_mod(str)]; + } else if tag == tag_path_elt_name { + let str = ebml::doc_as_str(elt_doc); + result += [ast_map::path_name(str)]; + } else { + // ignore tag_path_len element + } + } + + ret result; +} + +fn item_name(item: ebml::doc) -> ast::ident { + let name = ebml::get_doc(item, tag_paths_data_name); + str::from_bytes(ebml::doc_data(name)) +} + +fn lookup_item_name(data: @[u8], id: ast::node_id) -> ast::ident { + item_name(lookup_item(id, data)) +} + +fn lookup_def(cnum: ast::crate_num, data: @[u8], did_: ast::def_id) -> + ast::def { + let item = lookup_item(did_.node, data); + let fam_ch = item_family(item); + let did = {crate: cnum, node: did_.node}; + // We treat references to enums as references to types. + alt check fam_ch { + 'c' { ast::def_const(did) } + 'u' { ast::def_fn(did, ast::unsafe_fn) } + 'f' { ast::def_fn(did, ast::impure_fn) } + 'p' { ast::def_fn(did, ast::pure_fn) } + 'y' { ast::def_ty(did) } + 't' { ast::def_ty(did) } + 'm' { ast::def_mod(did) } + 'n' { ast::def_native_mod(did) } + 'v' { + let tid = variant_enum_id(item); + tid = {crate: cnum, node: tid.node}; + ast::def_variant(tid, did) + } + 'I' { ast::def_ty(did) } + } +} + +fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) + -> ty::ty_param_bounds_and_ty { + let item = lookup_item(id, cdata.data); + let t = item_type({crate: cdata.cnum, node: id}, item, tcx, cdata); + let tp_bounds = if family_has_type_params(item_family(item)) { + item_ty_param_bounds(item, tcx, cdata) + } else { @[] }; + ret {bounds: tp_bounds, ty: t}; +} + +fn get_type_param_count(data: @[u8], id: ast::node_id) -> uint { + item_ty_param_count(lookup_item(id, data)) +} + +fn get_impl_iface(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) + -> option<ty::t> { + item_impl_iface(lookup_item(id, cdata.data), tcx, cdata) +} + +fn get_symbol(data: @[u8], id: ast::node_id) -> str { + ret item_symbol(lookup_item(id, data)); +} + +fn get_item_path(cdata: cmd, id: ast::node_id) -> ast_map::path { + item_path(lookup_item(id, cdata.data)) +} + +fn maybe_get_item_ast(cdata: cmd, tcx: ty::ctxt, maps: maps, + id: ast::node_id) -> option<ast::inlined_item> { + let item_doc = lookup_item(id, cdata.data); + let path = vec::init(item_path(item_doc)); + astencode::decode_inlined_item(cdata, tcx, maps, path, item_doc) +} + +fn get_enum_variants(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) + -> [ty::variant_info] { + let data = cdata.data; + let items = ebml::get_doc(ebml::new_doc(data), tag_items); + let item = find_item(id, items); + let infos: [ty::variant_info] = []; + let variant_ids = enum_variant_ids(item, cdata); + let disr_val = 0; + for did: ast::def_id in variant_ids { + let item = find_item(did.node, items); + let ctor_ty = item_type({crate: cdata.cnum, node: id}, item, + tcx, cdata); + let name = item_name(item); + let arg_tys: [ty::t] = []; + alt ty::get(ctor_ty).struct { + ty::ty_fn(f) { + for a: ty::arg in f.inputs { arg_tys += [a.ty]; } + } + _ { /* Nullary enum variant. */ } + } + alt variant_disr_val(item) { + some(val) { disr_val = val; } + _ { /* empty */ } + } + infos += [@{args: arg_tys, ctor_ty: ctor_ty, name: name, + id: did, disr_val: disr_val}]; + disr_val += 1; + } + ret infos; +} + +fn item_impl_methods(cdata: cmd, item: ebml::doc, base_tps: uint) + -> [@middle::resolve::method_info] { + let rslt = []; + ebml::tagged_docs(item, tag_item_method) {|doc| + let m_did = parse_def_id(ebml::doc_data(doc)); + let mth_item = lookup_item(m_did.node, cdata.data); + rslt += [@{did: translate_def_id(cdata, m_did), + n_tps: item_ty_param_count(mth_item) - base_tps, + ident: item_name(mth_item)}]; + } + rslt +} + +fn get_impls_for_mod(cdata: cmd, m_id: ast::node_id, + name: option<ast::ident>) + -> @[@middle::resolve::_impl] { + let data = cdata.data; + let mod_item = lookup_item(m_id, data), result = []; + ebml::tagged_docs(mod_item, tag_mod_impl) {|doc| + let did = translate_def_id(cdata, parse_def_id(ebml::doc_data(doc))); + let item = lookup_item(did.node, data), nm = item_name(item); + if alt name { some(n) { n == nm } none { true } } { + let base_tps = item_ty_param_count(item); + result += [@{did: did, ident: nm, + methods: item_impl_methods(cdata, item, base_tps)}]; + } + } + @result +} + +fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) + -> @[ty::method] { + let data = cdata.data; + let item = lookup_item(id, data), result = []; + ebml::tagged_docs(item, tag_item_method) {|mth| + let bounds = item_ty_param_bounds(mth, tcx, cdata); + let name = item_name(mth); + let ty = doc_type(mth, tcx, cdata); + let fty = alt ty::get(ty).struct { ty::ty_fn(f) { f } + _ { tcx.sess.bug("get_iface_methods: id has non-function type"); + } }; + result += [{ident: name, tps: bounds, fty: fty, + purity: alt check item_family(mth) { + 'u' { ast::unsafe_fn } + 'f' { ast::impure_fn } + 'p' { ast::pure_fn } + }}]; + } + @result +} + +fn family_has_type_params(fam_ch: char) -> bool { + alt check fam_ch { + 'c' | 'T' | 'm' | 'n' { false } + 'f' | 'u' | 'p' | 'F' | 'U' | 'P' | 'y' | 't' | 'v' | 'i' | 'I' { true } + } +} + +fn family_names_type(fam_ch: char) -> bool { + alt fam_ch { 'y' | 't' | 'I' { true } _ { false } } +} + +fn read_path(d: ebml::doc) -> {path: str, pos: uint} { + let desc = ebml::doc_data(d); + let pos = io::u64_from_be_bytes(desc, 0u, 4u) as uint; + let pathbytes = vec::slice::<u8>(desc, 4u, vec::len::<u8>(desc)); + let path = str::from_bytes(pathbytes); + ret {path: path, pos: pos}; +} + +fn describe_def(items: ebml::doc, id: ast::def_id) -> str { + if id.crate != ast::local_crate { ret "external"; } + ret item_family_to_str(item_family(find_item(id.node, items))); +} + +fn item_family_to_str(fam: char) -> str { + alt check fam { + 'c' { ret "const"; } + 'f' { ret "fn"; } + 'u' { ret "unsafe fn"; } + 'p' { ret "pure fn"; } + 'F' { ret "native fn"; } + 'U' { ret "unsafe native fn"; } + 'P' { ret "pure native fn"; } + 'y' { ret "type"; } + 'T' { ret "native type"; } + 't' { ret "type"; } + 'm' { ret "mod"; } + 'n' { ret "native mod"; } + 'v' { ret "enum"; } + 'i' { ret "impl"; } + 'I' { ret "iface"; } + } +} + +fn get_meta_items(md: ebml::doc) -> [@ast::meta_item] { + let items: [@ast::meta_item] = []; + ebml::tagged_docs(md, tag_meta_item_word) {|meta_item_doc| + let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); + let n = str::from_bytes(ebml::doc_data(nd)); + items += [attr::mk_word_item(n)]; + }; + ebml::tagged_docs(md, tag_meta_item_name_value) {|meta_item_doc| + let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); + let vd = ebml::get_doc(meta_item_doc, tag_meta_item_value); + let n = str::from_bytes(ebml::doc_data(nd)); + let v = str::from_bytes(ebml::doc_data(vd)); + // FIXME (#611): Should be able to decode meta_name_value variants, + // but currently they can't be encoded + items += [attr::mk_name_value_item_str(n, v)]; + }; + ebml::tagged_docs(md, tag_meta_item_list) {|meta_item_doc| + let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); + let n = str::from_bytes(ebml::doc_data(nd)); + let subitems = get_meta_items(meta_item_doc); + items += [attr::mk_list_item(n, subitems)]; + }; + ret items; +} + +fn get_attributes(md: ebml::doc) -> [ast::attribute] { + let attrs: [ast::attribute] = []; + alt ebml::maybe_get_doc(md, tag_attributes) { + option::some(attrs_d) { + ebml::tagged_docs(attrs_d, tag_attribute) {|attr_doc| + let meta_items = get_meta_items(attr_doc); + // Currently it's only possible to have a single meta item on + // an attribute + assert (vec::len(meta_items) == 1u); + let meta_item = meta_items[0]; + attrs += + [{node: {style: ast::attr_outer, value: *meta_item}, + span: ast_util::dummy_sp()}]; + }; + } + option::none { } + } + ret attrs; +} + +fn list_meta_items(meta_items: ebml::doc, out: io::writer) { + for mi: @ast::meta_item in get_meta_items(meta_items) { + out.write_str(#fmt["%s\n", pprust::meta_item_to_str(*mi)]); + } +} + +fn list_crate_attributes(md: ebml::doc, hash: str, out: io::writer) { + out.write_str(#fmt("=Crate Attributes (%s)=\n", hash)); + + for attr: ast::attribute in get_attributes(md) { + out.write_str(#fmt["%s\n", pprust::attribute_to_str(attr)]); + } + + out.write_str("\n\n"); +} + +fn get_crate_attributes(data: @[u8]) -> [ast::attribute] { + ret get_attributes(ebml::new_doc(data)); +} + +type crate_dep = {cnum: ast::crate_num, ident: str}; + +fn get_crate_deps(data: @[u8]) -> [crate_dep] { + let deps: [crate_dep] = []; + let cratedoc = ebml::new_doc(data); + let depsdoc = ebml::get_doc(cratedoc, tag_crate_deps); + let crate_num = 1; + ebml::tagged_docs(depsdoc, tag_crate_dep) {|depdoc| + let depname = str::from_bytes(ebml::doc_data(depdoc)); + deps += [{cnum: crate_num, ident: depname}]; + crate_num += 1; + }; + ret deps; +} + +fn list_crate_deps(data: @[u8], out: io::writer) { + out.write_str("=External Dependencies=\n"); + + for dep: crate_dep in get_crate_deps(data) { + out.write_str(#fmt["%d %s\n", dep.cnum, dep.ident]); + } + + out.write_str("\n"); +} + +fn get_crate_hash(data: @[u8]) -> str { + let cratedoc = ebml::new_doc(data); + let hashdoc = ebml::get_doc(cratedoc, tag_crate_hash); + ret str::from_bytes(ebml::doc_data(hashdoc)); +} + +fn list_crate_items(bytes: @[u8], md: ebml::doc, out: io::writer) { + out.write_str("=Items=\n"); + let items = ebml::get_doc(md, tag_items); + iter_crate_items(bytes) {|path, did| + out.write_str(#fmt["%s (%s)\n", path, describe_def(items, did)]); + } + out.write_str("\n"); +} + +fn iter_crate_items(bytes: @[u8], proc: fn(str, ast::def_id)) { + let md = ebml::new_doc(bytes); + let paths = ebml::get_doc(md, tag_paths); + let index = ebml::get_doc(paths, tag_index); + let bs = ebml::get_doc(index, tag_index_buckets); + ebml::tagged_docs(bs, tag_index_buckets_bucket) {|bucket| + let et = tag_index_buckets_bucket_elt; + ebml::tagged_docs(bucket, et) {|elt| + let data = read_path(elt); + let {tag:_, doc:def} = ebml::doc_at(bytes, data.pos); + let did_doc = ebml::get_doc(def, tag_def_id); + let did = parse_def_id(ebml::doc_data(did_doc)); + proc(data.path, did); + }; + }; +} + +fn get_crate_module_paths(bytes: @[u8]) -> [(ast::def_id, str)] { + fn mod_of_path(p: str) -> str { + str::connect(vec::init(str::split_str(p, "::")), "::") + } + + // find all module (path, def_ids), which are not + // fowarded path due to renamed import or reexport + let res = []; + let mods = map::new_str_hash(); + iter_crate_items(bytes) {|path, did| + let m = mod_of_path(path); + if str::is_not_empty(m) { + // if m has a sub-item, it must be a module + mods.insert(m, true); + } + // Collect everything by now. There might be multiple + // paths pointing to the same did. Those will be + // unified later by using the mods map + res += [(did, path)]; + } + ret vec::filter(res) {|x| + let (_, xp) = x; + mods.contains_key(xp) + } +} + +fn list_crate_metadata(bytes: @[u8], out: io::writer) { + let hash = get_crate_hash(bytes); + let md = ebml::new_doc(bytes); + list_crate_attributes(md, hash, out); + list_crate_deps(bytes, out); + list_crate_items(bytes, md, out); +} + +// Translates a def_id from an external crate to a def_id for the current +// compilation environment. We use this when trying to load types from +// external crates - if those types further refer to types in other crates +// then we must translate the crate number from that encoded in the external +// crate to the correct local crate number. +fn translate_def_id(cdata: cmd, did: ast::def_id) -> ast::def_id { + if did.crate == ast::local_crate { + ret {crate: cdata.cnum, node: did.node}; + } + + alt cdata.cnum_map.find(did.crate) { + option::some(n) { ret {crate: n, node: did.node}; } + option::none { fail "didn't find a crate in the cnum_map"; } + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs new file mode 100644 index 00000000000..2da07511628 --- /dev/null +++ b/src/rustc/metadata/encoder.rs @@ -0,0 +1,766 @@ +// Metadata encoding + +import std::{io, ebml, map, list}; +import io::writer_util; +import ebml::writer; +import syntax::ast::*; +import syntax::print::pprust; +import syntax::ast_util; +import syntax::ast_util::local_def; +import common::*; +import middle::trans::common::crate_ctxt; +import middle::ty; +import middle::ty::node_id_to_type; +import middle::ast_map; +import front::attr; +import driver::session::session; +import std::serialization::serializer; + +export encode_metadata; +export encoded_ty; + +// used by astencode: +export def_to_str; +export encode_ctxt; +export write_type; +export encode_def_id; + +type abbrev_map = map::hashmap<ty::t, tyencode::ty_abbrev>; + +type encode_ctxt = {ccx: crate_ctxt, type_abbrevs: abbrev_map}; + +fn should_inline(_path: ast_map::path, attrs: [attribute]) -> bool { + attr::attrs_contains_name(attrs, "inline") +} + +// Path table encoding +fn encode_name(ebml_w: ebml::writer, name: str) { + ebml_w.wr_tagged_str(tag_paths_data_name, name); +} + +fn encode_def_id(ebml_w: ebml::writer, id: def_id) { + ebml_w.wr_tagged_str(tag_def_id, def_to_str(id)); +} + +fn encode_named_def_id(ebml_w: ebml::writer, name: str, id: def_id) { + ebml_w.wr_tag(tag_paths_data_item) {|| + encode_name(ebml_w, name); + encode_def_id(ebml_w, id); + } +} + +type entry<T> = {val: T, pos: uint}; + +fn encode_enum_variant_paths(ebml_w: ebml::writer, variants: [variant], + path: [str], &index: [entry<str>]) { + for variant: variant in variants { + add_to_index(ebml_w, path, index, variant.node.name); + ebml_w.wr_tag(tag_paths_data_item) {|| + encode_name(ebml_w, variant.node.name); + encode_def_id(ebml_w, local_def(variant.node.id)); + } + } +} + +fn add_to_index(ebml_w: ebml::writer, path: [str], &index: [entry<str>], + name: str) { + let full_path = path + [name]; + index += + [{val: str::connect(full_path, "::"), pos: ebml_w.writer.tell()}]; +} + +fn encode_native_module_item_paths(ebml_w: ebml::writer, nmod: native_mod, + path: [str], &index: [entry<str>]) { + for nitem: @native_item in nmod.items { + add_to_index(ebml_w, path, index, nitem.ident); + encode_named_def_id(ebml_w, nitem.ident, local_def(nitem.id)); + } +} + +fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str], + &index: [entry<str>]) { + // FIXME factor out add_to_index/start/encode_name/encode_def_id/end ops + for it: @item in module.items { + if !ast_util::is_exported(it.ident, module) { cont; } + alt it.node { + item_const(_, _) { + add_to_index(ebml_w, path, index, it.ident); + encode_named_def_id(ebml_w, it.ident, local_def(it.id)); + } + item_fn(_, tps, _) { + add_to_index(ebml_w, path, index, it.ident); + encode_named_def_id(ebml_w, it.ident, local_def(it.id)); + } + item_mod(_mod) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_mod); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + encode_module_item_paths(ebml_w, _mod, path + [it.ident], index); + ebml_w.end_tag(); + } + item_native_mod(nmod) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_mod); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + encode_native_module_item_paths(ebml_w, nmod, path + [it.ident], + index); + ebml_w.end_tag(); + } + item_ty(_, tps) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + ebml_w.end_tag(); + } + item_res(_, tps, _, _, ctor_id) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(ctor_id)); + ebml_w.end_tag(); + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + ebml_w.end_tag(); + } + item_class(_,_,_,_,_) { + fail "encode: implement item_class"; + } + item_enum(variants, tps) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + ebml_w.end_tag(); + encode_enum_variant_paths(ebml_w, variants, path, index); + } + item_iface(_, _) { + add_to_index(ebml_w, path, index, it.ident); + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, it.ident); + encode_def_id(ebml_w, local_def(it.id)); + ebml_w.end_tag(); + } + item_impl(_, _, _, _) {} + } + } +} + +fn encode_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, crate: @crate) + -> [entry<str>] { + let index: [entry<str>] = []; + let path: [str] = []; + ebml_w.start_tag(tag_paths); + encode_module_item_paths(ebml_w, crate.node.module, path, index); + encode_reexport_paths(ebml_w, ecx, index); + ebml_w.end_tag(); + ret index; +} + +fn encode_reexport_paths(ebml_w: ebml::writer, + ecx: @encode_ctxt, &index: [entry<str>]) { + ecx.ccx.exp_map.items {|path, defs| + for def in *defs { + index += [{val: path, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_paths_data_item); + encode_name(ebml_w, path); + encode_def_id(ebml_w, ast_util::def_id_of_def(def)); + ebml_w.end_tag(); + } + } +} + + +// Item info table encoding +fn encode_family(ebml_w: ebml::writer, c: char) { + ebml_w.start_tag(tag_items_data_item_family); + ebml_w.writer.write([c as u8]); + ebml_w.end_tag(); +} + +fn def_to_str(did: def_id) -> str { ret #fmt["%d:%d", did.crate, did.node]; } + +fn encode_type_param_bounds(ebml_w: ebml::writer, ecx: @encode_ctxt, + params: [ty_param]) { + let ty_str_ctxt = @{ds: def_to_str, + tcx: ecx.ccx.tcx, + abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)}; + for param in params { + ebml_w.start_tag(tag_items_data_item_ty_param_bounds); + let bs = ecx.ccx.tcx.ty_param_bounds.get(param.id); + tyencode::enc_bounds(ebml_w.writer, ty_str_ctxt, bs); + ebml_w.end_tag(); + } +} + +fn encode_variant_id(ebml_w: ebml::writer, vid: def_id) { + ebml_w.start_tag(tag_items_data_item_variant); + ebml_w.writer.write(str::bytes(def_to_str(vid))); + ebml_w.end_tag(); +} + +fn write_type(ecx: @encode_ctxt, ebml_w: ebml::writer, typ: ty::t) { + let ty_str_ctxt = + @{ds: def_to_str, + tcx: ecx.ccx.tcx, + abbrevs: tyencode::ac_use_abbrevs(ecx.type_abbrevs)}; + tyencode::enc_ty(ebml_w.writer, ty_str_ctxt, typ); +} + +fn encode_type(ecx: @encode_ctxt, ebml_w: ebml::writer, typ: ty::t) { + ebml_w.start_tag(tag_items_data_item_type); + write_type(ecx, ebml_w, typ); + ebml_w.end_tag(); +} + +fn encode_symbol(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id) { + ebml_w.start_tag(tag_items_data_item_symbol); + ebml_w.writer.write(str::bytes(ecx.ccx.item_symbols.get(id))); + ebml_w.end_tag(); +} + +fn encode_discriminant(ecx: @encode_ctxt, ebml_w: ebml::writer, id: node_id) { + ebml_w.start_tag(tag_items_data_item_symbol); + ebml_w.writer.write(str::bytes(ecx.ccx.discrim_symbols.get(id))); + ebml_w.end_tag(); +} + +fn encode_disr_val(_ecx: @encode_ctxt, ebml_w: ebml::writer, disr_val: int) { + ebml_w.start_tag(tag_disr_val); + ebml_w.writer.write(str::bytes(int::to_str(disr_val,10u))); + ebml_w.end_tag(); +} + +fn encode_enum_id(ebml_w: ebml::writer, id: def_id) { + ebml_w.start_tag(tag_items_data_item_enum_id); + ebml_w.writer.write(str::bytes(def_to_str(id))); + ebml_w.end_tag(); +} + +fn encode_enum_variant_info(ecx: @encode_ctxt, ebml_w: ebml::writer, + id: node_id, variants: [variant], + path: ast_map::path, &index: [entry<int>], + ty_params: [ty_param]) { + let disr_val = 0; + let i = 0; + let vi = ty::enum_variants(ecx.ccx.tcx, {crate: local_crate, node: id}); + for variant: variant in variants { + index += [{val: variant.node.id, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(variant.node.id)); + encode_family(ebml_w, 'v'); + encode_name(ebml_w, variant.node.name); + encode_enum_id(ebml_w, local_def(id)); + encode_type(ecx, ebml_w, + node_id_to_type(ecx.ccx.tcx, variant.node.id)); + if vec::len::<variant_arg>(variant.node.args) > 0u { + encode_symbol(ecx, ebml_w, variant.node.id); + } + encode_discriminant(ecx, ebml_w, variant.node.id); + if vi[i].disr_val != disr_val { + encode_disr_val(ecx, ebml_w, vi[i].disr_val); + disr_val = vi[i].disr_val; + } + encode_type_param_bounds(ebml_w, ecx, ty_params); + encode_path(ebml_w, path, ast_map::path_name(variant.node.name)); + ebml_w.end_tag(); + disr_val += 1; + i += 1; + } +} + +fn encode_path(ebml_w: ebml::writer, + path: ast_map::path, + name: ast_map::path_elt) { + fn encode_path_elt(ebml_w: ebml::writer, elt: ast_map::path_elt) { + let (tag, name) = alt elt { + ast_map::path_mod(name) { (tag_path_elt_mod, name) } + ast_map::path_name(name) { (tag_path_elt_name, name) } + }; + + ebml_w.wr_tagged_str(tag, name); + } + + ebml_w.wr_tag(tag_path) {|| + ebml_w.wr_tagged_u32(tag_path_len, (vec::len(path) + 1u) as u32); + vec::iter(path) {|pe| encode_path_elt(ebml_w, pe); } + encode_path_elt(ebml_w, name); + } +} + +fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod, + id: node_id, path: ast_map::path, name: ident) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(id)); + encode_family(ebml_w, 'm'); + encode_name(ebml_w, name); + alt ecx.ccx.maps.impl_map.get(id) { + list::cons(impls, @list::nil) { + for i in *impls { + if ast_util::is_exported(i.ident, md) { + ebml_w.wr_tagged_str(tag_mod_impl, def_to_str(i.did)); + } + } + } + _ { ecx.ccx.tcx.sess.bug("encode_info_for_mod: \ + undocumented invariant"); } + } + encode_path(ebml_w, path, ast_map::path_mod(name)); + ebml_w.end_tag(); +} + +fn purity_fn_family(p: purity) -> char { + alt p { + unsafe_fn { 'u' } + pure_fn { 'p' } + impure_fn { 'f' } + crust_fn { 'c' } + } +} + +fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, + &index: [entry<int>], path: ast_map::path) { + let tcx = ecx.ccx.tcx; + alt item.node { + item_const(_, _) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 'c'); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_symbol(ecx, ebml_w, item.id); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + } + item_fn(decl, tps, _) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, purity_fn_family(decl.purity)); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_symbol(ecx, ebml_w, item.id); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + if should_inline(path, item.attrs) { + astencode::encode_inlined_item(ecx, ebml_w, path, ii_item(item)); + } + ebml_w.end_tag(); + } + item_mod(m) { + encode_info_for_mod(ecx, ebml_w, m, item.id, path, item.ident); + } + item_native_mod(_) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 'n'); + encode_name(ebml_w, item.ident); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + } + item_ty(_, tps) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 'y'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_name(ebml_w, item.ident); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + } + item_enum(variants, tps) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 't'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_name(ebml_w, item.ident); + for v: variant in variants { + encode_variant_id(ebml_w, local_def(v.node.id)); + } + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + encode_enum_variant_info(ecx, ebml_w, item.id, variants, + path, index, tps); + } + item_class(_,_,_,_,_) { + fail "encode: implement item_class"; + } + item_res(_, tps, _, _, ctor_id) { + let fn_ty = node_id_to_type(tcx, ctor_id); + + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(ctor_id)); + encode_family(ebml_w, 'y'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, ty::ty_fn_ret(fn_ty)); + encode_name(ebml_w, item.ident); + encode_symbol(ecx, ebml_w, item.id); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + + index += [{val: ctor_id, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(ctor_id)); + encode_family(ebml_w, 'f'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, fn_ty); + encode_symbol(ecx, ebml_w, ctor_id); + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + } + item_impl(tps, ifce, _, methods) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 'i'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_name(ebml_w, item.ident); + for m in methods { + ebml_w.start_tag(tag_item_method); + ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id)))); + ebml_w.end_tag(); + } + alt ifce { + some(_) { + encode_symbol(ecx, ebml_w, item.id); + let i_ty = ty::lookup_item_type(tcx, local_def(item.id)).ty; + ebml_w.start_tag(tag_impl_iface); + write_type(ecx, ebml_w, i_ty); + ebml_w.end_tag(); + } + _ {} + } + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + + let impl_path = path + [ast_map::path_name(item.ident)]; + for m in methods { + index += [{val: m.id, pos: ebml_w.writer.tell()}]; + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(m.id)); + encode_family(ebml_w, purity_fn_family(m.decl.purity)); + encode_type_param_bounds(ebml_w, ecx, tps + m.tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id)); + encode_name(ebml_w, m.ident); + encode_symbol(ecx, ebml_w, m.id); + encode_path(ebml_w, impl_path, ast_map::path_name(m.ident)); + if should_inline(path, m.attrs) { + astencode::encode_inlined_item( + ecx, ebml_w, impl_path, + ii_method(local_def(item.id), m)); + } + ebml_w.end_tag(); + } + } + item_iface(tps, ms) { + ebml_w.start_tag(tag_items_data_item); + encode_def_id(ebml_w, local_def(item.id)); + encode_family(ebml_w, 'I'); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); + encode_name(ebml_w, item.ident); + let i = 0u; + for mty in *ty::iface_methods(tcx, local_def(item.id)) { + ebml_w.start_tag(tag_item_method); + encode_name(ebml_w, mty.ident); + encode_type_param_bounds(ebml_w, ecx, ms[i].tps); + encode_type(ecx, ebml_w, ty::mk_fn(tcx, mty.fty)); + encode_family(ebml_w, purity_fn_family(mty.purity)); + ebml_w.end_tag(); + i += 1u; + } + encode_path(ebml_w, path, ast_map::path_name(item.ident)); + ebml_w.end_tag(); + } + } +} + +fn encode_info_for_native_item(ecx: @encode_ctxt, ebml_w: ebml::writer, + nitem: @native_item, path: ast_map::path) { + ebml_w.start_tag(tag_items_data_item); + alt nitem.node { + native_item_fn(fn_decl, tps) { + encode_def_id(ebml_w, local_def(nitem.id)); + encode_family(ebml_w, purity_fn_family(fn_decl.purity)); + encode_type_param_bounds(ebml_w, ecx, tps); + encode_type(ecx, ebml_w, node_id_to_type(ecx.ccx.tcx, nitem.id)); + encode_symbol(ecx, ebml_w, nitem.id); + encode_path(ebml_w, path, ast_map::path_name(nitem.ident)); + } + } + ebml_w.end_tag(); +} + +fn encode_info_for_items(ecx: @encode_ctxt, ebml_w: ebml::writer, + crate_mod: _mod) -> [entry<int>] { + let index: [entry<int>] = []; + ebml_w.start_tag(tag_items_data); + index += [{val: crate_node_id, pos: ebml_w.writer.tell()}]; + encode_info_for_mod(ecx, ebml_w, crate_mod, crate_node_id, [], ""); + ecx.ccx.tcx.items.items {|key, val| + alt val { + middle::ast_map::node_item(i, path) { + index += [{val: key, pos: ebml_w.writer.tell()}]; + encode_info_for_item(ecx, ebml_w, i, index, *path); + } + middle::ast_map::node_native_item(i, path) { + index += [{val: key, pos: ebml_w.writer.tell()}]; + encode_info_for_native_item(ecx, ebml_w, i, *path); + } + _ { } + } + }; + ebml_w.end_tag(); + ret index; +} + + +// Path and definition ID indexing + +fn create_index<T: copy>(index: [entry<T>], hash_fn: fn@(T) -> uint) -> + [@[entry<T>]] { + let buckets: [@mutable [entry<T>]] = []; + uint::range(0u, 256u) {|_i| buckets += [@mutable []]; }; + for elt: entry<T> in index { + let h = hash_fn(elt.val); + *buckets[h % 256u] += [elt]; + } + + let buckets_frozen = []; + for bucket: @mutable [entry<T>] in buckets { + buckets_frozen += [@*bucket]; + } + ret buckets_frozen; +} + +fn encode_index<T>(ebml_w: ebml::writer, buckets: [@[entry<T>]], + write_fn: fn(io::writer, T)) { + let writer = ebml_w.writer; + ebml_w.start_tag(tag_index); + let bucket_locs: [uint] = []; + ebml_w.start_tag(tag_index_buckets); + for bucket: @[entry<T>] in buckets { + bucket_locs += [ebml_w.writer.tell()]; + ebml_w.start_tag(tag_index_buckets_bucket); + for elt: entry<T> in *bucket { + ebml_w.start_tag(tag_index_buckets_bucket_elt); + writer.write_be_uint(elt.pos, 4u); + write_fn(writer, elt.val); + ebml_w.end_tag(); + } + ebml_w.end_tag(); + } + ebml_w.end_tag(); + ebml_w.start_tag(tag_index_table); + for pos: uint in bucket_locs { writer.write_be_uint(pos, 4u); } + ebml_w.end_tag(); + ebml_w.end_tag(); +} + +fn write_str(writer: io::writer, &&s: str) { writer.write_str(s); } + +fn write_int(writer: io::writer, &&n: int) { + writer.write_be_uint(n as uint, 4u); +} + +fn encode_meta_item(ebml_w: ebml::writer, mi: meta_item) { + alt mi.node { + meta_word(name) { + ebml_w.start_tag(tag_meta_item_word); + ebml_w.start_tag(tag_meta_item_name); + ebml_w.writer.write(str::bytes(name)); + ebml_w.end_tag(); + ebml_w.end_tag(); + } + meta_name_value(name, value) { + alt value.node { + lit_str(value) { + ebml_w.start_tag(tag_meta_item_name_value); + ebml_w.start_tag(tag_meta_item_name); + ebml_w.writer.write(str::bytes(name)); + ebml_w.end_tag(); + ebml_w.start_tag(tag_meta_item_value); + ebml_w.writer.write(str::bytes(value)); + ebml_w.end_tag(); + ebml_w.end_tag(); + } + _ {/* FIXME (#611) */ } + } + } + meta_list(name, items) { + ebml_w.start_tag(tag_meta_item_list); + ebml_w.start_tag(tag_meta_item_name); + ebml_w.writer.write(str::bytes(name)); + ebml_w.end_tag(); + for inner_item: @meta_item in items { + encode_meta_item(ebml_w, *inner_item); + } + ebml_w.end_tag(); + } + } +} + +fn encode_attributes(ebml_w: ebml::writer, attrs: [attribute]) { + ebml_w.start_tag(tag_attributes); + for attr: attribute in attrs { + ebml_w.start_tag(tag_attribute); + encode_meta_item(ebml_w, attr.node.value); + ebml_w.end_tag(); + } + ebml_w.end_tag(); +} + +// So there's a special crate attribute called 'link' which defines the +// metadata that Rust cares about for linking crates. This attribute requires +// 'name' and 'vers' items, so if the user didn't provide them we will throw +// them in anyway with default values. +fn synthesize_crate_attrs(ecx: @encode_ctxt, crate: @crate) -> [attribute] { + + fn synthesize_link_attr(ecx: @encode_ctxt, items: [@meta_item]) -> + attribute { + + assert (ecx.ccx.link_meta.name != ""); + assert (ecx.ccx.link_meta.vers != ""); + + let name_item = + attr::mk_name_value_item_str("name", ecx.ccx.link_meta.name); + let vers_item = + attr::mk_name_value_item_str("vers", ecx.ccx.link_meta.vers); + + let other_items = + { + let tmp = attr::remove_meta_items_by_name(items, "name"); + attr::remove_meta_items_by_name(tmp, "vers") + }; + + let meta_items = [name_item, vers_item] + other_items; + let link_item = attr::mk_list_item("link", meta_items); + + ret attr::mk_attr(link_item); + } + + let attrs: [attribute] = []; + let found_link_attr = false; + for attr: attribute in crate.node.attrs { + attrs += + if attr::get_attr_name(attr) != "link" { + [attr] + } else { + alt attr.node.value.node { + meta_list(n, l) { + found_link_attr = true;; + [synthesize_link_attr(ecx, l)] + } + _ { [attr] } + } + }; + } + + if !found_link_attr { attrs += [synthesize_link_attr(ecx, [])]; } + + ret attrs; +} + +fn encode_crate_deps(ebml_w: ebml::writer, cstore: cstore::cstore) { + + fn get_ordered_names(cstore: cstore::cstore) -> [str] { + type hashkv = @{key: crate_num, val: cstore::crate_metadata}; + type numname = {crate: crate_num, ident: str}; + + // Pull the cnums and names out of cstore + let pairs: [mutable numname] = [mutable]; + cstore::iter_crate_data(cstore) {|key, val| + pairs += [mutable {crate: key, ident: val.name}]; + }; + + // Sort by cnum + fn lteq(kv1: numname, kv2: numname) -> bool { kv1.crate <= kv2.crate } + std::sort::quick_sort(lteq, pairs); + + // Sanity-check the crate numbers + let expected_cnum = 1; + for n: numname in pairs { + assert (n.crate == expected_cnum); + expected_cnum += 1; + } + + // Return just the names + fn name(kv: numname) -> str { kv.ident } + // mutable -> immutable hack for vec::map + let immpairs = vec::slice(pairs, 0u, vec::len(pairs)); + ret vec::map(immpairs, name); + } + + // We're just going to write a list of crate names, with the assumption + // that they are numbered 1 to n. + // FIXME: This is not nearly enough to support correct versioning + // but is enough to get transitive crate dependencies working. + ebml_w.start_tag(tag_crate_deps); + for cname: str in get_ordered_names(cstore) { + ebml_w.start_tag(tag_crate_dep); + ebml_w.writer.write(str::bytes(cname)); + ebml_w.end_tag(); + } + ebml_w.end_tag(); +} + +fn encode_hash(ebml_w: ebml::writer, hash: str) { + ebml_w.start_tag(tag_crate_hash); + ebml_w.writer.write(str::bytes(hash)); + ebml_w.end_tag(); +} + +fn encode_metadata(cx: crate_ctxt, crate: @crate) -> [u8] { + + let abbrevs = ty::new_ty_hash(); + let ecx = @{ccx: cx, type_abbrevs: abbrevs}; + + let buf = io::mk_mem_buffer(); + let buf_w = io::mem_buffer_writer(buf); + let ebml_w = ebml::mk_writer(buf_w); + + encode_hash(ebml_w, cx.link_meta.extras_hash); + + let crate_attrs = synthesize_crate_attrs(ecx, crate); + encode_attributes(ebml_w, crate_attrs); + + encode_crate_deps(ebml_w, cx.sess.cstore); + + // Encode and index the paths. + ebml_w.start_tag(tag_paths); + let paths_index = encode_item_paths(ebml_w, ecx, crate); + let paths_buckets = create_index(paths_index, hash_path); + encode_index(ebml_w, paths_buckets, write_str); + ebml_w.end_tag(); + + // Encode and index the items. + ebml_w.start_tag(tag_items); + let items_index = encode_info_for_items(ecx, ebml_w, crate.node.module); + let items_buckets = create_index(items_index, hash_node_id); + encode_index(ebml_w, items_buckets, write_int); + ebml_w.end_tag(); + + // Pad this, since something (LLVM, presumably) is cutting off the + // remaining % 4 bytes. + buf_w.write([0u8, 0u8, 0u8, 0u8]); + io::mem_buffer_buf(buf) +} + +// Get the encoded string for a type +fn encoded_ty(tcx: ty::ctxt, t: ty::t) -> str { + let cx = @{ds: def_to_str, tcx: tcx, abbrevs: tyencode::ac_no_abbrevs}; + let buf = io::mk_mem_buffer(); + tyencode::enc_ty(io::mem_buffer_writer(buf), cx, t); + ret io::mem_buffer_str(buf); +} + + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs new file mode 100644 index 00000000000..5d97777c0fb --- /dev/null +++ b/src/rustc/metadata/tydecode.rs @@ -0,0 +1,426 @@ +// Type decoding + +import syntax::ast; +import syntax::ast::*; +import syntax::ast_util; +import syntax::ast_util::respan; +import middle::ty; + +export parse_ty_data, parse_def_id; +export parse_bounds_data; + +// Compact string representation for ty::t values. API ty_str & +// parse_from_str. Extra parameters are for converting to/from def_ids in the +// data buffer. Whatever format you choose should not contain pipe characters. + +// Callback to translate defs to strs or back: +type conv_did = fn(ast::def_id) -> ast::def_id; + +type pstate = {data: @[u8], crate: int, mutable pos: uint, tcx: ty::ctxt}; + +fn peek(st: @pstate) -> char { + st.data[st.pos] as char +} + +fn next(st: @pstate) -> char { + let ch = st.data[st.pos] as char; + st.pos = st.pos + 1u; + ret ch; +} + +fn next_byte(st: @pstate) -> u8 { + let b = st.data[st.pos]; + st.pos = st.pos + 1u; + ret b; +} + +fn parse_ident(st: @pstate, last: char) -> ast::ident { + fn is_last(b: char, c: char) -> bool { ret c == b; } + ret parse_ident_(st, bind is_last(last, _)); +} + +fn parse_ident_(st: @pstate, is_last: fn@(char) -> bool) -> + ast::ident { + let rslt = ""; + while !is_last(peek(st)) { + rslt += str::from_byte(next_byte(st)); + } + ret rslt; +} + + +fn parse_ty_data(data: @[u8], crate_num: int, pos: uint, tcx: ty::ctxt, + conv: conv_did) -> ty::t { + let st = @{data: data, crate: crate_num, mutable pos: pos, tcx: tcx}; + parse_ty(st, conv) +} + +fn parse_ret_ty(st: @pstate, conv: conv_did) -> (ast::ret_style, ty::t) { + alt peek(st) { + '!' { next(st); (ast::noreturn, ty::mk_bot(st.tcx)) } + _ { (ast::return_val, parse_ty(st, conv)) } + } +} + +fn parse_constrs(st: @pstate, conv: conv_did) -> [@ty::constr] { + let rslt: [@ty::constr] = []; + alt peek(st) { + ':' { + do { + next(st); + rslt += [parse_constr(st, conv, parse_constr_arg)]; + } while peek(st) == ';' + } + _ { } + } + ret rslt; +} + +// FIXME less copy-and-paste +fn parse_ty_constrs(st: @pstate, conv: conv_did) -> [@ty::type_constr] { + let rslt: [@ty::type_constr] = []; + alt peek(st) { + ':' { + do { + next(st); + rslt += [parse_constr(st, conv, parse_ty_constr_arg)]; + } while peek(st) == ';' + } + _ { } + } + ret rslt; +} + +fn parse_path(st: @pstate) -> @ast::path { + let idents: [ast::ident] = []; + fn is_last(c: char) -> bool { ret c == '(' || c == ':'; } + idents += [parse_ident_(st, is_last)]; + while true { + alt peek(st) { + ':' { next(st); next(st); } + c { + if c == '(' { + ret @respan(ast_util::dummy_sp(), + {global: false, idents: idents, types: []}); + } else { idents += [parse_ident_(st, is_last)]; } + } + } + } + fail "parse_path: ill-formed path"; +} + +fn parse_constr_arg(st: @pstate) -> ast::fn_constr_arg { + alt peek(st) { + '*' { st.pos += 1u; ret ast::carg_base; } + c { + + /* how will we disambiguate between + an arg index and a lit argument? */ + if c >= '0' && c <= '9' { + next(st); + // FIXME + ret ast::carg_ident((c as uint) - 48u); + } else { + #error("Lit args are unimplemented"); + fail; // FIXME + } + /* + else { + auto lit = parse_lit(st, conv, ','); + args += [respan(st.span, ast::carg_lit(lit))]; + } + */ + } + } +} + +fn parse_ty_constr_arg(st: @pstate) -> ast::constr_arg_general_<@path> { + alt peek(st) { + '*' { st.pos += 1u; ret ast::carg_base; } + c { ret ast::carg_ident(parse_path(st)); } + } +} + +fn parse_constr<T: copy>(st: @pstate, conv: conv_did, + pser: fn(@pstate) -> ast::constr_arg_general_<T>) + -> @ty::constr_general<T> { + let sp = ast_util::dummy_sp(); // FIXME: use a real span + let args: [@sp_constr_arg<T>] = []; + let pth = parse_path(st); + let ignore: char = next(st); + assert (ignore == '('); + let def = parse_def(st, conv); + let an_arg: constr_arg_general_<T>; + do { + an_arg = pser(st); + // FIXME use a real span + args += [@respan(sp, an_arg)]; + ignore = next(st); + } while ignore == ';' + assert (ignore == ')'); + ret @respan(sp, {path: pth, args: args, id: def}); +} + +fn parse_ty_rust_fn(st: @pstate, conv: conv_did, p: ast::proto) -> ty::t { + ret ty::mk_fn(st.tcx, {proto: p with parse_ty_fn(st, conv)}); +} + +fn parse_proto(c: char) -> ast::proto { + alt c { + '~' { ast::proto_uniq } + '@' { ast::proto_box } + '*' { ast::proto_any } + '&' { ast::proto_block } + 'n' { ast::proto_bare } + _ { fail "illegal fn type kind " + str::from_char(c); } + } +} + +fn parse_ty(st: @pstate, conv: conv_did) -> ty::t { + alt check next(st) { + 'n' { ret ty::mk_nil(st.tcx); } + 'z' { ret ty::mk_bot(st.tcx); } + 'b' { ret ty::mk_bool(st.tcx); } + 'i' { ret ty::mk_int(st.tcx); } + 'u' { ret ty::mk_uint(st.tcx); } + 'l' { ret ty::mk_float(st.tcx); } + 'M' { + alt check next(st) { + 'b' { ret ty::mk_mach_uint(st.tcx, ast::ty_u8); } + 'w' { ret ty::mk_mach_uint(st.tcx, ast::ty_u16); } + 'l' { ret ty::mk_mach_uint(st.tcx, ast::ty_u32); } + 'd' { ret ty::mk_mach_uint(st.tcx, ast::ty_u64); } + 'B' { ret ty::mk_mach_int(st.tcx, ast::ty_i8); } + 'W' { ret ty::mk_mach_int(st.tcx, ast::ty_i16); } + 'L' { ret ty::mk_mach_int(st.tcx, ast::ty_i32); } + 'D' { ret ty::mk_mach_int(st.tcx, ast::ty_i64); } + 'f' { ret ty::mk_mach_float(st.tcx, ast::ty_f32); } + 'F' { ret ty::mk_mach_float(st.tcx, ast::ty_f64); } + } + } + 'c' { ret ty::mk_char(st.tcx); } + 'S' { ret ty::mk_str(st.tcx); } + 't' { + assert (next(st) == '['); + let def = parse_def(st, conv); + let params: [ty::t] = []; + while peek(st) != ']' { params += [parse_ty(st, conv)]; } + st.pos = st.pos + 1u; + ret ty::mk_enum(st.tcx, def, params); + } + 'x' { + assert (next(st) == '['); + let def = parse_def(st, conv); + let params: [ty::t] = []; + while peek(st) != ']' { params += [parse_ty(st, conv)]; } + st.pos = st.pos + 1u; + ret ty::mk_iface(st.tcx, def, params); + } + 'p' { + let did = parse_def(st, conv); + ret ty::mk_param(st.tcx, parse_int(st) as uint, did); + } + 's' { + assert next(st) == '['; + let params = []; + while peek(st) != ']' { params += [parse_ty(st, conv)]; } + st.pos += 1u; + ret ty::mk_self(st.tcx, params); + } + '@' { ret ty::mk_box(st.tcx, parse_mt(st, conv)); } + '~' { ret ty::mk_uniq(st.tcx, parse_mt(st, conv)); } + '*' { ret ty::mk_ptr(st.tcx, parse_mt(st, conv)); } + 'I' { ret ty::mk_vec(st.tcx, parse_mt(st, conv)); } + 'R' { + assert (next(st) == '['); + let fields: [ty::field] = []; + while peek(st) != ']' { + let name = ""; + while peek(st) != '=' { + name += str::from_byte(next_byte(st)); + } + st.pos = st.pos + 1u; + fields += [{ident: name, mt: parse_mt(st, conv)}]; + } + st.pos = st.pos + 1u; + ret ty::mk_rec(st.tcx, fields); + } + 'T' { + assert (next(st) == '['); + let params = []; + while peek(st) != ']' { params += [parse_ty(st, conv)]; } + st.pos = st.pos + 1u; + ret ty::mk_tup(st.tcx, params); + } + 'f' { + let proto = parse_proto(next(st)); + parse_ty_rust_fn(st, conv, proto) + } + 'r' { + assert (next(st) == '['); + let def = parse_def(st, conv); + let inner = parse_ty(st, conv); + let params: [ty::t] = []; + while peek(st) != ']' { params += [parse_ty(st, conv)]; } + st.pos = st.pos + 1u; + ret ty::mk_res(st.tcx, def, inner, params); + } + 'X' { ret ty::mk_var(st.tcx, parse_int(st)); } + 'Y' { ret ty::mk_type(st.tcx); } + 'y' { ret ty::mk_send_type(st.tcx); } + 'C' { + let ck = alt check next(st) { + '&' { ty::ck_block } + '@' { ty::ck_box } + '~' { ty::ck_uniq } + }; + ret ty::mk_opaque_closure_ptr(st.tcx, ck); + } + '#' { + let pos = parse_hex(st); + assert (next(st) == ':'); + let len = parse_hex(st); + assert (next(st) == '#'); + alt st.tcx.rcache.find({cnum: st.crate, pos: pos, len: len}) { + some(tt) { ret tt; } + none { + let ps = @{pos: pos with *st}; + let tt = parse_ty(ps, conv); + st.tcx.rcache.insert({cnum: st.crate, pos: pos, len: len}, tt); + ret tt; + } + } + } + 'A' { + assert (next(st) == '['); + let tt = parse_ty(st, conv); + let tcs = parse_ty_constrs(st, conv); + assert (next(st) == ']'); + ret ty::mk_constr(st.tcx, tt, tcs); + } + '"' { + let def = parse_def(st, conv); + let inner = parse_ty(st, conv); + ty::mk_with_id(st.tcx, inner, def) + } + 'B' { ty::mk_opaque_box(st.tcx) } + c { #error("unexpected char in type string: %c", c); fail;} + } +} + +fn parse_mt(st: @pstate, conv: conv_did) -> ty::mt { + let m; + alt peek(st) { + 'm' { next(st); m = ast::m_mutbl; } + '?' { next(st); m = ast::m_const; } + _ { m = ast::m_imm; } + } + ret {ty: parse_ty(st, conv), mutbl: m}; +} + +fn parse_def(st: @pstate, conv: conv_did) -> ast::def_id { + let def = []; + while peek(st) != '|' { def += [next_byte(st)]; } + st.pos = st.pos + 1u; + ret conv(parse_def_id(def)); +} + +fn parse_int(st: @pstate) -> int { + let n = 0; + while true { + let cur = peek(st); + if cur < '0' || cur > '9' { break; } + st.pos = st.pos + 1u; + n *= 10; + n += (cur as int) - ('0' as int); + } + ret n; +} + +fn parse_hex(st: @pstate) -> uint { + let n = 0u; + while true { + let cur = peek(st); + if (cur < '0' || cur > '9') && (cur < 'a' || cur > 'f') { break; } + st.pos = st.pos + 1u; + n *= 16u; + if '0' <= cur && cur <= '9' { + n += (cur as uint) - ('0' as uint); + } else { n += 10u + (cur as uint) - ('a' as uint); } + } + ret n; +} + +fn parse_ty_fn(st: @pstate, conv: conv_did) -> ty::fn_ty { + assert (next(st) == '['); + let inputs: [ty::arg] = []; + while peek(st) != ']' { + let mode = alt check peek(st) { + '&' { ast::by_mutbl_ref } + '-' { ast::by_move } + '+' { ast::by_copy } + '=' { ast::by_ref } + '#' { ast::by_val } + }; + st.pos += 1u; + inputs += [{mode: ast::expl(mode), ty: parse_ty(st, conv)}]; + } + st.pos += 1u; // eat the ']' + let cs = parse_constrs(st, conv); + let (ret_style, ret_ty) = parse_ret_ty(st, conv); + ret {proto: ast::proto_bare, inputs: inputs, output: ret_ty, + ret_style: ret_style, constraints: cs}; +} + + +// Rust metadata parsing +fn parse_def_id(buf: [u8]) -> ast::def_id { + let colon_idx = 0u; + let len = vec::len(buf); + while colon_idx < len && buf[colon_idx] != ':' as u8 { colon_idx += 1u; } + if colon_idx == len { + #error("didn't find ':' when parsing def id"); + fail; + } + let crate_part = vec::slice::<u8>(buf, 0u, colon_idx); + let def_part = vec::slice::<u8>(buf, colon_idx + 1u, len); + + let crate_part_vec = []; + let def_part_vec = []; + for b: u8 in crate_part { crate_part_vec += [b]; } + for b: u8 in def_part { def_part_vec += [b]; } + + let crate_num = option::get(uint::parse_buf(crate_part_vec, 10u)) as int; + let def_num = option::get(uint::parse_buf(def_part_vec, 10u)) as int; + ret {crate: crate_num, node: def_num}; +} + +fn parse_bounds_data(data: @[u8], start: uint, + crate_num: int, tcx: ty::ctxt, conv: conv_did) + -> @[ty::param_bound] { + let st = @{data: data, crate: crate_num, mutable pos: start, tcx: tcx}; + parse_bounds(st, conv) +} + +fn parse_bounds(st: @pstate, conv: conv_did) -> @[ty::param_bound] { + let bounds = []; + while true { + bounds += [alt check next(st) { + 'S' { ty::bound_send } + 'C' { ty::bound_copy } + 'I' { ty::bound_iface(parse_ty(st, conv)) } + '.' { break; } + }]; + } + @bounds +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/metadata/tyencode.rs b/src/rustc/metadata/tyencode.rs new file mode 100644 index 00000000000..aa08e313dd7 --- /dev/null +++ b/src/rustc/metadata/tyencode.rs @@ -0,0 +1,303 @@ +// Type encoding + +import std::io; +import io::writer_util; +import std::map::hashmap; +import syntax::ast::*; +import driver::session::session; +import middle::ty; +import syntax::print::pprust::*; + +export ctxt; +export ty_abbrev; +export ac_no_abbrevs; +export ac_use_abbrevs; +export enc_ty; +export enc_bounds; +export enc_mode; + +type ctxt = + // Def -> str Callback: + // The type context. + {ds: fn@(def_id) -> str, tcx: ty::ctxt, abbrevs: abbrev_ctxt}; + +// Compact string representation for ty.t values. API ty_str & parse_from_str. +// Extra parameters are for converting to/from def_ids in the string rep. +// Whatever format you choose should not contain pipe characters. +type ty_abbrev = {pos: uint, len: uint, s: @str}; + +enum abbrev_ctxt { ac_no_abbrevs, ac_use_abbrevs(hashmap<ty::t, ty_abbrev>), } + +fn cx_uses_abbrevs(cx: @ctxt) -> bool { + alt cx.abbrevs { + ac_no_abbrevs { ret false; } + ac_use_abbrevs(_) { ret true; } + } +} + +fn enc_ty(w: io::writer, cx: @ctxt, t: ty::t) { + alt cx.abbrevs { + ac_no_abbrevs { + let result_str = alt cx.tcx.short_names_cache.find(t) { + some(s) { *s } + none { + let buf = io::mk_mem_buffer(); + enc_sty(io::mem_buffer_writer(buf), cx, ty::get(t).struct); + cx.tcx.short_names_cache.insert(t, @io::mem_buffer_str(buf)); + io::mem_buffer_str(buf) + } + }; + w.write_str(result_str); + } + ac_use_abbrevs(abbrevs) { + alt abbrevs.find(t) { + some(a) { w.write_str(*a.s); ret; } + none { + let pos = w.tell(); + alt ty::type_def_id(t) { + some(def_id) { + w.write_char('"'); + w.write_str(cx.ds(def_id)); + w.write_char('|'); + } + _ {} + } + enc_sty(w, cx, ty::get(t).struct); + let end = w.tell(); + let len = end - pos; + fn estimate_sz(u: uint) -> uint { + let n = u; + let len = 0u; + while n != 0u { len += 1u; n = n >> 4u; } + ret len; + } + let abbrev_len = 3u + estimate_sz(pos) + estimate_sz(len); + if abbrev_len < len { + // I.e. it's actually an abbreviation. + let s = "#" + uint::to_str(pos, 16u) + ":" + + uint::to_str(len, 16u) + "#"; + let a = {pos: pos, len: len, s: @s}; + abbrevs.insert(t, a); + } + ret; + } + } + } + } +} +fn enc_mt(w: io::writer, cx: @ctxt, mt: ty::mt) { + alt mt.mutbl { + m_imm { } + m_mutbl { w.write_char('m'); } + m_const { w.write_char('?'); } + } + enc_ty(w, cx, mt.ty); +} +fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) { + alt st { + ty::ty_nil { w.write_char('n'); } + ty::ty_bot { w.write_char('z'); } + ty::ty_bool { w.write_char('b'); } + ty::ty_int(t) { + alt t { + ty_i { w.write_char('i'); } + ty_char { w.write_char('c'); } + ty_i8 { w.write_str("MB"); } + ty_i16 { w.write_str("MW"); } + ty_i32 { w.write_str("ML"); } + ty_i64 { w.write_str("MD"); } + } + } + ty::ty_uint(t) { + alt t { + ty_u { w.write_char('u'); } + ty_u8 { w.write_str("Mb"); } + ty_u16 { w.write_str("Mw"); } + ty_u32 { w.write_str("Ml"); } + ty_u64 { w.write_str("Md"); } + } + } + ty::ty_float(t) { + alt t { + ty_f { w.write_char('l'); } + ty_f32 { w.write_str("Mf"); } + ty_f64 { w.write_str("MF"); } + } + } + ty::ty_str { w.write_char('S'); } + ty::ty_enum(def, tys) { + w.write_str("t["); + w.write_str(cx.ds(def)); + w.write_char('|'); + for t: ty::t in tys { enc_ty(w, cx, t); } + w.write_char(']'); + } + ty::ty_iface(def, tys) { + w.write_str("x["); + w.write_str(cx.ds(def)); + w.write_char('|'); + for t: ty::t in tys { enc_ty(w, cx, t); } + w.write_char(']'); + } + ty::ty_tup(ts) { + w.write_str("T["); + for t in ts { enc_ty(w, cx, t); } + w.write_char(']'); + } + ty::ty_box(mt) { w.write_char('@'); enc_mt(w, cx, mt); } + ty::ty_uniq(mt) { w.write_char('~'); enc_mt(w, cx, mt); } + ty::ty_ptr(mt) { w.write_char('*'); enc_mt(w, cx, mt); } + ty::ty_vec(mt) { w.write_char('I'); enc_mt(w, cx, mt); } + ty::ty_rec(fields) { + w.write_str("R["); + for field: ty::field in fields { + w.write_str(field.ident); + w.write_char('='); + enc_mt(w, cx, field.mt); + } + w.write_char(']'); + } + ty::ty_fn(f) { + enc_proto(w, f.proto); + enc_ty_fn(w, cx, f); + } + ty::ty_res(def, ty, tps) { + w.write_str("r["); + w.write_str(cx.ds(def)); + w.write_char('|'); + enc_ty(w, cx, ty); + for t: ty::t in tps { enc_ty(w, cx, t); } + w.write_char(']'); + } + ty::ty_var(id) { w.write_char('X'); w.write_str(int::str(id)); } + ty::ty_param(id, did) { + w.write_char('p'); + w.write_str(cx.ds(did)); + w.write_char('|'); + w.write_str(uint::str(id)); + } + ty::ty_self(tps) { + w.write_str("s["); + for t in tps { enc_ty(w, cx, t); } + w.write_char(']'); + } + ty::ty_type { w.write_char('Y'); } + ty::ty_send_type { w.write_char('y'); } + ty::ty_opaque_closure_ptr(ty::ck_block) { w.write_str("C&"); } + ty::ty_opaque_closure_ptr(ty::ck_box) { w.write_str("C@"); } + ty::ty_opaque_closure_ptr(ty::ck_uniq) { w.write_str("C~"); } + ty::ty_constr(ty, cs) { + w.write_str("A["); + enc_ty(w, cx, ty); + for tc: @ty::type_constr in cs { enc_ty_constr(w, cx, tc); } + w.write_char(']'); + } + ty::ty_opaque_box { w.write_char('B'); } + ty::ty_class(def, tys) { + w.write_str("c["); + w.write_str(cx.ds(def)); + w.write_char('|'); + for t: ty::t in tys { enc_ty(w, cx, t); } + w.write_char(']'); + } + } +} +fn enc_proto(w: io::writer, proto: proto) { + alt proto { + proto_uniq { w.write_str("f~"); } + proto_box { w.write_str("f@"); } + proto_block { w.write_str("f&"); } + proto_any { w.write_str("f*"); } + proto_bare { w.write_str("fn"); } + } +} + +fn enc_mode(w: io::writer, cx: @ctxt, m: mode) { + alt ty::resolved_mode(cx.tcx, m) { + by_mutbl_ref { w.write_char('&'); } + by_move { w.write_char('-'); } + by_copy { w.write_char('+'); } + by_ref { w.write_char('='); } + by_val { w.write_char('#'); } + } +} + +fn enc_ty_fn(w: io::writer, cx: @ctxt, ft: ty::fn_ty) { + w.write_char('['); + for arg: ty::arg in ft.inputs { + enc_mode(w, cx, arg.mode); + enc_ty(w, cx, arg.ty); + } + w.write_char(']'); + let colon = true; + for c: @ty::constr in ft.constraints { + if colon { + w.write_char(':'); + colon = false; + } else { w.write_char(';'); } + enc_constr(w, cx, c); + } + alt ft.ret_style { + noreturn { w.write_char('!'); } + _ { enc_ty(w, cx, ft.output); } + } +} + +// FIXME less copy-and-paste +fn enc_constr(w: io::writer, cx: @ctxt, c: @ty::constr) { + w.write_str(path_to_str(c.node.path)); + w.write_char('('); + w.write_str(cx.ds(c.node.id)); + w.write_char('|'); + let semi = false; + for a: @constr_arg in c.node.args { + if semi { w.write_char(';'); } else { semi = true; } + alt a.node { + carg_base { w.write_char('*'); } + carg_ident(i) { w.write_uint(i); } + carg_lit(l) { w.write_str(lit_to_str(l)); } + } + } + w.write_char(')'); +} + +fn enc_ty_constr(w: io::writer, cx: @ctxt, c: @ty::type_constr) { + w.write_str(path_to_str(c.node.path)); + w.write_char('('); + w.write_str(cx.ds(c.node.id)); + w.write_char('|'); + let semi = false; + for a: @ty::ty_constr_arg in c.node.args { + if semi { w.write_char(';'); } else { semi = true; } + alt a.node { + carg_base { w.write_char('*'); } + carg_ident(p) { w.write_str(path_to_str(p)); } + carg_lit(l) { w.write_str(lit_to_str(l)); } + } + } + w.write_char(')'); +} + +fn enc_bounds(w: io::writer, cx: @ctxt, bs: @[ty::param_bound]) { + for bound in *bs { + alt bound { + ty::bound_send { w.write_char('S'); } + ty::bound_copy { w.write_char('C'); } + ty::bound_iface(tp) { + w.write_char('I'); + enc_ty(w, cx, tp); + } + } + } + w.write_char('.'); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/alias.rs b/src/rustc/middle/alias.rs new file mode 100644 index 00000000000..cb0785b4df1 --- /dev/null +++ b/src/rustc/middle/alias.rs @@ -0,0 +1,715 @@ + +import syntax::{ast, ast_util}; +import ast::{ident, fn_ident, node_id}; +import syntax::codemap::span; +import syntax::visit; +import visit::vt; +import std::list; +import std::util::unreachable; +import option::is_none; +import list::list; +import driver::session::session; +import pat_util::*; +import util::ppaux::ty_to_str; + +// This is not an alias-analyser (though it would merit from becoming one, or +// getting input from one, to be more precise). It is a pass that checks +// whether aliases are used in a safe way. + +enum copied { not_allowed, copied, not_copied, } +enum invalid_reason { overwritten, val_taken, } +type invalid = {reason: invalid_reason, + node_id: node_id, + sp: span, path: @ast::path}; + +enum unsafe_ty { contains(ty::t), mutbl_contains(ty::t), } + +type binding = @{node_id: node_id, + span: span, + root_var: option<node_id>, + local_id: uint, + unsafe_tys: [unsafe_ty], + mutable copied: copied}; + +// FIXME it may be worthwhile to use a linked list of bindings instead +type scope = {bs: [binding], + invalid: @mutable list<@invalid>}; + +fn mk_binding(cx: ctx, id: node_id, span: span, root_var: option<node_id>, + unsafe_tys: [unsafe_ty]) -> binding { + alt root_var { + some(r_id) { cx.ref_map.insert(id, r_id); } + _ {} + } + ret @{node_id: id, span: span, root_var: root_var, + local_id: local_id_of_node(cx, id), + unsafe_tys: unsafe_tys, + mutable copied: not_copied}; +} + +enum local_info { local(uint), } + +type copy_map = std::map::hashmap<node_id, ()>; +type ref_map = std::map::hashmap<node_id, node_id>; + +type ctx = {tcx: ty::ctxt, + copy_map: copy_map, + ref_map: ref_map, + mutable silent: bool}; + +fn check_crate(tcx: ty::ctxt, crate: @ast::crate) -> (copy_map, ref_map) { + // Stores information about function arguments that's otherwise not easily + // available. + let cx = @{tcx: tcx, + copy_map: std::map::new_int_hash(), + ref_map: std::map::new_int_hash(), + mutable silent: false}; + let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _), + visit_expr: bind visit_expr(cx, _, _, _), + visit_block: bind visit_block(cx, _, _, _) + with *visit::default_visitor::<scope>()}; + let sc = {bs: [], invalid: @mutable list::nil}; + visit::visit_crate(*crate, sc, visit::mk_vt(v)); + tcx.sess.abort_if_errors(); + ret (cx.copy_map, cx.ref_map); +} + +fn visit_fn(cx: @ctx, _fk: visit::fn_kind, decl: ast::fn_decl, + body: ast::blk, sp: span, + id: ast::node_id, sc: scope, v: vt<scope>) { + visit::visit_fn_decl(decl, sc, v); + let fty = ty::node_id_to_type(cx.tcx, id); + let args = ty::ty_fn_args(fty); + for arg in args { + alt ty::resolved_mode(cx.tcx, arg.mode) { + ast::by_val if ty::type_has_dynamic_size(cx.tcx, arg.ty) { + err(*cx, sp, "can not pass a dynamically-sized type by value"); + } + _ { /* fallthrough */ } + } + } + + // Blocks need to obey any restrictions from the enclosing scope, and may + // be called multiple times. + let proto = ty::ty_fn_proto(fty); + alt proto { + ast::proto_block | ast::proto_any { + check_loop(*cx, sc) {|| v.visit_block(body, sc, v);} + } + ast::proto_box | ast::proto_uniq | ast::proto_bare { + let sc = {bs: [], invalid: @mutable list::nil}; + v.visit_block(body, sc, v); + } + } +} + +fn visit_expr(cx: @ctx, ex: @ast::expr, sc: scope, v: vt<scope>) { + let handled = true; + alt ex.node { + ast::expr_call(f, args, _) { + check_call(*cx, sc, f, args); + handled = false; + } + ast::expr_alt(input, arms, _) { check_alt(*cx, input, arms, sc, v); } + ast::expr_for(decl, seq, blk) { + visit_expr(cx, seq, sc, v); + check_loop(*cx, sc) {|| check_for(*cx, decl, seq, blk, sc, v); } + } + ast::expr_path(pt) { + check_var(*cx, ex, pt, ex.id, false, sc); + handled = false; + } + ast::expr_swap(lhs, rhs) { + check_lval(cx, lhs, sc, v); + check_lval(cx, rhs, sc, v); + handled = false; + } + ast::expr_move(dest, src) { + visit_expr(cx, src, sc, v); + check_lval(cx, dest, sc, v); + check_lval(cx, src, sc, v); + } + ast::expr_assign(dest, src) | ast::expr_assign_op(_, dest, src) { + visit_expr(cx, src, sc, v); + check_lval(cx, dest, sc, v); + } + ast::expr_if(c, then, els) { check_if(c, then, els, sc, v); } + ast::expr_while(_, _) | ast::expr_do_while(_, _) { + check_loop(*cx, sc) {|| visit::visit_expr(ex, sc, v); } + } + _ { handled = false; } + } + if !handled { visit::visit_expr(ex, sc, v); } +} + +fn visit_block(cx: @ctx, b: ast::blk, sc: scope, v: vt<scope>) { + let sc = sc; + for stmt in b.node.stmts { + alt stmt.node { + ast::stmt_decl(@{node: ast::decl_item(it), _}, _) { + v.visit_item(it, sc, v); + } + ast::stmt_decl(@{node: ast::decl_local(locs), _}, _) { + for loc in locs { + alt loc.node.init { + some(init) { + if init.op == ast::init_move { + check_lval(cx, init.expr, sc, v); + } else { + visit_expr(cx, init.expr, sc, v); + } + } + none { } + } + } + } + ast::stmt_expr(ex, _) | ast::stmt_semi(ex, _) { + v.visit_expr(ex, sc, v); + } + } + } + visit::visit_expr_opt(b.node.expr, sc, v); +} + +fn add_bindings_for_let(cx: ctx, &bs: [binding], loc: @ast::local) { + alt loc.node.init { + some(init) { + if init.op == ast::init_move { + err(cx, loc.span, "can not move into a by-reference binding"); + } + let root = expr_root(cx, init.expr, false); + let root_var = path_def_id(cx, root.ex); + if is_none(root_var) { + err(cx, loc.span, "a reference binding can't be \ + rooted in a temporary"); + } + for proot in pattern_roots(cx.tcx, root.mutbl, loc.node.pat) { + let bnd = mk_binding(cx, proot.id, proot.span, root_var, + unsafe_set(proot.mutbl)); + // Don't implicitly copy explicit references + bnd.copied = not_allowed; + bs += [bnd]; + } + } + _ { + err(cx, loc.span, "by-reference bindings must be initialized"); + } + } +} + + +fn cant_copy(cx: ctx, b: binding) -> bool { + alt b.copied { + not_allowed { ret true; } + copied { ret false; } + not_copied {} + } + let ty = ty::node_id_to_type(cx.tcx, b.node_id); + if ty::type_allows_implicit_copy(cx.tcx, ty) { + b.copied = copied; + cx.copy_map.insert(b.node_id, ()); + if copy_is_expensive(cx.tcx, ty) { + cx.tcx.sess.span_warn(b.span, + "inserting an implicit copy for type " + + util::ppaux::ty_to_str(cx.tcx, ty)); + } + ret false; + } else { ret true; } +} + +fn check_call(cx: ctx, sc: scope, f: @ast::expr, args: [@ast::expr]) + -> [binding] { + let fty = ty::expr_ty(cx.tcx, f); + let arg_ts = ty::ty_fn_args(fty); + let mut_roots: [{arg: uint, node: node_id}] = []; + let bindings = []; + let i = 0u; + for arg_t: ty::arg in arg_ts { + let arg = args[i]; + let root = expr_root(cx, arg, false); + alt ty::resolved_mode(cx.tcx, arg_t.mode) { + ast::by_mutbl_ref { + alt path_def(cx, arg) { + some(def) { + let dnum = ast_util::def_id_of_def(def).node; + mut_roots += [{arg: i, node: dnum}]; + } + _ { } + } + } + ast::by_ref | ast::by_val | ast::by_move | ast::by_copy { } + } + let root_var = path_def_id(cx, root.ex); + let arg_copied = alt ty::resolved_mode(cx.tcx, arg_t.mode) { + ast::by_move | ast::by_copy { copied } + ast::by_mutbl_ref { not_allowed } + ast::by_ref | ast::by_val { not_copied } + }; + bindings += [@{node_id: arg.id, + span: arg.span, + root_var: root_var, + local_id: 0u, + unsafe_tys: unsafe_set(root.mutbl), + mutable copied: arg_copied}]; + i += 1u; + } + let f_may_close = + alt f.node { + ast::expr_path(_) { def_is_local_or_self(cx.tcx.def_map.get(f.id)) } + _ { true } + }; + if f_may_close { + let i = 0u; + for b in bindings { + let unsfe = vec::len(b.unsafe_tys) > 0u; + alt b.root_var { + some(rid) { + for o in sc.bs { + if o.node_id == rid && vec::len(o.unsafe_tys) > 0u { + unsfe = true; break; + } + } + } + _ {} + } + if unsfe && cant_copy(cx, b) { + err(cx, f.span, #fmt["function may alias with argument \ + %u, which is not immutably rooted", i]); + } + i += 1u; + } + } + let j = 0u; + for b in bindings { + for unsafe_ty in b.unsafe_tys { + let i = 0u; + for arg_t: ty::arg in arg_ts { + let mut_alias = + (ast::by_mutbl_ref == ty::arg_mode(cx.tcx, arg_t)); + if i != j && + ty_can_unsafely_include(cx, unsafe_ty, arg_t.ty, + mut_alias) && + cant_copy(cx, b) { + err(cx, args[i].span, + #fmt["argument %u may alias with argument %u, \ + which is not immutably rooted", i, j]); + } + i += 1u; + } + } + j += 1u; + } + // Ensure we're not passing a root by mutable alias. + + for {node: node, arg: arg} in mut_roots { + let i = 0u; + for b in bindings { + if i != arg { + alt b.root_var { + some(root) { + if node == root && cant_copy(cx, b) { + err(cx, args[arg].span, + "passing a mutable reference to a \ + variable that roots another reference"); + break; + } + } + none { } + } + } + i += 1u; + } + } + ret bindings; +} + +fn check_alt(cx: ctx, input: @ast::expr, arms: [ast::arm], sc: scope, + v: vt<scope>) { + v.visit_expr(input, sc, v); + let orig_invalid = *sc.invalid; + let all_invalid = orig_invalid; + let root = expr_root(cx, input, true); + for a: ast::arm in arms { + let new_bs = sc.bs; + let root_var = path_def_id(cx, root.ex); + let pat_id_map = pat_util::pat_id_map(cx.tcx.def_map, a.pats[0]); + type info = { + id: node_id, + mutable unsafe_tys: [unsafe_ty], + span: span}; + let binding_info: [info] = []; + for pat in a.pats { + for proot in pattern_roots(cx.tcx, root.mutbl, pat) { + let canon_id = pat_id_map.get(proot.name); + alt vec::find(binding_info, {|x| x.id == canon_id}) { + some(s) { s.unsafe_tys += unsafe_set(proot.mutbl); } + none { + binding_info += [ + {id: canon_id, + mutable unsafe_tys: unsafe_set(proot.mutbl), + span: proot.span}]; + } + } + } + } + for info in binding_info { + new_bs += [mk_binding(cx, info.id, info.span, root_var, + copy info.unsafe_tys)]; + } + *sc.invalid = orig_invalid; + visit::visit_arm(a, {bs: new_bs with sc}, v); + all_invalid = join_invalid(all_invalid, *sc.invalid); + } + *sc.invalid = all_invalid; +} + +fn check_for(cx: ctx, local: @ast::local, seq: @ast::expr, blk: ast::blk, + sc: scope, v: vt<scope>) { + let root = expr_root(cx, seq, false); + + // If this is a mutable vector, don't allow it to be touched. + let seq_t = ty::expr_ty(cx.tcx, seq); + let cur_mutbl = root.mutbl; + alt ty::get(seq_t).struct { + ty::ty_vec(mt) { + if mt.mutbl != ast::m_imm { + cur_mutbl = some(contains(seq_t)); + } + } + _ {} + } + let root_var = path_def_id(cx, root.ex); + let new_bs = sc.bs; + for proot in pattern_roots(cx.tcx, cur_mutbl, local.node.pat) { + new_bs += [mk_binding(cx, proot.id, proot.span, root_var, + unsafe_set(proot.mutbl))]; + } + visit::visit_block(blk, {bs: new_bs with sc}, v); +} + +fn check_var(cx: ctx, ex: @ast::expr, p: @ast::path, id: ast::node_id, + assign: bool, sc: scope) { + let def = cx.tcx.def_map.get(id); + if !def_is_local_or_self(def) { ret; } + let my_defnum = ast_util::def_id_of_def(def).node; + let my_local_id = local_id_of_node(cx, my_defnum); + let var_t = ty::expr_ty(cx.tcx, ex); + for b in sc.bs { + // excludes variables introduced since the alias was made + if my_local_id < b.local_id { + for unsafe_ty in b.unsafe_tys { + if ty_can_unsafely_include(cx, unsafe_ty, var_t, assign) { + let inv = @{reason: val_taken, node_id: b.node_id, + sp: ex.span, path: p}; + *sc.invalid = list::cons(inv, @*sc.invalid); + } + } + } else if b.node_id == my_defnum { + test_scope(cx, sc, b, p); + } + } +} + +fn check_lval(cx: @ctx, dest: @ast::expr, sc: scope, v: vt<scope>) { + alt dest.node { + ast::expr_path(p) { + let def = cx.tcx.def_map.get(dest.id); + let dnum = ast_util::def_id_of_def(def).node; + for b in sc.bs { + if b.root_var == some(dnum) { + let inv = @{reason: overwritten, node_id: b.node_id, + sp: dest.span, path: p}; + *sc.invalid = list::cons(inv, @*sc.invalid); + } + } + } + _ { visit_expr(cx, dest, sc, v); } + } +} + +fn check_if(c: @ast::expr, then: ast::blk, els: option<@ast::expr>, + sc: scope, v: vt<scope>) { + v.visit_expr(c, sc, v); + let orig_invalid = *sc.invalid; + v.visit_block(then, sc, v); + let then_invalid = *sc.invalid; + *sc.invalid = orig_invalid; + visit::visit_expr_opt(els, sc, v); + *sc.invalid = join_invalid(*sc.invalid, then_invalid); +} + +fn check_loop(cx: ctx, sc: scope, checker: fn()) { + let orig_invalid = filter_invalid(*sc.invalid, sc.bs); + checker(); + let new_invalid = filter_invalid(*sc.invalid, sc.bs); + // Have to check contents of loop again if it invalidated an alias + if list::len(orig_invalid) < list::len(new_invalid) { + let old_silent = cx.silent; + cx.silent = true; + checker(); + cx.silent = old_silent; + } + *sc.invalid = new_invalid; +} + +fn test_scope(cx: ctx, sc: scope, b: binding, p: @ast::path) { + let prob = find_invalid(b.node_id, *sc.invalid); + alt b.root_var { + some(dn) { + for other in sc.bs { + if !is_none(prob) { break; } + if other.node_id == dn { + prob = find_invalid(other.node_id, *sc.invalid); + } + } + } + _ {} + } + if !is_none(prob) && cant_copy(cx, b) { + let i = option::get(prob); + let msg = alt i.reason { + overwritten { "overwriting " + ast_util::path_name(i.path) } + val_taken { "taking the value of " + ast_util::path_name(i.path) } + }; + err(cx, i.sp, msg + " will invalidate reference " + + ast_util::path_name(p) + ", which is still used"); + } +} + +fn path_def(cx: ctx, ex: @ast::expr) -> option<ast::def> { + ret alt ex.node { + ast::expr_path(_) { some(cx.tcx.def_map.get(ex.id)) } + _ { none } + } +} + +fn path_def_id(cx: ctx, ex: @ast::expr) -> option<ast::node_id> { + alt ex.node { + ast::expr_path(_) { + ret some(ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)).node); + } + _ { ret none; } + } +} + +fn ty_can_unsafely_include(cx: ctx, needle: unsafe_ty, haystack: ty::t, + mutbl: bool) -> bool { + fn get_mutbl(cur: bool, mt: ty::mt) -> bool { + ret cur || mt.mutbl != ast::m_imm; + } + fn helper(tcx: ty::ctxt, needle: unsafe_ty, haystack: ty::t, mutbl: bool) + -> bool { + if alt needle { + contains(ty) { ty == haystack } + mutbl_contains(ty) { mutbl && ty == haystack } + } { ret true; } + alt ty::get(haystack).struct { + ty::ty_enum(_, ts) { + for t: ty::t in ts { + if helper(tcx, needle, t, mutbl) { ret true; } + } + ret false; + } + ty::ty_box(mt) | ty::ty_ptr(mt) | ty::ty_uniq(mt) { + ret helper(tcx, needle, mt.ty, get_mutbl(mutbl, mt)); + } + ty::ty_rec(fields) { + for f: ty::field in fields { + if helper(tcx, needle, f.mt.ty, get_mutbl(mutbl, f.mt)) { + ret true; + } + } + ret false; + } + ty::ty_tup(ts) { + for t in ts { if helper(tcx, needle, t, mutbl) { ret true; } } + ret false; + } + ty::ty_fn({proto: ast::proto_bare, _}) { ret false; } + // These may contain anything. + ty::ty_fn(_) | ty::ty_iface(_, _) { ret true; } + // A type param may include everything, but can only be + // treated as opaque downstream, and is thus safe unless we + // saw mutable fields, in which case the whole thing can be + // overwritten. + ty::ty_param(_, _) { ret mutbl; } + _ { ret false; } + } + } + ret helper(cx.tcx, needle, haystack, mutbl); +} + +fn def_is_local_or_self(d: ast::def) -> bool { + alt d { + ast::def_local(_, _) | ast::def_arg(_, _) | ast::def_binding(_) | + ast::def_upvar(_, _, _) | ast::def_self(_) { true } + _ { false } + } +} + +fn local_id_of_node(cx: ctx, id: node_id) -> uint { + alt cx.tcx.items.find(id) { + some(ast_map::node_arg(_, id)) | some(ast_map::node_local(id)) { id } + _ { 0u } + } +} + +// Heuristic, somewhat random way to decide whether to warn when inserting an +// implicit copy. +fn copy_is_expensive(tcx: ty::ctxt, ty: ty::t) -> bool { + fn score_ty(tcx: ty::ctxt, ty: ty::t) -> uint { + ret alt ty::get(ty).struct { + ty::ty_nil | ty::ty_bot | ty::ty_bool | ty::ty_int(_) | + ty::ty_uint(_) | ty::ty_float(_) | ty::ty_type | + ty::ty_ptr(_) { 1u } + ty::ty_box(_) | ty::ty_iface(_, _) { 3u } + ty::ty_constr(t, _) | ty::ty_res(_, t, _) { score_ty(tcx, t) } + ty::ty_fn(_) { 4u } + ty::ty_str | ty::ty_vec(_) | ty::ty_param(_, _) { 50u } + ty::ty_uniq(mt) { 1u + score_ty(tcx, mt.ty) } + ty::ty_enum(_, ts) | ty::ty_tup(ts) { + let sum = 0u; + for t in ts { sum += score_ty(tcx, t); } + sum + } + ty::ty_rec(fs) { + let sum = 0u; + for f in fs { sum += score_ty(tcx, f.mt.ty); } + sum + } + _ { + tcx.sess.warn(#fmt("score_ty: unexpected type %s", + ty_to_str(tcx, ty))); + 1u // ??? + } + }; + } + ret score_ty(tcx, ty) > 8u; +} + +type pattern_root = {id: node_id, + name: ident, + mutbl: option<unsafe_ty>, + span: span}; + +fn pattern_roots(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat) + -> [pattern_root] { + fn walk(tcx: ty::ctxt, mutbl: option<unsafe_ty>, pat: @ast::pat, + &set: [pattern_root]) { + alt pat.node { + ast::pat_ident(nm, sub) + if !pat_util::pat_is_variant(tcx.def_map, pat) { + set += [{id: pat.id, name: path_to_ident(nm), mutbl: mutbl, + span: pat.span}]; + alt sub { some(p) { walk(tcx, mutbl, p, set); } _ {} } + } + ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) | + ast::pat_ident(_, _) {} + ast::pat_enum(_, ps) | ast::pat_tup(ps) { + for p in ps { walk(tcx, mutbl, p, set); } + } + ast::pat_rec(fs, _) { + let ty = ty::node_id_to_type(tcx, pat.id); + for f in fs { + let m = ty::get_field(ty, f.ident).mt.mutbl != ast::m_imm, + c = if m { some(contains(ty)) } else { mutbl }; + walk(tcx, c, f.pat, set); + } + } + ast::pat_box(p) { + let ty = ty::node_id_to_type(tcx, pat.id); + let m = alt ty::get(ty).struct { + ty::ty_box(mt) { mt.mutbl != ast::m_imm } + _ { tcx.sess.span_bug(pat.span, "box pat has non-box type"); } + }, + c = if m {some(contains(ty)) } else { mutbl }; + walk(tcx, c, p, set); + } + ast::pat_uniq(p) { + let ty = ty::node_id_to_type(tcx, pat.id); + let m = alt ty::get(ty).struct { + ty::ty_uniq(mt) { mt.mutbl != ast::m_imm } + _ { tcx.sess.span_bug(pat.span, "uniq pat has non-uniq type"); } + }, + c = if m { some(contains(ty)) } else { mutbl }; + walk(tcx, c, p, set); + } + } + } + let set = []; + walk(tcx, mutbl, pat, set); + ret set; +} + +// Wraps the expr_root in mutbl.rs to also handle roots that exist through +// return-by-reference +fn expr_root(cx: ctx, ex: @ast::expr, autoderef: bool) + -> {ex: @ast::expr, mutbl: option<unsafe_ty>} { + let base_root = mutbl::expr_root(cx.tcx, ex, autoderef); + let unsafe_ty = none; + for d in *base_root.ds { + if d.mutbl { unsafe_ty = some(contains(d.outer_t)); break; } + } + ret {ex: base_root.ex, mutbl: unsafe_ty}; +} + +fn unsafe_set(from: option<unsafe_ty>) -> [unsafe_ty] { + alt from { some(t) { [t] } _ { [] } } +} + +fn find_invalid(id: node_id, lst: list<@invalid>) + -> option<@invalid> { + let cur = lst; + while true { + alt cur { + list::nil { break; } + list::cons(head, tail) { + if head.node_id == id { ret some(head); } + cur = *tail; + } + } + } + ret none; +} + +fn join_invalid(a: list<@invalid>, b: list<@invalid>) -> list<@invalid> { + let result = a; + list::iter(b) {|elt| + let found = false; + list::iter(a) {|e| if e == elt { found = true; } } + if !found { result = list::cons(elt, @result); } + } + result +} + +fn filter_invalid(src: list<@invalid>, bs: [binding]) -> list<@invalid> { + let out = list::nil, cur = src; + while cur != list::nil { + alt cur { + list::cons(head, tail) { + let p = vec::position(bs, {|b| b.node_id == head.node_id}); + if !is_none(p) { out = list::cons(head, @out); } + cur = *tail; + } + list::nil { + // typestate would help... + unreachable(); + } + } + } + ret out; +} + +fn err(cx: ctx, sp: span, err: str) { + if !cx.silent || !cx.tcx.sess.has_errors() { + cx.tcx.sess.span_err(sp, err); + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/ast_map.rs b/src/rustc/middle/ast_map.rs new file mode 100644 index 00000000000..673b3649191 --- /dev/null +++ b/src/rustc/middle/ast_map.rs @@ -0,0 +1,152 @@ +import std::map; +import syntax::ast::*; +import syntax::ast_util; +import syntax::ast_util::inlined_item_methods; +import syntax::{visit, codemap}; + +enum path_elt { path_mod(str), path_name(str) } +type path = [path_elt]; + +fn path_to_str_with_sep(p: path, sep: str) -> str { + let strs = vec::map(p) {|e| + alt e { + path_mod(s) { s } + path_name(s) { s } + } + }; + str::connect(strs, sep) +} + +fn path_to_str(p: path) -> str { + path_to_str_with_sep(p, "::") +} + +enum ast_node { + node_item(@item, @path), + node_native_item(@native_item, @path), + node_method(@method, def_id, @path), + node_variant(variant, def_id, @path), + node_expr(@expr), + // Locals are numbered, because the alias analysis needs to know in which + // order they are introduced. + node_arg(arg, uint), + node_local(uint), + node_res_ctor(@item), +} + +type map = std::map::map<node_id, ast_node>; +type ctx = {map: map, mutable path: path, mutable local_id: uint}; +type vt = visit::vt<ctx>; + +fn mk_ast_map_visitor() -> vt { + ret visit::mk_vt(@{ + visit_item: map_item, + visit_native_item: map_native_item, + visit_expr: map_expr, + visit_fn: map_fn, + visit_local: map_local, + visit_arm: map_arm + with *visit::default_visitor() + }); +} + +fn map_crate(c: crate) -> map { + let cx = {map: std::map::new_int_hash(), + mutable path: [], + mutable local_id: 0u}; + visit::visit_crate(c, cx, mk_ast_map_visitor()); + ret cx.map; +} + +// Used for items loaded from external crate that are being inlined into this +// crate: +fn map_decoded_item(map: map, path: path, ii: inlined_item) { + // I believe it is ok for the local IDs of inlined items from other crates + // to overlap with the local ids from this crate, so just generate the ids + // starting from 0. (In particular, I think these ids are only used in + // alias analysis, which we will not be running on the inlined items, and + // even if we did I think it only needs an ordering between local + // variables that are simultaneously in scope). + let cx = {map: map, + mutable path: path, + mutable local_id: 0u}; + let v = mk_ast_map_visitor(); + ii.accept(cx, v); +} + +fn map_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, + sp: codemap::span, id: node_id, cx: ctx, v: vt) { + for a in decl.inputs { + cx.map.insert(a.id, node_arg(a, cx.local_id)); + cx.local_id += 1u; + } + visit::visit_fn(fk, decl, body, sp, id, cx, v); +} + +fn number_pat(cx: ctx, pat: @pat) { + pat_util::walk_pat(pat) {|p| + alt p.node { + pat_ident(_, _) { + cx.map.insert(p.id, node_local(cx.local_id)); + cx.local_id += 1u; + } + _ {} + } + }; +} + +fn map_local(loc: @local, cx: ctx, v: vt) { + number_pat(cx, loc.node.pat); + visit::visit_local(loc, cx, v); +} + +fn map_arm(arm: arm, cx: ctx, v: vt) { + number_pat(cx, arm.pats[0]); + visit::visit_arm(arm, cx, v); +} + +fn map_item(i: @item, cx: ctx, v: vt) { + cx.map.insert(i.id, node_item(i, @cx.path)); + alt i.node { + item_impl(_, _, _, ms) { + let implid = ast_util::local_def(i.id); + for m in ms { cx.map.insert(m.id, node_method(m, implid, @cx.path)); } + } + item_res(_, _, _, dtor_id, ctor_id) { + cx.map.insert(ctor_id, node_res_ctor(i)); + cx.map.insert(dtor_id, node_item(i, @cx.path)); + } + item_enum(vs, _) { + for v in vs { + cx.map.insert(v.node.id, node_variant( + v, ast_util::local_def(i.id), + @(cx.path + [path_name(i.ident)]))); + } + } + _ { } + } + alt i.node { + item_mod(_) | item_native_mod(_) { cx.path += [path_mod(i.ident)]; } + _ { cx.path += [path_name(i.ident)]; } + } + visit::visit_item(i, cx, v); + vec::pop(cx.path); +} + +fn map_native_item(i: @native_item, cx: ctx, v: vt) { + cx.map.insert(i.id, node_native_item(i, @cx.path)); + visit::visit_native_item(i, cx, v); +} + +fn map_expr(ex: @expr, cx: ctx, v: vt) { + cx.map.insert(ex.id, node_expr(ex)); + visit::visit_expr(ex, cx, v); +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/block_use.rs b/src/rustc/middle/block_use.rs new file mode 100644 index 00000000000..f21dcd5cc40 --- /dev/null +++ b/src/rustc/middle/block_use.rs @@ -0,0 +1,42 @@ +import syntax::visit; +import syntax::ast::*; +import driver::session::session; + +type ctx = {tcx: ty::ctxt, mutable allow_block: bool}; + +fn check_crate(tcx: ty::ctxt, crate: @crate) { + let cx = {tcx: tcx, mutable allow_block: false}; + let v = visit::mk_vt(@{visit_expr: visit_expr + with *visit::default_visitor()}); + visit::visit_crate(*crate, cx, v); +} + +fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) { + if !cx.allow_block { + alt ty::get(ty::expr_ty(cx.tcx, ex)).struct { + ty::ty_fn({proto: p, _}) if is_blockish(p) { + cx.tcx.sess.span_err(ex.span, "expressions with block type \ + can only appear in callee or (by-ref) argument position"); + } + _ {} + } + } + let outer = cx.allow_block; + alt ex.node { + expr_call(f, args, _) { + cx.allow_block = true; + v.visit_expr(f, cx, v); + let i = 0u; + for arg_t in ty::ty_fn_args(ty::expr_ty(cx.tcx, f)) { + cx.allow_block = (ty::arg_mode(cx.tcx, arg_t) == by_ref); + v.visit_expr(args[i], cx, v); + i += 1u; + } + } + _ { + cx.allow_block = false; + visit::visit_expr(ex, cx, v); + } + } + cx.allow_block = outer; +} diff --git a/src/rustc/middle/capture.rs b/src/rustc/middle/capture.rs new file mode 100644 index 00000000000..c32623b30b1 --- /dev/null +++ b/src/rustc/middle/capture.rs @@ -0,0 +1,133 @@ +import syntax::{ast, ast_util}; +import driver::session::session; +import std::map; + +export capture_mode; +export capture_var; +export capture_map; +export check_capture_clause; +export compute_capture_vars; +export cap_copy; +export cap_move; +export cap_drop; +export cap_ref; + +enum capture_mode { + cap_copy, //< Copy the value into the closure. + cap_move, //< Move the value into the closure. + cap_drop, //< Drop value after creating closure. + cap_ref, //< Reference directly from parent stack frame (block fn). +} + +type capture_var = { + def: ast::def, //< The variable being accessed free. + mode: capture_mode //< How is the variable being accessed. +}; + +type capture_map = map::hashmap<ast::def_id, capture_var>; + +// checks the capture clause for a fn_expr() and issues warnings or +// errors for any irregularities which we identify. +fn check_capture_clause(tcx: ty::ctxt, + fn_expr_id: ast::node_id, + fn_proto: ast::proto, + cap_clause: ast::capture_clause) { + let freevars = freevars::get_freevars(tcx, fn_expr_id); + let seen_defs = map::new_int_hash(); + + let check_capture_item = fn@(&&cap_item: @ast::capture_item) { + let cap_def = tcx.def_map.get(cap_item.id); + if !vec::any(*freevars, {|fv| fv.def == cap_def}) { + tcx.sess.span_warn( + cap_item.span, + #fmt("Captured variable '%s' not used in closure", + cap_item.name)); + } + + let cap_def_id = ast_util::def_id_of_def(cap_def).node; + if !seen_defs.insert(cap_def_id, ()) { + tcx.sess.span_err( + cap_item.span, + #fmt("Variable '%s' captured more than once", + cap_item.name)); + } + }; + + let check_not_upvar = fn@(&&cap_item: @ast::capture_item) { + alt tcx.def_map.get(cap_item.id) { + ast::def_upvar(_, _, _) { + tcx.sess.span_err( + cap_item.span, + #fmt("Upvars (like '%s') cannot be moved into a closure", + cap_item.name)); + } + _ {} + } + }; + + let check_block_captures = fn@(v: [@ast::capture_item]) { + if check vec::is_not_empty(v) { + let cap_item0 = vec::head(v); + tcx.sess.span_err( + cap_item0.span, + "Cannot capture values explicitly with a block closure"); + } + }; + + alt fn_proto { + ast::proto_any | ast::proto_block { + check_block_captures(cap_clause.copies); + check_block_captures(cap_clause.moves); + } + ast::proto_bare | ast::proto_box | ast::proto_uniq { + vec::iter(cap_clause.copies, check_capture_item); + vec::iter(cap_clause.moves, check_capture_item); + vec::iter(cap_clause.moves, check_not_upvar); + } + } +} + +fn compute_capture_vars(tcx: ty::ctxt, + fn_expr_id: ast::node_id, + fn_proto: ast::proto, + cap_clause: ast::capture_clause) -> [capture_var] { + let freevars = freevars::get_freevars(tcx, fn_expr_id); + let cap_map = map::new_int_hash(); + + vec::iter(cap_clause.copies) { |cap_item| + let cap_def = tcx.def_map.get(cap_item.id); + let cap_def_id = ast_util::def_id_of_def(cap_def).node; + if vec::any(*freevars, {|fv| fv.def == cap_def}) { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_copy }); + } + } + + vec::iter(cap_clause.moves) { |cap_item| + let cap_def = tcx.def_map.get(cap_item.id); + let cap_def_id = ast_util::def_id_of_def(cap_def).node; + if vec::any(*freevars, {|fv| fv.def == cap_def}) { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_move }); + } else { + cap_map.insert(cap_def_id, { def:cap_def, mode:cap_drop }); + } + } + + let implicit_mode = alt fn_proto { + ast::proto_any | ast::proto_block { cap_ref } + ast::proto_bare | ast::proto_box | ast::proto_uniq { cap_copy } + }; + + vec::iter(*freevars) { |fvar| + let fvar_def_id = ast_util::def_id_of_def(fvar.def).node; + alt cap_map.find(fvar_def_id) { + option::some(_) { /* was explicitly named, do nothing */ } + option::none { + cap_map.insert(fvar_def_id, {def:fvar.def, mode:implicit_mode}); + } + } + } + + let result = []; + cap_map.values { |cap_var| result += [cap_var]; } + ret result; +} diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs new file mode 100644 index 00000000000..6d55aed095e --- /dev/null +++ b/src/rustc/middle/check_alt.rs @@ -0,0 +1,325 @@ + +import syntax::ast::*; +import syntax::ast_util::{variant_def_ids, dummy_sp, compare_lit_exprs, + lit_expr_eq, unguarded_pat}; +import syntax::codemap::span; +import pat_util::*; +import syntax::visit; +import driver::session::session; +import middle::ty; +import middle::ty::*; + +fn check_crate(tcx: ty::ctxt, crate: @crate) { + visit::visit_crate(*crate, (), visit::mk_vt(@{ + visit_expr: bind check_expr(tcx, _, _, _), + visit_local: bind check_local(tcx, _, _, _) + with *visit::default_visitor::<()>() + })); + tcx.sess.abort_if_errors(); +} + +fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) { + visit::visit_expr(ex, s, v); + alt ex.node { + expr_alt(scrut, arms, mode) { + check_arms(tcx, arms); + /* Check for exhaustiveness */ + if mode == alt_exhaustive { + let arms = vec::concat(vec::filter_map(arms, unguarded_pat)); + check_exhaustive(tcx, ex.span, arms); + } + } + _ { } + } +} + +fn check_arms(tcx: ty::ctxt, arms: [arm]) { + let i = 0; + /* Check for unreachable patterns */ + for arm: arm in arms { + for arm_pat: @pat in arm.pats { + let reachable = true; + let j = 0; + while j < i { + if option::is_none(arms[j].guard) { + for prev_pat: @pat in arms[j].pats { + if pattern_supersedes(tcx, prev_pat, arm_pat) { + reachable = false; + } + } + } + j += 1; + } + if !reachable { + tcx.sess.span_err(arm_pat.span, "unreachable pattern"); + } + } + i += 1; + } +} + +fn raw_pat(p: @pat) -> @pat { + alt p.node { + pat_ident(_, some(s)) { raw_pat(s) } + _ { p } + } +} + +fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: [@pat]) { + if pats.len() == 0u { + tcx.sess.span_err(sp, "non-exhaustive patterns"); + ret; + } + // If there a non-refutable pattern in the set, we're okay. + for pat in pats { if !is_refutable(tcx, pat) { ret; } } + + alt ty::get(ty::node_id_to_type(tcx, pats[0].id)).struct { + ty::ty_enum(id, _) { + check_exhaustive_enum(tcx, id, sp, pats); + } + ty::ty_box(_) { + check_exhaustive(tcx, sp, vec::filter_map(pats, {|p| + alt raw_pat(p).node { pat_box(sub) { some(sub) } _ { none } } + })); + } + ty::ty_uniq(_) { + check_exhaustive(tcx, sp, vec::filter_map(pats, {|p| + alt raw_pat(p).node { pat_uniq(sub) { some(sub) } _ { none } } + })); + } + ty::ty_tup(ts) { + let cols = vec::to_mut(vec::init_elt(ts.len(), [])); + for p in pats { + alt raw_pat(p).node { + pat_tup(sub) { + vec::iteri(sub) {|i, sp| cols[i] += [sp];} + } + _ {} + } + } + vec::iter(cols) {|col| check_exhaustive(tcx, sp, col); } + } + ty::ty_rec(fs) { + let cols = vec::init_elt(fs.len(), {mutable wild: false, + mutable pats: []}); + for p in pats { + alt raw_pat(p).node { + pat_rec(sub, _) { + vec::iteri(fs) {|i, field| + alt vec::find(sub, {|pf| pf.ident == field.ident }) { + some(pf) { cols[i].pats += [pf.pat]; } + none { cols[i].wild = true; } + } + } + } + _ {} + } + } + vec::iter(cols) {|col| + if !col.wild { check_exhaustive(tcx, sp, copy col.pats); } + } + } + ty::ty_bool { + let saw_true = false, saw_false = false; + for p in pats { + alt raw_pat(p).node { + pat_lit(@{node: expr_lit(@{node: lit_bool(b), _}), _}) { + if b { saw_true = true; } + else { saw_false = true; } + } + _ {} + } + } + if !saw_true { tcx.sess.span_err( + sp, "non-exhaustive bool patterns: true not covered"); } + if !saw_false { tcx.sess.span_err( + sp, "non-exhaustive bool patterns: false not covered"); } + } + ty::ty_nil { + let seen = vec::any(pats, {|p| + alt raw_pat(p).node { + pat_lit(@{node: expr_lit(@{node: lit_nil, _}), _}) { true } + _ { false } + } + }); + if !seen { tcx.sess.span_err(sp, "non-exhaustive patterns"); } + } + // Literal patterns are always considered non-exhaustive + _ { + tcx.sess.span_err(sp, "non-exhaustive literal patterns"); + } + } +} + +fn check_exhaustive_enum(tcx: ty::ctxt, enum_id: def_id, sp: span, + pats: [@pat]) { + let variants = enum_variants(tcx, enum_id); + let columns_by_variant = vec::map(*variants, {|v| + {mutable seen: false, + cols: vec::to_mut(vec::init_elt(v.args.len(), []))} + }); + + for pat in pats { + let pat = raw_pat(pat); + alt tcx.def_map.get(pat.id) { + def_variant(_, id) { + let variant_idx = + option::get(vec::position(*variants, {|v| v.id == id})); + columns_by_variant[variant_idx].seen = true; + alt pat.node { + pat_enum(_, args) { + vec::iteri(args) {|i, p| + columns_by_variant[variant_idx].cols[i] += [p]; + } + } + _ {} + } + } + _ {} + } + } + + vec::iteri(columns_by_variant) {|i, cv| + if !cv.seen { + tcx.sess.span_err(sp, "non-exhaustive patterns: variant `" + + variants[i].name + "` not covered"); + } else { + vec::iter(cv.cols) {|col| check_exhaustive(tcx, sp, col); } + } + } +} + +fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool { + fn patterns_supersede(tcx: ty::ctxt, as: [@pat], bs: [@pat]) -> bool { + let i = 0; + for a: @pat in as { + if !pattern_supersedes(tcx, a, bs[i]) { ret false; } + i += 1; + } + ret true; + } + fn field_patterns_supersede(tcx: ty::ctxt, fas: [field_pat], + fbs: [field_pat]) -> bool { + let wild = @{id: 0, node: pat_wild, span: dummy_sp()}; + for fa: field_pat in fas { + let pb = wild; + for fb: field_pat in fbs { + if fa.ident == fb.ident { pb = fb.pat; } + } + if !pattern_supersedes(tcx, fa.pat, pb) { ret false; } + } + ret true; + } + + alt a.node { + pat_ident(_, some(p)) { pattern_supersedes(tcx, p, b) } + pat_wild { true } + pat_ident(_, none) { + let opt_def_a = tcx.def_map.find(a.id); + alt opt_def_a { + some(def_variant(_, _)) { opt_def_a == tcx.def_map.find(b.id) } + // This is a binding + _ { true } + } + } + pat_enum(va, suba) { + alt b.node { + pat_enum(vb, subb) { + tcx.def_map.get(a.id) == tcx.def_map.get(b.id) && + patterns_supersede(tcx, suba, subb) + } + _ { false } + } + } + pat_rec(suba, _) { + alt b.node { + pat_rec(subb, _) { field_patterns_supersede(tcx, suba, subb) } + _ { false } + } + } + pat_tup(suba) { + alt b.node { + pat_tup(subb) { patterns_supersede(tcx, suba, subb) } + _ { false } + } + } + pat_box(suba) { + alt b.node { + pat_box(subb) { pattern_supersedes(tcx, suba, subb) } + _ { pattern_supersedes(tcx, suba, b) } + } + } + pat_uniq(suba) { + alt b.node { + pat_uniq(subb) { pattern_supersedes(tcx, suba, subb) } + _ { pattern_supersedes(tcx, suba, b) } + } + } + pat_lit(la) { + alt b.node { + pat_lit(lb) { lit_expr_eq(la, lb) } + _ { false } + } + } + pat_range(begina, enda) { + alt b.node { + pat_lit(lb) { + compare_lit_exprs(begina, lb) <= 0 && + compare_lit_exprs(enda, lb) >= 0 + } + pat_range(beginb, endb) { + compare_lit_exprs(begina, beginb) <= 0 && + compare_lit_exprs(enda, endb) >= 0 + } + _ { false } + } + } + } +} + +fn check_local(tcx: ty::ctxt, loc: @local, &&s: (), v: visit::vt<()>) { + visit::visit_local(loc, s, v); + if is_refutable(tcx, loc.node.pat) { + tcx.sess.span_err(loc.node.pat.span, + "refutable pattern in local binding"); + } +} + +fn is_refutable(tcx: ty::ctxt, pat: @pat) -> bool { + alt tcx.def_map.find(pat.id) { + some(def_variant(enum_id, var_id)) { + if vec::len(*ty::enum_variants(tcx, enum_id)) != 1u { ret true; } + } + _ {} + } + + alt pat.node { + pat_box(sub) | pat_uniq(sub) | pat_ident(_, some(sub)) { + is_refutable(tcx, sub) + } + pat_wild | pat_ident(_, none) { false } + pat_lit(_) | pat_range(_, _) { true } + pat_rec(fields, _) { + for it: field_pat in fields { + if is_refutable(tcx, it.pat) { ret true; } + } + false + } + pat_tup(elts) { + for elt in elts { if is_refutable(tcx, elt) { ret true; } } + false + } + pat_enum(_, args) { + for p: @pat in args { if is_refutable(tcx, p) { ret true; } } + false + } + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs new file mode 100644 index 00000000000..d04abffb320 --- /dev/null +++ b/src/rustc/middle/check_const.rs @@ -0,0 +1,98 @@ +import syntax::ast::*; +import syntax::{visit, ast_util}; +import driver::session::session; + +fn check_crate(sess: session, crate: @crate, method_map: typeck::method_map) { + visit::visit_crate(*crate, false, visit::mk_vt(@{ + visit_item: check_item, + visit_pat: check_pat, + visit_expr: bind check_expr(sess, method_map, _, _, _) + with *visit::default_visitor() + })); + sess.abort_if_errors(); +} + +fn check_item(it: @item, &&_is_const: bool, v: visit::vt<bool>) { + alt it.node { + item_const(_, ex) { v.visit_expr(ex, true, v); } + item_enum(vs, _) { + for var in vs { + option::may(var.node.disr_expr) {|ex| + v.visit_expr(ex, true, v); + } + } + } + _ { visit::visit_item(it, false, v); } + } +} + +fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) { + fn is_str(e: @expr) -> bool { + alt e.node { expr_lit(@{node: lit_str(_), _}) { true } _ { false } } + } + alt p.node { + // Let through plain string literals here + pat_lit(a) { if !is_str(a) { v.visit_expr(a, true, v); } } + pat_range(a, b) { + if !is_str(a) { v.visit_expr(a, true, v); } + if !is_str(b) { v.visit_expr(b, true, v); } + } + _ { visit::visit_pat(p, false, v); } + } +} + +fn check_expr(sess: session, method_map: typeck::method_map, e: @expr, + &&is_const: bool, v: visit::vt<bool>) { + if is_const { + alt e.node { + expr_unary(box(_), _) | expr_unary(uniq(_), _) | + expr_unary(deref, _){ + sess.span_err(e.span, + "disallowed operator in constant expression"); + ret; + } + expr_lit(@{node: lit_str(_), _}) { + sess.span_err(e.span, + "string constants are not supported"); + } + expr_binary(_, _, _) | expr_unary(_, _) { + if method_map.contains_key(e.id) { + sess.span_err(e.span, "user-defined operators are not \ + allowed in constant expressions"); + } + } + expr_lit(_) {} + _ { + sess.span_err(e.span, + "constant contains unimplemented expression type"); + ret; + } + } + } + alt e.node { + expr_lit(@{node: lit_int(v, t), _}) { + if t != ty_char { + if (v as u64) > ast_util::int_ty_max( + if t == ty_i { sess.targ_cfg.int_type } else { t }) { + sess.span_err(e.span, "literal out of range for its type"); + } + } + } + expr_lit(@{node: lit_uint(v, t), _}) { + if v > ast_util::uint_ty_max( + if t == ty_u { sess.targ_cfg.uint_type } else { t }) { + sess.span_err(e.span, "literal out of range for its type"); + } + } + _ {} + } + visit::visit_expr(e, is_const, v); +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/fn_usage.rs b/src/rustc/middle/fn_usage.rs new file mode 100644 index 00000000000..c415f5deb05 --- /dev/null +++ b/src/rustc/middle/fn_usage.rs @@ -0,0 +1,94 @@ +import syntax::ast; +import syntax::visit; +import syntax::print::pprust::expr_to_str; +import driver::session::session; + +export check_crate_fn_usage; + +type fn_usage_ctx = { + tcx: ty::ctxt, + unsafe_fn_legal: bool, + generic_bare_fn_legal: bool +}; + +fn fn_usage_expr(expr: @ast::expr, + ctx: fn_usage_ctx, + v: visit::vt<fn_usage_ctx>) { + alt expr.node { + ast::expr_path(path) { + if !ctx.unsafe_fn_legal { + alt ctx.tcx.def_map.find(expr.id) { + some(ast::def_fn(_, ast::unsafe_fn)) { + log(error, ("expr=", expr_to_str(expr))); + ctx.tcx.sess.span_fatal( + expr.span, + "unsafe functions can only be called"); + } + + _ {} + } + } + if !ctx.generic_bare_fn_legal + && ty::expr_has_ty_params(ctx.tcx, expr) { + alt ty::get(ty::expr_ty(ctx.tcx, expr)).struct { + ty::ty_fn({proto: ast::proto_bare, _}) { + ctx.tcx.sess.span_fatal( + expr.span, + "generic bare functions can only be called or bound"); + } + _ { } + } + } + } + + ast::expr_call(f, args, _) { + let f_ctx = {unsafe_fn_legal: true, + generic_bare_fn_legal: true with ctx}; + v.visit_expr(f, f_ctx, v); + + let args_ctx = {unsafe_fn_legal: false, + generic_bare_fn_legal: false with ctx}; + visit::visit_exprs(args, args_ctx, v); + } + + ast::expr_bind(f, args) { + let f_ctx = {unsafe_fn_legal: false, + generic_bare_fn_legal: true with ctx}; + v.visit_expr(f, f_ctx, v); + + let args_ctx = {unsafe_fn_legal: false, + generic_bare_fn_legal: false with ctx}; + for arg in args { + visit::visit_expr_opt(arg, args_ctx, v); + } + } + + _ { + let subctx = {unsafe_fn_legal: false, + generic_bare_fn_legal: false with ctx}; + visit::visit_expr(expr, subctx, v); + } + } +} + +fn check_crate_fn_usage(tcx: ty::ctxt, crate: @ast::crate) { + let visit = + visit::mk_vt( + @{visit_expr: fn_usage_expr + with *visit::default_visitor()}); + let ctx = { + tcx: tcx, + unsafe_fn_legal: false, + generic_bare_fn_legal: false + }; + visit::visit_crate(*crate, ctx, visit); +} + + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/freevars.rs b/src/rustc/middle/freevars.rs new file mode 100644 index 00000000000..65a4ae627ea --- /dev/null +++ b/src/rustc/middle/freevars.rs @@ -0,0 +1,121 @@ +// A pass that annotates for each loops and functions with the free +// variables that they contain. + +import syntax::print::pprust::path_to_str; +import std::map::*; +import option::*; +import syntax::{ast, ast_util, visit}; +import middle::resolve; +import syntax::codemap::span; + +export annotate_freevars; +export freevar_map; +export freevar_info; +export freevar_entry; +export get_freevars; +export has_freevars; + +// A vector of defs representing the free variables referred to in a function. +// (The def_upvar will already have been stripped). +type freevar_entry = { + def: ast::def, //< The variable being accessed free. + span: span //< First span where it is accessed (there can be multiple) +}; +type freevar_info = @[@freevar_entry]; +type freevar_map = hashmap<ast::node_id, freevar_info>; + +// Searches through part of the AST for all references to locals or +// upvars in this frame and returns the list of definition IDs thus found. +// Since we want to be able to collect upvars in some arbitrary piece +// of the AST, we take a walker function that we invoke with a visitor +// in order to start the search. +fn collect_freevars(def_map: resolve::def_map, blk: ast::blk) + -> freevar_info { + let seen = new_int_hash(); + let refs = @mutable []; + + fn ignore_item(_i: @ast::item, &&_depth: int, _v: visit::vt<int>) { } + + let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) { + alt expr.node { + ast::expr_fn(proto, decl, _, captures) { + if proto != ast::proto_bare { + visit::visit_expr(expr, depth + 1, v); + } + } + ast::expr_fn_block(_, _) { + visit::visit_expr(expr, depth + 1, v); + } + ast::expr_path(path) { + let i = 0; + alt def_map.find(expr.id) { + none { fail ("Not found: " + path_to_str(path)) } + some(df) { + let def = df; + while i < depth { + alt copy def { + ast::def_upvar(_, inner, _) { def = *inner; } + _ { break; } + } + i += 1; + } + if i == depth { // Made it to end of loop + let dnum = ast_util::def_id_of_def(def).node; + if !seen.contains_key(dnum) { + *refs += [@{def:def, span:expr.span}]; + seen.insert(dnum, ()); + } + } + } + } + } + _ { visit::visit_expr(expr, depth, v); } + } + }; + + let v = visit::mk_vt(@{visit_item: ignore_item, visit_expr: walk_expr + with *visit::default_visitor()}); + v.visit_block(blk, 1, v); + ret @*refs; +} + +// Build a map from every function and for-each body to a set of the +// freevars contained in it. The implementation is not particularly +// efficient as it fully recomputes the free variables at every +// node of interest rather than building up the free variables in +// one pass. This could be improved upon if it turns out to matter. +fn annotate_freevars(def_map: resolve::def_map, crate: @ast::crate) -> + freevar_map { + let freevars = new_int_hash(); + + let walk_fn = fn@(_fk: visit::fn_kind, _decl: ast::fn_decl, + blk: ast::blk, _sp: span, nid: ast::node_id) { + let vars = collect_freevars(def_map, blk); + freevars.insert(nid, vars); + }; + + let visitor = + visit::mk_simple_visitor(@{visit_fn: walk_fn + with *visit::default_simple_visitor()}); + visit::visit_crate(*crate, (), visitor); + + ret freevars; +} + +fn get_freevars(tcx: ty::ctxt, fid: ast::node_id) -> freevar_info { + alt tcx.freevars.find(fid) { + none { fail "get_freevars: " + int::str(fid) + " has no freevars"; } + some(d) { ret d; } + } +} +fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool { + ret vec::len(*get_freevars(tcx, fid)) != 0u; +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/inline.rs b/src/rustc/middle/inline.rs new file mode 100644 index 00000000000..91d69168faf --- /dev/null +++ b/src/rustc/middle/inline.rs @@ -0,0 +1,100 @@ +import std::map::hashmap; +import syntax::ast; +import syntax::ast_util; +import syntax::ast_util::inlined_item_methods; +import syntax::visit; +import middle::typeck::method_map; +import middle::trans::common::maps; +import metadata::csearch; + +export inline_map; +export instantiate_inlines; + +type inline_map = hashmap<ast::def_id, ast::inlined_item>; + +enum ctxt = { + tcx: ty::ctxt, + maps: maps, + inline_map: inline_map, + mut to_process: [ast::inlined_item] +}; + +fn instantiate_inlines(enabled: bool, + tcx: ty::ctxt, + maps: maps, + crate: @ast::crate) -> inline_map { + let vt = visit::mk_vt(@{ + visit_expr: fn@(e: @ast::expr, cx: ctxt, vt: visit::vt<ctxt>) { + visit::visit_expr(e, cx, vt); + cx.visit_expr(e); + } + with *visit::default_visitor::<ctxt>() + }); + let inline_map = ast_util::new_def_id_hash(); + let cx = ctxt({tcx: tcx, maps: maps, + inline_map: inline_map, mutable to_process: []}); + if enabled { visit::visit_crate(*crate, cx, vt); } + while !vec::is_empty(cx.to_process) { + let to_process = []; + to_process <-> cx.to_process; + #debug["Recursively looking at inlined items"]; + vec::iter(to_process) {|ii| + ii.accept(cx, vt); + } + } + ret inline_map; +} + +impl methods for ctxt { + fn visit_expr(e: @ast::expr) { + + // Look for fn items or methods that are referenced which + // ought to be inlined. + + alt e.node { + ast::expr_path(_) { + alt self.tcx.def_map.get(e.id) { + ast::def_fn(did, _) { + self.maybe_enqueue_fn(did); + } + _ { /* not a fn item, fallthrough */ } + } + } + ast::expr_field(_, _, _) { + alt self.maps.method_map.find(e.id) { + some(origin) { + self.maybe_enqueue_impl_method(origin); + } + _ { /* not an impl method, fallthrough */ } + } + } + _ { /* fallthrough */ } + } + } + + fn maybe_enqueue_fn(did: ast::def_id) { + if did.crate == ast::local_crate { ret; } + if self.inline_map.contains_key(did) { ret; } + alt csearch::maybe_get_item_ast(self.tcx, self.maps, did) { + none { + /* no AST attached, do not inline */ + #debug["No AST attached to def %s", + ty::item_path_str(self.tcx, did)]; + } + some(ii) { /* Found an AST, add to table: */ + #debug["Inlining def %s", ty::item_path_str(self.tcx, did)]; + self.to_process += [ii]; + self.inline_map.insert(did, ii); + } + } + } + + fn maybe_enqueue_impl_method(method_origin: typeck::method_origin) { + alt method_origin { + typeck::method_static(did) { self.maybe_enqueue_fn(did); } + typeck::method_param(_, _, _, _) | typeck::method_iface(_, _) { + /* fallthrough */ + } + } + } +} diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs new file mode 100644 index 00000000000..ffe2873a3b7 --- /dev/null +++ b/src/rustc/middle/kind.rs @@ -0,0 +1,282 @@ +import syntax::{visit, ast_util}; +import syntax::ast::*; +import syntax::codemap::span; +import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable}; +import driver::session::session; + +// Kind analysis pass. There are three kinds: +// +// sendable: scalar types, and unique types containing only sendable types +// copyable: boxes, closures, and uniques containing copyable types +// noncopyable: resources, or unique types containing resources +// +// This pass ensures that type parameters are only instantiated with types +// whose kinds are equal or less general than the way the type parameter was +// annotated (with the `send` or `copy` keyword). +// +// It also verifies that noncopyable kinds are not copied. Sendability is not +// applied, since none of our language primitives send. Instead, the sending +// primitives in the stdlib are explicitly annotated to only take sendable +// types. + +fn kind_to_str(k: kind) -> str { + alt k { + kind_sendable { "sendable" } + kind_copyable { "copyable" } + kind_noncopyable { "noncopyable" } + } +} + +type rval_map = std::map::hashmap<node_id, ()>; + +type ctx = {tcx: ty::ctxt, + rval_map: rval_map, + method_map: typeck::method_map, + last_uses: last_use::last_uses}; + +fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map, + last_uses: last_use::last_uses, crate: @crate) + -> rval_map { + let ctx = {tcx: tcx, + rval_map: std::map::new_int_hash(), + method_map: method_map, + last_uses: last_uses}; + let visit = visit::mk_vt(@{ + visit_expr: check_expr, + visit_stmt: check_stmt, + visit_block: check_block, + visit_fn: check_fn + with *visit::default_visitor() + }); + visit::visit_crate(*crate, ctx, visit); + tcx.sess.abort_if_errors(); + ret ctx.rval_map; +} + +// Yields the appropriate function to check the kind of closed over +// variables. `id` is the node_id for some expression that creates the +// closure. +fn with_appropriate_checker(cx: ctx, id: node_id, + b: fn(fn@(ctx, ty::t, sp: span))) { + let fty = ty::node_id_to_type(cx.tcx, id); + alt ty::ty_fn_proto(fty) { + proto_uniq { b(check_send); } + proto_box { b(check_copy); } + proto_bare { b(check_none); } + proto_any | proto_block { /* no check needed */ } + } +} + +// Check that the free variables used in a shared/sendable closure conform +// to the copy/move kind bounds. Then recursively check the function body. +fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span, + fn_id: node_id, cx: ctx, v: visit::vt<ctx>) { + + // n.b.: This could be the body of either a fn decl or a fn expr. In the + // former case, the prototype will be proto_bare and no check occurs. In + // the latter case, we do not check the variables that in the capture + // clause (as we don't have access to that here) but just those that + // appear free. The capture clauses are checked below, in check_expr(). + // + // We could do this check also in check_expr(), but it seems more + // "future-proof" to do it this way, as check_fn_body() is supposed to be + // the common flow point for all functions that appear in the AST. + + with_appropriate_checker(cx, fn_id) { |checker| + for @{def, span} in *freevars::get_freevars(cx.tcx, fn_id) { + let id = ast_util::def_id_of_def(def).node; + if checker == check_copy { + let last_uses = alt check cx.last_uses.find(fn_id) { + some(last_use::closes_over(vars)) { vars } + none { [] } + }; + if option::is_some(vec::position_elt(last_uses, id)) { cont; } + } + let ty = ty::node_id_to_type(cx.tcx, id); + checker(cx, ty, span); + } + } + + visit::visit_fn(fk, decl, body, sp, fn_id, cx, v); +} + +fn check_fn_cap_clause(cx: ctx, + id: node_id, + cap_clause: capture_clause) { + // Check that the variables named in the clause which are not free vars + // (if any) are also legal. freevars are checked above in check_fn(). + // This is kind of a degenerate case, as captured variables will generally + // appear free in the body. + let freevars = freevars::get_freevars(cx.tcx, id); + let freevar_ids = vec::map(*freevars, { |freevar| + ast_util::def_id_of_def(freevar.def).node + }); + //log("freevar_ids", freevar_ids); + with_appropriate_checker(cx, id) { |checker| + let check_var = fn@(&&cap_item: @capture_item) { + let cap_def = cx.tcx.def_map.get(cap_item.id); + let cap_def_id = ast_util::def_id_of_def(cap_def).node; + if !vec::contains(freevar_ids, cap_def_id) { + let ty = ty::node_id_to_type(cx.tcx, cap_def_id); + checker(cx, ty, cap_item.span); + } + }; + vec::iter(cap_clause.copies, check_var); + vec::iter(cap_clause.moves, check_var); + } +} + +fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) { + alt b.node.expr { + some(ex) { maybe_copy(cx, ex); } + _ {} + } + visit::visit_block(b, cx, v); +} + +fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) { + alt e.node { + expr_assign(_, ex) | expr_assign_op(_, _, ex) | + expr_unary(box(_), ex) | expr_unary(uniq(_), ex) | + expr_ret(some(ex)) { maybe_copy(cx, ex); } + expr_copy(expr) { check_copy_ex(cx, expr, false); } + // Vector add copies. + expr_binary(add, ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); } + expr_rec(fields, def) { + for field in fields { maybe_copy(cx, field.node.expr); } + alt def { + some(ex) { + // All noncopyable fields must be overridden + let t = ty::expr_ty(cx.tcx, ex); + let ty_fields = alt ty::get(t).struct { + ty::ty_rec(f) { f } + _ { cx.tcx.sess.span_bug(ex.span, "Bad expr type in record"); } + }; + for tf in ty_fields { + if !vec::any(fields, {|f| f.node.ident == tf.ident}) && + !ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) { + cx.tcx.sess.span_err(ex.span, + "copying a noncopyable value"); + } + } + } + _ {} + } + } + expr_tup(exprs) | expr_vec(exprs, _) { + for expr in exprs { maybe_copy(cx, expr); } + } + expr_bind(_, args) { + for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } } + } + expr_call(f, args, _) { + let i = 0u; + for arg_t in ty::ty_fn_args(ty::expr_ty(cx.tcx, f)) { + alt ty::arg_mode(cx.tcx, arg_t) { + by_copy { maybe_copy(cx, args[i]); } + by_ref | by_val | by_mutbl_ref | by_move { } + } + i += 1u; + } + } + expr_path(_) { + alt cx.tcx.node_type_substs.find(e.id) { + some(ts) { + let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id)); + let bounds = ty::lookup_item_type(cx.tcx, did).bounds; + let i = 0u; + for ty in ts { + let kind = ty::type_kind(cx.tcx, ty); + let p_kind = ty::param_bounds_to_kind(bounds[i]); + if !ty::kind_lteq(p_kind, kind) { + cx.tcx.sess.span_err(e.span, "instantiating a " + + kind_to_str(p_kind) + + " type parameter with a " + + kind_to_str(kind) + " type"); + } + i += 1u; + } + } + none {} + } + } + expr_fn(_, _, _, cap_clause) { + check_fn_cap_clause(cx, e.id, *cap_clause); + } + _ { } + } + visit::visit_expr(e, cx, v); +} + +fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) { + alt stmt.node { + stmt_decl(@{node: decl_local(locals), _}, _) { + for local in locals { + alt local.node.init { + some({op: init_assign, expr}) { maybe_copy(cx, expr); } + _ {} + } + } + } + _ {} + } + visit::visit_stmt(stmt, cx, v); +} + +fn maybe_copy(cx: ctx, ex: @expr) { + check_copy_ex(cx, ex, true); +} + +fn is_nullary_variant(cx: ctx, ex: @expr) -> bool { + alt ex.node { + expr_path(_) { + alt cx.tcx.def_map.get(ex.id) { + def_variant(edid, vdid) { + vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u + } + _ { false } + } + } + _ { false } + } +} + +fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) { + if ty::expr_is_lval(cx.method_map, ex) && + !cx.last_uses.contains_key(ex.id) && + !is_nullary_variant(cx, ex) { + let ty = ty::expr_ty(cx.tcx, ex); + check_copy(cx, ty, ex.span); + // FIXME turn this on again once vector types are no longer unique. + // Right now, it is too annoying to be useful. + /* if warn && ty::type_is_unique(ty) { + cx.tcx.sess.span_warn(ex.span, "copying a unique value"); + }*/ + } +} + +fn check_copy(cx: ctx, ty: ty::t, sp: span) { + if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) { + cx.tcx.sess.span_err(sp, "copying a noncopyable value"); + } +} + +fn check_send(cx: ctx, ty: ty::t, sp: span) { + if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) { + cx.tcx.sess.span_err(sp, "not a sendable value"); + } +} + +fn check_none(cx: ctx, _ty: ty::t, sp: span) { + cx.tcx.sess.span_err(sp, "attempted dynamic environment capture"); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/last_use.rs b/src/rustc/middle/last_use.rs new file mode 100644 index 00000000000..12ffdfccb41 --- /dev/null +++ b/src/rustc/middle/last_use.rs @@ -0,0 +1,373 @@ +import syntax::{visit, ast_util}; +import syntax::ast::*; +import syntax::codemap::span; +import std::list::{is_not_empty, list, nil, cons, tail}; +import std::util::unreachable; +import std::list; + +// Last use analysis pass. +// +// Finds the last read of each value stored in a local variable or +// callee-owned argument (arguments with by-move or by-copy passing +// style). This is a limited form of liveness analysis, peformed +// (perhaps foolishly) directly on the AST. +// +// The algorithm walks the AST, keeping a set of (def, last_use) +// pairs. When the function is exited, or the local is overwritten, +// the current set of last uses is marked with 'true' in a table. +// Other branches may later overwrite them with 'false' again, since +// they may find a use coming after them. (Marking an expression as a +// last use is only done if it has not already been marked with +// 'false'.) +// +// Some complexity is added to deal with joining control flow branches +// (by `break` or conditionals), and for handling loops. + +// Marks expr_paths that are last uses. +enum is_last_use { + is_last_use, + has_last_use, + closes_over([node_id]), +} +type last_uses = std::map::hashmap<node_id, is_last_use>; + +enum seen { unset, seen(node_id), } +enum block_type { func, loop, } + +enum use { var_use(node_id), close_over(node_id), } +type set = [{def: node_id, uses: list<use>}]; +type bl = @{type: block_type, mutable second: bool, mutable exits: [set]}; + +enum use_id { path(node_id), close(node_id, node_id) } +fn hash_use_id(id: use_id) -> uint { + (alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint +} + +type ctx = {last_uses: std::map::hashmap<use_id, bool>, + def_map: resolve::def_map, + ref_map: alias::ref_map, + tcx: ty::ctxt, + // The current set of local last uses + mutable current: set, + mutable blocks: list<bl>}; + +fn find_last_uses(c: @crate, def_map: resolve::def_map, + ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses { + let v = visit::mk_vt(@{visit_expr: visit_expr, + visit_stmt: visit_stmt, + visit_fn: visit_fn + with *visit::default_visitor()}); + let cx = {last_uses: std::map::mk_hashmap(hash_use_id, {|a, b| a == b}), + def_map: def_map, + ref_map: ref_map, + tcx: tcx, + mutable current: [], + mutable blocks: nil}; + visit::visit_crate(*c, cx, v); + let mini_table = std::map::new_int_hash(); + cx.last_uses.items {|key, val| + if !val { ret; } + alt key { + path(id) { + mini_table.insert(id, is_last_use); + let def_node = ast_util::def_id_of_def(def_map.get(id)).node; + mini_table.insert(def_node, has_last_use); + } + close(fn_id, local_id) { + mini_table.insert(local_id, has_last_use); + let known = alt check mini_table.find(fn_id) { + some(closes_over(ids)) { ids } + none { [] } + }; + mini_table.insert(fn_id, closes_over(known + [local_id])); + } + } + } + ret mini_table; +} + +fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) { + alt ex.node { + expr_ret(oexpr) { + visit::visit_expr_opt(oexpr, cx, v); + if !add_block_exit(cx, func) { leave_fn(cx); } + } + expr_fail(oexpr) { + visit::visit_expr_opt(oexpr, cx, v); + leave_fn(cx); + } + expr_break { add_block_exit(cx, loop); } + expr_while(_, _) | expr_do_while(_, _) { + visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);} + } + expr_for(_, coll, blk) { + v.visit_expr(coll, cx, v); + visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);} + } + expr_alt(input, arms, _) { + v.visit_expr(input, cx, v); + let before = cx.current, sets = []; + for arm in arms { + cx.current = before; + v.visit_arm(arm, cx, v); + sets += [cx.current]; + } + cx.current = join_branches(sets); + } + expr_if(cond, then, els) { + v.visit_expr(cond, cx, v); + let cur = cx.current; + visit::visit_block(then, cx, v); + cx.current <-> cur; + visit::visit_expr_opt(els, cx, v); + cx.current = join_branches([cur, cx.current]); + } + expr_path(_) { + let my_def = cx.def_map.get(ex.id); + let my_def_id = ast_util::def_id_of_def(my_def).node; + alt cx.ref_map.find(my_def_id) { + option::some(root_id) { + clear_in_current(cx, root_id, false); + } + _ { + option::may(def_is_owned_local(cx, my_def)) {|nid| + clear_in_current(cx, nid, false); + cx.current += [{def: nid, + uses: cons(var_use(ex.id), @nil)}]; + } + } + } + } + expr_swap(lhs, rhs) { + clear_if_path(cx, lhs, v, false); + clear_if_path(cx, rhs, v, false); + } + expr_move(dest, src) | expr_assign(dest, src) { + v.visit_expr(src, cx, v); + clear_if_path(cx, dest, v, true); + } + expr_assign_op(_, dest, src) { + v.visit_expr(src, cx, v); + v.visit_expr(dest, cx, v); + clear_if_path(cx, dest, v, true); + } + expr_fn(_, _, _, cap_clause) { + // n.b.: safe to ignore copies, as if they are unused + // then they are ignored, otherwise they will show up + // as freevars in the body. + vec::iter(cap_clause.moves) {|ci| + clear_def_if_local(cx, cx.def_map.get(ci.id), false); + } + visit::visit_expr(ex, cx, v); + } + expr_call(f, args, _) { + v.visit_expr(f, cx, v); + let i = 0u, fns = []; + let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f)); + for arg in args { + alt arg.node { + expr_fn(_, _, _, _) | expr_fn_block(_, _) + if is_blockish(ty::ty_fn_proto(arg_ts[i].ty)) { + fns += [arg]; + } + _ { + alt ty::arg_mode(cx.tcx, arg_ts[i]) { + by_mutbl_ref { clear_if_path(cx, arg, v, false); } + _ { v.visit_expr(arg, cx, v); } + } + } + } + i += 1u; + } + for f in fns { v.visit_expr(f, cx, v); } + } + _ { visit::visit_expr(ex, cx, v); } + } +} + +fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) { + alt s.node { + stmt_decl(@{node: decl_local(ls), _}, _) { + shadow_in_current(cx, {|id| + for local in ls { + let found = false; + pat_util::pat_bindings(cx.tcx.def_map, local.node.pat, + {|pid, _a, _b| + if pid == id { found = true; } + }); + if found { ret true; } + } + false + }); + } + _ {} + } + visit::visit_stmt(s, cx, v); +} + +fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, + sp: span, id: node_id, + cx: ctx, v: visit::vt<ctx>) { + let fty = ty::node_id_to_type(cx.tcx, id); + let proto = ty::ty_fn_proto(fty); + alt proto { + proto_any | proto_block { + visit_block(func, cx, {|| + shadow_in_current(cx, {|id| + for arg in decl.inputs { if arg.id == id { ret true; } } + false + }); + visit::visit_fn(fk, decl, body, sp, id, cx, v); + }); + } + proto_box | proto_uniq | proto_bare { + alt cx.tcx.freevars.find(id) { + some(vars) { + for v in *vars { + option::may(def_is_owned_local(cx, v.def)) {|nid| + clear_in_current(cx, nid, false); + cx.current += [{def: nid, + uses: cons(close_over(id), @nil)}]; + } + } + } + _ {} + } + let old_cur = [], old_blocks = nil; + cx.blocks <-> old_blocks; + cx.current <-> old_cur; + visit::visit_fn(fk, decl, body, sp, id, cx, v); + cx.blocks <-> old_blocks; + leave_fn(cx); + cx.current <-> old_cur; + } + } +} + +fn visit_block(tp: block_type, cx: ctx, visit: fn()) { + let local = @{type: tp, mutable second: false, mutable exits: []}; + cx.blocks = cons(local, @cx.blocks); + visit(); + local.second = true; + local.exits = []; + visit(); + let cx_blocks = cx.blocks; + cx.blocks = tail(cx_blocks); + local.exits += [cx.current]; + cx.current = join_branches(local.exits); +} + +fn add_block_exit(cx: ctx, tp: block_type) -> bool { + let cur = cx.blocks; + while cur != nil { + alt cur { + cons(b, tail) { + if (b.type == tp) { + if !b.second { b.exits += [cx.current]; } + ret true; + } + cur = *tail; + } + nil { + // typestate can't use the while loop condition -- + // *sigh* + unreachable(); + } + } + } + ret false; +} + +fn join_branches(branches: [set]) -> set { + let found: set = [], i = 0u, l = vec::len(branches); + for set in branches { + i += 1u; + for {def, uses} in set { + if !vec::any(found, {|v| v.def == def}) { + let j = i, nne = uses; + while j < l { + for {def: d2, uses} in branches[j] { + if d2 == def { + list::iter(uses) {|e| + if !list::has(nne, e) { nne = cons(e, @nne); } + } + } + } + j += 1u; + } + found += [{def: def, uses: nne}]; + } + } + } + ret found; +} + +fn leave_fn(cx: ctx) { + for {def, uses} in cx.current { + list::iter(uses) {|use| + let key = alt use { + var_use(pth_id) { path(pth_id) } + close_over(fn_id) { close(fn_id, def) } + }; + if !cx.last_uses.contains_key(key) { + cx.last_uses.insert(key, true); + } + } + } +} + +fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) { + let out = []; + cx.current <-> out; + for e in out { if !p(e.def) { cx.current += [e]; } } +} + +fn clear_in_current(cx: ctx, my_def: node_id, to: bool) { + for {def, uses} in cx.current { + if def == my_def { + list::iter(uses) {|use| + let key = alt use { + var_use(pth_id) { path(pth_id) } + close_over(fn_id) { close(fn_id, def) } + }; + if !to || !cx.last_uses.contains_key(key) { + cx.last_uses.insert(key, to); + } + } + cx.current = vec::filter(copy cx.current, {|x| x.def != my_def}); + break; + } + } +} + +fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> { + alt d { + def_local(id, _) { some(id) } + def_arg(id, m) { + alt ty::resolved_mode(cx.tcx, m) { + by_copy | by_move { some(id) } + by_ref | by_val | by_mutbl_ref { none } + } + } + def_upvar(_, d, fn_id) { + if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) { + def_is_owned_local(cx, *d) + } else { none } + } + _ { none } + } +} + +fn clear_def_if_local(cx: ctx, d: def, to: bool) { + alt def_is_owned_local(cx, d) { + some(nid) { clear_in_current(cx, nid, to); } + _ {} + } +} + +fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) { + alt ex.node { + expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); } + _ { v.visit_expr(ex, cx, v); } + } +} diff --git a/src/rustc/middle/lint.rs b/src/rustc/middle/lint.rs new file mode 100644 index 00000000000..f46a50ea4a1 --- /dev/null +++ b/src/rustc/middle/lint.rs @@ -0,0 +1,164 @@ +import driver::session::session; +import middle::ty::ctxt; +import syntax::{ast, visit}; +import front::attr; +import std::io; +import io::writer_util; + +enum option { + ctypes, +} + +impl opt_ for option { + fn desc() -> str { + "lint: " + alt self { + ctypes { "ctypes usage checking" } + } + } + fn run(tcx: ty::ctxt, crate: @ast::crate, time_pass: bool) { + let checker = alt self { + ctypes { + bind check_ctypes(tcx, crate) + } + }; + time(time_pass, self.desc(), checker); + } +} + +// FIXME: Copied from driver.rs, to work around a bug(#1566) +fn time(do_it: bool, what: str, thunk: fn()) { + if !do_it{ ret thunk(); } + let start = std::time::precise_time_s(); + thunk(); + let end = std::time::precise_time_s(); + io::stdout().write_str(#fmt("time: %3.3f s\t%s\n", + end - start, what)); +} + +// Merge lint options specified by crate attributes and rustc command +// line. Precedence: cmdline > attribute > default +fn merge_opts(attrs: [ast::attribute], cmd_opts: [(option, bool)]) -> + [(option, bool)] { + fn str_to_option(name: str) -> (option, bool) { + ret alt check name { + "ctypes" { (ctypes, true) } + "no_ctypes" { (ctypes, false) } + } + } + + fn meta_to_option(meta: @ast::meta_item) -> (option, bool) { + ret alt meta.node { + ast::meta_word(name) { + str_to_option(name) + } + _ { fail "meta_to_option: meta_list contains a non-meta-word"; } + }; + } + + fn default() -> [(option, bool)] { + [(ctypes, true)] + } + + fn contains(xs: [(option, bool)], x: option) -> bool { + for (o, _) in xs { + if o == x { ret true; } + } + ret false; + } + + let result = cmd_opts; + + let lint_metas = + attr::attr_metas(attr::find_attrs_by_name(attrs, "lint")); + + vec::iter(lint_metas) {|mi| + alt mi.node { + ast::meta_list(_, list) { + vec::iter(list) {|e| + let (o, v) = meta_to_option(e); + if !contains(cmd_opts, o) { + result += [(o, v)]; + } + } + } + _ { } + } + }; + + for (o, v) in default() { + if !contains(result, o) { + result += [(o, v)]; + } + } + + ret result; +} + +fn check_ctypes(tcx: ty::ctxt, crate: @ast::crate) { + fn check_native_fn(tcx: ty::ctxt, decl: ast::fn_decl) { + let tys = vec::map(decl.inputs) {|a| a.ty }; + for ty in (tys + [decl.output]) { + alt ty.node { + ast::ty_path(_, id) { + alt tcx.def_map.get(id) { + ast::def_prim_ty(ast::ty_int(ast::ty_i)) { + tcx.sess.span_warn( + ty.span, + "found rust type `int` in native module, while \ + ctypes::c_int or ctypes::long should be used"); + } + ast::def_prim_ty(ast::ty_uint(ast::ty_u)) { + tcx.sess.span_warn( + ty.span, + "found rust type `uint` in native module, while \ + ctypes::c_uint or ctypes::ulong should be used"); + } + _ { } + } + } + _ { } + } + } + } + + fn check_item(tcx: ty::ctxt, it: @ast::item) { + alt it.node { + ast::item_native_mod(nmod) { + for ni in nmod.items { + alt ni.node { + ast::native_item_fn(decl, tps) { + check_native_fn(tcx, decl); + } + _ { } + } + } + } + _ {/* nothing to do */ } + } + } + + let visit = visit::mk_simple_visitor(@{ + visit_item: bind check_item(tcx, _) + with *visit::default_simple_visitor() + }); + visit::visit_crate(*crate, (), visit); +} + +fn check_crate(tcx: ty::ctxt, crate: @ast::crate, + opts: [(option, bool)], time: bool) { + let lint_opts = lint::merge_opts(crate.node.attrs, opts); + for (lopt, switch) in lint_opts { + if switch == true { + lopt.run(tcx, crate, time); + } + } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/mutbl.rs b/src/rustc/middle/mutbl.rs new file mode 100644 index 00000000000..836b2631427 --- /dev/null +++ b/src/rustc/middle/mutbl.rs @@ -0,0 +1,321 @@ +import syntax::ast::*; +import syntax::visit; +import syntax::ast_util; +import driver::session::session; + +enum deref_t { unbox(bool), field, index, } + +type deref = @{mutbl: bool, kind: deref_t, outer_t: ty::t}; + +// Finds the root (the thing that is dereferenced) for the given expr, and a +// vec of dereferences that were used on this root. Note that, in this vec, +// the inner derefs come in front, so foo.bar[1] becomes rec(ex=foo, +// ds=[index,field]) +fn expr_root(tcx: ty::ctxt, ex: @expr, autoderef: bool) -> + {ex: @expr, ds: @[deref]} { + fn maybe_auto_unbox(tcx: ty::ctxt, t: ty::t) -> {t: ty::t, ds: [deref]} { + let ds = [], t = t; + while true { + alt ty::get(t).struct { + ty::ty_box(mt) { + ds += [@{mutbl: mt.mutbl == m_mutbl, + kind: unbox(false), + outer_t: t}]; + t = mt.ty; + } + ty::ty_uniq(mt) { + ds += [@{mutbl: mt.mutbl == m_mutbl, + kind: unbox(false), + outer_t: t}]; + t = mt.ty; + } + ty::ty_res(_, inner, tps) { + ds += [@{mutbl: false, kind: unbox(false), outer_t: t}]; + t = ty::substitute_type_params(tcx, tps, inner); + } + ty::ty_enum(did, tps) { + let variants = ty::enum_variants(tcx, did); + if vec::len(*variants) != 1u || + vec::len(variants[0].args) != 1u { + break; + } + ds += [@{mutbl: false, kind: unbox(false), outer_t: t}]; + t = ty::substitute_type_params(tcx, tps, variants[0].args[0]); + } + _ { break; } + } + } + ret {t: t, ds: ds}; + } + let ds: [deref] = [], ex = ex; + while true { + alt copy ex.node { + expr_field(base, ident, _) { + let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, base)); + let is_mutbl = false; + alt ty::get(auto_unbox.t).struct { + ty::ty_rec(fields) { + for fld: ty::field in fields { + if str::eq(ident, fld.ident) { + is_mutbl = fld.mt.mutbl == m_mutbl; + break; + } + } + } + _ {} + } + ds += [@{mutbl: is_mutbl, kind: field, outer_t: auto_unbox.t}]; + ds += auto_unbox.ds; + ex = base; + } + expr_index(base, _) { + let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, base)); + alt ty::get(auto_unbox.t).struct { + ty::ty_vec(mt) { + ds += + [@{mutbl: mt.mutbl == m_mutbl, + kind: index, + outer_t: auto_unbox.t}]; + } + ty::ty_str { + ds += [@{mutbl: false, kind: index, outer_t: auto_unbox.t}]; + } + _ { break; } + } + ds += auto_unbox.ds; + ex = base; + } + expr_unary(op, base) { + if op == deref { + let base_t = ty::expr_ty(tcx, base); + let is_mutbl = false, ptr = false; + alt ty::get(base_t).struct { + ty::ty_box(mt) { is_mutbl = mt.mutbl == m_mutbl; } + ty::ty_uniq(mt) { is_mutbl = mt.mutbl == m_mutbl; } + ty::ty_res(_, _, _) { } + ty::ty_enum(_, _) { } + ty::ty_ptr(mt) { + is_mutbl = mt.mutbl == m_mutbl; + ptr = true; + } + _ { tcx.sess.span_bug(base.span, "Ill-typed base \ + expression in deref"); } + } + ds += [@{mutbl: is_mutbl, kind: unbox(ptr && is_mutbl), + outer_t: base_t}]; + ex = base; + } else { break; } + } + _ { break; } + } + } + if autoderef { + let auto_unbox = maybe_auto_unbox(tcx, ty::expr_ty(tcx, ex)); + ds += auto_unbox.ds; + } + ret {ex: ex, ds: @ds}; +} + +// Actual mutbl-checking pass + +type mutbl_map = std::map::hashmap<node_id, ()>; +type ctx = {tcx: ty::ctxt, mutbl_map: mutbl_map}; + +fn check_crate(tcx: ty::ctxt, crate: @crate) -> mutbl_map { + let cx = @{tcx: tcx, mutbl_map: std::map::new_int_hash()}; + let v = @{visit_expr: bind visit_expr(cx, _, _, _), + visit_decl: bind visit_decl(cx, _, _, _) + with *visit::default_visitor()}; + visit::visit_crate(*crate, (), visit::mk_vt(v)); + ret cx.mutbl_map; +} + +enum msg { msg_assign, msg_move_out, msg_mutbl_ref, } + +fn mk_err(cx: @ctx, span: syntax::codemap::span, msg: msg, name: str) { + cx.tcx.sess.span_err(span, alt msg { + msg_assign { "assigning to " + name } + msg_move_out { "moving out of " + name } + msg_mutbl_ref { "passing " + name + " by mutable reference" } + }); +} + +fn visit_decl(cx: @ctx, d: @decl, &&e: (), v: visit::vt<()>) { + visit::visit_decl(d, e, v); + alt d.node { + decl_local(locs) { + for loc in locs { + alt loc.node.init { + some(init) { + if init.op == init_move { check_move_rhs(cx, init.expr); } + } + none { } + } + } + } + _ { } + } +} + +fn visit_expr(cx: @ctx, ex: @expr, &&e: (), v: visit::vt<()>) { + alt ex.node { + expr_call(f, args, _) { check_call(cx, f, args); } + expr_bind(f, args) { check_bind(cx, f, args); } + expr_swap(lhs, rhs) { + check_lval(cx, lhs, msg_assign); + check_lval(cx, rhs, msg_assign); + } + expr_move(dest, src) { + check_lval(cx, dest, msg_assign); + check_move_rhs(cx, src); + } + expr_assign(dest, src) | expr_assign_op(_, dest, src) { + check_lval(cx, dest, msg_assign); + } + expr_fn(_, _, _, cap) { + for moved in cap.moves { + let def = cx.tcx.def_map.get(moved.id); + alt is_immutable_def(cx, def) { + some(name) { mk_err(cx, moved.span, msg_move_out, moved.name); } + _ { } + } + cx.mutbl_map.insert(ast_util::def_id_of_def(def).node, ()); + } + } + _ { } + } + visit::visit_expr(ex, e, v); +} + +fn check_lval(cx: @ctx, dest: @expr, msg: msg) { + alt dest.node { + expr_path(p) { + let def = cx.tcx.def_map.get(dest.id); + alt is_immutable_def(cx, def) { + some(name) { mk_err(cx, dest.span, msg, name); } + _ { } + } + cx.mutbl_map.insert(ast_util::def_id_of_def(def).node, ()); + } + _ { + let root = expr_root(cx.tcx, dest, false); + if vec::len(*root.ds) == 0u { + if msg != msg_move_out { + mk_err(cx, dest.span, msg, "non-lvalue"); + } + } else if !root.ds[0].mutbl { + let name = + alt root.ds[0].kind { + mutbl::unbox(_) { "immutable box" } + mutbl::field { "immutable field" } + mutbl::index { "immutable vec content" } + }; + mk_err(cx, dest.span, msg, name); + } + } + } +} + +fn check_move_rhs(cx: @ctx, src: @expr) { + alt src.node { + expr_path(p) { + alt cx.tcx.def_map.get(src.id) { + def_self(_) { + mk_err(cx, src.span, msg_move_out, "method self"); + } + _ { } + } + check_lval(cx, src, msg_move_out); + } + _ { + let root = expr_root(cx.tcx, src, false); + + // Not a path and no-derefs means this is a temporary. + if vec::len(*root.ds) != 0u && + root.ds[vec::len(*root.ds) - 1u].kind != unbox(true) { + cx.tcx.sess.span_err(src.span, "moving out of a data structure"); + } + } + } +} + +fn check_call(cx: @ctx, f: @expr, args: [@expr]) { + let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f)); + let i = 0u; + for arg_t: ty::arg in arg_ts { + alt ty::resolved_mode(cx.tcx, arg_t.mode) { + by_mutbl_ref { check_lval(cx, args[i], msg_mutbl_ref); } + by_move { check_lval(cx, args[i], msg_move_out); } + by_ref | by_val | by_copy { } + } + i += 1u; + } +} + +fn check_bind(cx: @ctx, f: @expr, args: [option<@expr>]) { + let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f)); + let i = 0u; + for arg in args { + alt arg { + some(expr) { + let o_msg = alt ty::resolved_mode(cx.tcx, arg_ts[i].mode) { + by_mutbl_ref { some("by mutable reference") } + by_move { some("by move") } + _ { none } + }; + alt o_msg { + some(name) { + cx.tcx.sess.span_err( + expr.span, "can not bind an argument passed " + name); + } + none {} + } + } + _ {} + } + i += 1u; + } +} + +fn is_immutable_def(cx: @ctx, def: def) -> option<str> { + alt def { + def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) | + def_use(_) { + some("static item") + } + def_arg(_, m) { + alt ty::resolved_mode(cx.tcx, m) { + by_ref | by_val { some("argument") } + by_mutbl_ref | by_move | by_copy { none } + } + } + def_self(_) { some("self argument") } + def_upvar(_, inner, node_id) { + let ty = ty::node_id_to_type(cx.tcx, node_id); + let proto = ty::ty_fn_proto(ty); + ret alt proto { + proto_any | proto_block { is_immutable_def(cx, *inner) } + _ { some("upvar") } + }; + } + + // Note: we should *always* allow all local variables to be assigned + // here and then guarantee in the typestate pass that immutable local + // variables are assigned at most once. But this requires a new kind of + // propagation (def. not assigned), so I didn't do that. + def_local(_, false) if cx.tcx.sess.opts.enforce_mut_vars { + some("immutable local variable") + } + + def_binding(_) { some("binding") } + _ { none } + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/pat_util.rs b/src/rustc/middle/pat_util.rs new file mode 100644 index 00000000000..1a6856e36bf --- /dev/null +++ b/src/rustc/middle/pat_util.rs @@ -0,0 +1,70 @@ +import syntax::ast::*; +import syntax::ast_util; +import syntax::ast_util::respan; +import syntax::fold; +import syntax::fold::*; +import syntax::codemap::span; + +export walk_pat; +export pat_binding_ids, pat_bindings, pat_id_map; +export pat_is_variant; +export path_to_ident; + +type pat_id_map = std::map::hashmap<str, node_id>; + +// This is used because same-named variables in alternative patterns need to +// use the node_id of their namesake in the first pattern. +fn pat_id_map(dm: resolve::def_map, pat: @pat) -> pat_id_map { + let map = std::map::new_str_hash(); + pat_bindings(dm, pat) {|p_id, _s, n| + map.insert(path_to_ident(n), p_id); + }; + ret map; +} + +fn pat_is_variant(dm: resolve::def_map, pat: @pat) -> bool { + alt pat.node { + pat_enum(_, _) { true } + pat_ident(_, none) { + alt dm.find(pat.id) { + some(def_variant(_, _)) { true } + _ { false } + } + } + _ { false } + } +} + +// This does *not* normalize. The pattern should be already normalized +// if you want to get a normalized pattern out of it. +// Could return a constrained type in order to express that (future work) +fn pat_bindings(dm: resolve::def_map, pat: @pat, + it: fn(node_id, span, @path)) { + walk_pat(pat) {|p| + alt p.node { + pat_ident(pth, _) if !pat_is_variant(dm, p) { + it(p.id, p.span, pth); + } + _ {} + } + } +} + +fn walk_pat(pat: @pat, it: fn(@pat)) { + it(pat); + alt pat.node { + pat_ident(pth, some(p)) { walk_pat(p, it); } + pat_rec(fields, _) { for f in fields { walk_pat(f.pat, it); } } + pat_enum(_, s) | pat_tup(s) { for p in s { walk_pat(p, it); } } + pat_box(s) | pat_uniq(s) { walk_pat(s, it); } + pat_wild | pat_lit(_) | pat_range(_, _) | pat_ident(_, none) {} + } +} + +fn pat_binding_ids(dm: resolve::def_map, pat: @pat) -> [node_id] { + let found = []; + pat_bindings(dm, pat) {|b_id, _sp, _pt| found += [b_id]; }; + ret found; +} + +fn path_to_ident(p: @path) -> ident { vec::last_total(p.node.idents) } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs new file mode 100644 index 00000000000..117641c9fe8 --- /dev/null +++ b/src/rustc/middle/resolve.rs @@ -0,0 +1,2381 @@ +import syntax::{ast, ast_util, codemap}; +import syntax::ast::*; +import ast::{ident, fn_ident, def, def_id, node_id}; +import syntax::ast_util::{local_def, def_id_of_def, class_item_ident}; +import pat_util::*; + +import front::attr; +import metadata::{csearch, cstore}; +import driver::session::session; +import util::common::*; +import std::map::{new_int_hash, new_str_hash}; +import syntax::codemap::span; +import syntax::visit; +import visit::vt; +import std::{list, deque}; +import std::map::hashmap; +import std::list::{list, nil, cons}; +import option::{is_none, is_some}; +import syntax::print::pprust::*; + +export resolve_crate, resolve_crate_reexports; +export def_map, ext_map, exp_map, impl_map; +export _impl, iscopes, method_info; + +// Resolving happens in two passes. The first pass collects defids of all +// (internal) imports and modules, so that they can be looked up when needed, +// and then uses this information to resolve the imports. The second pass +// locates all names (in expressions, types, and alt patterns) and resolves +// them, storing the resulting def in the AST nodes. + +enum scope { + scope_toplevel, + scope_crate, + scope_item(@ast::item), + scope_bare_fn(ast::fn_decl, node_id, [ast::ty_param]), + scope_fn_expr(ast::fn_decl, node_id, [ast::ty_param]), + scope_native_item(@ast::native_item), + scope_loop(@ast::local), // there's only 1 decl per loop. + scope_block(ast::blk, @mutable uint, @mutable uint), + scope_arm(ast::arm), + scope_method(ast::node_id, [ast::ty_param]), +} + +type scopes = list<scope>; + +fn top_scope() -> scopes { + cons(scope_crate, @cons(scope_toplevel, @nil)) +} + +enum import_state { + todo(ast::node_id, ast::ident, @[ast::ident], span, scopes), + is_glob(@[ast::ident], scopes, span), + resolving(span), + resolved(option<def>, /* value */ + option<def>, /* type */ + option<def>, /* module */ + @[@_impl], /* impls */ + /* used for reporting unused import warning */ + ast::ident, span), +} + +enum glob_import_state { + glob_resolving(span), + glob_resolved(option<def>, /* value */ + option<def>, /* type */ + option<def>), /* module */ +} + +type ext_hash = hashmap<{did: def_id, ident: str, ns: namespace}, def>; + +fn new_ext_hash() -> ext_hash { + type key = {did: def_id, ident: str, ns: namespace}; + fn hash(v: key) -> uint { + ret str::hash(v.ident) + util::common::hash_def(v.did) + + alt v.ns { + ns_val(_) { 1u } + ns_type { 2u } + ns_module { 3u } + }; + } + fn eq(v1: key, v2: key) -> bool { + ret util::common::def_eq(v1.did, v2.did) && + str::eq(v1.ident, v2.ident) && v1.ns == v2.ns; + } + ret std::map::mk_hashmap::<key, def>(hash, eq); +} + +enum mod_index_entry { + mie_view_item(ident, node_id, span), + mie_import_ident(node_id, span), + mie_item(@ast::item), + mie_class_item(node_id, /* parent class name */ + @ast::class_item), /* class member */ + mie_native_item(@ast::native_item), + mie_enum_variant(/* variant index */uint, + /*parts of enum item*/ [variant], + node_id, span), +} + +type mod_index = hashmap<ident, list<mod_index_entry>>; + +// A tuple of an imported def and the view_path from its originating import +type glob_imp_def = {def: def, path: @ast::view_path}; + +type indexed_mod = { + m: option<ast::_mod>, + index: mod_index, + mutable glob_imports: [glob_imp_def], + mutable globbed_exports: [ident], + glob_imported_names: hashmap<str, glob_import_state>, + path: str +}; + +/* native modules can't contain enums, and we don't store their ASTs because + we only need to look at them to determine exports, which they can't + control.*/ + +type def_map = hashmap<node_id, def>; +type ext_map = hashmap<def_id, [ident]>; +type exp_map = hashmap<str, @mutable [def]>; +type impl_map = hashmap<node_id, iscopes>; +type impl_cache = hashmap<def_id, option<@[@_impl]>>; + +type env = + {cstore: cstore::cstore, + def_map: def_map, + ast_map: ast_map::map, + imports: hashmap<ast::node_id, import_state>, + exp_map: exp_map, + mod_map: hashmap<ast::node_id, @indexed_mod>, + block_map: hashmap<ast::node_id, [glob_imp_def]>, + ext_map: ext_map, + impl_map: impl_map, + impl_cache: impl_cache, + ext_cache: ext_hash, + used_imports: {mutable track: bool, + mutable data: [ast::node_id]}, + mutable reported: [{ident: str, sc: scope}], + mutable ignored_imports: [node_id], + mutable current_tp: option<uint>, + mutable resolve_unexported: bool, + sess: session}; + + +// Used to distinguish between lookups from outside and from inside modules, +// since export restrictions should only be applied for the former. +enum dir { inside, outside, } + +// There are two types of ns_value enum: "definitely a enum"; and "enum or +// other value". This is so that lookup can behave differently when looking up +// a variable name that's not yet in scope to check if it's already bound to a +// enum. +enum namespace { ns_val(enumness), ns_type, ns_module, } +enum enumness { + definite_enum, + value_or_enum +} + +fn resolve_crate(sess: session, amap: ast_map::map, crate: @ast::crate) -> + {def_map: def_map, exp_map: exp_map, impl_map: impl_map} { + let e = create_env(sess, amap); + map_crate(e, crate); + resolve_imports(*e); + check_exports(e); + resolve_names(e, crate); + resolve_impls(e, crate); + // check_for_collisions must happen after resolve_names so we + // don't complain if a pattern uses the same nullary enum twice + check_for_collisions(e, *crate); + if sess.opts.warn_unused_imports { + check_unused_imports(e); + } + ret {def_map: e.def_map, exp_map: e.exp_map, impl_map: e.impl_map}; +} + +// Used by rustdoc +fn resolve_crate_reexports(sess: session, amap: ast_map::map, + crate: @ast::crate) -> exp_map { + let e = create_env(sess, amap); + map_crate(e, crate); + resolve_imports(*e); + check_exports(e); + ret e.exp_map; +} + +fn create_env(sess: session, amap: ast_map::map) -> @env { + @{cstore: sess.cstore, + def_map: new_int_hash(), + ast_map: amap, + imports: new_int_hash(), + exp_map: new_str_hash(), + mod_map: new_int_hash(), + block_map: new_int_hash(), + ext_map: new_def_hash(), + impl_map: new_int_hash(), + impl_cache: new_def_hash(), + ext_cache: new_ext_hash(), + used_imports: {mutable track: false, mutable data: []}, + mutable reported: [], + mutable ignored_imports: [], + mutable current_tp: none, + mutable resolve_unexported: false, + sess: sess} +} + +fn iter_export_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { + alt vi.node { + ast::view_item_export(vps) { + for vp in vps { + f(vp); + } + } + _ {} + } +} + +fn iter_import_paths(vi: ast::view_item, f: fn(vp: @ast::view_path)) { + alt vi.node { + ast::view_item_import(vps) { + for vp in vps { + f(vp); + } + } + _ {} + } +} + +fn iter_effective_import_paths(vi: ast::view_item, + f: fn(vp: @ast::view_path)) { + iter_import_paths(vi, f); + iter_export_paths(vi) {|vp| + alt vp.node { + ast::view_path_simple(_, _, _) { } + // FIXME: support uniform ident-list exports eventually; + // at the moment they have half a meaning as reaching into + // tags. + ast::view_path_list(_, _, _) {} + ast::view_path_glob(_,_) { + f(vp); + } + } + } +} + +// Locate all modules and imports and index them, so that the next passes can +// resolve through them. +fn map_crate(e: @env, c: @ast::crate) { + + fn index_vi(e: @env, i: @ast::view_item, sc: scopes, _v: vt<scopes>) { + iter_effective_import_paths(*i) { |vp| + alt vp.node { + ast::view_path_simple(name, path, id) { + e.imports.insert(id, todo(id, name, path, vp.span, sc)); + } + ast::view_path_glob(path, id) { + e.imports.insert(id, is_glob(path, sc, vp.span)); + } + ast::view_path_list(mod_path, idents, _) { + for ident in idents { + let t = todo(ident.node.id, ident.node.name, + @(*mod_path + [ident.node.name]), + ident.span, sc); + e.imports.insert(ident.node.id, t); + } + } + } + } + } + + fn path_from_scope(sc: scopes, n: str) -> str { + let path = n + "::"; + list::iter(sc) {|s| + alt s { + scope_item(i) { path = i.ident + "::" + path; } + _ {} + } + } + path + } + + fn index_i(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) { + visit_item_with_scope(e, i, sc, v); + alt i.node { + ast::item_mod(md) { + e.mod_map.insert(i.id, + @{m: some(md), + index: index_mod(md), + mutable glob_imports: [], + mutable globbed_exports: [], + glob_imported_names: new_str_hash(), + path: path_from_scope(sc, i.ident)}); + } + ast::item_native_mod(nmd) { + e.mod_map.insert(i.id, + @{m: none::<ast::_mod>, + index: index_nmod(nmd), + mutable glob_imports: [], + mutable globbed_exports: [], + glob_imported_names: new_str_hash(), + path: path_from_scope(sc, i.ident)}); + } + _ { } + } + } + + // Note: a glob export works as an implict import, along with a + // re-export of anything that was exported at the glob-target location. + // So we wind up reusing the glob-import machinery when looking at + // glob exports. They just do re-exporting in a later step. + fn link_glob(e: @env, vi: @ast::view_item, sc: scopes, _v: vt<scopes>) { + iter_effective_import_paths(*vi) { |vp| + alt vp.node { + ast::view_path_glob(path, _) { + alt follow_import(*e, sc, *path, vp.span) { + some(imp) { + let glob = {def: imp, path: vp}; + alt list::head(sc) { + scope_item(i) { + e.mod_map.get(i.id).glob_imports += [glob]; + } + scope_block(b, _, _) { + let globs = alt e.block_map.find(b.node.id) { + some(globs) { globs + [glob] } + none { [glob] } + }; + e.block_map.insert(b.node.id, globs); + } + scope_crate { + e.mod_map.get(ast::crate_node_id).glob_imports + += [glob]; + } + _ { e.sess.span_bug(vi.span, "Unexpected scope in a \ + glob import"); } + } + } + _ { } + } + } + _ { } + } + } + } + + // First, find all the modules, and index the names that they contain + let v_map_mod = + @{visit_view_item: bind index_vi(e, _, _, _), + visit_item: bind index_i(e, _, _, _), + visit_block: visit_block_with_scope + with *visit::default_visitor::<scopes>()}; + visit::visit_crate(*c, top_scope(), visit::mk_vt(v_map_mod)); + + // Register the top-level mod + e.mod_map.insert(ast::crate_node_id, + @{m: some(c.node.module), + index: index_mod(c.node.module), + mutable glob_imports: [], + mutable globbed_exports: [], + glob_imported_names: new_str_hash(), + path: ""}); + + // Next, assemble the links for globbed imports and exports. + let v_link_glob = + @{visit_view_item: bind link_glob(e, _, _, _), + visit_block: visit_block_with_scope, + visit_item: bind visit_item_with_scope(e, _, _, _) + with *visit::default_visitor::<scopes>()}; + visit::visit_crate(*c, top_scope(), visit::mk_vt(v_link_glob)); + +} + +fn resolve_imports(e: env) { + e.used_imports.track = true; + e.imports.values {|v| + alt v { + todo(node_id, name, path, span, scopes) { + resolve_import(e, local_def(node_id), name, *path, span, scopes); + } + resolved(_, _, _, _, _, _) | is_glob(_, _, _) { } + _ { e.sess.bug("Shouldn't see a resolving in resolve_imports"); } + } + }; + e.used_imports.track = false; + e.sess.abort_if_errors(); +} + +fn check_unused_imports(e: @env) { + e.imports.items {|k, v| + alt v { + resolved(_, _, _, _, name, sp) { + if !vec::contains(e.used_imports.data, k) { + e.sess.span_warn(sp, "unused import " + name); + } + } + _ { } + } + }; +} + +fn resolve_capture_item(e: @env, sc: scopes, &&cap_item: @ast::capture_item) { + let dcur = lookup_in_scope_strict( + *e, sc, cap_item.span, cap_item.name, ns_val(value_or_enum)); + maybe_insert(e, cap_item.id, dcur); +} + +fn maybe_insert(e: @env, id: node_id, def: option<def>) { + alt def { + some(df) { e.def_map.insert(id, df); } + _ {} + } +} + +fn resolve_names(e: @env, c: @ast::crate) { + e.used_imports.track = true; + let v = + @{visit_native_item: visit_native_item_with_scope, + visit_item: bind visit_item_with_scope(e, _, _, _), + visit_block: visit_block_with_scope, + visit_decl: visit_decl_with_scope, + visit_arm: visit_arm_with_scope, + visit_local: bind visit_local_with_scope(e, _, _, _), + visit_pat: bind walk_pat(e, _, _, _), + visit_expr: bind walk_expr(e, _, _, _), + visit_ty: bind walk_ty(e, _, _, _), + visit_ty_params: bind walk_tps(e, _, _, _), + visit_constr: bind walk_constr(e, _, _, _, _, _), + visit_fn: bind visit_fn_with_scope(e, _, _, _, _, _, _, _) + with *visit::default_visitor()}; + visit::visit_crate(*c, top_scope(), visit::mk_vt(v)); + e.used_imports.track = false; + e.sess.abort_if_errors(); + + fn walk_expr(e: @env, exp: @ast::expr, sc: scopes, v: vt<scopes>) { + visit_expr_with_scope(exp, sc, v); + alt exp.node { + ast::expr_path(p) { + maybe_insert(e, exp.id, + lookup_path_strict(*e, sc, exp.span, p.node, + ns_val(value_or_enum))); + } + ast::expr_fn(_, _, _, cap_clause) { + let rci = bind resolve_capture_item(e, sc, _); + vec::iter(cap_clause.copies, rci); + vec::iter(cap_clause.moves, rci); + } + _ { } + } + } + fn walk_ty(e: @env, t: @ast::ty, sc: scopes, v: vt<scopes>) { + visit::visit_ty(t, sc, v); + alt t.node { + ast::ty_path(p, id) { + maybe_insert(e, id, + lookup_path_strict(*e, sc, t.span, p.node, ns_type)); + } + _ { } + } + } + fn walk_tps(e: @env, tps: [ast::ty_param], sc: scopes, v: vt<scopes>) { + let outer_current_tp = e.current_tp, current = 0u; + for tp in tps { + e.current_tp = some(current); + for bound in *tp.bounds { + alt bound { + bound_iface(t) { v.visit_ty(t, sc, v); } + _ {} + } + } + current += 1u; + } + e.current_tp = outer_current_tp; + } + fn walk_constr(e: @env, p: @ast::path, sp: span, id: node_id, sc: scopes, + _v: vt<scopes>) { + maybe_insert(e, id, lookup_path_strict(*e, sc, + sp, p.node, ns_val(value_or_enum))); + } + fn walk_pat(e: @env, pat: @ast::pat, sc: scopes, v: vt<scopes>) { + visit::visit_pat(pat, sc, v); + alt pat.node { + ast::pat_enum(p, _) { + alt lookup_path_strict(*e, sc, p.span, p.node, + ns_val(value_or_enum)) { + some(fnd@ast::def_variant(_,_)) { + e.def_map.insert(pat.id, fnd); + } + _ { + e.sess.span_err(p.span, + "not a enum variant: " + + ast_util::path_name(p)); + } + } + } + /* Here we determine whether a given pat_ident binds a new + variable a refers to a nullary enum. */ + ast::pat_ident(p, none) { + alt lookup_in_scope(*e, sc, p.span, path_to_ident(p), + ns_val(definite_enum)) { + some(fnd@ast::def_variant(_,_)) { + e.def_map.insert(pat.id, fnd); + } + _ { + // Binds a var -- nothing needs to be done + } + } + } + _ { } + } + } +} + + +// Visit helper functions +fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) { + // Some magic here. Items with the !resolve_unexported attribute + // cause us to consider every name to be exported when resolving their + // contents. This is used to allow the test runner to run unexported + // tests. + let old_resolve_unexported = e.resolve_unexported; + e.resolve_unexported |= + attr::contains_name(attr::attr_metas(i.attrs), + "!resolve_unexported"); + + let sc = cons(scope_item(i), @sc); + alt i.node { + ast::item_impl(tps, ifce, sty, methods) { + visit::visit_ty_params(tps, sc, v); + alt ifce { some(ty) { v.visit_ty(ty, sc, v); } _ {} } + v.visit_ty(sty, sc, v); + for m in methods { + v.visit_ty_params(m.tps, sc, v); + let msc = cons(scope_method(i.id, tps + m.tps), @sc); + v.visit_fn(visit::fk_method(m.ident, []), + m.decl, m.body, m.span, m.id, msc, v); + } + } + ast::item_iface(tps, methods) { + visit::visit_ty_params(tps, sc, v); + for m in methods { + let msc = cons(scope_method(i.id, tps + m.tps), @sc); + for a in m.decl.inputs { v.visit_ty(a.ty, msc, v); } + v.visit_ty(m.decl.output, msc, v); + } + } + ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) { + visit::visit_ty_params(tps, sc, v); + let class_scope = cons(scope_item(i), @sc); + /* visit the constructor... */ + visit_fn_with_scope(e, visit::fk_item_fn(i.ident, tps), ctor_decl, + ctor_block, ctor_block.span, ctor_id, + class_scope, v); + /* visit the items */ + for cm in members { + alt cm.node.decl { + class_method(i) { visit_item_with_scope(e, i, class_scope, v); } + instance_var(_,t,_,_) { v.visit_ty(t, class_scope, v); } + } + } + } + _ { visit::visit_item(i, sc, v); } + } + + e.resolve_unexported = old_resolve_unexported; +} + +fn visit_native_item_with_scope(ni: @ast::native_item, sc: scopes, + v: vt<scopes>) { + visit::visit_native_item(ni, cons(scope_native_item(ni), @sc), v); +} + +fn visit_fn_with_scope(e: @env, fk: visit::fn_kind, decl: ast::fn_decl, + body: ast::blk, sp: span, + id: node_id, sc: scopes, v: vt<scopes>) { + // is this a main fn declaration? + alt fk { + visit::fk_item_fn(nm, _) { + if is_main_name([ast_map::path_name(nm)]) && + !e.sess.building_library { + // This is a main function -- set it in the session + // as the main ID + e.sess.main_fn = some((id, sp)); + } + } + _ { /* fallthrough */ } + } + + // here's where we need to set up the mapping + // for f's constrs in the table. + for c: @ast::constr in decl.constraints { resolve_constr(e, c, sc, v); } + let scope = alt fk { + visit::fk_item_fn(_, tps) | visit::fk_res(_, tps) | + visit::fk_method(_, tps) { scope_bare_fn(decl, id, tps) } + visit::fk_anon(ast::proto_bare) { scope_bare_fn(decl, id, []) } + visit::fk_anon(_) | visit::fk_fn_block { scope_fn_expr(decl, id, []) } + }; + + visit::visit_fn(fk, decl, body, sp, id, cons(scope, @sc), v); +} + +fn visit_block_with_scope(b: ast::blk, sc: scopes, v: vt<scopes>) { + let pos = @mutable 0u, loc = @mutable 0u; + let block_sc = cons(scope_block(b, pos, loc), @sc); + for vi in b.node.view_items { v.visit_view_item(vi, block_sc, v); } + for stmt in b.node.stmts { + v.visit_stmt(stmt, block_sc, v);; + *pos += 1u;; + *loc = 0u; + } + visit::visit_expr_opt(b.node.expr, block_sc, v); +} + +fn visit_decl_with_scope(d: @decl, sc: scopes, v: vt<scopes>) { + let loc_pos = alt list::head(sc) { + scope_block(_, _, pos) { pos } + _ { @mutable 0u } + }; + alt d.node { + decl_local(locs) { + for loc in locs { v.visit_local(loc, sc, v);; *loc_pos += 1u; } + } + decl_item(it) { v.visit_item(it, sc, v); } + } +} + +fn visit_arm_with_scope(a: ast::arm, sc: scopes, v: vt<scopes>) { + for p: @pat in a.pats { v.visit_pat(p, sc, v); } + let sc_inner = cons(scope_arm(a), @sc); + visit::visit_expr_opt(a.guard, sc_inner, v); + v.visit_block(a.body, sc_inner, v); +} + +fn visit_expr_with_scope(x: @ast::expr, sc: scopes, v: vt<scopes>) { + alt x.node { + ast::expr_for(decl, coll, blk) { + let new_sc = cons(scope_loop(decl), @sc); + v.visit_expr(coll, sc, v); + v.visit_local(decl, new_sc, v); + v.visit_block(blk, new_sc, v); + } + _ { visit::visit_expr(x, sc, v); } + } +} + +// This is only for irrefutable patterns (e.g. ones that appear in a let) +// So if x occurs, and x is already known to be a enum, that's always an error +fn visit_local_with_scope(e: @env, loc: @local, sc:scopes, v:vt<scopes>) { + // Check whether the given local has the same name as a enum that's + // in scope + // We disallow this, in order to make alt patterns consisting of + // a single identifier unambiguous (does the pattern "foo" refer + // to enum foo, or is it binding a new name foo?) + alt loc.node.pat.node { + pat_ident(an_ident,_) { + // Be sure to pass definite_enum to lookup_in_scope so that + // if this is a name that's being shadowed, we don't die + alt lookup_in_scope(*e, sc, loc.span, + path_to_ident(an_ident), ns_val(definite_enum)) { + some(ast::def_variant(enum_id,variant_id)) { + // Declaration shadows a enum that's in scope. + // That's an error. + e.sess.span_err(loc.span, + #fmt("Declaration of %s shadows a enum that's in scope", + path_to_ident(an_ident))); + } + _ {} + } + } + _ {} + } + visit::visit_local(loc, sc, v); +} + + +fn follow_import(e: env, sc: scopes, path: [ident], sp: span) -> + option<def> { + let path_len = vec::len(path); + let dcur = lookup_in_scope_strict(e, sc, sp, path[0], ns_module); + let i = 1u; + while true { + alt dcur { + some(dcur_def) { + if i == path_len { break; } + dcur = + lookup_in_mod_strict(e, dcur_def, sp, path[i], + ns_module, outside); + i += 1u; + } + _ { break; } + } + } + if i == path_len { + alt dcur { + some(ast::def_mod(_)) | some(ast::def_native_mod(_)) { ret dcur; } + _ { + e.sess.span_err(sp, str::connect(path, "::") + + " does not name a module."); + ret none; + } + } + } else { ret none; } +} + +fn resolve_constr(e: @env, c: @ast::constr, sc: scopes, _v: vt<scopes>) { + alt lookup_path_strict(*e, sc, c.span, c.node.path.node, + ns_val(value_or_enum)) { + some(d@ast::def_fn(_,ast::pure_fn)) { + e.def_map.insert(c.node.id, d); + } + _ { + let s = path_to_str(c.node.path); + e.sess.span_err(c.span, #fmt("%s is not declared pure. Try \ + `pure fn %s` instead of `fn %s`.", s, s, s)); + } + } +} + +// Import resolution +fn resolve_import(e: env, defid: ast::def_id, name: ast::ident, + ids: [ast::ident], sp: codemap::span, sc: scopes) { + fn register(e: env, id: node_id, cx: ctxt, sp: codemap::span, + name: ast::ident, lookup: fn(namespace) -> option<def>, + impls: [@_impl]) { + let val = lookup(ns_val(value_or_enum)), typ = lookup(ns_type), + md = lookup(ns_module); + if is_none(val) && is_none(typ) && is_none(md) && + vec::len(impls) == 0u { + unresolved_err(e, cx, sp, name, "import"); + } else { + e.imports.insert(id, resolved(val, typ, md, @impls, name, sp)); + } + } + // Temporarily disable this import and the imports coming after during + // resolution of this import. + fn find_imports_after(e: env, id: node_id, sc: scopes) -> [node_id] { + fn lst(my_id: node_id, vis: [@view_item]) -> [node_id] { + let imports = [], found = false; + for vi in vis { + iter_effective_import_paths(*vi) {|vp| + alt vp.node { + view_path_simple(_, _, id) + | view_path_glob(_, id) { + if id == my_id { found = true; } + if found { imports += [id]; } + } + view_path_list(_, ids, _) { + for id in ids { + if id.node.id == my_id { found = true; } + if found { imports += [id.node.id]; } + } + } + } + } + } + imports + } + alt sc { + cons(scope_item(@{node: item_mod(m), _}), _) { + lst(id, m.view_items) + } + cons(scope_item(@{node: item_native_mod(m), _}), _) { + lst(id, m.view_items) + } + cons(scope_block(b, _, _), _) { + lst(id, b.node.view_items) + } + cons(scope_crate, _) { + lst(id, + option::get(e.mod_map.get(ast::crate_node_id).m).view_items) + } + _ { + e.sess.bug("find_imports_after: nil or unexpected scope"); + } + } + } + // This function has cleanup code at the end. Do not return without going + // through that. + e.imports.insert(defid.node, resolving(sp)); + let ignored = find_imports_after(e, defid.node, sc); + e.ignored_imports <-> ignored; + let n_idents = vec::len(ids); + let end_id = ids[n_idents - 1u]; + if n_idents == 1u { + register(e, defid.node, in_scope(sc), sp, name, + {|ns| lookup_in_scope(e, sc, sp, end_id, ns) }, []); + } else { + alt lookup_in_scope(e, sc, sp, ids[0], ns_module) { + none { + unresolved_err(e, in_scope(sc), sp, ids[0], ns_name(ns_module)); + } + some(dcur_) { + let dcur = dcur_, i = 1u; + while true { + if i == n_idents - 1u { + let impls = []; + find_impls_in_mod(e, dcur, impls, some(end_id)); + register(e, defid.node, in_mod(dcur), sp, name, {|ns| + lookup_in_mod(e, dcur, sp, end_id, ns, outside) + }, impls); + break; + } else { + dcur = alt lookup_in_mod(e, dcur, sp, ids[i], ns_module, + outside) { + some(dcur) { dcur } + none { + unresolved_err(e, in_mod(dcur), sp, ids[i], + ns_name(ns_module)); + break; + } + }; + i += 1u; + } + } + } + } + } + e.ignored_imports <-> ignored; + // If we couldn't resolve the import, don't leave it in a partially + // resolved state, to avoid having it reported later as a cyclic + // import + alt e.imports.find(defid.node) { + some(resolving(sp)) { + e.imports.insert(defid.node, resolved(none, none, none, @[], "", sp)); + } + _ { } + } +} + + +// Utilities +fn ns_name(ns: namespace) -> str { + alt ns { + ns_type { "typename" } + ns_val(v) { + alt (v) { + value_or_enum { "name" } + definite_enum { "enum" } + } + } + ns_module { "modulename" } + } +} + +enum ctxt { in_mod(def), in_scope(scopes), } + +fn unresolved_err(e: env, cx: ctxt, sp: span, name: ident, kind: str) { + fn find_fn_or_mod_scope(sc: scopes) -> option<scope> { + let sc = sc; + while true { + alt sc { + cons(cur, rest) { + alt cur { + scope_crate | scope_bare_fn(_, _, _) | + scope_fn_expr(_, _, _) | + scope_item(@{node: ast::item_mod(_), _}) { + ret some(cur); + } + _ { sc = *rest; } + } + } + _ { ret none; } + } + } + fail; + } + let path = name; + alt cx { + in_scope(sc) { + alt find_fn_or_mod_scope(sc) { + some(err_scope) { + for rs: {ident: str, sc: scope} in e.reported { + if str::eq(rs.ident, name) && err_scope == rs.sc { ret; } + } + e.reported += [{ident: name, sc: err_scope}]; + } + _ {} + } + } + in_mod(def) { + let did = def_id_of_def(def); + if did.crate == ast::local_crate { + path = e.mod_map.get(did.node).path + path; + } else if did.node != ast::crate_node_id { + let paths = e.ext_map.get(did); + if vec::len(paths) > 0u { + path = str::connect(paths, "::") + "::" + path; + } + } + } + } + e.sess.span_err(sp, mk_unresolved_msg(path, kind)); +} + +fn unresolved_fatal(e: env, sp: span, id: ident, kind: str) -> ! { + e.sess.span_fatal(sp, mk_unresolved_msg(id, kind)); +} + +fn mk_unresolved_msg(id: ident, kind: str) -> str { + ret #fmt["unresolved %s: %s", kind, id]; +} + +// Lookup helpers +fn lookup_path_strict(e: env, sc: scopes, sp: span, pth: ast::path_, + ns: namespace) -> option<def> { + let n_idents = vec::len(pth.idents); + let headns = if n_idents == 1u { ns } else { ns_module }; + + let first_scope = if pth.global { top_scope() } else { sc }; + + let dcur_ = + lookup_in_scope_strict(e, first_scope, sp, pth.idents[0], headns); + + alt dcur_ { + none { ret none; } + some(dcur__) { + let i = 1u; + let dcur = dcur__; + while i < n_idents { + let curns = if n_idents == i + 1u { ns } else { ns_module }; + alt lookup_in_mod_strict(e, dcur, sp, pth.idents[i], + curns, outside) { + none { break; } + some(thing) { dcur = thing; } + } + i += 1u; + } + ret some(dcur); + } + } +} + +fn lookup_in_scope_strict(e: env, sc: scopes, sp: span, name: ident, + ns: namespace) -> option<def> { + alt lookup_in_scope(e, sc, sp, name, ns) { + none { + unresolved_err(e, in_scope(sc), sp, name, ns_name(ns)); + ret none; + } + some(d) { ret some(d); } + } +} + +fn scope_is_fn(sc: scope) -> bool { + ret alt sc { + scope_bare_fn(_, _, _) | scope_native_item(_) { true } + _ { false } + }; +} + +// Returns: +// none - does not close +// some(node_id) - closes via the expr w/ node_id +fn scope_closes(sc: scope) -> option<node_id> { + alt sc { + scope_fn_expr(_, node_id, _) { some(node_id) } + _ { none } + } +} + +fn def_is_local(d: def) -> bool { + alt d { + ast::def_arg(_, _) | ast::def_local(_, _) | ast::def_binding(_) | + ast::def_upvar(_, _, _) { true } + _ { false } + } +} + +fn def_is_self(d: def) -> bool { + alt d { + ast::def_self(_) { true } + _ { false } + } +} + +fn def_is_ty_arg(d: def) -> bool { + ret alt d { ast::def_ty_param(_, _) { true } _ { false } }; +} + +fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) + -> option<def> { + + fn in_scope(e: env, sp: span, name: ident, s: scope, ns: namespace) -> + option<def> { + alt s { + scope_toplevel { + if ns == ns_type { + ret some(ast::def_prim_ty(alt name { + "bool" { ast::ty_bool } + "int" { ast::ty_int(ast::ty_i) } + "uint" { ast::ty_uint(ast::ty_u) } + "float" { ast::ty_float(ast::ty_f) } + "str" { ast::ty_str } + "char" { ast::ty_int(ast::ty_char) } + "i8" { ast::ty_int(ast::ty_i8) } + "i16" { ast::ty_int(ast::ty_i16) } + "i32" { ast::ty_int(ast::ty_i32) } + "i64" { ast::ty_int(ast::ty_i64) } + "u8" { ast::ty_uint(ast::ty_u8) } + "u16" { ast::ty_uint(ast::ty_u16) } + "u32" { ast::ty_uint(ast::ty_u32) } + "u64" { ast::ty_uint(ast::ty_u64) } + "f32" { ast::ty_float(ast::ty_f32) } + "f64" { ast::ty_float(ast::ty_f64) } + _ { ret none; } + })); + } + } + scope_crate { + ret lookup_in_local_mod(e, ast::crate_node_id, sp, + name, ns, inside); + } + scope_item(it) { + alt it.node { + ast::item_impl(tps, _, _, _) { + if ns == ns_type { ret lookup_in_ty_params(e, name, tps); } + } + ast::item_enum(_, tps) | ast::item_ty(_, tps) { + if ns == ns_type { ret lookup_in_ty_params(e, name, tps); } + } + ast::item_iface(tps, _) { + if ns == ns_type { + if name == "self" { + ret some(def_self(it.id)); + } + ret lookup_in_ty_params(e, name, tps); + } + } + ast::item_mod(_) { + ret lookup_in_local_mod(e, it.id, sp, name, ns, inside); + } + ast::item_native_mod(m) { + ret lookup_in_local_native_mod(e, it.id, sp, name, ns); + } + ast::item_class(tps, members, ctor_id, _, _) { + if ns == ns_type { + ret lookup_in_ty_params(e, name, tps); + } + if ns == ns_val(value_or_enum) && name == it.ident { + ret some(ast::def_fn(local_def(ctor_id), + ast::impure_fn)); + } + if ns == ns_val(value_or_enum) { + ret lookup_in_class(local_def(it.id), + members, name); + } + // FIXME: AST allows other items to appear in a class, + // but that might not be wise + } + _ { } + } + } + scope_method(id, tps) { + if (name == "self" && ns == ns_val(value_or_enum)) { + ret some(ast::def_self(id)); + } else if ns == ns_type { + ret lookup_in_ty_params(e, name, tps); + } + } + scope_native_item(it) { + alt it.node { + ast::native_item_fn(decl, ty_params) { + ret lookup_in_fn(e, name, decl, ty_params, ns); + } + _ { + e.sess.span_bug(it.span, "lookup_in_scope: \ + scope_native_item doesn't refer to a native item"); + } + } + } + scope_bare_fn(decl, _, ty_params) | + scope_fn_expr(decl, _, ty_params) { + ret lookup_in_fn(e, name, decl, ty_params, ns); + } + scope_loop(local) { + if ns == ns_val(value_or_enum) { + alt lookup_in_pat(e, name, local.node.pat) { + some(nid) { ret some(ast::def_binding(nid)); } + _ { } + } + } + } + scope_block(b, pos, loc) { + ret lookup_in_block(e, name, sp, b.node, *pos, *loc, ns); + } + scope_arm(a) { + if ns == ns_val(value_or_enum) { + alt lookup_in_pat(e, name, a.pats[0]) { + some(nid) { ret some(ast::def_binding(nid)); } + _ { ret none; } + } + } + } + } + ret none; + } + let left_fn = false; + let closing = []; + // Used to determine whether self is in scope + let left_fn_level2 = false; + let sc = sc; + while true { + alt copy sc { + nil { ret none; } + cons(hd, tl) { + alt in_scope(e, sp, name, hd, ns) { + some(df_) { + let df = df_; + let local = def_is_local(df), self_scope = def_is_self(df); + if left_fn && local || left_fn_level2 && self_scope + || scope_is_fn(hd) && left_fn && def_is_ty_arg(df) { + let msg = alt ns { + ns_type { + "attempt to use a type argument out of scope" + } + ns_val(v) { + alt(v) { + /* If we were looking for a enum, at this point + we know it's bound to a non-enum value, and + we can return none instead of failing */ + definite_enum { ret none; } + _ { "attempted dynamic environment-capture" } + } + } + _ { "attempted dynamic environment-capture" } + }; + e.sess.span_fatal(sp, msg); + } else if local || self_scope { + let i = vec::len(closing); + while i > 0u { + i -= 1u; + #debug["name=%s df=%?", name, df]; + assert def_is_local(df) || def_is_self(df); + let df_id = def_id_of_def(df).node; + df = ast::def_upvar(df_id, @df, closing[i]); + } + } + ret some(df); + } + _ {} + } + if left_fn { + left_fn_level2 = true; + } else if ns != ns_module { + left_fn = scope_is_fn(hd); + alt scope_closes(hd) { + some(node_id) { closing += [node_id]; } + _ { } + } + } + sc = *tl; + } + } + } + e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh +} + +fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param]) + -> option<def> { + let n = 0u; + for tp: ast::ty_param in ty_params { + if str::eq(tp.ident, name) && alt e.current_tp { + some(cur) { n < cur } none { true } + } { ret some(ast::def_ty_param(local_def(tp.id), n)); } + n += 1u; + } + ret none; +} + +fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option<node_id> { + let found = none; + + pat_util::pat_bindings(e.def_map, pat) {|p_id, _sp, n| + if str::eq(path_to_ident(n), name) + { found = some(p_id); } + }; + ret found; +} + +fn lookup_in_fn(e: env, name: ident, decl: ast::fn_decl, + ty_params: [ast::ty_param], + ns: namespace) -> option<def> { + alt ns { + ns_val(value_or_enum) { + for a: ast::arg in decl.inputs { + if str::eq(a.ident, name) { + ret some(ast::def_arg(a.id, a.mode)); + } + } + ret none; + } + ns_type { ret lookup_in_ty_params(e, name, ty_params); } + _ { ret none; } + } +} + +/* + FIXME: not sure about this code. maybe this should be handled + using the mod_index stuff + */ +fn lookup_in_class(parent_id: def_id, + members: [@class_item], name: ident) + -> option<def> { + for m in members { + alt m.node.decl { + instance_var(v_name,_,_,id) { + if v_name == name { + ret some(def_class_field(parent_id, local_def(id))); + } + } + class_method(i) { + if i.ident == name { + ret some(def_class_method(parent_id, local_def(i.id))); + } + } + } + } + ret none; +} + +fn lookup_in_block(e: env, name: ident, sp: span, b: ast::blk_, pos: uint, + loc_pos: uint, ns: namespace) -> option<def> { + + let i = vec::len(b.stmts); + while i > 0u { + i -= 1u; + let st = b.stmts[i]; + alt st.node { + ast::stmt_decl(d, _) { + alt d.node { + ast::decl_local(locs) { + if i <= pos { + let j = vec::len(locs); + while j > 0u { + j -= 1u; + let loc = locs[j]; + if ns == ns_val(value_or_enum) + && (i < pos || j < loc_pos) { + alt lookup_in_pat(e, name, loc.node.pat) { + some(nid) { + ret some(ast::def_local(nid, + loc.node.is_mutbl)); + } + _ { } + } + } + } + } + } + ast::decl_item(it) { + alt it.node { + ast::item_enum(variants, _) { + if ns == ns_type { + if str::eq(it.ident, name) { + ret some(ast::def_ty(local_def(it.id))); + } + } else { + alt ns { + ns_val(_) { + for v: ast::variant in variants { + if str::eq(v.node.name, name) { + let i = v.node.id; + ret some(ast::def_variant + (local_def(it.id), local_def(i))); + } + } + } + _ {} + } + } + } + _ { + if str::eq(it.ident, name) { + let found = found_def_item(it, ns); + if !is_none(found) { + ret found; + } + } + } + } + } + } + } + _ { } + } + } + for vi in b.view_items { + + let is_import = false; + alt vi.node { + ast::view_item_import(_) { is_import = true; } + _ {} + } + + alt vi.node { + + ast::view_item_import(vps) | ast::view_item_export(vps) { + for vp in vps { + alt vp.node { + ast::view_path_simple(ident, _, id) { + if is_import && name == ident { + ret lookup_import(e, local_def(id), ns); + } + } + + ast::view_path_list(path, idents, _) { + for ident in idents { + if name == ident.node.name { + let def = local_def(ident.node.id); + ret lookup_import(e, def, ns); + } + } + } + + ast::view_path_glob(_, _) { + alt e.block_map.find(b.id) { + some(globs) { + let found = lookup_in_globs(e, globs, sp, name, + ns, inside); + if found != none { + ret found; + } + } + _ {} + } + } + } + } + } + _ { e.sess.span_bug(vi.span, "Unexpected view_item in block"); } + } + } + ret none; +} + +fn found_def_item(i: @ast::item, ns: namespace) -> option<def> { + alt i.node { + ast::item_const(_, _) { + if ns == ns_val(value_or_enum) { + ret some(ast::def_const(local_def(i.id))); } + } + ast::item_fn(decl, _, _) { + if ns == ns_val(value_or_enum) { + ret some(ast::def_fn(local_def(i.id), decl.purity)); + } + } + ast::item_mod(_) { + if ns == ns_module { ret some(ast::def_mod(local_def(i.id))); } + } + ast::item_native_mod(_) { + if ns == ns_module { ret some(ast::def_native_mod(local_def(i.id))); } + } + ast::item_ty(_, _) | item_iface(_, _) | item_enum(_, _) { + if ns == ns_type { ret some(ast::def_ty(local_def(i.id))); } + } + ast::item_res(_, _, _, _, ctor_id) { + alt ns { + ns_val(value_or_enum) { + ret some(ast::def_fn(local_def(ctor_id), ast::impure_fn)); + } + ns_type { ret some(ast::def_ty(local_def(i.id))); } + _ { } + } + } + ast::item_class(_, _, _, _, _) { + if ns == ns_type { + ret some(ast::def_class(local_def(i.id))); + } + } + ast::item_impl(_,_,_,_) { /* ??? */ } + } + ret none; +} + +fn lookup_in_mod_strict(e: env, m: def, sp: span, name: ident, + ns: namespace, dr: dir) -> option<def> { + alt lookup_in_mod(e, m, sp, name, ns, dr) { + none { + unresolved_err(e, in_mod(m), sp, name, ns_name(ns)); + ret none; + } + some(d) { ret some(d); } + } +} + +fn lookup_in_mod(e: env, m: def, sp: span, name: ident, ns: namespace, + dr: dir) -> option<def> { + let defid = def_id_of_def(m); + if defid.crate != ast::local_crate { + // examining a module in an external crate + let cached = e.ext_cache.find({did: defid, ident: name, ns: ns}); + if !is_none(cached) { ret cached; } + let path = [name]; + if defid.node != ast::crate_node_id { + path = cstore::get_path(e.cstore, defid) + path; + } + alt lookup_external(e, defid.crate, path, ns) { + some(df) { + e.ext_cache.insert({did: defid, ident: name, ns: ns}, df); + ret some(df); + } + _ { ret none; } + } + } + alt m { + ast::def_mod(defid) { + ret lookup_in_local_mod(e, defid.node, sp, name, ns, dr); + } + ast::def_native_mod(defid) { + ret lookup_in_local_native_mod(e, defid.node, sp, name, ns); + } + _ { + // Precondition + e.sess.span_bug(sp, "lookup_in_mod was passed a non-mod def"); + } + } +} + +fn found_view_item(e: env, id: node_id) -> option<def> { + alt cstore::find_use_stmt_cnum(e.cstore, id) { + some(cnum) { + some(ast::def_mod({crate: cnum, node: ast::crate_node_id})) + } + none { + // This can happen if we didn't load external crate info. + // Rustdoc depends on this. + none + } + } +} + +fn lookup_import(e: env, defid: def_id, ns: namespace) -> option<def> { + // Imports are simply ignored when resolving themselves. + if vec::contains(e.ignored_imports, defid.node) { ret none; } + alt e.imports.get(defid.node) { + todo(node_id, name, path, span, scopes) { + resolve_import(e, local_def(node_id), name, *path, span, scopes); + ret lookup_import(e, defid, ns); + } + resolving(sp) { + e.sess.span_err(sp, "cyclic import"); + ret none; + } + resolved(val, typ, md, _, _, _) { + if e.used_imports.track { + e.used_imports.data += [defid.node]; + } + ret alt ns { ns_val(_) { val } ns_type { typ } + ns_module { md } }; + } + is_glob(_,_,_) { + e.sess.bug("lookup_import: can't handle is_glob"); + } + } +} + +fn lookup_in_local_native_mod(e: env, node_id: node_id, sp: span, id: ident, + ns: namespace) -> option<def> { + ret lookup_in_local_mod(e, node_id, sp, id, ns, inside); +} + +fn is_exported(e: env, i: ident, m: @indexed_mod) -> bool { + + alt m.m { + some(_m) { + if ast_util::is_exported(i, _m) { ret true; } + } + _ {} + } + + ret vec::contains(m.globbed_exports, i) + || e.resolve_unexported; +} + +fn lookup_in_local_mod(e: env, node_id: node_id, sp: span, id: ident, + ns: namespace, dr: dir) -> option<def> { + let info = e.mod_map.get(node_id); + if dr == outside && !is_exported(e, id, info) { + // if we're in a native mod, then dr==inside, so info.m is some _mod + ret none; // name is not visible + } + alt info.index.find(id) { + none { } + some(lst) { + let found = list::find(lst, bind lookup_in_mie(e, _, ns)); + if !is_none(found) { + ret found; + } + } + } + // not local or explicitly imported; try globs: + ret lookup_glob_in_mod(e, info, sp, id, ns, outside); +} + +fn lookup_in_globs(e: env, globs: [glob_imp_def], sp: span, id: ident, + ns: namespace, dr: dir) -> option<def> { + fn lookup_in_mod_(e: env, def: glob_imp_def, sp: span, name: ident, + ns: namespace, dr: dir) -> option<glob_imp_def> { + alt def.path.node { + + ast::view_path_glob(_, id) { + if vec::contains(e.ignored_imports, id) { ret none; } + } + + _ { + e.sess.span_bug(sp, "lookup_in_globs: not a glob"); + } + } + alt lookup_in_mod(e, def.def, sp, name, ns, dr) { + some(d) { option::some({def: d, path: def.path}) } + none { none } + } + } + let matches = vec::filter_map(copy globs, + bind lookup_in_mod_(e, _, sp, id, ns, dr)); + if vec::len(matches) == 0u { + ret none; + } + else if vec::len(matches) == 1u || ns == ns_val(definite_enum) { + ret some(matches[0].def); + } else { + for match: glob_imp_def in matches { + let sp = match.path.span; + e.sess.span_note(sp, #fmt["'%s' is imported here", id]); + } + e.sess.span_fatal(sp, "'" + id + "' is glob-imported from" + + " multiple different modules."); + } +} + +fn lookup_glob_in_mod(e: env, info: @indexed_mod, sp: span, id: ident, + wanted_ns: namespace, dr: dir) -> option<def> { + // since we don't know what names we have in advance, + // absence takes the place of todo() + if !info.glob_imported_names.contains_key(id) { + info.glob_imported_names.insert(id, glob_resolving(sp)); + // kludge + let val_ns = if wanted_ns == ns_val(definite_enum) { + ns_val(definite_enum) + } else { + ns_val(value_or_enum) + }; + let globs = info.glob_imports; + let val = lookup_in_globs(e, globs, sp, id, val_ns, dr); + let typ = lookup_in_globs(e, globs, sp, id, ns_type, dr); + let md = lookup_in_globs(e, globs, sp, id, ns_module, dr); + info.glob_imported_names.insert(id, glob_resolved(val, typ, md)); + } + alt info.glob_imported_names.get(id) { + glob_resolving(sp) { + ret none; + } + glob_resolved(val, typ, md) { + ret alt wanted_ns { + ns_val(_) { val } + ns_type { typ } + ns_module { md } + }; + } + } +} + +fn lookup_in_mie(e: env, mie: mod_index_entry, ns: namespace) -> + option<def> { + alt mie { + mie_view_item(_, id, _) { + if ns == ns_module { ret found_view_item(e, id); } + } + mie_import_ident(id, _) { ret lookup_import(e, local_def(id), ns); } + mie_item(item) { ret found_def_item(item, ns); } + mie_enum_variant(variant_idx, variants, parent_id, parent_span) { + alt ns { + ns_val(_) { + let vid = variants[variant_idx].node.id; + ret some(ast::def_variant(local_def(parent_id), + local_def(vid))); + } + _ { ret none; } + } + } + mie_native_item(native_item) { + alt native_item.node { + ast::native_item_fn(decl, _) { + if ns == ns_val(value_or_enum) { + ret some(ast::def_fn(local_def(native_item.id), + decl.purity)); + } + } + } + } + mie_class_item(parent_id, class_item) { + alt class_item.node.decl { + instance_var(_,_,_,id) { + ret some(ast::def_class_field(local_def(parent_id), + local_def(id))); + } + class_method(it) { + ret some(ast::def_class_method(local_def(parent_id), + local_def(it.id))); + } + } + } + } + ret none; +} + + +// Module indexing +fn add_to_index(index: hashmap<ident, list<mod_index_entry>>, id: ident, + ent: mod_index_entry) { + alt index.find(id) { + none { index.insert(id, cons(ent, @nil::<mod_index_entry>)); } + some(prev) { index.insert(id, cons(ent, @prev)); } + } +} + +fn index_view_items(view_items: [@ast::view_item], + index: hashmap<ident, list<mod_index_entry>>) { + for vi in view_items { + alt vi.node { + ast::view_item_use(ident, _, id) { + add_to_index(index, ident, mie_view_item(ident, id, vi.span)); + } + _ {} + } + + iter_effective_import_paths(*vi) {|vp| + alt vp.node { + ast::view_path_simple(ident, _, id) { + add_to_index(index, ident, mie_import_ident(id, vp.span)); + } + ast::view_path_list(_, idents, _) { + for ident in idents { + add_to_index(index, ident.node.name, + mie_import_ident(ident.node.id, + ident.span)); + } + } + + // globbed imports have to be resolved lazily. + ast::view_path_glob(_, _) {} + } + } + } +} + +fn index_mod(md: ast::_mod) -> mod_index { + let index = new_str_hash::<list<mod_index_entry>>(); + + index_view_items(md.view_items, index); + + for it: @ast::item in md.items { + alt it.node { + ast::item_const(_, _) | ast::item_fn(_, _, _) | ast::item_mod(_) | + ast::item_native_mod(_) | ast::item_ty(_, _) | + ast::item_res(_, _, _, _, _) | + ast::item_impl(_, _, _, _) | ast::item_iface(_, _) { + add_to_index(index, it.ident, mie_item(it)); + } + ast::item_enum(variants, _) { + add_to_index(index, it.ident, mie_item(it)); + let variant_idx: uint = 0u; + for v: ast::variant in variants { + add_to_index(index, v.node.name, + mie_enum_variant(variant_idx, variants, + it.id, it.span)); + variant_idx += 1u; + } + } + ast::item_class(tps, items, ctor_id, ctor_decl, ctor_body) { + // add the class name itself + add_to_index(index, it.ident, mie_item(it)); + // add the constructor decl + add_to_index(index, it.ident, + mie_item(@{ident: it.ident, attrs: [], + id: ctor_id, + node: + item_fn(ctor_decl, tps, ctor_body), + span: ctor_body.span})); + // add the members + for ci in items { + add_to_index(index, class_item_ident(ci), + mie_class_item(it.id, ci)); + } + } + } + } + ret index; +} + + +fn index_nmod(md: ast::native_mod) -> mod_index { + let index = new_str_hash::<list<mod_index_entry>>(); + + index_view_items(md.view_items, index); + + for it: @ast::native_item in md.items { + add_to_index(index, it.ident, mie_native_item(it)); + } + ret index; +} + + +// External lookups +fn ns_for_def(d: def) -> namespace { + alt d { + ast::def_variant(_, _) { ns_val(definite_enum) } + ast::def_fn(_, _) | ast::def_self(_) | + ast::def_const(_) | ast::def_arg(_, _) | ast::def_local(_, _) | + ast::def_upvar(_, _, _) | ast::def_self(_) | + ast::def_class_field(_,_) | ast::def_class_method(_,_) + { ns_val(value_or_enum) } + ast::def_mod(_) | ast::def_native_mod(_) { ns_module } + ast::def_ty(_) | ast::def_binding(_) | ast::def_use(_) | + ast::def_ty_param(_, _) | ast::def_prim_ty(_) | ast::def_class(_) + { ns_type } + } +} + +// if we're searching for a value, it's ok if we found +// a enum +fn ns_ok(wanted:namespace, actual:namespace) -> bool { + alt actual { + ns_val(definite_enum) { + alt wanted { + ns_val(_) { true } + _ { false } + } + } + _ { wanted == actual} + } +} + +fn lookup_external(e: env, cnum: int, ids: [ident], ns: namespace) -> + option<def> { + for d: def in csearch::lookup_defs(e.sess.cstore, cnum, ids) { + e.ext_map.insert(def_id_of_def(d), ids); + if ns_ok(ns, ns_for_def(d)) { ret some(d); } + } + ret none; +} + + +// Collision detection +fn check_for_collisions(e: @env, c: ast::crate) { + // Module indices make checking those relatively simple -- just check each + // name for multiple entities in the same namespace. + e.mod_map.values {|val| + val.index.items {|k, v| check_mod_name(*e, k, v); }; + }; + // Other scopes have to be checked the hard way. + let v = + @{visit_item: bind check_item(e, _, _, _), + visit_block: bind check_block(e, _, _, _), + visit_arm: bind check_arm(e, _, _, _), + visit_expr: bind check_expr(e, _, _, _), + visit_ty: bind check_ty(e, _, _, _) with *visit::default_visitor()}; + visit::visit_crate(c, (), visit::mk_vt(v)); +} + +fn check_mod_name(e: env, name: ident, entries: list<mod_index_entry>) { + let saw_mod = false; + let saw_type = false; + let saw_value = false; + let entries = entries; + fn dup(e: env, sp: span, word: str, name: ident) { + e.sess.span_fatal(sp, "duplicate definition of " + word + name); + } + while true { + alt entries { + cons(entry, rest) { + if !is_none(lookup_in_mie(e, entry, ns_val(value_or_enum))) { + if saw_value { + dup(e, mie_span(entry), "", name); + } else { saw_value = true; } + } + if !is_none(lookup_in_mie(e, entry, ns_type)) { + if saw_type { + dup(e, mie_span(entry), "type ", name); + } else { saw_type = true; } + } + if !is_none(lookup_in_mie(e, entry, ns_module)) { + if saw_mod { + dup(e, mie_span(entry), "module ", name); + } else { saw_mod = true; } + } + entries = *rest; + } + nil { break; } + } + } +} + +fn mie_span(mie: mod_index_entry) -> span { + ret alt mie { + mie_view_item(_, _, span) { span } + mie_import_ident(_, span) { span } + mie_item(item) { item.span } + mie_enum_variant(_, _, _, span) { span } + mie_native_item(item) { item.span } + mie_class_item(_,item) { item.span } + }; +} + +fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) { + fn typaram_names(tps: [ast::ty_param]) -> [ident] { + let x: [ast::ident] = []; + for tp: ast::ty_param in tps { x += [tp.ident]; } + ret x; + } + visit::visit_item(i, x, v); + alt i.node { + ast::item_fn(decl, ty_params, _) { + check_fn(*e, i.span, decl); + ensure_unique(*e, i.span, typaram_names(ty_params), ident_id, + "type parameter"); + } + ast::item_enum(_, ty_params) { + ensure_unique(*e, i.span, typaram_names(ty_params), ident_id, + "type parameter"); + } + _ { } + } +} + +fn check_pat(e: @env, ch: checker, p: @ast::pat) { + pat_util::pat_bindings(e.def_map, p) {|_i, p_sp, n| + add_name(ch, p_sp, path_to_ident(n)); + }; +} + +fn check_arm(e: @env, a: ast::arm, &&x: (), v: vt<()>) { + visit::visit_arm(a, x, v); + let ch0 = checker(*e, "binding"); + check_pat(e, ch0, a.pats[0]); + let seen0 = ch0.seen; + let i = vec::len(a.pats); + while i > 1u { + i -= 1u; + let ch = checker(*e, "binding"); + check_pat(e, ch, a.pats[i]); + + // Ensure the bindings introduced in this pattern are the same as in + // the first pattern. + if vec::len(ch.seen) != vec::len(seen0) { + e.sess.span_err(a.pats[i].span, + "inconsistent number of bindings"); + } else { + for name: ident in ch.seen { + if is_none(vec::find(seen0, bind str::eq(name, _))) { + // Fight the alias checker + let name_ = name; + e.sess.span_err(a.pats[i].span, + "binding " + name_ + + " does not occur in first pattern"); + } + } + } + } +} + +fn check_block(e: @env, b: ast::blk, &&x: (), v: vt<()>) { + visit::visit_block(b, x, v); + let values = checker(*e, "value"); + let types = checker(*e, "type"); + let mods = checker(*e, "module"); + for st: @ast::stmt in b.node.stmts { + alt st.node { + ast::stmt_decl(d, _) { + alt d.node { + ast::decl_local(locs) { + let local_values = checker(*e, "value"); + for loc in locs { + pat_util::pat_bindings(e.def_map, loc.node.pat) + {|_i, p_sp, n| + let ident = path_to_ident(n); + add_name(local_values, p_sp, ident); + check_name(values, p_sp, ident); + }; + } + } + ast::decl_item(it) { + alt it.node { + ast::item_enum(variants, _) { + add_name(types, it.span, it.ident); + for v: ast::variant in variants { + add_name(values, v.span, v.node.name); + } + } + ast::item_mod(_) | ast::item_native_mod(_) { + add_name(mods, it.span, it.ident); + } + ast::item_const(_, _) | ast::item_fn(_, _, _) { + add_name(values, it.span, it.ident); + } + ast::item_ty(_, _) | ast::item_iface(_, _) { + add_name(types, it.span, it.ident); + } + ast::item_res(_, _, _, _, _) { + add_name(types, it.span, it.ident); + add_name(values, it.span, it.ident); + } + _ { } + } + } + } + } + _ { } + } + } +} + +fn check_fn(e: env, sp: span, decl: ast::fn_decl) { + fn arg_name(a: ast::arg) -> ident { ret a.ident; } + ensure_unique(e, sp, decl.inputs, arg_name, "argument"); +} + +fn check_expr(e: @env, ex: @ast::expr, &&x: (), v: vt<()>) { + alt ex.node { + ast::expr_rec(fields, _) { + fn field_name(f: ast::field) -> ident { ret f.node.ident; } + ensure_unique(*e, ex.span, fields, field_name, "field"); + } + _ { } + } + visit::visit_expr(ex, x, v); +} + +fn check_ty(e: @env, ty: @ast::ty, &&x: (), v: vt<()>) { + alt ty.node { + ast::ty_rec(fields) { + fn field_name(f: ast::ty_field) -> ident { ret f.node.ident; } + ensure_unique(*e, ty.span, fields, field_name, "field"); + } + _ { } + } + visit::visit_ty(ty, x, v); +} + +type checker = @{mutable seen: [ident], kind: str, sess: session}; + +fn checker(e: env, kind: str) -> checker { + let seen: [ident] = []; + ret @{mutable seen: seen, kind: kind, sess: e.sess}; +} + +fn check_name(ch: checker, sp: span, name: ident) { + for s: ident in ch.seen { + if str::eq(s, name) { + ch.sess.span_fatal(sp, "duplicate " + ch.kind + " name: " + name); + } + } +} +fn add_name(ch: checker, sp: span, name: ident) { + check_name(ch, sp, name); + ch.seen += [name]; +} + +fn ident_id(&&i: ident) -> ident { ret i; } + +fn ensure_unique<T>(e: env, sp: span, elts: [T], id: fn(T) -> ident, + kind: str) { + let ch = checker(e, kind); + for elt: T in elts { add_name(ch, sp, id(elt)); } +} + +fn check_exports(e: @env) { + + fn iter_mod(e: env, m: def, sp: span, _dr: dir, + f: fn(ident: ident, def: def)) { + let defid = def_id_of_def(m); + + if defid.crate != ast::local_crate { + // FIXME: ought to support external export-globs eventually. + e.sess.span_unimpl(sp, "glob-export of items in external crate"); + } else { + + let mid = def_id_of_def(m); + assert mid.crate == ast::local_crate; + let ixm = e.mod_map.get(mid.node); + + ixm.index.items() {|ident, mies| + list::iter(mies) {|mie| + alt mie { + mie_item(item) { + let defs = + [ found_def_item(item, ns_val(value_or_enum)), + found_def_item(item, ns_type), + found_def_item(item, ns_module) ]; + for d in defs { + alt d { + some(def) { + f(ident, def); + } + _ {} + } + } + } + _ { + let s = "glob-export from mod with non-items"; + e.sess.span_unimpl(sp, s); + } + } + } + } + } + } + + + + fn lookup_glob_any(e: @env, info: @indexed_mod, sp: span, path: str, + ident: ident) -> bool { + let lookup = + bind lookup_glob_in_mod(*e, info, sp, ident, _, inside); + let (m, v, t) = (lookup(ns_module), + lookup(ns_val(value_or_enum)), + lookup(ns_type)); + let full_path = path + ident; + maybe_add_reexport(e, full_path, m); + maybe_add_reexport(e, full_path, v); + maybe_add_reexport(e, full_path, t); + is_some(m) || is_some(v) || is_some(t) + } + + fn maybe_add_reexport(e: @env, path: str, def: option<def>) { + alt def { + some(def) { + alt e.exp_map.find(path) { + some(v) { + // If there are multiple reexports of the same def + // using the same path, then we only need one copy + if !vec::contains(*v, def) { + *v += [def]; + } + } + none { e.exp_map.insert(path, @mutable [def]); } + } + } + _ {} + } + } + + fn check_export(e: @env, ident: str, _mod: @indexed_mod, + vi: @view_item) { + let found_something = false; + let full_path = _mod.path + ident; + if _mod.index.contains_key(ident) { + found_something = true; + let xs = _mod.index.get(ident); + list::iter(xs) {|x| + alt x { + mie_import_ident(id, _) { + alt e.imports.get(id) { + resolved(v, t, m, _, rid, _) { + maybe_add_reexport(e, full_path, v); + maybe_add_reexport(e, full_path, t); + maybe_add_reexport(e, full_path, m); + } + _ { } + } + } + _ { } + } + } + } + found_something |= lookup_glob_any(e, _mod, vi.span, + _mod.path, ident); + if !found_something { + e.sess.span_warn(vi.span, + #fmt("exported item %s is not defined", ident)); + } + } + + fn check_enum_ok(e: @env, sp:span, id: ident, _mod: @indexed_mod) + -> node_id { + alt _mod.index.find(id) { + none { e.sess.span_fatal(sp, #fmt("error: undefined id %s \ + in an export", id)); } + some(ms) { + let maybe_id = list::find(ms) {|m| + alt m { + mie_item(an_item) { + alt an_item.node { + item_enum(_,_) { /* OK */ some(an_item.id) } + _ { none } + } + } + _ { none } + } + }; + alt maybe_id { + some(an_id) { ret an_id; } + _ { e.sess.span_fatal(sp, #fmt("error: %s does not refer \ + to an enumeration", id)); } + } + } + } + } + + fn check_export_enum_list(e: @env, _mod: @indexed_mod, + span: codemap::span, id: ast::ident, + ids: [ast::path_list_ident]) { + if vec::len(ids) == 0u { + let _ = check_enum_ok(e, span, id, _mod); + } else { + let parent_id = check_enum_ok(e, span, id, _mod); + for variant_id in ids { + alt _mod.index.find(variant_id.node.name) { + some(ms) { + list::iter(ms) {|m| + alt m { + mie_enum_variant(_, _, actual_parent_id, _) { + if actual_parent_id != parent_id { + let msg = #fmt("variant %s \ + doesn't belong to enum %s", + variant_id.node.name, + id); + e.sess.span_err(span, msg); + } + } + _ { + e.sess.span_err(span, + #fmt("%s is not a variant", + variant_id.node.name)); + } + } + } + } + _ { + e.sess.span_err(span, + #fmt("%s is not a variant", + variant_id.node.name)); + } + } + } + } + } + + e.mod_map.values {|_mod| + alt _mod.m { + some(m) { + let glob_is_re_exported = new_int_hash(); + + for vi in m.view_items { + iter_export_paths(*vi) { |vp| + alt vp.node { + ast::view_path_simple(ident, _, _) { + check_export(e, ident, _mod, vi); + } + ast::view_path_list(path, ids, _) { + let id = if vec::len(*path) == 1u { + path[0] + } else { + e.sess.span_fatal(vp.span, + #fmt("bad export name-list")) + }; + check_export_enum_list(e, _mod, vp.span, id, ids); + } + ast::view_path_glob(_, node_id) { + glob_is_re_exported.insert(node_id, ()); + } + } + } + } + // Now follow the export-glob links and fill in the + // globbed_exports and exp_map lists. + for glob in _mod.glob_imports { + alt check glob.path.node { + ast::view_path_glob(path, node_id) { + if ! glob_is_re_exported.contains_key(node_id) { + cont; + } + } + } + iter_mod(*e, glob.def, + glob.path.span, outside) {|ident, def| + let full_path = _mod.path + ident; + _mod.globbed_exports += [ident]; + maybe_add_reexport(e, full_path, some(def)); + } + } + } + none { } + } + } +} + +// Impl resolution + +type method_info = {did: def_id, n_tps: uint, ident: ast::ident}; +type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]}; +type iscopes = list<@[@_impl]>; + +fn resolve_impls(e: @env, c: @ast::crate) { + visit::visit_crate(*c, nil, visit::mk_vt(@{ + visit_block: bind visit_block_with_impl_scope(e, _, _, _), + visit_mod: bind visit_mod_with_impl_scope(e, _, _, _, _, _), + visit_expr: bind resolve_impl_in_expr(e, _, _, _) + with *visit::default_visitor() + })); +} + +fn find_impls_in_view_item(e: env, vi: @ast::view_item, + &impls: [@_impl], sc: option<iscopes>) { + fn lookup_imported_impls(e: env, id: ast::node_id, + act: fn(@[@_impl])) { + alt e.imports.get(id) { + resolved(_, _, _, is, _, _) { act(is); } + todo(node_id, name, path, span, scopes) { + resolve_import(e, local_def(node_id), name, *path, span, + scopes); + alt check e.imports.get(id) { + resolved(_, _, _, is, _, _) { act(is); } + } + } + _ {} + } + } + + iter_effective_import_paths(*vi) { |vp| + alt vp.node { + ast::view_path_simple(name, pt, id) { + let found = []; + if vec::len(*pt) == 1u { + option::may(sc) {|sc| + list::iter(sc) {|level| + if vec::len(found) > 0u { ret; } + for imp in *level { + if imp.ident == pt[0] { + found += [@{ident: name with *imp}]; + } + } + if vec::len(found) > 0u { impls += found; } + } + } + } else { + lookup_imported_impls(e, id) {|is| + for i in *is { impls += [@{ident: name with *i}]; } + } + } + } + + ast::view_path_list(base, names, _) { + for nm in names { + lookup_imported_impls(e, nm.node.id) {|is| impls += *is; } + } + } + + ast::view_path_glob(ids, id) { + alt check e.imports.get(id) { + is_glob(path, sc, sp) { + alt follow_import(e, sc, *path, sp) { + some(def) { find_impls_in_mod(e, def, impls, none); } + _ {} + } + } + } + } + } + } +} + +fn find_impls_in_item(e: env, i: @ast::item, &impls: [@_impl], + name: option<ident>, + ck_exports: option<@indexed_mod>) { + alt i.node { + ast::item_impl(_, ifce, _, mthds) { + if alt name { some(n) { n == i.ident } _ { true } } && + alt ck_exports { + some(m) { is_exported(e, i.ident, m) } + _ { true } + } { + impls += [@{did: local_def(i.id), + ident: i.ident, + methods: vec::map(mthds, {|m| + @{did: local_def(m.id), + n_tps: vec::len(m.tps), + ident: m.ident} + })}]; + } + } + _ {} + } +} + +fn find_impls_in_mod_by_id(e: env, defid: def_id, &impls: [@_impl], + name: option<ident>) { + let cached; + alt e.impl_cache.find(defid) { + some(some(v)) { cached = v; } + some(none) { ret; } + none { + e.impl_cache.insert(defid, none); + cached = if defid.crate == ast::local_crate { + let tmp = []; + let mi = e.mod_map.get(defid.node); + let md = option::get(mi.m); + for vi in md.view_items { + find_impls_in_view_item(e, vi, tmp, none); + } + for i in md.items { + find_impls_in_item(e, i, tmp, none, none); + } + @vec::filter(tmp) {|i| is_exported(e, i.ident, mi)} + } else { + csearch::get_impls_for_mod(e.sess.cstore, defid, none) + }; + e.impl_cache.insert(defid, some(cached)); + } + } + alt name { + some(n) { + for im in *cached { + if n == im.ident { impls += [im]; } + } + } + _ { impls += *cached; } + } +} + +fn find_impls_in_mod(e: env, m: def, &impls: [@_impl], + name: option<ident>) { + alt m { + ast::def_mod(defid) { + find_impls_in_mod_by_id(e, defid, impls, name); + } + _ {} + } +} + +fn visit_block_with_impl_scope(e: @env, b: ast::blk, sc: iscopes, + v: vt<iscopes>) { + let impls = []; + for vi in b.node.view_items { + find_impls_in_view_item(*e, vi, impls, some(sc)); + } + for st in b.node.stmts { + alt st.node { + ast::stmt_decl(@{node: ast::decl_item(i), _}, _) { + find_impls_in_item(*e, i, impls, none, none); + } + _ {} + } + } + let sc = if vec::len(impls) > 0u { cons(@impls, @sc) } else { sc }; + visit::visit_block(b, sc, v); +} + +fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, id: node_id, + sc: iscopes, v: vt<iscopes>) { + let impls = []; + for vi in m.view_items { + find_impls_in_view_item(*e, vi, impls, some(sc)); + } + for i in m.items { find_impls_in_item(*e, i, impls, none, none); } + let impls = @impls; + visit::visit_mod(m, s, id, if vec::len(*impls) > 0u { + cons(impls, @sc) + } else { + sc + }, v); + e.impl_map.insert(id, cons(impls, @nil)); +} + +fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt<iscopes>) { + alt x.node { + // Store the visible impls in all exprs that might need them + ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) | + ast::expr_binary(_, _, _) | ast::expr_unary(_, _) | + ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) { + e.impl_map.insert(x.id, sc); + } + _ {} + } + visit::visit_expr(x, sc, v); +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs new file mode 100644 index 00000000000..29200a47287 --- /dev/null +++ b/src/rustc/middle/trans/alt.rs @@ -0,0 +1,741 @@ +import driver::session::session; +import lib::llvm::llvm; +import lib::llvm::{ValueRef, BasicBlockRef}; +import pat_util::*; +import build::*; +import base::*; +import syntax::ast; +import syntax::ast_util; +import syntax::ast_util::{dummy_sp}; +import syntax::ast::def_id; +import syntax::codemap::span; +import syntax::print::pprust::pat_to_str; +import back::abi; +import resolve::def_map; + +import common::*; + +// An option identifying a branch (either a literal, a enum variant or a +// range) +enum opt { + lit(@ast::expr), + var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}), + range(@ast::expr, @ast::expr) +} +fn opt_eq(a: opt, b: opt) -> bool { + alt (a, b) { + (lit(a), lit(b)) { ast_util::compare_lit_exprs(a, b) == 0 } + (range(a1, a2), range(b1, b2)) { + ast_util::compare_lit_exprs(a1, b1) == 0 && + ast_util::compare_lit_exprs(a2, b2) == 0 + } + (var(a, _), var(b, _)) { a == b } + _ { false } + } +} + +enum opt_result { + single_result(result), + range_result(result, result), +} +fn trans_opt(bcx: block, o: opt) -> opt_result { + let ccx = bcx.ccx(), bcx = bcx; + alt o { + lit(l) { + alt l.node { + ast::expr_lit(@{node: ast::lit_str(s), _}) { + let strty = ty::mk_str(bcx.tcx()); + let cell = empty_dest_cell(); + bcx = tvec::trans_str(bcx, s, by_val(cell)); + add_clean_temp(bcx, *cell, strty); + ret single_result(rslt(bcx, *cell)); + } + _ { + ret single_result( + rslt(bcx, trans_const_expr(ccx, l))); + } + } + } + var(disr_val, _) { ret single_result(rslt(bcx, C_int(ccx, disr_val))); } + range(l1, l2) { + ret range_result(rslt(bcx, trans_const_expr(ccx, l1)), + rslt(bcx, trans_const_expr(ccx, l2))); + } + } +} + +fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> opt { + let vdef = ast_util::variant_def_ids(tcx.def_map.get(pat_id)); + let variants = ty::enum_variants(tcx, vdef.enm); + for v: ty::variant_info in *variants { + if vdef.var == v.id { ret var(v.disr_val, vdef); } + } + fail; +} + +type bind_map = [{ident: ast::ident, val: ValueRef}]; +fn assoc(key: str, list: bind_map) -> option<ValueRef> { + for elt: {ident: ast::ident, val: ValueRef} in list { + if str::eq(elt.ident, key) { ret some(elt.val); } + } + ret none; +} + +type match_branch = + @{pats: [@ast::pat], + bound: bind_map, + data: @{body: BasicBlockRef, + guard: option<@ast::expr>, + id_map: pat_id_map}}; +type match = [match_branch]; + +fn has_nested_bindings(m: match, col: uint) -> bool { + for br in m { + alt br.pats[col].node { + ast::pat_ident(_, some(_)) { ret true; } + _ {} + } + } + ret false; +} + +fn expand_nested_bindings(m: match, col: uint, val: ValueRef) -> match { + let result = []; + for br in m { + alt br.pats[col].node { + ast::pat_ident(name, some(inner)) { + let pats = vec::slice(br.pats, 0u, col) + [inner] + + vec::slice(br.pats, col + 1u, br.pats.len()); + result += [@{pats: pats, + bound: br.bound + [{ident: path_to_ident(name), + val: val}] + with *br}]; + } + _ { result += [br]; } + } + } + result +} + +type enter_pat = fn(@ast::pat) -> option<[@ast::pat]>; + +fn enter_match(dm: def_map, m: match, col: uint, val: ValueRef, + e: enter_pat) -> match { + let result = []; + for br: match_branch in m { + alt e(br.pats[col]) { + some(sub) { + let pats = sub + vec::slice(br.pats, 0u, col) + + vec::slice(br.pats, col + 1u, br.pats.len()); + let self = br.pats[col]; + let bound = alt self.node { + ast::pat_ident(name, none) if !pat_is_variant(dm, self) { + br.bound + [{ident: path_to_ident(name), val: val}] + } + _ { br.bound } + }; + result += [@{pats: pats, bound: bound with *br}]; + } + none { } + } + } + ret result; +} + +fn enter_default(dm: def_map, m: match, col: uint, val: ValueRef) -> match { + enter_match(dm, m, col, val) {|p| + alt p.node { + ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) { some([]) } + ast::pat_ident(_, none) if !pat_is_variant(dm, p) { + some([]) + } + _ { none } + } + } +} + +fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint, + variant_size: uint, val: ValueRef) -> match { + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + enter_match(tcx.def_map, m, col, val) {|p| + alt p.node { + ast::pat_enum(_, subpats) { + if opt_eq(variant_opt(tcx, p.id), opt) { some(subpats) } + else { none } + } + ast::pat_ident(_, none) if pat_is_variant(tcx.def_map, p) { + if opt_eq(variant_opt(tcx, p.id), opt) { some([]) } + else { none } + } + ast::pat_lit(l) { + if opt_eq(lit(l), opt) { some([]) } else { none } + } + ast::pat_range(l1, l2) { + if opt_eq(range(l1, l2), opt) { some([]) } else { none } + } + _ { some(vec::init_elt(variant_size, dummy)) } + } + } +} + +fn enter_rec(dm: def_map, m: match, col: uint, fields: [ast::ident], + val: ValueRef) -> match { + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + enter_match(dm, m, col, val) {|p| + alt p.node { + ast::pat_rec(fpats, _) { + let pats = []; + for fname: ast::ident in fields { + let pat = dummy; + for fpat: ast::field_pat in fpats { + if str::eq(fpat.ident, fname) { pat = fpat.pat; break; } + } + pats += [pat]; + } + some(pats) + } + _ { some(vec::init_elt(fields.len(), dummy)) } + } + } +} + +fn enter_tup(dm: def_map, m: match, col: uint, val: ValueRef, + n_elts: uint) -> match { + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + enter_match(dm, m, col, val) {|p| + alt p.node { + ast::pat_tup(elts) { some(elts) } + _ { some(vec::init_elt(n_elts, dummy)) } + } + } +} + +fn enter_box(dm: def_map, m: match, col: uint, val: ValueRef) -> match { + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + enter_match(dm, m, col, val) {|p| + alt p.node { + ast::pat_box(sub) { some([sub]) } + _ { some([dummy]) } + } + } +} + +fn enter_uniq(dm: def_map, m: match, col: uint, val: ValueRef) -> match { + let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()}; + enter_match(dm, m, col, val) {|p| + alt p.node { + ast::pat_uniq(sub) { some([sub]) } + _ { some([dummy]) } + } + } +} + +fn get_options(ccx: crate_ctxt, m: match, col: uint) -> [opt] { + fn add_to_set(&set: [opt], val: opt) { + for l in set { if opt_eq(l, val) { ret; } } + set += [val]; + } + + let found = []; + for br in m { + let cur = br.pats[col]; + if pat_is_variant(ccx.tcx.def_map, cur) { + add_to_set(found, variant_opt(ccx.tcx, br.pats[col].id)); + } else { + alt cur.node { + ast::pat_lit(l) { add_to_set(found, lit(l)); } + ast::pat_range(l1, l2) { + add_to_set(found, range(l1, l2)); + } + _ {} + } + } + } + ret found; +} + +fn extract_variant_args(bcx: block, pat_id: ast::node_id, + vdefs: {enm: def_id, var: def_id}, val: ValueRef) -> + {vals: [ValueRef], bcx: block} { + let ccx = bcx.fcx.ccx, bcx = bcx; + // invariant: + // pat_id must have the same length ty_param_substs as vdefs? + let ty_param_substs = node_id_type_params(bcx, pat_id); + let blobptr = val; + let variants = ty::enum_variants(ccx.tcx, vdefs.enm); + let args = []; + let size = ty::enum_variant_with_id(ccx.tcx, vdefs.enm, + vdefs.var).args.len(); + if size > 0u && (*variants).len() != 1u { + let enumptr = + PointerCast(bcx, val, T_opaque_enum_ptr(ccx)); + blobptr = GEPi(bcx, enumptr, [0, 1]); + } + let i = 0u; + let vdefs_tg = vdefs.enm; + let vdefs_var = vdefs.var; + while i < size { + let r = + // invariant needed: + // how do we know it even makes sense to pass in ty_param_substs + // here? What if it's [] and the enum type has variables in it? + GEP_enum(bcx, blobptr, vdefs_tg, vdefs_var, + ty_param_substs, i); + bcx = r.bcx; + args += [r.val]; + i += 1u; + } + ret {vals: args, bcx: bcx}; +} + +fn collect_record_fields(m: match, col: uint) -> [ast::ident] { + let fields = []; + for br: match_branch in m { + alt br.pats[col].node { + ast::pat_rec(fs, _) { + for f: ast::field_pat in fs { + if !vec::any(fields, bind str::eq(f.ident, _)) { + fields += [f.ident]; + } + } + } + _ { } + } + } + ret fields; +} + +fn any_box_pat(m: match, col: uint) -> bool { + for br: match_branch in m { + alt br.pats[col].node { ast::pat_box(_) { ret true; } _ { } } + } + ret false; +} + +fn any_uniq_pat(m: match, col: uint) -> bool { + for br: match_branch in m { + alt br.pats[col].node { ast::pat_uniq(_) { ret true; } _ { } } + } + ret false; +} + +fn any_tup_pat(m: match, col: uint) -> bool { + for br: match_branch in m { + alt br.pats[col].node { ast::pat_tup(_) { ret true; } _ { } } + } + ret false; +} + +type exit_node = {bound: bind_map, from: BasicBlockRef, to: BasicBlockRef}; +type mk_fail = fn@() -> BasicBlockRef; + +fn pick_col(m: match) -> uint { + fn score(p: @ast::pat) -> uint { + alt p.node { + ast::pat_lit(_) | ast::pat_enum(_, _) | ast::pat_range(_, _) { 1u } + ast::pat_ident(_, some(p)) { score(p) } + _ { 0u } + } + } + let scores = vec::to_mut(vec::init_elt(m[0].pats.len(), 0u)); + for br: match_branch in m { + let i = 0u; + for p: @ast::pat in br.pats { scores[i] += score(p); i += 1u; } + } + let max_score = 0u; + let best_col = 0u; + let i = 0u; + for score: uint in scores { + // Irrefutable columns always go first, they'd only be duplicated in + // the branches. + if score == 0u { ret i; } + // If no irrefutable ones are found, we pick the one with the biggest + // branching factor. + if score > max_score { max_score = score; best_col = i; } + i += 1u; + } + ret best_col; +} + +fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail, + &exits: [exit_node]) { + let bcx = bcx, tcx = bcx.tcx(), dm = tcx.def_map; + if m.len() == 0u { Br(bcx, f()); ret; } + if m[0].pats.len() == 0u { + let data = m[0].data; + alt data.guard { + some(e) { + // Temporarily set bindings. They'll be rewritten to PHI nodes + // for the actual arm block. + data.id_map.items {|key, val| + let loc = local_mem(option::get(assoc(key, m[0].bound))); + bcx.fcx.lllocals.insert(val, loc); + }; + let {bcx: guard_cx, val} = with_scope_result(bcx, "guard") {|bcx| + trans_temp_expr(bcx, e) + }; + bcx = with_cond(guard_cx, Not(guard_cx, val)) {|bcx| + compile_submatch(bcx, vec::tail(m), vals, f, exits); + bcx + }; + } + _ { } + } + if !bcx.unreachable { + exits += [{bound: m[0].bound, from: bcx.llbb, to: data.body}]; + } + Br(bcx, data.body); + ret; + } + + let col = pick_col(m); + let val = vals[col]; + let m = if has_nested_bindings(m, col) { + expand_nested_bindings(m, col, val) + } else { + m + }; + + let vals_left = + vec::slice(vals, 0u, col) + + vec::slice(vals, col + 1u, vals.len()); + let ccx = bcx.fcx.ccx; + let pat_id = 0; + for br: match_branch in m { + // Find a real id (we're adding placeholder wildcard patterns, but + // each column is guaranteed to have at least one real pattern) + if pat_id == 0 { pat_id = br.pats[col].id; } + } + + let rec_fields = collect_record_fields(m, col); + // Separate path for extracting and binding record fields + if rec_fields.len() > 0u { + let rec_ty = node_id_type(bcx, pat_id); + let fields = ty::get_fields(rec_ty); + let rec_vals = []; + for field_name: ast::ident in rec_fields { + let ix = option::get(ty::field_idx(field_name, fields)); + let r = GEP_tup_like(bcx, rec_ty, val, [0, ix as int]); + rec_vals += [r.val]; + bcx = r.bcx; + } + compile_submatch(bcx, enter_rec(dm, m, col, rec_fields, val), + rec_vals + vals_left, f, exits); + ret; + } + + if any_tup_pat(m, col) { + let tup_ty = node_id_type(bcx, pat_id); + let n_tup_elts = alt ty::get(tup_ty).struct { + ty::ty_tup(elts) { elts.len() } + _ { ccx.sess.bug("Non-tuple type in tuple pattern"); } + }; + let tup_vals = [], i = 0u; + while i < n_tup_elts { + let r = GEP_tup_like(bcx, tup_ty, val, [0, i as int]); + tup_vals += [r.val]; + bcx = r.bcx; + i += 1u; + } + compile_submatch(bcx, enter_tup(dm, m, col, val, n_tup_elts), + tup_vals + vals_left, f, exits); + ret; + } + + // Unbox in case of a box field + if any_box_pat(m, col) { + let box = Load(bcx, val); + let unboxed = GEPi(bcx, box, [0, abi::box_field_body]); + compile_submatch(bcx, enter_box(dm, m, col, val), [unboxed] + + vals_left, f, exits); + ret; + } + + if any_uniq_pat(m, col) { + let unboxed = Load(bcx, val); + compile_submatch(bcx, enter_uniq(dm, m, col, val), + [unboxed] + vals_left, f, exits); + ret; + } + + // Decide what kind of branch we need + let opts = get_options(ccx, m, col); + enum branch_kind { no_branch, single, switch, compare, } + let kind = no_branch; + let test_val = val; + if opts.len() > 0u { + alt opts[0] { + var(_, vdef) { + if (*ty::enum_variants(tcx, vdef.enm)).len() == 1u { + kind = single; + } else { + let enumptr = + PointerCast(bcx, val, T_opaque_enum_ptr(ccx)); + let discrimptr = GEPi(bcx, enumptr, [0, 0]); + test_val = Load(bcx, discrimptr); + kind = switch; + } + } + lit(l) { + test_val = Load(bcx, val); + let pty = node_id_type(bcx, pat_id); + kind = if ty::type_is_integral(pty) { switch } + else { compare }; + } + range(_, _) { + test_val = Load(bcx, val); + kind = compare; + } + } + } + for o: opt in opts { + alt o { + range(_, _) { kind = compare; break; } + _ { } + } + } + let else_cx = alt kind { + no_branch | single { bcx } + _ { sub_block(bcx, "match_else") } + }; + let sw = if kind == switch { + Switch(bcx, test_val, else_cx.llbb, opts.len()) + } else { C_int(ccx, 0) }; // Placeholder for when not using a switch + + // Compile subtrees for each option + for opt: opt in opts { + let opt_cx = sub_block(bcx, "match_case"); + alt kind { + single { Br(bcx, opt_cx.llbb); } + switch { + let res = trans_opt(bcx, opt); + alt check res { + single_result(r) { + llvm::LLVMAddCase(sw, r.val, opt_cx.llbb); + bcx = r.bcx; + } + } + } + compare { + let t = node_id_type(bcx, pat_id); + let {bcx: after_cx, val: matches} = + with_scope_result(bcx, "compare_scope") {|bcx| + alt trans_opt(bcx, opt) { + single_result({bcx, val}) { + trans_compare(bcx, ast::eq, test_val, t, val, t) + } + range_result({val: vbegin, _}, {bcx, val: vend}) { + let {bcx, val: ge} = trans_compare(bcx, ast::ge, test_val, + t, vbegin, t); + let {bcx, val: le} = trans_compare(bcx, ast::le, test_val, + t, vend, t); + {bcx: bcx, val: And(bcx, ge, le)} + } + } + }; + bcx = sub_block(after_cx, "compare_next"); + CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); + } + _ { } + } + let size = 0u; + let unpacked = []; + alt opt { + var(_, vdef) { + let args = extract_variant_args(opt_cx, pat_id, vdef, val); + size = args.vals.len(); + unpacked = args.vals; + opt_cx = args.bcx; + } + lit(_) | range(_, _) { } + } + compile_submatch(opt_cx, enter_opt(tcx, m, opt, col, size, val), + unpacked + vals_left, f, exits); + } + + // Compile the fall-through case + if kind == compare { Br(bcx, else_cx.llbb); } + if kind != single { + compile_submatch(else_cx, enter_default(dm, m, col, val), vals_left, + f, exits); + } +} + +// Returns false for unreachable blocks +fn make_phi_bindings(bcx: block, map: [exit_node], + ids: pat_util::pat_id_map) -> bool { + let our_block = bcx.llbb as uint; + let success = true, bcx = bcx; + ids.items {|name, node_id| + let llbbs = []; + let vals = []; + for ex: exit_node in map { + if ex.to as uint == our_block { + alt assoc(name, ex.bound) { + some(val) { llbbs += [ex.from]; vals += [val]; } + none { } + } + } + } + if vals.len() > 0u { + let local = Phi(bcx, val_ty(vals[0]), vals, llbbs); + bcx.fcx.lllocals.insert(node_id, local_mem(local)); + } else { success = false; } + }; + if success { + // Copy references that the alias analysis considered unsafe + ids.values {|node_id| + if bcx.ccx().maps.copy_map.contains_key(node_id) { + let local = alt bcx.fcx.lllocals.find(node_id) { + some(local_mem(x)) { x } + _ { bcx.tcx().sess.bug("Someone \ + forgot to document an invariant in \ + make_phi_bindings"); } + }; + let e_ty = node_id_type(bcx, node_id); + let {bcx: abcx, val: alloc} = alloc_ty(bcx, e_ty); + bcx = copy_val(abcx, INIT, alloc, + load_if_immediate(abcx, local, e_ty), + e_ty); + add_clean(bcx, alloc, e_ty); + bcx.fcx.lllocals.insert(node_id, local_mem(alloc)); + } + }; + } else { + Unreachable(bcx); + } + ret success; +} + +fn trans_alt(bcx: block, expr: @ast::expr, arms: [ast::arm], + dest: dest) -> block { + with_scope(bcx, "alt") {|bcx| trans_alt_inner(bcx, expr, arms, dest)} +} + +fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm], + dest: dest) -> block { + let bcx = scope_cx, tcx = bcx.tcx(); + let bodies = [], match = []; + + let {bcx, val, _} = trans_temp_expr(bcx, expr); + if bcx.unreachable { ret bcx; } + + for a in arms { + let body = scope_block(bcx, "case_body"); + body.block_span = some(a.body.span); + let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]); + bodies += [body]; + for p in a.pats { + match += [@{pats: [p], + bound: [], + data: @{body: body.llbb, guard: a.guard, + id_map: id_map}}]; + } + } + + // Cached fail-on-fallthrough block + let fail_cx = @mutable none; + fn mk_fail(bcx: block, sp: span, + done: @mutable option<BasicBlockRef>) -> BasicBlockRef { + alt *done { some(bb) { ret bb; } _ { } } + let fail_cx = sub_block(bcx, "case_fallthrough"); + trans_fail(fail_cx, some(sp), "non-exhaustive match failure");; + *done = some(fail_cx.llbb); + ret fail_cx.llbb; + } + + let exit_map = []; + let t = node_id_type(bcx, expr.id); + let {bcx, val: spilled} = spill_if_immediate(bcx, val, t); + compile_submatch(bcx, match, [spilled], + bind mk_fail(scope_cx, expr.span, fail_cx), exit_map); + + let arm_cxs = [], arm_dests = [], i = 0u; + for a in arms { + let body_cx = bodies[i]; + let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]); + if make_phi_bindings(body_cx, exit_map, id_map) { + let arm_dest = dup_for_join(dest); + arm_dests += [arm_dest]; + let arm_cx = trans_block(body_cx, a.body, arm_dest); + arm_cx = trans_block_cleanups(arm_cx, body_cx); + arm_cxs += [arm_cx]; + } + i += 1u; + } + join_returns(scope_cx, arm_cxs, arm_dests, dest) +} + +// Not alt-related, but similar to the pattern-munging code above +fn bind_irrefutable_pat(bcx: block, pat: @ast::pat, val: ValueRef, + make_copy: bool) -> block { + let ccx = bcx.fcx.ccx, bcx = bcx; + + // Necessary since bind_irrefutable_pat is called outside trans_alt + alt pat.node { + ast::pat_ident(_,inner) { + if pat_is_variant(bcx.tcx().def_map, pat) { ret bcx; } + if make_copy || ccx.maps.copy_map.contains_key(pat.id) { + let ty = node_id_type(bcx, pat.id); + let llty = type_of::type_of(ccx, ty); + let alloc = alloca(bcx, llty); + bcx = copy_val(bcx, INIT, alloc, + load_if_immediate(bcx, val, ty), ty); + bcx.fcx.lllocals.insert(pat.id, local_mem(alloc)); + add_clean(bcx, alloc, ty); + } else { bcx.fcx.lllocals.insert(pat.id, local_mem(val)); } + alt inner { + some(pat) { bcx = bind_irrefutable_pat(bcx, pat, val, true); } + _ {} + } + } + ast::pat_enum(_, sub) { + let vdefs = ast_util::variant_def_ids(ccx.tcx.def_map.get(pat.id)); + let args = extract_variant_args(bcx, pat.id, vdefs, val); + let i = 0; + for argval: ValueRef in args.vals { + bcx = bind_irrefutable_pat(bcx, sub[i], argval, make_copy); + i += 1; + } + } + ast::pat_rec(fields, _) { + let rec_ty = node_id_type(bcx, pat.id); + let rec_fields = ty::get_fields(rec_ty); + for f: ast::field_pat in fields { + let ix = option::get(ty::field_idx(f.ident, rec_fields)); + // how to get rid of this check? + let r = GEP_tup_like(bcx, rec_ty, val, [0, ix as int]); + bcx = bind_irrefutable_pat(r.bcx, f.pat, r.val, make_copy); + } + } + ast::pat_tup(elems) { + let tup_ty = node_id_type(bcx, pat.id); + let i = 0u; + for elem in elems { + let r = GEP_tup_like(bcx, tup_ty, val, [0, i as int]); + bcx = bind_irrefutable_pat(r.bcx, elem, r.val, make_copy); + i += 1u; + } + } + ast::pat_box(inner) { + let box = Load(bcx, val); + let unboxed = + GEPi(bcx, box, [0, abi::box_field_body]); + bcx = bind_irrefutable_pat(bcx, inner, unboxed, true); + } + ast::pat_uniq(inner) { + let val = Load(bcx, val); + bcx = bind_irrefutable_pat(bcx, inner, val, true); + } + ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) { } + } + ret bcx; +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs new file mode 100644 index 00000000000..6947a6875c4 --- /dev/null +++ b/src/rustc/middle/trans/base.rs @@ -0,0 +1,4978 @@ +// trans.rs: Translate the completed AST to the LLVM IR. +// +// Some functions here, such as trans_block and trans_expr, return a value -- +// the result of the translation to LLVM -- while others, such as trans_fn, +// trans_impl, and trans_item, are called only for the side effect of adding a +// particular definition to the LLVM IR output we're producing. +// +// Hopefully useful general knowledge about trans: +// +// * There's no way to find out the ty::t type of a ValueRef. Doing so +// would be "trying to get the eggs out of an omelette" (credit: +// pcwalton). You can, instead, find out its TypeRef by calling val_ty, +// but many TypeRefs correspond to one ty::t; for instance, tup(int, int, +// int) and rec(x=int, y=int, z=int) will have the same TypeRef. + +import ctypes::c_uint; +import std::{map, time}; +import std::map::hashmap; +import std::map::{new_int_hash, new_str_hash}; +import driver::session; +import session::session; +import front::attr; +import middle::inline::inline_map; +import back::{link, abi, upcall}; +import syntax::{ast, ast_util, codemap}; +import ast_util::inlined_item_methods; +import ast_util::local_def; +import syntax::visit; +import syntax::codemap::span; +import syntax::print::pprust::{expr_to_str, stmt_to_str, path_to_str}; +import pat_util::*; +import visit::vt; +import util::common::*; +import lib::llvm::{llvm, mk_target_data, mk_type_names}; +import lib::llvm::{ModuleRef, ValueRef, TypeRef, BasicBlockRef}; +import lib::llvm::{True, False}; +import link::{mangle_internal_name_by_type_only, + mangle_internal_name_by_seq, + mangle_internal_name_by_path, + mangle_internal_name_by_path_and_seq, + mangle_exported_name}; +import metadata::{csearch, cstore}; +import util::ppaux::{ty_to_str, ty_to_short_str}; + +import common::*; +import build::*; +import shape::*; +import type_of::*; +import type_of::type_of; // Issue #1873 +import ast_map::{path, path_mod, path_name}; + +// Destinations + +// These are passed around by the code generating functions to track the +// destination of a computation's value. + +enum dest { + by_val(@mutable ValueRef), + save_in(ValueRef), + ignore, +} + +fn dest_str(ccx: crate_ctxt, d: dest) -> str { + alt d { + by_val(v) { #fmt["by_val(%s)", val_str(ccx.tn, *v)] } + save_in(v) { #fmt["save_in(%s)", val_str(ccx.tn, v)] } + ignore { "ignore" } + } +} + +fn empty_dest_cell() -> @mutable ValueRef { + ret @mutable llvm::LLVMGetUndef(T_nil()); +} + +fn dup_for_join(dest: dest) -> dest { + alt dest { + by_val(_) { by_val(empty_dest_cell()) } + _ { dest } + } +} + +fn join_returns(parent_cx: block, in_cxs: [block], + in_ds: [dest], out_dest: dest) -> block { + let out = sub_block(parent_cx, "join"); + let reachable = false, i = 0u, phi = none; + for cx in in_cxs { + if !cx.unreachable { + Br(cx, out.llbb); + reachable = true; + alt in_ds[i] { + by_val(cell) { + if option::is_none(phi) { + phi = some(EmptyPhi(out, val_ty(*cell))); + } + AddIncomingToPhi(option::get(phi), *cell, cx.llbb); + } + _ {} + } + } + i += 1u; + } + if !reachable { + Unreachable(out); + } else { + alt out_dest { + by_val(cell) { *cell = option::get(phi); } + _ {} + } + } + ret out; +} + +// Used to put an immediate value in a dest. +fn store_in_dest(bcx: block, val: ValueRef, dest: dest) -> block { + alt dest { + ignore {} + by_val(cell) { *cell = val; } + save_in(addr) { Store(bcx, val, addr); } + } + ret bcx; +} + +fn get_dest_addr(dest: dest) -> ValueRef { + alt dest { + save_in(a) { a } + _ { fail "get_dest_addr: not a save_in"; } + } +} + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +fn sanitize(s: str) -> str { + let result = ""; + for c: u8 in s { + if c == '@' as u8 { + result += "boxed_"; + } else { + if c == ',' as u8 { + result += "_"; + } else { + if c == '{' as u8 || c == '(' as u8 { + result += "_of_"; + } else { + if c != 10u8 && c != '}' as u8 && c != ')' as u8 && + c != ' ' as u8 && c != '\t' as u8 && c != ';' as u8 + { + let v = [c]; + result += str::from_bytes(v); + } + } + } + } + } + ret result; +} + + +fn log_fn_time(ccx: crate_ctxt, name: str, start: time::timeval, + end: time::timeval) { + let elapsed = 1000 * ((end.sec - start.sec) as int) + + ((end.usec as int) - (start.usec as int)) / 1000; + *ccx.stats.fn_times += [{ident: name, time: elapsed}]; +} + + +fn decl_fn(llmod: ModuleRef, name: str, cc: lib::llvm::CallConv, + llty: TypeRef) -> ValueRef { + let llfn: ValueRef = str::as_buf(name, {|buf| + llvm::LLVMGetOrInsertFunction(llmod, buf, llty) + }); + lib::llvm::SetFunctionCallConv(llfn, cc); + ret llfn; +} + +fn decl_cdecl_fn(llmod: ModuleRef, name: str, llty: TypeRef) -> ValueRef { + ret decl_fn(llmod, name, lib::llvm::CCallConv, llty); +} + + +// Only use this if you are going to actually define the function. It's +// not valid to simply declare a function as internal. +fn decl_internal_cdecl_fn(llmod: ModuleRef, name: str, llty: TypeRef) -> + ValueRef { + let llfn = decl_cdecl_fn(llmod, name, llty); + lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); + ret llfn; +} + +fn get_extern_fn(externs: hashmap<str, ValueRef>, llmod: ModuleRef, name: str, + cc: lib::llvm::CallConv, ty: TypeRef) -> ValueRef { + if externs.contains_key(name) { ret externs.get(name); } + let f = decl_fn(llmod, name, cc, ty); + externs.insert(name, f); + ret f; +} + +fn get_extern_const(externs: hashmap<str, ValueRef>, llmod: ModuleRef, + name: str, ty: TypeRef) -> ValueRef { + if externs.contains_key(name) { ret externs.get(name); } + let c = str::as_buf(name, {|buf| llvm::LLVMAddGlobal(llmod, ty, buf) }); + externs.insert(name, c); + ret c; +} + +fn get_simple_extern_fn(cx: block, + externs: hashmap<str, ValueRef>, + llmod: ModuleRef, + name: str, n_args: int) -> ValueRef { + let ccx = cx.fcx.ccx; + let inputs = vec::init_elt(n_args as uint, ccx.int_type); + let output = ccx.int_type; + let t = T_fn(inputs, output); + ret get_extern_fn(externs, llmod, name, lib::llvm::CCallConv, t); +} + +fn trans_native_call(cx: block, externs: hashmap<str, ValueRef>, + llmod: ModuleRef, name: str, args: [ValueRef]) -> + ValueRef { + let n = args.len() as int; + let llnative: ValueRef = + get_simple_extern_fn(cx, externs, llmod, name, n); + let call_args: [ValueRef] = []; + for a: ValueRef in args { + call_args += [ZExtOrBitCast(cx, a, cx.ccx().int_type)]; + } + ret Call(cx, llnative, call_args); +} + +fn trans_free(cx: block, v: ValueRef) -> block { + Call(cx, cx.ccx().upcalls.free, [PointerCast(cx, v, T_ptr(T_i8()))]); + cx +} + +fn trans_shared_free(cx: block, v: ValueRef) -> block { + Call(cx, cx.ccx().upcalls.shared_free, + [PointerCast(cx, v, T_ptr(T_i8()))]); + ret cx; +} + +fn umax(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { + let cond = ICmp(cx, lib::llvm::IntULT, a, b); + ret Select(cx, cond, b, a); +} + +fn umin(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { + let cond = ICmp(cx, lib::llvm::IntULT, a, b); + ret Select(cx, cond, a, b); +} + +fn alloca(cx: block, t: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(t); } + ret Alloca(raw_block(cx.fcx, cx.fcx.llstaticallocas), t); +} + +fn dynastack_alloca(cx: block, t: TypeRef, n: ValueRef, ty: ty::t) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(t)); } + let bcx = cx; + let dy_cx = raw_block(cx.fcx, cx.fcx.lldynamicallocas); + alt cx.fcx.llobstacktoken { + none { + cx.fcx.llobstacktoken = some(mk_obstack_token(cx.ccx(), cx.fcx)); + } + some(_) {/* no-op */ } + } + + let dynastack_alloc = bcx.ccx().upcalls.dynastack_alloc; + let llsz = Mul(dy_cx, + C_uint(bcx.ccx(), llsize_of_real(bcx.ccx(), t)), + n); + + let lltydesc = get_tydesc_simple(cx, ty, false).val; + + let llresult = Call(dy_cx, dynastack_alloc, [llsz, lltydesc]); + ret PointerCast(dy_cx, llresult, T_ptr(t)); +} + +fn mk_obstack_token(ccx: crate_ctxt, fcx: fn_ctxt) -> + ValueRef { + let cx = raw_block(fcx, fcx.lldynamicallocas); + ret Call(cx, ccx.upcalls.dynastack_mark, []); +} + +// Given a pointer p, returns a pointer sz(p) (i.e., inc'd by sz bytes). +// The type of the returned pointer is always i8*. If you care about the +// return type, use bump_ptr(). +fn ptr_offs(bcx: block, base: ValueRef, sz: ValueRef) -> ValueRef { + let raw = PointerCast(bcx, base, T_ptr(T_i8())); + GEP(bcx, raw, [sz]) +} + +// Increment a pointer by a given amount and then cast it to be a pointer +// to a given type. +fn bump_ptr(bcx: block, t: ty::t, base: ValueRef, sz: ValueRef) -> + ValueRef { + let ccx = bcx.ccx(); + let bumped = ptr_offs(bcx, base, sz); + if check type_has_static_size(ccx, t) { + let typ = T_ptr(type_of(ccx, t)); + PointerCast(bcx, bumped, typ) + } else { bumped } +} + +// Replacement for the LLVM 'GEP' instruction when field-indexing into a +// tuple-like structure (tup, rec) with a static index. This one is driven off +// ty::struct and knows what to do when it runs into a ty_param stuck in the +// middle of the thing it's GEP'ing into. Much like size_of and align_of, +// above. +fn GEP_tup_like(bcx: block, t: ty::t, base: ValueRef, ixs: [int]) + -> result { + fn compute_off(bcx: block, + off: ValueRef, + t: ty::t, + ixs: [int], + n: uint) -> (block, ValueRef, ty::t) { + if n == ixs.len() { + ret (bcx, off, t); + } + + let ix = ixs[n]; + let bcx = bcx, off = off; + int::range(0, ix) {|i| + let comp_t = ty::get_element_type(t, i as uint); + let align = align_of(bcx, comp_t); + bcx = align.bcx; + off = align_to(bcx, off, align.val); + let sz = size_of(bcx, comp_t); + bcx = sz.bcx; + off = Add(bcx, off, sz.val); + } + + let comp_t = ty::get_element_type(t, ix as uint); + let align = align_of(bcx, comp_t); + bcx = align.bcx; + off = align_to(bcx, off, align.val); + + be compute_off(bcx, off, comp_t, ixs, n+1u); + } + + if !ty::type_has_dynamic_size(bcx.tcx(), t) { + ret rslt(bcx, GEPi(bcx, base, ixs)); + } + + #debug["GEP_tup_like(t=%s,base=%s,ixs=%?)", + ty_to_str(bcx.tcx(), t), + val_str(bcx.ccx().tn, base), + ixs]; + + // We require that ixs start with 0 and we expect the input to be a + // pointer to an instance of type t, so we can safely ignore ixs[0], + // basically. + assert ixs[0] == 0; + + let (bcx, off, tar_t) = { + compute_off(bcx, C_int(bcx.ccx(), 0), t, ixs, 1u) + }; + ret rslt(bcx, bump_ptr(bcx, tar_t, base, off)); +} + + +// Replacement for the LLVM 'GEP' instruction when field indexing into a enum. +// This function uses GEP_tup_like() above and automatically performs casts as +// appropriate. @llblobptr is the data part of a enum value; its actual type +// is meaningless, as it will be cast away. +fn GEP_enum(cx: block, llblobptr: ValueRef, enum_id: ast::def_id, + variant_id: ast::def_id, ty_substs: [ty::t], + ix: uint) -> result { + let variant = ty::enum_variant_with_id(cx.tcx(), enum_id, variant_id); + assert ix < variant.args.len(); + + // Synthesize a tuple type so that GEP_tup_like() can work its magic. + // Separately, store the type of the element we're interested in. + + let arg_tys = variant.args; + + let true_arg_tys: [ty::t] = []; + for aty: ty::t in arg_tys { + // Would be nice to have a way of stating the invariant + // that ty_substs is valid for aty + let arg_ty = ty::substitute_type_params(cx.tcx(), ty_substs, aty); + true_arg_tys += [arg_ty]; + } + + // We know that ix < len(variant.args) -- so + // it's safe to do this. (Would be nice to have + // typestate guarantee that a dynamic bounds check + // error can't happen here, but that's in the future.) + let elem_ty = true_arg_tys[ix]; + + let tup_ty = ty::mk_tup(cx.tcx(), true_arg_tys); + // Cast the blob pointer to the appropriate type, if we need to (i.e. if + // the blob pointer isn't dynamically sized). + + let llunionptr: ValueRef; + let ccx = cx.ccx(); + if check type_has_static_size(ccx, tup_ty) { + let llty = type_of(ccx, tup_ty); + llunionptr = TruncOrBitCast(cx, llblobptr, T_ptr(llty)); + } else { llunionptr = llblobptr; } + + // Do the GEP_tup_like(). + let rs = GEP_tup_like(cx, tup_ty, llunionptr, [0, ix as int]); + // Cast the result to the appropriate type, if necessary. + + let val = if check type_has_static_size(ccx, elem_ty) { + let llelemty = type_of(ccx, elem_ty); + PointerCast(rs.bcx, rs.val, T_ptr(llelemty)) + } else { rs.val }; + + ret rslt(rs.bcx, val); +} + +// trans_shared_malloc: expects a type indicating which pointer type we want +// and a size indicating how much space we want malloc'd. +fn trans_shared_malloc(cx: block, llptr_ty: TypeRef, llsize: ValueRef) + -> result { + let rval = Call(cx, cx.ccx().upcalls.shared_malloc, [llsize]); + ret rslt(cx, PointerCast(cx, rval, llptr_ty)); +} + +// Returns a pointer to the body for the box. The box may be an opaque +// box. The result will be casted to the type of body_t, if it is statically +// known. +// +// The runtime equivalent is box_body() in "rust_internal.h". +fn opaque_box_body(bcx: block, + body_t: ty::t, + boxptr: ValueRef) -> ValueRef { + let ccx = bcx.ccx(); + let boxptr = PointerCast(bcx, boxptr, T_ptr(T_box_header(ccx))); + let bodyptr = GEPi(bcx, boxptr, [1]); + if check type_has_static_size(ccx, body_t) { + PointerCast(bcx, bodyptr, T_ptr(type_of(ccx, body_t))) + } else { + PointerCast(bcx, bodyptr, T_ptr(T_i8())) + } +} + +// trans_malloc_boxed_raw: expects an unboxed type and returns a pointer to +// enough space for a box of that type. This includes a rust_opaque_box +// header. +fn trans_malloc_boxed_raw(bcx: block, t: ty::t, + &static_ti: option<@tydesc_info>) -> result { + let bcx = bcx, ccx = bcx.ccx(); + + // Grab the TypeRef type of box_ptr, because that's what trans_raw_malloc + // wants. + let box_ptr = ty::mk_imm_box(bcx.tcx(), t); + let llty = type_of(ccx, box_ptr); + + // Get the tydesc for the body: + let {bcx, val: lltydesc} = get_tydesc(bcx, t, true, static_ti); + lazily_emit_all_tydesc_glue(ccx, static_ti); + + // Allocate space: + let rval = Call(bcx, ccx.upcalls.malloc, [lltydesc]); + ret rslt(bcx, PointerCast(bcx, rval, llty)); +} + +// trans_malloc_boxed: usefully wraps trans_malloc_box_raw; allocates a box, +// initializes the reference count to 1, and pulls out the body and rc +fn trans_malloc_boxed(bcx: block, t: ty::t) -> + {bcx: block, box: ValueRef, body: ValueRef} { + let ti = none; + let {bcx, val:box} = trans_malloc_boxed_raw(bcx, t, ti); + let body = GEPi(bcx, box, [0, abi::box_field_body]); + ret {bcx: bcx, box: box, body: body}; +} + +// Type descriptor and type glue stuff + +// Given a type and a field index into its corresponding type descriptor, +// returns an LLVM ValueRef of that field from the tydesc, generating the +// tydesc if necessary. +fn field_of_tydesc(cx: block, t: ty::t, escapes: bool, field: int) -> + result { + let tydesc = get_tydesc_simple(cx, t, escapes); + ret rslt(tydesc.bcx, + GEPi(tydesc.bcx, tydesc.val, [0, field])); +} + +// Given a type containing ty params, build a vector containing a ValueRef for +// each of the ty params it uses (from the current frame) and a vector of the +// indices of the ty params present in the type. This is used solely for +// constructing derived tydescs. +fn linearize_ty_params(cx: block, t: ty::t) -> + {params: [uint], descs: [ValueRef]} { + let param_vals = [], param_defs = []; + ty::walk_ty(cx.tcx(), t) {|t| + alt ty::get(t).struct { + ty::ty_param(pid, _) { + if !vec::any(param_defs, {|d| d == pid}) { + param_vals += [cx.fcx.lltyparams[pid].desc]; + param_defs += [pid]; + } + } + _ { } + } + } + ret {params: param_defs, descs: param_vals}; +} + +fn trans_stack_local_derived_tydesc(cx: block, llsz: ValueRef, + llalign: ValueRef, llroottydesc: ValueRef, + llfirstparam: ValueRef, n_params: uint) + -> ValueRef { + let llmyroottydesc = alloca(cx, cx.ccx().tydesc_type); + + // By convention, desc 0 is the root descriptor. + let llroottydesc = Load(cx, llroottydesc); + Store(cx, llroottydesc, llmyroottydesc); + + // Store a pointer to the rest of the descriptors. + let ccx = cx.ccx(); + store_inbounds(cx, llfirstparam, llmyroottydesc, + [0, abi::tydesc_field_first_param]); + store_inbounds(cx, C_uint(ccx, n_params), llmyroottydesc, + [0, abi::tydesc_field_n_params]); + store_inbounds(cx, llsz, llmyroottydesc, + [0, abi::tydesc_field_size]); + store_inbounds(cx, llalign, llmyroottydesc, + [0, abi::tydesc_field_align]); + // FIXME legacy field, can be dropped + store_inbounds(cx, C_uint(ccx, 0u), llmyroottydesc, + [0, abi::tydesc_field_obj_params]); + ret llmyroottydesc; +} + +fn get_derived_tydesc(cx: block, t: ty::t, escapes: bool, + &static_ti: option<@tydesc_info>) -> result { + alt cx.fcx.derived_tydescs.find(t) { + some(info) { + // If the tydesc escapes in this context, the cached derived + // tydesc also has to be one that was marked as escaping. + if !(escapes && !info.escapes) { + ret rslt(cx, info.lltydesc); + } + } + none {/* fall through */ } + } + + cx.ccx().stats.n_derived_tydescs += 1u; + let bcx = raw_block(cx.fcx, cx.fcx.llderivedtydescs); + let tys = linearize_ty_params(bcx, t); + let root_ti = get_static_tydesc(bcx.ccx(), t, tys.params); + static_ti = some(root_ti); + lazily_emit_all_tydesc_glue(cx.ccx(), static_ti); + let root = root_ti.tydesc; + let sz = size_of(bcx, t); + bcx = sz.bcx; + let align = align_of(bcx, t); + bcx = align.bcx; + + // Store the captured type descriptors in an alloca if the caller isn't + // promising to do so itself. + let n_params = ty::count_ty_params(bcx.tcx(), t); + + assert n_params == tys.params.len(); + assert n_params == tys.descs.len(); + + let llparamtydescs = + alloca(bcx, T_array(T_ptr(bcx.ccx().tydesc_type), n_params + 1u)); + let i = 0; + + // If the type descriptor escapes, we need to add in the root as + // the first parameter, because upcall_get_type_desc() expects it. + if escapes { + Store(bcx, root, GEPi(bcx, llparamtydescs, [0, 0])); + i += 1; + } + + for td: ValueRef in tys.descs { + Store(bcx, td, GEPi(bcx, llparamtydescs, [0, i])); + i += 1; + } + + let llfirstparam = + PointerCast(bcx, llparamtydescs, + T_ptr(T_ptr(bcx.ccx().tydesc_type))); + + let v; + if escapes { + let ccx = bcx.ccx(); + let td_val = + Call(bcx, ccx.upcalls.get_type_desc, + [C_null(T_ptr(T_nil())), sz.val, + align.val, C_uint(ccx, 1u + n_params), llfirstparam, + C_uint(ccx, 0u)]); + v = td_val; + } else { + v = trans_stack_local_derived_tydesc(bcx, sz.val, align.val, root, + llfirstparam, n_params); + } + bcx.fcx.derived_tydescs.insert(t, {lltydesc: v, escapes: escapes}); + ret rslt(cx, v); +} + +fn get_tydesc_simple(bcx: block, t: ty::t, escapes: bool) -> result { + let ti = none; + get_tydesc(bcx, t, escapes, ti) +} + +fn get_tydesc(cx: block, t: ty::t, escapes: bool, + &static_ti: option<@tydesc_info>) -> result { + + // Is the supplied type a type param? If so, return the passed-in tydesc. + alt ty::type_param(t) { + some(id) { ret rslt(cx, cx.fcx.lltyparams[id].desc); } + none {/* fall through */ } + } + + // Does it contain a type param? If so, generate a derived tydesc. + if ty::type_has_params(t) { + ret get_derived_tydesc(cx, t, escapes, static_ti); + } + // Otherwise, generate a tydesc if necessary, and return it. + let info = get_static_tydesc(cx.ccx(), t, []); + static_ti = some(info); + ret rslt(cx, info.tydesc); +} + +fn get_static_tydesc(ccx: crate_ctxt, t: ty::t, ty_params: [uint]) + -> @tydesc_info { + alt ccx.tydescs.find(t) { + some(info) { ret info; } + none { + ccx.stats.n_static_tydescs += 1u; + let info = declare_tydesc(ccx, t, ty_params); + ccx.tydescs.insert(t, info); + ret info; + } + } +} + +fn set_no_inline(f: ValueRef) { + llvm::LLVMAddFunctionAttr(f, lib::llvm::NoInlineAttribute as c_uint, + 0u as c_uint); +} + +// Tell LLVM to emit the information necessary to unwind the stack for the +// function f. +fn set_uwtable(f: ValueRef) { + llvm::LLVMAddFunctionAttr(f, lib::llvm::UWTableAttribute as c_uint, + 0u as c_uint); +} + +fn set_always_inline(f: ValueRef) { + llvm::LLVMAddFunctionAttr(f, lib::llvm::AlwaysInlineAttribute as c_uint, + 0u as c_uint); +} + +fn set_custom_stack_growth_fn(f: ValueRef) { + // FIXME: Remove this hack to work around the lack of u64 in the FFI. + llvm::LLVMAddFunctionAttr(f, 0u as c_uint, 1u as c_uint); +} + +fn set_glue_inlining(f: ValueRef, t: ty::t) { + if ty::type_is_structural(t) { + set_no_inline(f); + } else { set_always_inline(f); } +} + + +// Generates the declaration for (but doesn't emit) a type descriptor. +fn declare_tydesc(ccx: crate_ctxt, t: ty::t, ty_params: [uint]) + -> @tydesc_info { + log(debug, "+++ declare_tydesc " + ty_to_str(ccx.tcx, t)); + let llsize; + let llalign; + if check type_has_static_size(ccx, t) { + let llty = type_of(ccx, t); + llsize = llsize_of(ccx, llty); + llalign = llalign_of(ccx, llty); + } else { + // These will be overwritten as the derived tydesc is generated, so + // we create placeholder values. + + llsize = C_int(ccx, 0); + llalign = C_int(ccx, 0); + } + let name; + if ccx.sess.opts.debuginfo { + name = mangle_internal_name_by_type_only(ccx, t, "tydesc"); + name = sanitize(name); + } else { name = mangle_internal_name_by_seq(ccx, "tydesc"); } + let gvar = str::as_buf(name, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, ccx.tydesc_type, buf) + }); + let info = + @{ty: t, + tydesc: gvar, + size: llsize, + align: llalign, + mutable take_glue: none, + mutable drop_glue: none, + mutable free_glue: none, + ty_params: ty_params}; + log(debug, "--- declare_tydesc " + ty_to_str(ccx.tcx, t)); + ret info; +} + +type glue_helper = fn@(block, ValueRef, ty::t); + +fn declare_generic_glue(ccx: crate_ctxt, t: ty::t, llfnty: TypeRef, + name: str) -> ValueRef { + let name = name; + let fn_nm; + if ccx.sess.opts.debuginfo { + fn_nm = mangle_internal_name_by_type_only(ccx, t, "glue_" + name); + fn_nm = sanitize(fn_nm); + } else { fn_nm = mangle_internal_name_by_seq(ccx, "glue_" + name); } + let llfn = decl_cdecl_fn(ccx.llmod, fn_nm, llfnty); + set_glue_inlining(llfn, t); + ret llfn; +} + +fn make_generic_glue_inner(ccx: crate_ctxt, t: ty::t, + llfn: ValueRef, helper: glue_helper, + ty_params: [uint]) -> ValueRef { + let fcx = new_fn_ctxt(ccx, [], llfn, none); + lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); + ccx.stats.n_glues_created += 1u; + // Any nontrivial glue is with values passed *by alias*; this is a + // requirement since in many contexts glue is invoked indirectly and + // the caller has no idea if it's dealing with something that can be + // passed by value. + + let llty = if check type_has_static_size(ccx, t) { + T_ptr(type_of(ccx, t)) + } else { T_ptr(T_i8()) }; + + let ty_param_count = ty_params.len(); + let lltyparams = llvm::LLVMGetParam(llfn, 2u as c_uint); + let load_env_bcx = raw_block(fcx, fcx.llloadenv); + let lltydescs = [mutable]; + let p = 0u; + while p < ty_param_count { + let llparam = GEPi(load_env_bcx, lltyparams, [p as int]); + llparam = Load(load_env_bcx, llparam); + vec::grow_set(lltydescs, ty_params[p], 0 as ValueRef, llparam); + p += 1u; + } + + fcx.lltyparams = vec::map(vec::from_mut(lltydescs), {|d| + {desc: d, dicts: none} + }); + + let bcx = top_scope_block(fcx, none); + let lltop = bcx.llbb; + let llrawptr0 = llvm::LLVMGetParam(llfn, 3u as c_uint); + let llval0 = BitCast(bcx, llrawptr0, llty); + helper(bcx, llval0, t); + finish_fn(fcx, lltop); + ret llfn; +} + +fn make_generic_glue(ccx: crate_ctxt, t: ty::t, llfn: ValueRef, + helper: glue_helper, ty_params: [uint], name: str) + -> ValueRef { + if !ccx.sess.opts.stats { + ret make_generic_glue_inner(ccx, t, llfn, helper, ty_params); + } + + let start = time::get_time(); + let llval = make_generic_glue_inner(ccx, t, llfn, helper, ty_params); + let end = time::get_time(); + log_fn_time(ccx, "glue " + name + " " + ty_to_short_str(ccx.tcx, t), + start, end); + ret llval; +} + +fn emit_tydescs(ccx: crate_ctxt) { + ccx.tydescs.items {|key, val| + let glue_fn_ty = T_ptr(T_glue_fn(ccx)); + let ti = val; + let take_glue = + alt ti.take_glue { + none { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + some(v) { ccx.stats.n_real_glues += 1u; v } + }; + let drop_glue = + alt ti.drop_glue { + none { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + some(v) { ccx.stats.n_real_glues += 1u; v } + }; + let free_glue = + alt ti.free_glue { + none { ccx.stats.n_null_glues += 1u; C_null(glue_fn_ty) } + some(v) { ccx.stats.n_real_glues += 1u; v } + }; + + let shape = shape_of(ccx, key, ti.ty_params); + let shape_tables = + llvm::LLVMConstPointerCast(ccx.shape_cx.llshapetables, + T_ptr(T_i8())); + + let tydesc = + C_named_struct(ccx.tydesc_type, + [C_null(T_ptr(T_ptr(ccx.tydesc_type))), + ti.size, // size + ti.align, // align + take_glue, // take_glue + drop_glue, // drop_glue + free_glue, // free_glue + C_null(T_ptr(T_i8())), // unused + C_null(glue_fn_ty), // sever_glue + C_null(glue_fn_ty), // mark_glue + C_null(glue_fn_ty), // unused + C_null(T_ptr(T_i8())), // cmp_glue + C_shape(ccx, shape), // shape + shape_tables, // shape_tables + C_int(ccx, 0), // n_params + C_int(ccx, 0)]); // n_obj_params + + let gvar = ti.tydesc; + llvm::LLVMSetInitializer(gvar, tydesc); + llvm::LLVMSetGlobalConstant(gvar, True); + lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage); + }; +} + +fn make_take_glue(cx: block, v: ValueRef, t: ty::t) { + let bcx = cx; + // NB: v is a *pointer* to type t here, not a direct value. + bcx = alt ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box { + incr_refcnt_of_boxed(bcx, Load(bcx, v)) + } + ty::ty_uniq(_) { + let r = uniq::duplicate(bcx, Load(bcx, v), t); + Store(r.bcx, r.val, v); + r.bcx + } + ty::ty_vec(_) | ty::ty_str { + let r = tvec::duplicate(bcx, Load(bcx, v), t); + Store(r.bcx, r.val, v); + r.bcx + } + ty::ty_send_type { + // sendable type descriptors are basically unique pointers, + // they must be cloned when copied: + let r = Load(bcx, v); + let s = Call(bcx, bcx.ccx().upcalls.create_shared_type_desc, [r]); + Store(bcx, s, v); + bcx + } + ty::ty_fn(_) { + closure::make_fn_glue(bcx, v, t, take_ty) + } + ty::ty_iface(_, _) { + let box = Load(bcx, GEPi(bcx, v, [0, 1])); + incr_refcnt_of_boxed(bcx, box) + } + ty::ty_opaque_closure_ptr(ck) { + closure::make_opaque_cbox_take_glue(bcx, ck, v) + } + _ if ty::type_is_structural(t) { + iter_structural_ty(bcx, v, t, take_ty) + } + _ { bcx } + }; + + build_return(bcx); +} + +fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) -> block { + let ccx = cx.ccx(); + maybe_validate_box(cx, box_ptr); + let rc_ptr = GEPi(cx, box_ptr, [0, abi::box_field_refcnt]); + let rc = Load(cx, rc_ptr); + rc = Add(cx, rc, C_int(ccx, 1)); + Store(cx, rc, rc_ptr); + ret cx; +} + +fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { + // v is a pointer to the actual box component of the type here. The + // ValueRef will have the wrong type here (make_generic_glue is casting + // everything to a pointer to the type that the glue acts on). + let ccx = bcx.ccx(); + let bcx = alt ty::get(t).struct { + ty::ty_box(body_mt) { + let v = PointerCast(bcx, v, type_of(ccx, t)); + let body = GEPi(bcx, v, [0, abi::box_field_body]); + let bcx = drop_ty(bcx, body, body_mt.ty); + trans_free(bcx, v) + } + ty::ty_opaque_box { + let v = PointerCast(bcx, v, type_of(ccx, t)); + let td = Load(bcx, GEPi(bcx, v, [0, abi::box_field_tydesc])); + let valptr = GEPi(bcx, v, [0, abi::box_field_body]); + call_tydesc_glue_full(bcx, valptr, td, abi::tydesc_field_drop_glue, + none); + trans_free(bcx, v) + } + ty::ty_uniq(content_mt) { + let v = PointerCast(bcx, v, type_of(ccx, t)); + uniq::make_free_glue(bcx, v, t) + } + ty::ty_vec(_) | ty::ty_str { + tvec::make_free_glue(bcx, PointerCast(bcx, v, type_of(ccx, t)), t) + } + ty::ty_send_type { + // sendable type descriptors are basically unique pointers, + // they must be freed. + let ccx = bcx.ccx(); + let v = PointerCast(bcx, v, T_ptr(ccx.tydesc_type)); + Call(bcx, ccx.upcalls.free_shared_type_desc, [v]); + bcx + } + ty::ty_fn(_) { + closure::make_fn_glue(bcx, v, t, free_ty) + } + ty::ty_opaque_closure_ptr(ck) { + closure::make_opaque_cbox_free_glue(bcx, ck, v) + } + _ { bcx } + }; + build_return(bcx); +} + +fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { + // NB: v0 is an *alias* of type t here, not a direct value. + let ccx = bcx.ccx(); + let bcx = alt ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box { + decr_refcnt_maybe_free(bcx, Load(bcx, v0), t) + } + ty::ty_uniq(_) | ty::ty_vec(_) | ty::ty_str | ty::ty_send_type { + free_ty(bcx, Load(bcx, v0), t) + } + ty::ty_res(did, inner, tps) { + trans_res_drop(bcx, v0, did, inner, tps) + } + ty::ty_fn(_) { + closure::make_fn_glue(bcx, v0, t, drop_ty) + } + ty::ty_iface(_, _) { + let box = Load(bcx, GEPi(bcx, v0, [0, 1])); + decr_refcnt_maybe_free(bcx, box, ty::mk_opaque_box(ccx.tcx)) + } + ty::ty_opaque_closure_ptr(ck) { + closure::make_opaque_cbox_drop_glue(bcx, ck, v0) + } + _ { + if ty::type_needs_drop(ccx.tcx, t) && + ty::type_is_structural(t) { + iter_structural_ty(bcx, v0, t, drop_ty) + } else { bcx } + } + }; + build_return(bcx); +} + +fn trans_res_drop(bcx: block, rs: ValueRef, did: ast::def_id, + inner_t: ty::t, tps: [ty::t]) -> block { + let ccx = bcx.ccx(); + let inner_t_s = ty::substitute_type_params(ccx.tcx, tps, inner_t); + let tup_ty = ty::mk_tup(ccx.tcx, [ty::mk_int(ccx.tcx), inner_t_s]); + + let {bcx, val: drop_flag} = GEP_tup_like(bcx, tup_ty, rs, [0, 0]); + with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) {|bcx| + let {bcx, val: valptr} = GEP_tup_like(bcx, tup_ty, rs, [0, 1]); + // Find and call the actual destructor. + let dtor_addr = common::get_res_dtor(ccx, did, inner_t); + let args = [bcx.fcx.llretptr, null_env_ptr(bcx)]; + for tp in tps { + let td = get_tydesc_simple(bcx, tp, false); + args += [td.val]; + bcx = td.bcx; + } + // Kludge to work around the fact that we know the precise type of the + // value here, but the dtor expects a type that still has opaque + // pointers for type variables. + let val_llty = lib::llvm::fn_ty_param_tys + (llvm::LLVMGetElementType + (llvm::LLVMTypeOf(dtor_addr)))[args.len()]; + let val_cast = BitCast(bcx, valptr, val_llty); + Call(bcx, dtor_addr, args + [val_cast]); + + bcx = drop_ty(bcx, valptr, inner_t_s); + Store(bcx, C_u8(0u), drop_flag); + bcx + } +} + +fn maybe_validate_box(_cx: block, _box_ptr: ValueRef) { + // Uncomment this when debugging annoying use-after-free + // bugs. But do not commit with this uncommented! Big performance hit. + + // let cx = _cx, box_ptr = _box_ptr; + // let ccx = cx.ccx(); + // warn_not_to_commit(ccx, "validate_box() is uncommented"); + // let raw_box_ptr = PointerCast(cx, box_ptr, T_ptr(T_i8())); + // Call(cx, ccx.upcalls.validate_box, [raw_box_ptr]); +} + +fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t) -> block { + let ccx = bcx.ccx(); + maybe_validate_box(bcx, box_ptr); + + let llbox_ty = T_opaque_box_ptr(ccx); + let box_ptr = PointerCast(bcx, box_ptr, llbox_ty); + with_cond(bcx, IsNotNull(bcx, box_ptr)) {|bcx| + let rc_ptr = GEPi(bcx, box_ptr, [0, abi::box_field_refcnt]); + let rc = Sub(bcx, Load(bcx, rc_ptr), C_int(ccx, 1)); + Store(bcx, rc, rc_ptr); + let zero_test = ICmp(bcx, lib::llvm::IntEQ, C_int(ccx, 0), rc); + with_cond(bcx, zero_test) {|bcx| free_ty(bcx, box_ptr, t)} + } +} + +// Structural comparison: a rather involved form of glue. +fn maybe_name_value(cx: crate_ctxt, v: ValueRef, s: str) { + if cx.sess.opts.save_temps { + let _: () = str::as_buf(s, {|buf| llvm::LLVMSetValueName(v, buf) }); + } +} + + +// Used only for creating scalar comparison glue. +enum scalar_type { nil_type, signed_int, unsigned_int, floating_point, } + + +fn compare_scalar_types(cx: block, lhs: ValueRef, rhs: ValueRef, + t: ty::t, op: ast::binop) -> result { + let f = bind compare_scalar_values(cx, lhs, rhs, _, op); + + alt ty::get(t).struct { + ty::ty_nil { ret rslt(cx, f(nil_type)); } + ty::ty_bool | ty::ty_ptr(_) { ret rslt(cx, f(unsigned_int)); } + ty::ty_int(_) { ret rslt(cx, f(signed_int)); } + ty::ty_uint(_) { ret rslt(cx, f(unsigned_int)); } + ty::ty_float(_) { ret rslt(cx, f(floating_point)); } + ty::ty_type { + ret rslt(trans_fail(cx, none, + "attempt to compare values of type type"), + C_nil()); + } + _ { + // Should never get here, because t is scalar. + cx.sess().bug("non-scalar type passed to \ + compare_scalar_types"); + } + } +} + + +// A helper function to do the actual comparison of scalar values. +fn compare_scalar_values(cx: block, lhs: ValueRef, rhs: ValueRef, + nt: scalar_type, op: ast::binop) -> ValueRef { + fn die_(cx: block) -> ! { + cx.tcx().sess.bug("compare_scalar_values: must be a\ + comparison operator"); + } + let die = bind die_(cx); + alt nt { + nil_type { + // We don't need to do actual comparisons for nil. + // () == () holds but () < () does not. + alt op { + ast::eq | ast::le | ast::ge { ret C_bool(true); } + ast::ne | ast::lt | ast::gt { ret C_bool(false); } + // refinements would be nice + _ { die(); } + } + } + floating_point { + let cmp = alt op { + ast::eq { lib::llvm::RealOEQ } + ast::ne { lib::llvm::RealUNE } + ast::lt { lib::llvm::RealOLT } + ast::le { lib::llvm::RealOLE } + ast::gt { lib::llvm::RealOGT } + ast::ge { lib::llvm::RealOGE } + _ { die(); } + }; + ret FCmp(cx, cmp, lhs, rhs); + } + signed_int { + let cmp = alt op { + ast::eq { lib::llvm::IntEQ } + ast::ne { lib::llvm::IntNE } + ast::lt { lib::llvm::IntSLT } + ast::le { lib::llvm::IntSLE } + ast::gt { lib::llvm::IntSGT } + ast::ge { lib::llvm::IntSGE } + _ { die(); } + }; + ret ICmp(cx, cmp, lhs, rhs); + } + unsigned_int { + let cmp = alt op { + ast::eq { lib::llvm::IntEQ } + ast::ne { lib::llvm::IntNE } + ast::lt { lib::llvm::IntULT } + ast::le { lib::llvm::IntULE } + ast::gt { lib::llvm::IntUGT } + ast::ge { lib::llvm::IntUGE } + _ { die(); } + }; + ret ICmp(cx, cmp, lhs, rhs); + } + } +} + +type val_pair_fn = fn@(block, ValueRef, ValueRef) -> block; +type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> block; + +fn load_inbounds(cx: block, p: ValueRef, idxs: [int]) -> ValueRef { + ret Load(cx, GEPi(cx, p, idxs)); +} + +fn store_inbounds(cx: block, v: ValueRef, p: ValueRef, + idxs: [int]) { + Store(cx, v, GEPi(cx, p, idxs)); +} + +// Iterates through the elements of a structural type. +fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t, + f: val_and_ty_fn) -> block { + fn iter_variant(cx: block, a_tup: ValueRef, + variant: ty::variant_info, tps: [ty::t], tid: ast::def_id, + f: val_and_ty_fn) -> block { + if variant.args.len() == 0u { ret cx; } + let fn_ty = variant.ctor_ty; + let ccx = cx.ccx(); + let cx = cx; + alt ty::get(fn_ty).struct { + ty::ty_fn({inputs: args, _}) { + let j = 0u; + let v_id = variant.id; + for a: ty::arg in args { + let rslt = GEP_enum(cx, a_tup, tid, v_id, tps, j); + let llfldp_a = rslt.val; + cx = rslt.bcx; + let ty_subst = ty::substitute_type_params(ccx.tcx, tps, a.ty); + cx = f(cx, llfldp_a, ty_subst); + j += 1u; + } + } + _ { cx.tcx().sess.bug("iter_variant: not a function type"); } + } + ret cx; + } + + /* + Typestate constraint that shows the unimpl case doesn't happen? + */ + let cx = cx; + alt ty::get(t).struct { + ty::ty_rec(fields) { + let i: int = 0; + for fld: ty::field in fields { + let {bcx: bcx, val: llfld_a} = GEP_tup_like(cx, t, av, [0, i]); + cx = f(bcx, llfld_a, fld.mt.ty); + i += 1; + } + } + ty::ty_tup(args) { + let i = 0; + for arg in args { + let {bcx: bcx, val: llfld_a} = GEP_tup_like(cx, t, av, [0, i]); + cx = f(bcx, llfld_a, arg); + i += 1; + } + } + ty::ty_res(_, inner, tps) { + let tcx = cx.tcx(); + let inner1 = ty::substitute_type_params(tcx, tps, inner); + let inner_t_s = ty::substitute_type_params(tcx, tps, inner); + let tup_t = ty::mk_tup(tcx, [ty::mk_int(tcx), inner_t_s]); + let {bcx: bcx, val: llfld_a} = GEP_tup_like(cx, tup_t, av, [0, 1]); + ret f(bcx, llfld_a, inner1); + } + ty::ty_enum(tid, tps) { + let variants = ty::enum_variants(cx.tcx(), tid); + let n_variants = (*variants).len(); + + // Cast the enums to types we can GEP into. + if n_variants == 1u { + ret iter_variant(cx, av, variants[0], tps, tid, f); + } + + let ccx = cx.ccx(); + let llenumty = T_opaque_enum_ptr(ccx); + let av_enum = PointerCast(cx, av, llenumty); + let lldiscrim_a_ptr = GEPi(cx, av_enum, [0, 0]); + let llunion_a_ptr = GEPi(cx, av_enum, [0, 1]); + let lldiscrim_a = Load(cx, lldiscrim_a_ptr); + + // NB: we must hit the discriminant first so that structural + // comparison know not to proceed when the discriminants differ. + cx = f(cx, lldiscrim_a_ptr, ty::mk_int(cx.tcx())); + let unr_cx = sub_block(cx, "enum-iter-unr"); + Unreachable(unr_cx); + let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, n_variants); + let next_cx = sub_block(cx, "enum-iter-next"); + for variant: ty::variant_info in *variants { + let variant_cx = + sub_block(cx, + "enum-iter-variant-" + + int::to_str(variant.disr_val, 10u)); + AddCase(llswitch, C_int(ccx, variant.disr_val), variant_cx.llbb); + variant_cx = + iter_variant(variant_cx, llunion_a_ptr, variant, tps, tid, f); + Br(variant_cx, next_cx.llbb); + } + ret next_cx; + } + _ { cx.sess().unimpl("type in iter_structural_ty"); } + } + ret cx; +} + +fn lazily_emit_all_tydesc_glue(ccx: crate_ctxt, + static_ti: option<@tydesc_info>) { + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, static_ti); + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, static_ti); + lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, static_ti); +} + +fn lazily_emit_all_generic_info_tydesc_glues(ccx: crate_ctxt, + gi: generic_info) { + for ti: option<@tydesc_info> in gi.static_tis { + lazily_emit_all_tydesc_glue(ccx, ti); + } +} + +fn lazily_emit_tydesc_glue(ccx: crate_ctxt, field: int, + static_ti: option<@tydesc_info>) { + alt static_ti { + none { } + some(ti) { + if field == abi::tydesc_field_take_glue { + alt ti.take_glue { + some(_) { } + none { + #debug("+++ lazily_emit_tydesc_glue TAKE %s", + ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = declare_generic_glue + (ccx, ti.ty, T_glue_fn(ccx), "take"); + ti.take_glue = some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, + make_take_glue, + ti.ty_params, "take"); + #debug("--- lazily_emit_tydesc_glue TAKE %s", + ty_to_str(ccx.tcx, ti.ty)); + } + } + } else if field == abi::tydesc_field_drop_glue { + alt ti.drop_glue { + some(_) { } + none { + #debug("+++ lazily_emit_tydesc_glue DROP %s", + ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = + declare_generic_glue(ccx, ti.ty, T_glue_fn(ccx), "drop"); + ti.drop_glue = some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, + make_drop_glue, + ti.ty_params, "drop"); + #debug("--- lazily_emit_tydesc_glue DROP %s", + ty_to_str(ccx.tcx, ti.ty)); + } + } + } else if field == abi::tydesc_field_free_glue { + alt ti.free_glue { + some(_) { } + none { + #debug("+++ lazily_emit_tydesc_glue FREE %s", + ty_to_str(ccx.tcx, ti.ty)); + let glue_fn = + declare_generic_glue(ccx, ti.ty, T_glue_fn(ccx), "free"); + ti.free_glue = some(glue_fn); + make_generic_glue(ccx, ti.ty, glue_fn, + make_free_glue, + ti.ty_params, "free"); + #debug("--- lazily_emit_tydesc_glue FREE %s", + ty_to_str(ccx.tcx, ti.ty)); + } + } + } + } + } +} + +fn call_tydesc_glue_full(cx: block, v: ValueRef, tydesc: ValueRef, + field: int, static_ti: option<@tydesc_info>) { + lazily_emit_tydesc_glue(cx.ccx(), field, static_ti); + if cx.unreachable { ret; } + + let static_glue_fn = none; + alt static_ti { + none {/* no-op */ } + some(sti) { + if field == abi::tydesc_field_take_glue { + static_glue_fn = sti.take_glue; + } else if field == abi::tydesc_field_drop_glue { + static_glue_fn = sti.drop_glue; + } else if field == abi::tydesc_field_free_glue { + static_glue_fn = sti.free_glue; + } + } + } + + let llrawptr = PointerCast(cx, v, T_ptr(T_i8())); + let lltydescs = + GEPi(cx, tydesc, [0, abi::tydesc_field_first_param]); + lltydescs = Load(cx, lltydescs); + + let llfn; + alt static_glue_fn { + none { + let llfnptr = GEPi(cx, tydesc, [0, field]); + llfn = Load(cx, llfnptr); + } + some(sgf) { llfn = sgf; } + } + + Call(cx, llfn, [C_null(T_ptr(T_nil())), C_null(T_ptr(T_nil())), + lltydescs, llrawptr]); +} + +fn call_tydesc_glue(cx: block, v: ValueRef, t: ty::t, field: int) -> + block { + let ti: option<@tydesc_info> = none::<@tydesc_info>; + let {bcx: bcx, val: td} = get_tydesc(cx, t, false, ti); + call_tydesc_glue_full(bcx, v, td, field, ti); + ret bcx; +} + +fn call_cmp_glue(cx: block, lhs: ValueRef, rhs: ValueRef, t: ty::t, + llop: ValueRef) -> result { + // We can't use call_tydesc_glue_full() and friends here because compare + // glue has a special signature. + + let bcx = cx; + + let r = spill_if_immediate(bcx, lhs, t); + let lllhs = r.val; + bcx = r.bcx; + r = spill_if_immediate(bcx, rhs, t); + let llrhs = r.val; + bcx = r.bcx; + + let llrawlhsptr = BitCast(bcx, lllhs, T_ptr(T_i8())); + let llrawrhsptr = BitCast(bcx, llrhs, T_ptr(T_i8())); + r = get_tydesc_simple(bcx, t, false); + let lltydesc = r.val; + bcx = r.bcx; + let lltydescs = + GEPi(bcx, lltydesc, [0, abi::tydesc_field_first_param]); + lltydescs = Load(bcx, lltydescs); + + let llfn = bcx.ccx().upcalls.cmp_type; + + let llcmpresultptr = alloca(bcx, T_i1()); + Call(bcx, llfn, [llcmpresultptr, lltydesc, lltydescs, + llrawlhsptr, llrawrhsptr, llop]); + ret rslt(bcx, Load(bcx, llcmpresultptr)); +} + +fn take_ty(cx: block, v: ValueRef, t: ty::t) -> block { + if ty::type_needs_drop(cx.tcx(), t) { + ret call_tydesc_glue(cx, v, t, abi::tydesc_field_take_glue); + } + ret cx; +} + +fn drop_ty(cx: block, v: ValueRef, t: ty::t) -> block { + if ty::type_needs_drop(cx.tcx(), t) { + ret call_tydesc_glue(cx, v, t, abi::tydesc_field_drop_glue); + } + ret cx; +} + +fn drop_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block { + alt ty::get(t).struct { + ty::ty_uniq(_) | ty::ty_vec(_) | ty::ty_str { free_ty(bcx, v, t) } + ty::ty_box(_) | ty::ty_opaque_box { + decr_refcnt_maybe_free(bcx, v, t) + } + _ { bcx.tcx().sess.bug("drop_ty_immediate: non-box ty"); } + } +} + +fn take_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> result { + alt ty::get(t).struct { + ty::ty_box(_) | ty::ty_opaque_box { + rslt(incr_refcnt_of_boxed(bcx, v), v) + } + ty::ty_uniq(_) { + uniq::duplicate(bcx, v, t) + } + ty::ty_str | ty::ty_vec(_) { tvec::duplicate(bcx, v, t) } + _ { rslt(bcx, v) } + } +} + +fn free_ty(cx: block, v: ValueRef, t: ty::t) -> block { + if ty::type_needs_drop(cx.tcx(), t) { + ret call_tydesc_glue(cx, v, t, abi::tydesc_field_free_glue); + } + ret cx; +} + +fn call_memmove(cx: block, dst: ValueRef, src: ValueRef, + n_bytes: ValueRef) -> result { + // FIXME: Provide LLVM with better alignment information when the + // alignment is statically known (it must be nothing more than a constant + // int, or LLVM complains -- not even a constant element of a tydesc + // works). + + let ccx = cx.ccx(); + let key = alt ccx.sess.targ_cfg.arch { + session::arch_x86 | session::arch_arm { "llvm.memmove.p0i8.p0i8.i32" } + session::arch_x86_64 { "llvm.memmove.p0i8.p0i8.i64" } + }; + let i = ccx.intrinsics; + assert (i.contains_key(key)); + let memmove = i.get(key); + let src_ptr = PointerCast(cx, src, T_ptr(T_i8())); + let dst_ptr = PointerCast(cx, dst, T_ptr(T_i8())); + let size = IntCast(cx, n_bytes, ccx.int_type); + let align = C_i32(1i32); + let volatile = C_bool(false); + let ret_val = Call(cx, memmove, [dst_ptr, src_ptr, size, + align, volatile]); + ret rslt(cx, ret_val); +} + +fn memmove_ty(bcx: block, dst: ValueRef, src: ValueRef, t: ty::t) -> + block { + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, t) { + if ty::type_is_structural(t) { + let llsz = llsize_of(ccx, type_of(ccx, t)); + ret call_memmove(bcx, dst, src, llsz).bcx; + } + Store(bcx, Load(bcx, src), dst); + ret bcx; + } + + let {bcx, val: llsz} = size_of(bcx, t); + ret call_memmove(bcx, dst, src, llsz).bcx; +} + +enum copy_action { INIT, DROP_EXISTING, } + +// These are the types that are passed by pointer. +fn type_is_structural_or_param(t: ty::t) -> bool { + if ty::type_is_structural(t) { ret true; } + alt ty::get(t).struct { + ty::ty_param(_, _) { ret true; } + _ { ret false; } + } +} + +fn copy_val(cx: block, action: copy_action, dst: ValueRef, + src: ValueRef, t: ty::t) -> block { + if action == DROP_EXISTING && + (type_is_structural_or_param(t) || + ty::type_is_unique(t)) { + let dstcmp = load_if_immediate(cx, dst, t); + let cast = PointerCast(cx, dstcmp, val_ty(src)); + // Self-copy check + with_cond(cx, ICmp(cx, lib::llvm::IntNE, cast, src)) {|bcx| + copy_val_no_check(bcx, action, dst, src, t) + } + } else { + copy_val_no_check(cx, action, dst, src, t) + } +} + +fn copy_val_no_check(bcx: block, action: copy_action, dst: ValueRef, + src: ValueRef, t: ty::t) -> block { + let ccx = bcx.ccx(), bcx = bcx; + if ty::type_is_scalar(t) { + Store(bcx, src, dst); + ret bcx; + } + if ty::type_is_nil(t) || ty::type_is_bot(t) { ret bcx; } + if ty::type_is_boxed(t) || ty::type_is_vec(t) || + ty::type_is_unique_box(t) { + if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); } + Store(bcx, src, dst); + ret take_ty(bcx, dst, t); + } + if type_is_structural_or_param(t) { + if action == DROP_EXISTING { bcx = drop_ty(bcx, dst, t); } + bcx = memmove_ty(bcx, dst, src, t); + ret take_ty(bcx, dst, t); + } + ccx.sess.bug("unexpected type in trans::copy_val_no_check: " + + ty_to_str(ccx.tcx, t)); +} + + +// This works like copy_val, except that it deinitializes the source. +// Since it needs to zero out the source, src also needs to be an lval. +// FIXME: We always zero out the source. Ideally we would detect the +// case where a variable is always deinitialized by block exit and thus +// doesn't need to be dropped. +fn move_val(cx: block, action: copy_action, dst: ValueRef, + src: lval_result, t: ty::t) -> block { + let src_val = src.val; + let tcx = cx.tcx(), cx = cx; + if ty::type_is_scalar(t) { + if src.kind == owned { src_val = Load(cx, src_val); } + Store(cx, src_val, dst); + ret cx; + } else if ty::type_is_nil(t) || ty::type_is_bot(t) { + ret cx; + } else if ty::type_is_boxed(t) || ty::type_is_unique(t) { + if src.kind == owned { src_val = Load(cx, src_val); } + if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); } + Store(cx, src_val, dst); + if src.kind == owned { ret zero_alloca(cx, src.val, t); } + // If we're here, it must be a temporary. + revoke_clean(cx, src_val); + ret cx; + } else if type_is_structural_or_param(t) { + if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); } + cx = memmove_ty(cx, dst, src_val, t); + if src.kind == owned { ret zero_alloca(cx, src_val, t); } + // If we're here, it must be a temporary. + revoke_clean(cx, src_val); + ret cx; + } + cx.sess().bug("unexpected type in trans::move_val: " + + ty_to_str(tcx, t)); +} + +fn store_temp_expr(cx: block, action: copy_action, dst: ValueRef, + src: lval_result, t: ty::t, last_use: bool) + -> block { + // Lvals in memory are not temporaries. Copy them. + if src.kind != temporary && !last_use { + let v = if src.kind == owned { + load_if_immediate(cx, src.val, t) + } else { + src.val + }; + ret copy_val(cx, action, dst, v, t); + } + ret move_val(cx, action, dst, src, t); +} + +fn trans_crate_lit(cx: crate_ctxt, lit: ast::lit) -> ValueRef { + alt lit.node { + ast::lit_int(i, t) { C_integral(T_int_ty(cx, t), i as u64, True) } + ast::lit_uint(u, t) { C_integral(T_uint_ty(cx, t), u, False) } + ast::lit_float(fs, t) { C_floating(fs, T_float_ty(cx, t)) } + ast::lit_bool(b) { C_bool(b) } + ast::lit_nil { C_nil() } + ast::lit_str(s) { + cx.sess.span_unimpl(lit.span, "unique string in this context"); + } + } +} + +fn trans_lit(cx: block, lit: ast::lit, dest: dest) -> block { + if dest == ignore { ret cx; } + alt lit.node { + ast::lit_str(s) { ret tvec::trans_str(cx, s, dest); } + _ { + ret store_in_dest(cx, trans_crate_lit(cx.ccx(), lit), dest); + } + } +} + +fn trans_unary(bcx: block, op: ast::unop, e: @ast::expr, + un_expr: @ast::expr, dest: dest) -> block { + // Check for user-defined method call + alt bcx.ccx().maps.method_map.find(un_expr.id) { + some(origin) { + let callee_id = ast_util::op_expr_callee_id(un_expr); + let fty = node_id_type(bcx, callee_id); + ret trans_call_inner(bcx, fty, {|bcx| + impl::trans_method_callee(bcx, callee_id, e, origin) + }, [], un_expr.id, dest); + } + _ {} + } + + if dest == ignore { ret trans_expr(bcx, e, ignore); } + let e_ty = expr_ty(bcx, e); + alt op { + ast::not { + let {bcx, val} = trans_temp_expr(bcx, e); + ret store_in_dest(bcx, Not(bcx, val), dest); + } + ast::neg { + let {bcx, val} = trans_temp_expr(bcx, e); + let neg = if ty::type_is_fp(e_ty) { + FNeg(bcx, val) + } else { Neg(bcx, val) }; + ret store_in_dest(bcx, neg, dest); + } + ast::box(_) { + let {bcx, box, body} = trans_malloc_boxed(bcx, e_ty); + add_clean_free(bcx, box, false); + // Cast the body type to the type of the value. This is needed to + // make enums work, since enums have a different LLVM type depending + // on whether they're boxed or not + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, e_ty) { + let llety = T_ptr(type_of(ccx, e_ty)); + body = PointerCast(bcx, body, llety); + } + bcx = trans_expr_save_in(bcx, e, body); + revoke_clean(bcx, box); + ret store_in_dest(bcx, box, dest); + } + ast::uniq(_) { + ret uniq::trans_uniq(bcx, e, un_expr.id, dest); + } + ast::deref { + bcx.sess().bug("deref expressions should have been \ + translated using trans_lval(), not \ + trans_unary()"); + } + } +} + +fn trans_compare(cx: block, op: ast::binop, lhs: ValueRef, + _lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result { + if ty::type_is_scalar(rhs_t) { + let rs = compare_scalar_types(cx, lhs, rhs, rhs_t, op); + ret rslt(rs.bcx, rs.val); + } + + // Determine the operation we need. + let llop; + alt op { + ast::eq | ast::ne { llop = C_u8(abi::cmp_glue_op_eq); } + ast::lt | ast::ge { llop = C_u8(abi::cmp_glue_op_lt); } + ast::le | ast::gt { llop = C_u8(abi::cmp_glue_op_le); } + _ { cx.tcx().sess.bug("trans_compare got non-comparison-op"); } + } + + let rs = call_cmp_glue(cx, lhs, rhs, rhs_t, llop); + + // Invert the result if necessary. + alt op { + ast::eq | ast::lt | ast::le { ret rslt(rs.bcx, rs.val); } + ast::ne | ast::ge | ast::gt { + ret rslt(rs.bcx, Not(rs.bcx, rs.val)); + } + _ { cx.tcx().sess.bug("trans_compare got\ + non-comparison-op"); } + } +} + +fn cast_shift_expr_rhs(cx: block, op: ast::binop, + lhs: ValueRef, rhs: ValueRef) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, + bind Trunc(cx, _, _), bind ZExt(cx, _, _)) +} + +fn cast_shift_const_rhs(op: ast::binop, + lhs: ValueRef, rhs: ValueRef) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, + llvm::LLVMConstTrunc, llvm::LLVMConstZExt) +} + +fn cast_shift_rhs(op: ast::binop, + lhs: ValueRef, rhs: ValueRef, + trunc: fn(ValueRef, TypeRef) -> ValueRef, + zext: fn(ValueRef, TypeRef) -> ValueRef + ) -> ValueRef { + + // Shifts may have any size int on the rhs + if ast_util::is_shift_binop(op) { + let rhs_llty = val_ty(rhs); + let lhs_llty = val_ty(lhs); + let rhs_sz = llvm::LLVMGetIntTypeWidth(rhs_llty); + let lhs_sz = llvm::LLVMGetIntTypeWidth(lhs_llty); + if lhs_sz < rhs_sz { + trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME: If shifting by negative values becomes not undefined + // then this is wrong. + zext(rhs, lhs_llty) + } else { + rhs + } + } else { + rhs + } +} + +// Important to get types for both lhs and rhs, because one might be _|_ +// and the other not. +fn trans_eager_binop(cx: block, op: ast::binop, lhs: ValueRef, + lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t, dest: dest) + -> block { + if dest == ignore { ret cx; } + let intype = lhs_t; + if ty::type_is_bot(intype) { intype = rhs_t; } + let is_float = ty::type_is_fp(intype); + + let rhs = cast_shift_expr_rhs(cx, op, lhs, rhs); + + if op == ast::add && ty::type_is_sequence(intype) { + ret tvec::trans_add(cx, intype, lhs, rhs, dest); + } + let cx = cx, val = alt op { + ast::add { + if is_float { FAdd(cx, lhs, rhs) } + else { Add(cx, lhs, rhs) } + } + ast::subtract { + if is_float { FSub(cx, lhs, rhs) } + else { Sub(cx, lhs, rhs) } + } + ast::mul { + if is_float { FMul(cx, lhs, rhs) } + else { Mul(cx, lhs, rhs) } + } + ast::div { + if is_float { FDiv(cx, lhs, rhs) } + else if ty::type_is_signed(intype) { + SDiv(cx, lhs, rhs) + } else { UDiv(cx, lhs, rhs) } + } + ast::rem { + if is_float { FRem(cx, lhs, rhs) } + else if ty::type_is_signed(intype) { + SRem(cx, lhs, rhs) + } else { URem(cx, lhs, rhs) } + } + ast::bitor { Or(cx, lhs, rhs) } + ast::bitand { And(cx, lhs, rhs) } + ast::bitxor { Xor(cx, lhs, rhs) } + ast::lsl { Shl(cx, lhs, rhs) } + ast::lsr { LShr(cx, lhs, rhs) } + ast::asr { AShr(cx, lhs, rhs) } + _ { + let cmpr = trans_compare(cx, op, lhs, lhs_t, rhs, rhs_t); + cx = cmpr.bcx; + cmpr.val + } + }; + ret store_in_dest(cx, val, dest); +} + +fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop, + dst: @ast::expr, src: @ast::expr) -> block { + let t = expr_ty(bcx, src); + let lhs_res = trans_lval(bcx, dst); + assert (lhs_res.kind == owned); + + // A user-defined operator method + alt bcx.ccx().maps.method_map.find(ex.id) { + some(origin) { + let callee_id = ast_util::op_expr_callee_id(ex); + let fty = node_id_type(bcx, callee_id); + ret trans_call_inner(bcx, fty, {|bcx| + // FIXME provide the already-computed address, not the expr + impl::trans_method_callee(bcx, callee_id, dst, origin) + }, [src], ex.id, save_in(lhs_res.val)); + } + _ {} + } + + // Special case for `+= [x]` + alt ty::get(t).struct { + ty::ty_vec(_) { + alt src.node { + ast::expr_vec(args, _) { + ret tvec::trans_append_literal(lhs_res.bcx, + lhs_res.val, t, args); + } + _ { } + } + } + _ { } + } + let {bcx, val: rhs_val} = trans_temp_expr(lhs_res.bcx, src); + if ty::type_is_sequence(t) { + alt op { + ast::add { + ret tvec::trans_append(bcx, t, lhs_res.val, rhs_val); + } + _ { } + } + } + ret trans_eager_binop(bcx, op, Load(bcx, lhs_res.val), t, rhs_val, t, + save_in(lhs_res.val)); +} + +fn autoderef(cx: block, v: ValueRef, t: ty::t) -> result_t { + let v1: ValueRef = v; + let t1: ty::t = t; + let ccx = cx.ccx(); + while true { + alt ty::get(t1).struct { + ty::ty_box(mt) { + let body = GEPi(cx, v1, [0, abi::box_field_body]); + t1 = mt.ty; + + // Since we're changing levels of box indirection, we may have + // to cast this pointer, since statically-sized enum types have + // different types depending on whether they're behind a box + // or not. + if check type_has_static_size(ccx, t1) { + let llty = type_of(ccx, t1); + v1 = PointerCast(cx, body, T_ptr(llty)); + } else { v1 = body; } + } + ty::ty_uniq(_) { + let derefed = uniq::autoderef(v1, t1); + t1 = derefed.t; + v1 = derefed.v; + } + ty::ty_res(did, inner, tps) { + t1 = ty::substitute_type_params(ccx.tcx, tps, inner); + v1 = GEPi(cx, v1, [0, 1]); + } + ty::ty_enum(did, tps) { + let variants = ty::enum_variants(ccx.tcx, did); + if (*variants).len() != 1u || variants[0].args.len() != 1u { + break; + } + t1 = + ty::substitute_type_params(ccx.tcx, tps, variants[0].args[0]); + if check type_has_static_size(ccx, t1) { + v1 = PointerCast(cx, v1, T_ptr(type_of(ccx, t1))); + } else { } // FIXME: typestate hack + } + _ { break; } + } + v1 = load_if_immediate(cx, v1, t1); + } + ret {bcx: cx, val: v1, ty: t1}; +} + +// refinement types would obviate the need for this +enum lazy_binop_ty { lazy_and, lazy_or } + +fn trans_lazy_binop(bcx: block, op: lazy_binop_ty, a: @ast::expr, + b: @ast::expr, dest: dest) -> block { + + let {bcx: past_lhs, val: lhs} = with_scope_result(bcx, "lhs") + {|bcx| trans_temp_expr(bcx, a)}; + if past_lhs.unreachable { ret past_lhs; } + let join = sub_block(bcx, "join"), before_rhs = sub_block(bcx, "rhs"); + + alt op { + lazy_and { CondBr(past_lhs, lhs, before_rhs.llbb, join.llbb); } + lazy_or { CondBr(past_lhs, lhs, join.llbb, before_rhs.llbb); } + } + let {bcx: past_rhs, val: rhs} = with_scope_result(before_rhs, "rhs") + {|bcx| trans_temp_expr(bcx, b)}; + + if past_rhs.unreachable { ret store_in_dest(join, lhs, dest); } + Br(past_rhs, join.llbb); + let phi = Phi(join, T_bool(), [lhs, rhs], [past_lhs.llbb, past_rhs.llbb]); + ret store_in_dest(join, phi, dest); +} + +fn trans_binary(bcx: block, op: ast::binop, lhs: @ast::expr, + rhs: @ast::expr, dest: dest, ex: @ast::expr) -> block { + // User-defined operators + alt bcx.ccx().maps.method_map.find(ex.id) { + some(origin) { + let callee_id = ast_util::op_expr_callee_id(ex); + let fty = node_id_type(bcx, callee_id); + ret trans_call_inner(bcx, fty, {|bcx| + impl::trans_method_callee(bcx, callee_id, lhs, origin) + }, [rhs], ex.id, dest); + } + _ {} + } + + // First couple cases are lazy: + alt op { + ast::and { + ret trans_lazy_binop(bcx, lazy_and, lhs, rhs, dest); + } + ast::or { + ret trans_lazy_binop(bcx, lazy_or, lhs, rhs, dest); + } + _ { + // Remaining cases are eager: + let lhs_res = trans_temp_expr(bcx, lhs); + let rhs_res = trans_temp_expr(lhs_res.bcx, rhs); + ret trans_eager_binop(rhs_res.bcx, op, lhs_res.val, + expr_ty(bcx, lhs), rhs_res.val, + expr_ty(bcx, rhs), dest); + } + } +} + +fn trans_if(cx: block, cond: @ast::expr, thn: ast::blk, + els: option<@ast::expr>, dest: dest) + -> block { + let {bcx, val: cond_val} = trans_temp_expr(cx, cond); + + let then_dest = dup_for_join(dest); + let else_dest = dup_for_join(dest); + let then_cx = scope_block(bcx, "then"); + then_cx.block_span = some(thn.span); + let else_cx = scope_block(bcx, "else"); + option::may(els) {|e| else_cx.block_span = some(e.span); } + CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb); + let then_bcx = trans_block(then_cx, thn, then_dest); + then_bcx = trans_block_cleanups(then_bcx, then_cx); + // Calling trans_block directly instead of trans_expr + // because trans_expr will create another scope block + // context for the block, but we've already got the + // 'else' context + let else_bcx = alt els { + some(elexpr) { + alt elexpr.node { + ast::expr_if(_, _, _) { + let elseif_blk = ast_util::block_from_expr(elexpr); + trans_block(else_cx, elseif_blk, else_dest) + } + ast::expr_block(blk) { + trans_block(else_cx, blk, else_dest) + } + // would be nice to have a constraint on ifs + _ { cx.tcx().sess.bug("Strange alternative in if"); } + } + } + _ { else_cx } + }; + else_bcx = trans_block_cleanups(else_bcx, else_cx); + ret join_returns(cx, [then_bcx, else_bcx], [then_dest, else_dest], dest); +} + +fn trans_for(cx: block, local: @ast::local, seq: @ast::expr, + body: ast::blk) -> block { + fn inner(bcx: block, local: @ast::local, curr: ValueRef, t: ty::t, + body: ast::blk, outer_next_cx: block) -> block { + let next_cx = sub_block(bcx, "next"); + let scope_cx = loop_scope_block(bcx, cont_other(next_cx), + outer_next_cx, "for loop scope", + body.span); + Br(bcx, scope_cx.llbb); + let curr = PointerCast(bcx, curr, + T_ptr(type_of_or_i8(bcx.ccx(), t))); + let bcx = alt::bind_irrefutable_pat(scope_cx, local.node.pat, + curr, false); + bcx = trans_block(bcx, body, ignore); + cleanup_and_Br(bcx, scope_cx, next_cx.llbb); + ret next_cx; + } + let ccx = cx.ccx(); + let next_cx = sub_block(cx, "next"); + let seq_ty = expr_ty(cx, seq); + let {bcx: bcx, val: seq} = trans_temp_expr(cx, seq); + let seq = PointerCast(bcx, seq, T_ptr(ccx.opaque_vec_type)); + let fill = tvec::get_fill(bcx, seq); + if ty::type_is_str(seq_ty) { + fill = Sub(bcx, fill, C_int(ccx, 1)); + } + let bcx = tvec::iter_vec_raw(bcx, seq, seq_ty, fill, + bind inner(_, local, _, _, body, next_cx)); + Br(bcx, next_cx.llbb); + ret next_cx; +} + +fn trans_while(cx: block, cond: @ast::expr, body: ast::blk) + -> block { + let next_cx = sub_block(cx, "while next"); + let cond_cx = loop_scope_block(cx, cont_self, next_cx, + "while cond", body.span); + let body_cx = scope_block(cond_cx, "while loop body"); + Br(cx, cond_cx.llbb); + let cond_res = trans_temp_expr(cond_cx, cond); + let cond_bcx = trans_block_cleanups(cond_res.bcx, cond_cx); + CondBr(cond_bcx, cond_res.val, body_cx.llbb, next_cx.llbb); + let body_end = trans_block(body_cx, body, ignore); + cleanup_and_Br(body_end, body_cx, cond_cx.llbb); + ret next_cx; +} + +fn trans_do_while(cx: block, body: ast::blk, cond: @ast::expr) -> + block { + let next_cx = sub_block(cx, "next"); + let body_cx = + loop_scope_block(cx, cont_self, next_cx, + "do-while loop body", body.span); + let body_end = trans_block(body_cx, body, ignore); + let cond_cx = scope_block(body_cx, "do-while cond"); + cleanup_and_Br(body_end, body_cx, cond_cx.llbb); + let cond_res = trans_temp_expr(cond_cx, cond); + let cond_bcx = trans_block_cleanups(cond_res.bcx, cond_cx); + CondBr(cond_bcx, cond_res.val, body_cx.llbb, next_cx.llbb); + Br(cx, body_cx.llbb); + ret next_cx; +} + +type generic_info = {item_type: ty::t, + static_tis: [option<@tydesc_info>], + tydescs: [ValueRef], + param_bounds: @[ty::param_bounds], + origins: option<typeck::dict_res>}; + +enum generic_callee { + generic_full(generic_info), + generic_mono(ty::t), + generic_none, +} + +enum lval_kind { + temporary, //< Temporary value passed by value if of immediate type + owned, //< Non-temporary value passed by pointer + owned_imm, //< Non-temporary value passed by value +} +type local_var_result = {val: ValueRef, kind: lval_kind}; +type lval_result = {bcx: block, val: ValueRef, kind: lval_kind}; +enum callee_env { + null_env, + is_closure, + self_env(ValueRef, ty::t), + dict_env(ValueRef, ValueRef), +} +type lval_maybe_callee = {bcx: block, + val: ValueRef, + kind: lval_kind, + env: callee_env, + generic: generic_callee}; + +fn null_env_ptr(bcx: block) -> ValueRef { + C_null(T_opaque_box_ptr(bcx.ccx())) +} + +fn lval_from_local_var(bcx: block, r: local_var_result) -> lval_result { + ret { bcx: bcx, val: r.val, kind: r.kind }; +} + +fn lval_owned(bcx: block, val: ValueRef) -> lval_result { + ret {bcx: bcx, val: val, kind: owned}; +} +fn lval_temp(bcx: block, val: ValueRef) -> lval_result { + ret {bcx: bcx, val: val, kind: temporary}; +} + +fn lval_no_env(bcx: block, val: ValueRef, kind: lval_kind) + -> lval_maybe_callee { + ret {bcx: bcx, val: val, kind: kind, env: is_closure, + generic: generic_none}; +} + +fn trans_external_path(cx: block, did: ast::def_id, + tpt: ty::ty_param_bounds_and_ty) -> ValueRef { + let ccx = cx.fcx.ccx; + let name = csearch::get_symbol(ccx.sess.cstore, did); + ret get_extern_const(ccx.externs, ccx.llmod, name, + type_of_ty_param_bounds_and_ty(ccx, tpt)); +} + +fn monomorphic_fn(ccx: crate_ctxt, fn_id: ast::def_id, substs: [ty::t], + dicts: option<typeck::dict_res>) + -> option<{llfn: ValueRef, fty: ty::t}> { + let substs = vec::map(substs, {|t| + alt ty::get(t).struct { + ty::ty_box(mt) { ty::mk_opaque_box(ccx.tcx) } + _ { t } + } + }); + let hash_id = @{def: fn_id, substs: substs, dicts: alt dicts { + some(os) { vec::map(*os, {|o| impl::dict_id(ccx.tcx, o)}) } + none { [] } + }}; + alt ccx.monomorphized.find(hash_id) { + some(val) { ret some(val); } + none {} + } + + let tpt = ty::lookup_item_type(ccx.tcx, fn_id); + let mono_ty = ty::substitute_type_params(ccx.tcx, substs, tpt.ty); + let llfty = type_of_fn_from_ty(ccx, mono_ty, []); + + let map_node = ccx.tcx.items.get(fn_id.node); + // Get the path so that we can create a symbol + let (pt, name) = alt map_node { + ast_map::node_item(i, pt) { (pt, i.ident) } + ast_map::node_variant(v, _, pt) { (pt, v.node.name) } + ast_map::node_method(m, _, pt) { (pt, m.ident) } + // We can't monomorphize native functions + ast_map::node_native_item(_, _) { ret none; } + _ { fail "Unexpected node type"; } + }; + let pt = *pt + [path_name(ccx.names(name))]; + let s = mangle_exported_name(ccx, pt, mono_ty); + let lldecl = decl_cdecl_fn(ccx.llmod, s, llfty); + ccx.monomorphized.insert(hash_id, {llfn: lldecl, fty: mono_ty}); + + let psubsts = some({tys: substs, dicts: dicts, bounds: tpt.bounds}); + alt check map_node { + ast_map::node_item(@{node: ast::item_fn(decl, _, body), _}, _) { + trans_fn(ccx, pt, decl, body, lldecl, no_self, [], + psubsts, fn_id.node); + } + ast_map::node_item(@{node: ast::item_res(decl, _, _, _, _), _}, _) { + trans_res_ctor(ccx, pt, decl, fn_id.node, [], psubsts, lldecl); + } + ast_map::node_variant(v, enum_id, _) { + let tvs = ty::enum_variants(ccx.tcx, enum_id); + let this_tv = option::get(vec::find(*tvs, {|tv| + tv.id.node == fn_id.node})); + trans_enum_variant(ccx, enum_id.node, v, this_tv.disr_val, + (*tvs).len() == 1u, [], psubsts, lldecl); + } + ast_map::node_method(mth, impl_def_id, _) { + let selfty = ty::lookup_item_type(ccx.tcx, impl_def_id).ty; + let selfty = ty::substitute_type_params(ccx.tcx, substs, selfty); + trans_fn(ccx, pt, mth.decl, mth.body, lldecl, + impl_self(selfty), [], psubsts, fn_id.node); + } + } + some({llfn: lldecl, fty: mono_ty}) +} + +fn lval_static_fn(bcx: block, fn_id: ast::def_id, id: ast::node_id, + substs: option<([ty::t], typeck::dict_res)>) + -> lval_maybe_callee { + let ccx = bcx.ccx(); + let tcx = ccx.tcx; + let tys = node_id_type_params(bcx, id); + let tpt = ty::lookup_item_type(tcx, fn_id); + + // Check whether this fn has an inlined copy and, if so, redirect fn_id to + // the local id of the inlined copy. + let fn_id = { + if fn_id.crate == ast::local_crate { + fn_id + } else { + alt ccx.inline_map.find(fn_id) { + none { fn_id } + some(ii) { + #debug["Found inlined version of %s with id %d", + ty::item_path_str(tcx, fn_id), + ii.id()]; + {crate: ast::local_crate, + node: ii.id()} + } + } + } + }; + + // The awkwardness below mostly stems from the fact that we're mixing + // monomorphized and non-monomorphized functions at the moment. If + // monomorphizing becomes the only approach, this'll be much simpler. + if ccx.sess.opts.monomorphize && + (option::is_some(substs) || tys.len() > 0u) && + fn_id.crate == ast::local_crate && + !vec::any(tys, {|t| ty::type_has_params(t)}) { + let mono = alt substs { + some((stys, dicts)) { + if (stys.len() + tys.len()) > 0u { + monomorphic_fn(ccx, fn_id, stys + tys, some(dicts)) + } else { none } + } + none { + alt ccx.maps.dict_map.find(id) { + some(dicts) { + alt impl::resolve_dicts_in_fn_ctxt(bcx.fcx, dicts) { + some(dicts) { monomorphic_fn(ccx, fn_id, tys, some(dicts)) } + none { none } + } + } + none { monomorphic_fn(ccx, fn_id, tys, none) } + } + } + }; + alt mono { + some({llfn, fty}) { + ret {bcx: bcx, val: llfn, + kind: owned, env: null_env, + generic: generic_mono(fty)}; + } + none {} + } + } + + let val = if fn_id.crate == ast::local_crate { + // Internal reference. + assert (ccx.item_ids.contains_key(fn_id.node)); + ccx.item_ids.get(fn_id.node) + } else { + // External reference. + trans_external_path(bcx, fn_id, tpt) + }; + + // FIXME: Need to support external crust functions + if fn_id.crate == ast::local_crate { + alt bcx.tcx().def_map.find(id) { + some(ast::def_fn(_, ast::crust_fn)) { + // Crust functions are just opaque pointers + let val = PointerCast(bcx, val, T_ptr(T_i8())); + ret lval_no_env(bcx, val, owned_imm); + } + _ { } + } + } + + let gen = generic_none, bcx = bcx; + if tys.len() > 0u { + let tydescs = [], tis = []; + for t in tys { + let ti = none; + let td = get_tydesc(bcx, t, true, ti); + tis += [ti]; + bcx = td.bcx; + tydescs += [td.val]; + } + gen = generic_full({item_type: tpt.ty, + static_tis: tis, + tydescs: tydescs, + param_bounds: tpt.bounds, + origins: ccx.maps.dict_map.find(id)}); + } + ret {bcx: bcx, val: val, kind: owned, env: null_env, generic: gen}; +} + +fn lookup_discriminant(ccx: crate_ctxt, vid: ast::def_id) -> ValueRef { + alt ccx.discrims.find(vid) { + none { + // It's an external discriminant that we haven't seen yet. + assert (vid.crate != ast::local_crate); + let sym = csearch::get_symbol(ccx.sess.cstore, vid); + let gvar = str::as_buf(sym, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) + }); + lib::llvm::SetLinkage(gvar, lib::llvm::ExternalLinkage); + llvm::LLVMSetGlobalConstant(gvar, True); + ccx.discrims.insert(vid, gvar); + ret gvar; + } + some(llval) { ret llval; } + } +} + +fn trans_local_var(cx: block, def: ast::def) -> local_var_result { + fn take_local(table: hashmap<ast::node_id, local_val>, + id: ast::node_id) -> local_var_result { + alt table.find(id) { + some(local_mem(v)) { {val: v, kind: owned} } + some(local_imm(v)) { {val: v, kind: owned_imm} } + r { fail("take_local: internal error"); } + } + } + alt def { + ast::def_upvar(nid, _, _) { + assert (cx.fcx.llupvars.contains_key(nid)); + ret { val: cx.fcx.llupvars.get(nid), kind: owned }; + } + ast::def_arg(nid, _) { + assert (cx.fcx.llargs.contains_key(nid)); + ret take_local(cx.fcx.llargs, nid); + } + ast::def_local(nid, _) | ast::def_binding(nid) { + assert (cx.fcx.lllocals.contains_key(nid)); + ret take_local(cx.fcx.lllocals, nid); + } + ast::def_self(nid) { + let slf = option::get(cx.fcx.llself); + let ptr = PointerCast(cx, slf.v, + T_ptr(type_of_or_i8(cx.ccx(), slf.t))); + ret {val: ptr, kind: owned}; + } + _ { + cx.sess().unimpl("unsupported def type in trans_local_def"); + } + } +} + +fn trans_path(cx: block, id: ast::node_id) + -> lval_maybe_callee { + ret trans_var(cx, cx.tcx().def_map.get(id), id); +} + +fn trans_var(cx: block, def: ast::def, id: ast::node_id) + -> lval_maybe_callee { + let ccx = cx.ccx(); + alt def { + ast::def_fn(did, _) { + ret lval_static_fn(cx, did, id, none); + } + ast::def_variant(tid, vid) { + if ty::enum_variant_with_id(ccx.tcx, tid, vid).args.len() > 0u { + // N-ary variant. + ret lval_static_fn(cx, vid, id, none); + } else { + // Nullary variant. + let enum_ty = node_id_type(cx, id); + let alloc_result = alloc_ty(cx, enum_ty); + let llenumblob = alloc_result.val; + let llenumty = type_of_enum(ccx, tid, enum_ty); + let bcx = alloc_result.bcx; + let llenumptr = PointerCast(bcx, llenumblob, T_ptr(llenumty)); + let lldiscrimptr = GEPi(bcx, llenumptr, [0, 0]); + let lldiscrim_gv = lookup_discriminant(bcx.fcx.ccx, vid); + let lldiscrim = Load(bcx, lldiscrim_gv); + Store(bcx, lldiscrim, lldiscrimptr); + ret lval_no_env(bcx, llenumptr, temporary); + } + } + ast::def_const(did) { + if did.crate == ast::local_crate { + assert (ccx.consts.contains_key(did.node)); + ret lval_no_env(cx, ccx.consts.get(did.node), owned); + } else { + let tp = node_id_type(cx, id); + let val = trans_external_path(cx, did, {bounds: @[], ty: tp}); + ret lval_no_env(cx, load_if_immediate(cx, val, tp), owned_imm); + } + } + _ { + let loc = trans_local_var(cx, def); + ret lval_no_env(cx, loc.val, loc.kind); + } + } +} + +fn trans_rec_field(bcx: block, base: @ast::expr, + field: ast::ident) -> lval_result { + let {bcx, val} = trans_temp_expr(bcx, base); + let {bcx, val, ty} = autoderef(bcx, val, expr_ty(bcx, base)); + let fields = alt ty::get(ty).struct { + ty::ty_rec(fs) { fs } + // Constraint? + _ { bcx.tcx().sess.span_bug(base.span, "trans_rec_field:\ + base expr has non-record type"); } + }; + let ix = option::get(ty::field_idx(field, fields)); + let {bcx, val} = GEP_tup_like(bcx, ty, val, [0, ix as int]); + ret {bcx: bcx, val: val, kind: owned}; +} + +fn trans_index(cx: block, ex: @ast::expr, base: @ast::expr, + idx: @ast::expr) -> lval_result { + let base_ty = expr_ty(cx, base); + let exp = trans_temp_expr(cx, base); + let lv = autoderef(exp.bcx, exp.val, base_ty); + let ix = trans_temp_expr(lv.bcx, idx); + let v = lv.val; + let bcx = ix.bcx; + let ccx = cx.ccx(); + + // Cast to an LLVM integer. Rust is less strict than LLVM in this regard. + let ix_val; + let ix_size = llsize_of_real(cx.ccx(), val_ty(ix.val)); + let int_size = llsize_of_real(cx.ccx(), ccx.int_type); + if ix_size < int_size { + ix_val = ZExt(bcx, ix.val, ccx.int_type); + } else if ix_size > int_size { + ix_val = Trunc(bcx, ix.val, ccx.int_type); + } else { ix_val = ix.val; } + + let unit_ty = node_id_type(cx, ex.id); + let unit_sz = size_of(bcx, unit_ty); + bcx = unit_sz.bcx; + maybe_name_value(cx.ccx(), unit_sz.val, "unit_sz"); + let scaled_ix = Mul(bcx, ix_val, unit_sz.val); + maybe_name_value(cx.ccx(), scaled_ix, "scaled_ix"); + let lim = tvec::get_fill(bcx, v); + let body = tvec::get_dataptr(bcx, v, type_of_or_i8(ccx, unit_ty)); + let bounds_check = ICmp(bcx, lib::llvm::IntUGE, scaled_ix, lim); + bcx = with_cond(bcx, bounds_check) {|bcx| + // fail: bad bounds check. + trans_fail(bcx, some(ex.span), "bounds check") + }; + let elt = if check type_has_static_size(ccx, unit_ty) { + let elt_1 = GEP(bcx, body, [ix_val]); + let llunitty = type_of(ccx, unit_ty); + PointerCast(bcx, elt_1, T_ptr(llunitty)) + } else { + body = PointerCast(bcx, body, T_ptr(T_i8())); + GEP(bcx, body, [scaled_ix]) + }; + ret lval_owned(bcx, elt); +} + +fn expr_is_lval(bcx: block, e: @ast::expr) -> bool { + let ccx = bcx.ccx(); + ty::expr_is_lval(ccx.maps.method_map, e) +} + +fn trans_callee(bcx: block, e: @ast::expr) -> lval_maybe_callee { + alt e.node { + ast::expr_path(_) { ret trans_path(bcx, e.id); } + ast::expr_field(base, ident, _) { + // Lval means this is a record field, so not a method + if !expr_is_lval(bcx, e) { + alt bcx.ccx().maps.method_map.find(e.id) { + some(origin) { // An impl method + ret impl::trans_method_callee(bcx, e.id, base, origin); + } + _ { + bcx.ccx().sess.span_bug(e.span, "trans_callee: weird expr"); + } + } + } + } + _ {} + } + let lv = trans_temp_lval(bcx, e); + ret lval_no_env(lv.bcx, lv.val, lv.kind); +} + +// Use this when you know you are compiling an lval. +// The additional bool returned indicates whether it's mem (that is +// represented as an alloca or heap, hence needs a 'load' to be used as an +// immediate). +fn trans_lval(cx: block, e: @ast::expr) -> lval_result { + alt e.node { + ast::expr_path(_) { + let v = trans_path(cx, e.id); + ret lval_maybe_callee_to_lval(v, expr_ty(cx, e)); + } + ast::expr_field(base, ident, _) { + ret trans_rec_field(cx, base, ident); + } + ast::expr_index(base, idx) { + ret trans_index(cx, e, base, idx); + } + ast::expr_unary(ast::deref, base) { + let ccx = cx.ccx(); + let sub = trans_temp_expr(cx, base); + let t = expr_ty(cx, base); + let val = alt check ty::get(t).struct { + ty::ty_box(_) { + GEPi(sub.bcx, sub.val, [0, abi::box_field_body]) + } + ty::ty_res(_, _, _) { + GEPi(sub.bcx, sub.val, [0, 1]) + } + ty::ty_enum(_, _) { + let ety = expr_ty(cx, e); + let ellty = if check type_has_static_size(ccx, ety) { + T_ptr(type_of(ccx, ety)) + } else { T_typaram_ptr(ccx.tn) }; + PointerCast(sub.bcx, sub.val, ellty) + } + ty::ty_ptr(_) | ty::ty_uniq(_) { sub.val } + }; + ret lval_owned(sub.bcx, val); + } + _ { cx.sess().span_bug(e.span, "non-lval in trans_lval"); } + } +} + +fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result { + let must_bind = alt c.generic { generic_full(_) { true } _ { false } } || + alt c.env { self_env(_, _) | dict_env(_, _) { true } _ { false } }; + if must_bind { + let n_args = ty::ty_fn_args(ty).len(); + let args = vec::init_elt(n_args, none); + let space = alloc_ty(c.bcx, ty); + let bcx = closure::trans_bind_1(space.bcx, ty, c, args, ty, + save_in(space.val)); + add_clean_temp(bcx, space.val, ty); + {bcx: bcx, val: space.val, kind: temporary} + } else { + alt check c.env { + is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} } + null_env { + let llfnty = llvm::LLVMGetElementType(val_ty(c.val)); + let llfn = create_real_fn_pair(c.bcx, llfnty, c.val, + null_env_ptr(c.bcx)); + {bcx: c.bcx, val: llfn, kind: temporary} + } + } + } +} + +fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, + llsrc: ValueRef, signed: bool) -> ValueRef { + let srcsz = llvm::LLVMGetIntTypeWidth(llsrctype); + let dstsz = llvm::LLVMGetIntTypeWidth(lldsttype); + ret if dstsz == srcsz { + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { ZExtOrBitCast(bcx, llsrc, lldsttype) }; +} + +fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef, + llsrc: ValueRef) -> ValueRef { + let srcsz = lib::llvm::float_width(llsrctype); + let dstsz = lib::llvm::float_width(lldsttype); + ret if dstsz > srcsz { + FPExt(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + FPTrunc(bcx, llsrc, lldsttype) + } else { llsrc }; +} + +fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id, + dest: dest) -> block { + let ccx = cx.ccx(); + let t_out = node_id_type(cx, id); + alt ty::get(t_out).struct { + ty::ty_iface(_, _) { ret impl::trans_cast(cx, e, id, dest); } + _ {} + } + let e_res = trans_temp_expr(cx, e); + let ll_t_in = val_ty(e_res.val); + let t_in = expr_ty(cx, e); + let ll_t_out = type_of(ccx, t_out); + + enum kind { pointer, integral, float, enum_, other, } + fn t_kind(t: ty::t) -> kind { + ret if ty::type_is_fp(t) { float } + else if ty::type_is_unsafe_ptr(t) { pointer } + else if ty::type_is_integral(t) { integral } + else if ty::type_is_enum(t) { enum_ } + else { other }; + } + let k_in = t_kind(t_in); + let k_out = t_kind(t_out); + let s_in = k_in == integral && ty::type_is_signed(t_in); + + let newval = + alt {in: k_in, out: k_out} { + {in: integral, out: integral} { + int_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val, s_in) + } + {in: float, out: float} { + float_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val) + } + {in: integral, out: float} { + if s_in { + SIToFP(e_res.bcx, e_res.val, ll_t_out) + } else { UIToFP(e_res.bcx, e_res.val, ll_t_out) } + } + {in: float, out: integral} { + if ty::type_is_signed(t_out) { + FPToSI(e_res.bcx, e_res.val, ll_t_out) + } else { FPToUI(e_res.bcx, e_res.val, ll_t_out) } + } + {in: integral, out: pointer} { + IntToPtr(e_res.bcx, e_res.val, ll_t_out) + } + {in: pointer, out: integral} { + PtrToInt(e_res.bcx, e_res.val, ll_t_out) + } + {in: pointer, out: pointer} { + PointerCast(e_res.bcx, e_res.val, ll_t_out) + } + {in: enum_, out: integral} | {in: enum_, out: float} { + let cx = e_res.bcx; + let llenumty = T_opaque_enum_ptr(ccx); + let av_enum = PointerCast(cx, e_res.val, llenumty); + let lldiscrim_a_ptr = GEPi(cx, av_enum, [0, 0]); + let lldiscrim_a = Load(cx, lldiscrim_a_ptr); + alt k_out { + integral {int_cast(e_res.bcx, ll_t_out, + val_ty(lldiscrim_a), lldiscrim_a, true)} + float {SIToFP(e_res.bcx, lldiscrim_a, ll_t_out)} + _ { ccx.sess.bug("Translating unsupported cast.") } + } + } + _ { ccx.sess.bug("Translating unsupported cast.") } + }; + ret store_in_dest(e_res.bcx, newval, dest); +} + +// temp_cleanups: cleanups that should run only if failure occurs before the +// call takes place: +fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr, + &temp_cleanups: [ValueRef]) -> result { + let ccx = cx.ccx(); + let e_ty = expr_ty(cx, e); + let is_bot = ty::type_is_bot(e_ty); + let lv = trans_temp_lval(cx, e); + let bcx = lv.bcx; + let val = lv.val; + let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode); + if is_bot { + // For values of type _|_, we generate an + // "undef" value, as such a value should never + // be inspected. It's important for the value + // to have type lldestty (the callee's expected type). + val = llvm::LLVMGetUndef(lldestty); + } else if arg_mode == ast::by_ref || arg_mode == ast::by_val { + let copied = false, imm = ty::type_is_immediate(e_ty); + if arg_mode == ast::by_ref && lv.kind != owned && imm { + val = do_spill_noroot(bcx, val); + copied = true; + } + if ccx.maps.copy_map.contains_key(e.id) && lv.kind != temporary { + if !copied { + let alloc = alloc_ty(bcx, e_ty); + bcx = copy_val(alloc.bcx, INIT, alloc.val, + load_if_immediate(alloc.bcx, val, e_ty), e_ty); + val = alloc.val; + } else { bcx = take_ty(bcx, val, e_ty); } + add_clean(bcx, val, e_ty); + } + if arg_mode == ast::by_val && (lv.kind == owned || !imm) { + val = Load(bcx, val); + } + } else if arg_mode == ast::by_copy || arg_mode == ast::by_move { + let {bcx: cx, val: alloc} = alloc_ty(bcx, e_ty); + let move_out = arg_mode == ast::by_move || + ccx.maps.last_uses.contains_key(e.id); + bcx = cx; + if lv.kind == temporary { revoke_clean(bcx, val); } + if lv.kind == owned || !ty::type_is_immediate(e_ty) { + bcx = memmove_ty(bcx, alloc, val, e_ty); + if move_out && ty::type_needs_drop(ccx.tcx, e_ty) { + bcx = zero_alloca(bcx, val, e_ty); + } + } else { Store(bcx, val, alloc); } + val = alloc; + if lv.kind != temporary && !move_out { + bcx = take_ty(bcx, val, e_ty); + } + + // In the event that failure occurs before the call actually + // happens, have to cleanup this copy: + add_clean_temp_mem(bcx, val, e_ty); + temp_cleanups += [val]; + } else if ty::type_is_immediate(e_ty) && lv.kind != owned { + let r = do_spill(bcx, val, e_ty); + val = r.val; + bcx = r.bcx; + } + + if !is_bot && arg.ty != e_ty || ty::type_has_params(arg.ty) { + val = PointerCast(bcx, val, lldestty); + } + ret rslt(bcx, val); +} + + +// NB: must keep 4 fns in sync: +// +// - type_of_fn +// - create_llargs_for_fn_args. +// - new_fn_ctxt +// - trans_args +fn trans_args(cx: block, llenv: ValueRef, + gen: generic_callee, es: [@ast::expr], fn_ty: ty::t, + dest: dest) + -> {bcx: block, + args: [ValueRef], + retslot: ValueRef} { + + let temp_cleanups = []; + let args = ty::ty_fn_args(fn_ty); + let llargs: [ValueRef] = []; + let lltydescs: [ValueRef] = []; + + let ccx = cx.ccx(); + let bcx = cx; + + let retty = ty::ty_fn_ret(fn_ty), full_retty = retty; + alt gen { + generic_full(g) { + lazily_emit_all_generic_info_tydesc_glues(ccx, g); + let i = 0u, n_orig = 0u; + for param in *g.param_bounds { + lltydescs += [g.tydescs[i]]; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let res = impl::get_dict( + bcx, option::get(g.origins)[n_orig]); + lltydescs += [res.val]; + bcx = res.bcx; + n_orig += 1u; + } + _ {} + } + } + i += 1u; + } + args = ty::ty_fn_args(g.item_type); + retty = ty::ty_fn_ret(g.item_type); + } + generic_mono(t) { + args = ty::ty_fn_args(t); + retty = ty::ty_fn_ret(t); + } + _ { } + } + // Arg 0: Output pointer. + let llretslot = alt dest { + ignore { + if ty::type_is_nil(retty) { + llvm::LLVMGetUndef(T_ptr(T_nil())) + } else { + let {bcx: cx, val} = alloc_ty(bcx, full_retty); + bcx = cx; + val + } + } + save_in(dst) { dst } + by_val(_) { + let {bcx: cx, val} = alloc_ty(bcx, full_retty); + bcx = cx; + val + } + }; + + if retty != full_retty || ty::type_has_params(retty) { + // It's possible that the callee has some generic-ness somewhere in + // its return value -- say a method signature within an obj or a fn + // type deep in a structure -- which the caller has a concrete view + // of. If so, cast the caller's view of the restlot to the callee's + // view, for the sake of making a type-compatible call. + let llretty = T_ptr(type_of(ccx, retty)); + llargs += [PointerCast(cx, llretslot, llretty)]; + } else { llargs += [llretslot]; } + + // Arg 1: Env (closure-bindings / self value) + llargs += [llenv]; + + // Args >2: ty_params ... + llargs += lltydescs; + + // ... then explicit args. + + // First we figure out the caller's view of the types of the arguments. + // This will be needed if this is a generic call, because the callee has + // to cast her view of the arguments to the caller's view. + let arg_tys = type_of_explicit_args(ccx, args); + let i = 0u; + for e: @ast::expr in es { + let r = trans_arg_expr(bcx, args[i], arg_tys[i], e, temp_cleanups); + bcx = r.bcx; + llargs += [r.val]; + i += 1u; + } + + // now that all arguments have been successfully built, we can revoke any + // temporary cleanups, as they are only needed if argument construction + // should fail (for example, cleanup of copy mode args). + vec::iter(temp_cleanups) {|c| + revoke_clean(bcx, c) + } + + ret {bcx: bcx, + args: llargs, + retslot: llretslot}; +} + +fn trans_call(in_cx: block, f: @ast::expr, + args: [@ast::expr], id: ast::node_id, dest: dest) + -> block { + trans_call_inner(in_cx, expr_ty(in_cx, f), + {|cx| trans_callee(cx, f)}, args, id, dest) +} + +fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, + get_callee: fn(block) -> lval_maybe_callee, + args: [@ast::expr], id: ast::node_id, dest: dest) + -> block { + with_scope(in_cx, "call") {|cx| + let f_res = get_callee(cx); + let bcx = f_res.bcx, ccx = cx.ccx(); + + let faddr = f_res.val; + let llenv, dict_param = none; + alt f_res.env { + null_env { + llenv = llvm::LLVMGetUndef(T_opaque_box_ptr(ccx)); + } + self_env(e, _) { + llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx)); + } + dict_env(dict, e) { + llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx)); + dict_param = some(dict); + } + is_closure { + // It's a closure. Have to fetch the elements + if f_res.kind == owned { + faddr = load_if_immediate(bcx, faddr, fn_expr_ty); + } + let pair = faddr; + faddr = GEPi(bcx, pair, [0, abi::fn_field_code]); + faddr = Load(bcx, faddr); + let llclosure = GEPi(bcx, pair, [0, abi::fn_field_box]); + llenv = Load(bcx, llclosure); + } + } + + let ret_ty = node_id_type(bcx, id); + let args_res = + trans_args(bcx, llenv, f_res.generic, args, fn_expr_ty, dest); + bcx = args_res.bcx; + let llargs = args_res.args; + option::may(dict_param) {|dict| llargs = [dict] + llargs} + let llretslot = args_res.retslot; + + /* If the block is terminated, + then one or more of the args has + type _|_. Since that means it diverges, the code + for the call itself is unreachable. */ + bcx = invoke_full(bcx, faddr, llargs); + alt dest { + ignore { + if llvm::LLVMIsUndef(llretslot) != lib::llvm::True { + bcx = drop_ty(bcx, llretslot, ret_ty); + } + } + save_in(_) { } // Already saved by callee + by_val(cell) { + *cell = Load(bcx, llretslot); + } + } + if ty::type_is_bot(ret_ty) { Unreachable(bcx); } + bcx + } +} + +fn invoke(bcx: block, llfn: ValueRef, + llargs: [ValueRef]) -> block { + ret invoke_(bcx, llfn, llargs, Invoke); +} + +fn invoke_full(bcx: block, llfn: ValueRef, llargs: [ValueRef]) + -> block { + ret invoke_(bcx, llfn, llargs, Invoke); +} + +fn invoke_(bcx: block, llfn: ValueRef, llargs: [ValueRef], + invoker: fn(block, ValueRef, [ValueRef], + BasicBlockRef, BasicBlockRef)) -> block { + // FIXME: May be worth turning this into a plain call when there are no + // cleanups to run + if bcx.unreachable { ret bcx; } + let normal_bcx = sub_block(bcx, "normal return"); + invoker(bcx, llfn, llargs, normal_bcx.llbb, get_landing_pad(bcx)); + ret normal_bcx; +} + +fn get_landing_pad(bcx: block) -> BasicBlockRef { + fn in_lpad_scope_cx(bcx: block, f: fn(scope_info)) { + let bcx = bcx; + while true { + alt bcx.kind { + block_scope(info) { + if info.cleanups.len() > 0u || bcx.parent == parent_none { + f(info); ret; + } + } + _ {} + } + bcx = block_parent(bcx); + } + } + + let cached = none, pad_bcx = bcx; // Guaranteed to be set below + in_lpad_scope_cx(bcx) {|info| + // If there is a valid landing pad still around, use it + alt info.landing_pad { + some(target) { cached = some(target); ret; } + none {} + } + pad_bcx = sub_block(bcx, "unwind"); + info.landing_pad = some(pad_bcx.llbb); + } + alt cached { some(b) { ret b; } none {} } // Can't return from block above + // The landing pad return type (the type being propagated). Not sure what + // this represents but it's determined by the personality function and + // this is what the EH proposal example uses. + let llretty = T_struct([T_ptr(T_i8()), T_i32()]); + // The exception handling personality function. This is the C++ + // personality function __gxx_personality_v0, wrapped in our naming + // convention. + let personality = bcx.ccx().upcalls.rust_personality; + // The only landing pad clause will be 'cleanup' + let llretval = LandingPad(pad_bcx, llretty, personality, 1u); + // The landing pad block is a cleanup + SetCleanup(pad_bcx, llretval); + + // Because we may have unwound across a stack boundary, we must call into + // the runtime to figure out which stack segment we are on and place the + // stack limit back into the TLS. + Call(pad_bcx, bcx.ccx().upcalls.reset_stack_limit, []); + + // We store the retval in a function-central alloca, so that calls to + // Resume can find it. + alt bcx.fcx.personality { + some(addr) { Store(pad_bcx, llretval, addr); } + none { + let addr = alloca(pad_bcx, val_ty(llretval)); + bcx.fcx.personality = some(addr); + Store(pad_bcx, llretval, addr); + } + } + + // Unwind all parent scopes, and finish with a Resume instr + cleanup_and_leave(pad_bcx, none, none); + ret pad_bcx.llbb; +} + +fn trans_tup(bcx: block, elts: [@ast::expr], id: ast::node_id, + dest: dest) -> block { + let t = node_id_type(bcx, id); + let bcx = bcx; + let addr = alt dest { + ignore { + for ex in elts { bcx = trans_expr(bcx, ex, ignore); } + ret bcx; + } + save_in(pos) { pos } + _ { bcx.tcx().sess.bug("trans_tup: weird dest"); } + }; + let temp_cleanups = [], i = 0; + for e in elts { + let dst = GEP_tup_like(bcx, t, addr, [0, i]); + let e_ty = expr_ty(bcx, e); + bcx = trans_expr_save_in(dst.bcx, e, dst.val); + add_clean_temp_mem(bcx, dst.val, e_ty); + temp_cleanups += [dst.val]; + i += 1; + } + for cleanup in temp_cleanups { revoke_clean(bcx, cleanup); } + ret bcx; +} + +fn trans_rec(bcx: block, fields: [ast::field], + base: option<@ast::expr>, id: ast::node_id, + dest: dest) -> block { + let t = node_id_type(bcx, id); + let bcx = bcx; + let addr = alt dest { + ignore { + for fld in fields { + bcx = trans_expr(bcx, fld.node.expr, ignore); + } + ret bcx; + } + save_in(pos) { pos } + _ { bcx.tcx().sess.bug("trans_rec: weird dest"); } + }; + + let ty_fields = alt ty::get(t).struct { + ty::ty_rec(f) { f } + _ { bcx.tcx().sess.bug("trans_rec: id doesn't\ + have a record type") } }; + let temp_cleanups = []; + for fld in fields { + let ix = option::get(vec::position(ty_fields, {|ft| + str::eq(fld.node.ident, ft.ident) + })); + let dst = GEP_tup_like(bcx, t, addr, [0, ix as int]); + bcx = trans_expr_save_in(dst.bcx, fld.node.expr, dst.val); + add_clean_temp_mem(bcx, dst.val, ty_fields[ix].mt.ty); + temp_cleanups += [dst.val]; + } + alt base { + some(bexp) { + let {bcx: cx, val: base_val} = trans_temp_expr(bcx, bexp), i = 0; + bcx = cx; + // Copy over inherited fields + for tf in ty_fields { + if !vec::any(fields, {|f| str::eq(f.node.ident, tf.ident)}) { + let dst = GEP_tup_like(bcx, t, addr, [0, i]); + let base = GEP_tup_like(bcx, t, base_val, [0, i]); + let val = load_if_immediate(base.bcx, base.val, tf.mt.ty); + bcx = copy_val(base.bcx, INIT, dst.val, val, tf.mt.ty); + } + i += 1; + } + } + none {} + }; + + // Now revoke the cleanups as we pass responsibility for the data + // structure on to the caller + for cleanup in temp_cleanups { revoke_clean(bcx, cleanup); } + ret bcx; +} + +// Store the result of an expression in the given memory location, ensuring +// that nil or bot expressions get ignore rather than save_in as destination. +fn trans_expr_save_in(bcx: block, e: @ast::expr, dest: ValueRef) + -> block { + let t = expr_ty(bcx, e); + let do_ignore = ty::type_is_bot(t) || ty::type_is_nil(t); + ret trans_expr(bcx, e, if do_ignore { ignore } else { save_in(dest) }); +} + +// Call this to compile an expression that you need as an intermediate value, +// and you want to know whether you're dealing with an lval or not (the kind +// field in the returned struct). For non-intermediates, use trans_expr or +// trans_expr_save_in. For intermediates where you don't care about lval-ness, +// use trans_temp_expr. +fn trans_temp_lval(bcx: block, e: @ast::expr) -> lval_result { + let bcx = bcx; + if expr_is_lval(bcx, e) { + ret trans_lval(bcx, e); + } else { + let ty = expr_ty(bcx, e); + if ty::type_is_nil(ty) || ty::type_is_bot(ty) { + bcx = trans_expr(bcx, e, ignore); + ret {bcx: bcx, val: C_nil(), kind: temporary}; + } else if ty::type_is_immediate(ty) { + let cell = empty_dest_cell(); + bcx = trans_expr(bcx, e, by_val(cell)); + add_clean_temp(bcx, *cell, ty); + ret {bcx: bcx, val: *cell, kind: temporary}; + } else { + let {bcx, val: scratch} = alloc_ty(bcx, ty); + bcx = trans_expr_save_in(bcx, e, scratch); + add_clean_temp(bcx, scratch, ty); + ret {bcx: bcx, val: scratch, kind: temporary}; + } + } +} + +// Use only for intermediate values. See trans_expr and trans_expr_save_in for +// expressions that must 'end up somewhere' (or get ignored). +fn trans_temp_expr(bcx: block, e: @ast::expr) -> result { + let {bcx, val, kind} = trans_temp_lval(bcx, e); + if kind == owned { + val = load_if_immediate(bcx, val, expr_ty(bcx, e)); + } + ret {bcx: bcx, val: val}; +} + +// Translate an expression, with the dest argument deciding what happens with +// the result. Invariants: +// - exprs returning nil or bot always get dest=ignore +// - exprs with non-immediate type never get dest=by_val +fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { + let tcx = bcx.tcx(); + debuginfo::update_source_pos(bcx, e.span); + + #debug["trans_expr(e=%s,e.id=%d,dest=%s,ty=%s)", + expr_to_str(e), + e.id, + dest_str(bcx.ccx(), dest), + ty_to_str(tcx, expr_ty(bcx, e))]; + + if expr_is_lval(bcx, e) { + ret lval_to_dps(bcx, e, dest); + } + + alt e.node { + ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) { + ret trans_if(bcx, cond, thn, els, dest); + } + ast::expr_alt(expr, arms, _) { + ret alt::trans_alt(bcx, expr, arms, dest); + } + ast::expr_block(blk) { + ret with_scope(bcx, "block-expr body") {|bcx| + bcx.block_span = some(blk.span); + trans_block(bcx, blk, dest) + }; + } + ast::expr_rec(args, base) { + ret trans_rec(bcx, args, base, e.id, dest); + } + ast::expr_tup(args) { ret trans_tup(bcx, args, e.id, dest); } + ast::expr_lit(lit) { ret trans_lit(bcx, *lit, dest); } + ast::expr_vec(args, _) { ret tvec::trans_vec(bcx, args, e.id, dest); } + ast::expr_binary(op, lhs, rhs) { + ret trans_binary(bcx, op, lhs, rhs, dest, e); + } + ast::expr_unary(op, x) { + assert op != ast::deref; // lvals are handled above + ret trans_unary(bcx, op, x, e, dest); + } + ast::expr_fn(proto, decl, body, cap_clause) { + ret closure::trans_expr_fn( + bcx, proto, decl, body, e.span, e.id, *cap_clause, dest); + } + ast::expr_fn_block(decl, body) { + alt ty::get(expr_ty(bcx, e)).struct { + ty::ty_fn({proto, _}) { + #debug("translating fn_block %s with type %s", + expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e))); + let cap_clause = { copies: [], moves: [] }; + ret closure::trans_expr_fn( + bcx, proto, decl, body, e.span, e.id, cap_clause, dest); + } + _ { + fail "Type of fn block is not a function!"; + } + } + } + ast::expr_bind(f, args) { + ret closure::trans_bind( + bcx, f, args, e.id, dest); + } + ast::expr_copy(a) { + if !expr_is_lval(bcx, a) { + ret trans_expr(bcx, a, dest); + } + else { ret lval_to_dps(bcx, a, dest); } + } + ast::expr_cast(val, _) { ret trans_cast(bcx, val, e.id, dest); } + ast::expr_call(f, args, _) { + ret trans_call(bcx, f, args, e.id, dest); + } + ast::expr_field(base, _, _) { + if dest == ignore { ret trans_expr(bcx, base, ignore); } + let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e); + let lv = lval_maybe_callee_to_lval(callee, ty); + revoke_clean(lv.bcx, lv.val); + ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty); + } + ast::expr_index(base, idx) { + // If it is here, it's not an lval, so this is a user-defined index op + let origin = bcx.ccx().maps.method_map.get(e.id); + let callee_id = ast_util::op_expr_callee_id(e); + let fty = node_id_type(bcx, callee_id); + ret trans_call_inner(bcx, fty, {|bcx| + impl::trans_method_callee(bcx, callee_id, base, origin) + }, [idx], e.id, dest); + } + + // These return nothing + ast::expr_break { + assert dest == ignore; + ret trans_break(bcx); + } + ast::expr_cont { + assert dest == ignore; + ret trans_cont(bcx); + } + ast::expr_ret(ex) { + assert dest == ignore; + ret trans_ret(bcx, ex); + } + ast::expr_be(ex) { + ret trans_be(bcx, ex); + } + ast::expr_fail(expr) { + assert dest == ignore; + ret trans_fail_expr(bcx, some(e.span), expr); + } + ast::expr_log(_, lvl, a) { + assert dest == ignore; + ret trans_log(lvl, bcx, a); + } + ast::expr_assert(a) { + assert dest == ignore; + ret trans_check_expr(bcx, a, "Assertion"); + } + ast::expr_check(ast::checked_expr, a) { + assert dest == ignore; + ret trans_check_expr(bcx, a, "Predicate"); + } + ast::expr_check(ast::claimed_expr, a) { + assert dest == ignore; + /* Claims are turned on and off by a global variable + that the RTS sets. This case generates code to + check the value of that variable, doing nothing + if it's set to false and acting like a check + otherwise. */ + let c = get_extern_const(bcx.ccx().externs, bcx.ccx().llmod, + "check_claims", T_bool()); + ret with_cond(bcx, Load(bcx, c)) {|bcx| + trans_check_expr(bcx, a, "Claim") + }; + } + ast::expr_for(decl, seq, body) { + assert dest == ignore; + ret trans_for(bcx, decl, seq, body); + } + ast::expr_while(cond, body) { + assert dest == ignore; + ret trans_while(bcx, cond, body); + } + ast::expr_do_while(body, cond) { + assert dest == ignore; + ret trans_do_while(bcx, body, cond); + } + ast::expr_assign(dst, src) { + assert dest == ignore; + let src_r = trans_temp_lval(bcx, src); + let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); + assert kind == owned; + ret store_temp_expr(bcx, DROP_EXISTING, addr, src_r, + expr_ty(bcx, src), + bcx.ccx().maps.last_uses.contains_key(src.id)); + } + ast::expr_move(dst, src) { + // FIXME: calculate copy init-ness in typestate. + assert dest == ignore; + let src_r = trans_temp_lval(bcx, src); + let {bcx, val: addr, kind} = trans_lval(src_r.bcx, dst); + assert kind == owned; + ret move_val(bcx, DROP_EXISTING, addr, src_r, + expr_ty(bcx, src)); + } + ast::expr_swap(dst, src) { + assert dest == ignore; + let lhs_res = trans_lval(bcx, dst); + assert lhs_res.kind == owned; + let rhs_res = trans_lval(lhs_res.bcx, src); + let t = expr_ty(bcx, src); + let {bcx: bcx, val: tmp_alloc} = alloc_ty(rhs_res.bcx, t); + // Swap through a temporary. + bcx = move_val(bcx, INIT, tmp_alloc, lhs_res, t); + bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t); + ret move_val(bcx, INIT, rhs_res.val, lval_owned(bcx, tmp_alloc), t); + } + ast::expr_assign_op(op, dst, src) { + assert dest == ignore; + ret trans_assign_op(bcx, e, op, dst, src); + } + _ { bcx.tcx().sess.span_bug(e.span, "trans_expr reached\ + fall-through case"); } + + } +} + +fn lval_to_dps(bcx: block, e: @ast::expr, dest: dest) -> block { + let lv = trans_lval(bcx, e), ccx = bcx.ccx(); + let {bcx, val, kind} = lv; + let last_use = kind == owned && ccx.maps.last_uses.contains_key(e.id); + let ty = expr_ty(bcx, e); + alt dest { + by_val(cell) { + if kind == temporary { + revoke_clean(bcx, val); + *cell = val; + } else if last_use { + *cell = Load(bcx, val); + if ty::type_needs_drop(ccx.tcx, ty) { + bcx = zero_alloca(bcx, val, ty); + } + } else { + if kind == owned { val = Load(bcx, val); } + let {bcx: cx, val} = take_ty_immediate(bcx, val, ty); + *cell = val; + bcx = cx; + } + } + save_in(loc) { + bcx = store_temp_expr(bcx, INIT, loc, lv, ty, last_use); + } + ignore {} + } + ret bcx; +} + +fn do_spill(cx: block, v: ValueRef, t: ty::t) -> result { + // We have a value but we have to spill it, and root it, to pass by alias. + let bcx = cx; + + if ty::type_is_bot(t) { + ret rslt(bcx, C_null(T_ptr(T_i8()))); + } + + let r = alloc_ty(bcx, t); + bcx = r.bcx; + let llptr = r.val; + + Store(bcx, v, llptr); + + ret rslt(bcx, llptr); +} + +// Since this function does *not* root, it is the caller's responsibility to +// ensure that the referent is pointed to by a root. +fn do_spill_noroot(cx: block, v: ValueRef) -> ValueRef { + let llptr = alloca(cx, val_ty(v)); + Store(cx, v, llptr); + ret llptr; +} + +fn spill_if_immediate(cx: block, v: ValueRef, t: ty::t) -> result { + if ty::type_is_immediate(t) { ret do_spill(cx, v, t); } + ret rslt(cx, v); +} + +fn load_if_immediate(cx: block, v: ValueRef, t: ty::t) -> ValueRef { + if ty::type_is_immediate(t) { ret Load(cx, v); } + ret v; +} + +fn trans_log(lvl: @ast::expr, bcx: block, e: @ast::expr) -> block { + let ccx = bcx.ccx(); + if ty::type_is_bot(expr_ty(bcx, lvl)) { + ret trans_expr(bcx, lvl, ignore); + } + + let modpath = [path_mod(ccx.link_meta.name)] + + vec::filter(bcx.fcx.path, {|e| + alt e { path_mod(_) { true } _ { false } } + }); + let modname = path_str(modpath); + + let global = if ccx.module_data.contains_key(modname) { + ccx.module_data.get(modname) + } else { + let s = link::mangle_internal_name_by_path_and_seq( + ccx, modpath, "loglevel"); + let global = str::as_buf(s, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf) + }); + llvm::LLVMSetGlobalConstant(global, False); + llvm::LLVMSetInitializer(global, C_null(T_i32())); + lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage); + ccx.module_data.insert(modname, global); + global + }; + let current_level = Load(bcx, global); + let {bcx, val: level} = with_scope_result(bcx, "level") {|bcx| + trans_temp_expr(bcx, lvl) + }; + + with_cond(bcx, ICmp(bcx, lib::llvm::IntUGE, current_level, level)) {|bcx| + with_scope(bcx, "log") {|bcx| + let {bcx, val, _} = trans_temp_expr(bcx, e); + let e_ty = expr_ty(bcx, e); + let {bcx, val: tydesc} = get_tydesc_simple(bcx, e_ty, false); + // Call the polymorphic log function. + let {bcx, val} = spill_if_immediate(bcx, val, e_ty); + let val = PointerCast(bcx, val, T_ptr(T_i8())); + Call(bcx, ccx.upcalls.log_type, [tydesc, val, level]); + bcx + } + } +} + +fn trans_check_expr(bcx: block, e: @ast::expr, s: str) -> block { + let expr_str = s + " " + expr_to_str(e) + " failed"; + let {bcx, val} = with_scope_result(bcx, "check") {|bcx| + trans_temp_expr(bcx, e) + }; + with_cond(bcx, Not(bcx, val)) {|bcx| + trans_fail(bcx, some(e.span), expr_str) + } +} + +fn trans_fail_expr(bcx: block, sp_opt: option<span>, + fail_expr: option<@ast::expr>) -> block { + let bcx = bcx; + alt fail_expr { + some(expr) { + let ccx = bcx.ccx(), tcx = ccx.tcx; + let expr_res = trans_temp_expr(bcx, expr); + let e_ty = expr_ty(bcx, expr); + bcx = expr_res.bcx; + + if ty::type_is_str(e_ty) { + let data = tvec::get_dataptr( + bcx, expr_res.val, type_of_or_i8( + ccx, ty::mk_mach_uint(tcx, ast::ty_u8))); + ret trans_fail_value(bcx, sp_opt, data); + } else if bcx.unreachable || ty::type_is_bot(e_ty) { + ret bcx; + } else { + bcx.sess().span_bug( + expr.span, "fail called with unsupported type " + + ty_to_str(tcx, e_ty)); + } + } + _ { ret trans_fail(bcx, sp_opt, "explicit failure"); } + } +} + +fn trans_fail(bcx: block, sp_opt: option<span>, fail_str: str) -> + block { + let V_fail_str = C_cstr(bcx.ccx(), fail_str); + ret trans_fail_value(bcx, sp_opt, V_fail_str); +} + +fn trans_fail_value(bcx: block, sp_opt: option<span>, + V_fail_str: ValueRef) -> block { + let ccx = bcx.ccx(); + let V_filename; + let V_line; + alt sp_opt { + some(sp) { + let sess = bcx.sess(); + let loc = codemap::lookup_char_pos(sess.parse_sess.cm, sp.lo); + V_filename = C_cstr(bcx.ccx(), loc.file.name); + V_line = loc.line as int; + } + none { V_filename = C_cstr(bcx.ccx(), "<runtime>"); V_line = 0; } + } + let V_str = PointerCast(bcx, V_fail_str, T_ptr(T_i8())); + V_filename = PointerCast(bcx, V_filename, T_ptr(T_i8())); + let args = [V_str, V_filename, C_int(ccx, V_line)]; + let bcx = invoke(bcx, bcx.ccx().upcalls._fail, args); + Unreachable(bcx); + ret bcx; +} + +fn trans_break_cont(bcx: block, to_end: bool) + -> block { + // Locate closest loop block, outputting cleanup as we go. + let unwind = bcx, target = bcx; + while true { + alt unwind.kind { + block_scope({is_loop: some({cnt, brk}), _}) { + target = if to_end { + brk + } else { + alt cnt { + cont_other(o) { o } + cont_self { unwind } + } + }; + break; + } + _ {} + } + unwind = alt check unwind.parent { + parent_some(cx) { cx } + parent_none { + bcx.sess().bug + (if to_end { "break" } else { "cont" } + " outside a loop"); + } + }; + } + cleanup_and_Br(bcx, unwind, target.llbb); + Unreachable(bcx); + ret bcx; +} + +fn trans_break(cx: block) -> block { + ret trans_break_cont(cx, true); +} + +fn trans_cont(cx: block) -> block { + ret trans_break_cont(cx, false); +} + +fn trans_ret(bcx: block, e: option<@ast::expr>) -> block { + let bcx = bcx; + alt e { + some(x) { bcx = trans_expr_save_in(bcx, x, bcx.fcx.llretptr); } + _ {} + } + cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn)); + Unreachable(bcx); + ret bcx; +} + +fn build_return(bcx: block) { Br(bcx, bcx.fcx.llreturn); } + +fn trans_be(cx: block, e: @ast::expr) -> block { + // FIXME: Turn this into a real tail call once + // calling convention issues are settled + ret trans_ret(cx, some(e)); +} + +fn init_local(bcx: block, local: @ast::local) -> block { + let ty = node_id_type(bcx, local.node.id); + let llptr = alt bcx.fcx.lllocals.find(local.node.id) { + some(local_mem(v)) { v } + some(_) { bcx.tcx().sess.span_bug(local.span, + "init_local: Someone forgot to document why it's\ + safe to assume local.node.init must be local_mem!"); + } + // This is a local that is kept immediate + none { + let initexpr = alt local.node.init { + some({expr, _}) { expr } + none { bcx.tcx().sess.span_bug(local.span, + "init_local: Someone forgot to document why it's\ + safe to assume local.node.init isn't none!"); } + }; + let {bcx, val, kind} = trans_temp_lval(bcx, initexpr); + if kind != temporary { + if kind == owned { val = Load(bcx, val); } + let rs = take_ty_immediate(bcx, val, ty); + bcx = rs.bcx; val = rs.val; + add_clean_temp(bcx, val, ty); + } + bcx.fcx.lllocals.insert(local.node.pat.id, local_imm(val)); + ret bcx; + } + }; + + let bcx = bcx; + alt local.node.init { + some(init) { + if init.op == ast::init_assign || !expr_is_lval(bcx, init.expr) { + bcx = trans_expr_save_in(bcx, init.expr, llptr); + } else { // This is a move from an lval, must perform an actual move + let sub = trans_lval(bcx, init.expr); + bcx = move_val(sub.bcx, INIT, llptr, sub, ty); + } + } + _ { bcx = zero_alloca(bcx, llptr, ty); } + } + // Make a note to drop this slot on the way out. + add_clean(bcx, llptr, ty); + ret alt::bind_irrefutable_pat(bcx, local.node.pat, llptr, false); +} + +fn zero_alloca(cx: block, llptr: ValueRef, t: ty::t) + -> block { + let bcx = cx; + let ccx = cx.ccx(); + if check type_has_static_size(ccx, t) { + let llty = type_of(ccx, t); + Store(bcx, C_null(llty), llptr); + } else { + let key = alt ccx.sess.targ_cfg.arch { + session::arch_x86 | session::arch_arm { "llvm.memset.p0i8.i32" } + session::arch_x86_64 { "llvm.memset.p0i8.i64" } + }; + let i = ccx.intrinsics; + let memset = i.get(key); + let dst_ptr = PointerCast(cx, llptr, T_ptr(T_i8())); + let size = size_of(cx, t); + bcx = size.bcx; + let align = C_i32(1i32); // cannot use computed value here. + let volatile = C_bool(false); + Call(cx, memset, [dst_ptr, C_u8(0u), size.val, align, volatile]); + } + ret bcx; +} + +fn trans_stmt(cx: block, s: ast::stmt) -> block { + #debug["trans_expr(%s)", stmt_to_str(s)]; + + if (!cx.sess().opts.no_asm_comments) { + add_span_comment(cx, s.span, stmt_to_str(s)); + } + + let bcx = cx; + debuginfo::update_source_pos(cx, s.span); + + alt s.node { + ast::stmt_expr(e, _) | ast::stmt_semi(e, _) { + bcx = trans_expr(cx, e, ignore); + } + ast::stmt_decl(d, _) { + alt d.node { + ast::decl_local(locals) { + for local in locals { + bcx = init_local(bcx, local); + if cx.sess().opts.extra_debuginfo { + debuginfo::create_local_var(bcx, local); + } + } + } + ast::decl_item(i) { trans_item(cx.fcx.ccx, *i); } + } + } + _ { cx.sess().unimpl("stmt variant"); } + } + + ret bcx; +} + +// You probably don't want to use this one. See the +// next three functions instead. +fn new_block(cx: fn_ctxt, parent: block_parent, kind: block_kind, + name: str, block_span: option<span>) -> block { + let s = ""; + if cx.ccx.sess.opts.save_temps || cx.ccx.sess.opts.debuginfo { + s = cx.ccx.names(name); + } + let llbb: BasicBlockRef = str::as_buf(s, {|buf| + llvm::LLVMAppendBasicBlock(cx.llfn, buf) + }); + let bcx = @{llbb: llbb, + mutable terminated: false, + mutable unreachable: false, + parent: parent, + kind: kind, + mutable block_span: block_span, + fcx: cx}; + alt parent { + parent_some(cx) { + if cx.unreachable { Unreachable(bcx); } + } + _ {} + } + ret bcx; +} + +fn simple_block_scope() -> block_kind { + block_scope({is_loop: none, mutable cleanups: [], + mutable cleanup_paths: [], mutable landing_pad: none}) +} + +// Use this when you're at the top block of a function or the like. +fn top_scope_block(fcx: fn_ctxt, sp: option<span>) -> block { + ret new_block(fcx, parent_none, simple_block_scope(), + "function top level", sp); +} + +fn scope_block(bcx: block, n: str) -> block { + ret new_block(bcx.fcx, parent_some(bcx), simple_block_scope(), + n, none); +} + +fn loop_scope_block(bcx: block, _cont: loop_cont, + _break: block, n: str, sp: span) + -> block { + ret new_block(bcx.fcx, parent_some(bcx), block_scope({ + is_loop: some({cnt: _cont, brk: _break}), + mutable cleanups: [], + mutable cleanup_paths: [], + mutable landing_pad: none + }), n, some(sp)); +} + + +// Use this when you're making a general CFG BB within a scope. +fn sub_block(bcx: block, n: str) -> block { + ret new_block(bcx.fcx, parent_some(bcx), block_non_scope, n, none); +} + +fn raw_block(fcx: fn_ctxt, llbb: BasicBlockRef) -> block { + ret @{llbb: llbb, + mutable terminated: false, + mutable unreachable: false, + parent: parent_none, + kind: block_non_scope, + mutable block_span: none, + fcx: fcx}; +} + + +// trans_block_cleanups: Go through all the cleanups attached to this +// block and execute them. +// +// When translating a block that introdces new variables during its scope, we +// need to make sure those variables go out of scope when the block ends. We +// do that by running a 'cleanup' function for each variable. +// trans_block_cleanups runs all the cleanup functions for the block. +fn trans_block_cleanups(bcx: block, cleanup_cx: block) -> + block { + if bcx.unreachable { ret bcx; } + let bcx = bcx; + alt check cleanup_cx.kind { + block_scope({cleanups, _}) { + vec::riter(cleanups) {|cu| + alt cu { clean(cfn) | clean_temp(_, cfn) { bcx = cfn(bcx); } } + } + } + } + ret bcx; +} + +// In the last argument, some(block) mean jump to this block, and none means +// this is a landing pad and leaving should be accomplished with a resume +// instruction. +fn cleanup_and_leave(bcx: block, upto: option<BasicBlockRef>, + leave: option<BasicBlockRef>) { + let cur = bcx, bcx = bcx; + while true { + alt cur.kind { + block_scope(info) if info.cleanups.len() > 0u { + for exists in info.cleanup_paths { + if exists.target == leave { + Br(bcx, exists.dest); + ret; + } + } + let sub_cx = sub_block(bcx, "cleanup"); + Br(bcx, sub_cx.llbb); + info.cleanup_paths += [{target: leave, dest: sub_cx.llbb}]; + bcx = trans_block_cleanups(sub_cx, cur); + } + _ {} + } + alt upto { + some(bb) { if cur.llbb == bb { break; } } + _ {} + } + cur = alt cur.parent { + parent_some(next) { next } + parent_none { assert option::is_none(upto); break; } + }; + } + alt leave { + some(target) { Br(bcx, target); } + none { Resume(bcx, Load(bcx, option::get(bcx.fcx.personality))); } + } +} + +fn cleanup_and_Br(bcx: block, upto: block, + target: BasicBlockRef) { + cleanup_and_leave(bcx, some(upto.llbb), some(target)); +} + +fn leave_block(bcx: block, out_of: block) -> block { + let next_cx = sub_block(block_parent(out_of), "next"); + if bcx.unreachable { Unreachable(next_cx); } + cleanup_and_Br(bcx, out_of, next_cx.llbb); + next_cx +} + +fn with_scope(bcx: block, name: str, f: fn(block) -> block) -> block { + let scope_cx = scope_block(bcx, name); + Br(bcx, scope_cx.llbb); + leave_block(f(scope_cx), scope_cx) +} + +fn with_scope_result(bcx: block, name: str, f: fn(block) -> result) + -> result { + let scope_cx = scope_block(bcx, name); + Br(bcx, scope_cx.llbb); + let {bcx, val} = f(scope_cx); + {bcx: leave_block(bcx, scope_cx), val: val} +} + +fn with_cond(bcx: block, val: ValueRef, f: fn(block) -> block) -> block { + let next_cx = sub_block(bcx, "next"), cond_cx = sub_block(bcx, "cond"); + CondBr(bcx, val, cond_cx.llbb, next_cx.llbb); + let after_cx = f(cond_cx); + if !after_cx.terminated { Br(after_cx, next_cx.llbb); } + next_cx +} + +fn trans_fn_cleanups(fcx: fn_ctxt, cx: block) { + option::may(fcx.llobstacktoken) {|lltoken| + Call(cx, fcx.ccx.upcalls.dynastack_free, [lltoken]); + } +} + +fn block_locals(b: ast::blk, it: fn(@ast::local)) { + for s: @ast::stmt in b.node.stmts { + alt s.node { + ast::stmt_decl(d, _) { + alt d.node { + ast::decl_local(locals) { + for local in locals { it(local); } + } + _ {/* fall through */ } + } + } + _ {/* fall through */ } + } + } +} + +fn alloc_ty(cx: block, t: ty::t) -> result { + let bcx = cx, ccx = cx.ccx(); + let llty = type_of(ccx, t); + let val = if type_has_static_size(ccx, t) { + alloca(bcx, llty) + } else { + // NB: we have to run this particular 'size_of' in a + // block built on the llderivedtydescs block for the fn, + // so that the size dominates the array_alloca that + // comes next. + let n = size_of(raw_block(cx.fcx, cx.fcx.llderivedtydescs), + t); + bcx.fcx.llderivedtydescs = n.bcx.llbb; + PointerCast(bcx, dynastack_alloca(bcx, T_i8(), n.val, t), T_ptr(llty)) + }; + + // NB: since we've pushed all size calculations in this + // function up to the alloca block, we actually return the + // block passed into us unmodified; it doesn't really + // have to be passed-and-returned here, but it fits + // past caller conventions and may well make sense again, + // so we leave it as-is. + + ret rslt(cx, val); +} + +fn alloc_local(cx: block, local: @ast::local) -> block { + let t = node_id_type(cx, local.node.id); + let simple_name = alt local.node.pat.node { + ast::pat_ident(pth, none) { some(path_to_ident(pth)) } + _ { none } + }; + // Do not allocate space for locals that can be kept immediate. + let ccx = cx.ccx(); + if option::is_some(simple_name) && + !ccx.maps.mutbl_map.contains_key(local.node.pat.id) && + !ccx.maps.last_uses.contains_key(local.node.pat.id) && + ty::type_is_immediate(t) { + alt local.node.init { + some({op: ast::init_assign, _}) { ret cx; } + _ {} + } + } + let {bcx, val} = alloc_ty(cx, t); + if cx.sess().opts.debuginfo { + option::may(simple_name) {|name| + str::as_buf(name, {|buf| + llvm::LLVMSetValueName(val, buf) + }); + } + } + cx.fcx.lllocals.insert(local.node.id, local_mem(val)); + ret bcx; +} + +fn trans_block(bcx: block, b: ast::blk, dest: dest) + -> block { + let bcx = bcx; + block_locals(b) {|local| bcx = alloc_local(bcx, local); }; + for s: @ast::stmt in b.node.stmts { + debuginfo::update_source_pos(bcx, b.span); + bcx = trans_stmt(bcx, *s); + } + alt b.node.expr { + some(e) { + let bt = ty::type_is_bot(expr_ty(bcx, e)); + debuginfo::update_source_pos(bcx, e.span); + bcx = trans_expr(bcx, e, if bt { ignore } else { dest }); + } + _ { assert dest == ignore || bcx.unreachable; } + } + ret bcx; +} + +// Creates the standard quartet of basic blocks: static allocas, copy args, +// derived tydescs, and dynamic allocas. +fn mk_standard_basic_blocks(llfn: ValueRef) -> + {sa: BasicBlockRef, + ca: BasicBlockRef, + dt: BasicBlockRef, + da: BasicBlockRef, + rt: BasicBlockRef} { + ret {sa: str::as_buf("static_allocas", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) }), + ca: str::as_buf("load_env", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) }), + dt: str::as_buf("derived_tydescs", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) }), + da: str::as_buf("dynamic_allocas", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) }), + rt: str::as_buf("return", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) })}; +} + + +// NB: must keep 4 fns in sync: +// +// - type_of_fn +// - create_llargs_for_fn_args. +// - new_fn_ctxt +// - trans_args +fn new_fn_ctxt_w_id(ccx: crate_ctxt, path: path, + llfndecl: ValueRef, id: ast::node_id, + param_substs: option<param_substs>, + sp: option<span>) -> fn_ctxt { + let llbbs = mk_standard_basic_blocks(llfndecl); + ret @{llfn: llfndecl, + llenv: llvm::LLVMGetParam(llfndecl, 1u as c_uint), + llretptr: llvm::LLVMGetParam(llfndecl, 0u as c_uint), + mutable llstaticallocas: llbbs.sa, + mutable llloadenv: llbbs.ca, + mutable llderivedtydescs_first: llbbs.dt, + mutable llderivedtydescs: llbbs.dt, + mutable lldynamicallocas: llbbs.da, + mutable llreturn: llbbs.rt, + mutable llobstacktoken: none::<ValueRef>, + mutable llself: none, + mutable personality: none, + llargs: new_int_hash::<local_val>(), + lllocals: new_int_hash::<local_val>(), + llupvars: new_int_hash::<ValueRef>(), + mutable lltyparams: [], + derived_tydescs: ty::new_ty_hash(), + id: id, + param_substs: param_substs, + span: sp, + path: path, + ccx: ccx}; +} + +fn new_fn_ctxt(ccx: crate_ctxt, path: path, llfndecl: ValueRef, + sp: option<span>) -> fn_ctxt { + ret new_fn_ctxt_w_id(ccx, path, llfndecl, -1, none, sp); +} + +// NB: must keep 4 fns in sync: +// +// - type_of_fn +// - create_llargs_for_fn_args. +// - new_fn_ctxt +// - trans_args + +// create_llargs_for_fn_args: Creates a mapping from incoming arguments to +// allocas created for them. +// +// When we translate a function, we need to map its incoming arguments to the +// spaces that have been created for them (by code in the llallocas field of +// the function's fn_ctxt). create_llargs_for_fn_args populates the llargs +// field of the fn_ctxt with +fn create_llargs_for_fn_args(cx: fn_ctxt, + ty_self: self_arg, + args: [ast::arg], + tps_bounds: [ty::param_bounds]) { + // Skip the implicit arguments 0, and 1. + let arg_n = first_tp_arg; + alt ty_self { + impl_self(tt) { + cx.llself = some({v: cx.llenv, t: tt}); + } + no_self {} + } + for bounds in tps_bounds { + let lltydesc = llvm::LLVMGetParam(cx.llfn, arg_n as c_uint); + let dicts = none; + arg_n += 1u; + for bound in *bounds { + alt bound { + ty::bound_iface(_) { + let dict = llvm::LLVMGetParam(cx.llfn, arg_n as c_uint); + arg_n += 1u; + dicts = some(alt dicts { + none { [dict] } + some(ds) { ds + [dict] } + }); + } + _ {} + } + } + cx.lltyparams += [{desc: lltydesc, dicts: dicts}]; + } + + // Populate the llargs field of the function context with the ValueRefs + // that we get from llvm::LLVMGetParam for each argument. + for arg: ast::arg in args { + let llarg = llvm::LLVMGetParam(cx.llfn, arg_n as c_uint); + assert (llarg as int != 0); + // Note that this uses local_mem even for things passed by value. + // copy_args_to_allocas will overwrite the table entry with local_imm + // before it's actually used. + cx.llargs.insert(arg.id, local_mem(llarg)); + arg_n += 1u; + } +} + +fn copy_args_to_allocas(fcx: fn_ctxt, bcx: block, args: [ast::arg], + arg_tys: [ty::arg]) -> block { + let tcx = bcx.tcx(); + let arg_n: uint = 0u, bcx = bcx; + let epic_fail = fn@() -> ! { + tcx.sess.bug("Someone forgot\ + to document an invariant in copy_args_to_allocas!"); + }; + for arg in arg_tys { + let id = args[arg_n].id; + let argval = alt fcx.llargs.get(id) { local_mem(v) { v } + _ { epic_fail() } }; + alt ty::resolved_mode(tcx, arg.mode) { + ast::by_mutbl_ref { } + ast::by_move | ast::by_copy { add_clean(bcx, argval, arg.ty); } + ast::by_val { + if !ty::type_is_immediate(arg.ty) { + let {bcx: cx, val: alloc} = alloc_ty(bcx, arg.ty); + bcx = cx; + Store(bcx, argval, alloc); + fcx.llargs.insert(id, local_mem(alloc)); + } else { + fcx.llargs.insert(id, local_imm(argval)); + } + } + ast::by_ref {} + } + if fcx.ccx.sess.opts.extra_debuginfo { + debuginfo::create_arg(bcx, args[arg_n], args[arg_n].ty.span); + } + arg_n += 1u; + } + ret bcx; +} + +// Ties up the llstaticallocas -> llloadenv -> llderivedtydescs -> +// lldynamicallocas -> lltop edges, and builds the return block. +fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) { + tie_up_header_blocks(fcx, lltop); + let ret_cx = raw_block(fcx, fcx.llreturn); + trans_fn_cleanups(fcx, ret_cx); + RetVoid(ret_cx); +} + +fn tie_up_header_blocks(fcx: fn_ctxt, lltop: BasicBlockRef) { + Br(raw_block(fcx, fcx.llstaticallocas), fcx.llloadenv); + Br(raw_block(fcx, fcx.llloadenv), fcx.llderivedtydescs_first); + Br(raw_block(fcx, fcx.llderivedtydescs), fcx.lldynamicallocas); + Br(raw_block(fcx, fcx.lldynamicallocas), lltop); +} + +enum self_arg { impl_self(ty::t), no_self, } + +// trans_closure: Builds an LLVM function out of a source function. +// If the function closes over its environment a closure will be +// returned. +fn trans_closure(ccx: crate_ctxt, path: path, decl: ast::fn_decl, + body: ast::blk, llfndecl: ValueRef, + ty_self: self_arg, + tps_bounds: [ty::param_bounds], + param_substs: option<param_substs>, + id: ast::node_id, maybe_load_env: fn(fn_ctxt)) { + set_uwtable(llfndecl); + + // Set up arguments to the function. + let fcx = new_fn_ctxt_w_id(ccx, path, llfndecl, id, param_substs, + some(body.span)); + create_llargs_for_fn_args(fcx, ty_self, decl.inputs, tps_bounds); + + // Create the first basic block in the function and keep a handle on it to + // pass to finish_fn later. + let bcx_top = top_scope_block(fcx, some(body.span)), bcx = bcx_top; + let lltop = bcx.llbb; + let block_ty = node_id_type(bcx, body.node.id); + + let arg_tys = ty::ty_fn_args(node_id_type(bcx, id)); + bcx = copy_args_to_allocas(fcx, bcx, decl.inputs, arg_tys); + + maybe_load_env(fcx); + + // This call to trans_block is the place where we bridge between + // translation calls that don't have a return value (trans_crate, + // trans_mod, trans_item, et cetera) and those that do + // (trans_block, trans_expr, et cetera). + if option::is_none(body.node.expr) || + ty::type_is_bot(block_ty) || + ty::type_is_nil(block_ty) { + bcx = trans_block(bcx, body, ignore); + } else { + bcx = trans_block(bcx, body, save_in(fcx.llretptr)); + } + cleanup_and_Br(bcx, bcx_top, fcx.llreturn); + + // Insert the mandatory first few basic blocks before lltop. + finish_fn(fcx, lltop); +} + +// trans_fn: creates an LLVM function corresponding to a source language +// function. +fn trans_fn(ccx: crate_ctxt, + path: path, + decl: ast::fn_decl, + body: ast::blk, + llfndecl: ValueRef, + ty_self: self_arg, + tps_bounds: [ty::param_bounds], + param_substs: option<param_substs>, + id: ast::node_id) { + let do_time = ccx.sess.opts.stats; + let start = if do_time { time::get_time() } + else { {sec: 0u32, usec: 0u32} }; + trans_closure(ccx, path, decl, body, llfndecl, ty_self, + tps_bounds, param_substs, id, {|fcx| + if ccx.sess.opts.extra_debuginfo { + debuginfo::create_function(fcx); + } + }); + if do_time { + let end = time::get_time(); + log_fn_time(ccx, path_str(path), start, end); + } +} + +fn trans_res_ctor(ccx: crate_ctxt, path: path, dtor: ast::fn_decl, + ctor_id: ast::node_id, tps_bounds: [ty::param_bounds], + param_substs: option<param_substs>, llfndecl: ValueRef) { + // Create a function for the constructor + let fcx = new_fn_ctxt_w_id(ccx, path, llfndecl, ctor_id, + param_substs, none); + create_llargs_for_fn_args(fcx, no_self, dtor.inputs, tps_bounds); + let bcx = top_scope_block(fcx, none), lltop = bcx.llbb; + let fty = node_id_type(bcx, ctor_id); + let arg_t = ty::ty_fn_args(fty)[0].ty; + let tup_t = ty::mk_tup(ccx.tcx, [ty::mk_mach_uint(ccx.tcx, ast::ty_u8), + arg_t]); + let arg = alt fcx.llargs.find(dtor.inputs[0].id) { + some(local_mem(x)) { x } + _ { ccx.sess.bug("Someone forgot to document an invariant \ + in trans_res_ctor"); } + }; + let llretptr = fcx.llretptr; + if ty::type_has_dynamic_size(ccx.tcx, ty::ty_fn_ret(fty)) { + let llret_t = T_ptr(T_struct([ccx.int_type, llvm::LLVMTypeOf(arg)])); + llretptr = BitCast(bcx, llretptr, llret_t); + } + + let {bcx, val: dst} = GEP_tup_like(bcx, tup_t, llretptr, [0, 1]); + bcx = memmove_ty(bcx, dst, arg, arg_t); + let flag = GEP_tup_like(bcx, tup_t, llretptr, [0, 0]); + bcx = flag.bcx; + let one = C_u8(1u); + Store(bcx, one, flag.val); + build_return(bcx); + finish_fn(fcx, lltop); +} + + +fn trans_enum_variant(ccx: crate_ctxt, enum_id: ast::node_id, + variant: ast::variant, disr: int, is_degen: bool, + ty_params: [ast::ty_param], + param_substs: option<param_substs>, + llfndecl: ValueRef) { + // Translate variant arguments to function arguments. + let fn_args = [], i = 0u; + for varg in variant.node.args { + fn_args += [{mode: ast::expl(ast::by_copy), + ty: varg.ty, + ident: "arg" + uint::to_str(i, 10u), + id: varg.id}]; + } + let fcx = new_fn_ctxt_w_id(ccx, [], llfndecl, variant.node.id, + param_substs, none); + create_llargs_for_fn_args(fcx, no_self, fn_args, + param_bounds(ccx, ty_params)); + let ty_param_substs = alt param_substs { + some(substs) { substs.tys } + none { + let i = 0u; + vec::map(ty_params, {|tp| + i += 1u; + ty::mk_param(ccx.tcx, i - 1u, local_def(tp.id)) + }) + } + }; + let bcx = top_scope_block(fcx, none), lltop = bcx.llbb; + let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id)); + bcx = copy_args_to_allocas(fcx, bcx, fn_args, arg_tys); + + // Cast the enum to a type we can GEP into. + let llblobptr = if is_degen { + fcx.llretptr + } else { + let llenumptr = + PointerCast(bcx, fcx.llretptr, T_opaque_enum_ptr(ccx)); + let lldiscrimptr = GEPi(bcx, llenumptr, [0, 0]); + Store(bcx, C_int(ccx, disr), lldiscrimptr); + GEPi(bcx, llenumptr, [0, 1]) + }; + let i = 0u; + let t_id = local_def(enum_id); + let v_id = local_def(variant.node.id); + for va: ast::variant_arg in variant.node.args { + let rslt = GEP_enum(bcx, llblobptr, t_id, v_id, ty_param_substs, i); + bcx = rslt.bcx; + let lldestptr = rslt.val; + // If this argument to this function is a enum, it'll have come in to + // this function as an opaque blob due to the way that type_of() + // works. So we have to cast to the destination's view of the type. + let llarg = alt check fcx.llargs.find(va.id) { + some(local_mem(x)) { x } + }; + let arg_ty = arg_tys[i].ty; + if ty::type_has_params(arg_ty) { + lldestptr = PointerCast(bcx, lldestptr, val_ty(llarg)); + } + bcx = memmove_ty(bcx, lldestptr, llarg, arg_ty); + i += 1u; + } + build_return(bcx); + finish_fn(fcx, lltop); +} + + +// FIXME: this should do some structural hash-consing to avoid +// duplicate constants. I think. Maybe LLVM has a magical mode +// that does so later on? +fn trans_const_expr(cx: crate_ctxt, e: @ast::expr) -> ValueRef { + alt e.node { + ast::expr_lit(lit) { ret trans_crate_lit(cx, *lit); } + ast::expr_binary(b, e1, e2) { + let te1 = trans_const_expr(cx, e1); + let te2 = trans_const_expr(cx, e2); + + let te2 = cast_shift_const_rhs(b, te1, te2); + + /* Neither type is bottom, and we expect them to be unified already, + * so the following is safe. */ + let ty = ty::expr_ty(cx.tcx, e1); + let is_float = ty::type_is_fp(ty); + let signed = ty::type_is_signed(ty); + ret alt b { + ast::add { + if is_float { llvm::LLVMConstFAdd(te1, te2) } + else { llvm::LLVMConstAdd(te1, te2) } + } + ast::subtract { + if is_float { llvm::LLVMConstFSub(te1, te2) } + else { llvm::LLVMConstSub(te1, te2) } + } + ast::mul { + if is_float { llvm::LLVMConstFMul(te1, te2) } + else { llvm::LLVMConstMul(te1, te2) } + } + ast::div { + if is_float { llvm::LLVMConstFDiv(te1, te2) } + else if signed { llvm::LLVMConstSDiv(te1, te2) } + else { llvm::LLVMConstUDiv(te1, te2) } + } + ast::rem { + if is_float { llvm::LLVMConstFRem(te1, te2) } + else if signed { llvm::LLVMConstSRem(te1, te2) } + else { llvm::LLVMConstURem(te1, te2) } + } + ast::and | + ast::or { cx.sess.span_unimpl(e.span, "binop logic"); } + ast::bitxor { llvm::LLVMConstXor(te1, te2) } + ast::bitand { llvm::LLVMConstAnd(te1, te2) } + ast::bitor { llvm::LLVMConstOr(te1, te2) } + ast::lsl { llvm::LLVMConstShl(te1, te2) } + ast::lsr { llvm::LLVMConstLShr(te1, te2) } + ast::asr { llvm::LLVMConstAShr(te1, te2) } + ast::eq | + ast::lt | + ast::le | + ast::ne | + ast::ge | + ast::gt { cx.sess.span_unimpl(e.span, "binop comparator"); } + } + } + ast::expr_unary(u, e) { + let te = trans_const_expr(cx, e); + let ty = ty::expr_ty(cx.tcx, e); + let is_float = ty::type_is_fp(ty); + ret alt u { + ast::box(_) | + ast::uniq(_) | + ast::deref { cx.sess.span_bug(e.span, + "bad unop type in trans_const_expr"); } + ast::not { llvm::LLVMConstNot(te) } + ast::neg { + if is_float { llvm::LLVMConstFNeg(te) } + else { llvm::LLVMConstNeg(te) } + } + } + } + _ { cx.sess.span_bug(e.span, + "bad constant expression type in trans_const_expr"); } + } +} + +fn trans_const(cx: crate_ctxt, e: @ast::expr, id: ast::node_id) { + let v = trans_const_expr(cx, e); + + // The scalars come back as 1st class LLVM vals + // which we have to stick into global constants. + + alt cx.consts.find(id) { + some(g) { + llvm::LLVMSetInitializer(g, v); + llvm::LLVMSetGlobalConstant(g, True); + } + _ { cx.sess.span_fatal(e.span, "Unbound const in trans_const"); } + } +} + +fn trans_item(ccx: crate_ctxt, item: ast::item) { + let path = alt check ccx.tcx.items.get(item.id) { + ast_map::node_item(_, p) { p } + }; + alt item.node { + ast::item_fn(decl, tps, body) { + let llfndecl = alt ccx.item_ids.find(item.id) { + some(llfndecl) { llfndecl } + _ { + ccx.sess.span_fatal(item.span, + "unbound function item in trans_item"); + } + }; + if decl.purity != ast::crust_fn { + trans_fn(ccx, *path + [path_name(item.ident)], decl, body, + llfndecl, no_self, param_bounds(ccx, tps), + none, item.id); + } else { + native::trans_crust_fn(ccx, *path + [path_name(item.ident)], + decl, body, llfndecl, item.id); + } + } + ast::item_impl(tps, _, _, ms) { + impl::trans_impl(ccx, *path, item.ident, ms, item.id, tps); + } + ast::item_res(decl, tps, body, dtor_id, ctor_id) { + let llctor_decl = ccx.item_ids.get(ctor_id); + trans_res_ctor(ccx, *path, decl, ctor_id, + param_bounds(ccx, tps), none, llctor_decl); + + // Create a function for the destructor + alt ccx.item_ids.find(item.id) { + some(lldtor_decl) { + trans_fn(ccx, *path + [path_name(item.ident)], decl, body, + lldtor_decl, no_self, param_bounds(ccx, tps), + none, dtor_id); + } + _ { + ccx.sess.span_fatal(item.span, "unbound dtor in trans_item"); + } + } + } + ast::item_mod(m) { + trans_mod(ccx, m); + } + ast::item_enum(variants, tps) { + let degen = variants.len() == 1u; + let vi = ty::enum_variants(ccx.tcx, local_def(item.id)); + let i = 0; + for variant: ast::variant in variants { + if variant.node.args.len() > 0u { + trans_enum_variant(ccx, item.id, variant, + vi[i].disr_val, degen, tps, + none, ccx.item_ids.get(variant.node.id)); + } + i += 1; + } + } + ast::item_const(_, expr) { trans_const(ccx, expr, item.id); } + ast::item_native_mod(native_mod) { + let abi = alt attr::native_abi(item.attrs) { + either::right(abi_) { abi_ } + either::left(msg) { ccx.sess.span_fatal(item.span, msg) } + }; + native::trans_native_mod(ccx, native_mod, abi); + } + _ {/* fall through */ } + } +} + +// Translate a module. Doing this amounts to translating the items in the +// module; there ends up being no artifact (aside from linkage names) of +// separate modules in the compiled program. That's because modules exist +// only as a convenience for humans working with the code, to organize names +// and control visibility. +fn trans_mod(ccx: crate_ctxt, m: ast::_mod) { + for item in m.items { trans_item(ccx, *item); } +} + +fn compute_ii_method_info(ccx: crate_ctxt, + impl_did: ast::def_id, + m: @ast::method, + f: fn(ty::t, [ty::param_bounds], ast_map::path)) { + let {bounds: impl_bnds, ty: impl_ty} = + ty::lookup_item_type(ccx.tcx, impl_did); + let m_bounds = *impl_bnds + param_bounds(ccx, m.tps); + let impl_path = ty::item_path(ccx.tcx, impl_did); + let m_path = impl_path + [path_name(m.ident)]; + f(impl_ty, m_bounds, m_path); +} + +fn trans_inlined_items(ccx: crate_ctxt, inline_map: inline_map) { + inline_map.values {|ii| + alt ii { + ast::ii_item(item) { + trans_item(ccx, *item) + } + ast::ii_method(impl_did, m) { + compute_ii_method_info(ccx, impl_did, m) { + |impl_ty, m_bounds, m_path| + let llfndecl = ccx.item_ids.get(m.id); + trans_fn(ccx, m_path, m.decl, m.body, + llfndecl, impl_self(impl_ty), m_bounds, + none, m.id); + } + } + } + } +} + +fn get_pair_fn_ty(llpairty: TypeRef) -> TypeRef { + // Bit of a kludge: pick the fn typeref out of the pair. + ret struct_elt(llpairty, 0u); +} + +fn register_fn(ccx: crate_ctxt, sp: span, path: path, flav: str, + ty_params: [ast::ty_param], node_id: ast::node_id) { + let t = ty::node_id_to_type(ccx.tcx, node_id); + let bnds = param_bounds(ccx, ty_params); + register_fn_full(ccx, sp, path, flav, bnds, node_id, t); +} + +fn param_bounds(ccx: crate_ctxt, tps: [ast::ty_param]) -> [ty::param_bounds] { + vec::map(tps) {|tp| ccx.tcx.ty_param_bounds.get(tp.id) } +} + +fn register_fn_full(ccx: crate_ctxt, sp: span, path: path, flav: str, + bnds: [ty::param_bounds], node_id: ast::node_id, + node_type: ty::t) { + let llfty = type_of_fn_from_ty(ccx, node_type, bnds); + register_fn_fuller(ccx, sp, path, flav, node_id, node_type, + lib::llvm::CCallConv, llfty); +} + +fn register_fn_fuller(ccx: crate_ctxt, sp: span, path: path, _flav: str, + node_id: ast::node_id, node_type: ty::t, + cc: lib::llvm::CallConv, llfty: TypeRef) { + let ps: str = mangle_exported_name(ccx, path, node_type); + let llfn: ValueRef = decl_fn(ccx.llmod, ps, cc, llfty); + ccx.item_ids.insert(node_id, llfn); + ccx.item_symbols.insert(node_id, ps); + + #debug["register_fn_fuller created fn %s for item %d with path %s", + val_str(ccx.tn, llfn), node_id, ast_map::path_to_str(path)]; + + let is_main = is_main_name(path) && !ccx.sess.building_library; + if is_main { create_main_wrapper(ccx, sp, llfn, node_type); } +} + +// Create a _rust_main(args: [str]) function which will be called from the +// runtime rust_start function +fn create_main_wrapper(ccx: crate_ctxt, sp: span, main_llfn: ValueRef, + main_node_type: ty::t) { + + if ccx.main_fn != none::<ValueRef> { + ccx.sess.span_fatal(sp, "multiple 'main' functions"); + } + + let main_takes_argv = + // invariant! + alt ty::get(main_node_type).struct { + ty::ty_fn({inputs, _}) { inputs.len() != 0u } + _ { ccx.sess.span_fatal(sp, "main has a non-function type"); } + }; + + let llfn = create_main(ccx, main_llfn, main_takes_argv); + ccx.main_fn = some(llfn); + create_entry_fn(ccx, llfn); + + fn create_main(ccx: crate_ctxt, main_llfn: ValueRef, + takes_argv: bool) -> ValueRef { + let unit_ty = ty::mk_str(ccx.tcx); + let vecarg_ty: ty::arg = + {mode: ast::expl(ast::by_val), + ty: ty::mk_vec(ccx.tcx, {ty: unit_ty, mutbl: ast::m_imm})}; + let nt = ty::mk_nil(ccx.tcx); + let llfty = type_of_fn(ccx, [vecarg_ty], nt, []); + let llfdecl = decl_fn(ccx.llmod, "_rust_main", + lib::llvm::CCallConv, llfty); + + let fcx = new_fn_ctxt(ccx, [], llfdecl, none); + + let bcx = top_scope_block(fcx, none); + let lltop = bcx.llbb; + + let lloutputarg = llvm::LLVMGetParam(llfdecl, 0 as c_uint); + let llenvarg = llvm::LLVMGetParam(llfdecl, 1 as c_uint); + let args = [lloutputarg, llenvarg]; + if takes_argv { args += [llvm::LLVMGetParam(llfdecl, 2 as c_uint)]; } + Call(bcx, main_llfn, args); + build_return(bcx); + + finish_fn(fcx, lltop); + + ret llfdecl; + } + + fn create_entry_fn(ccx: crate_ctxt, rust_main: ValueRef) { + #[cfg(target_os = "win32")] + fn main_name() -> str { ret "WinMain@16"; } + #[cfg(target_os = "macos")] + fn main_name() -> str { ret "main"; } + #[cfg(target_os = "linux")] + fn main_name() -> str { ret "main"; } + #[cfg(target_os = "freebsd")] + fn main_name() -> str { ret "main"; } + let llfty = T_fn([ccx.int_type, ccx.int_type], ccx.int_type); + let llfn = decl_cdecl_fn(ccx.llmod, main_name(), llfty); + let llbb = str::as_buf("top", {|buf| + llvm::LLVMAppendBasicBlock(llfn, buf) + }); + let bld = *ccx.builder; + llvm::LLVMPositionBuilderAtEnd(bld, llbb); + let crate_map = ccx.crate_map; + let start_ty = T_fn([val_ty(rust_main), ccx.int_type, ccx.int_type, + val_ty(crate_map)], ccx.int_type); + let start = str::as_buf("rust_start", {|buf| + llvm::LLVMAddGlobal(ccx.llmod, start_ty, buf) + }); + let args = [rust_main, llvm::LLVMGetParam(llfn, 0 as c_uint), + llvm::LLVMGetParam(llfn, 1 as c_uint), crate_map]; + let result = unsafe { + llvm::LLVMBuildCall(bld, start, vec::unsafe::to_ptr(args), + args.len() as c_uint, noname()) + }; + llvm::LLVMBuildRet(bld, result); + } +} + +// Create a /real/ closure: this is like create_fn_pair, but creates a +// a fn value on the stack with a specified environment (which need not be +// on the stack). +fn create_real_fn_pair(cx: block, llfnty: TypeRef, llfn: ValueRef, + llenvptr: ValueRef) -> ValueRef { + let pair = alloca(cx, T_fn_pair(cx.ccx(), llfnty)); + fill_fn_pair(cx, pair, llfn, llenvptr); + ret pair; +} + +fn fill_fn_pair(bcx: block, pair: ValueRef, llfn: ValueRef, + llenvptr: ValueRef) { + let ccx = bcx.ccx(); + let code_cell = GEPi(bcx, pair, [0, abi::fn_field_code]); + Store(bcx, llfn, code_cell); + let env_cell = GEPi(bcx, pair, [0, abi::fn_field_box]); + let llenvblobptr = PointerCast(bcx, llenvptr, T_opaque_box_ptr(ccx)); + Store(bcx, llenvblobptr, env_cell); +} + +fn collect_native_item(ccx: crate_ctxt, + abi: @mutable option<ast::native_abi>, + i: @ast::native_item) { + alt i.node { + ast::native_item_fn(_, tps) { + let id = i.id; + let node_type = ty::node_id_to_type(ccx.tcx, id); + let fn_abi = + alt attr::get_meta_item_value_str_by_name(i.attrs, "abi") { + option::none { + // if abi isn't specified for this function, inherit from + // its enclosing native module + option::get(*abi) + } + _ { + alt attr::native_abi(i.attrs) { + either::right(abi_) { abi_ } + either::left(msg) { ccx.sess.span_fatal(i.span, msg) } + } + } + }; + alt fn_abi { + ast::native_abi_rust_intrinsic { + // For intrinsics: link the function directly to the intrinsic + // function itself. + let fn_type = type_of_fn_from_ty( + ccx, node_type, param_bounds(ccx, tps)); + let ri_name = "rust_intrinsic_" + native::link_name(i); + let llnativefn = get_extern_fn( + ccx.externs, ccx.llmod, ri_name, + lib::llvm::CCallConv, fn_type); + ccx.item_ids.insert(id, llnativefn); + ccx.item_symbols.insert(id, ri_name); + } + + ast::native_abi_cdecl | ast::native_abi_stdcall { + // For true external functions: create a rust wrapper + // and link to that. The rust wrapper will handle + // switching to the C stack. + let path = *alt check ccx.tcx.items.get(i.id) { + ast_map::node_native_item(_, p) { p } + } + [path_name(i.ident)]; + register_fn(ccx, i.span, path, "native fn", tps, i.id); + } + } + } + _ { } + } +} + +fn item_path(ccx: crate_ctxt, i: @ast::item) -> path { + *alt check ccx.tcx.items.get(i.id) { + ast_map::node_item(_, p) { p } + } + [path_name(i.ident)] +} + +fn collect_item(ccx: crate_ctxt, abi: @mutable option<ast::native_abi>, + i: @ast::item) { + let my_path = item_path(ccx, i); + alt i.node { + ast::item_const(_, _) { + let typ = ty::node_id_to_type(ccx.tcx, i.id); + let s = mangle_exported_name(ccx, my_path, typ); + let g = str::as_buf(s, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, type_of(ccx, typ), buf) + }); + ccx.item_symbols.insert(i.id, s); + ccx.consts.insert(i.id, g); + } + ast::item_native_mod(native_mod) { + // Propagate the native ABI down to collect_native_item(), + alt attr::native_abi(i.attrs) { + either::left(msg) { ccx.sess.span_fatal(i.span, msg); } + either::right(abi_) { *abi = option::some(abi_); } + } + } + ast::item_fn(decl, tps, _) { + if decl.purity != ast::crust_fn { + register_fn(ccx, i.span, my_path, "fn", tps, + i.id); + } else { + native::register_crust_fn(ccx, i.span, my_path, i.id); + } + } + ast::item_impl(tps, _, _, methods) { + let path = my_path + [path_name(int::str(i.id))]; + for m in methods { + register_fn(ccx, i.span, + path + [path_name(m.ident)], + "impl_method", tps + m.tps, m.id); + } + } + ast::item_res(_, tps, _, dtor_id, ctor_id) { + register_fn(ccx, i.span, my_path, "res_ctor", tps, ctor_id); + // Note that the destructor is associated with the item's id, not + // the dtor_id. This is a bit counter-intuitive, but simplifies + // ty_res, which would have to carry around two def_ids otherwise + // -- one to identify the type, and one to find the dtor symbol. + let t = ty::node_id_to_type(ccx.tcx, dtor_id); + register_fn_full(ccx, i.span, my_path + [path_name("dtor")], + "res_dtor", param_bounds(ccx, tps), i.id, t); + } + ast::item_enum(variants, tps) { + for variant in variants { + if variant.node.args.len() != 0u { + register_fn(ccx, i.span, + my_path + [path_name(variant.node.name)], + "enum", tps, variant.node.id); + } + } + } + _ { } + } +} + +fn collect_items(ccx: crate_ctxt, crate: @ast::crate) { + let abi = @mutable none::<ast::native_abi>; + visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ + visit_native_item: bind collect_native_item(ccx, abi, _), + visit_item: bind collect_item(ccx, abi, _) + with *visit::default_simple_visitor() + })); +} + +fn collect_inlined_items(ccx: crate_ctxt, inline_map: inline::inline_map) { + let abi = @mutable none::<ast::native_abi>; + inline_map.values {|ii| + alt ii { + ast::ii_item(item) { + collect_item(ccx, abi, item); + alt item.node { + ast::item_fn(_, _, _) { + set_always_inline(ccx.item_ids.get(item.id)); + } + _ { /* fallthrough */ } + } + } + + ast::ii_method(impl_did, m) { + compute_ii_method_info(ccx, impl_did, m) { + |_impl_ty, m_bounds, m_path| + let mthd_ty = ty::node_id_to_type(ccx.tcx, m.id); + register_fn_full(ccx, m.span, m_path, "impl_method", + m_bounds, m.id, mthd_ty); + } + } + } + } +} + +// The constant translation pass. +fn trans_constant(ccx: crate_ctxt, it: @ast::item) { + alt it.node { + ast::item_enum(variants, _) { + let vi = ty::enum_variants(ccx.tcx, {crate: ast::local_crate, + node: it.id}); + let i = 0, path = item_path(ccx, it); + for variant in variants { + let p = path + [path_name(variant.node.name), + path_name("discrim")]; + let s = mangle_exported_name(ccx, p, ty::mk_int(ccx.tcx)); + let disr_val = vi[i].disr_val; + let discrim_gvar = str::as_buf(s, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) + }); + llvm::LLVMSetInitializer(discrim_gvar, C_int(ccx, disr_val)); + llvm::LLVMSetGlobalConstant(discrim_gvar, True); + ccx.discrims.insert( + local_def(variant.node.id), discrim_gvar); + ccx.discrim_symbols.insert(variant.node.id, s); + i += 1; + } + } + ast::item_impl(tps, some(@{node: ast::ty_path(_, id), _}), _, ms) { + let i_did = ast_util::def_id_of_def(ccx.tcx.def_map.get(id)); + impl::trans_impl_vtable(ccx, item_path(ccx, it), i_did, ms, tps, it); + } + ast::item_iface(_, _) { + if !vec::any(*ty::iface_methods(ccx.tcx, local_def(it.id)), {|m| + ty::type_has_vars(ty::mk_fn(ccx.tcx, m.fty))}) { + impl::trans_iface_vtable(ccx, item_path(ccx, it), it); + } + } + _ { } + } +} + +fn trans_constants(ccx: crate_ctxt, crate: @ast::crate) { + visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ + visit_item: bind trans_constant(ccx, _) + with *visit::default_simple_visitor() + })); +} + +fn vp2i(cx: block, v: ValueRef) -> ValueRef { + let ccx = cx.ccx(); + ret PtrToInt(cx, v, ccx.int_type); +} + +fn p2i(ccx: crate_ctxt, v: ValueRef) -> ValueRef { + ret llvm::LLVMConstPtrToInt(v, ccx.int_type); +} + +fn declare_intrinsics(llmod: ModuleRef) -> hashmap<str, ValueRef> { + let T_memmove32_args: [TypeRef] = + [T_ptr(T_i8()), T_ptr(T_i8()), T_i32(), T_i32(), T_i1()]; + let T_memmove64_args: [TypeRef] = + [T_ptr(T_i8()), T_ptr(T_i8()), T_i64(), T_i32(), T_i1()]; + let T_memset32_args: [TypeRef] = + [T_ptr(T_i8()), T_i8(), T_i32(), T_i32(), T_i1()]; + let T_memset64_args: [TypeRef] = + [T_ptr(T_i8()), T_i8(), T_i64(), T_i32(), T_i1()]; + let T_trap_args: [TypeRef] = []; + let gcroot = + decl_cdecl_fn(llmod, "llvm.gcroot", + T_fn([T_ptr(T_ptr(T_i8())), T_ptr(T_i8())], T_void())); + let gcread = + decl_cdecl_fn(llmod, "llvm.gcread", + T_fn([T_ptr(T_i8()), T_ptr(T_ptr(T_i8()))], T_void())); + let memmove32 = + decl_cdecl_fn(llmod, "llvm.memmove.p0i8.p0i8.i32", + T_fn(T_memmove32_args, T_void())); + let memmove64 = + decl_cdecl_fn(llmod, "llvm.memmove.p0i8.p0i8.i64", + T_fn(T_memmove64_args, T_void())); + let memset32 = + decl_cdecl_fn(llmod, "llvm.memset.p0i8.i32", + T_fn(T_memset32_args, T_void())); + let memset64 = + decl_cdecl_fn(llmod, "llvm.memset.p0i8.i64", + T_fn(T_memset64_args, T_void())); + let trap = decl_cdecl_fn(llmod, "llvm.trap", T_fn(T_trap_args, T_void())); + let intrinsics = new_str_hash::<ValueRef>(); + intrinsics.insert("llvm.gcroot", gcroot); + intrinsics.insert("llvm.gcread", gcread); + intrinsics.insert("llvm.memmove.p0i8.p0i8.i32", memmove32); + intrinsics.insert("llvm.memmove.p0i8.p0i8.i64", memmove64); + intrinsics.insert("llvm.memset.p0i8.i32", memset32); + intrinsics.insert("llvm.memset.p0i8.i64", memset64); + intrinsics.insert("llvm.trap", trap); + ret intrinsics; +} + +fn declare_dbg_intrinsics(llmod: ModuleRef, + intrinsics: hashmap<str, ValueRef>) { + let declare = + decl_cdecl_fn(llmod, "llvm.dbg.declare", + T_fn([T_metadata(), T_metadata()], T_void())); + let value = + decl_cdecl_fn(llmod, "llvm.dbg.value", + T_fn([T_metadata(), T_i64(), T_metadata()], T_void())); + intrinsics.insert("llvm.dbg.declare", declare); + intrinsics.insert("llvm.dbg.value", value); +} + +fn trap(bcx: block) { + let v: [ValueRef] = []; + alt bcx.ccx().intrinsics.find("llvm.trap") { + some(x) { Call(bcx, x, v); } + _ { bcx.sess().bug("unbound llvm.trap in trap"); } + } +} + +fn create_module_map(ccx: crate_ctxt) -> ValueRef { + let elttype = T_struct([ccx.int_type, ccx.int_type]); + let maptype = T_array(elttype, ccx.module_data.size() + 1u); + let map = str::as_buf("_rust_mod_map", {|buf| + llvm::LLVMAddGlobal(ccx.llmod, maptype, buf) + }); + lib::llvm::SetLinkage(map, lib::llvm::InternalLinkage); + let elts: [ValueRef] = []; + ccx.module_data.items {|key, val| + let elt = C_struct([p2i(ccx, C_cstr(ccx, key)), + p2i(ccx, val)]); + elts += [elt]; + }; + let term = C_struct([C_int(ccx, 0), C_int(ccx, 0)]); + elts += [term]; + llvm::LLVMSetInitializer(map, C_array(elttype, elts)); + ret map; +} + + +fn decl_crate_map(sess: session::session, mapname: str, + llmod: ModuleRef) -> ValueRef { + let targ_cfg = sess.targ_cfg; + let int_type = T_int(targ_cfg); + let n_subcrates = 1; + let cstore = sess.cstore; + while cstore::have_crate_data(cstore, n_subcrates) { n_subcrates += 1; } + let mapname = if sess.building_library { mapname } else { "toplevel" }; + let sym_name = "_rust_crate_map_" + mapname; + let arrtype = T_array(int_type, n_subcrates as uint); + let maptype = T_struct([int_type, arrtype]); + let map = str::as_buf(sym_name, {|buf| + llvm::LLVMAddGlobal(llmod, maptype, buf) + }); + lib::llvm::SetLinkage(map, lib::llvm::ExternalLinkage); + ret map; +} + +// FIXME use hashed metadata instead of crate names once we have that +fn fill_crate_map(ccx: crate_ctxt, map: ValueRef) { + let subcrates: [ValueRef] = []; + let i = 1; + let cstore = ccx.sess.cstore; + while cstore::have_crate_data(cstore, i) { + let nm = "_rust_crate_map_" + cstore::get_crate_data(cstore, i).name; + let cr = str::as_buf(nm, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, ccx.int_type, buf) + }); + subcrates += [p2i(ccx, cr)]; + i += 1; + } + subcrates += [C_int(ccx, 0)]; + llvm::LLVMSetInitializer(map, C_struct( + [p2i(ccx, create_module_map(ccx)), + C_array(ccx.int_type, subcrates)])); +} + +fn write_metadata(cx: crate_ctxt, crate: @ast::crate) { + if !cx.sess.building_library { ret; } + let llmeta = C_bytes(metadata::encoder::encode_metadata(cx, crate)); + let llconst = C_struct([llmeta]); + let llglobal = str::as_buf("rust_metadata", {|buf| + llvm::LLVMAddGlobal(cx.llmod, val_ty(llconst), buf) + }); + llvm::LLVMSetInitializer(llglobal, llconst); + str::as_buf(cx.sess.targ_cfg.target_strs.meta_sect_name, {|buf| + llvm::LLVMSetSection(llglobal, buf) + }); + lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage); + + let t_ptr_i8 = T_ptr(T_i8()); + llglobal = llvm::LLVMConstBitCast(llglobal, t_ptr_i8); + let llvm_used = str::as_buf("llvm.used", {|buf| + llvm::LLVMAddGlobal(cx.llmod, T_array(t_ptr_i8, 1u), buf) + }); + lib::llvm::SetLinkage(llvm_used, lib::llvm::AppendingLinkage); + llvm::LLVMSetInitializer(llvm_used, C_array(t_ptr_i8, [llglobal])); +} + +// Writes the current ABI version into the crate. +fn write_abi_version(ccx: crate_ctxt) { + mk_global(ccx, "rust_abi_version", C_uint(ccx, abi::abi_version), + false); +} + +fn trans_crate(sess: session::session, crate: @ast::crate, tcx: ty::ctxt, + output: str, emap: resolve::exp_map, maps: maps, + inline_map: inline::inline_map) + -> (ModuleRef, link::link_meta) { + let sha = std::sha1::mk_sha1(); + let link_meta = link::build_link_meta(sess, *crate, output, sha); + + // Append ".rc" to crate name as LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifer is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = link_meta.name + ".rc"; + + let llmod = str::as_buf(llmod_id, {|buf| + llvm::LLVMModuleCreateWithNameInContext + (buf, llvm::LLVMGetGlobalContext()) + }); + let data_layout = sess.targ_cfg.target_strs.data_layout; + let targ_triple = sess.targ_cfg.target_strs.target_triple; + let _: () = + str::as_buf(data_layout, + {|buf| llvm::LLVMSetDataLayout(llmod, buf) }); + let _: () = + str::as_buf(targ_triple, + {|buf| llvm::LLVMSetTarget(llmod, buf) }); + let targ_cfg = sess.targ_cfg; + let td = mk_target_data(sess.targ_cfg.target_strs.data_layout); + let tn = mk_type_names(); + let intrinsics = declare_intrinsics(llmod); + if sess.opts.extra_debuginfo { + declare_dbg_intrinsics(llmod, intrinsics); + } + let int_type = T_int(targ_cfg); + let float_type = T_float(targ_cfg); + let task_type = T_task(targ_cfg); + let taskptr_type = T_ptr(task_type); + lib::llvm::associate_type(tn, "taskptr", taskptr_type); + let tydesc_type = T_tydesc(targ_cfg); + lib::llvm::associate_type(tn, "tydesc", tydesc_type); + let crate_map = decl_crate_map(sess, link_meta.name, llmod); + let dbg_cx = if sess.opts.debuginfo { + option::some(@{llmetadata: map::new_int_hash(), + names: new_namegen()}) + } else { + option::none + }; + + let ccx = + @{sess: sess, + llmod: llmod, + td: td, + tn: tn, + externs: new_str_hash::<ValueRef>(), + intrinsics: intrinsics, + item_ids: new_int_hash::<ValueRef>(), + exp_map: emap, + item_symbols: new_int_hash::<str>(), + mutable main_fn: none::<ValueRef>, + link_meta: link_meta, + enum_sizes: ty::new_ty_hash(), + discrims: ast_util::new_def_id_hash::<ValueRef>(), + discrim_symbols: new_int_hash::<str>(), + consts: new_int_hash::<ValueRef>(), + tydescs: ty::new_ty_hash(), + dicts: map::mk_hashmap(hash_dict_id, {|a, b| a == b}), + monomorphized: map::mk_hashmap(hash_mono_id, {|a, b| a == b}), + module_data: new_str_hash::<ValueRef>(), + lltypes: ty::new_ty_hash(), + names: new_namegen(), + sha: sha, + type_sha1s: ty::new_ty_hash(), + type_short_names: ty::new_ty_hash(), + tcx: tcx, + maps: maps, + inline_map: inline_map, + stats: + {mutable n_static_tydescs: 0u, + mutable n_derived_tydescs: 0u, + mutable n_glues_created: 0u, + mutable n_null_glues: 0u, + mutable n_real_glues: 0u, + fn_times: @mutable []}, + upcalls: + upcall::declare_upcalls(targ_cfg, tn, tydesc_type, + llmod), + tydesc_type: tydesc_type, + int_type: int_type, + float_type: float_type, + task_type: task_type, + opaque_vec_type: T_opaque_vec(targ_cfg), + builder: BuilderRef_res(llvm::LLVMCreateBuilder()), + shape_cx: mk_ctxt(llmod), + crate_map: crate_map, + dbg_cx: dbg_cx, + mutable do_not_commit_warning_issued: false}; + collect_items(ccx, crate); + collect_inlined_items(ccx, inline_map); + trans_constants(ccx, crate); + trans_mod(ccx, crate.node.module); + trans_inlined_items(ccx, inline_map); + fill_crate_map(ccx, crate_map); + emit_tydescs(ccx); + gen_shape_tables(ccx); + write_abi_version(ccx); + + // Translate the metadata. + write_metadata(ccx, crate); + if ccx.sess.opts.stats { + #error("--- trans stats ---"); + #error("n_static_tydescs: %u", ccx.stats.n_static_tydescs); + #error("n_derived_tydescs: %u", ccx.stats.n_derived_tydescs); + #error("n_glues_created: %u", ccx.stats.n_glues_created); + #error("n_null_glues: %u", ccx.stats.n_null_glues); + #error("n_real_glues: %u", ccx.stats.n_real_glues); + + for timing: {ident: str, time: int} in *ccx.stats.fn_times { + #error("time: %s took %d ms", timing.ident, timing.time); + } + } + ret (llmod, link_meta); +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/trans/build.rs b/src/rustc/middle/trans/build.rs new file mode 100644 index 00000000000..e449aae2382 --- /dev/null +++ b/src/rustc/middle/trans/build.rs @@ -0,0 +1,676 @@ +import ctypes::{c_uint, c_int}; +import str::sbuf; +import lib::llvm::llvm; +import syntax::codemap; +import codemap::span; +import lib::llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, ModuleRef}; +import lib::llvm::{Opcode, IntPredicate, RealPredicate, True, False, + CallConv}; +import common::*; + +fn B(cx: block) -> BuilderRef { + let b = *cx.fcx.ccx.builder; + llvm::LLVMPositionBuilderAtEnd(b, cx.llbb); + ret b; +} + +// The difference between a block being unreachable and being terminated is +// somewhat obscure, and has to do with error checking. When a block is +// terminated, we're saying that trying to add any further statements in the +// block is an error. On the other hand, if something is unreachable, that +// means that the block was terminated in some way that we don't want to check +// for (fail/break/ret statements, call to diverging functions, etc), and +// further instructions to the block should simply be ignored. + +fn RetVoid(cx: block) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + llvm::LLVMBuildRetVoid(B(cx)); +} + +fn Ret(cx: block, V: ValueRef) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + llvm::LLVMBuildRet(B(cx), V); +} + +fn AggregateRet(cx: block, RetVals: [ValueRef]) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + unsafe { + llvm::LLVMBuildAggregateRet(B(cx), vec::unsafe::to_ptr(RetVals), + RetVals.len() as c_uint); + } +} + +fn Br(cx: block, Dest: BasicBlockRef) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + llvm::LLVMBuildBr(B(cx), Dest); +} + +fn CondBr(cx: block, If: ValueRef, Then: BasicBlockRef, + Else: BasicBlockRef) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + llvm::LLVMBuildCondBr(B(cx), If, Then, Else); +} + +fn Switch(cx: block, V: ValueRef, Else: BasicBlockRef, NumCases: uint) + -> ValueRef { + if cx.unreachable { ret _Undef(V); } + assert !cx.terminated; + cx.terminated = true; + ret llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases as c_uint); +} + +fn AddCase(S: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef) { + if llvm::LLVMIsUndef(S) == lib::llvm::True { ret; } + llvm::LLVMAddCase(S, OnVal, Dest); +} + +fn IndirectBr(cx: block, Addr: ValueRef, NumDests: uint) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests as c_uint); +} + +// This is a really awful way to get a zero-length c-string, but better (and a +// lot more efficient) than doing str::as_buf("", ...) every time. +fn noname() -> sbuf unsafe { + const cnull: uint = 0u; + ret unsafe::reinterpret_cast(ptr::addr_of(cnull)); +} + +fn Invoke(cx: block, Fn: ValueRef, Args: [ValueRef], + Then: BasicBlockRef, Catch: BasicBlockRef) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + #debug["Invoke(%s with arguments (%s))", + val_str(cx.ccx().tn, Fn), + str::connect(vec::map(Args, {|a|val_str(cx.ccx().tn, a)}), + ", ")]; + unsafe { + llvm::LLVMBuildInvoke(B(cx), Fn, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, Then, Catch, + noname()); + } +} + +fn FastInvoke(cx: block, Fn: ValueRef, Args: [ValueRef], + Then: BasicBlockRef, Catch: BasicBlockRef) { + if cx.unreachable { ret; } + assert (!cx.terminated); + cx.terminated = true; + unsafe { + let v = llvm::LLVMBuildInvoke(B(cx), Fn, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, + Then, Catch, noname()); + lib::llvm::SetInstructionCallConv(v, lib::llvm::FastCallConv); + } +} + +fn Unreachable(cx: block) { + if cx.unreachable { ret; } + cx.unreachable = true; + if !cx.terminated { llvm::LLVMBuildUnreachable(B(cx)); } +} + +fn _Undef(val: ValueRef) -> ValueRef { + ret llvm::LLVMGetUndef(val_ty(val)); +} + +/* Arithmetic */ +fn Add(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildAdd(B(cx), LHS, RHS, noname()); +} + +fn NSWAdd(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNSWAdd(B(cx), LHS, RHS, noname()); +} + +fn NUWAdd(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNUWAdd(B(cx), LHS, RHS, noname()); +} + +fn FAdd(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildFAdd(B(cx), LHS, RHS, noname()); +} + +fn Sub(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildSub(B(cx), LHS, RHS, noname()); +} + +fn NSWSub(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNSWSub(B(cx), LHS, RHS, noname()); +} + +fn NUWSub(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNUWSub(B(cx), LHS, RHS, noname()); +} + +fn FSub(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildFSub(B(cx), LHS, RHS, noname()); +} + +fn Mul(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildMul(B(cx), LHS, RHS, noname()); +} + +fn NSWMul(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNSWMul(B(cx), LHS, RHS, noname()); +} + +fn NUWMul(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildNUWMul(B(cx), LHS, RHS, noname()); +} + +fn FMul(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildFMul(B(cx), LHS, RHS, noname()); +} + +fn UDiv(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildUDiv(B(cx), LHS, RHS, noname()); +} + +fn SDiv(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildSDiv(B(cx), LHS, RHS, noname()); +} + +fn ExactSDiv(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildExactSDiv(B(cx), LHS, RHS, noname()); +} + +fn FDiv(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildFDiv(B(cx), LHS, RHS, noname()); +} + +fn URem(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildURem(B(cx), LHS, RHS, noname()); +} + +fn SRem(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildSRem(B(cx), LHS, RHS, noname()); +} + +fn FRem(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildFRem(B(cx), LHS, RHS, noname()); +} + +fn Shl(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildShl(B(cx), LHS, RHS, noname()); +} + +fn LShr(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildLShr(B(cx), LHS, RHS, noname()); +} + +fn AShr(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildAShr(B(cx), LHS, RHS, noname()); +} + +fn And(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildAnd(B(cx), LHS, RHS, noname()); +} + +fn Or(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildOr(B(cx), LHS, RHS, noname()); +} + +fn Xor(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildXor(B(cx), LHS, RHS, noname()); +} + +fn BinOp(cx: block, Op: Opcode, LHS: ValueRef, RHS: ValueRef) -> + ValueRef { + if cx.unreachable { ret _Undef(LHS); } + ret llvm::LLVMBuildBinOp(B(cx), Op, LHS, RHS, noname()); +} + +fn Neg(cx: block, V: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(V); } + ret llvm::LLVMBuildNeg(B(cx), V, noname()); +} + +fn NSWNeg(cx: block, V: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(V); } + ret llvm::LLVMBuildNSWNeg(B(cx), V, noname()); +} + +fn NUWNeg(cx: block, V: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(V); } + ret llvm::LLVMBuildNUWNeg(B(cx), V, noname()); +} +fn FNeg(cx: block, V: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(V); } + ret llvm::LLVMBuildFNeg(B(cx), V, noname()); +} + +fn Not(cx: block, V: ValueRef) -> ValueRef { + if cx.unreachable { ret _Undef(V); } + ret llvm::LLVMBuildNot(B(cx), V, noname()); +} + +/* Memory */ +fn Malloc(cx: block, Ty: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); } + ret llvm::LLVMBuildMalloc(B(cx), Ty, noname()); +} + +fn ArrayMalloc(cx: block, Ty: TypeRef, Val: ValueRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); } + ret llvm::LLVMBuildArrayMalloc(B(cx), Ty, Val, noname()); +} + +fn Alloca(cx: block, Ty: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); } + ret llvm::LLVMBuildAlloca(B(cx), Ty, noname()); +} + +fn ArrayAlloca(cx: block, Ty: TypeRef, Val: ValueRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); } + ret llvm::LLVMBuildArrayAlloca(B(cx), Ty, Val, noname()); +} + +fn Free(cx: block, PointerVal: ValueRef) { + if cx.unreachable { ret; } + llvm::LLVMBuildFree(B(cx), PointerVal); +} + +fn Load(cx: block, PointerVal: ValueRef) -> ValueRef { + let ccx = cx.fcx.ccx; + if cx.unreachable { + let ty = val_ty(PointerVal); + let eltty = if llvm::LLVMGetTypeKind(ty) == 11 as c_int { + llvm::LLVMGetElementType(ty) } else { ccx.int_type }; + ret llvm::LLVMGetUndef(eltty); + } + ret llvm::LLVMBuildLoad(B(cx), PointerVal, noname()); +} + +fn Store(cx: block, Val: ValueRef, Ptr: ValueRef) { + if cx.unreachable { ret; } + #debug["Store %s -> %s", + val_str(cx.ccx().tn, Val), + val_str(cx.ccx().tn, Ptr)]; + llvm::LLVMBuildStore(B(cx), Val, Ptr); +} + +fn GEP(cx: block, Pointer: ValueRef, Indices: [ValueRef]) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); } + unsafe { + ret llvm::LLVMBuildGEP(B(cx), Pointer, vec::unsafe::to_ptr(Indices), + Indices.len() as c_uint, noname()); + } +} + +// Simple wrapper around GEP that takes an array of ints and wraps them +// in C_i32() +fn GEPi(cx: block, base: ValueRef, ixs: [int]) -> ValueRef { + let v: [ValueRef] = []; + for i: int in ixs { v += [C_i32(i as i32)]; } + ret InBoundsGEP(cx, base, v); +} + +fn InBoundsGEP(cx: block, Pointer: ValueRef, Indices: [ValueRef]) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); } + unsafe { + ret llvm::LLVMBuildInBoundsGEP(B(cx), Pointer, + vec::unsafe::to_ptr(Indices), + Indices.len() as c_uint, + noname()); + } +} + +fn StructGEP(cx: block, Pointer: ValueRef, Idx: uint) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); } + ret llvm::LLVMBuildStructGEP(B(cx), Pointer, Idx as c_uint, noname()); +} + +fn GlobalString(cx: block, _Str: sbuf) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); } + ret llvm::LLVMBuildGlobalString(B(cx), _Str, noname()); +} + +fn GlobalStringPtr(cx: block, _Str: sbuf) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); } + ret llvm::LLVMBuildGlobalStringPtr(B(cx), _Str, noname()); +} + +/* Casts */ +fn Trunc(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildTrunc(B(cx), Val, DestTy, noname()); +} + +fn ZExt(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildZExt(B(cx), Val, DestTy, noname()); +} + +fn SExt(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildSExt(B(cx), Val, DestTy, noname()); +} + +fn FPToUI(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildFPToUI(B(cx), Val, DestTy, noname()); +} + +fn FPToSI(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildFPToSI(B(cx), Val, DestTy, noname()); +} + +fn UIToFP(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildUIToFP(B(cx), Val, DestTy, noname()); +} + +fn SIToFP(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildSIToFP(B(cx), Val, DestTy, noname()); +} + +fn FPTrunc(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildFPTrunc(B(cx), Val, DestTy, noname()); +} + +fn FPExt(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildFPExt(B(cx), Val, DestTy, noname()); +} + +fn PtrToInt(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildPtrToInt(B(cx), Val, DestTy, noname()); +} + +fn IntToPtr(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildIntToPtr(B(cx), Val, DestTy, noname()); +} + +fn BitCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildBitCast(B(cx), Val, DestTy, noname()); +} + +fn ZExtOrBitCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildZExtOrBitCast(B(cx), Val, DestTy, noname()); +} + +fn SExtOrBitCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildSExtOrBitCast(B(cx), Val, DestTy, noname()); +} + +fn TruncOrBitCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildTruncOrBitCast(B(cx), Val, DestTy, noname()); +} + +fn Cast(cx: block, Op: Opcode, Val: ValueRef, DestTy: TypeRef, + _Name: sbuf) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildCast(B(cx), Op, Val, DestTy, noname()); +} + +fn PointerCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildPointerCast(B(cx), Val, DestTy, noname()); +} + +fn IntCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildIntCast(B(cx), Val, DestTy, noname()); +} + +fn FPCast(cx: block, Val: ValueRef, DestTy: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); } + ret llvm::LLVMBuildFPCast(B(cx), Val, DestTy, noname()); +} + + +/* Comparisons */ +fn ICmp(cx: block, Op: IntPredicate, LHS: ValueRef, RHS: ValueRef) + -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); } + ret llvm::LLVMBuildICmp(B(cx), Op as c_uint, LHS, RHS, noname()); +} + +fn FCmp(cx: block, Op: RealPredicate, LHS: ValueRef, RHS: ValueRef) + -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); } + ret llvm::LLVMBuildFCmp(B(cx), Op as c_uint, LHS, RHS, noname()); +} + +/* Miscellaneous instructions */ +fn EmptyPhi(cx: block, Ty: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(Ty); } + ret llvm::LLVMBuildPhi(B(cx), Ty, noname()); +} + +fn Phi(cx: block, Ty: TypeRef, vals: [ValueRef], bbs: [BasicBlockRef]) + -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(Ty); } + assert vals.len() == bbs.len(); + let phi = EmptyPhi(cx, Ty); + unsafe { + llvm::LLVMAddIncoming(phi, vec::unsafe::to_ptr(vals), + vec::unsafe::to_ptr(bbs), + vals.len() as c_uint); + ret phi; + } +} + +fn AddIncomingToPhi(phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { + if llvm::LLVMIsUndef(phi) == lib::llvm::True { ret; } + unsafe { + let valptr = unsafe::reinterpret_cast(ptr::addr_of(val)); + let bbptr = unsafe::reinterpret_cast(ptr::addr_of(bb)); + llvm::LLVMAddIncoming(phi, valptr, bbptr, 1 as c_uint); + } +} + +fn _UndefReturn(cx: block, Fn: ValueRef) -> ValueRef { + let ccx = cx.fcx.ccx; + let ty = val_ty(Fn); + let retty = if llvm::LLVMGetTypeKind(ty) == 8 as c_int { + llvm::LLVMGetReturnType(ty) } else { ccx.int_type }; + ret llvm::LLVMGetUndef(retty); +} + +fn add_span_comment(bcx: block, sp: span, text: str) { + let ccx = bcx.ccx(); + if (!ccx.sess.opts.no_asm_comments) { + let s = text + " (" + codemap::span_to_str(sp, ccx.sess.codemap) + + ")"; + log(debug, s); + add_comment(bcx, s); + } +} + +fn add_comment(bcx: block, text: str) { + let ccx = bcx.ccx(); + if (!ccx.sess.opts.no_asm_comments) { + let sanitized = str::replace(text, "$", ""); + let comment_text = "; " + sanitized; + let asm = str::as_buf(comment_text, {|c| + str::as_buf("", {|e| + llvm::LLVMConstInlineAsm(T_fn([], T_void()), c, e, + False, False) + }) + }); + Call(bcx, asm, []); + } +} + +fn Call(cx: block, Fn: ValueRef, Args: [ValueRef]) -> ValueRef { + if cx.unreachable { ret _UndefReturn(cx, Fn); } + unsafe { + ret llvm::LLVMBuildCall(B(cx), Fn, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, noname()); + } +} + +fn FastCall(cx: block, Fn: ValueRef, Args: [ValueRef]) -> ValueRef { + if cx.unreachable { ret _UndefReturn(cx, Fn); } + unsafe { + let v = llvm::LLVMBuildCall(B(cx), Fn, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, noname()); + lib::llvm::SetInstructionCallConv(v, lib::llvm::FastCallConv); + ret v; + } +} + +fn CallWithConv(cx: block, Fn: ValueRef, Args: [ValueRef], + Conv: CallConv) -> ValueRef { + if cx.unreachable { ret _UndefReturn(cx, Fn); } + unsafe { + let v = llvm::LLVMBuildCall(B(cx), Fn, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, noname()); + lib::llvm::SetInstructionCallConv(v, Conv); + ret v; + } +} + +fn Select(cx: block, If: ValueRef, Then: ValueRef, Else: ValueRef) -> + ValueRef { + if cx.unreachable { ret _Undef(Then); } + ret llvm::LLVMBuildSelect(B(cx), If, Then, Else, noname()); +} + +fn VAArg(cx: block, list: ValueRef, Ty: TypeRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(Ty); } + ret llvm::LLVMBuildVAArg(B(cx), list, Ty, noname()); +} + +fn ExtractElement(cx: block, VecVal: ValueRef, Index: ValueRef) -> + ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); } + ret llvm::LLVMBuildExtractElement(B(cx), VecVal, Index, noname()); +} + +fn InsertElement(cx: block, VecVal: ValueRef, EltVal: ValueRef, + Index: ValueRef) { + if cx.unreachable { ret; } + llvm::LLVMBuildInsertElement(B(cx), VecVal, EltVal, Index, noname()); +} + +fn ShuffleVector(cx: block, V1: ValueRef, V2: ValueRef, + Mask: ValueRef) { + if cx.unreachable { ret; } + llvm::LLVMBuildShuffleVector(B(cx), V1, V2, Mask, noname()); +} + +fn ExtractValue(cx: block, AggVal: ValueRef, Index: uint) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); } + ret llvm::LLVMBuildExtractValue(B(cx), AggVal, Index as c_uint, noname()); +} + +fn InsertValue(cx: block, AggVal: ValueRef, EltVal: ValueRef, + Index: uint) { + if cx.unreachable { ret; } + llvm::LLVMBuildInsertValue(B(cx), AggVal, EltVal, Index as c_uint, + noname()); +} + +fn IsNull(cx: block, Val: ValueRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); } + ret llvm::LLVMBuildIsNull(B(cx), Val, noname()); +} + +fn IsNotNull(cx: block, Val: ValueRef) -> ValueRef { + if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); } + ret llvm::LLVMBuildIsNotNull(B(cx), Val, noname()); +} + +fn PtrDiff(cx: block, LHS: ValueRef, RHS: ValueRef) -> ValueRef { + let ccx = cx.fcx.ccx; + if cx.unreachable { ret llvm::LLVMGetUndef(ccx.int_type); } + ret llvm::LLVMBuildPtrDiff(B(cx), LHS, RHS, noname()); +} + +fn Trap(cx: block) { + if cx.unreachable { ret; } + let b = B(cx); + let BB: BasicBlockRef = llvm::LLVMGetInsertBlock(b); + let FN: ValueRef = llvm::LLVMGetBasicBlockParent(BB); + let M: ModuleRef = llvm::LLVMGetGlobalParent(FN); + let T: ValueRef = str::as_buf("llvm.trap", {|buf| + llvm::LLVMGetNamedFunction(M, buf) + }); + assert (T as int != 0); + let Args: [ValueRef] = []; + unsafe { + llvm::LLVMBuildCall(b, T, vec::unsafe::to_ptr(Args), + Args.len() as c_uint, noname()); + } +} + +fn LandingPad(cx: block, Ty: TypeRef, PersFn: ValueRef, + NumClauses: uint) -> ValueRef { + assert !cx.terminated && !cx.unreachable; + ret llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn, + NumClauses as c_uint, noname()); +} + +fn SetCleanup(_cx: block, LandingPad: ValueRef) { + llvm::LLVMSetCleanup(LandingPad, lib::llvm::True); +} + +fn Resume(cx: block, Exn: ValueRef) -> ValueRef { + assert (!cx.terminated); + cx.terminated = true; + ret llvm::LLVMBuildResume(B(cx), Exn); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs new file mode 100644 index 00000000000..4cd9eef01e3 --- /dev/null +++ b/src/rustc/middle/trans/closure.rs @@ -0,0 +1,925 @@ +import core::ctypes::c_uint; +import syntax::ast; +import syntax::ast_util; +import lib::llvm::llvm; +import lib::llvm::{ValueRef, TypeRef}; +import common::*; +import build::*; +import base::*; +import type_of::*; +import type_of::type_of; // Issue #1873 +import back::abi; +import syntax::codemap::span; +import syntax::print::pprust::expr_to_str; +import back::link::{ + mangle_internal_name_by_path, + mangle_internal_name_by_path_and_seq}; +import util::ppaux::ty_to_str; +import shape::{size_of}; +import ast_map::{path, path_mod, path_name}; +import driver::session::session; + +// ___Good to know (tm)__________________________________________________ +// +// The layout of a closure environment in memory is +// roughly as follows: +// +// struct rust_opaque_box { // see rust_internal.h +// unsigned ref_count; // only used for fn@() +// type_desc *tydesc; // describes closure_data struct +// rust_opaque_box *prev; // (used internally by memory alloc) +// rust_opaque_box *next; // (used internally by memory alloc) +// struct closure_data { +// type_desc *bound_tdescs[]; // bound descriptors +// struct { +// upvar1_t upvar1; +// ... +// upvarN_t upvarN; +// } bound_data; +// } +// }; +// +// Note that the closure is itself a rust_opaque_box. This is true +// even for fn~ and fn&, because we wish to keep binary compatibility +// between all kinds of closures. The allocation strategy for this +// closure depends on the closure type. For a sendfn, the closure +// (and the referenced type descriptors) will be allocated in the +// exchange heap. For a fn, the closure is allocated in the task heap +// and is reference counted. For a block, the closure is allocated on +// the stack. +// +// ## Opaque closures and the embedded type descriptor ## +// +// One interesting part of closures is that they encapsulate the data +// that they close over. So when I have a ptr to a closure, I do not +// know how many type descriptors it contains nor what upvars are +// captured within. That means I do not know precisely how big it is +// nor where its fields are located. This is called an "opaque +// closure". +// +// Typically an opaque closure suffices because we only manipulate it +// by ptr. The routine common::T_opaque_box_ptr() returns an +// appropriate type for such an opaque closure; it allows access to +// the box fields, but not the closure_data itself. +// +// But sometimes, such as when cloning or freeing a closure, we need +// to know the full information. That is where the type descriptor +// that defines the closure comes in handy. We can use its take and +// drop glue functions to allocate/free data as needed. +// +// ## Subtleties concerning alignment ## +// +// It is important that we be able to locate the closure data *without +// knowing the kind of data that is being bound*. This can be tricky +// because the alignment requirements of the bound data affects the +// alignment requires of the closure_data struct as a whole. However, +// right now this is a non-issue in any case, because the size of the +// rust_opaque_box header is always a mutiple of 16-bytes, which is +// the maximum alignment requirement we ever have to worry about. +// +// The only reason alignment matters is that, in order to learn what data +// is bound, we would normally first load the type descriptors: but their +// location is ultimately depend on their content! There is, however, a +// workaround. We can load the tydesc from the rust_opaque_box, which +// describes the closure_data struct and has self-contained derived type +// descriptors, and read the alignment from there. It's just annoying to +// do. Hopefully should this ever become an issue we'll have monomorphized +// and type descriptors will all be a bad dream. +// +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +enum environment_value { + // Evaluate expr and store result in env (used for bind). + env_expr(@ast::expr, ty::t), + + // Copy the value from this llvm ValueRef into the environment. + env_copy(ValueRef, ty::t, lval_kind), + + // Move the value from this llvm ValueRef into the environment. + env_move(ValueRef, ty::t, lval_kind), + + // Access by reference (used for blocks). + env_ref(ValueRef, ty::t, lval_kind), +} + +fn ev_to_str(ccx: crate_ctxt, ev: environment_value) -> str { + alt ev { + env_expr(ex, _) { expr_to_str(ex) } + env_copy(v, t, lk) { #fmt("copy(%s,%s)", val_str(ccx.tn, v), + ty_to_str(ccx.tcx, t)) } + env_move(v, t, lk) { #fmt("move(%s,%s)", val_str(ccx.tn, v), + ty_to_str(ccx.tcx, t)) } + env_ref(v, t, lk) { #fmt("ref(%s,%s)", val_str(ccx.tn, v), + ty_to_str(ccx.tcx, t)) } + } +} + +fn mk_tydesc_ty(tcx: ty::ctxt, ck: ty::closure_kind) -> ty::t { + ret alt ck { + ty::ck_block | ty::ck_box { ty::mk_type(tcx) } + ty::ck_uniq { ty::mk_send_type(tcx) } + }; +} + +fn mk_tuplified_uniq_cbox_ty(tcx: ty::ctxt, cdata_ty: ty::t) -> ty::t { + let tydesc_ty = mk_tydesc_ty(tcx, ty::ck_uniq); + let cbox_ty = tuplify_cbox_ty(tcx, cdata_ty, tydesc_ty); + ret ty::mk_imm_uniq(tcx, cbox_ty); +} + +// Given a closure ty, emits a corresponding tuple ty +fn mk_closure_tys(tcx: ty::ctxt, + ck: ty::closure_kind, + ty_params: [fn_ty_param], + bound_values: [environment_value]) + -> (ty::t, [ty::t]) { + let bound_tys = []; + + let tydesc_ty = mk_tydesc_ty(tcx, ck); + + // Compute the closed over tydescs + let param_ptrs = []; + for tp in ty_params { + param_ptrs += [tydesc_ty]; + option::may(tp.dicts) {|dicts| + for dict in dicts { param_ptrs += [tydesc_ty]; } + } + } + + // Compute the closed over data + for bv in bound_values { + bound_tys += [alt bv { + env_copy(_, t, _) { t } + env_move(_, t, _) { t } + env_ref(_, t, _) { t } + env_expr(_, t) { t } + }]; + } + let bound_data_ty = ty::mk_tup(tcx, bound_tys); + + let cdata_ty = ty::mk_tup(tcx, [ty::mk_tup(tcx, param_ptrs), + bound_data_ty]); + #debug["cdata_ty=%s", ty_to_str(tcx, cdata_ty)]; + ret (cdata_ty, bound_tys); +} + +fn allocate_cbox(bcx: block, + ck: ty::closure_kind, + cdata_ty: ty::t) + -> (block, ValueRef, [ValueRef]) { + + let ccx = bcx.ccx(), tcx = ccx.tcx; + + fn nuke_ref_count(bcx: block, box: ValueRef) { + // Initialize ref count to arbitrary value for debugging: + let ccx = bcx.ccx(); + let box = PointerCast(bcx, box, T_opaque_box_ptr(ccx)); + let ref_cnt = GEPi(bcx, box, [0, abi::box_field_refcnt]); + let rc = C_int(ccx, 0x12345678); + Store(bcx, rc, ref_cnt); + } + + fn store_uniq_tydesc(bcx: block, + cdata_ty: ty::t, + box: ValueRef, + &ti: option::t<@tydesc_info>) -> block { + let ccx = bcx.ccx(); + let bound_tydesc = GEPi(bcx, box, [0, abi::box_field_tydesc]); + let {bcx, val: td} = base::get_tydesc(bcx, cdata_ty, true, ti); + let td = Call(bcx, ccx.upcalls.create_shared_type_desc, [td]); + Store(bcx, td, bound_tydesc); + bcx + } + + // Allocate and initialize the box: + let ti = none; + let temp_cleanups = []; + let (bcx, box) = alt ck { + ty::ck_box { + let {bcx, val: box} = trans_malloc_boxed_raw(bcx, cdata_ty, ti); + (bcx, box) + } + ty::ck_uniq { + let uniq_cbox_ty = mk_tuplified_uniq_cbox_ty(tcx, cdata_ty); + let {bcx, val: box} = uniq::alloc_uniq(bcx, uniq_cbox_ty); + nuke_ref_count(bcx, box); + let bcx = store_uniq_tydesc(bcx, cdata_ty, box, ti); + (bcx, box) + } + ty::ck_block { + let cbox_ty = tuplify_box_ty(tcx, cdata_ty); + let {bcx, val: box} = base::alloc_ty(bcx, cbox_ty); + nuke_ref_count(bcx, box); + (bcx, box) + } + }; + + base::lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, ti); + base::lazily_emit_tydesc_glue(ccx, abi::tydesc_field_drop_glue, ti); + base::lazily_emit_tydesc_glue(ccx, abi::tydesc_field_free_glue, ti); + + ret (bcx, box, temp_cleanups); +} + +type closure_result = { + llbox: ValueRef, // llvalue of ptr to closure + cdata_ty: ty::t, // type of the closure data + bcx: block // final bcx +}; + +fn cast_if_we_can(bcx: block, llbox: ValueRef, t: ty::t) -> ValueRef { + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, t) { + let llty = type_of(ccx, t); + ret PointerCast(bcx, llbox, llty); + } else { + ret llbox; + } +} + +// Given a block context and a list of tydescs and values to bind +// construct a closure out of them. If copying is true, it is a +// heap allocated closure that copies the upvars into environment. +// Otherwise, it is stack allocated and copies pointers to the upvars. +fn store_environment( + bcx: block, lltyparams: [fn_ty_param], + bound_values: [environment_value], + ck: ty::closure_kind) + -> closure_result { + + fn maybe_clone_tydesc(bcx: block, + ck: ty::closure_kind, + td: ValueRef) -> ValueRef { + ret alt ck { + ty::ck_block | ty::ck_box { + td + } + ty::ck_uniq { + Call(bcx, bcx.ccx().upcalls.create_shared_type_desc, [td]) + } + }; + } + + let ccx = bcx.ccx(), tcx = ccx.tcx; + + // compute the shape of the closure + let (cdata_ty, bound_tys) = + mk_closure_tys(tcx, ck, lltyparams, bound_values); + + // allocate closure in the heap + let (bcx, llbox, temp_cleanups) = + allocate_cbox(bcx, ck, cdata_ty); + + // cbox_ty has the form of a tuple: (a, b, c) we want a ptr to a + // tuple. This could be a ptr in uniq or a box or on stack, + // whatever. + let cbox_ty = tuplify_box_ty(tcx, cdata_ty); + let cboxptr_ty = ty::mk_ptr(tcx, {ty:cbox_ty, mutbl:ast::m_imm}); + let llbox = cast_if_we_can(bcx, llbox, cboxptr_ty); + #debug["tuplify_box_ty = %s", ty_to_str(tcx, cbox_ty)]; + + // If necessary, copy tydescs describing type parameters into the + // appropriate slot in the closure. + let {bcx:bcx, val:ty_params_slot} = + GEP_tup_like(bcx, cbox_ty, llbox, + [0, abi::box_field_body, abi::closure_body_ty_params]); + let off = 0; + for tp in lltyparams { + let cloned_td = maybe_clone_tydesc(bcx, ck, tp.desc); + Store(bcx, cloned_td, GEPi(bcx, ty_params_slot, [0, off])); + off += 1; + option::may(tp.dicts, {|dicts| + for dict in dicts { + let cast = PointerCast(bcx, dict, val_ty(cloned_td)); + Store(bcx, cast, GEPi(bcx, ty_params_slot, [0, off])); + off += 1; + } + }); + } + + // Copy expr values into boxed bindings. + vec::iteri(bound_values) { |i, bv| + #debug["Copy %s into closure", ev_to_str(ccx, bv)]; + + if (!ccx.sess.opts.no_asm_comments) { + add_comment(bcx, #fmt("Copy %s into closure", + ev_to_str(ccx, bv))); + } + + let bound_data = GEP_tup_like(bcx, cbox_ty, llbox, + [0, abi::box_field_body, + abi::closure_body_bindings, i as int]); + bcx = bound_data.bcx; + let bound_data = bound_data.val; + alt bv { + env_expr(e, _) { + bcx = base::trans_expr_save_in(bcx, e, bound_data); + add_clean_temp_mem(bcx, bound_data, bound_tys[i]); + temp_cleanups += [bound_data]; + } + env_copy(val, ty, owned) { + let val1 = load_if_immediate(bcx, val, ty); + bcx = base::copy_val(bcx, INIT, bound_data, val1, ty); + } + env_copy(val, ty, owned_imm) { + bcx = base::copy_val(bcx, INIT, bound_data, val, ty); + } + env_copy(_, _, temporary) { + fail "Cannot capture temporary upvar"; + } + env_move(val, ty, kind) { + let src = {bcx:bcx, val:val, kind:kind}; + bcx = move_val(bcx, INIT, bound_data, src, ty); + } + env_ref(val, ty, owned) { + Store(bcx, val, bound_data); + } + env_ref(val, ty, owned_imm) { + let addr = do_spill_noroot(bcx, val); + Store(bcx, addr, bound_data); + } + env_ref(_, _, temporary) { + fail "Cannot capture temporary upvar"; + } + } + } + for cleanup in temp_cleanups { revoke_clean(bcx, cleanup); } + + ret {llbox: llbox, cdata_ty: cdata_ty, bcx: bcx}; +} + +// Given a context and a list of upvars, build a closure. This just +// collects the upvars and packages them up for store_environment. +fn build_closure(bcx0: block, + cap_vars: [capture::capture_var], + ck: ty::closure_kind, + id: ast::node_id) -> closure_result { + // If we need to, package up the iterator body to call + let env_vals = []; + let bcx = bcx0, ccx = bcx.ccx(), tcx = ccx.tcx; + + // Package up the captured upvars + vec::iter(cap_vars) { |cap_var| + #debug["Building closure: captured variable %?", cap_var]; + let lv = trans_local_var(bcx, cap_var.def); + let nid = ast_util::def_id_of_def(cap_var.def).node; + let ty = node_id_type(bcx, nid); + alt cap_var.mode { + capture::cap_ref { + assert ck == ty::ck_block; + ty = ty::mk_mut_ptr(tcx, ty); + env_vals += [env_ref(lv.val, ty, lv.kind)]; + } + capture::cap_copy { + let mv = alt check ccx.maps.last_uses.find(id) { + none { false } + some(last_use::closes_over(vars)) { vec::contains(vars, nid) } + }; + if mv { env_vals += [env_move(lv.val, ty, lv.kind)]; } + else { env_vals += [env_copy(lv.val, ty, lv.kind)]; } + } + capture::cap_move { + env_vals += [env_move(lv.val, ty, lv.kind)]; + } + capture::cap_drop { + assert lv.kind == owned; + bcx = drop_ty(bcx, lv.val, ty); + bcx = zero_alloca(bcx, lv.val, ty); + } + } + } + ret store_environment(bcx, copy bcx.fcx.lltyparams, env_vals, ck); +} + +// Given an enclosing block context, a new function context, a closure type, +// and a list of upvars, generate code to load and populate the environment +// with the upvars and type descriptors. +fn load_environment(enclosing_cx: block, + fcx: fn_ctxt, + cdata_ty: ty::t, + cap_vars: [capture::capture_var], + ck: ty::closure_kind) { + let bcx = raw_block(fcx, fcx.llloadenv); + + // Load a pointer to the closure data, skipping over the box header: + let llcdata = base::opaque_box_body(bcx, cdata_ty, fcx.llenv); + + // Populate the type parameters from the environment. We need to + // do this first because the tydescs are needed to index into + // the bindings if they are dynamically sized. + let {bcx, val: lltydescs} = GEP_tup_like(bcx, cdata_ty, llcdata, + [0, abi::closure_body_ty_params]); + let off = 0; + for tp in copy enclosing_cx.fcx.lltyparams { + let tydesc = Load(bcx, GEPi(bcx, lltydescs, [0, off])); + off += 1; + let dicts = option::map(tp.dicts, {|dicts| + let rslt = []; + for dict in dicts { + let dict = Load(bcx, GEPi(bcx, lltydescs, [0, off])); + rslt += [PointerCast(bcx, dict, T_ptr(T_dict()))]; + off += 1; + } + rslt + }); + fcx.lltyparams += [{desc: tydesc, dicts: dicts}]; + } + + // Populate the upvars from the environment. + let i = 0u; + vec::iter(cap_vars) { |cap_var| + alt cap_var.mode { + capture::cap_drop { /* ignore */ } + _ { + let upvarptr = + GEP_tup_like(bcx, cdata_ty, llcdata, + [0, abi::closure_body_bindings, i as int]); + bcx = upvarptr.bcx; + let llupvarptr = upvarptr.val; + alt ck { + ty::ck_block { llupvarptr = Load(bcx, llupvarptr); } + ty::ck_uniq | ty::ck_box { } + } + let def_id = ast_util::def_id_of_def(cap_var.def); + fcx.llupvars.insert(def_id.node, llupvarptr); + i += 1u; + } + } + } +} + +fn trans_expr_fn(bcx: block, + proto: ast::proto, + decl: ast::fn_decl, + body: ast::blk, + sp: span, + id: ast::node_id, + cap_clause: ast::capture_clause, + dest: dest) -> block { + if dest == ignore { ret bcx; } + let ccx = bcx.ccx(), bcx = bcx; + let fty = node_id_type(bcx, id); + let llfnty = type_of_fn_from_ty(ccx, fty, []); + let sub_path = bcx.fcx.path + [path_name("anon")]; + let s = mangle_internal_name_by_path(ccx, sub_path); + let llfn = decl_internal_cdecl_fn(ccx.llmod, s, llfnty); + register_fn(ccx, sp, sub_path, "anon fn", [], id); + + let trans_closure_env = fn@(ck: ty::closure_kind) -> ValueRef { + let cap_vars = capture::compute_capture_vars( + ccx.tcx, id, proto, cap_clause); + let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id); + trans_closure(ccx, sub_path, decl, body, llfn, no_self, [], + bcx.fcx.param_substs, id, {|fcx| + load_environment(bcx, fcx, cdata_ty, cap_vars, ck); + }); + llbox + }; + + let closure = alt proto { + ast::proto_any | ast::proto_block { trans_closure_env(ty::ck_block) } + ast::proto_box { trans_closure_env(ty::ck_box) } + ast::proto_uniq { trans_closure_env(ty::ck_uniq) } + ast::proto_bare { + trans_closure(ccx, sub_path, decl, body, llfn, no_self, [], none, + id, {|_fcx|}); + C_null(T_opaque_box_ptr(ccx)) + } + }; + fill_fn_pair(bcx, get_dest_addr(dest), llfn, closure); + ret bcx; +} + +fn trans_bind(cx: block, f: @ast::expr, args: [option<@ast::expr>], + id: ast::node_id, dest: dest) -> block { + let f_res = trans_callee(cx, f); + ret trans_bind_1(cx, expr_ty(cx, f), f_res, args, + node_id_type(cx, id), dest); +} + +fn trans_bind_1(cx: block, outgoing_fty: ty::t, + f_res: lval_maybe_callee, + args: [option<@ast::expr>], pair_ty: ty::t, + dest: dest) -> block { + let ccx = cx.ccx(); + let bound: [@ast::expr] = []; + for argopt: option<@ast::expr> in args { + alt argopt { none { } some(e) { bound += [e]; } } + } + let bcx = f_res.bcx; + if dest == ignore { + for ex in bound { bcx = trans_expr(bcx, ex, ignore); } + ret bcx; + } + + // Figure out which tydescs we need to pass, if any. + let (outgoing_fty_real, lltydescs, param_bounds) = alt f_res.generic { + generic_full(ginfo) { + let tds = [], orig = 0u; + vec::iter2(ginfo.tydescs, *ginfo.param_bounds) {|td, bounds| + tds += [td]; + for bound in *bounds { + alt bound { + ty::bound_iface(_) { + let dict = impl::get_dict( + bcx, option::get(ginfo.origins)[orig]); + tds += [PointerCast(bcx, dict.val, val_ty(td))]; + orig += 1u; + bcx = dict.bcx; + } + _ {} + } + } + } + lazily_emit_all_generic_info_tydesc_glues(ccx, ginfo); + (ginfo.item_type, tds, ginfo.param_bounds) + } + _ { (outgoing_fty, [], @[]) } + }; + + if bound.len() == 0u && lltydescs.len() == 0u && + (f_res.env == null_env || f_res.env == is_closure) { + // Trivial 'binding': just return the closure + let lv = lval_maybe_callee_to_lval(f_res, pair_ty); + ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, pair_ty); + } + + // Arrange for the bound function to live in the first binding spot + // if the function is not statically known. + let (env_vals, target_info) = alt f_res.env { + null_env { ([], target_static(f_res.val)) } + is_closure { + // Cast the function we are binding to be the type that the + // closure will expect it to have. The type the closure knows + // about has the type parameters substituted with the real types. + let llclosurety = T_ptr(type_of(ccx, outgoing_fty)); + let src_loc = PointerCast(bcx, f_res.val, llclosurety); + ([env_copy(src_loc, pair_ty, owned)], target_closure) + } + self_env(slf, slf_t) { + ([env_copy(slf, slf_t, owned)], target_self(f_res.val)) + } + dict_env(_, _) { + ccx.sess.unimpl("binding of dynamic method calls"); + } + }; + + // Actually construct the closure + let {llbox, cdata_ty, bcx} = store_environment( + bcx, vec::map(lltydescs, {|d| {desc: d, dicts: none}}), + env_vals + vec::map(bound, {|x| env_expr(x, expr_ty(bcx, x))}), + ty::ck_box); + + // Make thunk + let llthunk = trans_bind_thunk( + cx.fcx.ccx, cx.fcx.path, pair_ty, outgoing_fty_real, args, + cdata_ty, *param_bounds, target_info); + + // Fill the function pair + fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox); + ret bcx; +} + +fn make_fn_glue( + cx: block, + v: ValueRef, + t: ty::t, + glue_fn: fn@(block, v: ValueRef, t: ty::t) -> block) + -> block { + let bcx = cx; + let tcx = cx.tcx(); + + let fn_env = fn@(ck: ty::closure_kind) -> block { + let box_cell_v = GEPi(cx, v, [0, abi::fn_field_box]); + let box_ptr_v = Load(cx, box_cell_v); + with_cond(cx, IsNotNull(cx, box_ptr_v)) {|bcx| + let closure_ty = ty::mk_opaque_closure_ptr(tcx, ck); + glue_fn(bcx, box_cell_v, closure_ty) + } + }; + + ret alt ty::get(t).struct { + ty::ty_fn({proto: ast::proto_bare, _}) | + ty::ty_fn({proto: ast::proto_block, _}) | + ty::ty_fn({proto: ast::proto_any, _}) { bcx } + ty::ty_fn({proto: ast::proto_uniq, _}) { fn_env(ty::ck_uniq) } + ty::ty_fn({proto: ast::proto_box, _}) { fn_env(ty::ck_box) } + _ { fail "make_fn_glue invoked on non-function type" } + }; +} + +fn make_opaque_cbox_take_glue( + bcx: block, + ck: ty::closure_kind, + cboxptr: ValueRef) // ptr to ptr to the opaque closure + -> block { + // Easy cases: + alt ck { + ty::ck_block { ret bcx; } + ty::ck_box { ret incr_refcnt_of_boxed(bcx, Load(bcx, cboxptr)); } + ty::ck_uniq { /* hard case: */ } + } + + // Hard case, a deep copy: + let ccx = bcx.ccx(), tcx = ccx.tcx; + let llopaquecboxty = T_opaque_box_ptr(ccx); + let cbox_in = Load(bcx, cboxptr); + with_cond(bcx, IsNotNull(bcx, cbox_in)) {|bcx| + // Load the size from the type descr found in the cbox + let cbox_in = PointerCast(bcx, cbox_in, llopaquecboxty); + let tydescptr = GEPi(bcx, cbox_in, [0, abi::box_field_tydesc]); + let tydesc = Load(bcx, tydescptr); + let tydesc = PointerCast(bcx, tydesc, T_ptr(ccx.tydesc_type)); + let sz = Load(bcx, GEPi(bcx, tydesc, [0, abi::tydesc_field_size])); + + // Adjust sz to account for the rust_opaque_box header fields + let sz = Add(bcx, sz, shape::llsize_of(ccx, T_box_header(ccx))); + + // Allocate memory, update original ptr, and copy existing data + let malloc = ccx.upcalls.shared_malloc; + let cbox_out = Call(bcx, malloc, [sz]); + let cbox_out = PointerCast(bcx, cbox_out, llopaquecboxty); + let {bcx, val: _} = call_memmove(bcx, cbox_out, cbox_in, sz); + Store(bcx, cbox_out, cboxptr); + + // Take the (deeply cloned) type descriptor + let tydesc_out = GEPi(bcx, cbox_out, [0, abi::box_field_tydesc]); + let bcx = take_ty(bcx, tydesc_out, mk_tydesc_ty(tcx, ty::ck_uniq)); + + // Take the data in the tuple + let ti = none; + let cdata_out = GEPi(bcx, cbox_out, [0, abi::box_field_body]); + call_tydesc_glue_full(bcx, cdata_out, tydesc, + abi::tydesc_field_take_glue, ti); + bcx + } +} + +fn make_opaque_cbox_drop_glue( + bcx: block, + ck: ty::closure_kind, + cboxptr: ValueRef) // ptr to the opaque closure + -> block { + alt ck { + ty::ck_block { bcx } + ty::ck_box { + decr_refcnt_maybe_free(bcx, Load(bcx, cboxptr), + ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) + } + ty::ck_uniq { + free_ty(bcx, Load(bcx, cboxptr), + ty::mk_opaque_closure_ptr(bcx.tcx(), ck)) + } + } +} + +fn make_opaque_cbox_free_glue( + bcx: block, + ck: ty::closure_kind, + cbox: ValueRef) // ptr to the opaque closure + -> block { + alt ck { + ty::ck_block { ret bcx; } + ty::ck_box | ty::ck_uniq { /* hard cases: */ } + } + + let ccx = bcx.ccx(), tcx = ccx.tcx; + with_cond(bcx, IsNotNull(bcx, cbox)) {|bcx| + // Load the type descr found in the cbox + let lltydescty = T_ptr(ccx.tydesc_type); + let cbox = PointerCast(bcx, cbox, T_opaque_cbox_ptr(ccx)); + let tydescptr = GEPi(bcx, cbox, [0, abi::box_field_tydesc]); + let tydesc = Load(bcx, tydescptr); + let tydesc = PointerCast(bcx, tydesc, lltydescty); + + // Drop the tuple data then free the descriptor + let ti = none; + let cdata = GEPi(bcx, cbox, [0, abi::box_field_body]); + call_tydesc_glue_full(bcx, cdata, tydesc, + abi::tydesc_field_drop_glue, ti); + + // Free the ty descr (if necc) and the box itself + alt ck { + ty::ck_block { fail "Impossible"; } + ty::ck_box { + trans_free(bcx, cbox) + } + ty::ck_uniq { + let bcx = free_ty(bcx, tydesc, mk_tydesc_ty(tcx, ck)); + trans_shared_free(bcx, cbox) + } + } + } +} + +enum target_info { + target_closure, + target_static(ValueRef), + target_self(ValueRef), +} + +// pth is cx.path +fn trans_bind_thunk(ccx: crate_ctxt, + path: path, + incoming_fty: ty::t, + outgoing_fty: ty::t, + args: [option<@ast::expr>], + cdata_ty: ty::t, + param_bounds: [ty::param_bounds], + target_info: target_info) + -> {val: ValueRef, ty: TypeRef} { + let tcx = ccx.tcx; + #debug["trans_bind_thunk[incoming_fty=%s,outgoing_fty=%s,\ + cdata_ty=%s,param_bounds=%?]", + ty_to_str(tcx, incoming_fty), + ty_to_str(tcx, outgoing_fty), + ty_to_str(tcx, cdata_ty), + param_bounds]; + + // Here we're not necessarily constructing a thunk in the sense of + // "function with no arguments". The result of compiling 'bind f(foo, + // bar, baz)' would be a thunk that, when called, applies f to those + // arguments and returns the result. But we're stretching the meaning of + // the word "thunk" here to also mean the result of compiling, say, 'bind + // f(foo, _, baz)', or any other bind expression that binds f and leaves + // some (or all) of the arguments unbound. + + // Here, 'incoming_fty' is the type of the entire bind expression, while + // 'outgoing_fty' is the type of the function that is having some of its + // arguments bound. If f is a function that takes three arguments of type + // int and returns int, and we're translating, say, 'bind f(3, _, 5)', + // then outgoing_fty is the type of f, which is (int, int, int) -> int, + // and incoming_fty is the type of 'bind f(3, _, 5)', which is int -> int. + + // Once translated, the entire bind expression will be the call f(foo, + // bar, baz) wrapped in a (so-called) thunk that takes 'bar' as its + // argument and that has bindings of 'foo' to 3 and 'baz' to 5 and a + // pointer to 'f' all saved in its environment. So, our job is to + // construct and return that thunk. + + // Give the thunk a name, type, and value. + let s = mangle_internal_name_by_path_and_seq(ccx, path, "thunk"); + let llthunk_ty = get_pair_fn_ty(type_of(ccx, incoming_fty)); + let llthunk = decl_internal_cdecl_fn(ccx.llmod, s, llthunk_ty); + + // Create a new function context and block context for the thunk, and hold + // onto a pointer to the first block in the function for later use. + let fcx = new_fn_ctxt(ccx, path, llthunk, none); + let bcx = top_scope_block(fcx, none); + let lltop = bcx.llbb; + // Since we might need to construct derived tydescs that depend on + // our bound tydescs, we need to load tydescs out of the environment + // before derived tydescs are constructed. To do this, we load them + // in the load_env block. + let l_bcx = raw_block(fcx, fcx.llloadenv); + + // The 'llenv' that will arrive in the thunk we're creating is an + // environment that will contain the values of its arguments and a + // pointer to the original function. This environment is always + // stored like an opaque box (see big comment at the header of the + // file), so we load the body body, which contains the type descr + // and cached data. + let llcdata = base::opaque_box_body(l_bcx, cdata_ty, fcx.llenv); + + // "target", in this context, means the function that's having some of its + // arguments bound and that will be called inside the thunk we're + // creating. (In our running example, target is the function f.) Pick + // out the pointer to the target function from the environment. The + // target function lives in the first binding spot. + let (lltargetfn, lltargetenv, starting_idx) = alt target_info { + target_static(fptr) { + (fptr, llvm::LLVMGetUndef(T_opaque_cbox_ptr(ccx)), 0) + } + target_closure { + let {bcx: cx, val: pair} = + GEP_tup_like(bcx, cdata_ty, llcdata, + [0, abi::closure_body_bindings, 0]); + let lltargetenv = + Load(cx, GEPi(cx, pair, [0, abi::fn_field_box])); + let lltargetfn = Load + (cx, GEPi(cx, pair, [0, abi::fn_field_code])); + bcx = cx; + (lltargetfn, lltargetenv, 1) + } + target_self(fptr) { + let rs = GEP_tup_like(bcx, cdata_ty, llcdata, + [0, abi::closure_body_bindings, 0]); + bcx = rs.bcx; + (fptr, PointerCast(bcx, rs.val, T_opaque_cbox_ptr(ccx)), 1) + } + }; + + // And then, pick out the target function's own environment. That's what + // we'll use as the environment the thunk gets. + + // Get f's return type, which will also be the return type of the entire + // bind expression. + let outgoing_ret_ty = ty::ty_fn_ret(outgoing_fty); + + // Get the types of the arguments to f. + let outgoing_args = ty::ty_fn_args(outgoing_fty); + + // The 'llretptr' that will arrive in the thunk we're creating also needs + // to be the correct type. Cast it to f's return type, if necessary. + let llretptr = fcx.llretptr; + if ty::type_has_params(outgoing_ret_ty) { + let llretty = type_of(ccx, outgoing_ret_ty); + llretptr = PointerCast(bcx, llretptr, T_ptr(llretty)); + } + + // Set up the three implicit arguments to the thunk. + let llargs: [ValueRef] = [llretptr, lltargetenv]; + + // Copy in the type parameters. + let {bcx: l_bcx, val: param_record} = + GEP_tup_like(l_bcx, cdata_ty, llcdata, + [0, abi::closure_body_ty_params]); + let off = 0; + for param in param_bounds { + let dsc = Load(l_bcx, GEPi(l_bcx, param_record, [0, off])), + dicts = none; + llargs += [dsc]; + off += 1; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let dict = Load(l_bcx, GEPi(l_bcx, param_record, [0, off])); + dict = PointerCast(l_bcx, dict, T_ptr(T_dict())); + llargs += [dict]; + off += 1; + dicts = some(alt dicts { + none { [dict] } + some(ds) { ds + [dict] } + }); + } + _ {} + } + } + fcx.lltyparams += [{desc: dsc, dicts: dicts}]; + } + + let a: uint = first_tp_arg; // retptr, env come first + let b: int = starting_idx; + let outgoing_arg_index: uint = 0u; + let llout_arg_tys: [TypeRef] = + type_of_explicit_args(ccx, outgoing_args); + for arg: option<@ast::expr> in args { + let out_arg = outgoing_args[outgoing_arg_index]; + let llout_arg_ty = llout_arg_tys[outgoing_arg_index]; + alt arg { + // Arg provided at binding time; thunk copies it from + // closure. + some(e) { + let bound_arg = + GEP_tup_like(bcx, cdata_ty, llcdata, + [0, abi::closure_body_bindings, b]); + bcx = bound_arg.bcx; + let val = bound_arg.val; + + alt ty::resolved_mode(tcx, out_arg.mode) { + ast::by_val { + val = Load(bcx, val); + } + ast::by_copy { + let {bcx: cx, val: alloc} = alloc_ty(bcx, out_arg.ty); + bcx = memmove_ty(cx, alloc, val, out_arg.ty); + bcx = take_ty(bcx, alloc, out_arg.ty); + val = alloc; + } + ast::by_ref | ast::by_mutbl_ref | ast::by_move { } + } + + // If the type is parameterized, then we need to cast the + // type we actually have to the parameterized out type. + if ty::type_has_params(out_arg.ty) { + val = PointerCast(bcx, val, llout_arg_ty); + } + llargs += [val]; + b += 1; + } + + // Arg will be provided when the thunk is invoked. + none { + let arg: ValueRef = llvm::LLVMGetParam(llthunk, a as c_uint); + if ty::type_has_params(out_arg.ty) { + arg = PointerCast(bcx, arg, llout_arg_ty); + } + llargs += [arg]; + a += 1u; + } + } + outgoing_arg_index += 1u; + } + + // Cast the outgoing function to the appropriate type. + // This is necessary because the type of the function that we have + // in the closure does not know how many type descriptors the function + // needs to take. + let lltargetty = + type_of_fn_from_ty(ccx, outgoing_fty, param_bounds); + lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty)); + Call(bcx, lltargetfn, llargs); + build_return(bcx); + finish_fn(fcx, lltop); + ret {val: llthunk, ty: llthunk_ty}; +} diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs new file mode 100644 index 00000000000..4bc264e4098 --- /dev/null +++ b/src/rustc/middle/trans/common.rs @@ -0,0 +1,933 @@ +/** + Code that is useful in various trans modules. + +*/ + +import ctypes::unsigned; +import vec::unsafe::to_ptr; +import std::map::hashmap; +import syntax::ast; +import driver::session; +import session::session; +import middle::{resolve, ty}; +import back::{link, abi, upcall}; +import util::common::*; +import syntax::codemap::span; +import lib::llvm::{llvm, target_data, type_names, associate_type, + name_has_type}; +import lib::llvm::{ModuleRef, ValueRef, TypeRef, BasicBlockRef, BuilderRef}; +import lib::llvm::{True, False, Bool}; +import metadata::csearch; +import ast_map::path; +import middle::inline::inline_map; + +type namegen = fn@(str) -> str; +fn new_namegen() -> namegen { + let i = @mutable 0; + ret fn@(prefix: str) -> str { *i += 1; prefix + int::str(*i) }; +} + +type derived_tydesc_info = {lltydesc: ValueRef, escapes: bool}; + +type tydesc_info = + {ty: ty::t, + tydesc: ValueRef, + size: ValueRef, + align: ValueRef, + mutable take_glue: option<ValueRef>, + mutable drop_glue: option<ValueRef>, + mutable free_glue: option<ValueRef>, + ty_params: [uint]}; + +/* + * A note on nomenclature of linking: "upcall", "extern" and "native". + * + * An "extern" is an LLVM symbol we wind up emitting an undefined external + * reference to. This means "we don't have the thing in this compilation unit, + * please make sure you link it in at runtime". This could be a reference to + * C code found in a C library, or rust code found in a rust crate. + * + * A "native" is an extern that references C code. Called with cdecl. + * + * An upcall is a native call generated by the compiler (not corresponding to + * any user-written call in the code) into librustrt, to perform some helper + * task such as bringing a task to life, allocating memory, etc. + * + */ +type stats = + {mutable n_static_tydescs: uint, + mutable n_derived_tydescs: uint, + mutable n_glues_created: uint, + mutable n_null_glues: uint, + mutable n_real_glues: uint, + fn_times: @mutable [{ident: str, time: int}]}; + +resource BuilderRef_res(B: BuilderRef) { llvm::LLVMDisposeBuilder(B); } + +// Misc. auxiliary maps used in the crate_ctxt +type maps = { + mutbl_map: middle::mutbl::mutbl_map, + copy_map: middle::alias::copy_map, + last_uses: middle::last_use::last_uses, + impl_map: middle::resolve::impl_map, + method_map: middle::typeck::method_map, + dict_map: middle::typeck::dict_map +}; + +// Crate context. Every crate we compile has one of these. +type crate_ctxt = @{ + sess: session::session, + llmod: ModuleRef, + td: target_data, + tn: type_names, + externs: hashmap<str, ValueRef>, + intrinsics: hashmap<str, ValueRef>, + item_ids: hashmap<ast::node_id, ValueRef>, + exp_map: resolve::exp_map, + item_symbols: hashmap<ast::node_id, str>, + mutable main_fn: option<ValueRef>, + link_meta: link::link_meta, + enum_sizes: hashmap<ty::t, uint>, + discrims: hashmap<ast::def_id, ValueRef>, + discrim_symbols: hashmap<ast::node_id, str>, + consts: hashmap<ast::node_id, ValueRef>, + tydescs: hashmap<ty::t, @tydesc_info>, + dicts: hashmap<dict_id, ValueRef>, + monomorphized: hashmap<mono_id, {llfn: ValueRef, fty: ty::t}>, + module_data: hashmap<str, ValueRef>, + lltypes: hashmap<ty::t, TypeRef>, + names: namegen, + sha: std::sha1::sha1, + type_sha1s: hashmap<ty::t, str>, + type_short_names: hashmap<ty::t, str>, + tcx: ty::ctxt, + maps: maps, + inline_map: inline_map, + stats: stats, + upcalls: @upcall::upcalls, + tydesc_type: TypeRef, + int_type: TypeRef, + float_type: TypeRef, + task_type: TypeRef, + opaque_vec_type: TypeRef, + builder: BuilderRef_res, + shape_cx: shape::ctxt, + crate_map: ValueRef, + dbg_cx: option<@debuginfo::debug_ctxt>, + mutable do_not_commit_warning_issued: bool}; + +// Types used for llself. +type val_self_pair = {v: ValueRef, t: ty::t}; + +enum local_val { local_mem(ValueRef), local_imm(ValueRef), } + +type fn_ty_param = {desc: ValueRef, dicts: option<[ValueRef]>}; + +type param_substs = {tys: [ty::t], + dicts: option<typeck::dict_res>, + bounds: @[ty::param_bounds]}; + +// Function context. Every LLVM function we create will have one of +// these. +type fn_ctxt = @{ + // The ValueRef returned from a call to llvm::LLVMAddFunction; the + // address of the first instruction in the sequence of + // instructions for this function that will go in the .text + // section of the executable we're generating. + llfn: ValueRef, + + // The two implicit arguments that arrive in the function we're creating. + // For instance, foo(int, int) is really foo(ret*, env*, int, int). + llenv: ValueRef, + llretptr: ValueRef, + + // These elements: "hoisted basic blocks" containing + // administrative activities that have to happen in only one place in + // the function, due to LLVM's quirks. + // A block for all the function's static allocas, so that LLVM + // will coalesce them into a single alloca call. + mutable llstaticallocas: BasicBlockRef, + // A block containing code that copies incoming arguments to space + // already allocated by code in one of the llallocas blocks. + // (LLVM requires that arguments be copied to local allocas before + // allowing most any operation to be performed on them.) + mutable llloadenv: BasicBlockRef, + // The first and last block containing derived tydescs received from the + // runtime. See description of derived_tydescs, below. + mutable llderivedtydescs_first: BasicBlockRef, + mutable llderivedtydescs: BasicBlockRef, + // A block for all of the dynamically sized allocas. This must be + // after llderivedtydescs, because these sometimes depend on + // information computed from derived tydescs. + mutable lldynamicallocas: BasicBlockRef, + mutable llreturn: BasicBlockRef, + // The token used to clear the dynamic allocas at the end of this frame. + mutable llobstacktoken: option<ValueRef>, + // The 'self' value currently in use in this function, if there + // is one. + mutable llself: option<val_self_pair>, + // The a value alloca'd for calls to upcalls.rust_personality. Used when + // outputting the resume instruction. + mutable personality: option<ValueRef>, + + // Maps arguments to allocas created for them in llallocas. + llargs: hashmap<ast::node_id, local_val>, + // Maps the def_ids for local variables to the allocas created for + // them in llallocas. + lllocals: hashmap<ast::node_id, local_val>, + // Same as above, but for closure upvars + llupvars: hashmap<ast::node_id, ValueRef>, + + // A vector of incoming type descriptors and their associated iface dicts. + mutable lltyparams: [fn_ty_param], + + // Derived tydescs are tydescs created at runtime, for types that + // involve type parameters inside type constructors. For example, + // suppose a function parameterized by T creates a vector of type + // [T]. The function doesn't know what T is until runtime, and + // the function's caller knows T but doesn't know that a vector is + // involved. So a tydesc for [T] can't be created until runtime, + // when information about both "[T]" and "T" are available. When + // such a tydesc is created, we cache it in the derived_tydescs + // table for the next time that such a tydesc is needed. + derived_tydescs: hashmap<ty::t, derived_tydesc_info>, + + // The node_id of the function, or -1 if it doesn't correspond to + // a user-defined function. + id: ast::node_id, + + // If this function is being monomorphized, this contains the type + // substitutions used. + param_substs: option<param_substs>, + + // The source span and nesting context where this function comes from, for + // error reporting and symbol generation. + span: option<span>, + path: path, + + // This function's enclosing crate context. + ccx: crate_ctxt +}; + +fn warn_not_to_commit(ccx: crate_ctxt, msg: str) { + if !ccx.do_not_commit_warning_issued { + ccx.do_not_commit_warning_issued = true; + ccx.sess.warn(msg + " -- do not commit like this!"); + } +} + +enum cleanup { + clean(fn@(block) -> block), + clean_temp(ValueRef, fn@(block) -> block), +} + +// Used to remember and reuse existing cleanup paths +// target: none means the path ends in an resume instruction +type cleanup_path = {target: option<BasicBlockRef>, + dest: BasicBlockRef}; + +fn scope_clean_changed(info: scope_info) { + if info.cleanup_paths.len() > 0u { info.cleanup_paths = []; } + info.landing_pad = none; +} + +fn add_clean(cx: block, val: ValueRef, ty: ty::t) { + if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + in_scope_cx(cx) {|info| + info.cleanups += [clean(bind base::drop_ty(_, val, ty))]; + scope_clean_changed(info); + } +} +fn add_clean_temp(cx: block, val: ValueRef, ty: ty::t) { + if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + fn do_drop(bcx: block, val: ValueRef, ty: ty::t) -> + block { + if ty::type_is_immediate(ty) { + ret base::drop_ty_immediate(bcx, val, ty); + } else { + ret base::drop_ty(bcx, val, ty); + } + } + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(val, bind do_drop(_, val, ty))]; + scope_clean_changed(info); + } +} +fn add_clean_temp_mem(cx: block, val: ValueRef, ty: ty::t) { + if !ty::type_needs_drop(cx.tcx(), ty) { ret; } + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(val, bind base::drop_ty(_, val, ty))]; + scope_clean_changed(info); + } +} +fn add_clean_free(cx: block, ptr: ValueRef, shared: bool) { + let free_fn = if shared { bind base::trans_shared_free(_, ptr) } + else { bind base::trans_free(_, ptr) }; + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(ptr, free_fn)]; + scope_clean_changed(info); + } +} + +// Note that this only works for temporaries. We should, at some point, move +// to a system where we can also cancel the cleanup on local variables, but +// this will be more involved. For now, we simply zero out the local, and the +// drop glue checks whether it is zero. +fn revoke_clean(cx: block, val: ValueRef) { + in_scope_cx(cx) {|info| + let i = 0u; + for cu in info.cleanups { + alt cu { + clean_temp(v, _) if v == val { + info.cleanups = + vec::slice(info.cleanups, 0u, i) + + vec::slice(info.cleanups, i + 1u, info.cleanups.len()); + scope_clean_changed(info); + ret; + } + _ {} + } + i += 1u; + } + } +} + +fn get_res_dtor(ccx: crate_ctxt, did: ast::def_id, inner_t: ty::t) + -> ValueRef { + if did.crate == ast::local_crate { + alt ccx.item_ids.find(did.node) { + some(x) { ret x; } + _ { ccx.sess.bug("get_res_dtor: can't find resource dtor!"); } + } + } + + let param_bounds = ty::lookup_item_type(ccx.tcx, did).bounds; + let nil_res = ty::mk_nil(ccx.tcx); + let fn_mode = ast::expl(ast::by_ref); + let f_t = type_of::type_of_fn(ccx, [{mode: fn_mode, ty: inner_t}], + nil_res, *param_bounds); + ret base::get_extern_const(ccx.externs, ccx.llmod, + csearch::get_symbol(ccx.sess.cstore, + did), f_t); +} + +enum block_kind { + // A scope at the end of which temporary values created inside of it are + // cleaned up. May correspond to an actual block in the language, but also + // to an implicit scope, for example, calls introduce an implicit scope in + // which the arguments are evaluated and cleaned up. + block_scope(scope_info), + // A non-scope block is a basic block created as a translation artifact + // from translating code that expresses conditional logic rather than by + // explicit { ... } block structure in the source language. It's called a + // non-scope block because it doesn't introduce a new variable scope. + block_non_scope, +} + +enum loop_cont { cont_self, cont_other(block), } + +type scope_info = { + is_loop: option<{cnt: loop_cont, brk: block}>, + // A list of functions that must be run at when leaving this + // block, cleaning up any variables that were introduced in the + // block. + mutable cleanups: [cleanup], + // Existing cleanup paths that may be reused, indexed by destination and + // cleared when the set of cleanups changes. + mutable cleanup_paths: [cleanup_path], + // Unwinding landing pad. Also cleared when cleanups change. + mutable landing_pad: option<BasicBlockRef>, +}; + +// Basic block context. We create a block context for each basic block +// (single-entry, single-exit sequence of instructions) we generate from Rust +// code. Each basic block we generate is attached to a function, typically +// with many basic blocks per function. All the basic blocks attached to a +// function are organized as a directed graph. +type block = @{ + // The BasicBlockRef returned from a call to + // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic + // block to the function pointed to by llfn. We insert + // instructions into that block by way of this block context. + // The block pointing to this one in the function's digraph. + llbb: BasicBlockRef, + mutable terminated: bool, + mutable unreachable: bool, + parent: block_parent, + // The 'kind' of basic block this is. + kind: block_kind, + // The source span where the block came from, if it is a block that + // actually appears in the source code. + mutable block_span: option<span>, + // The function context for the function to which this block is + // attached. + fcx: fn_ctxt +}; + +// First two args are retptr, env +const first_tp_arg: uint = 2u; + +// FIXME move blocks to a class once those are finished, and simply use +// option<block> for this. +enum block_parent { parent_none, parent_some(block), } + +type result = {bcx: block, val: ValueRef}; +type result_t = {bcx: block, val: ValueRef, ty: ty::t}; + +fn rslt(bcx: block, val: ValueRef) -> result { + {bcx: bcx, val: val} +} + +fn ty_str(tn: type_names, t: TypeRef) -> str { + ret lib::llvm::type_to_str(tn, t); +} + +fn val_ty(&&v: ValueRef) -> TypeRef { ret llvm::LLVMTypeOf(v); } + +fn val_str(tn: type_names, v: ValueRef) -> str { ret ty_str(tn, val_ty(v)); } + +// Returns the nth element of the given LLVM structure type. +fn struct_elt(llstructty: TypeRef, n: uint) -> TypeRef unsafe { + let elt_count = llvm::LLVMCountStructElementTypes(llstructty) as uint; + assert (n < elt_count); + let elt_tys = vec::init_elt(elt_count, T_nil()); + llvm::LLVMGetStructElementTypes(llstructty, to_ptr(elt_tys)); + ret llvm::LLVMGetElementType(elt_tys[n]); +} + +fn in_scope_cx(cx: block, f: fn(scope_info)) { + let cur = cx; + while true { + alt cur.kind { + block_scope(info) { f(info); ret; } + _ {} + } + cur = block_parent(cur); + } +} + +fn block_parent(cx: block) -> block { + alt check cx.parent { parent_some(b) { b } } +} + +// Accessors + +impl bxc_cxs for block { + fn ccx() -> crate_ctxt { self.fcx.ccx } + fn tcx() -> ty::ctxt { self.fcx.ccx.tcx } + fn sess() -> session { self.fcx.ccx.sess } +} + +// LLVM type constructors. +fn T_void() -> TypeRef { + // Note: For the time being llvm is kinda busted here, it has the notion + // of a 'void' type that can only occur as part of the signature of a + // function, but no general unit type of 0-sized value. This is, afaict, + // vestigial from its C heritage, and we'll be attempting to submit a + // patch upstream to fix it. In the mean time we only model function + // outputs (Rust functions and C functions) using T_void, and model the + // Rust general purpose nil type you can construct as 1-bit (always + // zero). This makes the result incorrect for now -- things like a tuple + // of 10 nil values will have 10-bit size -- but it doesn't seem like we + // have any other options until it's fixed upstream. + + ret llvm::LLVMVoidType(); +} + +fn T_nil() -> TypeRef { + // NB: See above in T_void(). + + ret llvm::LLVMInt1Type(); +} + +fn T_metadata() -> TypeRef { ret llvm::LLVMMetadataType(); } + +fn T_i1() -> TypeRef { ret llvm::LLVMInt1Type(); } + +fn T_i8() -> TypeRef { ret llvm::LLVMInt8Type(); } + +fn T_i16() -> TypeRef { ret llvm::LLVMInt16Type(); } + +fn T_i32() -> TypeRef { ret llvm::LLVMInt32Type(); } + +fn T_i64() -> TypeRef { ret llvm::LLVMInt64Type(); } + +fn T_f32() -> TypeRef { ret llvm::LLVMFloatType(); } + +fn T_f64() -> TypeRef { ret llvm::LLVMDoubleType(); } + +fn T_bool() -> TypeRef { ret T_i1(); } + +fn T_int(targ_cfg: @session::config) -> TypeRef { + ret alt targ_cfg.arch { + session::arch_x86 { T_i32() } + session::arch_x86_64 { T_i64() } + session::arch_arm { T_i32() } + }; +} + +fn T_int_ty(cx: crate_ctxt, t: ast::int_ty) -> TypeRef { + alt t { + ast::ty_i { cx.int_type } + ast::ty_char { T_char() } + ast::ty_i8 { T_i8() } + ast::ty_i16 { T_i16() } + ast::ty_i32 { T_i32() } + ast::ty_i64 { T_i64() } + } +} + +fn T_uint_ty(cx: crate_ctxt, t: ast::uint_ty) -> TypeRef { + alt t { + ast::ty_u { cx.int_type } + ast::ty_u8 { T_i8() } + ast::ty_u16 { T_i16() } + ast::ty_u32 { T_i32() } + ast::ty_u64 { T_i64() } + } +} + +fn T_float_ty(cx: crate_ctxt, t: ast::float_ty) -> TypeRef { + alt t { + ast::ty_f { cx.float_type } + ast::ty_f32 { T_f32() } + ast::ty_f64 { T_f64() } + } +} + +fn T_float(targ_cfg: @session::config) -> TypeRef { + ret alt targ_cfg.arch { + session::arch_x86 { T_f64() } + session::arch_x86_64 { T_f64() } + session::arch_arm { T_f64() } + }; +} + +fn T_char() -> TypeRef { ret T_i32(); } + +fn T_size_t(targ_cfg: @session::config) -> TypeRef { + ret T_int(targ_cfg); +} + +fn T_fn(inputs: [TypeRef], output: TypeRef) -> TypeRef unsafe { + ret llvm::LLVMFunctionType(output, to_ptr(inputs), + inputs.len() as unsigned, + False); +} + +fn T_fn_pair(cx: crate_ctxt, tfn: TypeRef) -> TypeRef { + ret T_struct([T_ptr(tfn), T_opaque_cbox_ptr(cx)]); +} + +fn T_ptr(t: TypeRef) -> TypeRef { + ret llvm::LLVMPointerType(t, 0u as unsigned); +} + +fn T_struct(elts: [TypeRef]) -> TypeRef unsafe { + ret llvm::LLVMStructType(to_ptr(elts), elts.len() as unsigned, False); +} + +fn T_named_struct(name: str) -> TypeRef { + let c = llvm::LLVMGetGlobalContext(); + ret str::as_buf(name, {|buf| llvm::LLVMStructCreateNamed(c, buf) }); +} + +fn set_struct_body(t: TypeRef, elts: [TypeRef]) unsafe { + llvm::LLVMStructSetBody(t, to_ptr(elts), + elts.len() as unsigned, False); +} + +fn T_empty_struct() -> TypeRef { ret T_struct([]); } + +// A dict is, in reality, a vtable pointer followed by zero or more pointers +// to tydescs and other dicts that it closes over. But the types and number of +// those are rarely known to the code that needs to manipulate them, so they +// are described by this opaque type. +fn T_dict() -> TypeRef { T_array(T_ptr(T_i8()), 1u) } + +fn T_task(targ_cfg: @session::config) -> TypeRef { + let t = T_named_struct("task"); + + // Refcount + // Delegate pointer + // Stack segment pointer + // Runtime SP + // Rust SP + // GC chain + + + // Domain pointer + // Crate cache pointer + + let t_int = T_int(targ_cfg); + let elems = + [t_int, t_int, t_int, t_int, + t_int, t_int, t_int, t_int]; + set_struct_body(t, elems); + ret t; +} + +fn T_tydesc_field(cx: crate_ctxt, field: int) -> TypeRef unsafe { + // Bit of a kludge: pick the fn typeref out of the tydesc.. + + let tydesc_elts: [TypeRef] = + vec::init_elt::<TypeRef>(abi::n_tydesc_fields as uint, + T_nil()); + llvm::LLVMGetStructElementTypes(cx.tydesc_type, + to_ptr::<TypeRef>(tydesc_elts)); + let t = llvm::LLVMGetElementType(tydesc_elts[field]); + ret t; +} + +fn T_glue_fn(cx: crate_ctxt) -> TypeRef { + let s = "glue_fn"; + alt name_has_type(cx.tn, s) { some(t) { ret t; } _ {} } + let t = T_tydesc_field(cx, abi::tydesc_field_drop_glue); + associate_type(cx.tn, s, t); + ret t; +} + +fn T_tydesc(targ_cfg: @session::config) -> TypeRef { + let tydesc = T_named_struct("tydesc"); + let tydescpp = T_ptr(T_ptr(tydesc)); + let pvoid = T_ptr(T_i8()); + let glue_fn_ty = + T_ptr(T_fn([T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, + pvoid], T_void())); + + let int_type = T_int(targ_cfg); + let elems = + [tydescpp, int_type, int_type, + glue_fn_ty, glue_fn_ty, glue_fn_ty, + T_ptr(T_i8()), glue_fn_ty, glue_fn_ty, glue_fn_ty, T_ptr(T_i8()), + T_ptr(T_i8()), T_ptr(T_i8()), int_type, int_type]; + set_struct_body(tydesc, elems); + ret tydesc; +} + +fn T_array(t: TypeRef, n: uint) -> TypeRef { + ret llvm::LLVMArrayType(t, n as unsigned); +} + +// Interior vector. +// +// FIXME: Support user-defined vector sizes. +fn T_vec2(targ_cfg: @session::config, t: TypeRef) -> TypeRef { + ret T_struct([T_int(targ_cfg), // fill + T_int(targ_cfg), // alloc + T_array(t, 0u)]); // elements +} + +fn T_vec(ccx: crate_ctxt, t: TypeRef) -> TypeRef { + ret T_vec2(ccx.sess.targ_cfg, t); +} + +// Note that the size of this one is in bytes. +fn T_opaque_vec(targ_cfg: @session::config) -> TypeRef { + ret T_vec2(targ_cfg, T_i8()); +} + +// Let T be the content of a box @T. tuplify_box_ty(t) returns the +// representation of @T as a tuple (i.e., the ty::t version of what T_box() +// returns). +fn tuplify_box_ty(tcx: ty::ctxt, t: ty::t) -> ty::t { + ret tuplify_cbox_ty(tcx, t, ty::mk_type(tcx)); +} + +// As tuplify_box_ty(), but allows the caller to specify what type of type +// descr is embedded in the box (ty::type vs ty::send_type). This is useful +// for unique closure boxes, hence the name "cbox_ty" (closure box type). +fn tuplify_cbox_ty(tcx: ty::ctxt, t: ty::t, tydesc_t: ty::t) -> ty::t { + let ptr = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx), mutbl: ast::m_imm}); + ret ty::mk_tup(tcx, [ty::mk_uint(tcx), tydesc_t, + ptr, ptr, + t]); +} + +fn T_box_header_fields(cx: crate_ctxt) -> [TypeRef] { + let ptr = T_ptr(T_i8()); + ret [cx.int_type, T_ptr(cx.tydesc_type), ptr, ptr]; +} + +fn T_box_header(cx: crate_ctxt) -> TypeRef { + ret T_struct(T_box_header_fields(cx)); +} + +fn T_box(cx: crate_ctxt, t: TypeRef) -> TypeRef { + ret T_struct(T_box_header_fields(cx) + [t]); +} + +fn T_opaque_box(cx: crate_ctxt) -> TypeRef { + ret T_box(cx, T_i8()); +} + +fn T_opaque_box_ptr(cx: crate_ctxt) -> TypeRef { + ret T_ptr(T_opaque_box(cx)); +} + +fn T_port(cx: crate_ctxt, _t: TypeRef) -> TypeRef { + ret T_struct([cx.int_type]); // Refcount + +} + +fn T_chan(cx: crate_ctxt, _t: TypeRef) -> TypeRef { + ret T_struct([cx.int_type]); // Refcount + +} + +fn T_taskptr(cx: crate_ctxt) -> TypeRef { ret T_ptr(cx.task_type); } + + +// This type must never be used directly; it must always be cast away. +fn T_typaram(tn: type_names) -> TypeRef { + let s = "typaram"; + alt name_has_type(tn, s) { some(t) { ret t; } _ {} } + let t = T_i8(); + associate_type(tn, s, t); + ret t; +} + +fn T_typaram_ptr(tn: type_names) -> TypeRef { ret T_ptr(T_typaram(tn)); } + +fn T_opaque_cbox_ptr(cx: crate_ctxt) -> TypeRef { + // closures look like boxes (even when they are fn~ or fn&) + // see trans_closure.rs + ret T_opaque_box_ptr(cx); +} + +fn T_enum_variant(cx: crate_ctxt) -> TypeRef { + ret cx.int_type; +} + +fn T_enum(cx: crate_ctxt, size: uint) -> TypeRef { + let s = "enum_" + uint::to_str(size, 10u); + alt name_has_type(cx.tn, s) { some(t) { ret t; } _ {} } + let t = + if size == 0u { + T_struct([T_enum_variant(cx)]) + } else { T_struct([T_enum_variant(cx), T_array(T_i8(), size)]) }; + associate_type(cx.tn, s, t); + ret t; +} + +fn T_opaque_enum(cx: crate_ctxt) -> TypeRef { + let s = "opaque_enum"; + alt name_has_type(cx.tn, s) { some(t) { ret t; } _ {} } + let t = T_struct([T_enum_variant(cx), T_i8()]); + associate_type(cx.tn, s, t); + ret t; +} + +fn T_opaque_enum_ptr(cx: crate_ctxt) -> TypeRef { + ret T_ptr(T_opaque_enum(cx)); +} + +fn T_captured_tydescs(cx: crate_ctxt, n: uint) -> TypeRef { + ret T_struct(vec::init_elt::<TypeRef>(n, T_ptr(cx.tydesc_type))); +} + +fn T_opaque_iface(cx: crate_ctxt) -> TypeRef { + T_struct([T_ptr(cx.tydesc_type), T_opaque_box_ptr(cx)]) +} + +fn T_opaque_port_ptr() -> TypeRef { ret T_ptr(T_i8()); } + +fn T_opaque_chan_ptr() -> TypeRef { ret T_ptr(T_i8()); } + + +// LLVM constant constructors. +fn C_null(t: TypeRef) -> ValueRef { ret llvm::LLVMConstNull(t); } + +fn C_integral(t: TypeRef, u: u64, sign_extend: Bool) -> ValueRef { + let u_hi = (u >> 32u64) as unsigned; + let u_lo = u as unsigned; + ret llvm::LLVMRustConstInt(t, u_hi, u_lo, sign_extend); +} + +fn C_floating(s: str, t: TypeRef) -> ValueRef { + ret str::as_buf(s, {|buf| llvm::LLVMConstRealOfString(t, buf) }); +} + +fn C_nil() -> ValueRef { + // NB: See comment above in T_void(). + + ret C_integral(T_i1(), 0u64, False); +} + +fn C_bool(b: bool) -> ValueRef { + if b { + ret C_integral(T_bool(), 1u64, False); + } else { ret C_integral(T_bool(), 0u64, False); } +} + +fn C_i32(i: i32) -> ValueRef { + ret C_integral(T_i32(), i as u64, True); +} + +fn C_i64(i: i64) -> ValueRef { + ret C_integral(T_i64(), i as u64, True); +} + +fn C_int(cx: crate_ctxt, i: int) -> ValueRef { + ret C_integral(cx.int_type, i as u64, True); +} + +fn C_uint(cx: crate_ctxt, i: uint) -> ValueRef { + ret C_integral(cx.int_type, i as u64, False); +} + +fn C_u8(i: uint) -> ValueRef { ret C_integral(T_i8(), i as u64, False); } + + +// This is a 'c-like' raw string, which differs from +// our boxed-and-length-annotated strings. +fn C_cstr(cx: crate_ctxt, s: str) -> ValueRef { + let sc = str::as_buf(s) {|buf| + llvm::LLVMConstString(buf, str::len(s) as unsigned, False) + }; + let g = + str::as_buf(cx.names("str"), + {|buf| llvm::LLVMAddGlobal(cx.llmod, val_ty(sc), buf) }); + llvm::LLVMSetInitializer(g, sc); + llvm::LLVMSetGlobalConstant(g, True); + lib::llvm::SetLinkage(g, lib::llvm::InternalLinkage); + ret g; +} + +// Returns a Plain Old LLVM String: +fn C_postr(s: str) -> ValueRef { + ret str::as_buf(s) {|buf| + llvm::LLVMConstString(buf, str::len(s) as unsigned, False) + }; +} + +fn C_zero_byte_arr(size: uint) -> ValueRef unsafe { + let i = 0u; + let elts: [ValueRef] = []; + while i < size { elts += [C_u8(0u)]; i += 1u; } + ret llvm::LLVMConstArray(T_i8(), vec::unsafe::to_ptr(elts), + elts.len() as unsigned); +} + +fn C_struct(elts: [ValueRef]) -> ValueRef unsafe { + ret llvm::LLVMConstStruct(vec::unsafe::to_ptr(elts), + elts.len() as unsigned, False); +} + +fn C_named_struct(T: TypeRef, elts: [ValueRef]) -> ValueRef unsafe { + ret llvm::LLVMConstNamedStruct(T, vec::unsafe::to_ptr(elts), + elts.len() as unsigned); +} + +fn C_array(ty: TypeRef, elts: [ValueRef]) -> ValueRef unsafe { + ret llvm::LLVMConstArray(ty, vec::unsafe::to_ptr(elts), + elts.len() as unsigned); +} + +fn C_bytes(bytes: [u8]) -> ValueRef unsafe { + ret llvm::LLVMConstString( + unsafe::reinterpret_cast(vec::unsafe::to_ptr(bytes)), + bytes.len() as unsigned, False); +} + +fn C_shape(ccx: crate_ctxt, bytes: [u8]) -> ValueRef { + let llshape = C_bytes(bytes); + let llglobal = str::as_buf(ccx.names("shape"), {|buf| + llvm::LLVMAddGlobal(ccx.llmod, val_ty(llshape), buf) + }); + llvm::LLVMSetInitializer(llglobal, llshape); + llvm::LLVMSetGlobalConstant(llglobal, True); + lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage); + ret llvm::LLVMConstPointerCast(llglobal, T_ptr(T_i8())); +} + +pure fn type_has_static_size(cx: crate_ctxt, t: ty::t) -> bool { + !ty::type_has_dynamic_size(cx.tcx, t) +} + +// Used to identify cached dictionaries +enum dict_param { + dict_param_dict(dict_id), + dict_param_ty(ty::t), +} +type dict_id = @{def: ast::def_id, params: [dict_param]}; +fn hash_dict_id(&&dp: dict_id) -> uint { + let h = syntax::ast_util::hash_def_id(dp.def); + for param in dp.params { + h = h << 2u; + alt param { + dict_param_dict(d) { h += hash_dict_id(d); } + dict_param_ty(t) { h += ty::type_id(t); } + } + } + h +} + +// Used to identify cached monomorphized functions +type mono_id = @{def: ast::def_id, substs: [ty::t], dicts: [dict_id]}; +fn hash_mono_id(&&mi: mono_id) -> uint { + let h = syntax::ast_util::hash_def_id(mi.def); + for ty in mi.substs { h = (h << 2u) + ty::type_id(ty); } + for dict in mi.dicts { h = (h << 2u) + hash_dict_id(dict); } + h +} + +fn umax(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { + let cond = build::ICmp(cx, lib::llvm::IntULT, a, b); + ret build::Select(cx, cond, b, a); +} + +fn umin(cx: block, a: ValueRef, b: ValueRef) -> ValueRef { + let cond = build::ICmp(cx, lib::llvm::IntULT, a, b); + ret build::Select(cx, cond, a, b); +} + +fn align_to(cx: block, off: ValueRef, align: ValueRef) -> ValueRef { + let mask = build::Sub(cx, align, C_int(cx.ccx(), 1)); + let bumped = build::Add(cx, off, mask); + ret build::And(cx, bumped, build::Not(cx, mask)); +} + +fn path_str(p: path) -> str { + let r = "", first = true; + for e in p { + alt e { ast_map::path_name(s) | ast_map::path_mod(s) { + if first { first = false; } + else { r += "::"; } + r += s; + } } + } + r +} + +fn node_id_type(bcx: block, id: ast::node_id) -> ty::t { + let tcx = bcx.tcx(); + let t = ty::node_id_to_type(tcx, id); + alt bcx.fcx.param_substs { + some(substs) { ty::substitute_type_params(tcx, substs.tys, t) } + _ { t } + } +} +fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t { + node_id_type(bcx, ex.id) +} +fn node_id_type_params(bcx: block, id: ast::node_id) -> [ty::t] { + let tcx = bcx.tcx(); + let params = ty::node_id_to_type_params(tcx, id); + alt bcx.fcx.param_substs { + some(substs) { + vec::map(params) {|t| ty::substitute_type_params(tcx, substs.tys, t) } + } + _ { params } + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/trans/debuginfo.rs b/src/rustc/middle/trans/debuginfo.rs new file mode 100644 index 00000000000..47a3d2e5716 --- /dev/null +++ b/src/rustc/middle/trans/debuginfo.rs @@ -0,0 +1,876 @@ +import std::fs; +import std::map::hashmap; +import lib::llvm::llvm; +import lib::llvm::ValueRef; +import trans::common::*; +import trans::base; +import trans::build::B; +import middle::ty; +import syntax::{ast, codemap, ast_util}; +import codemap::span; +import ast::ty; +import pat_util::*; +import util::ppaux::ty_to_str; +import driver::session::session; + +export create_local_var; +export create_function; +export create_arg; +export update_source_pos; +export debug_ctxt; + +const LLVMDebugVersion: int = (9 << 16); + +const DW_LANG_RUST: int = 0x9000; +const DW_VIRTUALITY_none: int = 0; + +const CompileUnitTag: int = 17; +const FileDescriptorTag: int = 41; +const SubprogramTag: int = 46; +const SubroutineTag: int = 21; +const BasicTypeDescriptorTag: int = 36; +const AutoVariableTag: int = 256; +const ArgVariableTag: int = 257; +const ReturnVariableTag: int = 258; +const LexicalBlockTag: int = 11; +const PointerTypeTag: int = 15; +const StructureTypeTag: int = 19; +const MemberTag: int = 13; +const ArrayTypeTag: int = 1; +const SubrangeTag: int = 33; + +const DW_ATE_boolean: int = 0x02; +const DW_ATE_float: int = 0x04; +const DW_ATE_signed: int = 0x05; +const DW_ATE_signed_char: int = 0x06; +const DW_ATE_unsigned: int = 0x07; +const DW_ATE_unsigned_char: int = 0x08; + +fn llstr(s: str) -> ValueRef { + str::as_buf(s, {|sbuf| + llvm::LLVMMDString(sbuf, str::len(s) as ctypes::c_uint) + }) +} +fn lltag(lltag: int) -> ValueRef { + lli32(LLVMDebugVersion | lltag) +} +fn lli32(val: int) -> ValueRef { + C_i32(val as i32) +} +fn lli64(val: int) -> ValueRef { + C_i64(val as i64) +} +fn lli1(bval: bool) -> ValueRef { + C_bool(bval) +} +fn llmdnode(elems: [ValueRef]) -> ValueRef unsafe { + llvm::LLVMMDNode(vec::unsafe::to_ptr(elems), + vec::len(elems) as ctypes::c_uint) +} +fn llunused() -> ValueRef { + lli32(0x0) +} +fn llnull() -> ValueRef unsafe { + unsafe::reinterpret_cast(ptr::null::<ValueRef>()) +} + +fn add_named_metadata(cx: crate_ctxt, name: str, val: ValueRef) { + str::as_buf(name, {|sbuf| + llvm::LLVMAddNamedMetadataOperand(cx.llmod, sbuf, + val) + }) +} + +//////////////// + +type debug_ctxt = { + llmetadata: metadata_cache, + names: namegen +}; + +fn update_cache(cache: metadata_cache, mdtag: int, val: debug_metadata) { + let existing = if cache.contains_key(mdtag) { + cache.get(mdtag) + } else { + [] + }; + cache.insert(mdtag, existing + [val]); +} + +type metadata<T> = {node: ValueRef, data: T}; + +type file_md = {path: str}; +type compile_unit_md = {path: str}; +type subprogram_md = {id: ast::node_id}; +type local_var_md = {id: ast::node_id}; +type tydesc_md = {hash: uint}; +type block_md = {start: codemap::loc, end: codemap::loc}; +type argument_md = {id: ast::node_id}; +type retval_md = {id: ast::node_id}; + +type metadata_cache = hashmap<int, [debug_metadata]>; + +enum debug_metadata { + file_metadata(@metadata<file_md>), + compile_unit_metadata(@metadata<compile_unit_md>), + subprogram_metadata(@metadata<subprogram_md>), + local_var_metadata(@metadata<local_var_md>), + tydesc_metadata(@metadata<tydesc_md>), + block_metadata(@metadata<block_md>), + argument_metadata(@metadata<argument_md>), + retval_metadata(@metadata<retval_md>), +} + +fn cast_safely<T: copy, U>(val: T) -> U unsafe { + let val2 = val; + let val3 = unsafe::reinterpret_cast(val2); + unsafe::leak(val2); + ret val3; +} + +fn md_from_metadata<T>(val: debug_metadata) -> T unsafe { + alt val { + file_metadata(md) { cast_safely(md) } + compile_unit_metadata(md) { cast_safely(md) } + subprogram_metadata(md) { cast_safely(md) } + local_var_metadata(md) { cast_safely(md) } + tydesc_metadata(md) { cast_safely(md) } + block_metadata(md) { cast_safely(md) } + argument_metadata(md) { cast_safely(md) } + retval_metadata(md) { cast_safely(md) } + } +} + +fn cached_metadata<T: copy>(cache: metadata_cache, mdtag: int, + eq: fn(md: T) -> bool) -> option<T> unsafe { + if cache.contains_key(mdtag) { + let items = cache.get(mdtag); + for item in items { + let md: T = md_from_metadata::<T>(item); + if eq(md) { + ret option::some(md); + } + } + } + ret option::none; +} + +fn create_compile_unit(cx: crate_ctxt, full_path: str) + -> @metadata<compile_unit_md> unsafe { + let cache = get_cache(cx); + let tg = CompileUnitTag; + alt cached_metadata::<@metadata<compile_unit_md>>(cache, tg, + {|md| md.data.path == full_path}) { + option::some(md) { ret md; } + option::none {} + } + + let work_dir = cx.sess.working_dir; + let file_path = if str::starts_with(full_path, work_dir) { + str::slice(full_path, str::len(work_dir), str::len(full_path)) + } else { + full_path + }; + let unit_metadata = [lltag(tg), + llunused(), + lli32(DW_LANG_RUST), + llstr(file_path), + llstr(work_dir), + llstr(#env["CFG_VERSION"]), + lli1(false), // main compile unit + lli1(cx.sess.opts.optimize != 0u), + llstr(""), // flags (???) + lli32(0) // runtime version (???) + // list of enum types + // list of retained values + // list of subprograms + // list of global variables + ]; + let unit_node = llmdnode(unit_metadata); + add_named_metadata(cx, "llvm.dbg.cu", unit_node); + let mdval = @{node: unit_node, data: {path: full_path}}; + update_cache(cache, tg, compile_unit_metadata(mdval)); + ret mdval; +} + +fn get_cache(cx: crate_ctxt) -> metadata_cache { + option::get(cx.dbg_cx).llmetadata +} + +fn create_file(cx: crate_ctxt, full_path: str) -> @metadata<file_md> { + let cache = get_cache(cx);; + let tg = FileDescriptorTag; + alt cached_metadata::<@metadata<file_md>>( + cache, tg, {|md| md.data.path == full_path}) { + option::some(md) { ret md; } + option::none {} + } + + let fname = fs::basename(full_path); + let path = fs::dirname(full_path); + let unit_node = create_compile_unit(cx, full_path).node; + let file_md = [lltag(tg), + llstr(fname), + llstr(path), + unit_node]; + let val = llmdnode(file_md); + let mdval = @{node: val, data: {path: full_path}}; + update_cache(cache, tg, file_metadata(mdval)); + ret mdval; +} + +fn line_from_span(cm: codemap::codemap, sp: span) -> uint { + codemap::lookup_char_pos(cm, sp.lo).line +} + +fn create_block(cx: block) -> @metadata<block_md> { + let cache = get_cache(cx.ccx()); + let cx = cx; + while option::is_none(cx.block_span) { + alt cx.parent { + parent_some(b) { cx = b; } + parent_none { fail; } + } + } + let sp = option::get(cx.block_span); + + let start = codemap::lookup_char_pos(cx.sess().codemap, + sp.lo); + let fname = start.file.name; + let end = codemap::lookup_char_pos(cx.sess().codemap, + sp.hi); + let tg = LexicalBlockTag; + /*alt cached_metadata::<@metadata<block_md>>( + cache, tg, + {|md| start == md.data.start && end == md.data.end}) { + option::some(md) { ret md; } + option::none {} + }*/ + + let parent = alt cx.parent { + parent_none { create_function(cx.fcx).node } + parent_some(bcx) { create_block(bcx).node } + }; + let file_node = create_file(cx.ccx(), fname); + let unique_id = alt cache.find(LexicalBlockTag) { + option::some(v) { vec::len(v) as int } + option::none { 0 } + }; + let lldata = [lltag(tg), + parent, + lli32(start.line as int), + lli32(start.col as int), + file_node.node, + lli32(unique_id) + ]; + let val = llmdnode(lldata); + let mdval = @{node: val, data: {start: start, end: end}}; + //update_cache(cache, tg, block_metadata(mdval)); + ret mdval; +} + +fn size_and_align_of<T>() -> (int, int) { + (sys::size_of::<T>() as int, sys::align_of::<T>() as int) +} + +fn create_basic_type(cx: crate_ctxt, t: ty::t, ty: ast::prim_ty, span: span) + -> @metadata<tydesc_md> { + let cache = get_cache(cx); + let tg = BasicTypeDescriptorTag; + alt cached_metadata::<@metadata<tydesc_md>>( + cache, tg, {|md| ty::type_id(t) == md.data.hash}) { + option::some(md) { ret md; } + option::none {} + } + + let (name, (size, align), encoding) = alt check ty { + ast::ty_bool {("bool", size_and_align_of::<bool>(), DW_ATE_boolean)} + ast::ty_int(m) { alt m { + ast::ty_char {("char", size_and_align_of::<char>(), DW_ATE_unsigned)} + ast::ty_i {("int", size_and_align_of::<int>(), DW_ATE_signed)} + ast::ty_i8 {("i8", size_and_align_of::<i8>(), DW_ATE_signed_char)} + ast::ty_i16 {("i16", size_and_align_of::<i16>(), DW_ATE_signed)} + ast::ty_i32 {("i32", size_and_align_of::<i32>(), DW_ATE_signed)} + ast::ty_i64 {("i64", size_and_align_of::<i64>(), DW_ATE_signed)} + }} + ast::ty_uint(m) { alt m { + ast::ty_u {("uint", size_and_align_of::<uint>(), DW_ATE_unsigned)} + ast::ty_u8 {("u8", size_and_align_of::<u8>(), DW_ATE_unsigned_char)} + ast::ty_u16 {("u16", size_and_align_of::<u16>(), DW_ATE_unsigned)} + ast::ty_u32 {("u32", size_and_align_of::<u32>(), DW_ATE_unsigned)} + ast::ty_u64 {("u64", size_and_align_of::<u64>(), DW_ATE_unsigned)} + }} + ast::ty_float(m) { alt m { + ast::ty_f {("float", size_and_align_of::<float>(), DW_ATE_float)} + ast::ty_f32 {("f32", size_and_align_of::<f32>(), DW_ATE_float)} + ast::ty_f64 {("f64", size_and_align_of::<f64>(), DW_ATE_float)} + }} + }; + + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + let cu_node = create_compile_unit(cx, fname); + let lldata = [lltag(tg), + cu_node.node, + llstr(name), + file_node.node, + lli32(0), //XXX source line + lli64(size * 8), // size in bits + lli64(align * 8), // alignment in bits + lli64(0), //XXX offset? + lli32(0), //XXX flags? + lli32(encoding)]; + let llnode = llmdnode(lldata); + let mdval = @{node: llnode, data: {hash: ty::type_id(t)}}; + update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +fn create_pointer_type(cx: crate_ctxt, t: ty::t, span: span, + pointee: @metadata<tydesc_md>) + -> @metadata<tydesc_md> { + let tg = PointerTypeTag; + /*let cache = cx.llmetadata; + alt cached_metadata::<@metadata<tydesc_md>>( + cache, tg, {|md| ty::hash_ty(t) == ty::hash_ty(md.data.hash)}) { + option::some(md) { ret md; } + option::none {} + }*/ + let (size, align) = size_and_align_of::<ctypes::intptr_t>(); + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + //let cu_node = create_compile_unit(cx, fname); + let llnode = create_derived_type(tg, file_node.node, "", 0, size * 8, + align * 8, 0, pointee.node); + let mdval = @{node: llnode, data: {hash: ty::type_id(t)}}; + //update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +type struct_ctxt = { + file: ValueRef, + name: str, + line: int, + mutable members: [ValueRef], + mutable total_size: int, + align: int +}; + +fn finish_structure(cx: @struct_ctxt) -> ValueRef { + ret create_composite_type(StructureTypeTag, cx.name, cx.file, cx.line, + cx.total_size, cx.align, 0, option::none, + option::some(cx.members)); +} + +fn create_structure(file: @metadata<file_md>, name: str, line: int) + -> @struct_ctxt { + let cx = @{file: file.node, + name: name, + line: line, + mutable members: [], + mutable total_size: 0, + align: 64 //XXX different alignment per arch? + }; + ret cx; +} + +fn create_derived_type(type_tag: int, file: ValueRef, name: str, line: int, + size: int, align: int, offset: int, ty: ValueRef) + -> ValueRef { + let lldata = [lltag(type_tag), + file, + llstr(name), + file, + lli32(line), + lli64(size), + lli64(align), + lli64(offset), + lli32(0), + ty]; + ret llmdnode(lldata); +} + +fn add_member(cx: @struct_ctxt, name: str, line: int, size: int, align: int, + ty: ValueRef) { + cx.members += [create_derived_type(MemberTag, cx.file, name, line, + size * 8, align * 8, cx.total_size, + ty)]; + cx.total_size += size * 8; +} + +fn create_record(cx: crate_ctxt, t: ty::t, fields: [ast::ty_field], + span: span) -> @metadata<tydesc_md> { + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + let scx = create_structure(file_node, + option::get(cx.dbg_cx).names("rec"), + line_from_span(cx.sess.codemap, + span) as int); + for field in fields { + let field_t = ty::get_field(t, field.node.ident).mt.ty; + let ty_md = create_ty(cx, field_t, field.node.mt.ty); + let (size, align) = member_size_and_align(cx.tcx, field.node.mt.ty); + add_member(scx, field.node.ident, + line_from_span(cx.sess.codemap, field.span) as int, + size as int, align as int, ty_md.node); + } + let mdval = @{node: finish_structure(scx), data:{hash: ty::type_id(t)}}; + ret mdval; +} + +fn create_boxed_type(cx: crate_ctxt, outer: ty::t, _inner: ty::t, + span: span, boxed: @metadata<tydesc_md>) + -> @metadata<tydesc_md> { + //let tg = StructureTypeTag; + /*let cache = cx.llmetadata; + alt cached_metadata::<@metadata<tydesc_md>>( + cache, tg, {|md| ty::hash_ty(outer) == ty::hash_ty(md.data.hash)}) { + option::some(md) { ret md; } + option::none {} + }*/ + let fname = filename_from_span(cx, span); + let file_node = create_file(cx, fname); + //let cu_node = create_compile_unit_metadata(cx, fname); + let uint_t = ty::mk_uint(cx.tcx); + let refcount_type = create_basic_type(cx, uint_t, + ast::ty_uint(ast::ty_u), span); + let scx = create_structure(file_node, ty_to_str(cx.tcx, outer), 0); + add_member(scx, "refcnt", 0, sys::size_of::<uint>() as int, + sys::align_of::<uint>() as int, refcount_type.node); + add_member(scx, "boxed", 0, 8, //XXX member_size_and_align(??) + 8, //XXX just a guess + boxed.node); + let llnode = finish_structure(scx); + let mdval = @{node: llnode, data: {hash: ty::type_id(outer)}}; + //update_cache(cache, tg, tydesc_metadata(mdval)); + add_named_metadata(cx, "llvm.dbg.ty", llnode); + ret mdval; +} + +fn create_composite_type(type_tag: int, name: str, file: ValueRef, line: int, + size: int, align: int, offset: int, + derived: option<ValueRef>, + members: option<[ValueRef]>) + -> ValueRef { + let lldata = [lltag(type_tag), + file, + llstr(name), // type name + file, // source file definition + lli32(line), // source line definition + lli64(size), // size of members + lli64(align), // align + lli64(offset), // offset + lli32(0), // flags + if option::is_none(derived) { + llnull() + } else { // derived from + option::get(derived) + }, + if option::is_none(members) { + llnull() + } else { //members + llmdnode(option::get(members)) + }, + lli32(0), // runtime language + llnull() + ]; + ret llmdnode(lldata); +} + +fn create_vec(cx: crate_ctxt, vec_t: ty::t, elem_t: ty::t, + vec_ty_span: codemap::span, elem_ty: @ast::ty) + -> @metadata<tydesc_md> { + let fname = filename_from_span(cx, vec_ty_span); + let file_node = create_file(cx, fname); + let elem_ty_md = create_ty(cx, elem_t, elem_ty); + let scx = create_structure(file_node, ty_to_str(cx.tcx, vec_t), 0); + let size_t_type = create_basic_type(cx, ty::mk_uint(cx.tcx), + ast::ty_uint(ast::ty_u), vec_ty_span); + add_member(scx, "fill", 0, sys::size_of::<ctypes::size_t>() as int, + sys::align_of::<ctypes::size_t>() as int, size_t_type.node); + add_member(scx, "alloc", 0, sys::size_of::<ctypes::size_t>() as int, + sys::align_of::<ctypes::size_t>() as int, size_t_type.node); + let subrange = llmdnode([lltag(SubrangeTag), lli64(0), lli64(0)]); + let (arr_size, arr_align) = member_size_and_align(cx.tcx, elem_ty); + let data_ptr = create_composite_type(ArrayTypeTag, "", file_node.node, 0, + arr_size, arr_align, 0, + option::some(elem_ty_md.node), + option::some([subrange])); + add_member(scx, "data", 0, 0, // clang says the size should be 0 + sys::align_of::<u8>() as int, data_ptr); + let llnode = finish_structure(scx); + ret @{node: llnode, data: {hash: ty::type_id(vec_t)}}; +} + +fn member_size_and_align(tcx: ty::ctxt, ty: @ast::ty) -> (int, int) { + alt ty.node { + ast::ty_path(_, id) { + alt check tcx.def_map.get(id) { + ast::def_prim_ty(nty) { + alt check nty { + ast::ty_bool { size_and_align_of::<bool>() } + ast::ty_int(m) { alt m { + ast::ty_char { size_and_align_of::<char>() } + ast::ty_i { size_and_align_of::<int>() } + ast::ty_i8 { size_and_align_of::<i8>() } + ast::ty_i16 { size_and_align_of::<i16>() } + ast::ty_i32 { size_and_align_of::<i32>() } + ast::ty_i64 { size_and_align_of::<i64>() } + }} + ast::ty_uint(m) { alt m { + ast::ty_u { size_and_align_of::<uint>() } + ast::ty_u8 { size_and_align_of::<i8>() } + ast::ty_u16 { size_and_align_of::<u16>() } + ast::ty_u32 { size_and_align_of::<u32>() } + ast::ty_u64 { size_and_align_of::<u64>() } + }} + ast::ty_float(m) { alt m { + ast::ty_f { size_and_align_of::<float>() } + ast::ty_f32 { size_and_align_of::<f32>() } + ast::ty_f64 { size_and_align_of::<f64>() } + }} + } + } + } + } + ast::ty_box(_) | ast::ty_uniq(_) { + size_and_align_of::<ctypes::uintptr_t>() + } + ast::ty_rec(fields) { + let total_size = 0; + for field in fields { + let (size, _) = member_size_and_align(tcx, field.node.mt.ty); + total_size += size; + } + (total_size, 64) //XXX different align for other arches? + } + ast::ty_vec(_) { + size_and_align_of::<ctypes::uintptr_t>() + } + _ { fail "member_size_and_align: can't handle this type"; } + } +} + +fn create_ty(_cx: crate_ctxt, _t: ty::t, _ty: @ast::ty) + -> @metadata<tydesc_md> { + /*let cache = get_cache(cx); + alt cached_metadata::<@metadata<tydesc_md>>( + cache, tg, {|md| t == md.data.hash}) { + option::some(md) { ret md; } + option::none {} + }*/ + + /* FIXME I am disabling this code as part of the patch that moves + * recognition of named builtin types into resolve. I tried to fix + * it, but it seems to already be broken -- it's only called when + * --xg is given, and compiling with --xg fails on trivial programs. + * + * Generating an ast::ty from a ty::t seems like it should not be + * needed. It is only done to track spans, but you will not get the + * right spans anyway -- types tend to refer to stuff defined + * elsewhere, not be self-contained. + */ + + fail; + /* + fn t_to_ty(cx: crate_ctxt, t: ty::t, span: span) -> @ast::ty { + let ty = alt ty::get(t).struct { + ty::ty_nil { ast::ty_nil } + ty::ty_bot { ast::ty_bot } + ty::ty_bool { ast::ty_bool } + ty::ty_int(t) { ast::ty_int(t) } + ty::ty_float(t) { ast::ty_float(t) } + ty::ty_uint(t) { ast::ty_uint(t) } + ty::ty_box(mt) { ast::ty_box({ty: t_to_ty(cx, mt.ty, span), + mutbl: mt.mutbl}) } + ty::ty_uniq(mt) { ast::ty_uniq({ty: t_to_ty(cx, mt.ty, span), + mutbl: mt.mutbl}) } + ty::ty_rec(fields) { + let fs = []; + for field in fields { + fs += [{node: {ident: field.ident, + mt: {ty: t_to_ty(cx, field.mt.ty, span), + mutbl: field.mt.mutbl}}, + span: span}]; + } + ast::ty_rec(fs) + } + ty::ty_vec(mt) { ast::ty_vec({ty: t_to_ty(cx, mt.ty, span), + mutbl: mt.mutbl}) } + _ { + cx.sess.span_bug(span, "t_to_ty: Can't handle this type"); + } + }; + ret @{node: ty, span: span}; + } + + alt ty.node { + ast::ty_box(mt) { + let inner_t = alt ty::get(t).struct { + ty::ty_box(boxed) { boxed.ty } + _ { cx.sess.span_bug(ty.span, "t_to_ty was incoherent"); } + }; + let md = create_ty(cx, inner_t, mt.ty); + let box = create_boxed_type(cx, t, inner_t, ty.span, md); + ret create_pointer_type(cx, t, ty.span, box); + } + + ast::ty_uniq(mt) { + let inner_t = alt ty::get(t).struct { + ty::ty_uniq(boxed) { boxed.ty } + // Hoping we'll have a way to eliminate this check soon. + _ { cx.sess.span_bug(ty.span, "t_to_ty was incoherent"); } + }; + let md = create_ty(cx, inner_t, mt.ty); + ret create_pointer_type(cx, t, ty.span, md); + } + + ast::ty_infer { + let inferred = t_to_ty(cx, t, ty.span); + ret create_ty(cx, t, inferred); + } + + ast::ty_rec(fields) { + ret create_record(cx, t, fields, ty.span); + } + + ast::ty_vec(mt) { + let inner_t = ty::sequence_element_type(cx.tcx, t); + let inner_ast_t = t_to_ty(cx, inner_t, mt.ty.span); + let v = create_vec(cx, t, inner_t, ty.span, inner_ast_t); + ret create_pointer_type(cx, t, ty.span, v); + } + + ast::ty_path(_, id) { + alt cx.tcx.def_map.get(id) { + ast::def_prim_ty(pty) { + ret create_basic_type(cx, t, pty, ty.span); + } + _ {} + } + } + + _ {} + }; + */ +} + +fn filename_from_span(cx: crate_ctxt, sp: codemap::span) -> str { + codemap::lookup_char_pos(cx.sess.codemap, sp.lo).file.name +} + +fn create_var(type_tag: int, context: ValueRef, name: str, file: ValueRef, + line: int, ret_ty: ValueRef) -> ValueRef { + let lldata = [lltag(type_tag), + context, + llstr(name), + file, + lli32(line), + ret_ty, + lli32(0) + ]; + ret llmdnode(lldata); +} + +fn create_local_var(bcx: block, local: @ast::local) + -> @metadata<local_var_md> unsafe { + let cx = bcx.ccx(); + let cache = get_cache(cx); + let tg = AutoVariableTag; + alt cached_metadata::<@metadata<local_var_md>>( + cache, tg, {|md| md.data.id == local.node.id}) { + option::some(md) { ret md; } + option::none {} + } + + let name = alt local.node.pat.node { + ast::pat_ident(pth, _) { pat_util::path_to_ident(pth) } + // FIXME this should be handled + _ { fail "no single variable name for local"; } + }; + let loc = codemap::lookup_char_pos(cx.sess.codemap, + local.span.lo); + let ty = node_id_type(bcx, local.node.id); + let tymd = create_ty(cx, ty, local.node.ty); + let filemd = create_file(cx, loc.file.name); + let context = alt bcx.parent { + parent_none { create_function(bcx.fcx).node } + parent_some(_) { create_block(bcx).node } + }; + let mdnode = create_var(tg, context, name, filemd.node, + loc.line as int, tymd.node); + let mdval = @{node: mdnode, data: {id: local.node.id}}; + update_cache(cache, AutoVariableTag, local_var_metadata(mdval)); + + let llptr = alt bcx.fcx.lllocals.find(local.node.id) { + option::some(local_mem(v)) { v } + option::some(_) { + bcx.tcx().sess.span_bug(local.span, "local is bound to \ + something weird"); + } + option::none { + alt bcx.fcx.lllocals.get(local.node.pat.id) { + local_imm(v) { v } + _ { bcx.tcx().sess.span_bug(local.span, "local is bound to \ + something weird"); } + } + } + }; + let declargs = [llmdnode([llptr]), mdnode]; + trans::build::Call(bcx, cx.intrinsics.get("llvm.dbg.declare"), + declargs); + ret mdval; +} + +fn create_arg(bcx: block, arg: ast::arg, sp: span) + -> @metadata<argument_md> unsafe { + let fcx = bcx.fcx, cx = fcx.ccx; + let cache = get_cache(cx); + let tg = ArgVariableTag; + alt cached_metadata::<@metadata<argument_md>>( + cache, ArgVariableTag, {|md| md.data.id == arg.id}) { + option::some(md) { ret md; } + option::none {} + } + + let loc = codemap::lookup_char_pos(cx.sess.codemap, + sp.lo); + let ty = node_id_type(bcx, arg.id); + let tymd = create_ty(cx, ty, arg.ty); + let filemd = create_file(cx, loc.file.name); + let context = create_function(bcx.fcx); + let mdnode = create_var(tg, context.node, arg.ident, filemd.node, + loc.line as int, tymd.node); + let mdval = @{node: mdnode, data: {id: arg.id}}; + update_cache(cache, tg, argument_metadata(mdval)); + + let llptr = alt fcx.llargs.get(arg.id) { + local_mem(v) | local_imm(v) { v } + }; + let declargs = [llmdnode([llptr]), mdnode]; + trans::build::Call(bcx, cx.intrinsics.get("llvm.dbg.declare"), + declargs); + ret mdval; +} + +fn update_source_pos(cx: block, s: span) { + if !cx.sess().opts.debuginfo { + ret; + } + let cm = cx.sess().codemap; + let blockmd = create_block(cx); + let loc = codemap::lookup_char_pos(cm, s.lo); + let scopedata = [lli32(loc.line as int), + lli32(loc.col as int), + blockmd.node, + llnull()]; + let dbgscope = llmdnode(scopedata); + llvm::LLVMSetCurrentDebugLocation(trans::build::B(cx), dbgscope); +} + +fn create_function(fcx: fn_ctxt) -> @metadata<subprogram_md> { + let cx = fcx.ccx; + let dbg_cx = option::get(cx.dbg_cx); + + #debug("~~"); + log(debug, fcx.id); + + let sp = option::get(fcx.span); + log(debug, codemap::span_to_str(sp, cx.sess.codemap)); + + let (ident, ret_ty, id) = alt cx.tcx.items.get(fcx.id) { + ast_map::node_item(item, _) { + alt item.node { + ast::item_fn(decl, _, _) | ast::item_res(decl, _, _, _, _) { + (item.ident, decl.output, item.id) + } + _ { fcx.ccx.sess.span_bug(item.span, "create_function: item \ + bound to non-function"); } + } + } + ast_map::node_method(method, _, _) { + (method.ident, method.decl.output, method.id) + } + ast_map::node_res_ctor(item) { + alt item.node { + ast::item_res(decl, _, _, _, ctor_id) { + (item.ident, decl.output, ctor_id) + } + _ { fcx.ccx.sess.span_bug(item.span, "create_function: \ + expected an item_res here"); } + } + } + ast_map::node_expr(expr) { + alt expr.node { + ast::expr_fn(_, decl, _, _) { + (dbg_cx.names("fn"), decl.output, expr.id) + } + ast::expr_fn_block(decl, _) { + (dbg_cx.names("fn"), decl.output, expr.id) + } + _ { fcx.ccx.sess.span_bug(expr.span, "create_function: \ + expected an expr_fn or fn_block here"); } + } + } + _ { fcx.ccx.sess.bug("create_function: unexpected \ + sort of node"); } + }; + + log(debug, ident); + log(debug, id); + + let cache = get_cache(cx); + alt cached_metadata::<@metadata<subprogram_md>>( + cache, SubprogramTag, {|md| md.data.id == id}) { + option::some(md) { ret md; } + option::none {} + } + + let path = path_str(fcx.path); + + let loc = codemap::lookup_char_pos(cx.sess.codemap, + sp.lo); + let file_node = create_file(cx, loc.file.name).node; + let key = if cx.item_symbols.contains_key(fcx.id) { fcx.id } else { id }; + let mangled = cx.item_symbols.get(key); + let ty_node = if cx.sess.opts.extra_debuginfo { + alt ret_ty.node { + ast::ty_nil { llnull() } + _ { create_ty(cx, ty::node_id_to_type(cx.tcx, id), ret_ty).node } + } + } else { + llnull() + }; + let sub_node = create_composite_type(SubroutineTag, "", file_node, 0, 0, + 0, 0, option::none, + option::some([ty_node])); + + let fn_metadata = [lltag(SubprogramTag), + llunused(), + file_node, + llstr(ident), + llstr(path), //XXX fully-qualified C++ name + llstr(mangled), //XXX MIPS name????? + file_node, + lli32(loc.line as int), + sub_node, + lli1(false), //XXX static (check export) + lli1(true), // not extern + lli32(DW_VIRTUALITY_none), // virtual-ness + lli32(0i), //index into virt func + llnull(), // base type with vtbl + lli1(false), // artificial + lli1(cx.sess.opts.optimize != 0u), + fcx.llfn + //list of template params + //func decl descriptor + //list of func vars + ]; + let val = llmdnode(fn_metadata); + add_named_metadata(cx, "llvm.dbg.sp", val); + let mdval = @{node: val, data: {id: id}}; + update_cache(cache, SubprogramTag, subprogram_metadata(mdval)); + ret mdval; +} diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs new file mode 100644 index 00000000000..b3660e3f672 --- /dev/null +++ b/src/rustc/middle/trans/impl.rs @@ -0,0 +1,547 @@ +import ctypes::c_uint; +import base::*; +import common::*; +import type_of::*; +import build::*; +import driver::session::session; +import syntax::{ast, ast_util}; +import metadata::csearch; +import back::{link, abi}; +import lib::llvm::llvm; +import lib::llvm::{ValueRef, TypeRef}; +import lib::llvm::llvm::LLVMGetParam; +import ast_map::{path, path_mod, path_name}; + +// Translation functionality related to impls and ifaces +// +// Terminology: +// vtable: a table of function pointers pointing to method wrappers +// of an impl that implements an iface +// dict: a record containing a vtable pointer along with pointers to +// all tydescs and other dicts needed to run methods in this vtable +// (i.e. corresponding to the type parameters of the impl) +// wrapper: a function that takes a dict as first argument, along +// with the method-specific tydescs for a method (and all +// other args the method expects), which fetches the extra +// tydescs and dicts from the dict, splices them into the +// arglist, and calls through to the actual method +// +// Generic functions take, along with their normal arguments, a number +// of extra tydesc and dict arguments -- one tydesc for each type +// parameter, one dict (following the tydesc in the arg order) for +// each interface bound on a type parameter. +// +// Most dicts are completely static, and are allocated and filled at +// compile time. Dicts that depend on run-time values (tydescs or +// dicts for type parameter types) are built at run-time, and interned +// through upcall_intern_dict in the runtime. This means that dict +// pointers are self-contained things that do not need to be cleaned +// up. +// +// The trans_constants pass in trans.rs outputs the vtables. Typeck +// annotates nodes with information about the methods and dicts that +// are referenced (ccx.method_map and ccx.dict_map). + +fn trans_impl(ccx: crate_ctxt, path: path, name: ast::ident, + methods: [@ast::method], id: ast::node_id, + tps: [ast::ty_param]) { + let sub_path = path + [path_name(name)]; + for m in methods { + alt ccx.item_ids.find(m.id) { + some(llfn) { + let m_bounds = param_bounds(ccx, tps + m.tps); + trans_fn(ccx, sub_path + [path_name(m.ident)], m.decl, m.body, + llfn, impl_self(ty::node_id_to_type(ccx.tcx, id)), + m_bounds, none, m.id); + } + _ { + ccx.sess.bug("Unbound id in trans_impl"); + } + } + } +} + +fn trans_self_arg(bcx: block, base: @ast::expr) -> result { + let basety = expr_ty(bcx, base); + let m_by_ref = ast::expl(ast::by_ref); + let temp_cleanups = []; + let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety}, + T_ptr(type_of_or_i8(bcx.ccx(), basety)), base, + temp_cleanups); + + // by-ref self argument should not require cleanup in the case of + // other arguments failing: + assert temp_cleanups == []; + + ret result; +} + +fn trans_method_callee(bcx: block, callee_id: ast::node_id, + self: @ast::expr, origin: typeck::method_origin) + -> lval_maybe_callee { + alt origin { + typeck::method_static(did) { + trans_static_callee(bcx, callee_id, self, did, none) + } + typeck::method_param(iid, off, p, b) { + alt bcx.fcx.param_substs { + some(substs) { + trans_monomorphized_callee(bcx, callee_id, self, + iid, off, p, b, substs) + } + none { + trans_param_callee(bcx, callee_id, self, iid, off, p, b) + } + } + } + typeck::method_iface(iid, off) { + trans_iface_callee(bcx, callee_id, self, iid, off) + } + } +} + +// Method callee where the method is statically known +fn trans_static_callee(bcx: block, callee_id: ast::node_id, + base: @ast::expr, did: ast::def_id, + substs: option<([ty::t], typeck::dict_res)>) + -> lval_maybe_callee { + let {bcx, val} = trans_self_arg(bcx, base); + {env: self_env(val, node_id_type(bcx, base.id)) + with lval_static_fn(bcx, did, callee_id, substs)} +} + +fn wrapper_fn_ty(ccx: crate_ctxt, dict_ty: TypeRef, fty: ty::t, + tps: @[ty::param_bounds]) -> {ty: ty::t, llty: TypeRef} { + let bare_fn_ty = type_of_fn_from_ty(ccx, fty, *tps); + let {inputs, output} = llfn_arg_tys(bare_fn_ty); + {ty: fty, llty: T_fn([dict_ty] + inputs, output)} +} + +fn trans_vtable_callee(bcx: block, env: callee_env, dict: ValueRef, + callee_id: ast::node_id, iface_id: ast::def_id, + n_method: uint) -> lval_maybe_callee { + let bcx = bcx, ccx = bcx.ccx(), tcx = ccx.tcx; + let method = ty::iface_methods(tcx, iface_id)[n_method]; + let method_ty = ty::mk_fn(tcx, method.fty); + let {ty: fty, llty: llfty} = + wrapper_fn_ty(ccx, val_ty(dict), method_ty, method.tps); + let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])), + T_ptr(T_array(T_ptr(llfty), n_method + 1u))); + let mptr = Load(bcx, GEPi(bcx, vtable, [0, n_method as int])); + let generic = generic_none; + if (*method.tps).len() > 0u || ty::type_has_params(fty) { + let tydescs = [], tis = []; + let tptys = node_id_type_params(bcx, callee_id); + for t in vec::tail_n(tptys, tptys.len() - (*method.tps).len()) { + let ti = none; + let td = get_tydesc(bcx, t, true, ti); + tis += [ti]; + tydescs += [td.val]; + bcx = td.bcx; + } + generic = generic_full({item_type: fty, + static_tis: tis, + tydescs: tydescs, + param_bounds: method.tps, + origins: ccx.maps.dict_map.find(callee_id)}); + } + {bcx: bcx, val: mptr, kind: owned, + env: env, + generic: generic} +} + +fn trans_monomorphized_callee(bcx: block, callee_id: ast::node_id, + base: @ast::expr, iface_id: ast::def_id, + n_method: uint, n_param: uint, n_bound: uint, + substs: param_substs) -> lval_maybe_callee { + alt find_dict_in_fn_ctxt(substs, n_param, n_bound) { + typeck::dict_static(impl_did, tys, sub_origins) { + let tcx = bcx.tcx(); + if impl_did.crate != ast::local_crate { + ret trans_param_callee(bcx, callee_id, base, iface_id, + n_method, n_param, n_bound); + } + let mname = ty::iface_methods(tcx, iface_id)[n_method].ident; + let mth = alt check tcx.items.get(impl_did.node) { + ast_map::node_item(@{node: ast::item_impl(_, _, _, ms), _}, _) { + option::get(vec::find(ms, {|m| m.ident == mname})) + } + }; + ret trans_static_callee(bcx, callee_id, base, + ast_util::local_def(mth.id), + some((tys, sub_origins))); + } + typeck::dict_iface(iid) { + ret trans_iface_callee(bcx, callee_id, base, iid, n_method); + } + typeck::dict_param(n_param, n_bound) { + fail "dict_param left in monomorphized function's dict substs"; + } + } +} + + +// Method callee where the dict comes from a type param +fn trans_param_callee(bcx: block, callee_id: ast::node_id, + base: @ast::expr, iface_id: ast::def_id, n_method: uint, + n_param: uint, n_bound: uint) -> lval_maybe_callee { + let {bcx, val} = trans_self_arg(bcx, base); + let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound]; + trans_vtable_callee(bcx, dict_env(dict, val), dict, + callee_id, iface_id, n_method) +} + +// Method callee where the dict comes from a boxed iface +fn trans_iface_callee(bcx: block, callee_id: ast::node_id, + base: @ast::expr, iface_id: ast::def_id, n_method: uint) + -> lval_maybe_callee { + let {bcx, val} = trans_temp_expr(bcx, base); + let dict = Load(bcx, PointerCast(bcx, GEPi(bcx, val, [0, 0]), + T_ptr(T_ptr(T_dict())))); + let box = Load(bcx, GEPi(bcx, val, [0, 1])); + // FIXME[impl] I doubt this is alignment-safe + let self = GEPi(bcx, box, [0, abi::box_field_body]); + trans_vtable_callee(bcx, dict_env(dict, self), dict, + callee_id, iface_id, n_method) +} + +fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} { + let out_ty = llvm::LLVMGetReturnType(ft); + let n_args = llvm::LLVMCountParamTypes(ft); + let args = vec::init_elt(n_args as uint, 0 as TypeRef); + unsafe { llvm::LLVMGetParamTypes(ft, vec::unsafe::to_ptr(args)); } + {inputs: args, output: out_ty} +} + +fn trans_vtable(ccx: crate_ctxt, id: ast::node_id, name: str, + ptrs: [ValueRef]) { + let tbl = C_struct(ptrs); + let vt_gvar = str::as_buf(name, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, val_ty(tbl), buf) + }); + llvm::LLVMSetInitializer(vt_gvar, tbl); + llvm::LLVMSetGlobalConstant(vt_gvar, lib::llvm::True); + ccx.item_ids.insert(id, vt_gvar); + ccx.item_symbols.insert(id, name); +} + +fn find_dict_in_fn_ctxt(ps: param_substs, n_param: uint, n_bound: uint) + -> typeck::dict_origin { + let dict_off = n_bound, i = 0u; + // Dicts are stored in a flat array, finding the right one is + // somewhat awkward + for bounds in *ps.bounds { + i += 1u; + if i >= n_param { break; } + for bound in *bounds { + alt bound { ty::bound_iface(_) { dict_off += 1u; } _ {} } + } + } + option::get(ps.dicts)[dict_off] +} + +fn resolve_dicts_in_fn_ctxt(fcx: fn_ctxt, dicts: typeck::dict_res) + -> option<typeck::dict_res> { + let result = []; + for dict in *dicts { + result += [alt dict { + typeck::dict_static(iid, tys, sub) { + alt resolve_dicts_in_fn_ctxt(fcx, sub) { + some(sub) { + let tys = alt fcx.param_substs { + some(substs) { + vec::map(tys, {|t| + ty::substitute_type_params(fcx.ccx.tcx, substs.tys, t) + }) + } + _ { tys } + }; + typeck::dict_static(iid, tys, sub) + } + none { ret none; } + } + } + typeck::dict_param(n_param, n_bound) { + alt fcx.param_substs { + some(substs) { + find_dict_in_fn_ctxt(substs, n_param, n_bound) + } + none { ret none; } + } + } + _ { dict } + }]; + } + some(@result) +} + +fn trans_wrapper(ccx: crate_ctxt, pt: path, llfty: TypeRef, + fill: fn(ValueRef, block) -> block) + -> ValueRef { + let name = link::mangle_internal_name_by_path(ccx, pt); + let llfn = decl_internal_cdecl_fn(ccx.llmod, name, llfty); + let fcx = new_fn_ctxt(ccx, [], llfn, none); + let bcx = top_scope_block(fcx, none), lltop = bcx.llbb; + let bcx = fill(llfn, bcx); + build_return(bcx); + finish_fn(fcx, lltop); + ret llfn; +} + +fn trans_impl_wrapper(ccx: crate_ctxt, pt: path, + extra_tps: [ty::param_bounds], real_fn: ValueRef) + -> ValueRef { + let {inputs: real_args, output: real_ret} = + llfn_arg_tys(llvm::LLVMGetElementType(val_ty(real_fn))); + let extra_ptrs = []; + for tp in extra_tps { + extra_ptrs += [T_ptr(ccx.tydesc_type)]; + for bound in *tp { + alt bound { + ty::bound_iface(_) { extra_ptrs += [T_ptr(T_dict())]; } + _ {} + } + } + } + let env_ty = T_ptr(T_struct([T_ptr(T_i8())] + extra_ptrs)); + let n_extra_ptrs = extra_ptrs.len(); + + let wrap_args = [T_ptr(T_dict())] + + vec::slice(real_args, 0u, first_tp_arg) + + vec::slice(real_args, first_tp_arg + n_extra_ptrs, real_args.len()); + let llfn_ty = T_fn(wrap_args, real_ret); + trans_wrapper(ccx, pt, llfn_ty, {|llfn, bcx| + let dict = PointerCast(bcx, LLVMGetParam(llfn, 0 as c_uint), env_ty); + // retptr, self + let args = [LLVMGetParam(llfn, 1 as c_uint), + LLVMGetParam(llfn, 2 as c_uint)]; + let i = 0u; + // saved tydescs/dicts + while i < n_extra_ptrs { + i += 1u; + args += [load_inbounds(bcx, dict, [0, i as int])]; + } + // the rest of the parameters + let j = 3u as c_uint; + let params_total = llvm::LLVMCountParamTypes(llfn_ty); + while j < params_total { + args += [LLVMGetParam(llfn, j)]; + j += 1u as c_uint; + } + Call(bcx, real_fn, args); + bcx + }) +} + +fn trans_impl_vtable(ccx: crate_ctxt, pt: path, + iface_id: ast::def_id, ms: [@ast::method], + tps: [ast::ty_param], it: @ast::item) { + let new_pt = pt + [path_name(it.ident), path_name(int::str(it.id)), + path_name("wrap")]; + let extra_tps = param_bounds(ccx, tps); + let ptrs = vec::map(*ty::iface_methods(ccx.tcx, iface_id), {|im| + alt vec::find(ms, {|m| m.ident == im.ident}) { + some(m) { + let target = ccx.item_ids.get(m.id); + trans_impl_wrapper(ccx, new_pt + [path_name(m.ident)], + extra_tps, target) + } + _ { + ccx.sess.span_bug(it.span, "No matching method \ + in trans_impl_vtable"); + } + } + }); + let s = link::mangle_internal_name_by_path( + ccx, new_pt + [path_name("!vtable")]); + trans_vtable(ccx, it.id, s, ptrs); +} + +fn trans_iface_wrapper(ccx: crate_ctxt, pt: path, m: ty::method, + n: uint) -> ValueRef { + let {llty: llfty, _} = wrapper_fn_ty(ccx, T_ptr(T_i8()), + ty::mk_fn(ccx.tcx, m.fty), m.tps); + trans_wrapper(ccx, pt, llfty, {|llfn, bcx| + let param = PointerCast(bcx, LLVMGetParam(llfn, 2u as c_uint), + T_ptr(T_opaque_iface(ccx))); + let dict = Load(bcx, GEPi(bcx, param, [0, 0])); + let box = Load(bcx, GEPi(bcx, param, [0, 1])); + let self = GEPi(bcx, box, [0, abi::box_field_body]); + let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])), + T_ptr(T_array(T_ptr(llfty), n + 1u))); + let mptr = Load(bcx, GEPi(bcx, vtable, [0, n as int])); + let args = [PointerCast(bcx, dict, T_ptr(T_i8())), + LLVMGetParam(llfn, 1u as c_uint), + PointerCast(bcx, self, T_opaque_cbox_ptr(ccx))]; + let i = 3u as c_uint, total = llvm::LLVMCountParamTypes(llfty); + while i < total { + args += [LLVMGetParam(llfn, i)]; + i += 1u as c_uint; + } + Call(bcx, mptr, args); + bcx + }) +} + +fn trans_iface_vtable(ccx: crate_ctxt, pt: path, it: @ast::item) { + let new_pt = pt + [path_name(it.ident), path_name(int::str(it.id))]; + let i_did = ast_util::local_def(it.id), i = 0u; + let ptrs = vec::map(*ty::iface_methods(ccx.tcx, i_did), {|m| + let w = trans_iface_wrapper(ccx, new_pt + [path_name(m.ident)], m, i); + i += 1u; + w + }); + let s = link::mangle_internal_name_by_path( + ccx, new_pt + [path_name("!vtable")]); + trans_vtable(ccx, it.id, s, ptrs); +} + +fn dict_is_static(tcx: ty::ctxt, origin: typeck::dict_origin) -> bool { + alt origin { + typeck::dict_static(_, ts, origs) { + vec::all(ts, {|t| !ty::type_has_params(t)}) && + vec::all(*origs, {|o| dict_is_static(tcx, o)}) + } + typeck::dict_iface(_) { true } + _ { false } + } +} + +fn get_dict(bcx: block, origin: typeck::dict_origin) -> result { + let ccx = bcx.ccx(); + alt origin { + typeck::dict_static(impl_did, tys, sub_origins) { + if dict_is_static(ccx.tcx, origin) { + ret rslt(bcx, get_static_dict(bcx, origin)); + } + let {bcx, ptrs} = get_dict_ptrs(bcx, origin); + let pty = T_ptr(T_i8()), dict_ty = T_array(pty, ptrs.len()); + let dict = alloca(bcx, dict_ty), i = 0; + for ptr in ptrs { + Store(bcx, PointerCast(bcx, ptr, pty), GEPi(bcx, dict, [0, i])); + i += 1; + } + dict = Call(bcx, ccx.upcalls.intern_dict, + [C_uint(ccx, ptrs.len()), + PointerCast(bcx, dict, T_ptr(T_dict()))]); + rslt(bcx, dict) + } + typeck::dict_param(n_param, n_bound) { + rslt(bcx, option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound]) + } + typeck::dict_iface(did) { + ret rslt(bcx, get_static_dict(bcx, origin)); + } + } +} + +fn dict_id(tcx: ty::ctxt, origin: typeck::dict_origin) -> dict_id { + alt origin { + typeck::dict_static(did, ts, origs) { + let d_params = [], orig = 0u; + if ts.len() == 0u { ret @{def: did, params: d_params}; } + let impl_params = ty::lookup_item_type(tcx, did).bounds; + vec::iter2(ts, *impl_params) {|t, bounds| + d_params += [dict_param_ty(t)]; + for bound in *bounds { + alt bound { + ty::bound_iface(_) { + d_params += [dict_param_dict(dict_id(tcx, origs[orig]))]; + orig += 1u; + } + _ {} + } + } + } + @{def: did, params: d_params} + } + typeck::dict_iface(did) { + @{def: did, params: []} + } + _ { + tcx.sess.bug("Unexpected dict_param in dict_id"); + } + } +} + +fn get_static_dict(bcx: block, origin: typeck::dict_origin) + -> ValueRef { + let ccx = bcx.ccx(); + let id = dict_id(ccx.tcx, origin); + alt ccx.dicts.find(id) { + some(d) { ret d; } + none {} + } + let ptrs = C_struct(get_dict_ptrs(bcx, origin).ptrs); + let name = ccx.names("dict"); + let gvar = str::as_buf(name, {|buf| + llvm::LLVMAddGlobal(ccx.llmod, val_ty(ptrs), buf) + }); + llvm::LLVMSetGlobalConstant(gvar, lib::llvm::True); + llvm::LLVMSetInitializer(gvar, ptrs); + lib::llvm::SetLinkage(gvar, lib::llvm::InternalLinkage); + let cast = llvm::LLVMConstPointerCast(gvar, T_ptr(T_dict())); + ccx.dicts.insert(id, cast); + cast +} + +fn get_dict_ptrs(bcx: block, origin: typeck::dict_origin) + -> {bcx: block, ptrs: [ValueRef]} { + let ccx = bcx.ccx(); + fn get_vtable(ccx: crate_ctxt, did: ast::def_id) -> ValueRef { + if did.crate == ast::local_crate { + ccx.item_ids.get(did.node) + } else { + let name = csearch::get_symbol(ccx.sess.cstore, did); + get_extern_const(ccx.externs, ccx.llmod, name, T_ptr(T_i8())) + } + } + alt origin { + typeck::dict_static(impl_did, tys, sub_origins) { + let impl_params = ty::lookup_item_type(ccx.tcx, impl_did).bounds; + let ptrs = [get_vtable(ccx, impl_did)]; + let origin = 0u, bcx = bcx; + vec::iter2(*impl_params, tys) {|param, ty| + let rslt = get_tydesc_simple(bcx, ty, true); + ptrs += [rslt.val]; + bcx = rslt.bcx; + for bound in *param { + alt bound { + ty::bound_iface(_) { + let res = get_dict(bcx, sub_origins[origin]); + ptrs += [res.val]; + bcx = res.bcx; + origin += 1u; + } + _ {} + } + } + } + {bcx: bcx, ptrs: ptrs} + } + typeck::dict_iface(did) { + {bcx: bcx, ptrs: [get_vtable(ccx, did)]} + } + _ { + bcx.tcx().sess.bug("Unexpected dict_param in get_dict_ptrs"); + } + } +} + +fn trans_cast(bcx: block, val: @ast::expr, id: ast::node_id, dest: dest) + -> block { + if dest == ignore { ret trans_expr(bcx, val, ignore); } + let ccx = bcx.ccx(); + let v_ty = expr_ty(bcx, val); + let {bcx, box, body} = trans_malloc_boxed(bcx, v_ty); + add_clean_free(bcx, box, false); + bcx = trans_expr_save_in(bcx, val, body); + revoke_clean(bcx, box); + let result = get_dest_addr(dest); + Store(bcx, box, PointerCast(bcx, GEPi(bcx, result, [0, 1]), + T_ptr(val_ty(box)))); + let {bcx, val: dict} = get_dict(bcx, ccx.maps.dict_map.get(id)[0]); + Store(bcx, dict, PointerCast(bcx, GEPi(bcx, result, [0, 0]), + T_ptr(val_ty(dict)))); + bcx +} diff --git a/src/rustc/middle/trans/native.rs b/src/rustc/middle/trans/native.rs new file mode 100644 index 00000000000..22791047ef9 --- /dev/null +++ b/src/rustc/middle/trans/native.rs @@ -0,0 +1,359 @@ +import driver::session::session; +import syntax::codemap::span; +import ctypes::c_uint; +import front::attr; +import lib::llvm::{ llvm, TypeRef, ValueRef }; +import syntax::ast; +import back::link; +import common::*; +import build::*; +import base::*; +import type_of::*; + +export link_name, trans_native_mod, register_crust_fn, trans_crust_fn; + +fn link_name(i: @ast::native_item) -> str { + alt attr::get_meta_item_value_str_by_name(i.attrs, "link_name") { + none { ret i.ident; } + option::some(ln) { ret ln; } + } +} + +type c_stack_tys = { + arg_tys: [TypeRef], + ret_ty: TypeRef, + ret_def: bool, + bundle_ty: TypeRef, + shim_fn_ty: TypeRef +}; + +fn c_arg_and_ret_lltys(ccx: crate_ctxt, + id: ast::node_id) -> ([TypeRef], TypeRef, ty::t) { + alt ty::get(ty::node_id_to_type(ccx.tcx, id)).struct { + ty::ty_fn({inputs: arg_tys, output: ret_ty, _}) { + let llargtys = type_of_explicit_args(ccx, arg_tys); + let llretty = type_of::type_of(ccx, ret_ty); + (llargtys, llretty, ret_ty) + } + _ { ccx.sess.bug("c_arg_and_ret_lltys called on non-function type"); } + } +} + +fn c_stack_tys(ccx: crate_ctxt, + id: ast::node_id) -> @c_stack_tys { + let (llargtys, llretty, ret_ty) = c_arg_and_ret_lltys(ccx, id); + let bundle_ty = T_struct(llargtys + [T_ptr(llretty)]); + ret @{ + arg_tys: llargtys, + ret_ty: llretty, + ret_def: !ty::type_is_bot(ret_ty) && !ty::type_is_nil(ret_ty), + bundle_ty: bundle_ty, + shim_fn_ty: T_fn([T_ptr(bundle_ty)], T_void()) + }; +} + +type shim_arg_builder = fn(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef]; + +type shim_ret_builder = fn(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef, llretval: ValueRef); + +fn build_shim_fn_(ccx: crate_ctxt, + shim_name: str, + llbasefn: ValueRef, + tys: @c_stack_tys, + cc: lib::llvm::CallConv, + arg_builder: shim_arg_builder, + ret_builder: shim_ret_builder) -> ValueRef { + + let llshimfn = decl_internal_cdecl_fn( + ccx.llmod, shim_name, tys.shim_fn_ty); + + // Declare the body of the shim function: + let fcx = new_fn_ctxt(ccx, [], llshimfn, none); + let bcx = top_scope_block(fcx, none); + let lltop = bcx.llbb; + let llargbundle = llvm::LLVMGetParam(llshimfn, 0 as c_uint); + let llargvals = arg_builder(bcx, tys, llargbundle); + + // Create the call itself and store the return value: + let llretval = CallWithConv(bcx, llbasefn, + llargvals, cc); // r + + ret_builder(bcx, tys, llargbundle, llretval); + + build_return(bcx); + finish_fn(fcx, lltop); + + ret llshimfn; +} + +type wrap_arg_builder = fn(bcx: block, tys: @c_stack_tys, + llwrapfn: ValueRef, + llargbundle: ValueRef); + +type wrap_ret_builder = fn(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef); + +fn build_wrap_fn_(ccx: crate_ctxt, + tys: @c_stack_tys, + llshimfn: ValueRef, + llwrapfn: ValueRef, + shim_upcall: ValueRef, + arg_builder: wrap_arg_builder, + ret_builder: wrap_ret_builder) { + + let fcx = new_fn_ctxt(ccx, [], llwrapfn, none); + let bcx = top_scope_block(fcx, none); + let lltop = bcx.llbb; + + // Allocate the struct and write the arguments into it. + let llargbundle = alloca(bcx, tys.bundle_ty); + arg_builder(bcx, tys, llwrapfn, llargbundle); + + // Create call itself. + let llshimfnptr = PointerCast(bcx, llshimfn, T_ptr(T_i8())); + let llrawargbundle = PointerCast(bcx, llargbundle, T_ptr(T_i8())); + Call(bcx, shim_upcall, [llrawargbundle, llshimfnptr]); + ret_builder(bcx, tys, llargbundle); + + tie_up_header_blocks(fcx, lltop); + + // Make sure our standard return block (that we didn't use) is terminated + let ret_cx = raw_block(fcx, fcx.llreturn); + Unreachable(ret_cx); +} + +// For each native function F, we generate a wrapper function W and a shim +// function S that all work together. The wrapper function W is the function +// that other rust code actually invokes. Its job is to marshall the +// arguments into a struct. It then uses a small bit of assembly to switch +// over to the C stack and invoke the shim function. The shim function S then +// unpacks the arguments from the struct and invokes the actual function F +// according to its specified calling convention. +// +// Example: Given a native c-stack function F(x: X, y: Y) -> Z, +// we generate a wrapper function W that looks like: +// +// void W(Z* dest, void *env, X x, Y y) { +// struct { X x; Y y; Z *z; } args = { x, y, z }; +// call_on_c_stack_shim(S, &args); +// } +// +// The shim function S then looks something like: +// +// void S(struct { X x; Y y; Z *z; } *args) { +// *args->z = F(args->x, args->y); +// } +// +// However, if the return type of F is dynamically sized or of aggregate type, +// the shim function looks like: +// +// void S(struct { X x; Y y; Z *z; } *args) { +// F(args->z, args->x, args->y); +// } +// +// Note: on i386, the layout of the args struct is generally the same as the +// desired layout of the arguments on the C stack. Therefore, we could use +// upcall_alloc_c_stack() to allocate the `args` structure and switch the +// stack pointer appropriately to avoid a round of copies. (In fact, the shim +// function itself is unnecessary). We used to do this, in fact, and will +// perhaps do so in the future. +fn trans_native_mod(ccx: crate_ctxt, + native_mod: ast::native_mod, abi: ast::native_abi) { + fn build_shim_fn(ccx: crate_ctxt, + native_item: @ast::native_item, + tys: @c_stack_tys, + cc: lib::llvm::CallConv) -> ValueRef { + + fn build_args(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef] { + let llargvals = []; + let i = 0u; + let n = vec::len(tys.arg_tys); + while i < n { + let llargval = load_inbounds(bcx, llargbundle, [0, i as int]); + llargvals += [llargval]; + i += 1u; + } + ret llargvals; + } + + fn build_ret(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef, llretval: ValueRef) { + if tys.ret_def { + let n = vec::len(tys.arg_tys); + // R** llretptr = &args->r; + let llretptr = GEPi(bcx, llargbundle, [0, n as int]); + // R* llretloc = *llretptr; /* (args->r) */ + let llretloc = Load(bcx, llretptr); + // *args->r = r; + Store(bcx, llretval, llretloc); + } + } + + let lname = link_name(native_item); + // Declare the "prototype" for the base function F: + let llbasefnty = T_fn(tys.arg_tys, tys.ret_ty); + let llbasefn = decl_fn(ccx.llmod, lname, cc, llbasefnty); + // Name the shim function + let shim_name = lname + "__c_stack_shim"; + ret build_shim_fn_(ccx, shim_name, llbasefn, tys, cc, + build_args, build_ret); + } + + fn build_wrap_fn(ccx: crate_ctxt, + tys: @c_stack_tys, + num_tps: uint, + llshimfn: ValueRef, + llwrapfn: ValueRef) { + + fn build_args(bcx: block, tys: @c_stack_tys, + llwrapfn: ValueRef, llargbundle: ValueRef, + num_tps: uint) { + let i = 0u, n = vec::len(tys.arg_tys); + let implicit_args = first_tp_arg + num_tps; // ret + env + while i < n { + let llargval = llvm::LLVMGetParam( + llwrapfn, + (i + implicit_args) as c_uint); + store_inbounds(bcx, llargval, llargbundle, [0, i as int]); + i += 1u; + } + let llretptr = llvm::LLVMGetParam(llwrapfn, 0 as c_uint); + store_inbounds(bcx, llretptr, llargbundle, [0, n as int]); + } + + fn build_ret(bcx: block, _tys: @c_stack_tys, + _llargbundle: ValueRef) { + RetVoid(bcx); + } + + build_wrap_fn_(ccx, tys, llshimfn, llwrapfn, + ccx.upcalls.call_shim_on_c_stack, + bind build_args(_, _ ,_ , _, num_tps), + build_ret); + } + + let cc = lib::llvm::CCallConv; + alt abi { + ast::native_abi_rust_intrinsic { ret; } + ast::native_abi_cdecl { cc = lib::llvm::CCallConv; } + ast::native_abi_stdcall { cc = lib::llvm::X86StdcallCallConv; } + } + + for native_item in native_mod.items { + alt native_item.node { + ast::native_item_fn(fn_decl, tps) { + let id = native_item.id; + let tys = c_stack_tys(ccx, id); + alt ccx.item_ids.find(id) { + some(llwrapfn) { + let llshimfn = build_shim_fn(ccx, native_item, tys, cc); + build_wrap_fn(ccx, tys, vec::len(tps), llshimfn, llwrapfn); + } + none { + ccx.sess.span_bug( + native_item.span, + "unbound function item in trans_native_mod"); + } + } + } + } + } +} + +fn trans_crust_fn(ccx: crate_ctxt, path: ast_map::path, decl: ast::fn_decl, + body: ast::blk, llwrapfn: ValueRef, id: ast::node_id) { + + fn build_rust_fn(ccx: crate_ctxt, path: ast_map::path, + decl: ast::fn_decl, body: ast::blk, + id: ast::node_id) -> ValueRef { + let t = ty::node_id_to_type(ccx.tcx, id); + let ps = link::mangle_internal_name_by_path( + ccx, path + [ast_map::path_name("__rust_abi")]); + let llty = type_of_fn_from_ty(ccx, t, []); + let llfndecl = decl_internal_cdecl_fn(ccx.llmod, ps, llty); + trans_fn(ccx, path, decl, body, llfndecl, no_self, [], none, id); + ret llfndecl; + } + + fn build_shim_fn(ccx: crate_ctxt, path: ast_map::path, + llrustfn: ValueRef, tys: @c_stack_tys) -> ValueRef { + + fn build_args(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef) -> [ValueRef] { + let llargvals = []; + let i = 0u; + let n = vec::len(tys.arg_tys); + let llretptr = load_inbounds(bcx, llargbundle, [0, n as int]); + llargvals += [llretptr]; + let llenvptr = C_null(T_opaque_box_ptr(bcx.ccx())); + llargvals += [llenvptr]; + while i < n { + let llargval = load_inbounds(bcx, llargbundle, [0, i as int]); + llargvals += [llargval]; + i += 1u; + } + ret llargvals; + } + + fn build_ret(_bcx: block, _tys: @c_stack_tys, + _llargbundle: ValueRef, _llretval: ValueRef) { + // Nop. The return pointer in the Rust ABI function + // is wired directly into the return slot in the shim struct + } + + let shim_name = link::mangle_internal_name_by_path( + ccx, path + [ast_map::path_name("__rust_stack_shim")]); + ret build_shim_fn_(ccx, shim_name, llrustfn, tys, + lib::llvm::CCallConv, + build_args, build_ret); + } + + fn build_wrap_fn(ccx: crate_ctxt, llshimfn: ValueRef, + llwrapfn: ValueRef, tys: @c_stack_tys) { + + fn build_args(bcx: block, tys: @c_stack_tys, + llwrapfn: ValueRef, llargbundle: ValueRef) { + let llretptr = alloca(bcx, tys.ret_ty); + let i = 0u, n = vec::len(tys.arg_tys); + while i < n { + let llargval = llvm::LLVMGetParam( + llwrapfn, i as c_uint); + store_inbounds(bcx, llargval, llargbundle, [0, i as int]); + i += 1u; + } + store_inbounds(bcx, llretptr, llargbundle, [0, n as int]); + } + + fn build_ret(bcx: block, tys: @c_stack_tys, + llargbundle: ValueRef) { + let n = vec::len(tys.arg_tys); + let llretval = load_inbounds(bcx, llargbundle, [0, n as int]); + let llretval = Load(bcx, llretval); + Ret(bcx, llretval); + } + + build_wrap_fn_(ccx, tys, llshimfn, llwrapfn, + ccx.upcalls.call_shim_on_rust_stack, + build_args, build_ret); + } + + let tys = c_stack_tys(ccx, id); + // The internal Rust ABI function - runs on the Rust stack + let llrustfn = build_rust_fn(ccx, path, decl, body, id); + // The internal shim function - runs on the Rust stack + let llshimfn = build_shim_fn(ccx, path, llrustfn, tys); + // The external C function - runs on the C stack + build_wrap_fn(ccx, llshimfn, llwrapfn, tys) +} + +fn register_crust_fn(ccx: crate_ctxt, sp: span, + path: ast_map::path, node_id: ast::node_id) { + let t = ty::node_id_to_type(ccx.tcx, node_id); + let (llargtys, llretty, _) = c_arg_and_ret_lltys(ccx, node_id); + let llfty = T_fn(llargtys, llretty); + register_fn_fuller(ccx, sp, path, "crust fn", node_id, + t, lib::llvm::CCallConv, llfty); +} \ No newline at end of file diff --git a/src/rustc/middle/trans/shape.rs b/src/rustc/middle/trans/shape.rs new file mode 100644 index 00000000000..2805778e927 --- /dev/null +++ b/src/rustc/middle/trans/shape.rs @@ -0,0 +1,787 @@ +// A "shape" is a compact encoding of a type that is used by interpreted glue. +// This substitutes for the runtime tags used by e.g. MLs. + +import lib::llvm::llvm; +import lib::llvm::{True, False, ModuleRef, TypeRef, ValueRef}; +import driver::session; +import driver::session::session; +import trans::base; +import middle::trans::common::*; +import back::abi; +import middle::ty; +import middle::ty::field; +import syntax::ast; +import syntax::ast_util::dummy_sp; +import syntax::util::interner; +import util::common; +import trans::build::{Load, Store, Add, GEPi}; +import syntax::codemap::span; + +import std::map::hashmap; + +import ty_ctxt = middle::ty::ctxt; + +type res_info = {did: ast::def_id, t: ty::t}; + +type ctxt = + {mutable next_tag_id: u16, + pad: u16, + tag_id_to_index: hashmap<ast::def_id, u16>, + mutable tag_order: [ast::def_id], + resources: interner::interner<res_info>, + llshapetablesty: TypeRef, + llshapetables: ValueRef}; + +const shape_u8: u8 = 0u8; +const shape_u16: u8 = 1u8; +const shape_u32: u8 = 2u8; +const shape_u64: u8 = 3u8; +const shape_i8: u8 = 4u8; +const shape_i16: u8 = 5u8; +const shape_i32: u8 = 6u8; +const shape_i64: u8 = 7u8; +const shape_f32: u8 = 8u8; +const shape_f64: u8 = 9u8; +const shape_box: u8 = 10u8; +const shape_vec: u8 = 11u8; +const shape_enum: u8 = 12u8; +const shape_box_old: u8 = 13u8; // deprecated, remove after snapshot +const shape_struct: u8 = 17u8; +const shape_box_fn: u8 = 18u8; +const shape_UNUSED: u8 = 19u8; +const shape_res: u8 = 20u8; +const shape_var: u8 = 21u8; +const shape_uniq: u8 = 22u8; +const shape_opaque_closure_ptr: u8 = 23u8; // the closure itself. +const shape_uniq_fn: u8 = 25u8; +const shape_stack_fn: u8 = 26u8; +const shape_bare_fn: u8 = 27u8; +const shape_tydesc: u8 = 28u8; +const shape_send_tydesc: u8 = 29u8; +const shape_class: u8 = 30u8; + +fn hash_res_info(ri: res_info) -> uint { + let h = 5381u; + h *= 33u; + h += ri.did.crate as uint; + h *= 33u; + h += ri.did.node as uint; + h *= 33u; + h += ty::type_id(ri.t); + ret h; +} + +fn eq_res_info(a: res_info, b: res_info) -> bool { + ret a.did.crate == b.did.crate && a.did.node == b.did.node && a.t == b.t; +} + +fn mk_global(ccx: crate_ctxt, name: str, llval: ValueRef, internal: bool) -> + ValueRef { + let llglobal = + str::as_buf(name, + {|buf| + lib::llvm::llvm::LLVMAddGlobal(ccx.llmod, + val_ty(llval), buf) + }); + lib::llvm::llvm::LLVMSetInitializer(llglobal, llval); + lib::llvm::llvm::LLVMSetGlobalConstant(llglobal, True); + + if internal { + lib::llvm::SetLinkage(llglobal, lib::llvm::InternalLinkage); + } + + ret llglobal; +} + + +// Computes a set of variants of a enum that are guaranteed to have size and +// alignment at least as large as any other variant of the enum. This is an +// important performance optimization. +// +// FIXME: Use this in dynamic_size_of() as well. + +fn largest_variants(ccx: crate_ctxt, tag_id: ast::def_id) -> [uint] { + // Compute the minimum and maximum size and alignment for each variant. + // + // FIXME: We could do better here; e.g. we know that any variant that + // contains (T,T) must be as least as large as any variant that contains + // just T. + let ranges = []; + let variants = ty::enum_variants(ccx.tcx, tag_id); + for variant: ty::variant_info in *variants { + let bounded = true; + let {a: min_size, b: min_align} = {a: 0u, b: 0u}; + for elem_t: ty::t in variant.args { + if ty::type_has_params(elem_t) { + // FIXME: We could do better here; this causes us to + // conservatively assume that (int, T) has minimum size 0, + // when in fact it has minimum size sizeof(int). + bounded = false; + } else { + let llty = type_of::type_of(ccx, elem_t); + min_size += llsize_of_real(ccx, llty); + min_align += llalign_of_real(ccx, llty); + } + } + + ranges += + [{size: {min: min_size, bounded: bounded}, + align: {min: min_align, bounded: bounded}}]; + } + + // Initialize the candidate set to contain all variants. + let candidates = [mutable]; + for variant in *variants { candidates += [mutable true]; } + + // Do a pairwise comparison among all variants still in the candidate set. + // Throw out any variant that we know has size and alignment at least as + // small as some other variant. + let i = 0u; + while i < vec::len(ranges) - 1u { + if candidates[i] { + let j = i + 1u; + while j < vec::len(ranges) { + if candidates[j] { + if ranges[i].size.bounded && ranges[i].align.bounded && + ranges[j].size.bounded && ranges[j].align.bounded { + if ranges[i].size >= ranges[j].size && + ranges[i].align >= ranges[j].align { + // Throw out j. + candidates[j] = false; + } else if ranges[j].size >= ranges[i].size && + ranges[j].align >= ranges[j].align { + // Throw out i. + candidates[i] = false; + } + } + } + j += 1u; + } + } + i += 1u; + } + + // Return the resulting set. + let result = []; + i = 0u; + while i < vec::len(candidates) { + if candidates[i] { result += [i]; } + i += 1u; + } + ret result; +} + +fn round_up(size: u16, align: u8) -> u16 { + assert (align >= 1u8); + let alignment = align as u16; + ret size - 1u16 + alignment & !(alignment - 1u16); +} + +type size_align = {size: u16, align: u8}; + +fn compute_static_enum_size(ccx: crate_ctxt, largest_variants: [uint], + did: ast::def_id) -> size_align { + let max_size = 0u16; + let max_align = 1u8; + let variants = ty::enum_variants(ccx.tcx, did); + for vid: uint in largest_variants { + // We increment a "virtual data pointer" to compute the size. + let lltys = []; + for typ: ty::t in variants[vid].args { + lltys += [type_of::type_of(ccx, typ)]; + } + + let llty = trans::common::T_struct(lltys); + let dp = llsize_of_real(ccx, llty) as u16; + let variant_align = llalign_of_real(ccx, llty) as u8; + + if max_size < dp { max_size = dp; } + if max_align < variant_align { max_align = variant_align; } + } + + // Add space for the enum if applicable. + // FIXME (issue #792): This is wrong. If the enum starts with an 8 byte + // aligned quantity, we don't align it. + if vec::len(*variants) > 1u { + let variant_t = T_enum_variant(ccx); + max_size += llsize_of_real(ccx, variant_t) as u16; + let align = llalign_of_real(ccx, variant_t) as u8; + if max_align < align { max_align = align; } + } + + ret {size: max_size, align: max_align}; +} + +enum enum_kind { + tk_unit, // 1 variant, no data + tk_enum, // N variants, no data + tk_newtype, // 1 variant, data + tk_complex // N variants, no data +} + +fn enum_kind(ccx: crate_ctxt, did: ast::def_id) -> enum_kind { + let variants = ty::enum_variants(ccx.tcx, did); + if vec::any(*variants) {|v| vec::len(v.args) > 0u} { + if vec::len(*variants) == 1u { tk_newtype } + else { tk_complex } + } else { + if vec::len(*variants) <= 1u { tk_unit } + else { tk_enum } + } +} + +// Returns the code corresponding to the pointer size on this architecture. +fn s_int(tcx: ty_ctxt) -> u8 { + ret alt tcx.sess.targ_cfg.arch { + session::arch_x86 { shape_i32 } + session::arch_x86_64 { shape_i64 } + session::arch_arm { shape_i32 } + }; +} + +fn s_uint(tcx: ty_ctxt) -> u8 { + ret alt tcx.sess.targ_cfg.arch { + session::arch_x86 { shape_u32 } + session::arch_x86_64 { shape_u64 } + session::arch_arm { shape_u32 } + }; +} + +fn s_float(tcx: ty_ctxt) -> u8 { + ret alt tcx.sess.targ_cfg.arch { + session::arch_x86 { shape_f64 } + session::arch_x86_64 { shape_f64 } + session::arch_arm { shape_f64 } + }; +} + +fn s_variant_enum_t(tcx: ty_ctxt) -> u8 { + ret s_int(tcx); +} + +fn s_tydesc(_tcx: ty_ctxt) -> u8 { + ret shape_tydesc; +} + +fn s_send_tydesc(_tcx: ty_ctxt) -> u8 { + ret shape_send_tydesc; +} + +fn mk_ctxt(llmod: ModuleRef) -> ctxt { + let llshapetablesty = trans::common::T_named_struct("shapes"); + let llshapetables = str::as_buf("shapes", {|buf| + lib::llvm::llvm::LLVMAddGlobal(llmod, llshapetablesty, buf) + }); + + ret {mutable next_tag_id: 0u16, + pad: 0u16, + tag_id_to_index: common::new_def_hash(), + mutable tag_order: [], + resources: interner::mk(hash_res_info, eq_res_info), + llshapetablesty: llshapetablesty, + llshapetables: llshapetables}; +} + +fn add_bool(&dest: [u8], val: bool) { dest += [if val { 1u8 } else { 0u8 }]; } + +fn add_u16(&dest: [u8], val: u16) { + dest += [(val & 0xffu16) as u8, (val >> 8u16) as u8]; +} + +fn add_substr(&dest: [u8], src: [u8]) { + add_u16(dest, vec::len(src) as u16); + dest += src; +} + +fn shape_of(ccx: crate_ctxt, t: ty::t, ty_param_map: [uint]) -> [u8] { + let s = []; + + alt ty::get(t).struct { + ty::ty_nil | ty::ty_bool | ty::ty_uint(ast::ty_u8) | + ty::ty_bot { s += [shape_u8]; } + ty::ty_int(ast::ty_i) { s += [s_int(ccx.tcx)]; } + ty::ty_float(ast::ty_f) { s += [s_float(ccx.tcx)]; } + ty::ty_uint(ast::ty_u) | ty::ty_ptr(_) { s += [s_uint(ccx.tcx)]; } + ty::ty_type { s += [s_tydesc(ccx.tcx)]; } + ty::ty_send_type { s += [s_send_tydesc(ccx.tcx)]; } + ty::ty_int(ast::ty_i8) { s += [shape_i8]; } + ty::ty_uint(ast::ty_u16) { s += [shape_u16]; } + ty::ty_int(ast::ty_i16) { s += [shape_i16]; } + ty::ty_uint(ast::ty_u32) { s += [shape_u32]; } + ty::ty_int(ast::ty_i32) | ty::ty_int(ast::ty_char) {s += [shape_i32];} + ty::ty_uint(ast::ty_u64) { s += [shape_u64]; } + ty::ty_int(ast::ty_i64) { s += [shape_i64]; } + ty::ty_float(ast::ty_f32) { s += [shape_f32]; } + ty::ty_float(ast::ty_f64) { s += [shape_f64]; } + ty::ty_str { + s += [shape_vec]; + add_bool(s, true); // type is POD + let unit_ty = ty::mk_mach_uint(ccx.tcx, ast::ty_u8); + add_substr(s, shape_of(ccx, unit_ty, ty_param_map)); + } + ty::ty_enum(did, tps) { + alt enum_kind(ccx, did) { + tk_unit { + // FIXME: For now we do this. + s += [s_variant_enum_t(ccx.tcx)]; + } + tk_enum { s += [s_variant_enum_t(ccx.tcx)]; } + tk_newtype | tk_complex { + s += [shape_enum]; + + let sub = []; + + let id; + alt ccx.shape_cx.tag_id_to_index.find(did) { + none { + id = ccx.shape_cx.next_tag_id; + ccx.shape_cx.tag_id_to_index.insert(did, id); + ccx.shape_cx.tag_order += [did]; + ccx.shape_cx.next_tag_id += 1u16; + } + some(existing_id) { id = existing_id; } + } + add_u16(sub, id as u16); + + add_u16(sub, vec::len(tps) as u16); + for tp: ty::t in tps { + let subshape = shape_of(ccx, tp, ty_param_map); + add_u16(sub, vec::len(subshape) as u16); + sub += subshape; + } + + s += sub; + } + } + } + ty::ty_box(_) | ty::ty_opaque_box { + s += [shape_box]; + } + ty::ty_uniq(mt) { + s += [shape_uniq]; + add_substr(s, shape_of(ccx, mt.ty, ty_param_map)); + } + ty::ty_vec(mt) { + s += [shape_vec]; + add_bool(s, ty::type_is_pod(ccx.tcx, mt.ty)); + add_substr(s, shape_of(ccx, mt.ty, ty_param_map)); + } + ty::ty_rec(fields) { + s += [shape_struct]; + let sub = []; + for f: field in fields { + sub += shape_of(ccx, f.mt.ty, ty_param_map); + } + add_substr(s, sub); + } + ty::ty_tup(elts) { + s += [shape_struct]; + let sub = []; + for elt in elts { + sub += shape_of(ccx, elt, ty_param_map); + } + add_substr(s, sub); + } + ty::ty_iface(_, _) { s += [shape_box_fn]; } + ty::ty_class(_, _) { s += [shape_class]; } + ty::ty_res(did, raw_subt, tps) { + let subt = ty::substitute_type_params(ccx.tcx, tps, raw_subt); + let ri = {did: did, t: subt}; + let id = interner::intern(ccx.shape_cx.resources, ri); + + s += [shape_res]; + add_u16(s, id as u16); + add_u16(s, vec::len(tps) as u16); + for tp: ty::t in tps { + add_substr(s, shape_of(ccx, tp, ty_param_map)); + } + add_substr(s, shape_of(ccx, subt, ty_param_map)); + + } + ty::ty_param(n, _) { + // Find the type parameter in the parameter list. + alt vec::position_elt(ty_param_map, n) { + some(i) { s += [shape_var, i as u8]; } + none { fail "ty param not found in ty_param_map"; } + } + } + ty::ty_fn({proto: ast::proto_box, _}) { + s += [shape_box_fn]; + } + ty::ty_fn({proto: ast::proto_uniq, _}) { + s += [shape_uniq_fn]; + } + ty::ty_fn({proto: ast::proto_block, _}) | + ty::ty_fn({proto: ast::proto_any, _}) { + s += [shape_stack_fn]; + } + ty::ty_fn({proto: ast::proto_bare, _}) { + s += [shape_bare_fn]; + } + ty::ty_opaque_closure_ptr(_) { + s += [shape_opaque_closure_ptr]; + } + ty::ty_constr(inner_t, _) { + s += shape_of(ccx, inner_t, ty_param_map); + } + ty::ty_var(_) | ty::ty_self(_) { + ccx.sess.bug("shape_of: unexpected type struct found"); + } + } + + ret s; +} + +// FIXME: We might discover other variants as we traverse these. Handle this. +fn shape_of_variant(ccx: crate_ctxt, v: ty::variant_info, + ty_param_count: uint) -> [u8] { + let ty_param_map = []; + let i = 0u; + while i < ty_param_count { ty_param_map += [i]; i += 1u; } + + let s = []; + for t: ty::t in v.args { s += shape_of(ccx, t, ty_param_map); } + ret s; +} + +fn gen_enum_shapes(ccx: crate_ctxt) -> ValueRef { + // Loop over all the enum variants and write their shapes into a + // data buffer. As we do this, it's possible for us to discover + // new enums, so we must do this first. + let i = 0u; + let data = []; + let offsets = []; + while i < vec::len(ccx.shape_cx.tag_order) { + let did = ccx.shape_cx.tag_order[i]; + let variants = ty::enum_variants(ccx.tcx, did); + let item_tyt = ty::lookup_item_type(ccx.tcx, did); + let ty_param_count = vec::len(*item_tyt.bounds); + + vec::iter(*variants) {|v| + offsets += [vec::len(data) as u16]; + + let variant_shape = shape_of_variant(ccx, v, ty_param_count); + add_substr(data, variant_shape); + + let zname = str::bytes(v.name) + [0u8]; + add_substr(data, zname); + } + + i += 1u; + } + + // Now calculate the sizes of the header space (which contains offsets to + // info records for each enum) and the info space (which contains offsets + // to each variant shape). As we do so, build up the header. + + let header = []; + let info = []; + let header_sz = 2u16 * ccx.shape_cx.next_tag_id; + let data_sz = vec::len(data) as u16; + + let info_sz = 0u16; + for did_: ast::def_id in ccx.shape_cx.tag_order { + let did = did_; // Satisfy alias checker. + let num_variants = vec::len(*ty::enum_variants(ccx.tcx, did)) as u16; + add_u16(header, header_sz + info_sz); + info_sz += 2u16 * (num_variants + 2u16) + 3u16; + } + + // Construct the info tables, which contain offsets to the shape of each + // variant. Also construct the largest-variant table for each enum, which + // contains the variants that the size-of operation needs to look at. + + let lv_table = []; + i = 0u; + for did_: ast::def_id in ccx.shape_cx.tag_order { + let did = did_; // Satisfy alias checker. + let variants = ty::enum_variants(ccx.tcx, did); + add_u16(info, vec::len(*variants) as u16); + + // Construct the largest-variants table. + add_u16(info, + header_sz + info_sz + data_sz + (vec::len(lv_table) as u16)); + + let lv = largest_variants(ccx, did); + add_u16(lv_table, vec::len(lv) as u16); + for v: uint in lv { add_u16(lv_table, v as u16); } + + // Determine whether the enum has dynamic size. + let dynamic = false; + for variant: ty::variant_info in *variants { + for typ: ty::t in variant.args { + if ty::type_has_dynamic_size(ccx.tcx, typ) { dynamic = true; } + } + } + + // If we can, write in the static size and alignment of the enum. + // Otherwise, write a placeholder. + let size_align; + if dynamic { + size_align = {size: 0u16, align: 0u8}; + } else { size_align = compute_static_enum_size(ccx, lv, did); } + add_u16(info, size_align.size); + info += [size_align.align]; + + // Now write in the offset of each variant. + for v: ty::variant_info in *variants { + add_u16(info, header_sz + info_sz + offsets[i]); + i += 1u; + } + } + + assert (i == vec::len(offsets)); + assert (header_sz == vec::len(header) as u16); + assert (info_sz == vec::len(info) as u16); + assert (data_sz == vec::len(data) as u16); + + header += info; + header += data; + header += lv_table; + + ret mk_global(ccx, "tag_shapes", C_bytes(header), true); +} + +fn gen_resource_shapes(ccx: crate_ctxt) -> ValueRef { + let dtors = []; + let i = 0u; + let len = interner::len(ccx.shape_cx.resources); + while i < len { + let ri = interner::get(ccx.shape_cx.resources, i); + dtors += [trans::common::get_res_dtor(ccx, ri.did, ri.t)]; + i += 1u; + } + + ret mk_global(ccx, "resource_shapes", C_struct(dtors), true); +} + +fn gen_shape_tables(ccx: crate_ctxt) { + let lltagstable = gen_enum_shapes(ccx); + let llresourcestable = gen_resource_shapes(ccx); + trans::common::set_struct_body(ccx.shape_cx.llshapetablesty, + [val_ty(lltagstable), + val_ty(llresourcestable)]); + + let lltables = + C_named_struct(ccx.shape_cx.llshapetablesty, + [lltagstable, llresourcestable]); + lib::llvm::llvm::LLVMSetInitializer(ccx.shape_cx.llshapetables, lltables); + lib::llvm::llvm::LLVMSetGlobalConstant(ccx.shape_cx.llshapetables, True); + lib::llvm::SetLinkage(ccx.shape_cx.llshapetables, + lib::llvm::InternalLinkage); +} + +// ______________________________________________________________________ +// compute sizeof / alignof + +type metrics = { + bcx: block, + sz: ValueRef, + align: ValueRef +}; + +type tag_metrics = { + bcx: block, + sz: ValueRef, + align: ValueRef, + payload_align: ValueRef +}; + +fn size_of(bcx: block, t: ty::t) -> result { + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, t) { + rslt(bcx, llsize_of(ccx, type_of::type_of(ccx, t))) + } else { + let { bcx, sz, align: _ } = dynamic_metrics(bcx, t); + rslt(bcx, sz) + } +} + +fn align_of(bcx: block, t: ty::t) -> result { + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, t) { + rslt(bcx, llalign_of(ccx, type_of::type_of(ccx, t))) + } else { + let { bcx, sz: _, align } = dynamic_metrics(bcx, t); + rslt(bcx, align) + } +} + +fn metrics(bcx: block, t: ty::t) -> metrics { + let ccx = bcx.ccx(); + if check type_has_static_size(ccx, t) { + let llty = type_of::type_of(ccx, t); + { bcx: bcx, sz: llsize_of(ccx, llty), align: llalign_of(ccx, llty) } + } else { + dynamic_metrics(bcx, t) + } +} + +// Returns the real size of the given type for the current target. +fn llsize_of_real(cx: crate_ctxt, t: TypeRef) -> uint { + ret llvm::LLVMStoreSizeOfType(cx.td.lltd, t) as uint; +} + +// Returns the real alignment of the given type for the current target. +fn llalign_of_real(cx: crate_ctxt, t: TypeRef) -> uint { + ret llvm::LLVMPreferredAlignmentOfType(cx.td.lltd, t) as uint; +} + +fn llsize_of(cx: crate_ctxt, t: TypeRef) -> ValueRef { + ret llvm::LLVMConstIntCast(lib::llvm::llvm::LLVMSizeOf(t), cx.int_type, + False); +} + +fn llalign_of(cx: crate_ctxt, t: TypeRef) -> ValueRef { + ret llvm::LLVMConstIntCast(lib::llvm::llvm::LLVMAlignOf(t), cx.int_type, + False); +} + +// Computes the static size of a enum, without using mk_tup(), which is +// bad for performance. +// +// FIXME: Migrate trans over to use this. + +// Computes the size of the data part of a non-dynamically-sized enum. +fn static_size_of_enum(cx: crate_ctxt, t: ty::t) -> uint { + if cx.enum_sizes.contains_key(t) { ret cx.enum_sizes.get(t); } + alt ty::get(t).struct { + ty::ty_enum(tid, subtys) { + // Compute max(variant sizes). + let max_size = 0u; + let variants = ty::enum_variants(cx.tcx, tid); + for variant: ty::variant_info in *variants { + let tup_ty = simplify_type(cx.tcx, + ty::mk_tup(cx.tcx, variant.args)); + // Perform any type parameter substitutions. + tup_ty = ty::substitute_type_params(cx.tcx, subtys, tup_ty); + // Here we possibly do a recursive call. + let this_size = + llsize_of_real(cx, type_of::type_of(cx, tup_ty)); + if max_size < this_size { max_size = this_size; } + } + cx.enum_sizes.insert(t, max_size); + ret max_size; + } + _ { cx.sess.bug("static_size_of_enum called on non-enum"); } + } +} + +fn dynamic_metrics(cx: block, t: ty::t) -> metrics { + fn align_elements(cx: block, elts: [ty::t]) -> metrics { + // + // C padding rules: + // + // + // - Pad after each element so that next element is aligned. + // - Pad after final structure member so that whole structure + // is aligned to max alignment of interior. + // + + let off = C_int(cx.ccx(), 0); + let max_align = C_int(cx.ccx(), 1); + let bcx = cx; + for e: ty::t in elts { + let elt_align = align_of(bcx, e); + bcx = elt_align.bcx; + let elt_size = size_of(bcx, e); + bcx = elt_size.bcx; + let aligned_off = align_to(bcx, off, elt_align.val); + off = Add(bcx, aligned_off, elt_size.val); + max_align = umax(bcx, max_align, elt_align.val); + } + off = align_to(bcx, off, max_align); + ret { bcx: bcx, sz: off, align: max_align }; + } + + alt ty::get(t).struct { + ty::ty_param(p, _) { + let {bcx, val: tydesc} = base::get_tydesc_simple(cx, t, false); + let szptr = GEPi(bcx, tydesc, [0, abi::tydesc_field_size]); + let aptr = GEPi(bcx, tydesc, [0, abi::tydesc_field_align]); + {bcx: bcx, sz: Load(bcx, szptr), align: Load(bcx, aptr)} + } + ty::ty_rec(flds) { + let tys: [ty::t] = []; + for f: ty::field in flds { tys += [f.mt.ty]; } + align_elements(cx, tys) + } + ty::ty_tup(elts) { + let tys = []; + for tp in elts { tys += [tp]; } + align_elements(cx, tys) + } + ty::ty_enum(tid, tps) { + let bcx = cx; + let ccx = bcx.ccx(); + + let compute_max_variant_size = fn@(bcx: block) -> result { + // Compute max(variant sizes). + let bcx = bcx; + let max_size: ValueRef = C_int(ccx, 0); + let variants = ty::enum_variants(bcx.tcx(), tid); + for variant: ty::variant_info in *variants { + // Perform type substitution on the raw argument types. + let tys = vec::map(variant.args) {|raw_ty| + ty::substitute_type_params(cx.tcx(), tps, raw_ty) + }; + let rslt = align_elements(bcx, tys); + bcx = rslt.bcx; + max_size = umax(bcx, rslt.sz, max_size); + } + rslt(bcx, max_size) + }; + + let {bcx, val: sz} = alt enum_kind(ccx, tid) { + tk_unit | tk_enum { rslt(bcx, llsize_of(ccx, T_enum_variant(ccx))) } + tk_newtype { compute_max_variant_size(bcx) } + tk_complex { + let {bcx, val} = compute_max_variant_size(bcx); + rslt(bcx, Add(bcx, val, llsize_of(ccx, T_enum_variant(ccx)))) + } + }; + + { bcx: bcx, sz: sz, align: C_int(ccx, 1) } + } + _ { + cx.tcx().sess.bug("dynamic_metrics: type has static size"); + } + } +} + +// Creates a simpler, size-equivalent type. The resulting type is guaranteed +// to have (a) the same size as the type that was passed in; (b) to be non- +// recursive. This is done by replacing all boxes in a type with boxed unit +// types. +// This should reduce all pointers to some simple pointer type, to +// ensure that we don't recurse endlessly when computing the size of a +// nominal type that has pointers to itself in it. +fn simplify_type(tcx: ty::ctxt, typ: ty::t) -> ty::t { + fn nilptr(tcx: ty::ctxt) -> ty::t { + ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx), mutbl: ast::m_imm}) + } + fn simplifier(tcx: ty::ctxt, typ: ty::t) -> ty::t { + alt ty::get(typ).struct { + ty::ty_box(_) | ty::ty_opaque_box | ty::ty_uniq(_) | ty::ty_vec(_) | + ty::ty_ptr(_) { nilptr(tcx) } + ty::ty_fn(_) { ty::mk_tup(tcx, [nilptr(tcx), nilptr(tcx)]) } + ty::ty_res(_, sub, tps) { + let sub1 = ty::substitute_type_params(tcx, tps, sub); + ty::mk_tup(tcx, [ty::mk_int(tcx), simplify_type(tcx, sub1)]) + } + _ { typ } + } + } + ty::fold_ty(tcx, ty::fm_general(bind simplifier(tcx, _)), typ) +} + +// Given a tag type `ty`, returns the offset of the payload. +//fn tag_payload_offs(bcx: block, tag_id: ast::def_id, tps: [ty::t]) +// -> ValueRef { +// alt tag_kind(tag_id) { +// tk_unit | tk_enum | tk_newtype { C_int(bcx.ccx(), 0) } +// tk_complex { +// compute_tag_metrics(tag_id, tps) +// } +// } +//} diff --git a/src/rustc/middle/trans/tvec.rs b/src/rustc/middle/trans/tvec.rs new file mode 100644 index 00000000000..aeaf65db7f4 --- /dev/null +++ b/src/rustc/middle/trans/tvec.rs @@ -0,0 +1,315 @@ +import syntax::ast; +import driver::session::session; +import lib::llvm::{ValueRef, TypeRef}; +import back::abi; +import base::{call_memmove, trans_shared_malloc, + INIT, copy_val, load_if_immediate, get_tydesc, + sub_block, do_spill_noroot, + dest}; +import shape::{llsize_of, size_of}; +import build::*; +import common::*; + +fn get_fill(bcx: block, vptr: ValueRef) -> ValueRef { + Load(bcx, GEPi(bcx, vptr, [0, abi::vec_elt_fill])) +} +fn get_dataptr(bcx: block, vptr: ValueRef, unit_ty: TypeRef) + -> ValueRef { + let ptr = GEPi(bcx, vptr, [0, abi::vec_elt_elems]); + PointerCast(bcx, ptr, T_ptr(unit_ty)) +} + +fn pointer_add(bcx: block, ptr: ValueRef, bytes: ValueRef) -> ValueRef { + let old_ty = val_ty(ptr); + let bptr = PointerCast(bcx, ptr, T_ptr(T_i8())); + ret PointerCast(bcx, InBoundsGEP(bcx, bptr, [bytes]), old_ty); +} + +fn alloc_raw(bcx: block, fill: ValueRef, alloc: ValueRef) -> result { + let ccx = bcx.ccx(); + let llvecty = ccx.opaque_vec_type; + let vecsize = Add(bcx, alloc, llsize_of(ccx, llvecty)); + let {bcx: bcx, val: vecptr} = + trans_shared_malloc(bcx, T_ptr(llvecty), vecsize); + Store(bcx, fill, GEPi(bcx, vecptr, [0, abi::vec_elt_fill])); + Store(bcx, alloc, GEPi(bcx, vecptr, [0, abi::vec_elt_alloc])); + ret {bcx: bcx, val: vecptr}; +} + +type alloc_result = + {bcx: block, + val: ValueRef, + unit_ty: ty::t, + llunitsz: ValueRef, + llunitty: TypeRef}; + +fn alloc(bcx: block, vec_ty: ty::t, elts: uint) -> alloc_result { + let ccx = bcx.ccx(); + let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + let llunitty = type_of::type_of_or_i8(ccx, unit_ty); + let llvecty = T_vec(ccx, llunitty); + let {bcx: bcx, val: unit_sz} = size_of(bcx, unit_ty); + + let fill = Mul(bcx, C_uint(ccx, elts), unit_sz); + let alloc = if elts < 4u { + Mul(bcx, C_int(ccx, 4), unit_sz) + } else { + fill + }; + let {bcx: bcx, val: vptr} = alloc_raw(bcx, fill, alloc); + let vptr = PointerCast(bcx, vptr, T_ptr(llvecty)); + + ret {bcx: bcx, + val: vptr, + unit_ty: unit_ty, + llunitsz: unit_sz, + llunitty: llunitty}; +} + +fn duplicate(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> result { + let ccx = bcx.ccx(); + let fill = get_fill(bcx, vptr); + let size = Add(bcx, fill, llsize_of(ccx, ccx.opaque_vec_type)); + let {bcx: bcx, val: newptr} = + trans_shared_malloc(bcx, val_ty(vptr), size); + let bcx = call_memmove(bcx, newptr, vptr, size).bcx; + let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + Store(bcx, fill, GEPi(bcx, newptr, [0, abi::vec_elt_alloc])); + if ty::type_needs_drop(bcx.tcx(), unit_ty) { + bcx = iter_vec(bcx, newptr, vec_ty, base::take_ty); + } + ret rslt(bcx, newptr); +} +fn make_free_glue(bcx: block, vptr: ValueRef, vec_ty: ty::t) -> + block { + let tcx = bcx.tcx(), unit_ty = ty::sequence_element_type(tcx, vec_ty); + base::with_cond(bcx, IsNotNull(bcx, vptr)) {|bcx| + let bcx = if ty::type_needs_drop(tcx, unit_ty) { + iter_vec(bcx, vptr, vec_ty, base::drop_ty) + } else { bcx }; + base::trans_shared_free(bcx, vptr) + } +} + +fn trans_vec(bcx: block, args: [@ast::expr], id: ast::node_id, + dest: dest) -> block { + let ccx = bcx.ccx(), bcx = bcx; + if dest == base::ignore { + for arg in args { + bcx = base::trans_expr(bcx, arg, base::ignore); + } + ret bcx; + } + let vec_ty = node_id_type(bcx, id); + let {bcx: bcx, + val: vptr, + llunitsz: llunitsz, + unit_ty: unit_ty, + llunitty: llunitty} = + alloc(bcx, vec_ty, args.len()); + + add_clean_free(bcx, vptr, true); + // Store the individual elements. + let dataptr = get_dataptr(bcx, vptr, llunitty); + let i = 0u, temp_cleanups = [vptr]; + for e in args { + let lleltptr = if ty::type_has_dynamic_size(bcx.tcx(), unit_ty) { + InBoundsGEP(bcx, dataptr, [Mul(bcx, C_uint(ccx, i), llunitsz)]) + } else { InBoundsGEP(bcx, dataptr, [C_uint(ccx, i)]) }; + bcx = base::trans_expr_save_in(bcx, e, lleltptr); + add_clean_temp_mem(bcx, lleltptr, unit_ty); + temp_cleanups += [lleltptr]; + i += 1u; + } + for cln in temp_cleanups { revoke_clean(bcx, cln); } + ret base::store_in_dest(bcx, vptr, dest); +} + +fn trans_str(bcx: block, s: str, dest: dest) -> block { + let veclen = str::len(s) + 1u; // +1 for \0 + let {bcx: bcx, val: sptr, _} = + alloc(bcx, ty::mk_str(bcx.tcx()), veclen); + + let ccx = bcx.ccx(); + let llcstr = C_cstr(ccx, s); + let bcx = call_memmove(bcx, get_dataptr(bcx, sptr, T_i8()), llcstr, + C_uint(ccx, veclen)).bcx; + ret base::store_in_dest(bcx, sptr, dest); +} + +fn trans_append(cx: block, vec_ty: ty::t, lhsptr: ValueRef, + rhs: ValueRef) -> block { + // Cast to opaque interior vector types if necessary. + let ccx = cx.ccx(); + let unit_ty = ty::sequence_element_type(cx.tcx(), vec_ty); + let dynamic = ty::type_has_dynamic_size(cx.tcx(), unit_ty); + let (lhsptr, rhs) = + if !dynamic { + (lhsptr, rhs) + } else { + (PointerCast(cx, lhsptr, T_ptr(T_ptr(ccx.opaque_vec_type))), + PointerCast(cx, rhs, T_ptr(ccx.opaque_vec_type))) + }; + let strings = alt check ty::get(vec_ty).struct { + ty::ty_str { true } + ty::ty_vec(_) { false } + }; + + let {bcx: bcx, val: unit_sz} = size_of(cx, unit_ty); + let llunitty = type_of::type_of_or_i8(ccx, unit_ty); + + let lhs = Load(bcx, lhsptr); + let self_append = ICmp(bcx, lib::llvm::IntEQ, lhs, rhs); + let lfill = get_fill(bcx, lhs); + let rfill = get_fill(bcx, rhs); + let new_fill = Add(bcx, lfill, rfill); + if strings { new_fill = Sub(bcx, new_fill, C_int(ccx, 1)); } + let opaque_lhs = PointerCast(bcx, lhsptr, + T_ptr(T_ptr(ccx.opaque_vec_type))); + Call(bcx, cx.ccx().upcalls.vec_grow, + [opaque_lhs, new_fill]); + // Was overwritten if we resized + let lhs = Load(bcx, lhsptr); + rhs = Select(bcx, self_append, lhs, rhs); + + let lhs_data = get_dataptr(bcx, lhs, llunitty); + let lhs_off = lfill; + if strings { lhs_off = Sub(bcx, lhs_off, C_int(ccx, 1)); } + let write_ptr = pointer_add(bcx, lhs_data, lhs_off); + let write_ptr_ptr = do_spill_noroot(bcx, write_ptr); + let bcx = iter_vec_raw(bcx, rhs, vec_ty, rfill, + // We have to increment by the dynamically-computed size. + {|bcx, addr, _ty| + let write_ptr = Load(bcx, write_ptr_ptr); + let bcx = + copy_val(bcx, INIT, write_ptr, + load_if_immediate(bcx, addr, unit_ty), + unit_ty); + let incr = if dynamic { + unit_sz + } else { + C_int(ccx, 1) + }; + Store(bcx, InBoundsGEP(bcx, write_ptr, [incr]), + write_ptr_ptr); + ret bcx; + }); + ret bcx; +} + +fn trans_append_literal(bcx: block, vptrptr: ValueRef, vec_ty: ty::t, + vals: [@ast::expr]) -> block { + let ccx = bcx.ccx(); + let elt_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + let ti = none; + let {bcx: bcx, val: td} = get_tydesc(bcx, elt_ty, false, ti); + base::lazily_emit_tydesc_glue(ccx, abi::tydesc_field_take_glue, ti); + let opaque_v = PointerCast(bcx, vptrptr, + T_ptr(T_ptr(ccx.opaque_vec_type))); + for val in vals { + let {bcx: e_bcx, val: elt} = base::trans_temp_expr(bcx, val); + bcx = e_bcx; + let r = base::spill_if_immediate(bcx, elt, elt_ty); + let spilled = r.val; + bcx = r.bcx; + Call(bcx, bcx.ccx().upcalls.vec_push, + [opaque_v, td, PointerCast(bcx, spilled, T_ptr(T_i8()))]); + } + ret bcx; +} + +fn trans_add(bcx: block, vec_ty: ty::t, lhs: ValueRef, + rhs: ValueRef, dest: dest) -> block { + let ccx = bcx.ccx(); + let strings = alt ty::get(vec_ty).struct { + ty::ty_str { true } + _ { false } + }; + let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + let llunitty = type_of::type_of_or_i8(ccx, unit_ty); + let {bcx: bcx, val: llunitsz} = size_of(bcx, unit_ty); + + let lhs_fill = get_fill(bcx, lhs); + if strings { lhs_fill = Sub(bcx, lhs_fill, C_int(ccx, 1)); } + let rhs_fill = get_fill(bcx, rhs); + let new_fill = Add(bcx, lhs_fill, rhs_fill); + let {bcx: bcx, val: new_vec_ptr} = alloc_raw(bcx, new_fill, new_fill); + new_vec_ptr = PointerCast(bcx, new_vec_ptr, T_ptr(T_vec(ccx, llunitty))); + + let write_ptr_ptr = do_spill_noroot + (bcx, get_dataptr(bcx, new_vec_ptr, llunitty)); + let copy_fn = fn@(bcx: block, addr: ValueRef, + _ty: ty::t) -> block { + let ccx = bcx.ccx(); + let write_ptr = Load(bcx, write_ptr_ptr); + let bcx = copy_val(bcx, INIT, write_ptr, + load_if_immediate(bcx, addr, unit_ty), unit_ty); + let incr = + if ty::type_has_dynamic_size(bcx.tcx(), unit_ty) { + llunitsz + } else { + C_int(ccx, 1) + }; + Store(bcx, InBoundsGEP(bcx, write_ptr, [incr]), + write_ptr_ptr); + ret bcx; + }; + + let bcx = iter_vec_raw(bcx, lhs, vec_ty, lhs_fill, copy_fn); + bcx = iter_vec_raw(bcx, rhs, vec_ty, rhs_fill, copy_fn); + ret base::store_in_dest(bcx, new_vec_ptr, dest); +} + +type val_and_ty_fn = fn@(block, ValueRef, ty::t) -> result; + +type iter_vec_block = fn(block, ValueRef, ty::t) -> block; + +fn iter_vec_raw(bcx: block, vptr: ValueRef, vec_ty: ty::t, + fill: ValueRef, f: iter_vec_block) -> block { + let ccx = bcx.ccx(); + let unit_ty = ty::sequence_element_type(bcx.tcx(), vec_ty); + let llunitty = type_of::type_of_or_i8(ccx, unit_ty); + let {bcx: bcx, val: unit_sz} = size_of(bcx, unit_ty); + let vptr = PointerCast(bcx, vptr, T_ptr(T_vec(ccx, llunitty))); + let data_ptr = get_dataptr(bcx, vptr, llunitty); + + // Calculate the last pointer address we want to handle. + // FIXME: Optimize this when the size of the unit type is statically + // known to not use pointer casts, which tend to confuse LLVM. + let data_end_ptr = pointer_add(bcx, data_ptr, fill); + + // Now perform the iteration. + let header_cx = sub_block(bcx, "iter_vec_loop_header"); + Br(bcx, header_cx.llbb); + let data_ptr = Phi(header_cx, val_ty(data_ptr), [data_ptr], [bcx.llbb]); + let not_yet_at_end = + ICmp(header_cx, lib::llvm::IntULT, data_ptr, data_end_ptr); + let body_cx = sub_block(header_cx, "iter_vec_loop_body"); + let next_cx = sub_block(header_cx, "iter_vec_next"); + CondBr(header_cx, not_yet_at_end, body_cx.llbb, next_cx.llbb); + body_cx = f(body_cx, data_ptr, unit_ty); + let increment = + if ty::type_has_dynamic_size(bcx.tcx(), unit_ty) { + unit_sz + } else { C_int(ccx, 1) }; + AddIncomingToPhi(data_ptr, InBoundsGEP(body_cx, data_ptr, [increment]), + body_cx.llbb); + Br(body_cx, header_cx.llbb); + ret next_cx; +} + +fn iter_vec(bcx: block, vptr: ValueRef, vec_ty: ty::t, + f: iter_vec_block) -> block { + let vptr = PointerCast(bcx, vptr, T_ptr(bcx.ccx().opaque_vec_type)); + ret iter_vec_raw(bcx, vptr, vec_ty, get_fill(bcx, vptr), f); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/trans/type_of.rs b/src/rustc/middle/trans/type_of.rs new file mode 100644 index 00000000000..f7095be55ff --- /dev/null +++ b/src/rustc/middle/trans/type_of.rs @@ -0,0 +1,145 @@ +import common::*; +import lib::llvm::{TypeRef}; +import syntax::ast; +import lib::llvm::llvm; + +fn type_of_explicit_args(cx: crate_ctxt, inputs: [ty::arg]) -> [TypeRef] { + vec::map(inputs) {|arg| + let arg_ty = arg.ty; + let llty = type_of(cx, arg_ty); + alt ty::resolved_mode(cx.tcx, arg.mode) { + ast::by_val { llty } + _ { T_ptr(llty) } + } + } +} + +fn type_of_fn(cx: crate_ctxt, inputs: [ty::arg], + output: ty::t, params: [ty::param_bounds]) -> TypeRef { + let atys: [TypeRef] = []; + + // Arg 0: Output pointer. + atys += [T_ptr(type_of(cx, output))]; + + // Arg 1: Environment + atys += [T_opaque_box_ptr(cx)]; + + // Args >2: ty params, if not acquired via capture... + for bounds in params { + atys += [T_ptr(cx.tydesc_type)]; + for bound in *bounds { + alt bound { + ty::bound_iface(_) { atys += [T_ptr(T_dict())]; } + _ {} + } + } + } + // ... then explicit args. + atys += type_of_explicit_args(cx, inputs); + ret T_fn(atys, llvm::LLVMVoidType()); +} + +// Given a function type and a count of ty params, construct an llvm type +fn type_of_fn_from_ty(cx: crate_ctxt, fty: ty::t, + param_bounds: [ty::param_bounds]) -> TypeRef { + type_of_fn(cx, ty::ty_fn_args(fty), ty::ty_fn_ret(fty), param_bounds) +} + +fn type_of(cx: crate_ctxt, t: ty::t) -> TypeRef { + assert !ty::type_has_vars(t); + // Check the cache. + + if cx.lltypes.contains_key(t) { ret cx.lltypes.get(t); } + let llty = alt ty::get(t).struct { + ty::ty_nil | ty::ty_bot { T_nil() } + ty::ty_bool { T_bool() } + ty::ty_int(t) { T_int_ty(cx, t) } + ty::ty_uint(t) { T_uint_ty(cx, t) } + ty::ty_float(t) { T_float_ty(cx, t) } + ty::ty_str { T_ptr(T_vec(cx, T_i8())) } + ty::ty_enum(did, _) { type_of_enum(cx, did, t) } + ty::ty_box(mt) { + let mt_ty = mt.ty; + T_ptr(T_box(cx, type_of(cx, mt_ty))) } + ty::ty_opaque_box { T_ptr(T_box(cx, T_i8())) } + ty::ty_uniq(mt) { + let mt_ty = mt.ty; + T_ptr(type_of(cx, mt_ty)) } + ty::ty_vec(mt) { + let mt_ty = mt.ty; + if ty::type_has_dynamic_size(cx.tcx, mt_ty) { + T_ptr(cx.opaque_vec_type) + } else { + T_ptr(T_vec(cx, type_of(cx, mt_ty))) } + } + ty::ty_ptr(mt) { + let mt_ty = mt.ty; + T_ptr(type_of(cx, mt_ty)) } + ty::ty_rec(fields) { + let tys: [TypeRef] = []; + for f: ty::field in fields { + let mt_ty = f.mt.ty; + tys += [type_of(cx, mt_ty)]; + } + T_struct(tys) + } + ty::ty_fn(_) { + T_fn_pair(cx, type_of_fn_from_ty(cx, t, [])) + } + ty::ty_iface(_, _) { T_opaque_iface(cx) } + ty::ty_res(_, sub, tps) { + let sub1 = ty::substitute_type_params(cx.tcx, tps, sub); + ret T_struct([T_i8(), type_of(cx, sub1)]); + } + ty::ty_param(_, _) { T_typaram(cx.tn) } + ty::ty_send_type | ty::ty_type { T_ptr(cx.tydesc_type) } + ty::ty_tup(elts) { + let tys = []; + for elt in elts { + tys += [type_of(cx, elt)]; + } + T_struct(tys) + } + ty::ty_opaque_closure_ptr(_) { T_opaque_box_ptr(cx) } + ty::ty_constr(subt,_) { type_of(cx, subt) } + + _ { fail "type_of not implemented for this kind of type"; } + }; + cx.lltypes.insert(t, llty); + ret llty; +} + +fn type_of_enum(cx: crate_ctxt, did: ast::def_id, t: ty::t) + -> TypeRef { + let degen = (*ty::enum_variants(cx.tcx, did)).len() == 1u; + if check type_has_static_size(cx, t) { + let size = shape::static_size_of_enum(cx, t); + if !degen { T_enum(cx, size) } + else if size == 0u { T_struct([T_enum_variant(cx)]) } + else { T_array(T_i8(), size) } + } + else { + if degen { T_struct([T_enum_variant(cx)]) } + else { T_opaque_enum(cx) } + } +} + +fn type_of_ty_param_bounds_and_ty + (ccx: crate_ctxt, tpt: ty::ty_param_bounds_and_ty) -> TypeRef { + let t = tpt.ty; + alt ty::get(t).struct { + ty::ty_fn(_) { + ret type_of_fn_from_ty(ccx, t, *tpt.bounds); + } + _ { + // fall through + } + } + type_of(ccx, t) +} + +fn type_of_or_i8(ccx: crate_ctxt, typ: ty::t) -> TypeRef { + if check type_has_static_size(ccx, typ) { + type_of(ccx, typ) + } else { T_i8() } +} diff --git a/src/rustc/middle/trans/uniq.rs b/src/rustc/middle/trans/uniq.rs new file mode 100644 index 00000000000..d6d32510858 --- /dev/null +++ b/src/rustc/middle/trans/uniq.rs @@ -0,0 +1,64 @@ +import syntax::ast; +import lib::llvm::ValueRef; +import common::*; +import build::*; +import base::*; +import shape::size_of; + +export trans_uniq, make_free_glue, autoderef, duplicate, alloc_uniq; + +fn trans_uniq(bcx: block, contents: @ast::expr, + node_id: ast::node_id, dest: dest) -> block { + let uniq_ty = node_id_type(bcx, node_id); + let {bcx, val: llptr} = alloc_uniq(bcx, uniq_ty); + add_clean_free(bcx, llptr, true); + bcx = trans_expr_save_in(bcx, contents, llptr); + revoke_clean(bcx, llptr); + ret store_in_dest(bcx, llptr, dest); +} + +fn alloc_uniq(cx: block, uniq_ty: ty::t) -> result { + let bcx = cx; + let contents_ty = content_ty(uniq_ty); + let r = size_of(bcx, contents_ty); + bcx = r.bcx; + let llsz = r.val; + + let llptrty = T_ptr(type_of::type_of(bcx.ccx(), contents_ty)); + + r = trans_shared_malloc(bcx, llptrty, llsz); + bcx = r.bcx; + let llptr = r.val; + + ret rslt(bcx, llptr); +} + +fn make_free_glue(bcx: block, vptr: ValueRef, t: ty::t) + -> block { + with_cond(bcx, IsNotNull(bcx, vptr)) {|bcx| + let bcx = drop_ty(bcx, vptr, content_ty(t)); + trans_shared_free(bcx, vptr) + } +} + +fn content_ty(t: ty::t) -> ty::t { + alt ty::get(t).struct { + ty::ty_uniq({ty: ct, _}) { ct } + _ { std::util::unreachable(); } + } +} + +fn autoderef(v: ValueRef, t: ty::t) -> {v: ValueRef, t: ty::t} { + let content_ty = content_ty(t); + ret {v: v, t: content_ty}; +} + +fn duplicate(bcx: block, v: ValueRef, t: ty::t) -> result { + let content_ty = content_ty(t); + let {bcx, val: llptr} = alloc_uniq(bcx, t); + + let src = load_if_immediate(bcx, v, content_ty); + let dst = llptr; + let bcx = copy_val(bcx, INIT, dst, src, content_ty); + ret rslt(bcx, dst); +} \ No newline at end of file diff --git a/src/rustc/middle/tstate/ann.rs b/src/rustc/middle/tstate/ann.rs new file mode 100644 index 00000000000..f3862e3d20f --- /dev/null +++ b/src/rustc/middle/tstate/ann.rs @@ -0,0 +1,253 @@ + +import tritv::*; + +type precond = t; + +/* 2 means "this constraint may or may not be true after execution" + 1 means "this constraint is definitely true after execution" + 0 means "this constraint is definitely false after execution" */ +type postcond = t; + + +/* 2 means "don't know about this constraint" + 1 means "this constraint is definitely true before entry" + 0 means "this constraint is definitely false on entry" */ +type prestate = t; + + +/* similar to postcond */ +type poststate = t; + + +/* 1 means "this variable is definitely initialized" + 0 means "don't know whether this variable is + initialized" */ + +/* + This says: this expression requires the constraints whose value is 1 in + <pre> to be true, and given the precondition, it guarantees that the + constraints in <post> whose values are 1 are true, and that the constraints + in <post> whose values are 0 are false. + */ + +/* named thus so as not to confuse with prestate and poststate */ +type pre_and_post = @{precondition: precond, postcondition: postcond}; + + +/* FIXME: once it's implemented: */ + +// : ((*.precondition).nbits == (*.postcondition).nbits); +type pre_and_post_state = {prestate: prestate, poststate: poststate}; + +type ts_ann = @{conditions: pre_and_post, states: pre_and_post_state}; + +fn true_precond(num_vars: uint) -> precond { be create_tritv(num_vars); } + +fn true_postcond(num_vars: uint) -> postcond { be true_precond(num_vars); } + +fn empty_prestate(num_vars: uint) -> prestate { be true_precond(num_vars); } + +fn empty_poststate(num_vars: uint) -> poststate { be true_precond(num_vars); } + +fn false_postcond(num_vars: uint) -> postcond { + let rslt = create_tritv(num_vars); + tritv_set_all(rslt); + ret rslt; +} + +fn empty_pre_post(num_vars: uint) -> pre_and_post { + ret @{precondition: empty_prestate(num_vars), + postcondition: empty_poststate(num_vars)}; +} + +fn empty_states(num_vars: uint) -> pre_and_post_state { + ret {prestate: true_precond(num_vars), + poststate: true_postcond(num_vars)}; +} + +fn empty_ann(num_vars: uint) -> ts_ann { + ret @{conditions: empty_pre_post(num_vars), + states: empty_states(num_vars)}; +} + +fn get_pre(&&p: pre_and_post) -> precond { ret p.precondition; } + +fn get_post(&&p: pre_and_post) -> postcond { ret p.postcondition; } + +fn difference(p1: precond, p2: precond) -> bool { + ret tritv_difference(p1, p2); +} + +fn union(p1: precond, p2: precond) -> bool { ret tritv_union(p1, p2); } + +fn intersect(p1: precond, p2: precond) -> bool { + ret tritv_intersect(p1, p2); +} + +fn pps_len(p: pre_and_post) -> uint { + // gratuitous check + + assert (p.precondition.nbits == p.postcondition.nbits); + ret p.precondition.nbits; +} + +fn require(i: uint, p: pre_and_post) { + // sets the ith bit in p's pre + tritv_set(i, p.precondition, ttrue); +} + +fn require_and_preserve(i: uint, p: pre_and_post) { + // sets the ith bit in p's pre and post + tritv_set(i, p.precondition, ttrue); + tritv_set(i, p.postcondition, ttrue); +} + +fn set_in_postcond(i: uint, p: pre_and_post) -> bool { + // sets the ith bit in p's post + ret set_in_postcond_(i, p.postcondition); +} + +fn set_in_postcond_(i: uint, p: postcond) -> bool { + let was_set = tritv_get(p, i); + tritv_set(i, p, ttrue); + ret was_set != ttrue; +} + +fn set_in_poststate(i: uint, s: pre_and_post_state) -> bool { + // sets the ith bit in p's post + ret set_in_poststate_(i, s.poststate); +} + +fn set_in_poststate_(i: uint, p: poststate) -> bool { + let was_set = tritv_get(p, i); + tritv_set(i, p, ttrue); + ret was_set != ttrue; + +} + +fn clear_in_poststate(i: uint, s: pre_and_post_state) -> bool { + // sets the ith bit in p's post + ret clear_in_poststate_(i, s.poststate); +} + +fn clear_in_poststate_(i: uint, s: poststate) -> bool { + let was_set = tritv_get(s, i); + tritv_set(i, s, tfalse); + ret was_set != tfalse; +} + +fn clear_in_prestate(i: uint, s: pre_and_post_state) -> bool { + // sets the ith bit in p's pre + ret clear_in_prestate_(i, s.prestate); +} + +fn clear_in_prestate_(i: uint, s: prestate) -> bool { + let was_set = tritv_get(s, i); + tritv_set(i, s, tfalse); + ret was_set != tfalse; +} + +fn clear_in_postcond(i: uint, s: pre_and_post) -> bool { + // sets the ith bit in p's post + let was_set = tritv_get(s.postcondition, i); + tritv_set(i, s.postcondition, tfalse); + ret was_set != tfalse; +} + +// Sets all the bits in a's precondition to equal the +// corresponding bit in p's precondition. +fn set_precondition(a: ts_ann, p: precond) { + tritv_copy(a.conditions.precondition, p); +} + + +// Sets all the bits in a's postcondition to equal the +// corresponding bit in p's postcondition. +fn set_postcondition(a: ts_ann, p: postcond) { + tritv_copy(a.conditions.postcondition, p); +} + + +// Sets all the bits in a's prestate to equal the +// corresponding bit in p's prestate. +fn set_prestate(a: ts_ann, p: prestate) -> bool { + ret tritv_copy(a.states.prestate, p); +} + + +// Sets all the bits in a's postcondition to equal the +// corresponding bit in p's postcondition. +fn set_poststate(a: ts_ann, p: poststate) -> bool { + ret tritv_copy(a.states.poststate, p); +} + + +// Set all the bits in p that are set in new +fn extend_prestate(p: prestate, new: poststate) -> bool { + ret tritv_union(p, new); +} + + +// Set all the bits in p that are set in new +fn extend_poststate(p: poststate, new: poststate) -> bool { + ret tritv_union(p, new); +} + +// Sets the given bit in p to "don't care" +// FIXME: is this correct? +fn relax_prestate(i: uint, p: prestate) -> bool { + let was_set = tritv_get(p, i); + tritv_set(i, p, dont_care); + ret was_set != dont_care; +} + +// Clears the given bit in p +fn relax_poststate(i: uint, p: poststate) -> bool { + ret relax_prestate(i, p); +} + +// Clears the given bit in p +fn relax_precond(i: uint, p: precond) { relax_prestate(i, p); } + +// Sets all the bits in p to "don't care" +fn clear(p: precond) { tritv_clear(p); } + +// Sets all the bits in p to true +fn set(p: precond) { tritv_set_all(p); } + +fn ann_precond(a: ts_ann) -> precond { ret a.conditions.precondition; } + +fn ann_prestate(a: ts_ann) -> prestate { ret a.states.prestate; } + +fn ann_poststate(a: ts_ann) -> poststate { ret a.states.poststate; } + +fn pp_clone(p: pre_and_post) -> pre_and_post { + ret @{precondition: clone(p.precondition), + postcondition: clone(p.postcondition)}; +} + +fn clone(p: prestate) -> prestate { ret tritv_clone(p); } + + +// returns true if a implies b +// that is, returns true except if for some bits c and d, +// c = 1 and d = either 0 or "don't know" +// FIXME: is this correct? +fn implies(a: t, b: t) -> bool { + let tmp = tritv_clone(b); + tritv_difference(tmp, a); + ret tritv_doesntcare(tmp); +} + +fn trit_str(t: trit) -> str { + alt t { dont_care { "?" } ttrue { "1" } tfalse { "0" } } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/annotate.rs b/src/rustc/middle/tstate/annotate.rs new file mode 100644 index 00000000000..d4271693112 --- /dev/null +++ b/src/rustc/middle/tstate/annotate.rs @@ -0,0 +1,75 @@ + +import syntax::ast::*; +import syntax::visit; +import syntax::codemap::span; +import util::common::{log_stmt}; +import aux::{num_constraints, get_fn_info, crate_ctxt, add_node}; +import ann::empty_ann; +import pat_util::pat_binding_ids; + +fn collect_ids_expr(e: @expr, rs: @mutable [node_id]) { *rs += [e.id]; } + +fn collect_ids_block(b: blk, rs: @mutable [node_id]) { *rs += [b.node.id]; } + +fn collect_ids_stmt(s: @stmt, rs: @mutable [node_id]) { + alt s.node { + stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) { + log(debug, "node_id " + int::str(id)); + log_stmt(*s); + *rs += [id]; + } + _ { } + } +} + +fn collect_ids_local(tcx: ty::ctxt, l: @local, rs: @mutable [node_id]) { + *rs += pat_binding_ids(tcx.def_map, l.node.pat); +} + +fn node_ids_in_fn(tcx: ty::ctxt, body: blk, rs: @mutable [node_id]) { + let collect_ids = + visit::mk_simple_visitor(@{visit_expr: bind collect_ids_expr(_, rs), + visit_block: bind collect_ids_block(_, rs), + visit_stmt: bind collect_ids_stmt(_, rs), + visit_local: + bind collect_ids_local(tcx, _, rs) + with *visit::default_simple_visitor()}); + collect_ids.visit_block(body, (), collect_ids); +} + +fn init_vecs(ccx: crate_ctxt, node_ids: [node_id], len: uint) { + for i: node_id in node_ids { + log(debug, int::str(i) + " |-> " + uint::str(len)); + add_node(ccx, i, empty_ann(len)); + } +} + +fn visit_fn(ccx: crate_ctxt, num_constraints: uint, body: blk) { + let node_ids: @mutable [node_id] = @mutable []; + node_ids_in_fn(ccx.tcx, body, node_ids); + let node_id_vec = *node_ids; + init_vecs(ccx, node_id_vec, num_constraints); +} + +fn annotate_in_fn(ccx: crate_ctxt, _fk: visit::fn_kind, _decl: fn_decl, + body: blk, _sp: span, id: node_id) { + let f_info = get_fn_info(ccx, id); + visit_fn(ccx, num_constraints(f_info), body); +} + +fn annotate_crate(ccx: crate_ctxt, crate: crate) { + let do_ann = + visit::mk_simple_visitor( + @{visit_fn: bind annotate_in_fn(ccx, _, _, _, _, _) + with *visit::default_simple_visitor()}); + visit::visit_crate(crate, (), do_ann); +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/auxiliary.rs b/src/rustc/middle/tstate/auxiliary.rs new file mode 100644 index 00000000000..df358a86004 --- /dev/null +++ b/src/rustc/middle/tstate/auxiliary.rs @@ -0,0 +1,1115 @@ +import option::*; +import pat_util::*; +import syntax::ast::*; +import syntax::ast_util::*; +import syntax::{visit, codemap}; +import codemap::span; +import std::map::{new_int_hash}; +import syntax::print::pprust::path_to_str; +import tstate::ann::{pre_and_post, pre_and_post_state, empty_ann, prestate, + poststate, precond, postcond, + set_prestate, set_poststate, set_in_poststate_, + extend_prestate, extend_poststate, set_precondition, + set_postcondition, ts_ann, + clear_in_postcond, + clear_in_poststate_}; +import tritv::*; +import bitvectors::promises_; +import driver::session::session; + +import syntax::print::pprust::{constr_args_to_str, lit_to_str}; + +// Used to communicate which operands should be invalidated +// to helper functions +enum oper_type { + oper_move, + oper_swap, + oper_assign, + oper_assign_op, + oper_pure, +} + +/* logging funs */ +fn def_id_to_str(d: def_id) -> str { + ret int::str(d.crate) + "," + int::str(d.node); +} + +fn comma_str(args: [@constr_arg_use]) -> str { + let rslt = ""; + let comma = false; + for a: @constr_arg_use in args { + if comma { rslt += ", "; } else { comma = true; } + alt a.node { + carg_base { rslt += "*"; } + carg_ident(i) { rslt += i.ident; } + carg_lit(l) { rslt += lit_to_str(l); } + } + } + ret rslt; +} + +fn constraint_to_str(tcx: ty::ctxt, c: sp_constr) -> str { + alt c.node { + ninit(id, i) { + ret #fmt("init(%s id=%d - arising from %s)", + i, id, codemap::span_to_str(c.span, tcx.sess.codemap)); + } + npred(p, _, args) { + ret #fmt("%s(%s) - arising from %s", + path_to_str(p), + comma_str(args), + codemap::span_to_str(c.span, tcx.sess.codemap)); + } + } +} + +fn tritv_to_str(fcx: fn_ctxt, v: tritv::t) -> str { + let s = ""; + let comma = false; + for p: norm_constraint in constraints(fcx) { + alt tritv_get(v, p.bit_num) { + dont_care { } + tt { + s += + if comma { ", " } else { comma = true; "" } + + if tt == tfalse { "!" } else { "" } + + constraint_to_str(fcx.ccx.tcx, p.c); + } + } + } + ret s; +} + +fn log_tritv(fcx: fn_ctxt, v: tritv::t) { + log(debug, tritv_to_str(fcx, v)); +} + +fn first_difference_string(fcx: fn_ctxt, expected: tritv::t, actual: tritv::t) + -> str { + let s: str = ""; + for c: norm_constraint in constraints(fcx) { + if tritv_get(expected, c.bit_num) == ttrue && + tritv_get(actual, c.bit_num) != ttrue { + ret constraint_to_str(fcx.ccx.tcx, c.c); + } + } + ret s; +} + +fn log_tritv_err(fcx: fn_ctxt, v: tritv::t) { + log(error, tritv_to_str(fcx, v)); +} + +fn tos(v: [uint]) -> str { + let rslt = ""; + for i: uint in v { + if i == 0u { + rslt += "0"; + } else if i == 1u { rslt += "1"; } else { rslt += "?"; } + } + ret rslt; +} + +fn log_cond(v: [uint]) { log(debug, tos(v)); } + +fn log_cond_err(v: [uint]) { log(error, tos(v)); } + +fn log_pp(pp: pre_and_post) { + let p1 = tritv::to_vec(pp.precondition); + let p2 = tritv::to_vec(pp.postcondition); + #debug("pre:"); + log_cond(p1); + #debug("post:"); + log_cond(p2); +} + +fn log_pp_err(pp: pre_and_post) { + let p1 = tritv::to_vec(pp.precondition); + let p2 = tritv::to_vec(pp.postcondition); + #error("pre:"); + log_cond_err(p1); + #error("post:"); + log_cond_err(p2); +} + +fn log_states(pp: pre_and_post_state) { + let p1 = tritv::to_vec(pp.prestate); + let p2 = tritv::to_vec(pp.poststate); + #debug("prestate:"); + log_cond(p1); + #debug("poststate:"); + log_cond(p2); +} + +fn log_states_err(pp: pre_and_post_state) { + let p1 = tritv::to_vec(pp.prestate); + let p2 = tritv::to_vec(pp.poststate); + #error("prestate:"); + log_cond_err(p1); + #error("poststate:"); + log_cond_err(p2); +} + +fn print_ident(i: ident) { log(debug, " " + i + " "); } + +fn print_idents(&idents: [ident]) { + if vec::len::<ident>(idents) == 0u { ret; } + log(debug, "an ident: " + vec::pop::<ident>(idents)); + print_idents(idents); +} + + +/* data structures */ + +/**********************************************************************/ + +/* Two different data structures represent constraints in different + contexts: constraint and norm_constraint. + +constraint gets used to record constraints in a table keyed by def_ids. +cinit constraints represent a single constraint, for the initialization +state of a variable; a cpred constraint, with a single operator and a +list of possible argument lists, could represent several constraints at +once. + +norm_constraint, in contrast, gets used when handling an instance +of a constraint rather than a definition of a constraint. It can +also be init or pred (ninit or npred), but the npred case just has +a single argument list. + +The representation of constraints, where multiple instances of the +same predicate are collapsed into one entry in the table, makes it +easier to look up a specific instance. + +Both types are in constrast with the constraint type defined in +syntax::ast, which is for predicate constraints only, and is what +gets generated by the parser. aux and ast share the same type +to represent predicate *arguments* however. This type +(constr_arg_general) is parameterized (see comments in syntax::ast). + +Both types store an ident and span, for error-logging purposes. +*/ +type pred_args_ = {args: [@constr_arg_use], bit_num: uint}; + +type pred_args = spanned<pred_args_>; + +// The attached node ID is the *defining* node ID +// for this local. +type constr_arg_use = spanned<constr_arg_general_<inst>>; + +enum constraint { + cinit(uint, span, ident), + + // FIXME: really only want it to be mutable during collect_locals. + // freeze it after that. + cpred(@path, @mutable [pred_args]), +} + +// An ninit variant has a node_id because it refers to a local var. +// An npred has a def_id since the definition of the typestate +// predicate need not be local. +// FIXME: would be nice to give both a def_id field, +// and give ninit a constraint saying it's local. +enum tsconstr { + ninit(node_id, ident), + npred(@path, def_id, [@constr_arg_use]), +} + +type sp_constr = spanned<tsconstr>; + +type norm_constraint = {bit_num: uint, c: sp_constr}; + +type constr_map = std::map::hashmap<def_id, constraint>; + +/* Contains stuff that has to be computed up front */ +/* For easy access, the fn_info stores two special constraints for each +function. i_return holds if all control paths in this function terminate +in either a return expression, or an appropriate tail expression. +i_diverge holds if all control paths in this function terminate in a fail +or diverging call. + +It might be tempting to use a single constraint C for both properties, +where C represents i_return and !C represents i_diverge. This is +inadvisable, because then the sense of the bit depends on context. If we're +inside a ! function, that reverses the sense of the bit: C would be +i_diverge and !C would be i_return. That's awkward, because we have to +pass extra context around to functions that shouldn't care. + +Okay, suppose C represents i_return and !C represents i_diverge, regardless +of context. Consider this code: + +if (foo) { ret; } else { fail; } + +C is true in the consequent and false in the alternative. What's T `join` +F, then? ? doesn't work, because this code should definitely-return if the +context is a returning function (and be definitely-rejected if the context +is a ! function). F doesn't work, because then the code gets incorrectly +rejected if the context is a returning function. T would work, but it +doesn't make sense for T `join` F to be T (consider init constraints, for +example).; + +So we need context. And so it seems clearer to just have separate +constraints. +*/ +type fn_info = + /* list, accumulated during pre/postcondition + computation, of all local variables that may be + used */ + // Doesn't seem to work without the @ -- bug + {constrs: constr_map, + num_constraints: uint, + cf: ret_style, + i_return: tsconstr, + i_diverge: tsconstr, + used_vars: @mutable [node_id]}; + +fn tsconstr_to_def_id(t: tsconstr) -> def_id { + alt t { ninit(id, _) { local_def(id) } npred(_, id, _) { id } } +} + +fn tsconstr_to_node_id(t: tsconstr) -> node_id { + alt t { + ninit(id, _) { id } + npred(_, id, _) { fail "tsconstr_to_node_id called on pred constraint" } + } +} + +/* mapping from node ID to typestate annotation */ +type node_ann_table = @mutable [mutable ts_ann]; + + +/* mapping from function name to fn_info map */ +type fn_info_map = std::map::hashmap<node_id, fn_info>; + +type fn_ctxt = + {enclosing: fn_info, id: node_id, name: ident, ccx: crate_ctxt}; + +type crate_ctxt = {tcx: ty::ctxt, node_anns: node_ann_table, fm: fn_info_map}; + +fn get_fn_info(ccx: crate_ctxt, id: node_id) -> fn_info { + assert (ccx.fm.contains_key(id)); + ret ccx.fm.get(id); +} + +fn add_node(ccx: crate_ctxt, i: node_id, a: ts_ann) { + let sz = vec::len(*ccx.node_anns); + if sz <= i as uint { + vec::grow(*ccx.node_anns, (i as uint) - sz + 1u, empty_ann(0u)); + } + ccx.node_anns[i] = a; +} + +fn get_ts_ann(ccx: crate_ctxt, i: node_id) -> option<ts_ann> { + if i as uint < vec::len(*ccx.node_anns) { + ret some::<ts_ann>(ccx.node_anns[i]); + } else { ret none::<ts_ann>; } +} + + +/********* utils ********/ +fn node_id_to_ts_ann(ccx: crate_ctxt, id: node_id) -> ts_ann { + alt get_ts_ann(ccx, id) { + none { + #error("node_id_to_ts_ann: no ts_ann for node_id %d", id); + fail; + } + some(tt) { ret tt; } + } +} + +fn node_id_to_poststate(ccx: crate_ctxt, id: node_id) -> poststate { + #debug("node_id_to_poststate"); + ret node_id_to_ts_ann(ccx, id).states.poststate; +} + +fn stmt_to_ann(ccx: crate_ctxt, s: stmt) -> ts_ann { + #debug("stmt_to_ann"); + alt s.node { + stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) { + ret node_id_to_ts_ann(ccx, id); + } + } +} + + +/* fails if e has no annotation */ +fn expr_states(ccx: crate_ctxt, e: @expr) -> pre_and_post_state { + #debug("expr_states"); + ret node_id_to_ts_ann(ccx, e.id).states; +} + + +/* fails if e has no annotation */ +fn expr_pp(ccx: crate_ctxt, e: @expr) -> pre_and_post { + #debug("expr_pp"); + ret node_id_to_ts_ann(ccx, e.id).conditions; +} + +fn stmt_pp(ccx: crate_ctxt, s: stmt) -> pre_and_post { + ret stmt_to_ann(ccx, s).conditions; +} + + +/* fails if b has no annotation */ +fn block_pp(ccx: crate_ctxt, b: blk) -> pre_and_post { + #debug("block_pp"); + ret node_id_to_ts_ann(ccx, b.node.id).conditions; +} + +fn clear_pp(pp: pre_and_post) { + ann::clear(pp.precondition); + ann::clear(pp.postcondition); +} + +fn clear_precond(ccx: crate_ctxt, id: node_id) { + let pp = node_id_to_ts_ann(ccx, id); + ann::clear(pp.conditions.precondition); +} + +fn block_states(ccx: crate_ctxt, b: blk) -> pre_and_post_state { + #debug("block_states"); + ret node_id_to_ts_ann(ccx, b.node.id).states; +} + +fn stmt_states(ccx: crate_ctxt, s: stmt) -> pre_and_post_state { + ret stmt_to_ann(ccx, s).states; +} + +fn expr_precond(ccx: crate_ctxt, e: @expr) -> precond { + ret expr_pp(ccx, e).precondition; +} + +fn expr_postcond(ccx: crate_ctxt, e: @expr) -> postcond { + ret expr_pp(ccx, e).postcondition; +} + +fn expr_prestate(ccx: crate_ctxt, e: @expr) -> prestate { + ret expr_states(ccx, e).prestate; +} + +fn expr_poststate(ccx: crate_ctxt, e: @expr) -> poststate { + ret expr_states(ccx, e).poststate; +} + +fn stmt_precond(ccx: crate_ctxt, s: stmt) -> precond { + ret stmt_pp(ccx, s).precondition; +} + +fn stmt_postcond(ccx: crate_ctxt, s: stmt) -> postcond { + ret stmt_pp(ccx, s).postcondition; +} + +fn states_to_poststate(ss: pre_and_post_state) -> poststate { + ret ss.poststate; +} + +fn stmt_prestate(ccx: crate_ctxt, s: stmt) -> prestate { + ret stmt_states(ccx, s).prestate; +} + +fn stmt_poststate(ccx: crate_ctxt, s: stmt) -> poststate { + ret stmt_states(ccx, s).poststate; +} + +fn block_precond(ccx: crate_ctxt, b: blk) -> precond { + ret block_pp(ccx, b).precondition; +} + +fn block_postcond(ccx: crate_ctxt, b: blk) -> postcond { + ret block_pp(ccx, b).postcondition; +} + +fn block_prestate(ccx: crate_ctxt, b: blk) -> prestate { + ret block_states(ccx, b).prestate; +} + +fn block_poststate(ccx: crate_ctxt, b: blk) -> poststate { + ret block_states(ccx, b).poststate; +} + +fn set_prestate_ann(ccx: crate_ctxt, id: node_id, pre: prestate) -> bool { + #debug("set_prestate_ann"); + ret set_prestate(node_id_to_ts_ann(ccx, id), pre); +} + +fn extend_prestate_ann(ccx: crate_ctxt, id: node_id, pre: prestate) -> bool { + #debug("extend_prestate_ann"); + ret extend_prestate(node_id_to_ts_ann(ccx, id).states.prestate, pre); +} + +fn set_poststate_ann(ccx: crate_ctxt, id: node_id, post: poststate) -> bool { + #debug("set_poststate_ann"); + ret set_poststate(node_id_to_ts_ann(ccx, id), post); +} + +fn extend_poststate_ann(ccx: crate_ctxt, id: node_id, post: poststate) -> + bool { + #debug("extend_poststate_ann"); + ret extend_poststate(node_id_to_ts_ann(ccx, id).states.poststate, post); +} + +fn set_pre_and_post(ccx: crate_ctxt, id: node_id, pre: precond, + post: postcond) { + #debug("set_pre_and_post"); + let tt = node_id_to_ts_ann(ccx, id); + set_precondition(tt, pre); + set_postcondition(tt, post); +} + +fn copy_pre_post(ccx: crate_ctxt, id: node_id, sub: @expr) { + #debug("set_pre_and_post"); + let p = expr_pp(ccx, sub); + copy_pre_post_(ccx, id, p.precondition, p.postcondition); +} + +fn copy_pre_post_(ccx: crate_ctxt, id: node_id, pre: prestate, + post: poststate) { + #debug("set_pre_and_post"); + let tt = node_id_to_ts_ann(ccx, id); + set_precondition(tt, pre); + set_postcondition(tt, post); +} + +/* sets all bits to *1* */ +fn set_postcond_false(ccx: crate_ctxt, id: node_id) { + let p = node_id_to_ts_ann(ccx, id); + ann::set(p.conditions.postcondition); +} + +fn pure_exp(ccx: crate_ctxt, id: node_id, p: prestate) -> bool { + ret set_prestate_ann(ccx, id, p) | set_poststate_ann(ccx, id, p); +} + +fn num_constraints(m: fn_info) -> uint { ret m.num_constraints; } + +fn new_crate_ctxt(cx: ty::ctxt) -> crate_ctxt { + let na: [mutable ts_ann] = [mutable]; + ret {tcx: cx, node_anns: @mutable na, fm: new_int_hash::<fn_info>()}; +} + +/* Use e's type to determine whether it returns. + If it has a function type with a ! annotation, +the answer is noreturn. */ +fn controlflow_expr(ccx: crate_ctxt, e: @expr) -> ret_style { + alt ty::get(ty::node_id_to_type(ccx.tcx, e.id)).struct { + ty::ty_fn(f) { ret f.ret_style; } + _ { ret return_val; } + } +} + +fn constraints_expr(cx: ty::ctxt, e: @expr) -> [@ty::constr] { + alt ty::get(ty::node_id_to_type(cx, e.id)).struct { + ty::ty_fn(f) { ret f.constraints; } + _ { ret []; } + } +} + +fn node_id_to_def_strict(cx: ty::ctxt, id: node_id) -> def { + alt cx.def_map.find(id) { + none { + #error("node_id_to_def: node_id %d has no def", id); + fail; + } + some(d) { ret d; } + } +} + +fn node_id_to_def(ccx: crate_ctxt, id: node_id) -> option<def> { + ret ccx.tcx.def_map.find(id); +} + +fn norm_a_constraint(id: def_id, c: constraint) -> [norm_constraint] { + alt c { + cinit(n, sp, i) { + ret [{bit_num: n, c: respan(sp, ninit(id.node, i))}]; + } + cpred(p, descs) { + let rslt: [norm_constraint] = []; + for pd: pred_args in *descs { + rslt += + [{bit_num: pd.node.bit_num, + c: respan(pd.span, npred(p, id, pd.node.args))}]; + } + ret rslt; + } + } +} + + +// Tried to write this as an iterator, but I got a +// non-exhaustive match in trans. +fn constraints(fcx: fn_ctxt) -> [norm_constraint] { + let rslt: [norm_constraint] = []; + fcx.enclosing.constrs.items {|key, val| + rslt += norm_a_constraint(key, val); + }; + ret rslt; +} + +// FIXME +// Would rather take an immutable vec as an argument, +// should freeze it at some earlier point. +fn match_args(fcx: fn_ctxt, occs: @mutable [pred_args], + occ: [@constr_arg_use]) -> uint { + #debug("match_args: looking at %s", + constr_args_to_str(fn@(i: inst) -> str { ret i.ident; }, occ)); + for pd: pred_args in *occs { + log(debug, + "match_args: candidate " + pred_args_to_str(pd)); + fn eq(p: inst, q: inst) -> bool { ret p.node == q.node; } + if ty::args_eq(eq, pd.node.args, occ) { ret pd.node.bit_num; } + } + fcx.ccx.tcx.sess.bug("match_args: no match for occurring args"); +} + +fn def_id_for_constr(tcx: ty::ctxt, t: node_id) -> def_id { + alt tcx.def_map.find(t) { + none { + tcx.sess.bug("node_id_for_constr: bad node_id " + int::str(t)); + } + some(def_fn(i, _)) { ret i; } + _ { tcx.sess.bug("node_id_for_constr: pred is not a function"); } + } +} + +fn expr_to_constr_arg(tcx: ty::ctxt, e: @expr) -> @constr_arg_use { + alt e.node { + expr_path(p) { + alt tcx.def_map.find(e.id) { + some(def_local(nid, _)) | some(def_arg(nid, _)) | + some(def_binding(nid)) | some(def_upvar(nid, _, _)) { + ret @respan(p.span, + carg_ident({ident: p.node.idents[0], node: nid})); + } + some(what) { + tcx.sess.span_bug(e.span, + #fmt("exprs_to_constr_args: non-local variable %? \ + as pred arg", what)); + } + none { + tcx.sess.span_bug(e.span, + "exprs_to_constr_args: unbound id as pred arg"); + + } + } + } + expr_lit(l) { ret @respan(e.span, carg_lit(l)); } + _ { + tcx.sess.span_fatal(e.span, + "Arguments to constrained functions must be " + + "literals or local variables"); + } + } +} + +fn exprs_to_constr_args(tcx: ty::ctxt, args: [@expr]) -> [@constr_arg_use] { + let f = bind expr_to_constr_arg(tcx, _); + let rslt: [@constr_arg_use] = []; + for e: @expr in args { rslt += [f(e)]; } + rslt +} + +fn expr_to_constr(tcx: ty::ctxt, e: @expr) -> sp_constr { + alt e.node { + expr_call(operator, args, _) { + alt operator.node { + expr_path(p) { + ret respan(e.span, + npred(p, def_id_for_constr(tcx, operator.id), + exprs_to_constr_args(tcx, args))); + } + _ { + tcx.sess.span_fatal(operator.span, + "Internal error: " + + " ill-formed operator \ + in predicate"); + } + } + } + _ { + tcx.sess.span_fatal(e.span, + "Internal error: " + " ill-formed predicate"); + } + } +} + +fn pred_args_to_str(p: pred_args) -> str { + "<" + uint::str(p.node.bit_num) + ", " + + constr_args_to_str(fn@(i: inst) -> str { ret i.ident; }, p.node.args) + + ">" +} + +fn substitute_constr_args(cx: ty::ctxt, actuals: [@expr], c: @ty::constr) -> + tsconstr { + let rslt: [@constr_arg_use] = []; + for a: @constr_arg in c.node.args { + rslt += [substitute_arg(cx, actuals, a)]; + } + ret npred(c.node.path, c.node.id, rslt); +} + +fn substitute_arg(cx: ty::ctxt, actuals: [@expr], a: @constr_arg) -> + @constr_arg_use { + let num_actuals = vec::len(actuals); + alt a.node { + carg_ident(i) { + if i < num_actuals { + ret expr_to_constr_arg(cx, actuals[i]); + } else { + cx.sess.span_fatal(a.span, "Constraint argument out of bounds"); + } + } + carg_base { ret @respan(a.span, carg_base); } + carg_lit(l) { ret @respan(a.span, carg_lit(l)); } + } +} + +fn pred_args_matches(pattern: [constr_arg_general_<inst>], desc: pred_args) -> + bool { + let i = 0u; + for c: @constr_arg_use in desc.node.args { + let n = pattern[i]; + alt c.node { + carg_ident(p) { + alt n { + carg_ident(q) { if p.node != q.node { ret false; } } + _ { ret false; } + } + } + carg_base { if n != carg_base { ret false; } } + carg_lit(l) { + alt n { + carg_lit(m) { if !lit_eq(l, m) { ret false; } } + _ { ret false; } + } + } + } + i += 1u; + } + ret true; +} + +fn find_instance_(pattern: [constr_arg_general_<inst>], descs: [pred_args]) -> + option<uint> { + for d: pred_args in descs { + if pred_args_matches(pattern, d) { ret some(d.node.bit_num); } + } + ret none; +} + +type inst = {ident: ident, node: node_id}; +type subst = [{from: inst, to: inst}]; + +fn find_instances(_fcx: fn_ctxt, subst: subst, c: constraint) -> + [{from: uint, to: uint}] { + + let rslt = []; + if vec::len(subst) == 0u { ret rslt; } + + alt c { + cinit(_, _, _) {/* this is dealt with separately */ } + cpred(p, descs) { + for d: pred_args in *descs { + if args_mention(d.node.args, find_in_subst_bool, subst) { + let old_bit_num = d.node.bit_num; + let new = replace(subst, d); + alt find_instance_(new, *descs) { + some(d1) { rslt += [{from: old_bit_num, to: d1}]; } + _ { } + } + } + } + } + } + rslt +} + +fn find_in_subst(id: node_id, s: subst) -> option<inst> { + for p: {from: inst, to: inst} in s { + if id == p.from.node { ret some(p.to); } + } + ret none; +} + +fn find_in_subst_bool(s: subst, id: node_id) -> bool { + is_some(find_in_subst(id, s)) +} + +fn insts_to_str(stuff: [constr_arg_general_<inst>]) -> str { + let rslt = "<"; + for i: constr_arg_general_<inst> in stuff { + rslt += + " " + + alt i { + carg_ident(p) { p.ident } + carg_base { "*" } + carg_lit(_) { "[lit]" } + } + " "; + } + rslt += ">"; + rslt +} + +fn replace(subst: subst, d: pred_args) -> [constr_arg_general_<inst>] { + let rslt: [constr_arg_general_<inst>] = []; + for c: @constr_arg_use in d.node.args { + alt c.node { + carg_ident(p) { + alt find_in_subst(p.node, subst) { + some(new) { rslt += [carg_ident(new)]; } + _ { rslt += [c.node]; } + } + } + _ { + // #error("##"); + rslt += [c.node]; + } + } + } + + /* + for (constr_arg_general_<tup(ident, def_id)> p in rslt) { + alt (p) { + case (carg_ident(?p)) { + log(error, p._0); + } + case (_) {} + } + } + */ + + ret rslt; +} + +enum if_ty { if_check, plain_if, } + +fn local_node_id_to_def_id_strict(fcx: fn_ctxt, sp: span, i: node_id) -> + def_id { + alt local_node_id_to_def(fcx, i) { + some(def_local(nid, _)) | some(def_arg(nid, _)) | + some(def_upvar(nid, _, _)) { + ret local_def(nid); + } + some(_) { + fcx.ccx.tcx.sess.span_fatal(sp, + "local_node_id_to_def_id: id \ + isn't a local"); + } + none { + // should really be bug. span_bug()? + fcx.ccx.tcx.sess.span_fatal(sp, + "local_node_id_to_def_id: id \ + is unbound"); + } + } +} + +fn local_node_id_to_def(fcx: fn_ctxt, i: node_id) -> option<def> { + fcx.ccx.tcx.def_map.find(i) +} + +fn local_node_id_to_def_id(fcx: fn_ctxt, i: node_id) -> option<def_id> { + alt local_node_id_to_def(fcx, i) { + some(def_local(nid, _)) | some(def_arg(nid, _)) | + some(def_binding(nid)) | some(def_upvar(nid, _, _)) { + some(local_def(nid)) + } + _ { none } + } +} + +fn local_node_id_to_local_def_id(fcx: fn_ctxt, i: node_id) -> + option<node_id> { + alt local_node_id_to_def_id(fcx, i) { + some(did) { some(did.node) } + _ { none } + } +} + +fn copy_in_postcond(fcx: fn_ctxt, parent_exp: node_id, dest: inst, src: inst, + ty: oper_type) { + let post = + node_id_to_ts_ann(fcx.ccx, parent_exp).conditions.postcondition; + copy_in_poststate_two(fcx, post, post, dest, src, ty); +} + +// FIXME refactor +fn copy_in_poststate(fcx: fn_ctxt, post: poststate, dest: inst, src: inst, + ty: oper_type) { + copy_in_poststate_two(fcx, post, post, dest, src, ty); +} + +// In target_post, set the bits corresponding to copies of any +// constraints mentioning src that are set in src_post, with +// dest substituted for src. +// (This doesn't create any new constraints. If a new, substituted +// constraint isn't already in the bit vector, it's ignored.) +fn copy_in_poststate_two(fcx: fn_ctxt, src_post: poststate, + target_post: poststate, dest: inst, src: inst, + ty: oper_type) { + let subst; + alt ty { + oper_swap { subst = [{from: dest, to: src}, {from: src, to: dest}]; } + oper_assign_op { + ret; // Don't do any propagation + } + _ { subst = [{from: src, to: dest}]; } + } + + + fcx.enclosing.constrs.values {|val| + // replace any occurrences of the src def_id with the + // dest def_id + let insts = find_instances(fcx, subst, val); + for p: {from: uint, to: uint} in insts { + if promises_(p.from, src_post) { + set_in_poststate_(p.to, target_post); + } + } + }; +} + +/* FIXME should refactor this better */ +fn forget_in_postcond(fcx: fn_ctxt, parent_exp: node_id, dead_v: node_id) { + // In the postcondition given by parent_exp, clear the bits + // for any constraints mentioning dead_v + let d = local_node_id_to_local_def_id(fcx, dead_v); + alt d { + some(d_id) { + for c: norm_constraint in constraints(fcx) { + if constraint_mentions(fcx, c, d_id) { + #debug("clearing constraint %u %s", + c.bit_num, + constraint_to_str(fcx.ccx.tcx, c.c)); + clear_in_postcond(c.bit_num, + node_id_to_ts_ann(fcx.ccx, + parent_exp).conditions); + } + } + } + _ { } + } +} + +fn forget_in_postcond_still_init(fcx: fn_ctxt, parent_exp: node_id, + dead_v: node_id) { + // In the postcondition given by parent_exp, clear the bits + // for any constraints mentioning dead_v + let d = local_node_id_to_local_def_id(fcx, dead_v); + alt d { + some(d_id) { + for c: norm_constraint in constraints(fcx) { + if non_init_constraint_mentions(fcx, c, d_id) { + clear_in_postcond(c.bit_num, + node_id_to_ts_ann(fcx.ccx, + parent_exp).conditions); + } + } + } + _ { } + } +} + +fn forget_in_poststate(fcx: fn_ctxt, p: poststate, dead_v: node_id) -> bool { + // In the poststate given by parent_exp, clear the bits + // for any constraints mentioning dead_v + let d = local_node_id_to_local_def_id(fcx, dead_v); + let changed = false; + alt d { + some(d_id) { + for c: norm_constraint in constraints(fcx) { + if constraint_mentions(fcx, c, d_id) { + changed |= clear_in_poststate_(c.bit_num, p); + } + } + } + _ { } + } + ret changed; +} + +fn forget_in_poststate_still_init(fcx: fn_ctxt, p: poststate, dead_v: node_id) + -> bool { + // In the poststate given by parent_exp, clear the bits + // for any constraints mentioning dead_v + let d = local_node_id_to_local_def_id(fcx, dead_v); + let changed = false; + alt d { + some(d_id) { + for c: norm_constraint in constraints(fcx) { + if non_init_constraint_mentions(fcx, c, d_id) { + changed |= clear_in_poststate_(c.bit_num, p); + } + } + } + _ { } + } + ret changed; +} + +fn any_eq(v: [node_id], d: node_id) -> bool { + for i: node_id in v { if i == d { ret true; } } + false +} + +fn constraint_mentions(_fcx: fn_ctxt, c: norm_constraint, v: node_id) -> + bool { + ret alt c.c.node { + ninit(id, _) { v == id } + npred(_, _, args) { args_mention(args, any_eq, [v]) } + }; +} + +fn non_init_constraint_mentions(_fcx: fn_ctxt, c: norm_constraint, v: node_id) + -> bool { + ret alt c.c.node { + ninit(_, _) { false } + npred(_, _, args) { args_mention(args, any_eq, [v]) } + }; +} + +fn args_mention<T>(args: [@constr_arg_use], + q: fn([T], node_id) -> bool, + s: [T]) -> bool { + /* + FIXME + The following version causes an assertion in trans to fail + (something about type_is_tup_like) + fn mentions<T>(&[T] s, &fn(&[T], def_id) -> bool q, + &@constr_arg_use a) -> bool { + alt (a.node) { + case (carg_ident(?p1)) { + auto res = q(s, p1._1); + log(error, (res)); + res + } + case (_) { false } + } + } + ret vec::any(bind mentions(s,q,_), args); + */ + + for a: @constr_arg_use in args { + alt a.node { carg_ident(p1) { if q(s, p1.node) { ret true; } } _ { } } + } + ret false; +} + +fn use_var(fcx: fn_ctxt, v: node_id) { *fcx.enclosing.used_vars += [v]; } + +// FIXME: This should be a function in vec::. +fn vec_contains(v: @mutable [node_id], i: node_id) -> bool { + for d: node_id in *v { if d == i { ret true; } } + ret false; +} + +fn op_to_oper_ty(io: init_op) -> oper_type { + alt io { init_move { oper_move } _ { oper_assign } } +} + +// default function visitor +fn do_nothing<T>(_fk: visit::fn_kind, _decl: fn_decl, _body: blk, + _sp: span, _id: node_id, + _t: T, _v: visit::vt<T>) { +} + + +fn args_to_constr_args(tcx: ty::ctxt, args: [arg], + indices: [@sp_constr_arg<uint>]) -> [@constr_arg_use] { + let actuals: [@constr_arg_use] = []; + let num_args = vec::len(args); + for a: @sp_constr_arg<uint> in indices { + actuals += + [@respan(a.span, + alt a.node { + carg_base { carg_base } + carg_ident(i) { + if i < num_args { + carg_ident({ident: args[i].ident, + node: args[i].id}) + } else { + tcx.sess.span_bug(a.span, + "Index out of bounds in \ + constraint arg"); + } + } + carg_lit(l) { carg_lit(l) } + })]; + } + ret actuals; +} + +fn ast_constr_to_ts_constr(tcx: ty::ctxt, args: [arg], c: @constr) -> + tsconstr { + let tconstr = ty::ast_constr_to_constr(tcx, c); + ret npred(tconstr.node.path, tconstr.node.id, + args_to_constr_args(tcx, args, tconstr.node.args)); +} + +fn ast_constr_to_sp_constr(tcx: ty::ctxt, args: [arg], c: @constr) -> + sp_constr { + let tconstr = ast_constr_to_ts_constr(tcx, args, c); + ret respan(c.span, tconstr); +} + +type binding = {lhs: [inst], rhs: option<initializer>}; + +fn local_to_bindings(tcx: ty::ctxt, loc: @local) -> binding { + let lhs = []; + pat_bindings(tcx.def_map, loc.node.pat) {|p_id, _s, name| + lhs += [{ident: path_to_ident(name), node: p_id}]; + }; + {lhs: lhs, rhs: loc.node.init} +} + +fn locals_to_bindings(tcx: ty::ctxt, locals: [@local]) -> [binding] { + let rslt = []; + for loc in locals { rslt += [local_to_bindings(tcx, loc)]; } + ret rslt; +} + +fn callee_modes(fcx: fn_ctxt, callee: node_id) -> [mode] { + let ty = ty::type_autoderef(fcx.ccx.tcx, + ty::node_id_to_type(fcx.ccx.tcx, callee)); + alt ty::get(ty).struct { + ty::ty_fn({inputs: args, _}) { + let modes = []; + for arg: ty::arg in args { modes += [arg.mode]; } + ret modes; + } + _ { + // Shouldn't happen; callee should be ty_fn. + fcx.ccx.tcx.sess.bug("non-fn callee type in callee_modes: " + + util::ppaux::ty_to_str(fcx.ccx.tcx, ty)); + } + } +} + +fn callee_arg_init_ops(fcx: fn_ctxt, callee: node_id) -> [init_op] { + vec::map(callee_modes(fcx, callee)) {|m| + alt ty::resolved_mode(fcx.ccx.tcx, m) { + by_move { init_move } + by_copy | by_ref | by_val | by_mutbl_ref { init_assign } + } + } +} + +fn anon_bindings(ops: [init_op], es: [@expr]) -> [binding] { + let bindings: [binding] = []; + let i = 0; + for op: init_op in ops { + bindings += [{lhs: [], rhs: some({op: op, expr: es[i]})}]; + i += 1; + } + ret bindings; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/bitvectors.rs b/src/rustc/middle/tstate/bitvectors.rs new file mode 100644 index 00000000000..6acb23d906a --- /dev/null +++ b/src/rustc/middle/tstate/bitvectors.rs @@ -0,0 +1,242 @@ +import syntax::ast::*; +import syntax::visit; +import option::*; +import aux::*; +import tstate::ann::{pre_and_post, precond, postcond, prestate, poststate, + relax_prestate, relax_precond, relax_poststate, + pps_len, true_precond, + difference, union, clone, + set_in_postcond, set_in_poststate, set_in_poststate_, + clear_in_poststate, clear_in_prestate, + clear_in_poststate_}; +import tritv::*; +import util::common::*; +import driver::session::session; + +fn bit_num(fcx: fn_ctxt, c: tsconstr) -> uint { + let d = tsconstr_to_def_id(c); + assert (fcx.enclosing.constrs.contains_key(d)); + let rslt = fcx.enclosing.constrs.get(d); + alt c { + ninit(_, _) { + alt rslt { + cinit(n, _, _) { ret n; } + _ { + fcx.ccx.tcx.sess.bug("bit_num: asked for init constraint," + + " found a pred constraint"); + } + } + } + npred(_, _, args) { + alt rslt { + cpred(_, descs) { ret match_args(fcx, descs, args); } + _ { + fcx.ccx.tcx.sess.bug("bit_num: asked for pred constraint," + + " found an init constraint"); + } + } + } + } +} + +fn promises(fcx: fn_ctxt, p: poststate, c: tsconstr) -> bool { + ret promises_(bit_num(fcx, c), p); +} + +fn promises_(n: uint, p: poststate) -> bool { ret tritv_get(p, n) == ttrue; } + +// v "happens after" u +fn seq_trit(u: trit, v: trit) -> trit { + alt v { ttrue { ttrue } tfalse { tfalse } dont_care { u } } +} + +// idea: q "happens after" p -- so if something is +// 1 in q and 0 in p, it's 1 in the result; however, +// if it's 0 in q and 1 in p, it's 0 in the result +fn seq_tritv(p: postcond, q: postcond) { + let i = 0u; + assert (p.nbits == q.nbits); + while i < p.nbits { + tritv_set(i, p, seq_trit(tritv_get(p, i), tritv_get(q, i))); + i += 1u; + } +} + +fn seq_postconds(fcx: fn_ctxt, ps: [postcond]) -> postcond { + let sz = vec::len(ps); + if sz >= 1u { + let prev = tritv_clone(ps[0]); + for p: postcond in vec::slice(ps, 1u, sz) { seq_tritv(prev, p); } + ret prev; + } else { ret ann::empty_poststate(num_constraints(fcx.enclosing)); } +} + +// Given a list of pres and posts for exprs e0 ... en, +// return the precondition for evaluating each expr in order. +// So, if e0's post is {x} and e1's pre is {x, y, z}, the entire +// precondition shouldn't include x. +fn seq_preconds(fcx: fn_ctxt, pps: [pre_and_post]) -> precond { + let sz: uint = vec::len(pps); + let num_vars: uint = num_constraints(fcx.enclosing); + + fn seq_preconds_go(fcx: fn_ctxt, pps: [pre_and_post], first: pre_and_post) + -> precond { + let sz: uint = vec::len(pps); + if sz >= 1u { + let second = pps[0]; + assert (pps_len(second) == num_constraints(fcx.enclosing)); + let second_pre = clone(second.precondition); + difference(second_pre, first.postcondition); + let next_first = clone(first.precondition); + union(next_first, second_pre); + let next_first_post = clone(first.postcondition); + seq_tritv(next_first_post, second.postcondition); + ret seq_preconds_go(fcx, vec::slice(pps, 1u, sz), + @{precondition: next_first, + postcondition: next_first_post}); + } else { ret first.precondition; } + } + + + if sz >= 1u { + let first = pps[0]; + assert (pps_len(first) == num_vars); + ret seq_preconds_go(fcx, vec::slice(pps, 1u, sz), first); + } else { ret true_precond(num_vars); } +} + +fn intersect_states(p: prestate, q: prestate) -> prestate { + let rslt = tritv_clone(p); + tritv_intersect(rslt, q); + ret rslt; +} + +fn gen(fcx: fn_ctxt, id: node_id, c: tsconstr) -> bool { + ret set_in_postcond(bit_num(fcx, c), + node_id_to_ts_ann(fcx.ccx, id).conditions); +} + +fn declare_var(fcx: fn_ctxt, c: tsconstr, pre: prestate) -> prestate { + let rslt = clone(pre); + relax_prestate(bit_num(fcx, c), rslt); + // idea is this is scoped + relax_poststate(bit_num(fcx, c), rslt); + ret rslt; +} + +fn relax_precond_expr(e: @expr, cx: relax_ctxt, vt: visit::vt<relax_ctxt>) { + relax_precond(cx.i as uint, expr_precond(cx.fcx.ccx, e)); + visit::visit_expr(e, cx, vt); +} + +fn relax_precond_stmt(s: @stmt, cx: relax_ctxt, vt: visit::vt<relax_ctxt>) { + relax_precond(cx.i as uint, stmt_precond(cx.fcx.ccx, *s)); + visit::visit_stmt(s, cx, vt); +} + +type relax_ctxt = {fcx: fn_ctxt, i: node_id}; + +fn relax_precond_block_inner(b: blk, cx: relax_ctxt, + vt: visit::vt<relax_ctxt>) { + relax_precond(cx.i as uint, block_precond(cx.fcx.ccx, b)); + visit::visit_block(b, cx, vt); +} + +fn relax_precond_block(fcx: fn_ctxt, i: node_id, b: blk) { + let cx = {fcx: fcx, i: i}; + let visitor = visit::default_visitor::<relax_ctxt>(); + visitor = + @{visit_block: relax_precond_block_inner, + visit_expr: relax_precond_expr, + visit_stmt: relax_precond_stmt, + visit_item: + fn@(_i: @item, _cx: relax_ctxt, _vt: visit::vt<relax_ctxt>) { }, + visit_fn: bind do_nothing(_, _, _, _, _, _, _) + with *visitor}; + let v1 = visit::mk_vt(visitor); + v1.visit_block(b, cx, v1); +} + +fn gen_poststate(fcx: fn_ctxt, id: node_id, c: tsconstr) -> bool { + #debug("gen_poststate"); + ret set_in_poststate(bit_num(fcx, c), + node_id_to_ts_ann(fcx.ccx, id).states); +} + +fn kill_prestate(fcx: fn_ctxt, id: node_id, c: tsconstr) -> bool { + ret clear_in_prestate(bit_num(fcx, c), + node_id_to_ts_ann(fcx.ccx, id).states); +} + +fn kill_all_prestate(fcx: fn_ctxt, id: node_id) { + tritv::tritv_kill(node_id_to_ts_ann(fcx.ccx, id).states.prestate); +} + + +fn kill_poststate(fcx: fn_ctxt, id: node_id, c: tsconstr) -> bool { + #debug("kill_poststate"); + ret clear_in_poststate(bit_num(fcx, c), + node_id_to_ts_ann(fcx.ccx, id).states); +} + +fn clear_in_poststate_expr(fcx: fn_ctxt, e: @expr, t: poststate) { + alt e.node { + expr_path(p) { + alt vec::last(p.node.idents) { + some(i) { + alt local_node_id_to_def(fcx, e.id) { + some(def_local(nid, _)) { + clear_in_poststate_(bit_num(fcx, ninit(nid, i)), t); + } + some(_) {/* ignore args (for now...) */ } + _ { + fcx.ccx.tcx.sess.bug("clear_in_poststate_expr: \ + unbound var"); + } + } + } + _ { fcx.ccx.tcx.sess.bug("clear_in_poststate_expr"); } + } + } + _ {/* do nothing */ } + } +} + +fn kill_poststate_(fcx: fn_ctxt, c: tsconstr, post: poststate) -> bool { + #debug("kill_poststate_"); + ret clear_in_poststate_(bit_num(fcx, c), post); +} + +fn set_in_poststate_ident(fcx: fn_ctxt, id: node_id, ident: ident, + t: poststate) -> bool { + ret set_in_poststate_(bit_num(fcx, ninit(id, ident)), t); +} + +fn set_in_prestate_constr(fcx: fn_ctxt, c: tsconstr, t: prestate) -> bool { + ret set_in_poststate_(bit_num(fcx, c), t); +} + +fn clear_in_poststate_ident(fcx: fn_ctxt, id: node_id, ident: ident, + parent: node_id) -> bool { + ret kill_poststate(fcx, parent, ninit(id, ident)); +} + +fn clear_in_prestate_ident(fcx: fn_ctxt, id: node_id, ident: ident, + parent: node_id) -> bool { + ret kill_prestate(fcx, parent, ninit(id, ident)); +} + +fn clear_in_poststate_ident_(fcx: fn_ctxt, id: node_id, ident: ident, + post: poststate) -> bool { + ret kill_poststate_(fcx, ninit(id, ident), post); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/ck.rs b/src/rustc/middle/tstate/ck.rs new file mode 100644 index 00000000000..08415119e4a --- /dev/null +++ b/src/rustc/middle/tstate/ck.rs @@ -0,0 +1,201 @@ + +import syntax::ast; +import ast::{stmt, fn_ident, node_id, crate, return_val, noreturn, expr}; +import syntax::{visit, print}; +import syntax::codemap::span; +import middle::ty; +import tstate::ann::{precond, prestate, + implies, ann_precond, ann_prestate}; +import aux::*; +import syntax::print::pprust::ty_to_str; +import bitvectors::*; +import annotate::annotate_crate; +import collect_locals::mk_f_to_fn_info; +import pre_post_conditions::fn_pre_post; +import states::find_pre_post_state_fn; +import driver::session::session; + +fn check_unused_vars(fcx: fn_ctxt) { + + // FIXME: could be more efficient + for c: norm_constraint in constraints(fcx) { + alt c.c.node { + ninit(id, v) { + if !vec_contains(fcx.enclosing.used_vars, id) && v[0] != '_' as u8 + { + fcx.ccx.tcx.sess.span_warn(c.c.span, "unused variable " + v); + } + } + _ {/* ignore pred constraints */ } + } + } +} + +fn check_states_expr(e: @expr, fcx: fn_ctxt, v: visit::vt<fn_ctxt>) { + visit::visit_expr(e, fcx, v); + + let prec: precond = expr_precond(fcx.ccx, e); + let pres: prestate = expr_prestate(fcx.ccx, e); + + + /* + log_err("check_states_expr:"); + util::common::log_expr_err(*e); + log_err("prec = "); + log_tritv_err(fcx, prec); + log_err("pres = "); + log_tritv_err(fcx, pres); + */ + + if !implies(pres, prec) { + let s = ""; + let diff = first_difference_string(fcx, prec, pres); + s += + "Unsatisfied precondition constraint (for example, " + diff + + ") for expression:\n"; + s += syntax::print::pprust::expr_to_str(e); + s += "\nPrecondition:\n"; + s += tritv_to_str(fcx, prec); + s += "\nPrestate:\n"; + s += tritv_to_str(fcx, pres); + fcx.ccx.tcx.sess.span_fatal(e.span, s); + } +} + +fn check_states_stmt(s: @stmt, fcx: fn_ctxt, v: visit::vt<fn_ctxt>) { + visit::visit_stmt(s, fcx, v); + + let a = stmt_to_ann(fcx.ccx, *s); + let prec: precond = ann_precond(a); + let pres: prestate = ann_prestate(a); + + + #debug("check_states_stmt:"); + log(debug, print::pprust::stmt_to_str(*s)); + #debug("prec = "); + log_tritv(fcx, prec); + #debug("pres = "); + log_tritv(fcx, pres); + + if !implies(pres, prec) { + let ss = ""; + let diff = first_difference_string(fcx, prec, pres); + ss += + "Unsatisfied precondition constraint (for example, " + diff + + ") for statement:\n"; + ss += syntax::print::pprust::stmt_to_str(*s); + ss += "\nPrecondition:\n"; + ss += tritv_to_str(fcx, prec); + ss += "\nPrestate: \n"; + ss += tritv_to_str(fcx, pres); + fcx.ccx.tcx.sess.span_fatal(s.span, ss); + } +} + +fn check_states_against_conditions(fcx: fn_ctxt, + fk: visit::fn_kind, + f_decl: ast::fn_decl, + f_body: ast::blk, + sp: span, + id: node_id) { + /* Postorder traversal instead of pre is important + because we want the smallest possible erroneous statement + or expression. */ + let visitor = visit::mk_vt( + @{visit_stmt: check_states_stmt, + visit_expr: check_states_expr, + visit_fn: bind do_nothing::<fn_ctxt>(_, _, _, _, _, _, _) + with *visit::default_visitor::<fn_ctxt>()}); + visit::visit_fn(fk, f_decl, f_body, sp, id, fcx, visitor); + + /* Check that the return value is initialized */ + let post = aux::block_poststate(fcx.ccx, f_body); + if !promises(fcx, post, fcx.enclosing.i_return) && + !ty::type_is_nil(ty::ty_fn_ret(ty::node_id_to_type( + fcx.ccx.tcx, id))) && + f_decl.cf == return_val { + fcx.ccx.tcx.sess.span_err(f_body.span, + "In function " + fcx.name + + ", not all control paths \ + return a value"); + fcx.ccx.tcx.sess.span_fatal(f_decl.output.span, + "see declared return type of '" + + ty_to_str(f_decl.output) + "'"); + } else if f_decl.cf == noreturn { + + // check that this really always fails + // Note that it's ok for i_diverge and i_return to both be true. + // In fact, i_diverge implies i_return. (But not vice versa!) + + if !promises(fcx, post, fcx.enclosing.i_diverge) { + fcx.ccx.tcx.sess.span_fatal(f_body.span, + "In non-returning function " + + fcx.name + + ", some control paths may \ + return to the caller"); + } + } + + /* Finally, check for unused variables */ + check_unused_vars(fcx); +} + +fn check_fn_states(fcx: fn_ctxt, + fk: visit::fn_kind, + f_decl: ast::fn_decl, + f_body: ast::blk, + sp: span, + id: node_id) { + /* Compute the pre- and post-states for this function */ + + // Fixpoint iteration + while find_pre_post_state_fn(fcx, f_decl, f_body) { } + + /* Now compare each expr's pre-state to its precondition + and post-state to its postcondition */ + + check_states_against_conditions(fcx, fk, f_decl, f_body, sp, id); +} + +fn fn_states(fk: visit::fn_kind, f_decl: ast::fn_decl, f_body: ast::blk, + sp: span, id: node_id, + ccx: crate_ctxt, v: visit::vt<crate_ctxt>) { + visit::visit_fn(fk, f_decl, f_body, sp, id, ccx, v); + /* Look up the var-to-bit-num map for this function */ + + assert (ccx.fm.contains_key(id)); + let f_info = ccx.fm.get(id); + let name = visit::name_of_fn(fk); + let fcx = {enclosing: f_info, id: id, name: name, ccx: ccx}; + check_fn_states(fcx, fk, f_decl, f_body, sp, id) +} + +fn check_crate(cx: ty::ctxt, crate: @crate) { + let ccx: crate_ctxt = new_crate_ctxt(cx); + /* Build the global map from function id to var-to-bit-num-map */ + + mk_f_to_fn_info(ccx, crate); + /* Add a blank ts_ann for every statement (and expression) */ + + annotate_crate(ccx, *crate); + /* Compute the pre and postcondition for every subexpression */ + + let vtor = visit::default_visitor::<crate_ctxt>(); + vtor = @{visit_fn: fn_pre_post with *vtor}; + visit::visit_crate(*crate, ccx, visit::mk_vt(vtor)); + + /* Check the pre- and postcondition against the pre- and poststate + for every expression */ + let vtor = visit::default_visitor::<crate_ctxt>(); + vtor = @{visit_fn: fn_states with *vtor}; + visit::visit_crate(*crate, ccx, visit::mk_vt(vtor)); +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/collect_locals.rs b/src/rustc/middle/tstate/collect_locals.rs new file mode 100644 index 00000000000..9b39814e141 --- /dev/null +++ b/src/rustc/middle/tstate/collect_locals.rs @@ -0,0 +1,175 @@ +import option::*; +import pat_util::*; +import syntax::ast::*; +import syntax::ast_util::*; +import syntax::visit; +import util::common::new_def_hash; +import syntax::codemap::span; +import syntax::ast_util::respan; +import driver::session::session; +import aux::*; + +type ctxt = {cs: @mutable [sp_constr], tcx: ty::ctxt}; + +fn collect_local(loc: @local, cx: ctxt, v: visit::vt<ctxt>) { + pat_bindings(cx.tcx.def_map, loc.node.pat) {|p_id, _s, id| + *cx.cs += [respan(loc.span, ninit(p_id, path_to_ident(id)))]; + }; + visit::visit_local(loc, cx, v); +} + +fn collect_pred(e: @expr, cx: ctxt, v: visit::vt<ctxt>) { + alt e.node { + expr_check(_, ch) { *cx.cs += [expr_to_constr(cx.tcx, ch)]; } + expr_if_check(ex, _, _) { *cx.cs += [expr_to_constr(cx.tcx, ex)]; } + + // If it's a call, generate appropriate instances of the + // call's constraints. + expr_call(operator, operands, _) { + for c: @ty::constr in constraints_expr(cx.tcx, operator) { + let ct: sp_constr = + respan(c.span, + aux::substitute_constr_args(cx.tcx, operands, c)); + *cx.cs += [ct]; + } + } + _ { } + } + // visit subexpressions + visit::visit_expr(e, cx, v); +} + +fn find_locals(tcx: ty::ctxt, + fk: visit::fn_kind, + f_decl: fn_decl, + f_body: blk, + sp: span, + id: node_id) -> ctxt { + let cx: ctxt = {cs: @mutable [], tcx: tcx}; + let visitor = visit::default_visitor::<ctxt>(); + + visitor = + @{visit_local: collect_local, + visit_expr: collect_pred, + visit_fn: bind do_nothing(_, _, _, _, _, _, _) + with *visitor}; + visit::visit_fn(fk, f_decl, f_body, sp, + id, cx, visit::mk_vt(visitor)); + ret cx; +} + +fn add_constraint(tcx: ty::ctxt, c: sp_constr, next: uint, tbl: constr_map) -> + uint { + log(debug, + constraint_to_str(tcx, c) + " |-> " + uint::str(next)); + alt c.node { + ninit(id, i) { tbl.insert(local_def(id), cinit(next, c.span, i)); } + npred(p, d_id, args) { + alt tbl.find(d_id) { + some(ct) { + alt ct { + cinit(_, _, _) { + tcx.sess.bug("add_constraint: same def_id used" + + " as a variable and a pred"); + } + cpred(_, pds) { + *pds += [respan(c.span, {args: args, bit_num: next})]; + } + } + } + none { + let rslt: @mutable [pred_args] = + @mutable [respan(c.span, {args: args, bit_num: next})]; + tbl.insert(d_id, cpred(p, rslt)); + } + } + } + } + ret next + 1u; +} + + +/* builds a table mapping each local var defined in f + to a bit number in the precondition/postcondition vectors */ +fn mk_fn_info(ccx: crate_ctxt, + fk: visit::fn_kind, + f_decl: fn_decl, + f_body: blk, + f_sp: span, + id: node_id) { + let name = visit::name_of_fn(fk); + let res_map = new_def_hash::<constraint>(); + let next: uint = 0u; + + let cx: ctxt = find_locals(ccx.tcx, fk, f_decl, f_body, f_sp, id); + /* now we have to add bit nums for both the constraints + and the variables... */ + + for c: sp_constr in { *cx.cs } { + next = add_constraint(cx.tcx, c, next, res_map); + } + /* if this function has any constraints, instantiate them to the + argument names and add them */ + let sc; + for c: @constr in f_decl.constraints { + sc = ast_constr_to_sp_constr(cx.tcx, f_decl.inputs, c); + next = add_constraint(cx.tcx, sc, next, res_map); + } + + /* Need to add constraints for args too, b/c they + can be deinitialized */ + for a: arg in f_decl.inputs { + next = add_constraint( + cx.tcx, + respan(f_sp, ninit(a.id, a.ident)), + next, + res_map); + } + + /* add the special i_diverge and i_return constraints + (see the type definition for auxiliary::fn_info for an explanation) */ + + // use the function name for the "returns" constraint" + let returns_id = ccx.tcx.sess.next_node_id(); + let returns_constr = ninit(returns_id, name); + next = + add_constraint(cx.tcx, respan(f_sp, returns_constr), next, res_map); + // and the name of the function, with a '!' appended to it, for the + // "diverges" constraint + let diverges_id = ccx.tcx.sess.next_node_id(); + let diverges_constr = ninit(diverges_id, name + "!"); + next = add_constraint(cx.tcx, respan(f_sp, diverges_constr), next, + res_map); + + let v: @mutable [node_id] = @mutable []; + let rslt = + {constrs: res_map, + num_constraints: next, + cf: f_decl.cf, + i_return: returns_constr, + i_diverge: diverges_constr, + used_vars: v}; + ccx.fm.insert(id, rslt); + #debug("%s has %u constraints", name, num_constraints(rslt)); +} + + +/* initializes the global fn_info_map (mapping each function ID, including + nested locally defined functions, onto a mapping from local variable name + to bit number) */ +fn mk_f_to_fn_info(ccx: crate_ctxt, c: @crate) { + let visitor = + visit::mk_simple_visitor(@{visit_fn: + bind mk_fn_info(ccx, _, _, _, _, _) + with *visit::default_simple_visitor()}); + visit::visit_crate(*c, (), visitor); +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs new file mode 100644 index 00000000000..8a83f7dd7b4 --- /dev/null +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -0,0 +1,718 @@ +import tstate::ann::*; +import aux::*; +import bitvectors::{bit_num, seq_preconds, seq_postconds, + intersect_states, + relax_precond_block, gen}; +import tritv::*; + +import pat_util::*; +import syntax::ast::*; +import syntax::ast_util::*; +import syntax::visit; +import util::common::{new_def_hash, log_expr, field_exprs, + has_nonlocal_exits, log_stmt}; +import syntax::codemap::span; +import driver::session::session; + +fn find_pre_post_mod(_m: _mod) -> _mod { + #debug("implement find_pre_post_mod!"); + fail; +} + +fn find_pre_post_native_mod(_m: native_mod) -> native_mod { + #debug("implement find_pre_post_native_mod"); + fail; +} + +fn find_pre_post_method(ccx: crate_ctxt, m: @method) { + assert (ccx.fm.contains_key(m.id)); + let fcx: fn_ctxt = + {enclosing: ccx.fm.get(m.id), + id: m.id, + name: m.ident, + ccx: ccx}; + find_pre_post_fn(fcx, m.body); +} + +fn find_pre_post_item(ccx: crate_ctxt, i: item) { + alt i.node { + item_const(_, e) { + // do nothing -- item_consts don't refer to local vars + } + item_fn(_, _, body) { + assert (ccx.fm.contains_key(i.id)); + let fcx = + {enclosing: ccx.fm.get(i.id), id: i.id, name: i.ident, ccx: ccx}; + find_pre_post_fn(fcx, body); + } + item_mod(m) { find_pre_post_mod(m); } + item_native_mod(nm) { find_pre_post_native_mod(nm); } + item_ty(_, _) | item_enum(_, _) | item_iface(_, _) { ret; } + item_res(_, _, body, dtor_id, _) { + let fcx = + {enclosing: ccx.fm.get(dtor_id), + id: dtor_id, + name: i.ident, + ccx: ccx}; + find_pre_post_fn(fcx, body); + } + item_class(_,_,_,_,_) { + fail "find_pre_post_item: implement item_class"; + } + item_impl(_, _, _, ms) { for m in ms { find_pre_post_method(ccx, m); } } + } +} + + +/* Finds the pre and postcondition for each expr in <args>; + sets the precondition in a to be the result of combining + the preconditions for <args>, and the postcondition in a to + be the union of all postconditions for <args> */ +fn find_pre_post_exprs(fcx: fn_ctxt, args: [@expr], id: node_id) { + if vec::len::<@expr>(args) > 0u { + #debug("find_pre_post_exprs: oper ="); + log_expr(*args[0]); + } + fn do_one(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); } + for e: @expr in args { do_one(fcx, e); } + + fn get_pp(ccx: crate_ctxt, &&e: @expr) -> pre_and_post { + ret expr_pp(ccx, e); + } + let pps = vec::map(args, bind get_pp(fcx.ccx, _)); + + set_pre_and_post(fcx.ccx, id, seq_preconds(fcx, pps), + seq_postconds(fcx, vec::map(pps, get_post))); +} + +fn find_pre_post_loop(fcx: fn_ctxt, l: @local, index: @expr, body: blk, + id: node_id) { + find_pre_post_expr(fcx, index); + find_pre_post_block(fcx, body); + pat_bindings(fcx.ccx.tcx.def_map, l.node.pat) {|p_id, _s, n| + let v_init = ninit(p_id, path_to_ident(n)); + relax_precond_block(fcx, bit_num(fcx, v_init) as node_id, body); + // Hack: for-loop index variables are frequently ignored, + // so we pretend they're used + use_var(fcx, p_id); + }; + + let loop_precond = + seq_preconds(fcx, [expr_pp(fcx.ccx, index), block_pp(fcx.ccx, body)]); + let loop_postcond = + intersect_states(expr_postcond(fcx.ccx, index), + block_postcond(fcx.ccx, body)); + copy_pre_post_(fcx.ccx, id, loop_precond, loop_postcond); +} + +// Generates a pre/post assuming that a is the +// annotation for an if-expression with consequent conseq +// and alternative maybe_alt +fn join_then_else(fcx: fn_ctxt, antec: @expr, conseq: blk, + maybe_alt: option<@expr>, id: node_id, chck: if_ty) { + find_pre_post_expr(fcx, antec); + find_pre_post_block(fcx, conseq); + alt maybe_alt { + none { + alt chck { + if_check { + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec); + gen(fcx, antec.id, c.node); + } + _ { } + } + + let precond_res = + seq_preconds(fcx, + [expr_pp(fcx.ccx, antec), + block_pp(fcx.ccx, conseq)]); + set_pre_and_post(fcx.ccx, id, precond_res, + expr_poststate(fcx.ccx, antec)); + } + some(altern) { + /* + if check = if_check, then + be sure that the predicate implied by antec + is *not* true in the alternative + */ + find_pre_post_expr(fcx, altern); + let precond_false_case = + seq_preconds(fcx, + [expr_pp(fcx.ccx, antec), expr_pp(fcx.ccx, altern)]); + let postcond_false_case = + seq_postconds(fcx, + [expr_postcond(fcx.ccx, antec), + expr_postcond(fcx.ccx, altern)]); + + /* Be sure to set the bit for the check condition here, + so that it's *not* set in the alternative. */ + alt chck { + if_check { + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec); + gen(fcx, antec.id, c.node); + } + _ { } + } + let precond_true_case = + seq_preconds(fcx, + [expr_pp(fcx.ccx, antec), + block_pp(fcx.ccx, conseq)]); + let postcond_true_case = + seq_postconds(fcx, + [expr_postcond(fcx.ccx, antec), + block_postcond(fcx.ccx, conseq)]); + + let precond_res = + seq_postconds(fcx, [precond_true_case, precond_false_case]); + let postcond_res = + intersect_states(postcond_true_case, postcond_false_case); + set_pre_and_post(fcx.ccx, id, precond_res, postcond_res); + } + } +} + +fn gen_if_local(fcx: fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id, + new_var: node_id, pth: @path) { + alt node_id_to_def(fcx.ccx, new_var) { + some(d) { + alt d { + def_local(nid, _) { + find_pre_post_expr(fcx, rhs); + let p = expr_pp(fcx.ccx, rhs); + set_pre_and_post(fcx.ccx, larger_id, p.precondition, + p.postcondition); + gen(fcx, larger_id, + ninit(nid, path_to_ident(pth))); + } + _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); } + } + } + _ { find_pre_post_exprs(fcx, [lhs, rhs], larger_id); } + } +} + +fn handle_update(fcx: fn_ctxt, parent: @expr, lhs: @expr, rhs: @expr, + ty: oper_type) { + find_pre_post_expr(fcx, rhs); + alt lhs.node { + expr_path(p) { + let post = expr_postcond(fcx.ccx, parent); + let tmp = tritv_clone(post); + + alt ty { + oper_move { + if is_path(rhs) { forget_in_postcond(fcx, parent.id, rhs.id); } + } + oper_swap { + forget_in_postcond_still_init(fcx, parent.id, lhs.id); + forget_in_postcond_still_init(fcx, parent.id, rhs.id); + } + oper_assign { + forget_in_postcond_still_init(fcx, parent.id, lhs.id); + } + _ { + // pure and assign_op require the lhs to be init'd + let df = node_id_to_def_strict(fcx.ccx.tcx, lhs.id); + alt df { + def_local(nid, _) { + let i = bit_num(fcx, ninit(nid, path_to_ident(p))); + require_and_preserve(i, expr_pp(fcx.ccx, lhs)); + } + _ { } + } + } + } + + gen_if_local(fcx, lhs, rhs, parent.id, lhs.id, p); + alt rhs.node { + expr_path(p1) { + let d = local_node_id_to_local_def_id(fcx, lhs.id); + let d1 = local_node_id_to_local_def_id(fcx, rhs.id); + alt d { + some(id) { + alt d1 { + some(id1) { + let instlhs = + {ident: path_to_ident(p), node: id}; + let instrhs = + {ident: path_to_ident(p1), node: id1}; + copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs, + ty); + } + _ { } + } + } + _ { } + } + } + _ {/* do nothing */ } + } + } + _ { find_pre_post_expr(fcx, lhs); } + } +} + +fn handle_var(fcx: fn_ctxt, rslt: pre_and_post, id: node_id, name: ident) { + handle_var_def(fcx, rslt, node_id_to_def_strict(fcx.ccx.tcx, id), name); +} + +fn handle_var_def(fcx: fn_ctxt, rslt: pre_and_post, def: def, name: ident) { + log(debug, ("handle_var_def: ", def, name)); + alt def { + def_local(nid, _) | def_arg(nid, _) { + use_var(fcx, nid); + let i = bit_num(fcx, ninit(nid, name)); + require_and_preserve(i, rslt); + } + _ {/* nothing to check */ } + } +} + +fn forget_args_moved_in(fcx: fn_ctxt, parent: @expr, modes: [mode], + operands: [@expr]) { + vec::iteri(modes) {|i,mode| + alt ty::resolved_mode(fcx.ccx.tcx, mode) { + by_move { forget_in_postcond(fcx, parent.id, operands[i].id); } + by_ref | by_val | by_mutbl_ref | by_copy { } + } + } +} + +fn find_pre_post_expr_fn_upvars(fcx: fn_ctxt, e: @expr) { + let rslt = expr_pp(fcx.ccx, e); + clear_pp(rslt); + for def in *freevars::get_freevars(fcx.ccx.tcx, e.id) { + log(debug, ("handle_var_def: def=", def)); + handle_var_def(fcx, rslt, def.def, "upvar"); + } +} + +/* Fills in annotations as a side effect. Does not rebuild the expr */ +fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { + let enclosing = fcx.enclosing; + let num_local_vars = num_constraints(enclosing); + fn do_rand_(fcx: fn_ctxt, e: @expr) { find_pre_post_expr(fcx, e); } + + + alt e.node { + expr_call(operator, operands, _) { + /* copy */ + + let args = operands; + args += [operator]; + + find_pre_post_exprs(fcx, args, e.id); + /* see if the call has any constraints on its type */ + for c: @ty::constr in constraints_expr(fcx.ccx.tcx, operator) { + let i = + bit_num(fcx, substitute_constr_args(fcx.ccx.tcx, args, c)); + require(i, expr_pp(fcx.ccx, e)); + } + + forget_args_moved_in(fcx, e, callee_modes(fcx, operator.id), + operands); + + /* if this is a failing call, its postcondition sets everything */ + alt controlflow_expr(fcx.ccx, operator) { + noreturn { set_postcond_false(fcx.ccx, e.id); } + _ { } + } + } + expr_vec(args, _) { find_pre_post_exprs(fcx, args, e.id); } + expr_path(p) { + let rslt = expr_pp(fcx.ccx, e); + clear_pp(rslt); + handle_var(fcx, rslt, e.id, path_to_ident(p)); + } + expr_log(_, lvl, arg) { + find_pre_post_exprs(fcx, [lvl, arg], e.id); + } + expr_fn(_, _, _, cap_clause) { + find_pre_post_expr_fn_upvars(fcx, e); + + let use_cap_item = fn@(&&cap_item: @capture_item) { + let d = local_node_id_to_local_def_id(fcx, cap_item.id); + option::may(d, { |id| use_var(fcx, id) }); + }; + vec::iter(cap_clause.copies, use_cap_item); + vec::iter(cap_clause.moves, use_cap_item); + + vec::iter(cap_clause.moves) { |cap_item| + log(debug, ("forget_in_postcond: ", cap_item)); + forget_in_postcond(fcx, e.id, cap_item.id); + } + } + expr_fn_block(_, _) { + find_pre_post_expr_fn_upvars(fcx, e); + } + expr_block(b) { + find_pre_post_block(fcx, b); + let p = block_pp(fcx.ccx, b); + set_pre_and_post(fcx.ccx, e.id, p.precondition, p.postcondition); + } + expr_rec(fields, maybe_base) { + let es = field_exprs(fields); + alt maybe_base { none {/* no-op */ } some(b) { es += [b]; } } + find_pre_post_exprs(fcx, es, e.id); + } + expr_tup(elts) { find_pre_post_exprs(fcx, elts, e.id); } + expr_copy(a) { + find_pre_post_expr(fcx, a); + copy_pre_post(fcx.ccx, e.id, a); + } + expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); } + expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); } + expr_assign(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_assign); } + expr_assign_op(_, lhs, rhs) { + /* Different from expr_assign in that the lhs *must* + already be initialized */ + + find_pre_post_exprs(fcx, [lhs, rhs], e.id); + forget_in_postcond_still_init(fcx, e.id, lhs.id); + } + expr_lit(_) { clear_pp(expr_pp(fcx.ccx, e)); } + expr_ret(maybe_val) { + alt maybe_val { + none { + clear_precond(fcx.ccx, e.id); + set_postcond_false(fcx.ccx, e.id); + } + some(ret_val) { + find_pre_post_expr(fcx, ret_val); + set_precondition(node_id_to_ts_ann(fcx.ccx, e.id), + expr_precond(fcx.ccx, ret_val)); + set_postcond_false(fcx.ccx, e.id); + } + } + } + expr_be(val) { + find_pre_post_expr(fcx, val); + set_pre_and_post(fcx.ccx, e.id, expr_prestate(fcx.ccx, val), + false_postcond(num_local_vars)); + } + expr_if(antec, conseq, maybe_alt) { + join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if); + } + expr_binary(bop, l, r) { + if lazy_binop(bop) { + find_pre_post_expr(fcx, l); + find_pre_post_expr(fcx, r); + let overall_pre = + seq_preconds(fcx, [expr_pp(fcx.ccx, l), expr_pp(fcx.ccx, r)]); + set_precondition(node_id_to_ts_ann(fcx.ccx, e.id), overall_pre); + set_postcondition(node_id_to_ts_ann(fcx.ccx, e.id), + expr_postcond(fcx.ccx, l)); + } else { find_pre_post_exprs(fcx, [l, r], e.id); } + } + expr_unary(_, operand) { + find_pre_post_expr(fcx, operand); + copy_pre_post(fcx.ccx, e.id, operand); + } + expr_cast(operand, _) { + find_pre_post_expr(fcx, operand); + copy_pre_post(fcx.ccx, e.id, operand); + } + expr_while(test, body) { + find_pre_post_expr(fcx, test); + find_pre_post_block(fcx, body); + set_pre_and_post(fcx.ccx, e.id, + seq_preconds(fcx, + [expr_pp(fcx.ccx, test), + block_pp(fcx.ccx, body)]), + intersect_states(expr_postcond(fcx.ccx, test), + block_postcond(fcx.ccx, body))); + } + expr_do_while(body, test) { + find_pre_post_block(fcx, body); + find_pre_post_expr(fcx, test); + let loop_postcond = + seq_postconds(fcx, + [block_postcond(fcx.ccx, body), + expr_postcond(fcx.ccx, test)]); + /* conservative approximation: if the body + could break or cont, the test may never be executed */ + + if has_nonlocal_exits(body) { + loop_postcond = empty_poststate(num_local_vars); + } + set_pre_and_post(fcx.ccx, e.id, + seq_preconds(fcx, + [block_pp(fcx.ccx, body), + expr_pp(fcx.ccx, test)]), + loop_postcond); + } + expr_for(d, index, body) { + find_pre_post_loop(fcx, d, index, body, e.id); + } + expr_index(val, sub) { find_pre_post_exprs(fcx, [val, sub], e.id); } + expr_alt(ex, alts, _) { + find_pre_post_expr(fcx, ex); + fn do_an_alt(fcx: fn_ctxt, an_alt: arm) -> pre_and_post { + alt an_alt.guard { + some(e) { find_pre_post_expr(fcx, e); } + _ {} + } + find_pre_post_block(fcx, an_alt.body); + ret block_pp(fcx.ccx, an_alt.body); + } + let alt_pps = []; + for a: arm in alts { alt_pps += [do_an_alt(fcx, a)]; } + fn combine_pp(antec: pre_and_post, fcx: fn_ctxt, &&pp: pre_and_post, + &&next: pre_and_post) -> pre_and_post { + union(pp.precondition, seq_preconds(fcx, [antec, next])); + intersect(pp.postcondition, next.postcondition); + ret pp; + } + let antec_pp = pp_clone(expr_pp(fcx.ccx, ex)); + let e_pp = + @{precondition: empty_prestate(num_local_vars), + postcondition: false_postcond(num_local_vars)}; + let g = bind combine_pp(antec_pp, fcx, _, _); + let alts_overall_pp = + vec::foldl(e_pp, alt_pps, g); + set_pre_and_post(fcx.ccx, e.id, alts_overall_pp.precondition, + alts_overall_pp.postcondition); + } + expr_field(operator, _, _) { + find_pre_post_expr(fcx, operator); + copy_pre_post(fcx.ccx, e.id, operator); + } + expr_fail(maybe_val) { + let prestate; + alt maybe_val { + none { prestate = empty_prestate(num_local_vars); } + some(fail_val) { + find_pre_post_expr(fcx, fail_val); + prestate = expr_precond(fcx.ccx, fail_val); + } + } + set_pre_and_post(fcx.ccx, e.id, + /* if execution continues after fail, + then everything is true! */ + prestate, false_postcond(num_local_vars)); + } + expr_assert(p) { + find_pre_post_expr(fcx, p); + copy_pre_post(fcx.ccx, e.id, p); + } + expr_check(_, p) { + find_pre_post_expr(fcx, p); + copy_pre_post(fcx.ccx, e.id, p); + /* predicate p holds after this expression executes */ + + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p); + gen(fcx, e.id, c.node); + } + expr_if_check(p, conseq, maybe_alt) { + join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check); + } + + + + + + + expr_bind(operator, maybe_args) { + let args = []; + let cmodes = callee_modes(fcx, operator.id); + let modes = []; + let i = 0; + for expr_opt: option<@expr> in maybe_args { + alt expr_opt { + none {/* no-op */ } + some(expr) { modes += [cmodes[i]]; args += [expr]; } + } + i += 1; + } + args += [operator]; /* ??? order of eval? */ + forget_args_moved_in(fcx, e, modes, args); + find_pre_post_exprs(fcx, args, e.id); + } + expr_break { clear_pp(expr_pp(fcx.ccx, e)); } + expr_cont { clear_pp(expr_pp(fcx.ccx, e)); } + expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } + } +} + +fn find_pre_post_stmt(fcx: fn_ctxt, s: stmt) { + #debug("stmt ="); + log_stmt(s); + alt s.node { + stmt_decl(adecl, id) { + alt adecl.node { + decl_local(alocals) { + let e_pp; + let prev_pp = empty_pre_post(num_constraints(fcx.enclosing)); + for alocal in alocals { + alt alocal.node.init { + some(an_init) { + /* LHS always becomes initialized, + whether or not this is a move */ + find_pre_post_expr(fcx, an_init.expr); + pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat) + {|p_id, _s, _n| + copy_pre_post(fcx.ccx, p_id, an_init.expr); + }; + /* Inherit ann from initializer, and add var being + initialized to the postcondition */ + copy_pre_post(fcx.ccx, id, an_init.expr); + + let p = none; + alt an_init.expr.node { + expr_path(_p) { p = some(_p); } + _ { } + } + + pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat) + {|p_id, _s, n| + let ident = path_to_ident(n); + alt p { + some(p) { + copy_in_postcond(fcx, id, + {ident: ident, node: p_id}, + {ident: + path_to_ident(p), + node: an_init.expr.id}, + op_to_oper_ty(an_init.op)); + } + none { } + } + gen(fcx, id, ninit(p_id, ident)); + }; + + if an_init.op == init_move && is_path(an_init.expr) { + forget_in_postcond(fcx, id, an_init.expr.id); + } + + /* Clear out anything that the previous initializer + guaranteed */ + e_pp = expr_pp(fcx.ccx, an_init.expr); + tritv_copy(prev_pp.precondition, + seq_preconds(fcx, [prev_pp, e_pp])); + /* Include the LHSs too, since those aren't in the + postconds of the RHSs themselves */ + pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat) + {|pat_id, _s, n| + set_in_postcond(bit_num(fcx, + ninit(pat_id, path_to_ident(n))), prev_pp); + }; + copy_pre_post_(fcx.ccx, id, prev_pp.precondition, + prev_pp.postcondition); + } + none { + pat_bindings(fcx.ccx.tcx.def_map, alocal.node.pat) + {|p_id, _s, _n| + clear_pp(node_id_to_ts_ann(fcx.ccx, p_id).conditions); + }; + clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions); + } + } + } + } + decl_item(anitem) { + clear_pp(node_id_to_ts_ann(fcx.ccx, id).conditions); + find_pre_post_item(fcx.ccx, *anitem); + } + } + } + stmt_expr(e, id) | stmt_semi(e, id) { + find_pre_post_expr(fcx, e); + copy_pre_post(fcx.ccx, id, e); + } + } +} + +fn find_pre_post_block(fcx: fn_ctxt, b: blk) { + /* Want to say that if there is a break or cont in this + block, then that invalidates the poststate upheld by + any of the stmts after it. + Given that the typechecker has run, we know any break will be in + a block that forms a loop body. So that's ok. There'll never be an + expr_break outside a loop body, therefore, no expr_break outside a block. + */ + + /* Conservative approximation for now: This says that if a block contains + *any* breaks or conts, then its postcondition doesn't promise anything. + This will mean that: + x = 0; + break; + + won't have a postcondition that says x is initialized, but that's ok. + */ + + let nv = num_constraints(fcx.enclosing); + fn do_one_(fcx: fn_ctxt, s: @stmt) { + find_pre_post_stmt(fcx, *s); + /* + #error("pre_post for stmt:"); + log_stmt_err(*s); + #error("is:"); + log_pp_err(stmt_pp(fcx.ccx, *s)); + */ + } + for s: @stmt in b.node.stmts { do_one_(fcx, s); } + fn do_inner_(fcx: fn_ctxt, &&e: @expr) { find_pre_post_expr(fcx, e); } + let do_inner = bind do_inner_(fcx, _); + option::map::<@expr, ()>(b.node.expr, do_inner); + + let pps: [pre_and_post] = []; + for s: @stmt in b.node.stmts { pps += [stmt_pp(fcx.ccx, *s)]; } + alt b.node.expr { + none {/* no-op */ } + some(e) { pps += [expr_pp(fcx.ccx, e)]; } + } + + let block_precond = seq_preconds(fcx, pps); + + let postconds = []; + for pp: pre_and_post in pps { postconds += [get_post(pp)]; } + + /* A block may be empty, so this next line ensures that the postconds + vector is non-empty. */ + postconds += [block_precond]; + + let block_postcond = empty_poststate(nv); + /* conservative approximation */ + + if !has_nonlocal_exits(b) { + block_postcond = seq_postconds(fcx, postconds); + } + set_pre_and_post(fcx.ccx, b.node.id, block_precond, block_postcond); +} + +fn find_pre_post_fn(fcx: fn_ctxt, body: blk) { + // hack + use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_return)); + use_var(fcx, tsconstr_to_node_id(fcx.enclosing.i_diverge)); + + find_pre_post_block(fcx, body); + + // Treat the tail expression as a return statement + alt body.node.expr { + some(tailexpr) { set_postcond_false(fcx.ccx, tailexpr.id); } + none {/* fallthrough */ } + } +} + +fn fn_pre_post(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span, + id: node_id, + ccx: crate_ctxt, v: visit::vt<crate_ctxt>) { + visit::visit_fn(fk, decl, body, sp, id, ccx, v); + assert (ccx.fm.contains_key(id)); + let fcx = + {enclosing: ccx.fm.get(id), + id: id, + name: visit::name_of_fn(fk), + ccx: ccx}; + find_pre_post_fn(fcx, body); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/states.rs b/src/rustc/middle/tstate/states.rs new file mode 100644 index 00000000000..b90e39f2b23 --- /dev/null +++ b/src/rustc/middle/tstate/states.rs @@ -0,0 +1,783 @@ +import ann::*; +import aux::*; +import tritv::{tritv_clone, tritv_set, ttrue}; + +import bitvectors::*; +import pat_util::*; +import syntax::ast::*; +import syntax::ast_util::*; +import syntax::codemap::span; +import middle::ty::{expr_ty, type_is_bot}; +import util::common::*; +import driver::session::session; + +fn forbid_upvar(fcx: fn_ctxt, rhs_id: node_id, sp: span, t: oper_type) { + alt t { + oper_move { + alt local_node_id_to_def(fcx, rhs_id) { + some(def_upvar(_, _, _)) { + fcx.ccx.tcx.sess.span_err(sp, + "Tried to deinitialize a variable \ + declared in a different scope"); + } + _ { } + } + } + _ {/* do nothing */ } + } +} + +fn handle_move_or_copy(fcx: fn_ctxt, post: poststate, rhs_path: @path, + rhs_id: node_id, instlhs: inst, init_op: init_op) { + forbid_upvar(fcx, rhs_id, rhs_path.span, op_to_oper_ty(init_op)); + + let rhs_d_id = local_node_id_to_def_id(fcx, rhs_id); + alt rhs_d_id { + some(rhsid) { + // RHS is a local var + let instrhs = + {ident: path_to_ident(rhs_path), node: rhsid.node}; + copy_in_poststate(fcx, post, instlhs, instrhs, + op_to_oper_ty(init_op)); + } + _ { + // not a local -- do nothing + } + } +} + +fn handle_fail(fcx: fn_ctxt, pres:prestate, post:poststate) { + // Remember what the old value of the "I return" trit was, so that + // we can avoid changing that (if it was true, there was a return + // that dominates this fail and the fail is unreachable) + if !promises(fcx, pres, fcx.enclosing.i_return) + // (only if we're in a diverging function -- you can fail when + // you're supposed to return, but not vice versa). + && fcx.enclosing.cf == noreturn { + kill_poststate_(fcx, fcx.enclosing.i_return, post); + } else { + // This code is unreachable (it's dominated by a return), + // so doesn't diverge. + kill_poststate_(fcx, fcx.enclosing.i_diverge, post); + } +} + +fn seq_states(fcx: fn_ctxt, pres: prestate, bindings: [binding]) -> + {changed: bool, post: poststate} { + let changed = false; + let post = tritv_clone(pres); + for b: binding in bindings { + alt b.rhs { + some(an_init) { + // an expression, with or without a destination + changed |= + find_pre_post_state_expr(fcx, post, an_init.expr) || changed; + post = tritv_clone(expr_poststate(fcx.ccx, an_init.expr)); + for i: inst in b.lhs { + alt an_init.expr.node { + expr_path(p) { + handle_move_or_copy(fcx, post, p, an_init.expr.id, i, + an_init.op); + } + _ { } + } + set_in_poststate_ident(fcx, i.node, i.ident, post); + } + + // Forget the RHS if we just moved it. + if an_init.op == init_move { + forget_in_poststate(fcx, post, an_init.expr.id); + } + } + none { + for i: inst in b.lhs { + // variables w/o an initializer + clear_in_poststate_ident_(fcx, i.node, i.ident, post); + } + } + } + } + ret {changed: changed, post: post}; +} + +fn find_pre_post_state_sub(fcx: fn_ctxt, pres: prestate, e: @expr, + parent: node_id, c: option<tsconstr>) -> bool { + let changed = find_pre_post_state_expr(fcx, pres, e); + + changed = set_prestate_ann(fcx.ccx, parent, pres) || changed; + + let post = tritv_clone(expr_poststate(fcx.ccx, e)); + alt c { + none { } + some(c1) { set_in_poststate_(bit_num(fcx, c1), post); } + } + + changed = set_poststate_ann(fcx.ccx, parent, post) || changed; + ret changed; +} + +fn find_pre_post_state_two(fcx: fn_ctxt, pres: prestate, lhs: @expr, + rhs: @expr, parent: node_id, ty: oper_type) -> + bool { + let changed = set_prestate_ann(fcx.ccx, parent, pres); + changed = find_pre_post_state_expr(fcx, pres, lhs) || changed; + changed = + find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, lhs), rhs) || + changed; + forbid_upvar(fcx, rhs.id, rhs.span, ty); + + let post = tritv_clone(expr_poststate(fcx.ccx, rhs)); + + alt lhs.node { + expr_path(p) { + // for termination, need to make sure intermediate changes don't set + // changed flag + // tmp remembers "old" constraints we'd otherwise forget, + // for substitution purposes + let tmp = tritv_clone(post); + + alt ty { + oper_move { + if is_path(rhs) { forget_in_poststate(fcx, post, rhs.id); } + forget_in_poststate_still_init(fcx, post, lhs.id); + } + oper_swap { + forget_in_poststate_still_init(fcx, post, lhs.id); + forget_in_poststate_still_init(fcx, post, rhs.id); + } + _ { forget_in_poststate_still_init(fcx, post, lhs.id); } + } + + gen_if_local(fcx, post, lhs); + alt rhs.node { + expr_path(p1) { + let d = local_node_id_to_local_def_id(fcx, lhs.id); + let d1 = local_node_id_to_local_def_id(fcx, rhs.id); + alt d { + some(id) { + alt d1 { + some(id1) { + let instlhs = + {ident: path_to_ident(p), node: id}; + let instrhs = + {ident: path_to_ident(p1), node: id1}; + copy_in_poststate_two(fcx, tmp, post, instlhs, instrhs, + ty); + } + _ { } + } + } + _ { } + } + } + _ {/* do nothing */ } + } + } + _ { } + } + changed = set_poststate_ann(fcx.ccx, parent, post) || changed; + ret changed; +} + +fn find_pre_post_state_call(fcx: fn_ctxt, pres: prestate, a: @expr, + id: node_id, ops: [init_op], bs: [@expr], + cf: ret_style) -> bool { + let changed = find_pre_post_state_expr(fcx, pres, a); + // FIXME: This could be a typestate constraint + if vec::len(bs) != vec::len(ops) { + fcx.ccx.tcx.sess.span_bug(a.span, + #fmt["mismatched arg lengths: \ + %u exprs vs. %u ops", + vec::len(bs), vec::len(ops)]); + } + ret find_pre_post_state_exprs(fcx, pres, id, ops, + bs, cf) || changed; +} + +fn find_pre_post_state_exprs(fcx: fn_ctxt, pres: prestate, id: node_id, + ops: [init_op], es: [@expr], cf: ret_style) -> + bool { + let rs = seq_states(fcx, pres, anon_bindings(ops, es)); + let changed = rs.changed | set_prestate_ann(fcx.ccx, id, pres); + /* if this is a failing call, it sets everything as initialized */ + alt cf { + noreturn { + let post = false_postcond(num_constraints(fcx.enclosing)); + handle_fail(fcx, pres, post); + changed |= set_poststate_ann(fcx.ccx, id, post); + } + _ { changed |= set_poststate_ann(fcx.ccx, id, rs.post); } + } + ret changed; +} + +fn find_pre_post_state_loop(fcx: fn_ctxt, pres: prestate, l: @local, + index: @expr, body: blk, id: node_id) -> bool { + // I'm confused about this -- how does the poststate for the body + // ever grow larger? It seems like it can't? + let loop_pres = intersect_states(pres, block_poststate(fcx.ccx, body)); + + let changed = + set_prestate_ann(fcx.ccx, id, loop_pres) | + find_pre_post_state_expr(fcx, pres, index); + + // Make sure the index vars are considered initialized + // in the body + let index_post = tritv_clone(expr_poststate(fcx.ccx, index)); + pat_bindings(fcx.ccx.tcx.def_map, l.node.pat) {|p_id, _s, n| + set_in_poststate_ident(fcx, p_id, path_to_ident(n), index_post); + }; + + changed |= find_pre_post_state_block(fcx, index_post, body); + + + if has_nonlocal_exits(body) { + // See [Break-unsound] + ret changed | set_poststate_ann(fcx.ccx, id, pres); + } else { + let res_p = + intersect_states(expr_poststate(fcx.ccx, index), + block_poststate(fcx.ccx, body)); + ret changed | set_poststate_ann(fcx.ccx, id, res_p); + } +} + +fn gen_if_local(fcx: fn_ctxt, p: poststate, e: @expr) -> bool { + alt e.node { + expr_path(pth) { + alt fcx.ccx.tcx.def_map.find(e.id) { + some(def_local(nid, _)) { + ret set_in_poststate_ident(fcx, nid, path_to_ident(pth), p); + } + _ { ret false; } + } + } + _ { ret false; } + } +} + +fn join_then_else(fcx: fn_ctxt, antec: @expr, conseq: blk, + maybe_alt: option<@expr>, id: node_id, chk: if_ty, + pres: prestate) -> bool { + let changed = + set_prestate_ann(fcx.ccx, id, pres) | + find_pre_post_state_expr(fcx, pres, antec); + + /* + log_err("join_then_else:"); + log_expr_err(*antec); + log_bitv_err(fcx, expr_prestate(fcx.ccx, antec)); + log_bitv_err(fcx, expr_poststate(fcx.ccx, antec)); + log_block_err(conseq); + log_bitv_err(fcx, block_prestate(fcx.ccx, conseq)); + log_bitv_err(fcx, block_poststate(fcx.ccx, conseq)); + log_err("****"); + log_bitv_err(fcx, expr_precond(fcx.ccx, antec)); + log_bitv_err(fcx, expr_postcond(fcx.ccx, antec)); + log_bitv_err(fcx, block_precond(fcx.ccx, conseq)); + log_bitv_err(fcx, block_postcond(fcx.ccx, conseq)); + */ + + alt maybe_alt { + none { + alt chk { + if_check { + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec); + let conseq_prestate = tritv_clone(expr_poststate(fcx.ccx, antec)); + tritv_set(bit_num(fcx, c.node), conseq_prestate, ttrue); + changed |= + find_pre_post_state_block(fcx, conseq_prestate, conseq) | + set_poststate_ann(fcx.ccx, id, + expr_poststate(fcx.ccx, antec)); + } + _ { + changed |= + find_pre_post_state_block(fcx, expr_poststate(fcx.ccx, antec), + conseq) | + set_poststate_ann(fcx.ccx, id, + expr_poststate(fcx.ccx, antec)); + } + } + } + some(altern) { + changed |= + find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, antec), + altern); + + let conseq_prestate = expr_poststate(fcx.ccx, antec); + alt chk { + if_check { + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, antec); + conseq_prestate = tritv_clone(conseq_prestate); + tritv_set(bit_num(fcx, c.node), conseq_prestate, ttrue); + } + _ { } + } + + + changed |= find_pre_post_state_block(fcx, conseq_prestate, conseq); + + let poststate_res = + intersect_states(block_poststate(fcx.ccx, conseq), + expr_poststate(fcx.ccx, altern)); + /* + fcx.ccx.tcx.sess.span_note(antec.span, + "poststate_res = " + aux::tritv_to_str(fcx, poststate_res)); + fcx.ccx.tcx.sess.span_note(antec.span, + "altern poststate = " + + aux::tritv_to_str(fcx, expr_poststate(fcx.ccx, altern))); + fcx.ccx.tcx.sess.span_note(antec.span, + "conseq poststate = " + aux::tritv_to_str(fcx, + block_poststate(fcx.ccx, conseq))); + */ + + changed |= set_poststate_ann(fcx.ccx, id, poststate_res); + } + } + ret changed; +} + +fn find_pre_post_state_cap_clause(fcx: fn_ctxt, e_id: node_id, + pres: prestate, cap_clause: capture_clause) + -> bool +{ + let ccx = fcx.ccx; + let pres_changed = set_prestate_ann(ccx, e_id, pres); + let post = tritv_clone(pres); + vec::iter(cap_clause.moves) { |cap_item| + forget_in_poststate(fcx, post, cap_item.id); + } + ret set_poststate_ann(ccx, e_id, post) || pres_changed; +} + +fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool { + let num_constrs = num_constraints(fcx.enclosing); + + + alt e.node { + expr_vec(elts, _) { + ret find_pre_post_state_exprs(fcx, pres, e.id, + vec::init_elt(vec::len(elts), + init_assign), elts, + return_val); + } + expr_call(operator, operands, _) { + ret find_pre_post_state_call(fcx, pres, operator, e.id, + callee_arg_init_ops(fcx, operator.id), + operands, + controlflow_expr(fcx.ccx, operator)); + } + expr_bind(operator, maybe_args) { + let args = []; + let callee_ops = callee_arg_init_ops(fcx, operator.id); + let ops = []; + let i = 0; + for a_opt: option<@expr> in maybe_args { + alt a_opt { + none {/* no-op */ } + some(a) { ops += [callee_ops[i]]; args += [a]; } + } + i += 1; + } + ret find_pre_post_state_call(fcx, pres, operator, e.id, ops, args, + return_val); + } + expr_path(_) { ret pure_exp(fcx.ccx, e.id, pres); } + expr_log(_, lvl, ex) { + ret find_pre_post_state_two(fcx, pres, lvl, ex, e.id, oper_pure); + } + expr_mac(_) { fcx.ccx.tcx.sess.bug("unexpanded macro"); } + expr_lit(l) { ret pure_exp(fcx.ccx, e.id, pres); } + expr_fn(_, _, _, cap_clause) { + ret find_pre_post_state_cap_clause(fcx, e.id, pres, *cap_clause); + } + expr_fn_block(_, _) { ret pure_exp(fcx.ccx, e.id, pres); } + expr_block(b) { + ret find_pre_post_state_block(fcx, pres, b) | + set_prestate_ann(fcx.ccx, e.id, pres) | + set_poststate_ann(fcx.ccx, e.id, block_poststate(fcx.ccx, b)); + } + expr_rec(fields, maybe_base) { + let exs = field_exprs(fields); + let changed = + find_pre_post_state_exprs(fcx, pres, e.id, + vec::init_elt(vec::len(fields), + init_assign), + exs, return_val); + + let base_pres = alt vec::last(exs) { none { pres } + some(f) { expr_poststate(fcx.ccx, f) }}; + option::may(maybe_base, {|base| + changed |= find_pre_post_state_expr(fcx, base_pres, base) | + set_poststate_ann(fcx.ccx, e.id, + expr_poststate(fcx.ccx, base))}); + ret changed; + } + expr_tup(elts) { + ret find_pre_post_state_exprs(fcx, pres, e.id, + vec::init_elt(vec::len(elts), + init_assign), elts, + return_val); + } + expr_copy(a) { ret find_pre_post_state_sub(fcx, pres, a, e.id, none); } + expr_move(lhs, rhs) { + ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_move); + } + expr_assign(lhs, rhs) { + ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_assign); + } + expr_swap(lhs, rhs) { + ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_swap); + // Could be more precise and actually swap the role of + // lhs and rhs in constraints + } + expr_ret(maybe_ret_val) { + let changed = set_prestate_ann(fcx.ccx, e.id, pres); + /* normally, everything is true if execution continues after + a ret expression (since execution never continues locally + after a ret expression */ + // FIXME should factor this out + let post = false_postcond(num_constrs); + // except for the "diverges" bit... + kill_poststate_(fcx, fcx.enclosing.i_diverge, post); + + set_poststate_ann(fcx.ccx, e.id, post); + + alt maybe_ret_val { + none {/* do nothing */ } + some(ret_val) { + changed |= find_pre_post_state_expr(fcx, pres, ret_val); + } + } + ret changed; + } + expr_be(val) { + let changed = set_prestate_ann(fcx.ccx, e.id, pres); + let post = false_postcond(num_constrs); + // except for the "diverges" bit... + kill_poststate_(fcx, fcx.enclosing.i_diverge, post); + set_poststate_ann(fcx.ccx, e.id, post); + ret changed | find_pre_post_state_expr(fcx, pres, val); + } + expr_if(antec, conseq, maybe_alt) { + ret join_then_else(fcx, antec, conseq, maybe_alt, e.id, plain_if, + pres); + } + expr_binary(bop, l, r) { + if lazy_binop(bop) { + let changed = find_pre_post_state_expr(fcx, pres, l); + changed |= + find_pre_post_state_expr(fcx, expr_poststate(fcx.ccx, l), r); + ret changed | set_prestate_ann(fcx.ccx, e.id, pres) | + set_poststate_ann(fcx.ccx, e.id, + expr_poststate(fcx.ccx, l)); + } else { + ret find_pre_post_state_two(fcx, pres, l, r, e.id, oper_pure); + } + } + expr_assign_op(op, lhs, rhs) { + ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, + oper_assign_op); + } + expr_while(test, body) { + /* + #error("in a while loop:"); + log_expr_err(*e); + aux::log_tritv_err(fcx, block_poststate(fcx.ccx, body)); + aux::log_tritv_err(fcx, pres); + */ + let loop_pres = + intersect_states(block_poststate(fcx.ccx, body), pres); + // aux::log_tritv_err(fcx, loop_pres); + // #error("---------------"); + + let changed = + set_prestate_ann(fcx.ccx, e.id, loop_pres) | + find_pre_post_state_expr(fcx, loop_pres, test) | + find_pre_post_state_block(fcx, expr_poststate(fcx.ccx, test), + body); + + /* conservative approximation: if a loop contains a break + or cont, we assume nothing about the poststate */ + /* which is still unsound -- see [Break-unsound] */ + if has_nonlocal_exits(body) { + ret changed | set_poststate_ann(fcx.ccx, e.id, pres); + } else { + let e_post = expr_poststate(fcx.ccx, test); + let b_post = block_poststate(fcx.ccx, body); + ret changed | + set_poststate_ann(fcx.ccx, e.id, + intersect_states(e_post, b_post)); + } + } + expr_do_while(body, test) { + let loop_pres = intersect_states(expr_poststate(fcx.ccx, test), pres); + + let changed = set_prestate_ann(fcx.ccx, e.id, loop_pres); + changed |= find_pre_post_state_block(fcx, loop_pres, body); + /* conservative approximination: if the body of the loop + could break or cont, we revert to the prestate + (TODO: could treat cont differently from break, since + if there's a cont, the test will execute) */ + + changed |= + find_pre_post_state_expr(fcx, block_poststate(fcx.ccx, body), + test); + + let breaks = has_nonlocal_exits(body); + if breaks { + // this should probably be true_poststate and not pres, + // b/c the body could invalidate stuff + // FIXME [Break-unsound] + // This is unsound as it is -- consider + // while (true) { + // x <- y; + // break; + // } + // The poststate wouldn't take into account that + // y gets deinitialized + changed |= set_poststate_ann(fcx.ccx, e.id, pres); + } else { + changed |= + set_poststate_ann(fcx.ccx, e.id, + expr_poststate(fcx.ccx, test)); + } + ret changed; + } + expr_for(d, index, body) { + ret find_pre_post_state_loop(fcx, pres, d, index, body, e.id); + } + expr_index(val, sub) { + ret find_pre_post_state_two(fcx, pres, val, sub, e.id, oper_pure); + } + expr_alt(val, alts, _) { + let changed = + set_prestate_ann(fcx.ccx, e.id, pres) | + find_pre_post_state_expr(fcx, pres, val); + let e_post = expr_poststate(fcx.ccx, val); + let a_post; + if vec::len(alts) > 0u { + a_post = false_postcond(num_constrs); + for an_alt: arm in alts { + alt an_alt.guard { + some(e) { + changed |= find_pre_post_state_expr(fcx, e_post, e); + } + _ {} + } + changed |= + find_pre_post_state_block(fcx, e_post, an_alt.body); + intersect(a_post, block_poststate(fcx.ccx, an_alt.body)); + // We deliberately do *not* update changed here, because + // we'd go into an infinite loop that way, and the change + // gets made after the if expression. + + } + } else { + // No alts; poststate is the poststate of the test + + a_post = e_post; + } + ret changed | set_poststate_ann(fcx.ccx, e.id, a_post); + } + expr_field(val, _, _) { + ret find_pre_post_state_sub(fcx, pres, val, e.id, none); + } + expr_unary(_, operand) { + ret find_pre_post_state_sub(fcx, pres, operand, e.id, none); + } + expr_cast(operand, _) { + ret find_pre_post_state_sub(fcx, pres, operand, e.id, none); + } + expr_fail(maybe_fail_val) { + // FIXME Should factor out this code, + // which also appears in find_pre_post_state_exprs + /* if execution continues after fail, then everything is true! + woo! */ + let post = false_postcond(num_constrs); + handle_fail(fcx, pres, post); + ret set_prestate_ann(fcx.ccx, e.id, pres) | + set_poststate_ann(fcx.ccx, e.id, post) | + option::maybe(false, maybe_fail_val, {|fail_val| + find_pre_post_state_expr(fcx, pres, fail_val)}); + } + expr_assert(p) { + ret find_pre_post_state_sub(fcx, pres, p, e.id, none); + } + expr_check(_, p) { + /* predicate p holds after this expression executes */ + let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p); + ret find_pre_post_state_sub(fcx, pres, p, e.id, some(c.node)); + } + expr_if_check(p, conseq, maybe_alt) { + ret join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check, pres); + } + expr_break { ret pure_exp(fcx.ccx, e.id, pres); } + expr_cont { ret pure_exp(fcx.ccx, e.id, pres); } + } +} + +fn find_pre_post_state_stmt(fcx: fn_ctxt, pres: prestate, s: @stmt) -> bool { + let stmt_ann = stmt_to_ann(fcx.ccx, *s); + + log(debug, "[" + fcx.name + "]"); + #debug("*At beginning: stmt = "); + log_stmt(*s); + #debug("*prestate = "); + log(debug, tritv::to_str(stmt_ann.states.prestate)); + #debug("*poststate ="); + log(debug, tritv::to_str(stmt_ann.states.prestate)); + + alt s.node { + stmt_decl(adecl, id) { + alt adecl.node { + decl_local(alocals) { + set_prestate(stmt_ann, pres); + let c_and_p = seq_states(fcx, pres, + locals_to_bindings(fcx.ccx.tcx, alocals)); + /* important to do this in one step to ensure + termination (don't want to set changed to true + for intermediate changes) */ + + let changed = + set_poststate(stmt_ann, c_and_p.post) | c_and_p.changed; + + #debug("Summary: stmt = "); + log_stmt(*s); + #debug("prestate = "); + log(debug, tritv::to_str(stmt_ann.states.prestate)); + #debug("poststate ="); + log(debug, tritv::to_str(stmt_ann.states.prestate)); + #debug("changed ="); + log(debug, changed); + + ret changed; + } + decl_item(an_item) { + ret set_prestate(stmt_ann, pres) | set_poststate(stmt_ann, pres); + /* the outer visitor will recurse into the item */ + } + } + } + stmt_expr(ex, _) | stmt_semi(ex, _) { + let changed = + find_pre_post_state_expr(fcx, pres, ex) | + set_prestate(stmt_ann, expr_prestate(fcx.ccx, ex)) | + set_poststate(stmt_ann, expr_poststate(fcx.ccx, ex)); + + + #debug("Finally:"); + log_stmt(*s); + log(debug, "prestate = "); + log(debug, tritv::to_str(stmt_ann.states.prestate)); + #debug("poststate ="); + log(debug, (tritv::to_str(stmt_ann.states.poststate))); + #debug("changed ="); + + ret changed; + } + _ { ret false; } + } +} + + +/* Updates the pre- and post-states of statements in the block, + returns a boolean flag saying whether any pre- or poststates changed */ +fn find_pre_post_state_block(fcx: fn_ctxt, pres0: prestate, b: blk) -> bool { + /* First, set the pre-states and post-states for every expression */ + + let pres = pres0; + /* Iterate over each stmt. The new prestate is <pres>. The poststate + consist of improving <pres> with whatever variables this stmt + initializes. Then <pres> becomes the new poststate. */ + + let changed = false; + for s: @stmt in b.node.stmts { + changed |= find_pre_post_state_stmt(fcx, pres, s); + pres = stmt_poststate(fcx.ccx, *s); + } + let post = pres; + alt b.node.expr { + none { } + some(e) { + changed |= find_pre_post_state_expr(fcx, pres, e); + post = expr_poststate(fcx.ccx, e); + } + } + + set_prestate_ann(fcx.ccx, b.node.id, pres0); + set_poststate_ann(fcx.ccx, b.node.id, post); + + + /* + #error("For block:"); + log_block_err(b); + #error("poststate = "); + log_states_err(block_states(fcx.ccx, b)); + #error("pres0:"); + log_tritv_err(fcx, pres0); + #error("post:"); + log_tritv_err(fcx, post); + #error("changed = "); + log(error, changed); + */ + + ret changed; +} + +fn find_pre_post_state_fn(fcx: fn_ctxt, + f_decl: fn_decl, + f_body: blk) -> bool { + let num_constrs = num_constraints(fcx.enclosing); + // All constraints are considered false until proven otherwise. + // This ensures that intersect works correctly. + kill_all_prestate(fcx, f_body.node.id); + + // Arguments start out initialized + let block_pre = block_prestate(fcx.ccx, f_body); + for a: arg in f_decl.inputs { + set_in_prestate_constr(fcx, ninit(a.id, a.ident), block_pre); + } + + // Instantiate any constraints on the arguments so we can use them + for c: @constr in f_decl.constraints { + let tsc = ast_constr_to_ts_constr(fcx.ccx.tcx, f_decl.inputs, c); + set_in_prestate_constr(fcx, tsc, block_pre); + } + + let changed = find_pre_post_state_block(fcx, block_pre, f_body); + + // Treat the tail expression as a return statement + alt f_body.node.expr { + some(tailexpr) { + + // We don't want to clear the diverges bit for bottom typed things, + // which really do diverge. I feel like there is a cleaner way + // to do this than checking the type. + if !type_is_bot(expr_ty(fcx.ccx.tcx, tailexpr)) { + let post = false_postcond(num_constrs); + // except for the "diverges" bit... + kill_poststate_(fcx, fcx.enclosing.i_diverge, post); + set_poststate_ann(fcx.ccx, f_body.node.id, post); + } + } + none {/* fallthrough */ } + } + + /* + #error("find_pre_post_state_fn"); + log(error, changed); + fcx.ccx.tcx.sess.span_note(f_body.span, fcx.name); + */ + + ret changed; +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/tstate/tritv.rs b/src/rustc/middle/tstate/tritv.rs new file mode 100644 index 00000000000..37cc22edf3b --- /dev/null +++ b/src/rustc/middle/tstate/tritv.rs @@ -0,0 +1,308 @@ +import std::bitv; + +export t; +export create_tritv; +export tritv_clone; +export tritv_set; +export to_vec; +export trit; +export dont_care; +export ttrue; +export tfalse; +export tritv_get; +export tritv_set_all; +export tritv_difference; +export tritv_union; +export tritv_intersect; +export tritv_copy; +export tritv_clear; +export tritv_kill; +export tritv_doesntcare; +export to_str; + +/* for a fixed index: + 10 = "this constraint may or may not be true after execution" + 01 = "this constraint is definitely true" + 00 = "this constraint is definitely false" + 11 should never appear + FIXME: typestate precondition (uncertain and val must + have the same length; 11 should never appear in a given position) +*/ + +type t = {uncertain: bitv::t, val: bitv::t, nbits: uint}; +enum trit { ttrue, tfalse, dont_care, } + +fn create_tritv(len: uint) -> t { + ret {uncertain: bitv::create(len, true), + val: bitv::create(len, false), + nbits: len}; +} + + +fn trit_minus(a: trit, b: trit) -> trit { + + /* 2 - anything = 2 + 1 - 1 = 2 + 1 - 0 is an error + 1 - 2 = 1 + 0 - 1 is an error + 0 - anything else - 0 + */ + alt a { + dont_care { dont_care } + ttrue { + alt b { + ttrue { dont_care } + tfalse { ttrue } + + + + + /* internally contradictory, but + I guess it'll get flagged? */ + dont_care { + ttrue + } + } + } + tfalse { + alt b { + ttrue { tfalse } + + + + + /* see above comment */ + _ { + tfalse + } + } + } + } +} + +fn trit_or(a: trit, b: trit) -> trit { + alt a { + dont_care { b } + ttrue { ttrue } + tfalse { + alt b { + ttrue { dont_care } + + + + + /* FIXME: ?????? */ + _ { + tfalse + } + } + } + } +} + +// FIXME: This still seems kind of dodgy to me (that is, +// that 1 + ? = 1. But it might work out given that +// all variables start out in a 0 state. Probably I need +// to make it so that all constraints start out in a 0 state +// (we consider a constraint false until proven true), too. +fn trit_and(a: trit, b: trit) -> trit { + alt a { + dont_care { b } + + + + + // also seems wrong for case b = ttrue + ttrue { + alt b { + dont_care { ttrue } + + + + + // ??? Seems wrong + ttrue { + ttrue + } + + + + + + // false wins, since if something is uninit + // on one path, we care + // (Rationale: it's always safe to assume that + // a var is uninitialized or that a constraint + // needs to be re-established) + tfalse { + tfalse + } + } + } + + + + + + // Rationale: if it's uninit on one path, + // we can consider it as uninit on all paths + tfalse { + tfalse + } + } + // if the result is dont_care, that means + // a and b were both dont_care +} + +fn change(changed: bool, old: trit, new: trit) -> bool { + changed || new != old +} + +fn tritv_difference(p1: t, p2: t) -> bool { + let i: uint = 0u; + assert (p1.nbits == p2.nbits); + let sz: uint = p1.nbits; + let changed = false; + while i < sz { + let old = tritv_get(p1, i); + let new = trit_minus(old, tritv_get(p2, i)); + changed = change(changed, old, new); + tritv_set(i, p1, new); + i += 1u; + } + ret changed; +} + +fn tritv_union(p1: t, p2: t) -> bool { + let i: uint = 0u; + assert (p1.nbits == p2.nbits); + let sz: uint = p1.nbits; + let changed = false; + while i < sz { + let old = tritv_get(p1, i); + let new = trit_or(old, tritv_get(p2, i)); + changed = change(changed, old, new); + tritv_set(i, p1, new); + i += 1u; + } + ret changed; +} + +fn tritv_intersect(p1: t, p2: t) -> bool { + let i: uint = 0u; + assert (p1.nbits == p2.nbits); + let sz: uint = p1.nbits; + let changed = false; + while i < sz { + let old = tritv_get(p1, i); + let new = trit_and(old, tritv_get(p2, i)); + changed = change(changed, old, new); + tritv_set(i, p1, new); + i += 1u; + } + ret changed; +} + +fn tritv_get(v: t, i: uint) -> trit { + let b1 = bitv::get(v.uncertain, i); + let b2 = bitv::get(v.val, i); + assert (!(b1 && b2)); + if b1 { dont_care } else if b2 { ttrue } else { tfalse } +} + +fn tritv_set(i: uint, v: t, t: trit) -> bool { + let old = tritv_get(v, i); + alt t { + dont_care { + bitv::set(v.uncertain, i, true); + bitv::set(v.val, i, false); + } + ttrue { bitv::set(v.uncertain, i, false); bitv::set(v.val, i, true); } + tfalse { + bitv::set(v.uncertain, i, false); + bitv::set(v.val, i, false); + } + } + ret change(false, old, t); +} + +fn tritv_copy(target: t, source: t) -> bool { + assert (target.nbits == source.nbits); + let changed = + !bitv::equal(target.uncertain, source.uncertain) || + !bitv::equal(target.val, source.val); + bitv::assign(target.uncertain, source.uncertain); + bitv::assign(target.val, source.val); + ret changed; +} + +fn tritv_set_all(v: t) { + let i: uint = 0u; + while i < v.nbits { tritv_set(i, v, ttrue); i += 1u; } +} + +fn tritv_clear(v: t) { + let i: uint = 0u; + while i < v.nbits { tritv_set(i, v, dont_care); i += 1u; } +} + +fn tritv_kill(v: t) { + let i: uint = 0u; + while i < v.nbits { tritv_set(i, v, tfalse); i += 1u; } +} + +fn tritv_clone(v: t) -> t { + ret {uncertain: bitv::clone(v.uncertain), + val: bitv::clone(v.val), + nbits: v.nbits}; +} + +fn tritv_doesntcare(v: t) -> bool { + let i: uint = 0u; + while i < v.nbits { + if tritv_get(v, i) != dont_care { ret false; } + i += 1u; + } + ret true; +} + +fn to_vec(v: t) -> [uint] { + let i: uint = 0u; + let rslt: [uint] = []; + while i < v.nbits { + rslt += + [alt tritv_get(v, i) { + dont_care { 2u } + ttrue { 1u } + tfalse { 0u } + }]; + i += 1u; + } + ret rslt; +} + +fn to_str(v: t) -> str { + let i: uint = 0u; + let rs: str = ""; + while i < v.nbits { + rs += + alt tritv_get(v, i) { + dont_care { "?" } + ttrue { "1" } + tfalse { "0" } + }; + i += 1u; + } + ret rs; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs new file mode 100644 index 00000000000..a7e218e581b --- /dev/null +++ b/src/rustc/middle/ty.rs @@ -0,0 +1,2400 @@ +import std::{ufind, map, smallintmap}; +import std::map::hashmap; +import driver::session; +import session::session; +import syntax::ast; +import syntax::ast::*; +import syntax::ast_util; +import syntax::codemap::span; +import metadata::csearch; +import util::common::*; +import util::ppaux::ty_to_str; +import util::ppaux::ty_constr_to_str; +import syntax::print::pprust::*; + +export node_id_to_type; +export node_id_to_type_params; +export arg; +export args_eq; +export ast_constr_to_constr; +export block_ty; +export constr; +export constr_general; +export constr_table; +export count_ty_params; +export ctxt; +export def_has_ty_params; +export expr_has_ty_params; +export expr_ty; +export expr_ty_params_and_ty; +export expr_is_lval; +export fold_ty; +export field; +export field_idx; +export get_field; +export get_fields; +export fm_general; +export get_element_type; +export is_binopable; +export is_pred_ty; +export lookup_item_type; +export method; +export method_idx; +export mk_class; +export mk_ctxt; +export mk_with_id, type_def_id; +export mt; +export node_type_table; +export pat_ty; +export sequence_element_type; +export sort_methods; +export stmt_node_id; +export sty; +export substitute_type_params; +export t; +export new_ty_hash; +export enum_variants, substd_enum_variants; +export iface_methods, store_iface_methods, impl_iface; +export enum_variant_with_id; +export ty_param_bounds_and_ty; +export ty_bool, mk_bool, type_is_bool; +export ty_bot, mk_bot, type_is_bot; +export ty_box, mk_box, mk_imm_box, type_is_box, type_is_boxed; +export ty_constr, mk_constr; +export ty_opaque_closure_ptr, mk_opaque_closure_ptr; +export ty_opaque_box, mk_opaque_box; +export ty_constr_arg; +export ty_float, mk_float, mk_mach_float, type_is_fp; +export ty_fn, fn_ty, mk_fn; +export ty_fn_proto, ty_fn_ret, ty_fn_ret_style; +export ty_int, mk_int, mk_mach_int, mk_char; +export ty_str, mk_str, type_is_str; +export ty_vec, mk_vec, type_is_vec; +export ty_nil, mk_nil, type_is_nil; +export ty_iface, mk_iface; +export ty_res, mk_res; +export ty_param, mk_param; +export ty_ptr, mk_ptr, mk_mut_ptr, type_is_unsafe_ptr; +export ty_rec, mk_rec; +export ty_enum, mk_enum, type_is_enum; +export ty_tup, mk_tup; +export ty_send_type, mk_send_type; +export ty_type, mk_type; +export ty_uint, mk_uint, mk_mach_uint; +export ty_uniq, mk_uniq, mk_imm_uniq, type_is_unique_box; +export ty_var, mk_var; +export ty_self, mk_self; +export get, type_has_params, type_has_vars, type_id; +export same_type; +export ty_var_id; +export ty_fn_args; +export type_constr; +export kind, kind_sendable, kind_copyable, kind_noncopyable; +export kind_can_be_copied, kind_can_be_sent, proto_kind, kind_lteq, type_kind; +export type_err; +export type_err_to_str; +export type_has_dynamic_size; +export type_needs_drop; +export type_allows_implicit_copy; +export type_is_integral; +export type_is_numeric; +export type_is_pod; +export type_is_scalar; +export type_is_immediate; +export type_is_sequence; +export type_is_signed; +export type_is_structural; +export type_is_copyable; +export type_is_tup_like; +export type_is_unique; +export type_is_c_like_enum; +export type_structurally_contains; +export type_structurally_contains_uniques; +export type_autoderef; +export type_param; +export canon_mode; +export resolved_mode; +export arg_mode; +export unify_mode; +export set_default_mode; +export unify; +export variant_info; +export walk_ty; +export occurs_check_fails; +export closure_kind; +export ck_block; +export ck_box; +export ck_uniq; +export param_bound, param_bounds, bound_copy, bound_send, bound_iface; +export param_bounds_to_kind; +export default_arg_mode_for_ty; +export item_path; +export item_path_str; + +// Data types + +// Note: after typeck, you should use resolved_mode() to convert this mode +// into an rmode, which will take into account the results of mode inference. +type arg = {mode: ast::mode, ty: t}; + +type field = {ident: ast::ident, mt: mt}; + +type param_bounds = @[param_bound]; + +type method = {ident: ast::ident, + tps: @[param_bounds], + fty: fn_ty, + purity: ast::purity}; + +type constr_table = hashmap<ast::node_id, [constr]>; + +type mt = {ty: t, mutbl: ast::mutability}; + + +// Contains information needed to resolve types and (in the future) look up +// the types of AST nodes. +type creader_cache = hashmap<{cnum: int, pos: uint, len: uint}, t>; + +type intern_key = {struct: sty, o_def_id: option<ast::def_id>}; + +type ctxt = + @{interner: hashmap<intern_key, t_box>, + mutable next_id: uint, + sess: session::session, + def_map: resolve::def_map, + node_types: node_type_table, + node_type_substs: hashmap<node_id, [t]>, + items: ast_map::map, + freevars: freevars::freevar_map, + tcache: type_cache, + rcache: creader_cache, + short_names_cache: hashmap<t, @str>, + needs_drop_cache: hashmap<t, bool>, + kind_cache: hashmap<t, kind>, + ast_ty_to_ty_cache: hashmap<@ast::ty, option<t>>, + enum_var_cache: hashmap<def_id, @[variant_info]>, + iface_method_cache: hashmap<def_id, @[method]>, + ty_param_bounds: hashmap<ast::node_id, param_bounds>, + inferred_modes: hashmap<ast::node_id, ast::mode>}; + +type t_box = @{struct: sty, + id: uint, + has_params: bool, + has_vars: bool, + o_def_id: option<ast::def_id>}; + +// To reduce refcounting cost, we're representing types as unsafe pointers +// throughout the compiler. These are simply casted t_box values. Use ty::get +// to cast them back to a box. (Without the cast, compiler performance suffers +// ~15%.) This does mean that a t value relies on the ctxt to keep its box +// alive, and using ty::get is unsafe when the ctxt is no longer alive. +enum t_opaque {} +type t = *t_opaque; + +pure fn get(t: t) -> t_box unsafe { + let t2 = unsafe::reinterpret_cast::<t, t_box>(t); + let t3 = t2; + unsafe::leak(t2); + t3 +} + +fn type_has_params(t: t) -> bool { get(t).has_params } +fn type_has_vars(t: t) -> bool { get(t).has_vars } +fn type_def_id(t: t) -> option<ast::def_id> { get(t).o_def_id } +fn type_id(t: t) -> uint { get(t).id } + +enum closure_kind { + ck_block, + ck_box, + ck_uniq, +} + +type fn_ty = {proto: ast::proto, + inputs: [arg], + output: t, + ret_style: ret_style, + constraints: [@constr]}; + +// NB: If you change this, you'll probably want to change the corresponding +// AST structure in front/ast::rs as well. +enum sty { + ty_nil, + ty_bot, + ty_bool, + ty_int(ast::int_ty), + ty_uint(ast::uint_ty), + ty_float(ast::float_ty), + ty_str, + ty_enum(def_id, [t]), + ty_box(mt), + ty_uniq(mt), + ty_vec(mt), + ty_ptr(mt), + ty_rec([field]), + ty_fn(fn_ty), + ty_iface(def_id, [t]), + ty_class(def_id, [t]), + ty_res(def_id, t, [t]), + ty_tup([t]), + + ty_var(int), // type variable during typechecking + ty_param(uint, def_id), // type parameter + ty_self([t]), // interface method self type + + ty_type, // type_desc* + ty_send_type, // type_desc* that has been cloned into exchange heap + ty_opaque_box, // used by monomorphizer to represend any @ box + ty_constr(t, [@type_constr]), + ty_opaque_closure_ptr(closure_kind), // ptr to env for fn, fn@, fn~ +} + +// In the middle end, constraints have a def_id attached, referring +// to the definition of the operator in the constraint. +type constr_general<ARG> = spanned<constr_general_<ARG, def_id>>; +type type_constr = constr_general<@path>; +type constr = constr_general<uint>; + +// Data structures used in type unification +enum type_err { + terr_mismatch, + terr_ret_style_mismatch(ast::ret_style, ast::ret_style), + terr_box_mutability, + terr_ptr_mutability, + terr_vec_mutability, + terr_tuple_size(uint, uint), + terr_record_size(uint, uint), + terr_record_mutability, + terr_record_fields(ast::ident, ast::ident), + terr_arg_count, + terr_mode_mismatch(mode, mode), + terr_constr_len(uint, uint), + terr_constr_mismatch(@type_constr, @type_constr), +} + +enum param_bound { + bound_copy, + bound_send, + bound_iface(t), +} + +fn param_bounds_to_kind(bounds: param_bounds) -> kind { + let kind = kind_noncopyable; + for bound in *bounds { + alt bound { + bound_copy { + if kind != kind_sendable { kind = kind_copyable; } + } + bound_send { kind = kind_sendable; } + _ {} + } + } + kind +} + +type ty_param_bounds_and_ty = {bounds: @[param_bounds], ty: t}; + +type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>; + +type node_type_table = @smallintmap::smallintmap<t>; + +fn mk_rcache() -> creader_cache { + type val = {cnum: int, pos: uint, len: uint}; + fn hash_cache_entry(k: val) -> uint { + ret (k.cnum as uint) + k.pos + k.len; + } + fn eq_cache_entries(a: val, b: val) -> bool { + ret a.cnum == b.cnum && a.pos == b.pos && a.len == b.len; + } + ret map::mk_hashmap(hash_cache_entry, eq_cache_entries); +} + +fn new_ty_hash<V: copy>() -> map::hashmap<t, V> { + map::mk_hashmap({|&&t: t| type_id(t)}, + {|&&a: t, &&b: t| type_id(a) == type_id(b)}) +} + +fn mk_ctxt(s: session::session, dm: resolve::def_map, amap: ast_map::map, + freevars: freevars::freevar_map) -> ctxt { + let interner = map::mk_hashmap({|&&k: intern_key| + hash_type_structure(k.struct) + + option::maybe(0u, k.o_def_id, ast_util::hash_def_id) + }, {|&&a, &&b| a == b}); + @{interner: interner, + mutable next_id: 0u, + sess: s, + def_map: dm, + node_types: @smallintmap::mk(), + node_type_substs: map::new_int_hash(), + items: amap, + freevars: freevars, + tcache: new_def_hash(), + rcache: mk_rcache(), + short_names_cache: new_ty_hash(), + needs_drop_cache: new_ty_hash(), + kind_cache: new_ty_hash(), + ast_ty_to_ty_cache: map::mk_hashmap(ast_util::hash_ty, ast_util::eq_ty), + enum_var_cache: new_def_hash(), + iface_method_cache: new_def_hash(), + ty_param_bounds: map::new_int_hash(), + inferred_modes: map::new_int_hash()} +} + + +// Type constructors +fn mk_t(cx: ctxt, st: sty) -> t { mk_t_with_id(cx, st, none) } + +// Interns a type/name combination, stores the resulting box in cx.interner, +// and returns the box as cast to an unsafe ptr (see comments for t above). +fn mk_t_with_id(cx: ctxt, st: sty, o_def_id: option<ast::def_id>) -> t { + let key = {struct: st, o_def_id: o_def_id}; + alt cx.interner.find(key) { + some(t) { unsafe { ret unsafe::reinterpret_cast(t); } } + _ {} + } + let has_params = false, has_vars = false; + fn derive_flags(&has_params: bool, &has_vars: bool, tt: t) { + let t = get(tt); + has_params |= t.has_params; + has_vars |= t.has_vars; + } + alt st { + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | + ty_str | ty_type | ty_send_type | ty_opaque_closure_ptr(_) | + ty_opaque_box {} + ty_param(_, _) { has_params = true; } + ty_var(_) | ty_self(_) { has_vars = true; } + ty_enum(_, tys) | ty_iface(_, tys) | ty_class(_, tys) { + for tt in tys { derive_flags(has_params, has_vars, tt); } + } + ty_box(m) | ty_uniq(m) | ty_vec(m) | ty_ptr(m) { + derive_flags(has_params, has_vars, m.ty); + } + ty_rec(flds) { + for f in flds { derive_flags(has_params, has_vars, f.mt.ty); } + } + ty_tup(ts) { + for tt in ts { derive_flags(has_params, has_vars, tt); } + } + ty_fn(f) { + for a in f.inputs { derive_flags(has_params, has_vars, a.ty); } + derive_flags(has_params, has_vars, f.output); + } + ty_res(_, tt, tps) { + derive_flags(has_params, has_vars, tt); + for tt in tps { derive_flags(has_params, has_vars, tt); } + } + ty_constr(tt, _) { + derive_flags(has_params, has_vars, tt); + } + } + let t = @{struct: st, + id: cx.next_id, + has_params: has_params, + has_vars: has_vars, + o_def_id: o_def_id}; + cx.interner.insert(key, t); + cx.next_id += 1u; + unsafe { unsafe::reinterpret_cast(t) } +} + +fn mk_nil(cx: ctxt) -> t { mk_t(cx, ty_nil) } + +fn mk_bot(cx: ctxt) -> t { mk_t(cx, ty_bot) } + +fn mk_bool(cx: ctxt) -> t { mk_t(cx, ty_bool) } + +fn mk_int(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_i)) } + +fn mk_float(cx: ctxt) -> t { mk_t(cx, ty_float(ast::ty_f)) } + +fn mk_uint(cx: ctxt) -> t { mk_t(cx, ty_uint(ast::ty_u)) } + +fn mk_mach_int(cx: ctxt, tm: ast::int_ty) -> t { mk_t(cx, ty_int(tm)) } + +fn mk_mach_uint(cx: ctxt, tm: ast::uint_ty) -> t { mk_t(cx, ty_uint(tm)) } + +fn mk_mach_float(cx: ctxt, tm: ast::float_ty) -> t { mk_t(cx, ty_float(tm)) } + +fn mk_char(cx: ctxt) -> t { mk_t(cx, ty_int(ast::ty_char)) } + +fn mk_str(cx: ctxt) -> t { mk_t(cx, ty_str) } + +fn mk_enum(cx: ctxt, did: ast::def_id, tys: [t]) -> t { + mk_t(cx, ty_enum(did, tys)) +} + +fn mk_box(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_box(tm)) } + +fn mk_imm_box(cx: ctxt, ty: t) -> t { mk_box(cx, {ty: ty, + mutbl: ast::m_imm}) } + +fn mk_uniq(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_uniq(tm)) } + +fn mk_imm_uniq(cx: ctxt, ty: t) -> t { mk_uniq(cx, {ty: ty, + mutbl: ast::m_imm}) } + +fn mk_ptr(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_ptr(tm)) } + +fn mk_mut_ptr(cx: ctxt, ty: t) -> t { mk_ptr(cx, {ty: ty, + mutbl: ast::m_mutbl}) } + +fn mk_vec(cx: ctxt, tm: mt) -> t { mk_t(cx, ty_vec(tm)) } + +fn mk_rec(cx: ctxt, fs: [field]) -> t { mk_t(cx, ty_rec(fs)) } + +fn mk_constr(cx: ctxt, t: t, cs: [@type_constr]) -> t { + mk_t(cx, ty_constr(t, cs)) +} + +fn mk_tup(cx: ctxt, ts: [t]) -> t { mk_t(cx, ty_tup(ts)) } + +fn mk_fn(cx: ctxt, fty: fn_ty) -> t { mk_t(cx, ty_fn(fty)) } + +fn mk_iface(cx: ctxt, did: ast::def_id, tys: [t]) -> t { + mk_t(cx, ty_iface(did, tys)) +} + +fn mk_class(cx: ctxt, class_id: ast::def_id, tys: [t]) -> t { + mk_t(cx, ty_class(class_id, tys)) +} + +fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t { + mk_t(cx, ty_res(did, inner, tps)) +} + +fn mk_var(cx: ctxt, v: int) -> t { mk_t(cx, ty_var(v)) } + +fn mk_self(cx: ctxt, tps: [t]) -> t { mk_t(cx, ty_self(tps)) } + +fn mk_param(cx: ctxt, n: uint, k: def_id) -> t { mk_t(cx, ty_param(n, k)) } + +fn mk_type(cx: ctxt) -> t { mk_t(cx, ty_type) } + +fn mk_send_type(cx: ctxt) -> t { mk_t(cx, ty_send_type) } + +fn mk_opaque_closure_ptr(cx: ctxt, ck: closure_kind) -> t { + mk_t(cx, ty_opaque_closure_ptr(ck)) +} + +fn mk_opaque_box(cx: ctxt) -> t { mk_t(cx, ty_opaque_box) } + +fn mk_with_id(cx: ctxt, base: t, def_id: ast::def_id) -> t { + mk_t_with_id(cx, get(base).struct, some(def_id)) +} + +// Converts s to its machine type equivalent +pure fn mach_sty(cfg: @session::config, t: t) -> sty { + alt get(t).struct { + ty_int(ast::ty_i) { ty_int(cfg.int_type) } + ty_uint(ast::ty_u) { ty_uint(cfg.uint_type) } + ty_float(ast::ty_f) { ty_float(cfg.float_type) } + s { s } + } +} + +fn default_arg_mode_for_ty(ty: ty::t) -> ast::rmode { + if ty::type_is_immediate(ty) { ast::by_val } + else { ast::by_ref } +} + +fn walk_ty(cx: ctxt, ty: t, f: fn(t)) { + alt get(ty).struct { + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | + ty_str | ty_send_type | ty_type | ty_opaque_box | + ty_opaque_closure_ptr(_) | ty_var(_) | ty_param(_, _) {} + ty_box(tm) | ty_vec(tm) | ty_ptr(tm) { walk_ty(cx, tm.ty, f); } + ty_enum(_, subtys) | ty_iface(_, subtys) | ty_class(_, subtys) + | ty_self(subtys) { + for subty: t in subtys { walk_ty(cx, subty, f); } + } + ty_rec(fields) { + for fl: field in fields { walk_ty(cx, fl.mt.ty, f); } + } + ty_tup(ts) { for tt in ts { walk_ty(cx, tt, f); } } + ty_fn(ft) { + for a: arg in ft.inputs { walk_ty(cx, a.ty, f); } + walk_ty(cx, ft.output, f); + } + ty_res(_, sub, tps) { + walk_ty(cx, sub, f); + for tp: t in tps { walk_ty(cx, tp, f); } + } + ty_constr(sub, _) { walk_ty(cx, sub, f); } + ty_uniq(tm) { walk_ty(cx, tm.ty, f); } + } + f(ty); +} + +enum fold_mode { + fm_var(fn@(int) -> t), + fm_param(fn@(uint, def_id) -> t), + fm_general(fn@(t) -> t), +} + +fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t { + let ty = ty_0; + + let tb = get(ty); + alt fld { + fm_var(_) { if !tb.has_vars { ret ty; } } + fm_param(_) { if !tb.has_params { ret ty; } } + fm_general(_) {/* no fast path */ } + } + + alt tb.struct { + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | + ty_str | ty_type | ty_send_type | ty_opaque_closure_ptr(_) | + ty_opaque_box {} + ty_box(tm) { + ty = mk_box(cx, {ty: fold_ty(cx, fld, tm.ty), mutbl: tm.mutbl}); + } + ty_uniq(tm) { + ty = mk_uniq(cx, {ty: fold_ty(cx, fld, tm.ty), mutbl: tm.mutbl}); + } + ty_ptr(tm) { + ty = mk_ptr(cx, {ty: fold_ty(cx, fld, tm.ty), mutbl: tm.mutbl}); + } + ty_vec(tm) { + ty = mk_vec(cx, {ty: fold_ty(cx, fld, tm.ty), mutbl: tm.mutbl}); + } + ty_enum(tid, subtys) { + ty = mk_enum(cx, tid, vec::map(subtys, {|t| fold_ty(cx, fld, t) })); + } + ty_iface(did, subtys) { + ty = mk_iface(cx, did, vec::map(subtys, {|t| fold_ty(cx, fld, t) })); + } + ty_self(subtys) { + ty = mk_self(cx, vec::map(subtys, {|t| fold_ty(cx, fld, t) })); + } + ty_rec(fields) { + let new_fields: [field] = []; + for fl: field in fields { + let new_ty = fold_ty(cx, fld, fl.mt.ty); + let new_mt = {ty: new_ty, mutbl: fl.mt.mutbl}; + new_fields += [{ident: fl.ident, mt: new_mt}]; + } + ty = mk_rec(cx, new_fields); + } + ty_tup(ts) { + let new_ts = []; + for tt in ts { new_ts += [fold_ty(cx, fld, tt)]; } + ty = mk_tup(cx, new_ts); + } + ty_fn(f) { + let new_args: [arg] = []; + for a: arg in f.inputs { + let new_ty = fold_ty(cx, fld, a.ty); + new_args += [{mode: a.mode, ty: new_ty}]; + } + ty = mk_fn(cx, {inputs: new_args, + output: fold_ty(cx, fld, f.output) + with f}); + } + ty_res(did, subty, tps) { + let new_tps = []; + for tp: t in tps { new_tps += [fold_ty(cx, fld, tp)]; } + ty = mk_res(cx, did, fold_ty(cx, fld, subty), new_tps); + } + ty_var(id) { + alt fld { fm_var(folder) { ty = folder(id); } _ {/* no-op */ } } + } + ty_param(id, did) { + alt fld { fm_param(folder) { ty = folder(id, did); } _ {} } + } + ty_constr(subty, cs) { + ty = mk_constr(cx, fold_ty(cx, fld, subty), cs); + } + _ { + cx.sess.fatal("Unsupported sort of type in fold_ty"); + } + } + alt tb.o_def_id { + some(did) { ty = mk_t_with_id(cx, get(ty).struct, some(did)); } + _ {} + } + + // If this is a general type fold, then we need to run it now. + alt fld { fm_general(folder) { ret folder(ty); } _ { ret ty; } } +} + + +// Type utilities + +fn type_is_nil(ty: t) -> bool { get(ty).struct == ty_nil } + +fn type_is_bot(ty: t) -> bool { get(ty).struct == ty_bot } + +fn type_is_bool(ty: t) -> bool { get(ty).struct == ty_bool } + +fn type_is_structural(ty: t) -> bool { + alt get(ty).struct { + ty_rec(_) | ty_tup(_) | ty_enum(_, _) | ty_fn(_) | + ty_iface(_, _) | ty_res(_, _, _) { true } + _ { false } + } +} + +fn type_is_copyable(cx: ctxt, ty: t) -> bool { + ret kind_can_be_copied(type_kind(cx, ty)); +} + +fn type_is_sequence(ty: t) -> bool { + alt get(ty).struct { + ty_str { ret true; } + ty_vec(_) { ret true; } + _ { ret false; } + } +} + +fn type_is_str(ty: t) -> bool { get(ty).struct == ty_str } + +fn sequence_element_type(cx: ctxt, ty: t) -> t { + alt get(ty).struct { + ty_str { ret mk_mach_uint(cx, ast::ty_u8); } + ty_vec(mt) { ret mt.ty; } + _ { cx.sess.bug("sequence_element_type called on non-sequence value"); } + } +} + +pure fn type_is_tup_like(ty: t) -> bool { + alt get(ty).struct { + ty_rec(_) | ty_tup(_) { true } + _ { false } + } +} + +fn get_element_type(ty: t, i: uint) -> t { + alt get(ty).struct { + ty_rec(flds) { ret flds[i].mt.ty; } + ty_tup(ts) { ret ts[i]; } + _ { fail "get_element_type called on invalid type"; } + } +} + +pure fn type_is_box(ty: t) -> bool { + alt get(ty).struct { + ty_box(_) { ret true; } + _ { ret false; } + } +} + +pure fn type_is_boxed(ty: t) -> bool { + alt get(ty).struct { + ty_box(_) | ty_opaque_box { true } + _ { false } + } +} + +pure fn type_is_unique_box(ty: t) -> bool { + alt get(ty).struct { + ty_uniq(_) { ret true; } + _ { ret false; } + } +} + +pure fn type_is_unsafe_ptr(ty: t) -> bool { + alt get(ty).struct { + ty_ptr(_) { ret true; } + _ { ret false; } + } +} + +pure fn type_is_vec(ty: t) -> bool { + ret alt get(ty).struct { + ty_vec(_) { true } + ty_str { true } + _ { false } + }; +} + +pure fn type_is_unique(ty: t) -> bool { + alt get(ty).struct { + ty_uniq(_) { ret true; } + ty_vec(_) { true } + ty_str { true } + _ { ret false; } + } +} + +pure fn type_is_scalar(ty: t) -> bool { + alt get(ty).struct { + ty_nil | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | + ty_send_type | ty_type | ty_ptr(_) { true } + _ { false } + } +} + +// FIXME maybe inline this for speed? +fn type_is_immediate(ty: t) -> bool { + ret type_is_scalar(ty) || type_is_boxed(ty) || + type_is_unique(ty); +} + +fn type_needs_drop(cx: ctxt, ty: t) -> bool { + alt cx.needs_drop_cache.find(ty) { + some(result) { ret result; } + none {/* fall through */ } + } + + let accum = false; + let result = alt get(ty).struct { + // scalar types + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | + ty_type | ty_ptr(_) { false } + ty_rec(flds) { + for f in flds { if type_needs_drop(cx, f.mt.ty) { accum = true; } } + accum + } + ty_tup(elts) { + for m in elts { if type_needs_drop(cx, m) { accum = true; } } + accum + } + ty_enum(did, tps) { + let variants = enum_variants(cx, did); + for variant in *variants { + for aty in variant.args { + // Perform any type parameter substitutions. + let arg_ty = substitute_type_params(cx, tps, aty); + if type_needs_drop(cx, arg_ty) { accum = true; } + } + if accum { break; } + } + accum + } + _ { true } + }; + + cx.needs_drop_cache.insert(ty, result); + ret result; +} + +enum kind { kind_sendable, kind_copyable, kind_noncopyable, } + +// Using these query functons is preferable to direct comparison or matching +// against the kind constants, as we may modify the kind hierarchy in the +// future. +pure fn kind_can_be_copied(k: kind) -> bool { + ret alt k { + kind_sendable { true } + kind_copyable { true } + kind_noncopyable { false } + }; +} + +pure fn kind_can_be_sent(k: kind) -> bool { + ret alt k { + kind_sendable { true } + kind_copyable { false } + kind_noncopyable { false } + }; +} + +fn proto_kind(p: proto) -> kind { + alt p { + ast::proto_any { kind_noncopyable } + ast::proto_block { kind_noncopyable } + ast::proto_box { kind_copyable } + ast::proto_uniq { kind_sendable } + ast::proto_bare { kind_sendable } + } +} + +fn kind_lteq(a: kind, b: kind) -> bool { + alt a { + kind_noncopyable { true } + kind_copyable { b != kind_noncopyable } + kind_sendable { b == kind_sendable } + } +} + +fn lower_kind(a: kind, b: kind) -> kind { + if kind_lteq(a, b) { a } else { b } +} + +fn type_kind(cx: ctxt, ty: t) -> kind { + alt cx.kind_cache.find(ty) { + some(result) { ret result; } + none {/* fall through */ } + } + + // Insert a default in case we loop back on self recursively. + cx.kind_cache.insert(ty, kind_sendable); + + let result = alt get(ty).struct { + // Scalar and unique types are sendable + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | + ty_ptr(_) | ty_send_type | ty_str { kind_sendable } + ty_type { kind_copyable } + ty_fn(f) { proto_kind(f.proto) } + ty_opaque_closure_ptr(ck_block) { kind_noncopyable } + ty_opaque_closure_ptr(ck_box) { kind_copyable } + ty_opaque_closure_ptr(ck_uniq) { kind_sendable } + // Those with refcounts-to-inner raise pinned to shared, + // lower unique to shared. Therefore just set result to shared. + ty_box(_) | ty_iface(_, _) | ty_opaque_box { kind_copyable } + // Boxes and unique pointers raise pinned to shared. + ty_vec(tm) | ty_uniq(tm) { type_kind(cx, tm.ty) } + // Records lower to the lowest of their members. + ty_rec(flds) { + let lowest = kind_sendable; + for f in flds { lowest = lower_kind(lowest, type_kind(cx, f.mt.ty)); } + lowest + } + // Tuples lower to the lowest of their members. + ty_tup(tys) { + let lowest = kind_sendable; + for ty in tys { lowest = lower_kind(lowest, type_kind(cx, ty)); } + lowest + } + // Enums lower to the lowest of their variants. + ty_enum(did, tps) { + let lowest = kind_sendable; + for variant in *enum_variants(cx, did) { + for aty in variant.args { + // Perform any type parameter substitutions. + let arg_ty = substitute_type_params(cx, tps, aty); + lowest = lower_kind(lowest, type_kind(cx, arg_ty)); + if lowest == kind_noncopyable { break; } + } + } + lowest + } + // Resources are always noncopyable. + ty_res(did, inner, tps) { kind_noncopyable } + ty_param(_, did) { + param_bounds_to_kind(cx.ty_param_bounds.get(did.node)) + } + ty_constr(t, _) { type_kind(cx, t) } + _ { cx.sess.bug("Bad type in type_kind"); } + }; + + cx.kind_cache.insert(ty, result); + ret result; +} + +fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) -> + bool { + let sty = get(ty).struct; + if test(sty) { ret true; } + alt sty { + ty_enum(did, tps) { + for variant in *enum_variants(cx, did) { + for aty in variant.args { + let sty = substitute_type_params(cx, tps, aty); + if type_structurally_contains(cx, sty, test) { ret true; } + } + } + ret false; + } + ty_rec(fields) { + for field in fields { + if type_structurally_contains(cx, field.mt.ty, test) { ret true; } + } + ret false; + } + ty_tup(ts) { + for tt in ts { + if type_structurally_contains(cx, tt, test) { ret true; } + } + ret false; + } + ty_res(_, sub, tps) { + let sty = substitute_type_params(cx, tps, sub); + ret type_structurally_contains(cx, sty, test); + } + _ { ret false; } + } +} + +pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool unchecked { + /* type_structurally_contains can't be declared pure + because it takes a function argument. But it should be + referentially transparent, since a given type's size should + never change once it's created. + (It would be interesting to think about how to make such properties + actually checkable. It seems to me like a lot of properties + that the type context tracks about types should be immutable.) + */ + type_has_params(ty) && type_structurally_contains(cx, ty) {|sty| + alt sty { + ty_param(_, _) { true } + _ { false } + } + } +} + +// Returns true for noncopyable types and types where a copy of a value can be +// distinguished from the value itself. I.e. types with mutable content that's +// not shared through a pointer. +fn type_allows_implicit_copy(cx: ctxt, ty: t) -> bool { + ret !type_structurally_contains(cx, ty, {|sty| + alt sty { + ty_param(_, _) { true } + ty_vec(mt) { + mt.mutbl != ast::m_imm + } + ty_rec(fields) { + for field in fields { + if field.mt.mutbl != ast::m_imm { + ret true; + } + } + false + } + _ { false } + } + }) && type_kind(cx, ty) != kind_noncopyable; +} + +fn type_structurally_contains_uniques(cx: ctxt, ty: t) -> bool { + ret type_structurally_contains(cx, ty, {|sty| + ret alt sty { + ty_uniq(_) { ret true; } + ty_vec(_) { true } + ty_str { true } + _ { ret false; } + }; + }); +} + +fn type_is_integral(ty: t) -> bool { + alt get(ty).struct { + ty_int(_) | ty_uint(_) | ty_bool { true } + _ { false } + } +} + +fn type_is_fp(ty: t) -> bool { + alt get(ty).struct { + ty_float(_) { true } + _ { false } + } +} + +fn type_is_numeric(ty: t) -> bool { + ret type_is_integral(ty) || type_is_fp(ty); +} + +fn type_is_signed(ty: t) -> bool { + alt get(ty).struct { + ty_int(_) { true } + _ { false } + } +} + +// Whether a type is Plain Old Data -- meaning it does not contain pointers +// that the cycle collector might care about. +fn type_is_pod(cx: ctxt, ty: t) -> bool { + let result = true; + alt get(ty).struct { + // Scalar types + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) | + ty_send_type | ty_type | ty_ptr(_) { result = true; } + // Boxed types + ty_str | ty_box(_) | ty_uniq(_) | ty_vec(_) | ty_fn(_) | + ty_iface(_, _) | ty_opaque_box { result = false; } + // Structural types + ty_enum(did, tps) { + let variants = enum_variants(cx, did); + for variant: variant_info in *variants { + let tup_ty = mk_tup(cx, variant.args); + + // Perform any type parameter substitutions. + tup_ty = substitute_type_params(cx, tps, tup_ty); + if !type_is_pod(cx, tup_ty) { result = false; } + } + } + ty_rec(flds) { + for f: field in flds { + if !type_is_pod(cx, f.mt.ty) { result = false; } + } + } + ty_tup(elts) { + for elt in elts { if !type_is_pod(cx, elt) { result = false; } } + } + ty_res(_, inner, tps) { + result = type_is_pod(cx, substitute_type_params(cx, tps, inner)); + } + ty_constr(subt, _) { result = type_is_pod(cx, subt); } + ty_param(_, _) { result = false; } + ty_opaque_closure_ptr(_) { result = true; } + _ { cx.sess.bug("unexpected type in type_is_pod"); } + } + + ret result; +} + +fn type_is_enum(ty: t) -> bool { + alt get(ty).struct { + ty_enum(_, _) { ret true; } + _ { ret false;} + } +} + +// Whether a type is enum like, that is a enum type with only nullary +// constructors +fn type_is_c_like_enum(cx: ctxt, ty: t) -> bool { + alt get(ty).struct { + ty_enum(did, tps) { + let variants = enum_variants(cx, did); + let some_n_ary = vec::any(*variants, {|v| vec::len(v.args) > 0u}); + ret !some_n_ary; + } + _ { ret false;} + } +} + +fn type_param(ty: t) -> option<uint> { + alt get(ty).struct { + ty_param(id, _) { ret some(id); } + _ {/* fall through */ } + } + ret none; +} + +// Returns a vec of all the type variables +// occurring in t. It may contain duplicates. +fn vars_in_type(cx: ctxt, ty: t) -> [int] { + let rslt = []; + walk_ty(cx, ty) {|ty| + alt get(ty).struct { ty_var(v) { rslt += [v]; } _ { } } + } + rslt +} + +fn type_autoderef(cx: ctxt, t: t) -> t { + let t1 = t; + while true { + alt get(t1).struct { + ty_box(mt) | ty_uniq(mt) { t1 = mt.ty; } + ty_res(_, inner, tps) { + t1 = substitute_type_params(cx, tps, inner); + } + ty_enum(did, tps) { + let variants = enum_variants(cx, did); + if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u { + break; + } + t1 = substitute_type_params(cx, tps, variants[0].args[0]); + } + _ { break; } + } + } + ret t1; +} + +// Type hashing. +fn hash_type_structure(st: sty) -> uint { + fn hash_uint(id: uint, n: uint) -> uint { (id << 2u) + n } + fn hash_def(id: uint, did: ast::def_id) -> uint { + let h = (id << 2u) + (did.crate as uint); + (h << 2u) + (did.node as uint) + } + fn hash_subty(id: uint, subty: t) -> uint { (id << 2u) + type_id(subty) } + fn hash_subtys(id: uint, subtys: [t]) -> uint { + let h = id; + for s in subtys { h = (h << 2u) + type_id(s) } + h + } + fn hash_type_constr(id: uint, c: @type_constr) -> uint { + let h = id; + h = (h << 2u) + hash_def(h, c.node.id); + // FIXME this makes little sense + for a in c.node.args { + alt a.node { + carg_base { h += h << 2u; } + carg_lit(_) { fail "lit args not implemented yet"; } + carg_ident(p) { h += h << 2u; } + } + } + h + } + alt st { + ty_nil { 0u } ty_bool { 1u } + ty_int(t) { + alt t { + ast::ty_i { 2u } ast::ty_char { 3u } ast::ty_i8 { 4u } + ast::ty_i16 { 5u } ast::ty_i32 { 6u } ast::ty_i64 { 7u } + } + } + ty_uint(t) { + alt t { + ast::ty_u { 8u } ast::ty_u8 { 9u } ast::ty_u16 { 10u } + ast::ty_u32 { 11u } ast::ty_u64 { 12u } + } + } + ty_float(t) { + alt t { ast::ty_f { 13u } ast::ty_f32 { 14u } ast::ty_f64 { 15u } } + } + ty_str { 17u } + ty_enum(did, tys) { + let h = hash_def(18u, did); + for typ: t in tys { h = hash_subty(h, typ); } + h + } + ty_box(mt) { hash_subty(19u, mt.ty) } + ty_vec(mt) { hash_subty(21u, mt.ty) } + ty_rec(fields) { + let h = 26u; + for f in fields { h = hash_subty(h, f.mt.ty); } + h + } + ty_tup(ts) { hash_subtys(25u, ts) } + ty_fn(f) { + let h = 27u; + for a in f.inputs { h = hash_subty(h, a.ty); } + hash_subty(h, f.output) + } + ty_var(v) { hash_uint(30u, v as uint) } + ty_param(pid, did) { hash_def(hash_uint(31u, pid), did) } + ty_self(ts) { + let h = 28u; + for t in ts { h = hash_subty(h, t); } + h + } + ty_type { 32u } + ty_bot { 34u } + ty_ptr(mt) { hash_subty(35u, mt.ty) } + ty_res(did, sub, tps) { + let h = hash_subty(hash_def(18u, did), sub); + hash_subtys(h, tps) + } + ty_constr(t, cs) { + let h = hash_subty(36u, t); + for c in cs { h = (h << 2u) + hash_type_constr(h, c); } + h + } + ty_uniq(mt) { hash_subty(37u, mt.ty) } + ty_send_type { 38u } + ty_iface(did, tys) { + let h = hash_def(40u, did); + for typ: t in tys { h = hash_subty(h, typ); } + h + } + ty_opaque_closure_ptr(ck_block) { 41u } + ty_opaque_closure_ptr(ck_box) { 42u } + ty_opaque_closure_ptr(ck_uniq) { 43u } + ty_opaque_box { 44u } + ty_class(did, tys) { + let h = hash_def(45u, did); + for typ: t in tys { h = hash_subty(h, typ); } + h + } + } +} + +fn arg_eq<T>(eq: fn(T, T) -> bool, + a: @sp_constr_arg<T>, + b: @sp_constr_arg<T>) + -> bool { + alt a.node { + ast::carg_base { + alt b.node { ast::carg_base { ret true; } _ { ret false; } } + } + ast::carg_ident(s) { + alt b.node { ast::carg_ident(t) { ret eq(s, t); } _ { ret false; } } + } + ast::carg_lit(l) { + alt b.node { + ast::carg_lit(m) { ret ast_util::lit_eq(l, m); } _ { ret false; } + } + } + } +} + +fn args_eq<T>(eq: fn(T, T) -> bool, + a: [@sp_constr_arg<T>], + b: [@sp_constr_arg<T>]) -> bool { + let i: uint = 0u; + for arg: @sp_constr_arg<T> in a { + if !arg_eq(eq, arg, b[i]) { ret false; } + i += 1u; + } + ret true; +} + +fn constr_eq(c: @constr, d: @constr) -> bool { + fn eq_int(&&x: uint, &&y: uint) -> bool { ret x == y; } + ret path_to_str(c.node.path) == path_to_str(d.node.path) && + // FIXME: hack + args_eq(eq_int, c.node.args, d.node.args); +} + +fn constrs_eq(cs: [@constr], ds: [@constr]) -> bool { + if vec::len(cs) != vec::len(ds) { ret false; } + let i = 0u; + for c: @constr in cs { if !constr_eq(c, ds[i]) { ret false; } i += 1u; } + ret true; +} + +fn node_id_to_type(cx: ctxt, id: ast::node_id) -> t { + smallintmap::get(*cx.node_types, id as uint) +} + +fn node_id_to_type_params(cx: ctxt, id: ast::node_id) -> [t] { + alt cx.node_type_substs.find(id) { + none { ret []; } + some(ts) { ret ts; } + } +} + +fn node_id_has_type_params(cx: ctxt, id: ast::node_id) -> bool { + ret cx.node_type_substs.contains_key(id); +} + +// Returns the number of distinct type parameters in the given type. +fn count_ty_params(cx: ctxt, ty: t) -> uint { + let param_indices = []; + walk_ty(cx, ty) {|t| + alt get(t).struct { + ty_param(param_idx, _) { + if !vec::any(param_indices, {|i| i == param_idx}) { + param_indices += [param_idx]; + } + } + _ {} + } + } + vec::len(param_indices) +} + +// Type accessors for substructures of types +fn ty_fn_args(fty: t) -> [arg] { + alt get(fty).struct { + ty_fn(f) { f.inputs } + _ { fail "ty_fn_args() called on non-fn type"; } + } +} + +fn ty_fn_proto(fty: t) -> ast::proto { + alt get(fty).struct { + ty_fn(f) { f.proto } + _ { fail "ty_fn_proto() called on non-fn type"; } + } +} + +pure fn ty_fn_ret(fty: t) -> t { + alt get(fty).struct { + ty_fn(f) { f.output } + _ { fail "ty_fn_ret() called on non-fn type"; } + } +} + +fn ty_fn_ret_style(fty: t) -> ast::ret_style { + alt get(fty).struct { + ty_fn(f) { f.ret_style } + _ { fail "ty_fn_ret_style() called on non-fn type"; } + } +} + +fn is_fn_ty(fty: t) -> bool { + alt get(fty).struct { + ty_fn(_) { ret true; } + _ { ret false; } + } +} + +// Just checks whether it's a fn that returns bool, +// not its purity. +fn is_pred_ty(fty: t) -> bool { + is_fn_ty(fty) && type_is_bool(ty_fn_ret(fty)) +} + +fn ty_var_id(typ: t) -> int { + alt get(typ).struct { + ty_var(vid) { ret vid; } + _ { #error("ty_var_id called on non-var ty"); fail; } + } +} + + +// Type accessors for AST nodes +fn block_ty(cx: ctxt, b: ast::blk) -> t { + ret node_id_to_type(cx, b.node.id); +} + + +// Returns the type of a pattern as a monotype. Like @expr_ty, this function +// doesn't provide type parameter substitutions. +fn pat_ty(cx: ctxt, pat: @ast::pat) -> t { + ret node_id_to_type(cx, pat.id); +} + + +// Returns the type of an expression as a monotype. +// +// NB: This type doesn't provide type parameter substitutions; e.g. if you +// ask for the type of "id" in "id(3)", it will return "fn(&int) -> int" +// instead of "fn(t) -> T with T = int". If this isn't what you want, see +// expr_ty_params_and_ty() below. +fn expr_ty(cx: ctxt, expr: @ast::expr) -> t { + ret node_id_to_type(cx, expr.id); +} + +fn expr_ty_params_and_ty(cx: ctxt, expr: @ast::expr) -> {params: [t], ty: t} { + ret {params: node_id_to_type_params(cx, expr.id), + ty: node_id_to_type(cx, expr.id)}; +} + +fn expr_has_ty_params(cx: ctxt, expr: @ast::expr) -> bool { + ret node_id_has_type_params(cx, expr.id); +} + +fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool { + alt e.node { + ast::expr_path(_) | ast::expr_unary(ast::deref, _) { true } + ast::expr_field(_, _, _) | ast::expr_index(_, _) { + !method_map.contains_key(e.id) + } + _ { false } + } +} + +fn stmt_node_id(s: @ast::stmt) -> ast::node_id { + alt s.node { + ast::stmt_decl(_, id) | stmt_expr(_, id) | stmt_semi(_, id) { + ret id; + } + } +} + +fn field_idx(id: ast::ident, fields: [field]) -> option<uint> { + let i = 0u; + for f in fields { if f.ident == id { ret some(i); } i += 1u; } + ret none; +} + +fn get_field(rec_ty: t, id: ast::ident) -> field { + alt check vec::find(get_fields(rec_ty), {|f| str::eq(f.ident, id) }) { + some(f) { f } + } +} + +fn get_fields(rec_ty:t) -> [field] { + alt check get(rec_ty).struct { + ty_rec(fields) { fields } + } +} + +fn method_idx(id: ast::ident, meths: [method]) -> option<uint> { + let i = 0u; + for m in meths { if m.ident == id { ret some(i); } i += 1u; } + ret none; +} + +fn sort_methods(meths: [method]) -> [method] { + fn method_lteq(a: method, b: method) -> bool { + ret str::le(a.ident, b.ident); + } + ret std::sort::merge_sort(bind method_lteq(_, _), meths); +} + +fn occurs_check_fails(tcx: ctxt, sp: option<span>, vid: int, rt: t) -> + bool { + // Fast path + if !type_has_vars(rt) { ret false; } + + // Occurs check! + if vec::contains(vars_in_type(tcx, rt), vid) { + alt sp { + some(s) { + // Maybe this should be span_err -- however, there's an + // assertion later on that the type doesn't contain + // variables, so in this case we have to be sure to die. + tcx.sess.span_fatal + (s, "Type inference failed because I \ + could not find a type\n that's both of the form " + + ty_to_str(tcx, mk_var(tcx, vid)) + + " and of the form " + ty_to_str(tcx, rt) + + ". Such a type would have to be infinitely large."); + } + _ { ret true; } + } + } else { ret false; } +} + +// Maintains a little union-set tree for inferred modes. `canon()` returns +// the current head value for `m0`. +fn canon<T:copy>(tbl: hashmap<ast::node_id, ast::inferable<T>>, + m0: ast::inferable<T>) -> ast::inferable<T> { + alt m0 { + ast::infer(id) { + alt tbl.find(id) { + none { m0 } + some(m1) { + let cm1 = canon(tbl, m1); + // path compression: + if cm1 != m1 { tbl.insert(id, cm1); } + cm1 + } + } + } + _ { m0 } + } +} + +// Maintains a little union-set tree for inferred modes. `resolve_mode()` +// returns the current head value for `m0`. +fn canon_mode(cx: ctxt, m0: ast::mode) -> ast::mode { + canon(cx.inferred_modes, m0) +} + +// Returns the head value for mode, failing if `m` was a infer(_) that +// was never inferred. This should be safe for use after typeck. +fn resolved_mode(cx: ctxt, m: ast::mode) -> ast::rmode { + alt canon_mode(cx, m) { + ast::infer(_) { + cx.sess.bug(#fmt["mode %? was never resolved", m]); + } + ast::expl(m0) { m0 } + } +} + +fn arg_mode(cx: ctxt, a: arg) -> ast::rmode { resolved_mode(cx, a.mode) } + +// Unifies `m1` and `m2`. Returns unified value or failure code. +fn unify_mode(cx: ctxt, m1: ast::mode, m2: ast::mode) + -> result::t<ast::mode, type_err> { + alt (canon_mode(cx, m1), canon_mode(cx, m2)) { + (m1, m2) if (m1 == m2) { + result::ok(m1) + } + (ast::infer(id1), ast::infer(id2)) { + cx.inferred_modes.insert(id2, m1); + result::ok(m1) + } + (ast::infer(id), m) | (m, ast::infer(id)) { + cx.inferred_modes.insert(id, m); + result::ok(m1) + } + (m1, m2) { + result::err(terr_mode_mismatch(m1, m2)) + } + } +} + +// If `m` was never unified, unifies it with `m_def`. Returns the final value +// for `m`. +fn set_default_mode(cx: ctxt, m: ast::mode, m_def: ast::rmode) { + alt canon_mode(cx, m) { + ast::infer(id) { + cx.inferred_modes.insert(id, ast::expl(m_def)); + } + ast::expl(_) { } + } +} + +// Type unification via Robinson's algorithm (Robinson 1965). Implemented as +// described in Hoder and Voronkov: +// +// http://www.cs.man.ac.uk/~hoderk/ubench/unification_full.pdf +mod unify { + export fixup_result; + export fixup_vars; + export fix_ok; + export fix_err; + export mk_var_bindings; + export resolve_type_structure; + export resolve_type_var; + export result; + export unify; + export ures_ok; + export ures_err; + export var_bindings; + export precise, in_bindings; + + enum result { ures_ok(t), ures_err(type_err), } + enum union_result { unres_ok, unres_err(type_err), } + enum fixup_result { + fix_ok(t), // fixup succeeded + fix_err(int), // fixup failed because a type variable was unresolved + } + type var_bindings = + {sets: ufind::ufind, types: smallintmap::smallintmap<t>}; + + enum unify_style { + precise, + in_bindings(@var_bindings), + } + type uctxt = {st: unify_style, tcx: ctxt}; + + fn mk_var_bindings() -> @var_bindings { + ret @{sets: ufind::make(), types: smallintmap::mk::<t>()}; + } + + // Unifies two sets. + fn union(cx: @uctxt, set_a: uint, set_b: uint, + variance: variance) -> union_result { + let vb = alt cx.st { + in_bindings(vb) { vb } + _ { cx.tcx.sess.bug("Someone forgot to document an invariant \ + in union"); } + }; + ufind::grow(vb.sets, math::max(set_a, set_b) + 1u); + let root_a = ufind::find(vb.sets, set_a); + let root_b = ufind::find(vb.sets, set_b); + + let replace_type = ( + fn@(vb: @var_bindings, t: t) { + ufind::union(vb.sets, set_a, set_b); + let root_c: uint = ufind::find(vb.sets, set_a); + smallintmap::insert::<t>(vb.types, root_c, t); + } + ); + + alt smallintmap::find(vb.types, root_a) { + none { + alt smallintmap::find(vb.types, root_b) { + none { ufind::union(vb.sets, set_a, set_b); ret unres_ok; } + some(t_b) { replace_type(vb, t_b); ret unres_ok; } + } + } + some(t_a) { + alt smallintmap::find(vb.types, root_b) { + none { replace_type(vb, t_a); ret unres_ok; } + some(t_b) { + alt unify_step(cx, t_a, t_b, variance) { + ures_ok(t_c) { replace_type(vb, t_c); ret unres_ok; } + ures_err(terr) { ret unres_err(terr); } + } + } + } + } + } + } + + fn record_var_binding(cx: @uctxt, key: int, typ: t, variance: variance) + -> result { + let vb = alt check cx.st { in_bindings(vb) { vb } }; + ufind::grow(vb.sets, (key as uint) + 1u); + let root = ufind::find(vb.sets, key as uint); + let result_type = typ; + alt smallintmap::find(vb.types, root) { + some(old_type) { + alt unify_step(cx, old_type, typ, variance) { + ures_ok(unified_type) { result_type = unified_type; } + rs { ret rs; } + } + } + none {/* fall through */ } + } + smallintmap::insert(vb.types, root, result_type); + ret ures_ok(mk_var(cx.tcx, key)); + } + + // Simple structural type comparison. + fn struct_cmp(cx: @uctxt, expected: t, actual: t) -> result { + let tcx = cx.tcx; + let cfg = tcx.sess.targ_cfg; + if mach_sty(cfg, expected) == mach_sty(cfg, actual) { + ret ures_ok(expected); + } + ret ures_err(terr_mismatch); + } + + // Right now this just checks that the lists of constraints are + // pairwise equal. + fn unify_constrs(base_t: t, expected: [@type_constr], + actual: [@type_constr]) -> result { + let expected_len = vec::len(expected); + let actual_len = vec::len(actual); + + if expected_len != actual_len { + ret ures_err(terr_constr_len(expected_len, actual_len)); + } + let i = 0u; + let rslt; + for c: @type_constr in expected { + rslt = unify_constr(base_t, c, actual[i]); + alt rslt { ures_ok(_) { } ures_err(_) { ret rslt; } } + i += 1u; + } + ret ures_ok(base_t); + } + fn unify_constr(base_t: t, expected: @type_constr, + actual_constr: @type_constr) -> result { + let ok_res = ures_ok(base_t); + let err_res = ures_err(terr_constr_mismatch(expected, actual_constr)); + if expected.node.id != actual_constr.node.id { ret err_res; } + let expected_arg_len = vec::len(expected.node.args); + let actual_arg_len = vec::len(actual_constr.node.args); + if expected_arg_len != actual_arg_len { ret err_res; } + let i = 0u; + let actual; + for a: @ty_constr_arg in expected.node.args { + actual = actual_constr.node.args[i]; + alt a.node { + carg_base { + alt actual.node { carg_base { } _ { ret err_res; } } + } + carg_lit(l) { + alt actual.node { + carg_lit(m) { if l != m { ret err_res; } } + _ { ret err_res; } + } + } + carg_ident(p) { + alt actual.node { + carg_ident(q) { if p.node != q.node { ret err_res; } } + _ { ret err_res; } + } + } + } + i += 1u; + } + ret ok_res; + } + + // Unifies two mutability flags. + fn unify_mut(expected: ast::mutability, actual: ast::mutability, + variance: variance) -> + option<(ast::mutability, variance)> { + + // If you're unifying on something mutable then we have to + // be invariant on the inner type + let newvariance = alt expected { + ast::m_mutbl { + variance_transform(variance, invariant) + } + _ { + variance_transform(variance, covariant) + } + }; + + if expected == actual { ret some((expected, newvariance)); } + if variance == covariant { + if expected == ast::m_const { + ret some((actual, newvariance)); + } + } else if variance == contravariant { + if actual == ast::m_const { + ret some((expected, newvariance)); + } + } + ret none; + } + fn unify_fn_proto(e_proto: ast::proto, a_proto: ast::proto, + variance: variance) -> option<result> { + // Prototypes form a diamond-shaped partial order: + // + // block + // ^ ^ + // shared send + // ^ ^ + // bare + // + // where "^" means "subtype of" (forgive the abuse of the term + // subtype). + fn sub_proto(p_sub: ast::proto, p_sup: ast::proto) -> bool { + ret alt (p_sub, p_sup) { + (_, ast::proto_any) { true } + (ast::proto_bare, _) { true } + + // Equal prototypes are always subprotos: + (_, _) { p_sub == p_sup } + }; + } + + ret alt variance { + invariant if e_proto == a_proto { none } + covariant if sub_proto(a_proto, e_proto) { none } + contravariant if sub_proto(e_proto, a_proto) { none } + _ { some(ures_err(terr_mismatch)) } + }; + } + fn unify_args(cx: @uctxt, e_args: [arg], a_args: [arg], + variance: variance) -> either::t<result, [arg]> { + if !vec::same_length(e_args, a_args) { + ret either::left(ures_err(terr_arg_count)); + } + // The variance changes (flips basically) when descending + // into arguments of function types + let variance = variance_transform(variance, contravariant); + // Would use vec::map2(), but for the need to return in case of + // error: + let i = 0u, result = []; + for expected_input in e_args { + let actual_input = a_args[i]; + i += 1u; + + // Unify the result modes. + let result_mode = + alt unify_mode(cx.tcx, expected_input.mode, + actual_input.mode) { + result::err(err) { ret either::left(ures_err(err)); } + result::ok(m) { m } + }; + + alt unify_step(cx, expected_input.ty, actual_input.ty, + variance) { + ures_ok(rty) { result += [{mode: result_mode, ty: rty}]; } + err { ret either::left(err); } + } + } + either::right(result) + } + fn unify_fn(cx: @uctxt, e_f: fn_ty, a_f: fn_ty, variance: variance) + -> result { + alt unify_fn_proto(e_f.proto, a_f.proto, variance) { + some(err) { ret err; } + none { /* fall through */ } + } + + if a_f.ret_style != ast::noreturn && a_f.ret_style != e_f.ret_style { + /* even though typestate checking is mostly + responsible for checking control flow annotations, + this check is necessary to ensure that the + annotation in an object method matches the + declared object type */ + ret ures_err(terr_ret_style_mismatch(e_f.ret_style, + a_f.ret_style)); + } + let result_ins = alt unify_args(cx, e_f.inputs, a_f.inputs, + variance) { + either::left(err) { ret err; } + either::right(ts) { ts } + }; + + // Check the output. + alt unify_step(cx, e_f.output, a_f.output, variance) { + ures_ok(rty) { + ures_ok(mk_fn(cx.tcx, {proto: e_f.proto, + inputs: result_ins, + output: rty + with a_f})) + } + x { x } + } + } + + // If the given type is a variable, returns the structure of that type. + fn resolve_type_structure(vb: @var_bindings, typ: t) -> + fixup_result { + alt get(typ).struct { + ty_var(vid) { + if vid as uint >= ufind::set_count(vb.sets) { ret fix_err(vid); } + let root_id = ufind::find(vb.sets, vid as uint); + alt smallintmap::find::<t>(vb.types, root_id) { + none { ret fix_err(vid); } + some(rt) { ret fix_ok(rt); } + } + } + _ { ret fix_ok(typ); } + } + } + + // Specifies the allowable subtyping between expected and actual types + enum variance { + // Actual may be a subtype of expected + covariant, + // Actual may be a supertype of expected + contravariant, + // Actual must be the same type as expected + invariant, + } + + // The calculation for recursive variance + // "Taming the Wildcards: Combining Definition- and Use-Site Variance" + // by John Altidor, et. al. + // + // I'm just copying the table from figure 1 - haven't actually + // read the paper (yet). + fn variance_transform(a: variance, b: variance) -> variance { + alt a { + covariant { + alt b { + covariant { covariant } + contravariant { contravariant } + invariant { invariant } + } + } + contravariant { + alt b { + covariant { contravariant } + contravariant { covariant } + invariant { invariant } + } + } + invariant { + alt b { + covariant { invariant } + contravariant { invariant } + invariant { invariant } + } + } + } + } + + fn unify_tps(cx: @uctxt, expected_tps: [t], actual_tps: [t], + variance: variance, finish: fn([t]) -> t) -> result { + let result_tps = [], i = 0u; + for exp in expected_tps { + let act = actual_tps[i]; + i += 1u; + let result = unify_step(cx, exp, act, variance); + alt result { + ures_ok(rty) { result_tps += [rty]; } + _ { ret result; } + } + } + ures_ok(finish(result_tps)) + } + fn unify_mt(cx: @uctxt, e_mt: mt, a_mt: mt, variance: variance, + mut_err: type_err, finish: fn(ctxt, mt) -> t) -> result { + alt unify_mut(e_mt.mutbl, a_mt.mutbl, variance) { + none { ures_err(mut_err) } + some((mutt, var)) { + alt unify_step(cx, e_mt.ty, a_mt.ty, var) { + ures_ok(result_sub) { + ures_ok(finish(cx.tcx, {ty: result_sub, mutbl: mutt})) + } + err { err } + } + } + } + } + + fn unify_step(cx: @uctxt, expected: t, actual: t, + variance: variance) -> result { + // Fast path. + if expected == actual { ret ures_ok(expected); } + + alt (get(expected).struct, get(actual).struct) { + (ty_var(e_id), ty_var(a_id)) { + alt union(cx, e_id as uint, a_id as uint, variance) { + unres_ok { ures_ok(actual) } + unres_err(err) { ures_err(err) } + } + } + (_, ty_var(a_id)) { + let v = variance_transform(variance, contravariant); + record_var_binding(cx, a_id, expected, v) + } + (ty_var(e_id), _) { + let v = variance_transform(variance, covariant); + record_var_binding(cx, e_id, actual, v) + } + (_, ty_bot) { ures_ok(expected) } + (ty_bot, _) { ures_ok(actual) } + (ty_nil, _) | (ty_bool, _) | (ty_int(_), _) | (ty_uint(_), _) | + (ty_float(_), _) | (ty_str, _) | (ty_send_type, _) { + struct_cmp(cx, expected, actual) + } + (ty_param(e_n, _), ty_param(a_n, _)) if e_n == a_n { + ures_ok(expected) + } + (ty_enum(e_id, e_tps), ty_enum(a_id, a_tps)) if e_id == a_id { + unify_tps(cx, e_tps, a_tps, variance, {|tps| + mk_enum(cx.tcx, e_id, tps) + }) + } + (ty_iface(e_id, e_tps), ty_iface(a_id, a_tps)) if e_id == a_id { + unify_tps(cx, e_tps, a_tps, variance, {|tps| + mk_iface(cx.tcx, e_id, tps) + }) + } + (ty_class(e_id, e_tps), ty_class(a_id, a_tps)) if e_id == a_id { + unify_tps(cx, e_tps, a_tps, variance, {|tps| + mk_class(cx.tcx, e_id, tps) + }) + } + (ty_box(e_mt), ty_box(a_mt)) { + unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability, mk_box) + } + (ty_uniq(e_mt), ty_uniq(a_mt)) { + unify_mt(cx, e_mt, a_mt, variance, terr_box_mutability, mk_uniq) + } + (ty_vec(e_mt), ty_vec(a_mt)) { + unify_mt(cx, e_mt, a_mt, variance, terr_vec_mutability, mk_vec) + } + (ty_ptr(e_mt), ty_ptr(a_mt)) { + unify_mt(cx, e_mt, a_mt, variance, terr_ptr_mutability, mk_ptr) + } + (ty_res(e_id, e_inner, e_tps), ty_res(a_id, a_inner, a_tps)) + if e_id == a_id { + alt unify_step(cx, e_inner, a_inner, variance) { + ures_ok(res_inner) { + unify_tps(cx, e_tps, a_tps, variance, {|tps| + mk_res(cx.tcx, a_id, res_inner, tps) + }) + } + err { err } + } + } + (ty_rec(e_fields), ty_rec(a_fields)) { + let e_len = e_fields.len(), a_len = a_fields.len(); + if e_len != a_len { + ret ures_err(terr_record_size(e_len, a_len)); + } + let result_fields = [], i = 0u; + while i < a_len { + let e_field = e_fields[i], a_field = a_fields[i]; + if e_field.ident != a_field.ident { + ret ures_err(terr_record_fields(e_field.ident, + a_field.ident)); + } + alt unify_mt(cx, e_field.mt, a_field.mt, variance, + terr_record_mutability, {|cx, mt| + result_fields += [{mt: mt with e_field}]; + mk_nil(cx) + }) { + ures_ok(_) {} + err { ret err; } + } + i += 1u; + } + ures_ok(mk_rec(cx.tcx, result_fields)) + } + (ty_tup(e_elems), ty_tup(a_elems)) { + let e_len = e_elems.len(), a_len = a_elems.len(); + if e_len != a_len { ret ures_err(terr_tuple_size(e_len, a_len)); } + let result_elems = [], i = 0u; + while i < a_len { + alt unify_step(cx, e_elems[i], a_elems[i], variance) { + ures_ok(rty) { result_elems += [rty]; } + err { ret err; } + } + i += 1u; + } + ures_ok(mk_tup(cx.tcx, result_elems)) + } + (ty_fn(e_fty), ty_fn(a_fty)) { + unify_fn(cx, e_fty, a_fty, variance) + } + (ty_constr(e_t, e_constrs), ty_constr(a_t, a_constrs)) { + // unify the base types... + alt unify_step(cx, e_t, a_t, variance) { + ures_ok(rty) { + // FIXME: probably too restrictive -- + // requires the constraints to be syntactically equal + unify_constrs(expected, e_constrs, a_constrs) + } + err { err } + } + } + (ty_constr(e_t, _), _) { + // If the actual type is *not* a constrained type, + // then we go ahead and just ignore the constraints on + // the expected type. typestate handles the rest. + unify_step(cx, e_t, actual, variance) + } + _ { ures_err(terr_mismatch) } + } + } + fn unify(expected: t, actual: t, st: unify_style, + tcx: ctxt) -> result { + let cx = @{st: st, tcx: tcx}; + ret unify_step(cx, expected, actual, covariant); + } + fn dump_var_bindings(tcx: ctxt, vb: @var_bindings) { + let i = 0u; + while i < vec::len::<ufind::node>(vb.sets.nodes) { + let sets = ""; + let j = 0u; + while j < vec::len::<option<uint>>(vb.sets.nodes) { + if ufind::find(vb.sets, j) == i { sets += #fmt[" %u", j]; } + j += 1u; + } + let typespec; + alt smallintmap::find::<t>(vb.types, i) { + none { typespec = ""; } + some(typ) { typespec = " =" + ty_to_str(tcx, typ); } + } + #error("set %u:%s%s", i, typespec, sets); + i += 1u; + } + } + + // Fixups and substitutions + // Takes an optional span - complain about occurs check violations + // iff the span is present (so that if we already know we're going + // to error anyway, we don't complain) + fn fixup_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings, + typ: t) -> fixup_result { + fn subst_vars(tcx: ctxt, sp: option<span>, vb: @var_bindings, + unresolved: @mutable option<int>, + vars_seen: std::list::list<int>, vid: int) -> t { + // Should really return a fixup_result instead of a t, but fold_ty + // doesn't allow returning anything but a t. + if vid as uint >= ufind::set_count(vb.sets) { + *unresolved = some(vid); + ret mk_var(tcx, vid); + } + let root_id = ufind::find(vb.sets, vid as uint); + alt smallintmap::find::<t>(vb.types, root_id) { + none { *unresolved = some(vid); ret mk_var(tcx, vid); } + some(rt) { + let give_up = false; + std::list::iter(vars_seen) {|v| + if v == vid { + give_up = true; + option::may(sp) {|sp| + tcx.sess.span_fatal( + sp, "can not instantiate infinite type"); + } + } + } + // Return the type unchanged, so we can error out + // downstream + if give_up { ret rt; } + ret fold_ty(tcx, fm_var(bind subst_vars( + tcx, sp, vb, unresolved, std::list::cons(vid, @vars_seen), + _)), rt); + } + } + } + let unresolved = @mutable none::<int>; + let rty = fold_ty(tcx, fm_var(bind subst_vars( + tcx, sp, vb, unresolved, std::list::nil, _)), typ); + let ur = *unresolved; + alt ur { + none { ret fix_ok(rty); } + some(var_id) { ret fix_err(var_id); } + } + } + fn resolve_type_var(tcx: ctxt, sp: option<span>, vb: @var_bindings, + vid: int) -> fixup_result { + if vid as uint >= ufind::set_count(vb.sets) { ret fix_err(vid); } + let root_id = ufind::find(vb.sets, vid as uint); + alt smallintmap::find::<t>(vb.types, root_id) { + none { ret fix_err(vid); } + some(rt) { ret fixup_vars(tcx, sp, vb, rt); } + } + } +} + +fn same_type(cx: ctxt, a: t, b: t) -> bool { + alt unify::unify(a, b, unify::precise, cx) { + unify::ures_ok(_) { true } + _ { false } + } +} + +fn type_err_to_str(err: type_err) -> str { + alt err { + terr_mismatch { ret "types differ"; } + terr_ret_style_mismatch(expect, actual) { + fn to_str(s: ast::ret_style) -> str { + alt s { + ast::noreturn { "non-returning" } + ast::return_val { "return-by-value" } + } + } + ret to_str(actual) + " function found where " + to_str(expect) + + " function was expected"; + } + terr_box_mutability { ret "boxed values differ in mutability"; } + terr_vec_mutability { ret "vectors differ in mutability"; } + terr_ptr_mutability { ret "pointers differ in mutability"; } + terr_tuple_size(e_sz, a_sz) { + ret "expected a tuple with " + uint::to_str(e_sz, 10u) + + " elements but found one with " + uint::to_str(a_sz, 10u) + + " elements"; + } + terr_record_size(e_sz, a_sz) { + ret "expected a record with " + uint::to_str(e_sz, 10u) + + " fields but found one with " + uint::to_str(a_sz, 10u) + + " fields"; + } + terr_record_mutability { ret "record elements differ in mutability"; } + terr_record_fields(e_fld, a_fld) { + ret "expected a record with field '" + e_fld + + "' but found one with field '" + a_fld + "'"; + } + terr_arg_count { ret "incorrect number of function parameters"; } + terr_mode_mismatch(e_mode, a_mode) { + ret "expected argument mode " + mode_to_str(e_mode) + " but found " + + mode_to_str(a_mode); + } + terr_constr_len(e_len, a_len) { + ret "Expected a type with " + uint::str(e_len) + + " constraints, but found one with " + uint::str(a_len) + + " constraints"; + } + terr_constr_mismatch(e_constr, a_constr) { + ret "Expected a type with constraint " + ty_constr_to_str(e_constr) + + " but found one with constraint " + + ty_constr_to_str(a_constr); + } + } +} + +// Replaces type parameters in the given type using the given list of +// substitions. +fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t { + // Precondition? idx < vec::len(substs) + fold_ty(cx, fm_param({|idx, _id| substs[idx]}), typ) +} + +fn def_has_ty_params(def: ast::def) -> bool { + alt def { + ast::def_fn(_, _) | ast::def_variant(_, _) { true } + _ { false } + } +} + +fn store_iface_methods(cx: ctxt, id: ast::node_id, ms: @[method]) { + cx.iface_method_cache.insert(ast_util::local_def(id), ms); +} + +fn iface_methods(cx: ctxt, id: ast::def_id) -> @[method] { + alt cx.iface_method_cache.find(id) { + some(ms) { ret ms; } + _ {} + } + // Local interfaces are supposed to have been added explicitly. + assert id.crate != ast::local_crate; + let result = csearch::get_iface_methods(cx, id); + cx.iface_method_cache.insert(id, result); + result +} + +fn impl_iface(cx: ctxt, id: ast::def_id) -> option<t> { + if id.crate == ast::local_crate { + option::map(cx.tcache.find(id), {|it| it.ty}) + } else { + csearch::get_impl_iface(cx, id) + } +} + +// Enum information +type variant_info = @{args: [t], ctor_ty: t, name: str, + id: ast::def_id, disr_val: int}; + +fn substd_enum_variants(cx: ctxt, id: ast::def_id, tps: [ty::t]) + -> [variant_info] { + vec::map(*enum_variants(cx, id)) { |variant_info| + let substd_args = vec::map(variant_info.args) {|aty| + substitute_type_params(cx, tps, aty) + }; + + let substd_ctor_ty = + substitute_type_params(cx, tps, variant_info.ctor_ty); + + @{args: substd_args, ctor_ty: substd_ctor_ty with *variant_info} + } +} + +fn item_path_str(cx: ctxt, id: ast::def_id) -> str { + ast_map::path_to_str(item_path(cx, id)) +} + +fn item_path(cx: ctxt, id: ast::def_id) -> ast_map::path { + if id.crate != ast::local_crate { + csearch::get_item_path(cx, id) + } else { + let node = cx.items.get(id.node); + alt node { + ast_map::node_item(item, path) { + let item_elt = alt item.node { + item_mod(_) | item_native_mod(_) { + ast_map::path_mod(item.ident) + } + _ { + ast_map::path_name(item.ident) + } + }; + *path + [item_elt] + } + + ast_map::node_native_item(nitem, path) { + *path + [ast_map::path_name(nitem.ident)] + } + + ast_map::node_method(method, _, path) { + *path + [ast_map::path_name(method.ident)] + } + + ast_map::node_variant(variant, _, path) { + vec::init(*path) + [ast_map::path_name(variant.node.name)] + } + + ast_map::node_expr(_) | ast_map::node_arg(_, _) | + ast_map::node_local(_) | ast_map::node_res_ctor(_) { + cx.sess.bug(#fmt["cannot find item_path for node %?", node]); + } + } + } +} + +fn enum_variants(cx: ctxt, id: ast::def_id) -> @[variant_info] { + alt cx.enum_var_cache.find(id) { + some(variants) { ret variants; } + _ { /* fallthrough */ } + } + let result = if ast::local_crate != id.crate { + @csearch::get_enum_variants(cx, id) + } else { + // FIXME: Now that the variants are run through the type checker (to + // check the disr_expr if it exists), this code should likely be + // moved there to avoid having to call eval_const_expr twice. + alt cx.items.get(id.node) { + ast_map::node_item(@{node: ast::item_enum(variants, _), _}, _) { + let disr_val = -1; + @vec::map(variants, {|variant| + let ctor_ty = node_id_to_type(cx, variant.node.id); + let arg_tys = if vec::len(variant.node.args) > 0u { + vec::map(ty_fn_args(ctor_ty), {|a| a.ty}) + } else { [] }; + alt variant.node.disr_expr { + some (ex) { + // FIXME: issue #1417 + disr_val = alt syntax::ast_util::eval_const_expr(ex) { + ast_util::const_int(val) {val as int} + _ { cx.sess.bug("tag_variants: bad disr expr"); } + } + } + _ {disr_val += 1;} + } + @{args: arg_tys, + ctor_ty: ctor_ty, + name: variant.node.name, + id: ast_util::local_def(variant.node.id), + disr_val: disr_val + } + }) + } + _ { cx.sess.bug("tag_variants: id not bound to an enum"); } + } + }; + cx.enum_var_cache.insert(id, result); + result +} + + +// Returns information about the enum variant with the given ID: +fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id, + variant_id: ast::def_id) -> variant_info { + let variants = enum_variants(cx, enum_id); + let i = 0u; + while i < vec::len::<variant_info>(*variants) { + let variant = variants[i]; + if def_eq(variant.id, variant_id) { ret variant; } + i += 1u; + } + cx.sess.bug("enum_variant_with_id(): no variant exists with that ID"); +} + + +// If the given item is in an external crate, looks up its type and adds it to +// the type cache. Returns the type parameters and type. +fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty { + alt cx.tcache.find(did) { + some(tpt) { ret tpt; } + none { + // The item is in this crate. The caller should have added it to the + // type cache already + assert did.crate != ast::local_crate; + let tyt = csearch::get_type(cx, did); + cx.tcache.insert(did, tyt); + ret tyt; + } + } +} + +fn is_binopable(_cx: ctxt, ty: t, op: ast::binop) -> bool { + const tycat_other: int = 0; + const tycat_bool: int = 1; + const tycat_int: int = 2; + const tycat_float: int = 3; + const tycat_str: int = 4; + const tycat_vec: int = 5; + const tycat_struct: int = 6; + const tycat_bot: int = 7; + + const opcat_add: int = 0; + const opcat_sub: int = 1; + const opcat_mult: int = 2; + const opcat_shift: int = 3; + const opcat_rel: int = 4; + const opcat_eq: int = 5; + const opcat_bit: int = 6; + const opcat_logic: int = 7; + + fn opcat(op: ast::binop) -> int { + alt op { + ast::add { opcat_add } + ast::subtract { opcat_sub } + ast::mul { opcat_mult } + ast::div { opcat_mult } + ast::rem { opcat_mult } + ast::and { opcat_logic } + ast::or { opcat_logic } + ast::bitxor { opcat_bit } + ast::bitand { opcat_bit } + ast::bitor { opcat_bit } + ast::lsl { opcat_shift } + ast::lsr { opcat_shift } + ast::asr { opcat_shift } + ast::eq { opcat_eq } + ast::ne { opcat_eq } + ast::lt { opcat_rel } + ast::le { opcat_rel } + ast::ge { opcat_rel } + ast::gt { opcat_rel } + } + } + + fn tycat(ty: t) -> int { + alt get(ty).struct { + ty_bool { tycat_bool } + ty_int(_) { tycat_int } + ty_uint(_) { tycat_int } + ty_float(_) { tycat_float } + ty_str { tycat_str } + ty_vec(_) { tycat_vec } + ty_rec(_) { tycat_struct } + ty_tup(_) { tycat_struct } + ty_enum(_, _) { tycat_struct } + ty_bot { tycat_bot } + _ { tycat_other } + } + } + + const t: bool = true; + const f: bool = false; + + /*. add, shift, bit + . sub, rel, logic + . mult, eq, */ + /*other*/ + /*bool*/ + /*int*/ + /*float*/ + /*str*/ + /*vec*/ + /*bot*/ + let tbl = + [[f, f, f, f, t, t, f, f], [f, f, f, f, t, t, t, t], + [t, t, t, t, t, t, t, f], [t, t, t, f, t, t, f, f], + [t, f, f, f, t, t, f, f], [t, f, f, f, t, t, f, f], + [f, f, f, f, t, t, f, f], [t, t, t, t, t, t, t, t]]; /*struct*/ + + ret tbl[tycat(ty)][opcat(op)]; +} + +fn ast_constr_to_constr<T>(tcx: ctxt, c: @ast::constr_general<T>) -> + @constr_general<T> { + alt tcx.def_map.find(c.node.id) { + some(ast::def_fn(pred_id, ast::pure_fn)) { + ret @ast_util::respan(c.span, + {path: c.node.path, + args: c.node.args, + id: pred_id}); + } + _ { + tcx.sess.span_fatal(c.span, + "Predicate " + path_to_str(c.node.path) + + " is unbound or bound to a non-function or an \ + impure function"); + } + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs new file mode 100644 index 00000000000..e1c56b9fc54 --- /dev/null +++ b/src/rustc/middle/typeck.rs @@ -0,0 +1,3341 @@ +import syntax::{ast, ast_util}; +import ast::spanned; +import syntax::ast_util::{local_def, respan}; +import syntax::visit; +import metadata::csearch; +import driver::session::session; +import util::common::*; +import syntax::codemap::span; +import pat_util::*; +import middle::ty; +import middle::ty::{node_id_to_type, arg, block_ty, + expr_ty, field, node_type_table, mk_nil, + ty_param_bounds_and_ty}; +import util::ppaux::ty_to_str; +import middle::ty::unify::{ures_ok, ures_err, fix_ok, fix_err}; +import std::smallintmap; +import std::map::{hashmap, new_int_hash}; +import syntax::print::pprust::*; + +export check_crate; +export method_map, method_origin, method_static, method_param, method_iface; +export dict_map, dict_res, dict_origin, dict_static, dict_param, dict_iface; + +enum method_origin { + method_static(ast::def_id), + // iface id, method num, param num, bound num + method_param(ast::def_id, uint, uint, uint), + method_iface(ast::def_id, uint), +} +type method_map = hashmap<ast::node_id, method_origin>; + +// Resolutions for bounds of all parameters, left to right, for a given path. +type dict_res = @[dict_origin]; +enum dict_origin { + dict_static(ast::def_id, [ty::t], dict_res), + // Param number, bound number + dict_param(uint, uint), + dict_iface(ast::def_id), +} +type dict_map = hashmap<ast::node_id, dict_res>; + +type ty_table = hashmap<ast::def_id, ty::t>; + +// Used for typechecking the methods of an impl +enum self_info { self_impl(ty::t) } + +type crate_ctxt = {mutable self_infos: [self_info], + impl_map: resolve::impl_map, + method_map: method_map, + dict_map: dict_map, + // Not at all sure it's right to put these here + /* node_id for the class this fn is in -- + none if it's not in a class */ + enclosing_class_id: option<ast::node_id>, + /* map from node_ids for enclosing-class + vars and methods to types */ + enclosing_class: class_map, + tcx: ty::ctxt}; + +type class_map = hashmap<ast::node_id, ty::t>; + +type fn_ctxt = + // var_bindings, locals and next_var_id are shared + // with any nested functions that capture the environment + // (and with any functions whose environment is being captured). + {ret_ty: ty::t, + purity: ast::purity, + proto: ast::proto, + var_bindings: @ty::unify::var_bindings, + locals: hashmap<ast::node_id, int>, + next_var_id: @mutable int, + ccx: @crate_ctxt}; + + +fn lookup_local(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> int { + alt fcx.locals.find(id) { + some(x) { x } + _ { + fcx.ccx.tcx.sess.span_fatal(sp, + "internal error looking up a local var") + } + } +} + +fn lookup_def(fcx: @fn_ctxt, sp: span, id: ast::node_id) -> ast::def { + alt fcx.ccx.tcx.def_map.find(id) { + some(x) { x } + _ { + fcx.ccx.tcx.sess.span_fatal(sp, + "internal error looking up a definition") + } + } +} + +// Returns the type parameter count and the type for the given definition. +fn ty_param_bounds_and_ty_for_def(fcx: @fn_ctxt, sp: span, defn: ast::def) -> + ty_param_bounds_and_ty { + alt defn { + ast::def_arg(nid, _) { + assert (fcx.locals.contains_key(nid)); + let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid)); + ret {bounds: @[], ty: typ}; + } + ast::def_local(nid, _) { + assert (fcx.locals.contains_key(nid)); + let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid)); + ret {bounds: @[], ty: typ}; + } + ast::def_self(id) { + alt get_self_info(fcx.ccx) { + some(self_impl(impl_t)) { + ret {bounds: @[], ty: impl_t}; + } + none { + fcx.ccx.tcx.sess.span_bug(sp, "def_self with no self_info"); + } + } + } + ast::def_fn(id, ast::crust_fn) { + // Crust functions are just u8 pointers + ret { + bounds: @[], + ty: ty::mk_ptr( + fcx.ccx.tcx, + { + ty: ty::mk_mach_uint(fcx.ccx.tcx, ast::ty_u8), + mutbl: ast::m_imm + }) + }; + } + ast::def_fn(id, _) | ast::def_const(id) | + ast::def_variant(_, id) | ast::def_class(id) + { ret ty::lookup_item_type(fcx.ccx.tcx, id); } + ast::def_binding(nid) { + assert (fcx.locals.contains_key(nid)); + let typ = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, sp, nid)); + ret {bounds: @[], ty: typ}; + } + ast::def_ty(_) | ast::def_prim_ty(_) { + fcx.ccx.tcx.sess.span_fatal(sp, "expected value but found type"); + } + ast::def_upvar(_, inner, _) { + ret ty_param_bounds_and_ty_for_def(fcx, sp, *inner); + } + ast::def_class_method(_, id) | ast::def_class_field(_, id) { + if id.crate != ast::local_crate { + fcx.ccx.tcx.sess.span_fatal(sp, + "class method or field referred to \ + out of scope"); + } + alt fcx.ccx.enclosing_class.find(id.node) { + some(a_ty) { ret {bounds: @[], ty: a_ty}; } + _ { fcx.ccx.tcx.sess.span_fatal(sp, + "class method or field referred to \ + out of scope"); } + } + } + + _ { + // FIXME: handle other names. + fcx.ccx.tcx.sess.unimpl("definition variant"); + } + } +} + +// Instantiates the given path, which must refer to an item with the given +// number of type parameters and type. +fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path, + tpt: ty_param_bounds_and_ty, sp: span, + id: ast::node_id) { + let ty_param_count = vec::len(*tpt.bounds); + let ty_substs_len = vec::len(pth.node.types); + if ty_substs_len > 0u { + if ty_param_count == 0u { + fcx.ccx.tcx.sess.span_fatal + (sp, "this item does not take type parameters"); + } else if ty_substs_len > ty_param_count { + fcx.ccx.tcx.sess.span_fatal + (sp, "too many type parameter provided for this item"); + } else if ty_substs_len < ty_param_count { + fcx.ccx.tcx.sess.span_fatal + (sp, "not enough type parameters provided for this item"); + } + if ty_param_count == 0u { + fcx.ccx.tcx.sess.span_fatal( + sp, "this item does not take type parameters"); + } + let substs = vec::map(pth.node.types, {|aty| + ast_ty_to_ty_crate(fcx.ccx, aty) + }); + write_ty_substs(fcx.ccx.tcx, id, tpt.ty, substs); + } else if ty_param_count > 0u { + let vars = vec::init_fn(ty_param_count, {|_i| next_ty_var(fcx)}); + write_ty_substs(fcx.ccx.tcx, id, tpt.ty, vars); + } else { + write_ty(fcx.ccx.tcx, id, tpt.ty); + } +} + +// Type tests +fn structurally_resolved_type(fcx: @fn_ctxt, sp: span, tp: ty::t) -> ty::t { + alt ty::unify::resolve_type_structure(fcx.var_bindings, tp) { + fix_ok(typ_s) { ret typ_s; } + fix_err(_) { + fcx.ccx.tcx.sess.span_fatal + (sp, "the type of this value must be known in this context"); + } + } +} + + +// Returns the one-level-deep structure of the given type. +fn structure_of(fcx: @fn_ctxt, sp: span, typ: ty::t) -> ty::sty { + ty::get(structurally_resolved_type(fcx, sp, typ)).struct +} + +// Returns the one-level-deep structure of the given type or none if it +// is not known yet. +fn structure_of_maybe(fcx: @fn_ctxt, _sp: span, typ: ty::t) -> + option<ty::sty> { + let r = + ty::unify::resolve_type_structure(fcx.var_bindings, typ); + ret alt r { + fix_ok(typ_s) { some(ty::get(typ_s).struct) } + fix_err(_) { none } + } +} + +fn type_is_integral(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + ret ty::type_is_integral(typ_s); +} + +fn type_is_scalar(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + ret ty::type_is_scalar(typ_s); +} + +fn type_is_c_like_enum(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + ret ty::type_is_c_like_enum(fcx.ccx.tcx, typ_s); +} + +enum mode { m_collect, m_check, m_check_tyvar(@fn_ctxt), } + +// Parses the programmer's textual representation of a type into our +// internal notion of a type. `getter` is a function that returns the type +// corresponding to a definition ID: +fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t { + fn getter(tcx: ty::ctxt, mode: mode, id: ast::def_id) + -> ty::ty_param_bounds_and_ty { + alt mode { + m_check | m_check_tyvar(_) { ty::lookup_item_type(tcx, id) } + m_collect { + if id.crate != ast::local_crate { csearch::get_type(tcx, id) } + else { + alt tcx.items.find(id.node) { + some(ast_map::node_item(item, _)) { + ty_of_item(tcx, mode, item) + } + some(ast_map::node_native_item(native_item, _)) { + ty_of_native_item(tcx, mode, native_item) + } + _ { + tcx.sess.bug("Unexpected sort of item in ast_ty_to_ty"); + } + } + } + } + } + } + alt tcx.ast_ty_to_ty_cache.find(ast_ty) { + some(some(ty)) { ret ty; } + some(none) { + tcx.sess.span_fatal(ast_ty.span, "illegal recursive type. \ + insert a enum in the cycle, \ + if this is desired)"); + } + none { } + } /* go on */ + + tcx.ast_ty_to_ty_cache.insert(ast_ty, none::<ty::t>); + fn ast_mt_to_mt(tcx: ty::ctxt, mode: mode, mt: ast::mt) -> ty::mt { + ret {ty: ast_ty_to_ty(tcx, mode, mt.ty), mutbl: mt.mutbl}; + } + fn instantiate(tcx: ty::ctxt, sp: span, mode: mode, + id: ast::def_id, args: [@ast::ty]) -> ty::t { + let ty_param_bounds_and_ty = getter(tcx, mode, id); + if vec::len(*ty_param_bounds_and_ty.bounds) == 0u { + ret ty_param_bounds_and_ty.ty; + } + + // The typedef is type-parametric. Do the type substitution. + let param_bindings: [ty::t] = []; + if vec::len(args) != vec::len(*ty_param_bounds_and_ty.bounds) { + tcx.sess.span_fatal(sp, "Wrong number of type arguments for a \ + polymorphic type"); + } + for ast_ty: @ast::ty in args { + param_bindings += [ast_ty_to_ty(tcx, mode, ast_ty)]; + } + let typ = + ty::substitute_type_params(tcx, param_bindings, + ty_param_bounds_and_ty.ty); + ret typ; + } + let typ = alt ast_ty.node { + ast::ty_nil { ty::mk_nil(tcx) } + ast::ty_bot { ty::mk_bot(tcx) } + ast::ty_box(mt) { + ty::mk_box(tcx, ast_mt_to_mt(tcx, mode, mt)) + } + ast::ty_uniq(mt) { + ty::mk_uniq(tcx, ast_mt_to_mt(tcx, mode, mt)) + } + ast::ty_vec(mt) { + ty::mk_vec(tcx, ast_mt_to_mt(tcx, mode, mt)) + } + ast::ty_ptr(mt) { + ty::mk_ptr(tcx, ast_mt_to_mt(tcx, mode, mt)) + } + ast::ty_tup(fields) { + let flds = vec::map(fields, bind ast_ty_to_ty(tcx, mode, _)); + ty::mk_tup(tcx, flds) + } + ast::ty_rec(fields) { + let flds: [field] = []; + for f: ast::ty_field in fields { + let tm = ast_mt_to_mt(tcx, mode, f.node.mt); + flds += [{ident: f.node.ident, mt: tm}]; + } + ty::mk_rec(tcx, flds) + } + ast::ty_fn(proto, decl) { + ty::mk_fn(tcx, ty_of_fn_decl(tcx, mode, proto, decl)) + } + ast::ty_path(path, id) { + let a_def = alt tcx.def_map.find(id) { + none { tcx.sess.span_fatal(ast_ty.span, #fmt("unbound path %s", + path_to_str(path))); } + some(d) { d }}; + alt a_def { + ast::def_ty(id) { + instantiate(tcx, ast_ty.span, mode, id, path.node.types) + } + ast::def_prim_ty(nty) { + alt nty { + ast::ty_bool { ty::mk_bool(tcx) } + ast::ty_int(it) { ty::mk_mach_int(tcx, it) } + ast::ty_uint(uit) { ty::mk_mach_uint(tcx, uit) } + ast::ty_float(ft) { ty::mk_mach_float(tcx, ft) } + ast::ty_str { ty::mk_str(tcx) } + } + } + ast::def_ty_param(id, n) { + if vec::len(path.node.types) > 0u { + tcx.sess.span_err(ast_ty.span, "provided type parameters to \ + a type parameter"); + } + ty::mk_param(tcx, n, id) + } + ast::def_self(iface_id) { + alt check tcx.items.get(iface_id) { + ast_map::node_item(@{node: ast::item_iface(tps, _), _}, _) { + if vec::len(tps) != vec::len(path.node.types) { + tcx.sess.span_err(ast_ty.span, "incorrect number of type \ + parameter to self type"); + } + ty::mk_self(tcx, vec::map(path.node.types, {|ast_ty| + ast_ty_to_ty(tcx, mode, ast_ty) + })) + } + } + } + ast::def_class(class_id) { + alt tcx.items.find(class_id.node) { + some(ast_map::node_item( + @{node: ast::item_class(tps, _, _, _, _), _}, _)) { + if vec::len(tps) != vec::len(path.node.types) { + tcx.sess.span_err(ast_ty.span, "incorrect number of \ + type parameters to object type"); + } + ty::mk_class(tcx, class_id, vec::map(path.node.types, + {|ast_ty| ast_ty_to_ty(tcx, mode, ast_ty)})) + } + _ { + tcx.sess.span_bug(ast_ty.span, "class id is unbound \ + in items"); + } + } + } + _ { + tcx.sess.span_fatal(ast_ty.span, + "found type name used as a variable"); + } + } + } + ast::ty_constr(t, cs) { + let out_cs = []; + for constr: @ast::ty_constr in cs { + out_cs += [ty::ast_constr_to_constr(tcx, constr)]; + } + ty::mk_constr(tcx, ast_ty_to_ty(tcx, mode, t), out_cs) + } + ast::ty_infer { + alt mode { + m_check_tyvar(fcx) { ret next_ty_var(fcx); } + _ { tcx.sess.span_bug(ast_ty.span, + "found `ty_infer` in unexpected place"); } + } + } + ast::ty_mac(_) { + tcx.sess.span_bug(ast_ty.span, + "found `ty_mac` in unexpected place"); + } + }; + tcx.ast_ty_to_ty_cache.insert(ast_ty, some(typ)); + ret typ; +} + +fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item) + -> ty::ty_param_bounds_and_ty { + let def_id = local_def(it.id); + alt tcx.tcache.find(def_id) { + some(tpt) { ret tpt; } + _ {} + } + alt it.node { + ast::item_const(t, _) { + let typ = ast_ty_to_ty(tcx, mode, t); + let tpt = {bounds: @[], ty: typ}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_fn(decl, tps, _) { + ret ty_of_fn(tcx, mode, decl, tps, local_def(it.id)); + } + ast::item_ty(t, tps) { + alt tcx.tcache.find(local_def(it.id)) { + some(tpt) { ret tpt; } + none { } + } + // Tell ast_ty_to_ty() that we want to perform a recursive + // call to resolve any named types. + let tpt = { + let t0 = ast_ty_to_ty(tcx, mode, t); + let t1 = { + // Do not associate a def id with a named, parameterized type + // like "foo<X>". This is because otherwise ty_to_str will + // print the name as merely "foo", as it has no way to + // reconstruct the value of X. + if vec::is_empty(tps) { + ty::mk_with_id(tcx, t0, def_id) + } else { + t0 + } + }; + {bounds: ty_param_bounds(tcx, mode, tps), ty: t1} + }; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_res(decl, tps, _, _, _) { + let {bounds, params} = mk_ty_params(tcx, tps); + let t_arg = ty_of_arg(tcx, mode, decl.inputs[0]); + let t = { + let t0 = ty::mk_res(tcx, local_def(it.id), t_arg.ty, params); + ty::mk_with_id(tcx, t0, def_id) + }; + let t_res = {bounds: bounds, ty: t}; + tcx.tcache.insert(local_def(it.id), t_res); + ret t_res; + } + ast::item_enum(_, tps) { + // Create a new generic polytype. + let {bounds, params} = mk_ty_params(tcx, tps); + let t = { + let t0 = ty::mk_enum(tcx, local_def(it.id), params); + ty::mk_with_id(tcx, t0, def_id) + }; + let tpt = {bounds: bounds, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_iface(tps, ms) { + let {bounds, params} = mk_ty_params(tcx, tps); + let t = { + let t0 = ty::mk_iface(tcx, local_def(it.id), params); + ty::mk_with_id(tcx, t0, def_id) + }; + let tpt = {bounds: bounds, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_class(tps,_,_,_,_) { + let {bounds,params} = mk_ty_params(tcx, tps); + let t = ty::mk_class(tcx, local_def(it.id), params); + let tpt = {bounds: bounds, ty: t}; + tcx.tcache.insert(local_def(it.id), tpt); + ret tpt; + } + ast::item_impl(_, _, _, _) | ast::item_mod(_) | + ast::item_native_mod(_) { fail; } + } +} +fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item) + -> ty::ty_param_bounds_and_ty { + alt it.node { + ast::native_item_fn(fn_decl, params) { + ret ty_of_native_fn_decl(tcx, mode, fn_decl, params, + local_def(it.id)); + } + } +} +fn ty_of_arg(tcx: ty::ctxt, mode: mode, a: ast::arg) -> ty::arg { + fn arg_mode(tcx: ty::ctxt, m: ast::mode, ty: ty::t) -> ast::mode { + alt m { + ast::infer(_) { + alt ty::get(ty).struct { + // If the type is not specified, then this must be a fn expr. + // Leave the mode as infer(_), it will get inferred based + // on constraints elsewhere. + ty::ty_var(_) { m } + + // If the type is known, then use the default for that type. + // Here we unify m and the default. This should update the + // tables in tcx but should never fail, because nothing else + // will have been unified with m yet: + _ { + let m1 = ast::expl(ty::default_arg_mode_for_ty(ty)); + result::get(ty::unify_mode(tcx, m, m1)) + } + } + } + ast::expl(_) { m } + } + } + + let ty = ast_ty_to_ty(tcx, mode, a.ty); + let mode = arg_mode(tcx, a.mode, ty); + {mode: mode, ty: ty} +} +fn ty_of_fn_decl(tcx: ty::ctxt, + mode: mode, + proto: ast::proto, + decl: ast::fn_decl) -> ty::fn_ty { + let input_tys = vec::map(decl.inputs) {|a| ty_of_arg(tcx, mode, a) }; + let output_ty = ast_ty_to_ty(tcx, mode, decl.output); + + let out_constrs = []; + for constr: @ast::constr in decl.constraints { + out_constrs += [ty::ast_constr_to_constr(tcx, constr)]; + } + {proto: proto, inputs: input_tys, + output: output_ty, ret_style: decl.cf, constraints: out_constrs} +} +fn ty_of_fn(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl, + ty_params: [ast::ty_param], def_id: ast::def_id) + -> ty::ty_param_bounds_and_ty { + let bounds = ty_param_bounds(tcx, mode, ty_params); + let tofd = ty_of_fn_decl(tcx, mode, ast::proto_bare, decl); + let tpt = {bounds: bounds, ty: ty::mk_fn(tcx, tofd)}; + tcx.tcache.insert(def_id, tpt); + ret tpt; +} +fn ty_of_native_fn_decl(tcx: ty::ctxt, mode: mode, decl: ast::fn_decl, + ty_params: [ast::ty_param], def_id: ast::def_id) + -> ty::ty_param_bounds_and_ty { + let input_tys = [], bounds = ty_param_bounds(tcx, mode, ty_params); + for a: ast::arg in decl.inputs { + input_tys += [ty_of_arg(tcx, mode, a)]; + } + let output_ty = ast_ty_to_ty(tcx, mode, decl.output); + + let t_fn = ty::mk_fn(tcx, {proto: ast::proto_bare, + inputs: input_tys, + output: output_ty, + ret_style: ast::return_val, + constraints: []}); + let tpt = {bounds: bounds, ty: t_fn}; + tcx.tcache.insert(def_id, tpt); + ret tpt; +} +fn ty_param_bounds(tcx: ty::ctxt, mode: mode, params: [ast::ty_param]) + -> @[ty::param_bounds] { + let result = []; + for param in params { + result += [alt tcx.ty_param_bounds.find(param.id) { + some(bs) { bs } + none { + let bounds = []; + for b in *param.bounds { + bounds += [alt b { + ast::bound_send { ty::bound_send } + ast::bound_copy { ty::bound_copy } + ast::bound_iface(t) { + let ity = ast_ty_to_ty(tcx, mode, t); + alt ty::get(ity).struct { + ty::ty_iface(_, _) {} + _ { + tcx.sess.span_fatal( + t.span, "type parameter bounds must be \ + interface types"); + } + } + ty::bound_iface(ity) + } + }]; + } + let boxed = @bounds; + tcx.ty_param_bounds.insert(param.id, boxed); + boxed + } + }]; + } + @result +} +fn ty_of_method(tcx: ty::ctxt, mode: mode, m: @ast::method) -> ty::method { + {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps), + fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl), + purity: m.decl.purity} +} +fn ty_of_ty_method(tcx: ty::ctxt, mode: mode, m: ast::ty_method) + -> ty::method { + {ident: m.ident, tps: ty_param_bounds(tcx, mode, m.tps), + fty: ty_of_fn_decl(tcx, mode, ast::proto_bare, m.decl), + purity: m.decl.purity} +} + +// A convenience function to use a crate_ctxt to resolve names for +// ast_ty_to_ty. +fn ast_ty_to_ty_crate(ccx: @crate_ctxt, &&ast_ty: @ast::ty) -> ty::t { + ret ast_ty_to_ty(ccx.tcx, m_check, ast_ty); +} + +// A wrapper around ast_ty_to_ty_crate that handles ty_infer. +fn ast_ty_to_ty_crate_infer(ccx: @crate_ctxt, &&ast_ty: @ast::ty) -> + option<ty::t> { + alt ast_ty.node { + ast::ty_infer { none } + _ { some(ast_ty_to_ty_crate(ccx, ast_ty)) } + } +} + + +// Functions that write types into the node type table +fn write_ty(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t) { + smallintmap::insert(*tcx.node_types, node_id as uint, ty); +} +fn write_substs(tcx: ty::ctxt, node_id: ast::node_id, +substs: [ty::t]) { + tcx.node_type_substs.insert(node_id, substs); +} +fn write_ty_substs(tcx: ty::ctxt, node_id: ast::node_id, ty: ty::t, + +substs: [ty::t]) { + let ty = if ty::type_has_params(ty) { + ty::substitute_type_params(tcx, substs, ty) + } else { ty }; + write_ty(tcx, node_id, ty); + write_substs(tcx, node_id, substs); +} +fn write_nil(tcx: ty::ctxt, node_id: ast::node_id) { + write_ty(tcx, node_id, ty::mk_nil(tcx)); +} +fn write_bot(tcx: ty::ctxt, node_id: ast::node_id) { + write_ty(tcx, node_id, ty::mk_bot(tcx)); +} + +fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param]) + -> {bounds: @[ty::param_bounds], params: [ty::t]} { + let i = 0u, bounds = ty_param_bounds(tcx, m_collect, atps); + {bounds: bounds, + params: vec::map(atps, {|atp| + let t = ty::mk_param(tcx, i, local_def(atp.id)); + i += 1u; + t + })} +} + +fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, + impl_tps: uint, if_m: ty::method, substs: [ty::t], + self_ty: ty::t) -> ty::t { + if impl_m.tps != if_m.tps { + tcx.sess.span_err(sp, "method `" + if_m.ident + + "` has an incompatible set of type parameters"); + ty::mk_fn(tcx, impl_m.fty) + } else if vec::len(impl_m.fty.inputs) != vec::len(if_m.fty.inputs) { + tcx.sess.span_err(sp,#fmt["method `%s` has %u parameters \ + but the iface has %u", + if_m.ident, + vec::len(impl_m.fty.inputs), + vec::len(if_m.fty.inputs)]); + ty::mk_fn(tcx, impl_m.fty) + } else { + let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f| + alt ty::get(f.ty).struct { + ty::ty_param(_, _) | ty::ty_self(_) + if alt i.mode { ast::infer(_) { true } _ { false } } { + {mode: ast::expl(ast::by_ref) with i} + } + _ { i } + } + }); + let impl_fty = ty::mk_fn(tcx, {inputs: auto_modes with impl_m.fty}); + // Add dummy substs for the parameters of the impl method + let substs = substs + vec::init_fn(vec::len(*if_m.tps), {|i| + ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) + }); + let if_fty = ty::mk_fn(tcx, if_m.fty); + if_fty = ty::substitute_type_params(tcx, substs, if_fty); + if ty::type_has_vars(if_fty) { + if_fty = fixup_self_in_method_ty(tcx, if_fty, substs, + self_full(self_ty, impl_tps)); + } + alt ty::unify::unify(impl_fty, if_fty, ty::unify::precise, tcx) { + ty::unify::ures_err(err) { + tcx.sess.span_err(sp, "method `" + if_m.ident + + "` has an incompatible type: " + + ty::type_err_to_str(err)); + impl_fty + } + ty::unify::ures_ok(tp) { tp } + } + } +} + +enum self_subst { self_param(ty::t, @fn_ctxt, span), self_full(ty::t, uint) } + +// Mangles an iface method ty to make its self type conform to the self type +// of a specific impl or bounded type parameter. This is rather involved +// because the type parameters of ifaces and impls are not required to line up +// (an impl can have less or more parameters than the iface it implements), so +// some mangling of the substituted types is required. +fn fixup_self_in_method_ty(cx: ty::ctxt, mty: ty::t, m_substs: [ty::t], + self: self_subst) -> ty::t { + if ty::type_has_vars(mty) { + ty::fold_ty(cx, ty::fm_general(fn@(t: ty::t) -> ty::t { + alt ty::get(t).struct { + ty::ty_self(tps) { + if vec::len(tps) > 0u { + // Move the substs into the type param system of the + // context. + let substs = vec::map(tps, {|t| + let f = fixup_self_in_method_ty(cx, t, m_substs, + self); + ty::substitute_type_params(cx, m_substs, f) + }); + alt self { + self_param(t, fcx, sp) { + // Simply ensure that the type parameters for the self + // type match the context. + vec::iter2(substs, m_substs) {|s, ms| + demand::simple(fcx, sp, s, ms); + } + t + } + self_full(selfty, impl_n_tps) { + // Add extra substs for impl type parameters. + while vec::len(substs) < impl_n_tps { + substs += [ty::mk_param(cx, vec::len(substs), + {crate: 0, node: 0})]; + } + // And for method type parameters. + let method_n_tps = + (vec::len(m_substs) - vec::len(tps)) as int; + if method_n_tps > 0 { + substs += vec::tail_n(m_substs, vec::len(m_substs) + - (method_n_tps as uint)); + } + // And then instantiate the self type using all those. + ty::substitute_type_params(cx, substs, selfty) + } + } + } else { + alt self { self_param(t, _, _) | self_full(t, _) { t } } + } + } + _ { t } + } + }), mty) + } else { mty } +} + +// Item collection - a pair of bootstrap passes: +// +// (1) Collect the IDs of all type items (typedefs) and store them in a table. +// +// (2) Translate the AST fragments that describe types to determine a type for +// each item. When we encounter a named type, we consult the table built +// in pass 1 to find its item, and recursively translate it. +// +// We then annotate the AST with the resulting types and return the annotated +// AST, along with a table mapping item IDs to their types. +mod collect { + fn get_enum_variant_types(tcx: ty::ctxt, enum_ty: ty::t, + variants: [ast::variant], + ty_params: [ast::ty_param]) { + // Create a set of parameter types shared among all the variants. + for variant in variants { + // Nullary enum constructors get turned into constants; n-ary enum + // constructors get turned into functions. + let result_ty = if vec::len(variant.node.args) == 0u { + enum_ty + } else { + // As above, tell ast_ty_to_ty() that trans_ty_item_to_ty() + // should be called to resolve named types. + let args: [arg] = []; + for va: ast::variant_arg in variant.node.args { + let arg_ty = ast_ty_to_ty(tcx, m_collect, va.ty); + args += [{mode: ast::expl(ast::by_copy), ty: arg_ty}]; + } + // FIXME: this will be different for constrained types + ty::mk_fn(tcx, + {proto: ast::proto_box, + inputs: args, output: enum_ty, + ret_style: ast::return_val, constraints: []}) + }; + let tpt = {bounds: ty_param_bounds(tcx, m_collect, ty_params), + ty: result_ty}; + tcx.tcache.insert(local_def(variant.node.id), tpt); + write_ty(tcx, variant.node.id, result_ty); + } + } + fn ensure_iface_methods(tcx: ty::ctxt, id: ast::node_id) { + alt check tcx.items.get(id) { + ast_map::node_item(@{node: ast::item_iface(_, ms), _}, _) { + ty::store_iface_methods(tcx, id, @vec::map(ms, {|m| + ty_of_ty_method(tcx, m_collect, m) + })); + } + } + } + fn convert_class_item(tcx: ty::ctxt, ci: ast::class_member) { + /* we want to do something here, b/c within the + scope of the class, it's ok to refer to fields & + methods unqualified */ + + /* they have these types *within the scope* of the + class. outside the class, it's done with expr_field */ + alt ci { + ast::instance_var(_,t,_,id) { + let tt = ast_ty_to_ty(tcx, m_collect, t); + write_ty(tcx, id, tt); + } + ast::class_method(it) { convert(tcx, it); } + } + } + fn convert(tcx: ty::ctxt, it: @ast::item) { + alt it.node { + // These don't define types. + ast::item_mod(_) | ast::item_native_mod(_) {} + ast::item_enum(variants, ty_params) { + let tpt = ty_of_item(tcx, m_collect, it); + write_ty(tcx, it.id, tpt.ty); + get_enum_variant_types(tcx, tpt.ty, variants, ty_params); + } + ast::item_impl(tps, ifce, selfty, ms) { + let i_bounds = ty_param_bounds(tcx, m_collect, tps); + let my_methods = []; + for m in ms { + let bounds = ty_param_bounds(tcx, m_collect, m.tps); + let mty = ty_of_method(tcx, m_collect, m); + my_methods += [{mty: mty, id: m.id, span: m.span}]; + let fty = ty::mk_fn(tcx, mty.fty); + tcx.tcache.insert(local_def(m.id), + {bounds: @(*i_bounds + *bounds), + ty: fty}); + write_ty(tcx, m.id, fty); + } + let selfty = ast_ty_to_ty(tcx, m_collect, selfty); + write_ty(tcx, it.id, selfty); + alt ifce { + some(t) { + let iface_ty = ast_ty_to_ty(tcx, m_collect, t); + tcx.tcache.insert(local_def(it.id), + {bounds: i_bounds, ty: iface_ty}); + alt ty::get(iface_ty).struct { + ty::ty_iface(did, tys) { + if did.crate == ast::local_crate { + ensure_iface_methods(tcx, did.node); + } + for if_m in *ty::iface_methods(tcx, did) { + alt vec::find(my_methods, + {|m| if_m.ident == m.mty.ident}) { + some({mty: m, id, span}) { + if m.purity != if_m.purity { + tcx.sess.span_err( + span, "method `" + m.ident + "`'s purity \ + not match the iface method's \ + purity"); + } + let mt = compare_impl_method( + tcx, span, m, vec::len(tps), if_m, tys, + selfty); + let old = tcx.tcache.get(local_def(id)); + if old.ty != mt { + tcx.tcache.insert(local_def(id), + {bounds: old.bounds, + ty: mt}); + write_ty(tcx, id, mt); + } + } + none { + tcx.sess.span_err(t.span, "missing method `" + + if_m.ident + "`"); + } + } + } + } + _ { + tcx.sess.span_fatal(t.span, "can only implement \ + interface types"); + } + } + } + _ {} + } + } + ast::item_res(decl, tps, _, dtor_id, ctor_id) { + let {bounds, params} = mk_ty_params(tcx, tps); + let def_id = local_def(it.id); + let t_arg = ty_of_arg(tcx, m_collect, decl.inputs[0]); + let t_res = ty::mk_res(tcx, def_id, t_arg.ty, params); + let t_res = ty::mk_with_id(tcx, t_res, def_id); + let t_ctor = ty::mk_fn(tcx, { + proto: ast::proto_box, + inputs: [{mode: ast::expl(ast::by_copy) with t_arg}], + output: t_res, + ret_style: ast::return_val, constraints: [] + }); + let t_dtor = ty::mk_fn(tcx, { + proto: ast::proto_box, + inputs: [t_arg], output: ty::mk_nil(tcx), + ret_style: ast::return_val, constraints: [] + }); + write_ty(tcx, it.id, t_res); + write_ty(tcx, ctor_id, t_ctor); + tcx.tcache.insert(local_def(ctor_id), + {bounds: bounds, ty: t_ctor}); + tcx.tcache.insert(def_id, {bounds: bounds, ty: t_res}); + write_ty(tcx, dtor_id, t_dtor); + } + ast::item_iface(_, ms) { + let tpt = ty_of_item(tcx, m_collect, it); + write_ty(tcx, it.id, tpt.ty); + ensure_iface_methods(tcx, it.id); + } + ast::item_class(tps, members, ctor_id, ctor_decl, ctor_block) { + // Write the class type + let {bounds,params} = mk_ty_params(tcx, tps); + let class_ty = ty::mk_class(tcx, local_def(it.id), params); + let tpt = {bounds: bounds, ty: class_ty}; + tcx.tcache.insert(local_def(it.id), tpt); + write_ty(tcx, it.id, class_ty); + // Write the ctor type + let t_ctor = ty::mk_fn(tcx, + ty_of_fn_decl(tcx, m_collect, + ast::proto_any, ctor_decl)); + write_ty(tcx, ctor_id, t_ctor); + tcx.tcache.insert(local_def(ctor_id), + {bounds: bounds, ty: t_ctor}); + /* FIXME: check for proper public/privateness */ + // Write the type of each of the members + for m in members { + convert_class_item(tcx, m.node.decl); + } + } + _ { + // This call populates the type cache with the converted type + // of the item in passing. All we have to do here is to write + // it into the node type table. + let tpt = ty_of_item(tcx, m_collect, it); + write_ty(tcx, it.id, tpt.ty); + } + } + } + fn convert_native(tcx: ty::ctxt, i: @ast::native_item) { + // As above, this call populates the type table with the converted + // type of the native item. We simply write it into the node type + // table. + let tpt = ty_of_native_item(tcx, m_collect, i); + alt i.node { + ast::native_item_fn(_, _) { write_ty(tcx, i.id, tpt.ty); } + } + } + fn collect_item_types(tcx: ty::ctxt, crate: @ast::crate) { + visit::visit_crate(*crate, (), visit::mk_simple_visitor(@{ + visit_item: bind convert(tcx, _), + visit_native_item: bind convert_native(tcx, _) + with *visit::default_simple_visitor() + })); + } +} + + +// Type unification +mod unify { + fn unify(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> + ty::unify::result { + ret ty::unify::unify(expected, actual, + ty::unify::in_bindings(fcx.var_bindings), + fcx.ccx.tcx); + } +} + + +// FIXME This is almost a duplicate of ty::type_autoderef, with structure_of +// instead of ty::struct. +fn do_autoderef(fcx: @fn_ctxt, sp: span, t: ty::t) -> ty::t { + let t1 = t; + while true { + alt structure_of(fcx, sp, t1) { + ty::ty_box(inner) | ty::ty_uniq(inner) { + alt ty::get(t1).struct { + ty::ty_var(v1) { + if ty::occurs_check_fails(fcx.ccx.tcx, some(sp), v1, + ty::mk_box(fcx.ccx.tcx, inner)) { + break; + } + } + _ { } + } + t1 = inner.ty; + } + ty::ty_res(_, inner, tps) { + t1 = ty::substitute_type_params(fcx.ccx.tcx, tps, inner); + } + ty::ty_enum(did, tps) { + let variants = ty::enum_variants(fcx.ccx.tcx, did); + if vec::len(*variants) != 1u || vec::len(variants[0].args) != 1u { + ret t1; + } + t1 = + ty::substitute_type_params(fcx.ccx.tcx, tps, + variants[0].args[0]); + } + _ { ret t1; } + } + } + fail; +} + +fn resolve_type_vars_if_possible(fcx: @fn_ctxt, typ: ty::t) -> ty::t { + alt ty::unify::fixup_vars(fcx.ccx.tcx, none, fcx.var_bindings, typ) { + fix_ok(new_type) { ret new_type; } + fix_err(_) { ret typ; } + } +} + + +// Demands - procedures that require that two types unify and emit an error +// message if they don't. +type ty_param_substs_and_ty = {substs: [ty::t], ty: ty::t}; + +mod demand { + fn simple(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t) -> + ty::t { + full(fcx, sp, expected, actual, []).ty + } + + fn with_substs(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t, + ty_param_substs_0: [ty::t]) -> ty_param_substs_and_ty { + full(fcx, sp, expected, actual, ty_param_substs_0) + } + + // Requires that the two types unify, and prints an error message if they + // don't. Returns the unified type and the type parameter substitutions. + fn full(fcx: @fn_ctxt, sp: span, expected: ty::t, actual: ty::t, + ty_param_substs_0: [ty::t]) -> + ty_param_substs_and_ty { + + let ty_param_substs: [mutable ty::t] = [mutable]; + let ty_param_subst_var_ids: [int] = []; + for ty_param_subst: ty::t in ty_param_substs_0 { + // Generate a type variable and unify it with the type parameter + // substitution. We will then pull out these type variables. + let t_0 = next_ty_var(fcx); + ty_param_substs += [mutable t_0]; + ty_param_subst_var_ids += [ty::ty_var_id(t_0)]; + simple(fcx, sp, ty_param_subst, t_0); + } + + fn mk_result(fcx: @fn_ctxt, result_ty: ty::t, + ty_param_subst_var_ids: [int]) -> + ty_param_substs_and_ty { + let result_ty_param_substs: [ty::t] = []; + for var_id: int in ty_param_subst_var_ids { + let tp_subst = ty::mk_var(fcx.ccx.tcx, var_id); + result_ty_param_substs += [tp_subst]; + } + ret {substs: result_ty_param_substs, ty: result_ty}; + } + + + alt unify::unify(fcx, expected, actual) { + ures_ok(t) { ret mk_result(fcx, t, ty_param_subst_var_ids); } + ures_err(err) { + let e_err = resolve_type_vars_if_possible(fcx, expected); + let a_err = resolve_type_vars_if_possible(fcx, actual); + fcx.ccx.tcx.sess.span_err(sp, + "mismatched types: expected `" + + ty_to_str(fcx.ccx.tcx, e_err) + + "` but found `" + + ty_to_str(fcx.ccx.tcx, a_err) + + "` (" + ty::type_err_to_str(err) + + ")"); + ret mk_result(fcx, expected, ty_param_subst_var_ids); + } + } + } +} + + +// Returns true if the two types unify and false if they don't. +fn are_compatible(fcx: @fn_ctxt, expected: ty::t, actual: ty::t) -> bool { + alt unify::unify(fcx, expected, actual) { + ures_ok(_) { ret true; } + ures_err(_) { ret false; } + } +} + + +// Returns the types of the arguments to a enum variant. +fn variant_arg_types(ccx: @crate_ctxt, _sp: span, vid: ast::def_id, + enum_ty_params: [ty::t]) -> [ty::t] { + let result: [ty::t] = []; + let tpt = ty::lookup_item_type(ccx.tcx, vid); + alt ty::get(tpt.ty).struct { + ty::ty_fn(f) { + // N-ary variant. + for arg: ty::arg in f.inputs { + let arg_ty = + ty::substitute_type_params(ccx.tcx, enum_ty_params, arg.ty); + result += [arg_ty]; + } + } + _ { + // Nullary variant. Do nothing, as there are no arguments. + } + } + /* result is a vector of the *expected* types of all the fields */ + + ret result; +} + + +// Type resolution: the phase that finds all the types in the AST with +// unresolved type variables and replaces "ty_var" types with their +// substitutions. +mod writeback { + + export resolve_type_vars_in_block; + export resolve_type_vars_in_expr; + + fn resolve_type_vars_in_type(fcx: @fn_ctxt, sp: span, typ: ty::t) -> + option<ty::t> { + if !ty::type_has_vars(typ) { ret some(typ); } + alt ty::unify::fixup_vars(fcx.ccx.tcx, some(sp), fcx.var_bindings, + typ) { + fix_ok(new_type) { ret some(new_type); } + fix_err(vid) { + if !fcx.ccx.tcx.sess.has_errors() { + fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \ + for this expression"); + } + ret none; + } + } + } + fn resolve_type_vars_for_node(wbcx: wb_ctxt, sp: span, id: ast::node_id) + -> option<ty::t> { + let fcx = wbcx.fcx, tcx = fcx.ccx.tcx; + alt resolve_type_vars_in_type(fcx, sp, ty::node_id_to_type(tcx, id)) { + none { + wbcx.success = false; + ret none; + } + + some(t) { + write_ty(tcx, id, t); + alt tcx.node_type_substs.find(id) { + some(substs) { + let new_substs = []; + for subst: ty::t in substs { + alt resolve_type_vars_in_type(fcx, sp, subst) { + some(t) { new_substs += [t]; } + none { wbcx.success = false; ret none; } + } + } + write_substs(tcx, id, new_substs); + } + none {} + } + ret some(t); + } + } + } + + type wb_ctxt = + // As soon as we hit an error we have to stop resolving + // the entire function + {fcx: @fn_ctxt, mutable success: bool}; + type wb_vt = visit::vt<wb_ctxt>; + + fn visit_stmt(s: @ast::stmt, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s)); + visit::visit_stmt(s, wbcx, v); + } + fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, e.span, e.id); + alt e.node { + ast::expr_fn(_, decl, _, _) | + ast::expr_fn_block(decl, _) { + vec::iter(decl.inputs) {|input| + let r_ty = resolve_type_vars_for_node(wbcx, e.span, input.id); + + // Just in case we never constrained the mode to anything, + // constrain it to the default for the type in question. + alt (r_ty, input.mode) { + (some(t), ast::infer(_)) { + let tcx = wbcx.fcx.ccx.tcx; + let m_def = ty::default_arg_mode_for_ty(t); + ty::set_default_mode(tcx, input.mode, m_def); + } + _ {} + } + } + } + _ { } + } + visit::visit_expr(e, wbcx, v); + } + fn visit_block(b: ast::blk, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, b.span, b.node.id); + visit::visit_block(b, wbcx, v); + } + fn visit_pat(p: @ast::pat, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + resolve_type_vars_for_node(wbcx, p.span, p.id); + visit::visit_pat(p, wbcx, v); + } + fn visit_local(l: @ast::local, wbcx: wb_ctxt, v: wb_vt) { + if !wbcx.success { ret; } + let var_id = lookup_local(wbcx.fcx, l.span, l.node.id); + let fix_rslt = + ty::unify::resolve_type_var(wbcx.fcx.ccx.tcx, some(l.span), + wbcx.fcx.var_bindings, var_id); + alt fix_rslt { + fix_ok(lty) { write_ty(wbcx.fcx.ccx.tcx, l.node.id, lty); } + fix_err(_) { + wbcx.fcx.ccx.tcx.sess.span_err(l.span, + "cannot determine a type \ + for this local variable"); + wbcx.success = false; + } + } + visit::visit_local(l, wbcx, v); + } + fn visit_item(_item: @ast::item, _wbcx: wb_ctxt, _v: wb_vt) { + // Ignore items + } + + fn resolve_type_vars_in_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool { + let wbcx = {fcx: fcx, mutable success: true}; + let visit = + visit::mk_vt(@{visit_item: visit_item, + visit_stmt: visit_stmt, + visit_expr: visit_expr, + visit_block: visit_block, + visit_pat: visit_pat, + visit_local: visit_local + with *visit::default_visitor()}); + visit::visit_expr(e, wbcx, visit); + ret wbcx.success; + } + + fn resolve_type_vars_in_block(fcx: @fn_ctxt, blk: ast::blk) -> bool { + let wbcx = {fcx: fcx, mutable success: true}; + let visit = + visit::mk_vt(@{visit_item: visit_item, + visit_stmt: visit_stmt, + visit_expr: visit_expr, + visit_block: visit_block, + visit_pat: visit_pat, + visit_local: visit_local + with *visit::default_visitor()}); + visit.visit_block(blk, wbcx, visit); + ret wbcx.success; + } +} + + +// Local variable gathering. We gather up all locals and create variable IDs +// for them before typechecking the function. +type gather_result = + {var_bindings: @ty::unify::var_bindings, + locals: hashmap<ast::node_id, int>, + next_var_id: @mutable int}; + +// Used only as a helper for check_fn. +fn gather_locals(ccx: @crate_ctxt, + decl: ast::fn_decl, + body: ast::blk, + id: ast::node_id, + old_fcx: option<@fn_ctxt>) -> gather_result { + let {vb: vb, locals: locals, nvi: nvi} = alt old_fcx { + none { + {vb: ty::unify::mk_var_bindings(), + locals: new_int_hash::<int>(), + nvi: @mutable 0} + } + some(fcx) { + {vb: fcx.var_bindings, + locals: fcx.locals, + nvi: fcx.next_var_id} + } + }; + let tcx = ccx.tcx; + + let next_var_id = fn@() -> int { let rv = *nvi; *nvi += 1; ret rv; }; + + let assign = fn@(nid: ast::node_id, ty_opt: option<ty::t>) { + let var_id = next_var_id(); + locals.insert(nid, var_id); + alt ty_opt { + none {/* nothing to do */ } + some(typ) { + ty::unify::unify(ty::mk_var(tcx, var_id), typ, + ty::unify::in_bindings(vb), tcx); + } + } + }; + + // Add formal parameters. + let args = ty::ty_fn_args(ty::node_id_to_type(ccx.tcx, id)); + let i = 0u; + for arg: ty::arg in args { + assign(decl.inputs[i].id, some(arg.ty)); + i += 1u; + } + + // Add explicitly-declared locals. + let visit_local = fn@(local: @ast::local, &&e: (), v: visit::vt<()>) { + let local_ty = ast_ty_to_ty_crate_infer(ccx, local.node.ty); + assign(local.node.id, local_ty); + visit::visit_local(local, e, v); + }; + + // Add pattern bindings. + let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) { + alt p.node { + ast::pat_ident(_, _) + if !pat_util::pat_is_variant(ccx.tcx.def_map, p) { + assign(p.id, none); + } + _ {} + } + visit::visit_pat(p, e, v); + }; + + // Don't descend into fns and items + fn visit_fn<T>(_fk: visit::fn_kind, _decl: ast::fn_decl, _body: ast::blk, + _sp: span, _id: ast::node_id, _t: T, _v: visit::vt<T>) { + } + fn visit_item<E>(_i: @ast::item, _e: E, _v: visit::vt<E>) { } + + let visit = + @{visit_local: visit_local, + visit_pat: visit_pat, + visit_fn: bind visit_fn(_, _, _, _, _, _, _), + visit_item: bind visit_item(_, _, _) + with *visit::default_visitor()}; + + visit::visit_block(body, (), visit::mk_vt(visit)); + ret {var_bindings: vb, + locals: locals, + next_var_id: nvi}; +} + +// AST fragment checking +fn check_lit(ccx: @crate_ctxt, lit: @ast::lit) -> ty::t { + alt lit.node { + ast::lit_str(_) { ty::mk_str(ccx.tcx) } + ast::lit_int(_, t) { ty::mk_mach_int(ccx.tcx, t) } + ast::lit_uint(_, t) { ty::mk_mach_uint(ccx.tcx, t) } + ast::lit_float(_, t) { ty::mk_mach_float(ccx.tcx, t) } + ast::lit_nil { ty::mk_nil(ccx.tcx) } + ast::lit_bool(_) { ty::mk_bool(ccx.tcx) } + } +} + +fn valid_range_bounds(from: @ast::expr, to: @ast::expr) -> bool { + ast_util::compare_lit_exprs(from, to) <= 0 +} + +fn check_pat_variant(fcx: @fn_ctxt, map: pat_util::pat_id_map, + pat: @ast::pat, path: @ast::path, subpats: [@ast::pat], + expected: ty::t) { + // Typecheck the path. + let tcx = fcx.ccx.tcx; + let v_def = lookup_def(fcx, path.span, pat.id); + let v_def_ids = ast_util::variant_def_ids(v_def); + let ctor_tpt = ty::lookup_item_type(tcx, v_def_ids.enm); + instantiate_path(fcx, path, ctor_tpt, pat.span, pat.id); + + // Take the enum type params out of `expected`. + alt structure_of(fcx, pat.span, expected) { + ty::ty_enum(_, expected_tps) { + let ctor_ty = ty::node_id_to_type(tcx, pat.id); + demand::with_substs(fcx, pat.span, expected, ctor_ty, + expected_tps); + // Get the number of arguments in this enum variant. + let arg_types = variant_arg_types(fcx.ccx, pat.span, + v_def_ids.var, expected_tps); + let subpats_len = subpats.len(), arg_len = arg_types.len(); + if arg_len > 0u { + // N-ary variant. + if arg_len != subpats_len { + let s = #fmt["this pattern has %u field%s, but the \ + corresponding variant has %u field%s", + subpats_len, + if subpats_len == 1u { "" } else { "s" }, + arg_len, + if arg_len == 1u { "" } else { "s" }]; + tcx.sess.span_err(pat.span, s); + } + + vec::iter2(subpats, arg_types) {|subpat, arg_ty| + check_pat(fcx, map, subpat, arg_ty); + } + } else if subpats_len > 0u { + tcx.sess.span_err + (pat.span, #fmt["this pattern has %u field%s, \ + but the corresponding variant has no fields", + subpats_len, + if subpats_len == 1u { "" } + else { "s" }]); + } + } + _ { + tcx.sess.span_err + (pat.span, + #fmt["mismatched types: expected enum but found `%s`", + ty_to_str(tcx, expected)]); + } + } +} + +// Pattern checking is top-down rather than bottom-up so that bindings get +// their types immediately. +fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat, + expected: ty::t) { + let tcx = fcx.ccx.tcx; + alt pat.node { + ast::pat_wild { + alt structure_of(fcx, pat.span, expected) { + ty::ty_enum(_, expected_tps) { + write_ty_substs(tcx, pat.id, expected, + expected_tps); + } + _ { + write_ty(tcx, pat.id, expected); + } + } + } + ast::pat_lit(lt) { + check_expr_with(fcx, lt, expected); + write_ty(tcx, pat.id, expr_ty(tcx, lt)); + } + ast::pat_range(begin, end) { + check_expr_with(fcx, begin, expected); + check_expr_with(fcx, end, expected); + let b_ty = resolve_type_vars_if_possible(fcx, expr_ty(tcx, begin)); + if !ty::same_type(tcx, b_ty, resolve_type_vars_if_possible( + fcx, expr_ty(tcx, end))) { + tcx.sess.span_err(pat.span, "mismatched types in range"); + } else if !ty::type_is_numeric(b_ty) { + tcx.sess.span_err(pat.span, "non-numeric type used in range"); + } else if !valid_range_bounds(begin, end) { + tcx.sess.span_err(begin.span, "lower range bound must be less \ + than upper"); + } + write_ty(tcx, pat.id, b_ty); + } + ast::pat_ident(name, sub) + if !pat_util::pat_is_variant(tcx.def_map, pat) { + let vid = lookup_local(fcx, pat.span, pat.id); + let typ = ty::mk_var(tcx, vid); + typ = demand::simple(fcx, pat.span, expected, typ); + let canon_id = map.get(path_to_ident(name)); + if canon_id != pat.id { + let ct = ty::mk_var(tcx, lookup_local(fcx, pat.span, canon_id)); + typ = demand::simple(fcx, pat.span, ct, typ); + } + write_ty(tcx, pat.id, typ); + alt sub { + some(p) { check_pat(fcx, map, p, expected); } + _ {} + } + } + ast::pat_ident(path, _) { + check_pat_variant(fcx, map, pat, path, [], expected); + } + ast::pat_enum(path, subpats) { + check_pat_variant(fcx, map, pat, path, subpats, expected); + } + ast::pat_rec(fields, etc) { + let ex_fields; + alt structure_of(fcx, pat.span, expected) { + ty::ty_rec(fields) { ex_fields = fields; } + _ { + tcx.sess.span_fatal + (pat.span, + #fmt["mismatched types: expected `%s` but found record", + ty_to_str(tcx, expected)]); + } + } + let f_count = vec::len(fields); + let ex_f_count = vec::len(ex_fields); + if ex_f_count < f_count || !etc && ex_f_count > f_count { + tcx.sess.span_fatal + (pat.span, #fmt["mismatched types: expected a record \ + with %u fields, found one with %u \ + fields", + ex_f_count, f_count]); + } + fn matches(name: str, f: ty::field) -> bool { + ret str::eq(name, f.ident); + } + for f: ast::field_pat in fields { + alt vec::find(ex_fields, bind matches(f.ident, _)) { + some(field) { check_pat(fcx, map, f.pat, field.mt.ty); } + none { + tcx.sess.span_fatal(pat.span, + #fmt["mismatched types: did not \ + expect a record with a field `%s`", + f.ident]); + } + } + } + write_ty(tcx, pat.id, expected); + } + ast::pat_tup(elts) { + let ex_elts; + alt structure_of(fcx, pat.span, expected) { + ty::ty_tup(elts) { ex_elts = elts; } + _ { + tcx.sess.span_fatal + (pat.span, + #fmt["mismatched types: expected `%s`, found tuple", + ty_to_str(tcx, expected)]); + } + } + let e_count = vec::len(elts); + if e_count != vec::len(ex_elts) { + tcx.sess.span_fatal + (pat.span, #fmt["mismatched types: expected a tuple \ + with %u fields, found one with %u \ + fields", vec::len(ex_elts), e_count]); + } + let i = 0u; + for elt in elts { check_pat(fcx, map, elt, ex_elts[i]); i += 1u; } + write_ty(tcx, pat.id, expected); + } + ast::pat_box(inner) { + alt structure_of(fcx, pat.span, expected) { + ty::ty_box(e_inner) { + check_pat(fcx, map, inner, e_inner.ty); + write_ty(tcx, pat.id, expected); + } + _ { + tcx.sess.span_fatal(pat.span, + "mismatched types: expected `" + + ty_to_str(tcx, expected) + + "` found box"); + } + } + } + ast::pat_uniq(inner) { + alt structure_of(fcx, pat.span, expected) { + ty::ty_uniq(e_inner) { + check_pat(fcx, map, inner, e_inner.ty); + write_ty(tcx, pat.id, expected); + } + _ { + tcx.sess.span_fatal(pat.span, + "mismatched types: expected `" + + ty_to_str(tcx, expected) + + "` found uniq"); + } + } + } + } +} + +fn require_unsafe(sess: session, f_purity: ast::purity, sp: span) { + alt f_purity { + ast::unsafe_fn { ret; } + _ { + sess.span_err( + sp, + "unsafe operation requires unsafe function or block"); + } + } +} + +fn require_impure(sess: session, f_purity: ast::purity, sp: span) { + alt f_purity { + ast::unsafe_fn { ret; } + ast::impure_fn | ast::crust_fn { ret; } + ast::pure_fn { + sess.span_err(sp, "Found impure expression in pure function decl"); + } + } +} + +fn require_pure_call(ccx: @crate_ctxt, caller_purity: ast::purity, + callee: @ast::expr, sp: span) { + if caller_purity == ast::unsafe_fn { ret; } + let callee_purity = alt ccx.tcx.def_map.find(callee.id) { + some(ast::def_fn(_, p)) { p } + some(ast::def_variant(_, _)) { ast::pure_fn } + _ { + alt ccx.method_map.find(callee.id) { + some(method_static(did)) { + if did.crate == ast::local_crate { + alt check ccx.tcx.items.get(did.node) { + ast_map::node_method(m, _, _) { m.decl.purity } + } + } else { + csearch::lookup_method_purity(ccx.tcx.sess.cstore, did) + } + } + some(method_param(iid, n_m, _, _)) | some(method_iface(iid, n_m)) { + ty::iface_methods(ccx.tcx, iid)[n_m].purity + } + none { ast::impure_fn } + } + } + }; + alt (caller_purity, callee_purity) { + (ast::impure_fn, ast::unsafe_fn) | (ast::crust_fn, ast::unsafe_fn) { + ccx.tcx.sess.span_err(sp, "safe function calls function marked \ + unsafe"); + } + (ast::pure_fn, ast::unsafe_fn) | (ast::pure_fn, ast::impure_fn) { + ccx.tcx.sess.span_err(sp, "pure function calls function not \ + known to be pure"); + } + _ {} + } +} + +type unifier = fn@(@fn_ctxt, span, ty::t, ty::t) -> ty::t; + +fn check_expr(fcx: @fn_ctxt, expr: @ast::expr) -> bool { + fn dummy_unify(_fcx: @fn_ctxt, _sp: span, _expected: ty::t, actual: ty::t) + -> ty::t { + actual + } + ret check_expr_with_unifier(fcx, expr, dummy_unify, + ty::mk_nil(fcx.ccx.tcx)); +} +fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool { + ret check_expr_with_unifier(fcx, expr, demand::simple, expected); +} + +fn impl_self_ty(tcx: ty::ctxt, did: ast::def_id) -> {n_tps: uint, ty: ty::t} { + if did.crate == ast::local_crate { + alt check tcx.items.get(did.node) { + ast_map::node_item(@{node: ast::item_impl(ts, _, st, _), + _}, _) { + {n_tps: vec::len(ts), ty: ast_ty_to_ty(tcx, m_check, st)} + } + } + } else { + let tpt = csearch::get_type(tcx, did); + {n_tps: vec::len(*tpt.bounds), ty: tpt.ty} + } +} + +fn lookup_method(fcx: @fn_ctxt, expr: @ast::expr, node_id: ast::node_id, + name: ast::ident, ty: ty::t, tps: [ty::t]) + -> option::t<method_origin> { + alt lookup_method_inner(fcx, expr, name, ty) { + some({method_ty: fty, n_tps: method_n_tps, substs, origin, self_sub}) { + let tcx = fcx.ccx.tcx; + let substs = substs, n_tps = vec::len(substs), n_tys = vec::len(tps); + let has_self = ty::type_has_params(fty); + if method_n_tps + n_tps > 0u { + if n_tys == 0u || n_tys != method_n_tps { + if n_tys != 0u { + tcx.sess.span_err + (expr.span, "incorrect number of type \ + parameters given for this method"); + + } + substs += vec::init_fn(method_n_tps, {|_i| + ty::mk_var(tcx, next_ty_var_id(fcx)) + }); + } else { + substs += tps; + } + write_ty_substs(tcx, node_id, fty, substs); + } else { + if n_tys > 0u { + tcx.sess.span_err(expr.span, "this method does not take type \ + parameters"); + } + write_ty(tcx, node_id, fty); + } + if has_self && !option::is_none(self_sub) { + let fty = ty::node_id_to_type(tcx, node_id); + fty = fixup_self_in_method_ty( + tcx, fty, substs, option::get(self_sub)); + write_ty(tcx, node_id, fty); + } + some(origin) + } + none { none } + } +} + +fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr, + name: ast::ident, ty: ty::t) + -> option::t<{method_ty: ty::t, n_tps: uint, substs: [ty::t], + origin: method_origin, + self_sub: option::t<self_subst>}> { + let tcx = fcx.ccx.tcx; + // First, see whether this is an interface-bounded parameter + alt ty::get(ty).struct { + ty::ty_param(n, did) { + let bound_n = 0u; + for bound in *tcx.ty_param_bounds.get(did.node) { + alt bound { + ty::bound_iface(t) { + let (iid, tps) = alt check ty::get(t).struct { + ty::ty_iface(i, tps) { (i, tps) } + }; + let ifce_methods = ty::iface_methods(tcx, iid); + alt vec::position(*ifce_methods, {|m| m.ident == name}) { + some(pos) { + let m = ifce_methods[pos]; + ret some({method_ty: ty::mk_fn(tcx, {proto: ast::proto_box + with m.fty}), + n_tps: vec::len(*m.tps), + substs: tps, + origin: method_param(iid, pos, n, bound_n), + self_sub: some(self_param(ty, fcx, expr.span)) + }); + } + _ {} + } + bound_n += 1u; + } + _ {} + } + } + } + ty::ty_iface(did, tps) { + let i = 0u; + for m in *ty::iface_methods(tcx, did) { + if m.ident == name { + let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty}); + if ty::type_has_vars(fty) { + tcx.sess.span_fatal( + expr.span, "can not call a method that contains a \ + self type through a boxed iface"); + } + ret some({method_ty: fty, + n_tps: vec::len(*m.tps), + substs: tps, + origin: method_iface(did, i), + self_sub: none}); + } + i += 1u; + } + } + _ {} + } + + fn ty_from_did(tcx: ty::ctxt, did: ast::def_id) -> ty::t { + if did.crate == ast::local_crate { + alt check tcx.items.get(did.node) { + ast_map::node_method(m, _, _) { + let mt = ty_of_method(tcx, m_check, m); + ty::mk_fn(tcx, {proto: ast::proto_box with mt.fty}) + } + } + } else { + alt check ty::get(csearch::get_type(tcx, did).ty).struct { + ty::ty_fn(fty) { + ty::mk_fn(tcx, {proto: ast::proto_box with fty}) + } + } + } + } + + let result = none, complained = false; + std::list::iter(fcx.ccx.impl_map.get(expr.id)) {|impls| + if option::is_some(result) { ret; } + for @{did, methods, _} in *impls { + alt vec::find(methods, {|m| m.ident == name}) { + some(m) { + let {n_tps, ty: self_ty} = impl_self_ty(tcx, did); + let {vars, ty: self_ty} = if n_tps > 0u { + bind_params(fcx, self_ty, n_tps) + } else { {vars: [], ty: self_ty} }; + alt unify::unify(fcx, ty, self_ty) { + ures_ok(_) { + if option::is_some(result) { + // FIXME[impl] score specificity to resolve ambiguity? + if !complained { + tcx.sess.span_err(expr.span, "multiple applicable \ + methods in scope"); + complained = true; + } + } else { + result = some({ + method_ty: ty_from_did(tcx, m.did), + n_tps: m.n_tps, + substs: vars, + origin: method_static(m.did), + self_sub: none + }); + } + } + _ {} + } + } + _ {} + } + } + } + result +} + +fn lookup_field_ty(cx: ty::ctxt, items:[@ast::class_item], + fieldname: ast::ident, sp: span) + -> ty::t { + for item in items { + // this is an access outside the class, so accessing a private + // field is an error + alt item.node.decl { + ast::instance_var(declname, t, _, _) if declname == fieldname { + alt item.node.privacy { + ast::priv { + cx.sess.span_fatal(sp, "Accessed private field outside \ + its enclosing class"); + } + ast::pub { + ret ast_ty_to_ty(cx, m_check, t); + } + } + } + _ { /* do nothing */ } + } + } + cx.sess.span_fatal(sp, #fmt("Unbound field %s", fieldname)); +} + +fn check_expr_fn_with_unifier(fcx: @fn_ctxt, + expr: @ast::expr, + proto: ast::proto, + decl: ast::fn_decl, + body: ast::blk, + unify: unifier, + expected: ty::t) { + let tcx = fcx.ccx.tcx; + let fty = ty::mk_fn(tcx, + ty_of_fn_decl(tcx, m_check_tyvar(fcx), proto, decl)); + + #debug("check_expr_fn_with_unifier %s fty=%s", + expr_to_str(expr), + ty_to_str(tcx, fty)); + + write_ty(tcx, expr.id, fty); + + // Unify the type of the function with the expected type before we + // typecheck the body so that we have more information about the + // argument types in the body. This is needed to make binops and + // record projection work on type inferred arguments. + unify(fcx, expr.span, expected, fty); + + check_fn(fcx.ccx, proto, decl, body, expr.id, some(fcx)); +} + +fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, + expected: ty::t) -> bool { + #debug("typechecking expr %s", + syntax::print::pprust::expr_to_str(expr)); + + // A generic function to factor out common logic from call and bind + // expressions. + fn check_call_or_bind(fcx: @fn_ctxt, sp: span, fty: ty::t, + args: [option<@ast::expr>]) -> bool { + let sty = structure_of(fcx, sp, fty); + // Grab the argument types + let arg_tys = alt sty { + ty::ty_fn({inputs: arg_tys, _}) { arg_tys } + _ { + fcx.ccx.tcx.sess.span_fatal(sp, "mismatched types: \ + expected function or native \ + function but found " + + ty_to_str(fcx.ccx.tcx, fty)) + } + }; + + // Check that the correct number of arguments were supplied. + let expected_arg_count = vec::len(arg_tys); + let supplied_arg_count = vec::len(args); + if expected_arg_count != supplied_arg_count { + fcx.ccx.tcx.sess.span_err( + sp, #fmt["this function takes %u parameter%s but %u \ + parameter%s supplied", expected_arg_count, + if expected_arg_count == 1u { + "" + } else { + "s" + }, + supplied_arg_count, + if supplied_arg_count == 1u { + " was" + } else { + "s were" + }]); + // HACK: build an arguments list with dummy arguments to + // check against + let dummy = {mode: ast::expl(ast::by_ref), + ty: ty::mk_bot(fcx.ccx.tcx)}; + arg_tys = vec::init_elt(supplied_arg_count, dummy); + } + + // Check the arguments. + // We do this in a pretty awful way: first we typecheck any arguments + // that are not anonymous functions, then we typecheck the anonymous + // functions. This is so that we have more information about the types + // of arguments when we typecheck the functions. This isn't really the + // right way to do this. + let check_args = fn@(check_blocks: bool) -> bool { + let i = 0u; + let bot = false; + for a_opt in args { + alt a_opt { + some(a) { + let is_block = alt a.node { + ast::expr_fn_block(_, _) { true } + _ { false } + }; + if is_block == check_blocks { + bot |= check_expr_with_unifier( + fcx, a, demand::simple, arg_tys[i].ty); + } + } + none { } + } + i += 1u; + } + ret bot; + }; + check_args(false) | check_args(true) + } + + // A generic function for checking assignment expressions + fn check_assignment(fcx: @fn_ctxt, _sp: span, lhs: @ast::expr, + rhs: @ast::expr, id: ast::node_id) -> bool { + let t = next_ty_var(fcx); + let bot = check_expr_with(fcx, lhs, t) | check_expr_with(fcx, rhs, t); + write_ty(fcx.ccx.tcx, id, ty::mk_nil(fcx.ccx.tcx)); + ret bot; + } + + // A generic function for checking call expressions + fn check_call(fcx: @fn_ctxt, sp: span, f: @ast::expr, args: [@ast::expr]) + -> bool { + let args_opt_0: [option<@ast::expr>] = []; + for arg: @ast::expr in args { + args_opt_0 += [some::<@ast::expr>(arg)]; + } + + let bot = check_expr(fcx, f); + // Call the generic checker. + bot | check_call_or_bind(fcx, sp, expr_ty(fcx.ccx.tcx, f), args_opt_0) + } + + // A generic function for doing all of the checking for call expressions + fn check_call_full(fcx: @fn_ctxt, sp: span, f: @ast::expr, + args: [@ast::expr], id: ast::node_id) -> bool { + let bot = check_call(fcx, sp, f, args); + /* here we're kind of hosed, as f can be any expr + need to restrict it to being an explicit expr_path if we're + inside a pure function, and need an environment mapping from + function name onto purity-designation */ + require_pure_call(fcx.ccx, fcx.purity, f, sp); + + // Pull the return type out of the type of the function. + let fty = ty::expr_ty(fcx.ccx.tcx, f); + let rt_1 = alt structure_of(fcx, sp, fty) { + ty::ty_fn(f) { + bot |= f.ret_style == ast::noreturn; + f.output + } + _ { fcx.ccx.tcx.sess.span_fatal(sp, "calling non-function"); } + }; + write_ty(fcx.ccx.tcx, id, rt_1); + ret bot; + } + + // A generic function for checking for or for-each loops + fn check_for(fcx: @fn_ctxt, local: @ast::local, + element_ty: ty::t, body: ast::blk, + node_id: ast::node_id) -> bool { + let locid = lookup_local(fcx, local.span, local.node.id); + let element_ty = demand::simple(fcx, local.span, element_ty, + ty::mk_var(fcx.ccx.tcx, locid)); + let bot = check_decl_local(fcx, local); + check_block_no_value(fcx, body); + // Unify type of decl with element type of the seq + demand::simple(fcx, local.span, + ty::node_id_to_type(fcx.ccx.tcx, local.node.id), + element_ty); + write_nil(fcx.ccx.tcx, node_id); + ret bot; + } + + + // A generic function for checking the then and else in an if + // or if-check + fn check_then_else(fcx: @fn_ctxt, thn: ast::blk, + elsopt: option<@ast::expr>, id: ast::node_id, + _sp: span) -> bool { + let (if_t, if_bot) = + alt elsopt { + some(els) { + let thn_bot = check_block(fcx, thn); + let thn_t = block_ty(fcx.ccx.tcx, thn); + let els_bot = check_expr_with(fcx, els, thn_t); + let els_t = expr_ty(fcx.ccx.tcx, els); + let if_t = if !ty::type_is_bot(els_t) { + els_t + } else { + thn_t + }; + (if_t, thn_bot & els_bot) + } + none { + check_block_no_value(fcx, thn); + (ty::mk_nil(fcx.ccx.tcx), false) + } + }; + write_ty(fcx.ccx.tcx, id, if_t); + ret if_bot; + } + + fn binop_method(op: ast::binop) -> option<str> { + alt op { + ast::add | ast::subtract | ast::mul | ast::div | ast::rem | + ast::bitxor | ast::bitand | ast::bitor | ast::lsl | ast::lsr | + ast::asr { some(ast_util::binop_to_str(op)) } + _ { none } + } + } + fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t, + opname: str, args: [option::t<@ast::expr>]) + -> option::t<ty::t> { + let callee_id = ast_util::op_expr_callee_id(op_ex); + alt lookup_method(fcx, op_ex, callee_id, opname, self_t, []) { + some(origin) { + let method_ty = ty::node_id_to_type(fcx.ccx.tcx, callee_id); + check_call_or_bind(fcx, op_ex.span, method_ty, args); + fcx.ccx.method_map.insert(op_ex.id, origin); + some(ty::ty_fn_ret(method_ty)) + } + _ { none } + } + } + fn check_binop(fcx: @fn_ctxt, ex: @ast::expr, ty: ty::t, + op: ast::binop, rhs: @ast::expr) -> ty::t { + let resolved_t = structurally_resolved_type(fcx, ex.span, ty); + let tcx = fcx.ccx.tcx; + if ty::is_binopable(tcx, resolved_t, op) { + ret alt op { + ast::eq | ast::lt | ast::le | ast::ne | ast::ge | + ast::gt { ty::mk_bool(tcx) } + _ { resolved_t } + }; + } + + alt binop_method(op) { + some(name) { + alt lookup_op_method(fcx, ex, resolved_t, name, [some(rhs)]) { + some(ret_ty) { ret ret_ty; } + _ {} + } + } + _ {} + } + tcx.sess.span_err( + ex.span, "binary operation " + ast_util::binop_to_str(op) + + " cannot be applied to type `" + ty_to_str(tcx, resolved_t) + + "`"); + resolved_t + } + fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str, + ex: @ast::expr, rhs_t: ty::t) -> ty::t { + alt lookup_op_method(fcx, ex, rhs_t, mname, []) { + some(ret_ty) { ret_ty } + _ { + fcx.ccx.tcx.sess.span_err( + ex.span, #fmt["cannot apply unary operator `%s` to type `%s`", + op_str, ty_to_str(fcx.ccx.tcx, rhs_t)]); + rhs_t + } + } + } + + let tcx = fcx.ccx.tcx; + let id = expr.id; + let bot = false; + alt expr.node { + ast::expr_lit(lit) { + let typ = check_lit(fcx.ccx, lit); + write_ty(tcx, id, typ); + } + ast::expr_binary(binop, lhs, rhs) { + let lhs_t = next_ty_var(fcx); + bot = check_expr_with(fcx, lhs, lhs_t); + + let rhs_bot = if !ast_util::is_shift_binop(binop) { + check_expr_with(fcx, rhs, lhs_t) + } else { + let rhs_bot = check_expr(fcx, rhs); + let rhs_t = expr_ty(tcx, rhs); + require_integral(fcx, rhs.span, rhs_t); + rhs_bot + }; + + if !ast_util::lazy_binop(binop) { bot |= rhs_bot; } + + let result = check_binop(fcx, expr, lhs_t, binop, rhs); + write_ty(tcx, id, result); + } + ast::expr_assign_op(op, lhs, rhs) { + require_impure(tcx.sess, fcx.purity, expr.span); + bot = check_assignment(fcx, expr.span, lhs, rhs, id); + let lhs_t = ty::expr_ty(tcx, lhs); + let result = check_binop(fcx, expr, lhs_t, op, rhs); + demand::simple(fcx, expr.span, result, lhs_t); + } + ast::expr_unary(unop, oper) { + bot = check_expr(fcx, oper); + let oper_t = expr_ty(tcx, oper); + alt unop { + ast::box(mutbl) { + oper_t = ty::mk_box(tcx, {ty: oper_t, mutbl: mutbl}); + } + ast::uniq(mutbl) { + oper_t = ty::mk_uniq(tcx, {ty: oper_t, mutbl: mutbl}); + } + ast::deref { + alt structure_of(fcx, expr.span, oper_t) { + ty::ty_box(inner) { oper_t = inner.ty; } + ty::ty_uniq(inner) { oper_t = inner.ty; } + ty::ty_res(_, inner, _) { oper_t = inner; } + ty::ty_enum(id, tps) { + let variants = ty::enum_variants(tcx, id); + if vec::len(*variants) != 1u || + vec::len(variants[0].args) != 1u { + tcx.sess.span_err(expr.span, + "can only dereference enums " + + "with a single variant which has a " + + "single argument"); + } + oper_t = + ty::substitute_type_params(tcx, tps, variants[0].args[0]); + } + ty::ty_ptr(inner) { + oper_t = inner.ty; + require_unsafe(tcx.sess, fcx.purity, expr.span); + } + _ { + tcx.sess.span_err(expr.span, + "dereferencing non-" + + "dereferenceable type: " + + ty_to_str(tcx, oper_t)); + } + } + } + ast::not { + oper_t = structurally_resolved_type(fcx, oper.span, oper_t); + if !(ty::type_is_integral(oper_t) || + ty::get(oper_t).struct == ty::ty_bool) { + oper_t = check_user_unop(fcx, "!", "!", expr, oper_t); + } + } + ast::neg { + oper_t = structurally_resolved_type(fcx, oper.span, oper_t); + if !(ty::type_is_integral(oper_t) || + ty::type_is_fp(oper_t)) { + oper_t = check_user_unop(fcx, "-", "unary-", expr, oper_t); + } + } + } + write_ty(tcx, id, oper_t); + } + ast::expr_path(pth) { + let defn = lookup_def(fcx, pth.span, id); + + let tpt = ty_param_bounds_and_ty_for_def(fcx, expr.span, defn); + if ty::def_has_ty_params(defn) { + instantiate_path(fcx, pth, tpt, expr.span, expr.id); + } else { + // The definition doesn't take type parameters. If the programmer + // supplied some, that's an error + if vec::len::<@ast::ty>(pth.node.types) > 0u { + tcx.sess.span_fatal(expr.span, + "this kind of value does not \ + take type parameters"); + } + write_ty(tcx, id, tpt.ty); + } + } + ast::expr_mac(_) { tcx.sess.bug("unexpanded macro"); } + ast::expr_fail(expr_opt) { + bot = true; + alt expr_opt { + none {/* do nothing */ } + some(e) { check_expr_with(fcx, e, ty::mk_str(tcx)); } + } + write_bot(tcx, id); + } + ast::expr_break { write_bot(tcx, id); bot = true; } + ast::expr_cont { write_bot(tcx, id); bot = true; } + ast::expr_ret(expr_opt) { + bot = true; + alt expr_opt { + none { + let nil = ty::mk_nil(tcx); + if !are_compatible(fcx, fcx.ret_ty, nil) { + tcx.sess.span_err(expr.span, + "ret; in function returning non-nil"); + } + } + some(e) { check_expr_with(fcx, e, fcx.ret_ty); } + } + write_bot(tcx, id); + } + ast::expr_be(e) { + // FIXME: prove instead of assert + assert (ast_util::is_call_expr(e)); + check_expr_with(fcx, e, fcx.ret_ty); + bot = true; + write_nil(tcx, id); + } + ast::expr_log(_, lv, e) { + bot = check_expr_with(fcx, lv, ty::mk_mach_uint(tcx, ast::ty_u32)); + bot |= check_expr(fcx, e); + write_nil(tcx, id); + } + ast::expr_check(_, e) { + bot = check_pred_expr(fcx, e); + write_nil(tcx, id); + } + ast::expr_if_check(cond, thn, elsopt) { + bot = + check_pred_expr(fcx, cond) | + check_then_else(fcx, thn, elsopt, id, expr.span); + } + ast::expr_assert(e) { + bot = check_expr_with(fcx, e, ty::mk_bool(tcx)); + write_nil(tcx, id); + } + ast::expr_copy(a) { + bot = check_expr_with_unifier(fcx, a, unify, expected); + write_ty(tcx, id, ty::node_id_to_type(tcx, a.id)); + } + ast::expr_move(lhs, rhs) { + require_impure(tcx.sess, fcx.purity, expr.span); + bot = check_assignment(fcx, expr.span, lhs, rhs, id); + } + ast::expr_assign(lhs, rhs) { + require_impure(tcx.sess, fcx.purity, expr.span); + bot = check_assignment(fcx, expr.span, lhs, rhs, id); + } + ast::expr_swap(lhs, rhs) { + require_impure(tcx.sess, fcx.purity, expr.span); + bot = check_assignment(fcx, expr.span, lhs, rhs, id); + } + ast::expr_if(cond, thn, elsopt) { + bot = + check_expr_with(fcx, cond, ty::mk_bool(tcx)) | + check_then_else(fcx, thn, elsopt, id, expr.span); + } + ast::expr_for(decl, seq, body) { + bot = check_expr(fcx, seq); + let elt_ty; + let ety = expr_ty(tcx, seq); + alt structure_of(fcx, expr.span, ety) { + ty::ty_vec(vec_elt_ty) { elt_ty = vec_elt_ty.ty; } + ty::ty_str { elt_ty = ty::mk_mach_uint(tcx, ast::ty_u8); } + _ { + tcx.sess.span_fatal(expr.span, + "mismatched types: expected vector or string " + + "but found `" + ty_to_str(tcx, ety) + "`"); + } + } + bot |= check_for(fcx, decl, elt_ty, body, id); + } + ast::expr_while(cond, body) { + bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)); + check_block_no_value(fcx, body); + write_ty(tcx, id, ty::mk_nil(tcx)); + } + ast::expr_do_while(body, cond) { + bot = check_expr_with(fcx, cond, ty::mk_bool(tcx)) | + check_block_no_value(fcx, body); + write_ty(tcx, id, block_ty(tcx, body)); + } + ast::expr_alt(expr, arms, _) { + bot = check_expr(fcx, expr); + + // Typecheck the patterns first, so that we get types for all the + // bindings. + let pattern_ty = ty::expr_ty(tcx, expr); + for arm: ast::arm in arms { + let id_map = pat_util::pat_id_map(tcx.def_map, arm.pats[0]); + for p: @ast::pat in arm.pats { + check_pat(fcx, id_map, p, pattern_ty); + } + } + // Now typecheck the blocks. + let result_ty = next_ty_var(fcx); + let arm_non_bot = false; + for arm: ast::arm in arms { + alt arm.guard { + some(e) { check_expr_with(fcx, e, ty::mk_bool(tcx)); } + none { } + } + if !check_block(fcx, arm.body) { arm_non_bot = true; } + let bty = block_ty(tcx, arm.body); + result_ty = demand::simple(fcx, arm.body.span, result_ty, bty); + } + bot |= !arm_non_bot; + if !arm_non_bot { result_ty = ty::mk_bot(tcx); } + write_ty(tcx, id, result_ty); + } + ast::expr_fn(proto, decl, body, captures) { + check_expr_fn_with_unifier(fcx, expr, proto, decl, body, + unify, expected); + capture::check_capture_clause(tcx, expr.id, proto, *captures); + } + ast::expr_fn_block(decl, body) { + // Take the prototype from the expected type, but default to block: + let proto = alt ty::get(expected).struct { + ty::ty_fn({proto, _}) { proto } + _ { ast::proto_box } + }; + #debug("checking expr_fn_block %s expected=%s", + expr_to_str(expr), + ty_to_str(tcx, expected)); + check_expr_fn_with_unifier(fcx, expr, proto, decl, body, + unify, expected); + } + ast::expr_block(b) { + // If this is an unchecked block, turn off purity-checking + bot = check_block(fcx, b); + let typ = + alt b.node.expr { + some(expr) { expr_ty(tcx, expr) } + none { ty::mk_nil(tcx) } + }; + write_ty(tcx, id, typ); + } + ast::expr_bind(f, args) { + // Call the generic checker. + bot = check_expr(fcx, f); + bot |= check_call_or_bind(fcx, expr.span, expr_ty(tcx, f), args); + + // Pull the argument and return types out. + let proto, arg_tys, rt, cf, constrs; + alt structure_of(fcx, expr.span, expr_ty(tcx, f)) { + // FIXME: + // probably need to munge the constrs to drop constraints + // for any bound args + ty::ty_fn(f) { + proto = f.proto; + arg_tys = f.inputs; + rt = f.output; + cf = f.ret_style; + constrs = f.constraints; + } + _ { fail "LHS of bind expr didn't have a function type?!"; } + } + + let proto = alt proto { + ast::proto_bare | ast::proto_box { ast::proto_box } + ast::proto_uniq | ast::proto_any | ast::proto_block { + tcx.sess.span_err(expr.span, + #fmt["cannot bind %s closures", + proto_to_str(proto)]); + proto // dummy value so compilation can proceed + } + }; + + // For each blank argument, add the type of that argument + // to the resulting function type. + let out_args = []; + let i = 0u; + while i < vec::len(args) { + alt args[i] { + some(_) {/* no-op */ } + none { out_args += [arg_tys[i]]; } + } + i += 1u; + } + + let ft = ty::mk_fn(tcx, {proto: proto, + inputs: out_args, output: rt, + ret_style: cf, constraints: constrs}); + write_ty(tcx, id, ft); + } + ast::expr_call(f, args, _) { + bot = check_call_full(fcx, expr.span, f, args, expr.id); + } + ast::expr_cast(e, t) { + bot = check_expr(fcx, e); + let t_1 = ast_ty_to_ty_crate(fcx.ccx, t); + let t_e = ty::expr_ty(tcx, e); + + alt ty::get(t_1).struct { + // This will be looked up later on + ty::ty_iface(_, _) {} + _ { + if ty::type_is_nil(t_e) { + tcx.sess.span_err(expr.span, "cast from nil: " + + ty_to_str(tcx, t_e) + " as " + + ty_to_str(tcx, t_1)); + } else if ty::type_is_nil(t_1) { + tcx.sess.span_err(expr.span, "cast to nil: " + + ty_to_str(tcx, t_e) + " as " + + ty_to_str(tcx, t_1)); + } + + let t_1_is_scalar = type_is_scalar(fcx, expr.span, t_1); + if type_is_c_like_enum(fcx,expr.span,t_e) && t_1_is_scalar { + /* this case is allowed */ + } else if !(type_is_scalar(fcx,expr.span,t_e) && t_1_is_scalar) { + // FIXME there are more forms of cast to support, eventually. + tcx.sess.span_err(expr.span, + "non-scalar cast: " + + ty_to_str(tcx, t_e) + " as " + + ty_to_str(tcx, t_1)); + } + } + } + write_ty(tcx, id, t_1); + } + ast::expr_vec(args, mutbl) { + let t: ty::t = next_ty_var(fcx); + for e: @ast::expr in args { bot |= check_expr_with(fcx, e, t); } + let typ = ty::mk_vec(tcx, {ty: t, mutbl: mutbl}); + write_ty(tcx, id, typ); + } + ast::expr_tup(elts) { + let elt_ts = []; + vec::reserve(elt_ts, vec::len(elts)); + for e in elts { + check_expr(fcx, e); + let ety = expr_ty(tcx, e); + elt_ts += [ety]; + } + let typ = ty::mk_tup(tcx, elt_ts); + write_ty(tcx, id, typ); + } + ast::expr_rec(fields, base) { + alt base { none {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } } + let fields_t: [spanned<field>] = []; + for f: ast::field in fields { + bot |= check_expr(fcx, f.node.expr); + let expr_t = expr_ty(tcx, f.node.expr); + let expr_mt = {ty: expr_t, mutbl: f.node.mutbl}; + // for the most precise error message, + // should be f.node.expr.span, not f.span + fields_t += + [respan(f.node.expr.span, + {ident: f.node.ident, mt: expr_mt})]; + } + alt base { + none { + fn get_node(f: spanned<field>) -> field { f.node } + let typ = ty::mk_rec(tcx, vec::map(fields_t, get_node)); + write_ty(tcx, id, typ); + } + some(bexpr) { + bot |= check_expr(fcx, bexpr); + let bexpr_t = expr_ty(tcx, bexpr); + let base_fields: [field] = []; + alt structure_of(fcx, expr.span, bexpr_t) { + ty::ty_rec(flds) { base_fields = flds; } + _ { + tcx.sess.span_fatal(expr.span, + "record update has non-record base"); + } + } + write_ty(tcx, id, bexpr_t); + for f: spanned<ty::field> in fields_t { + let found = false; + for bf: ty::field in base_fields { + if str::eq(f.node.ident, bf.ident) { + demand::simple(fcx, f.span, bf.mt.ty, f.node.mt.ty); + found = true; + } + } + if !found { + tcx.sess.span_fatal(f.span, + "unknown field in record update: " + + f.node.ident); + } + } + } + } + } + ast::expr_field(base, field, tys) { + bot |= check_expr(fcx, base); + let expr_t = structurally_resolved_type(fcx, expr.span, + expr_ty(tcx, base)); + let base_t = do_autoderef(fcx, expr.span, expr_t); + let handled = false, n_tys = vec::len(tys); + alt structure_of(fcx, expr.span, base_t) { + ty::ty_rec(fields) { + alt ty::field_idx(field, fields) { + some(ix) { + if n_tys > 0u { + tcx.sess.span_err(expr.span, + "can't provide type parameters \ + to a field access"); + } + write_ty(tcx, id, fields[ix].mt.ty); + handled = true; + } + _ {} + } + } + ty::ty_class(base_id, _params) { + // (1) verify that the class id actually has a field called + // field + // For now, this code assumes the class is defined in the local + // crate + // FIXME: handle field references to classes in external crate + let err = "Class ID is not bound to a class"; + let field_ty = alt fcx.ccx.tcx.items.find(base_id.node) { + some(ast_map::node_item(i,_)) { + alt i.node { + ast::item_class(_, items, _, _, _) { + lookup_field_ty(fcx.ccx.tcx, items, field, + expr.span) + } + _ { fcx.ccx.tcx.sess.span_bug(expr.span, err); } + } + } + _ { fcx.ccx.tcx.sess.span_bug(expr.span, err); } + }; + // (2) look up what field's type is, and return it + // FIXME: actually instantiate any type params + write_ty(tcx, id, field_ty); + handled = true; + } + _ {} + } + if !handled { + let tps = vec::map(tys, {|ty| ast_ty_to_ty_crate(fcx.ccx, ty)}); + alt lookup_method(fcx, expr, expr.id, field, expr_t, tps) { + some(origin) { + fcx.ccx.method_map.insert(id, origin); + } + none { + let t_err = resolve_type_vars_if_possible(fcx, expr_t); + let msg = #fmt["attempted access of field %s on type %s, but \ + no method implementation was found", + field, ty_to_str(tcx, t_err)]; + tcx.sess.span_err(expr.span, msg); + // NB: Adding a bogus type to allow typechecking to continue + write_ty(tcx, id, next_ty_var(fcx)); + } + } + } + } + ast::expr_index(base, idx) { + bot |= check_expr(fcx, base); + let raw_base_t = expr_ty(tcx, base); + let base_t = do_autoderef(fcx, expr.span, raw_base_t); + bot |= check_expr(fcx, idx); + let idx_t = expr_ty(tcx, idx); + alt structure_of(fcx, expr.span, base_t) { + ty::ty_vec(mt) { + require_integral(fcx, idx.span, idx_t); + write_ty(tcx, id, mt.ty); + } + ty::ty_str { + require_integral(fcx, idx.span, idx_t); + let typ = ty::mk_mach_uint(tcx, ast::ty_u8); + write_ty(tcx, id, typ); + } + _ { + let resolved = structurally_resolved_type(fcx, expr.span, + raw_base_t); + alt lookup_op_method(fcx, expr, resolved, "[]", + [some(idx)]) { + some(ret_ty) { write_ty(tcx, id, ret_ty); } + _ { + tcx.sess.span_fatal( + expr.span, "cannot index a value of type `" + + ty_to_str(tcx, base_t) + "`"); + } + } + } + } + } + _ { tcx.sess.unimpl("expr type in typeck::check_expr"); } + } + if bot { write_ty(tcx, expr.id, ty::mk_bot(tcx)); } + + unify(fcx, expr.span, expected, expr_ty(tcx, expr)); + ret bot; +} + +fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) { + if !type_is_integral(fcx, sp, t) { + fcx.ccx.tcx.sess.span_err(sp, "mismatched types: expected \ + `integer` but found `" + + ty_to_str(fcx.ccx.tcx, t) + "`"); + } +} + +fn next_ty_var_id(fcx: @fn_ctxt) -> int { + let id = *fcx.next_var_id; + *fcx.next_var_id += 1; + ret id; +} + +fn next_ty_var(fcx: @fn_ctxt) -> ty::t { + ret ty::mk_var(fcx.ccx.tcx, next_ty_var_id(fcx)); +} + +fn bind_params(fcx: @fn_ctxt, tp: ty::t, count: uint) + -> {vars: [ty::t], ty: ty::t} { + let vars = vec::init_fn(count, {|_i| next_ty_var(fcx)}); + {vars: vars, ty: ty::substitute_type_params(fcx.ccx.tcx, vars, tp)} +} + +fn get_self_info(ccx: @crate_ctxt) -> option<self_info> { + ret vec::last(ccx.self_infos); +} + +fn check_decl_initializer(fcx: @fn_ctxt, nid: ast::node_id, + init: ast::initializer) -> bool { + let lty = ty::mk_var(fcx.ccx.tcx, lookup_local(fcx, init.expr.span, nid)); + ret check_expr_with(fcx, init.expr, lty); +} + +fn check_decl_local(fcx: @fn_ctxt, local: @ast::local) -> bool { + let bot = false; + + let t = ty::mk_var(fcx.ccx.tcx, fcx.locals.get(local.node.id)); + write_ty(fcx.ccx.tcx, local.node.id, t); + alt local.node.init { + some(init) { + bot = check_decl_initializer(fcx, local.node.id, init); + } + _ {/* fall through */ } + } + let id_map = pat_util::pat_id_map(fcx.ccx.tcx.def_map, local.node.pat); + check_pat(fcx, id_map, local.node.pat, t); + ret bot; +} + +fn check_stmt(fcx: @fn_ctxt, stmt: @ast::stmt) -> bool { + let node_id; + let bot = false; + alt stmt.node { + ast::stmt_decl(decl, id) { + node_id = id; + alt decl.node { + ast::decl_local(ls) { + for l in ls { bot |= check_decl_local(fcx, l); } + } + ast::decl_item(_) {/* ignore for now */ } + } + } + ast::stmt_expr(expr, id) { + node_id = id; + bot = check_expr_with(fcx, expr, ty::mk_nil(fcx.ccx.tcx)); + } + ast::stmt_semi(expr, id) { + node_id = id; + bot = check_expr(fcx, expr); + } + } + write_nil(fcx.ccx.tcx, node_id); + ret bot; +} + +fn check_block_no_value(fcx: @fn_ctxt, blk: ast::blk) -> bool { + let bot = check_block(fcx, blk); + if !bot { + let blkty = ty::node_id_to_type(fcx.ccx.tcx, blk.node.id); + let nilty = ty::mk_nil(fcx.ccx.tcx); + demand::simple(fcx, blk.span, nilty, blkty); + } + ret bot; +} + +fn check_block(fcx0: @fn_ctxt, blk: ast::blk) -> bool { + let fcx = alt blk.node.rules { + ast::unchecked_blk { @{purity: ast::impure_fn with *fcx0} } + ast::unsafe_blk { @{purity: ast::unsafe_fn with *fcx0} } + ast::default_blk { fcx0 } + }; + let bot = false; + let warned = false; + for s: @ast::stmt in blk.node.stmts { + if bot && !warned && + alt s.node { + ast::stmt_decl(@{node: ast::decl_local(_), _}, _) | + ast::stmt_expr(_, _) | ast::stmt_semi(_, _) { + true + } + _ { false } + } { + fcx.ccx.tcx.sess.span_warn(s.span, "unreachable statement"); + warned = true; + } + bot |= check_stmt(fcx, s); + } + alt blk.node.expr { + none { write_nil(fcx.ccx.tcx, blk.node.id); } + some(e) { + if bot && !warned { + fcx.ccx.tcx.sess.span_warn(e.span, "unreachable expression"); + } + bot |= check_expr(fcx, e); + let ety = expr_ty(fcx.ccx.tcx, e); + write_ty(fcx.ccx.tcx, blk.node.id, ety); + } + } + if bot { + write_ty(fcx.ccx.tcx, blk.node.id, ty::mk_bot(fcx.ccx.tcx)); + } + ret bot; +} + +fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) { + // FIXME: this is kinda a kludge; we manufacture a fake function context + // and statement context for checking the initializer expression. + let rty = node_id_to_type(ccx.tcx, id); + let fcx: @fn_ctxt = + @{ret_ty: rty, + purity: ast::pure_fn, + proto: ast::proto_box, + var_bindings: ty::unify::mk_var_bindings(), + locals: new_int_hash::<int>(), + next_var_id: @mutable 0, + ccx: ccx}; + check_expr(fcx, e); + let cty = expr_ty(fcx.ccx.tcx, e); + let declty = fcx.ccx.tcx.tcache.get(local_def(id)).ty; + demand::simple(fcx, e.span, declty, cty); +} + +fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant], + id: ast::node_id) { + // FIXME: this is kinda a kludge; we manufacture a fake function context + // and statement context for checking the initializer expression. + let rty = node_id_to_type(ccx.tcx, id); + let fcx: @fn_ctxt = + @{ret_ty: rty, + purity: ast::pure_fn, + proto: ast::proto_box, + var_bindings: ty::unify::mk_var_bindings(), + locals: new_int_hash::<int>(), + next_var_id: @mutable 0, + ccx: ccx}; + let disr_vals: [int] = []; + let disr_val = 0; + for v in vs { + alt v.node.disr_expr { + some(e) { + check_expr(fcx, e); + let cty = expr_ty(fcx.ccx.tcx, e); + let declty = ty::mk_int(fcx.ccx.tcx); + demand::simple(fcx, e.span, declty, cty); + // FIXME: issue #1417 + // Also, check_expr (from check_const pass) doesn't guarantee that + // the expression in an form that eval_const_expr can handle, so + // we may still get an internal compiler error + alt syntax::ast_util::eval_const_expr(e) { + syntax::ast_util::const_int(val) { + disr_val = val as int; + } + _ { + ccx.tcx.sess.span_err(e.span, + "expected signed integer constant"); + } + } + } + _ {} + } + if vec::contains(disr_vals, disr_val) { + ccx.tcx.sess.span_err(v.span, + "discriminator value already exists."); + } + disr_vals += [disr_val]; + disr_val += 1; + } + let outer = true, did = local_def(id); + if ty::type_structurally_contains(ccx.tcx, rty, {|sty| + alt sty { + ty::ty_enum(id, _) if id == did { + if outer { outer = false; false } + else { true } + } + _ { false } + } + }) { + ccx.tcx.sess.span_fatal(sp, "illegal recursive enum type. \ + wrap the inner value in a box to \ + make it represenable"); + } +} + +// A generic function for checking the pred in a check +// or if-check +fn check_pred_expr(fcx: @fn_ctxt, e: @ast::expr) -> bool { + let bot = check_expr_with(fcx, e, ty::mk_bool(fcx.ccx.tcx)); + + /* e must be a call expr where all arguments are either + literals or slots */ + alt e.node { + ast::expr_call(operator, operands, _) { + if !ty::is_pred_ty(expr_ty(fcx.ccx.tcx, operator)) { + fcx.ccx.tcx.sess.span_err + (operator.span, + "operator in constraint has non-boolean return type"); + } + + alt operator.node { + ast::expr_path(oper_name) { + alt fcx.ccx.tcx.def_map.find(operator.id) { + some(ast::def_fn(_, ast::pure_fn)) { + // do nothing + } + _ { + fcx.ccx.tcx.sess.span_err(operator.span, + "Impure function as operator \ + in constraint"); + } + } + for operand: @ast::expr in operands { + if !ast_util::is_constraint_arg(operand) { + let s = + "Constraint args must be slot variables or literals"; + fcx.ccx.tcx.sess.span_err(e.span, s); + } + } + } + _ { + let s = "In a constraint, expected the \ + constraint name to be an explicit name"; + fcx.ccx.tcx.sess.span_err(e.span, s); + } + } + } + _ { fcx.ccx.tcx.sess.span_err(e.span, "check on non-predicate"); } + } + ret bot; +} + +fn check_constraints(fcx: @fn_ctxt, cs: [@ast::constr], args: [ast::arg]) { + let c_args; + let num_args = vec::len(args); + for c: @ast::constr in cs { + c_args = []; + for a: @spanned<ast::fn_constr_arg> in c.node.args { + c_args += [ + // "base" should not occur in a fn type thing, as of + // yet, b/c we don't allow constraints on the return type + + // Works b/c no higher-order polymorphism + /* + This is kludgy, and we probably shouldn't be assigning + node IDs here, but we're creating exprs that are + ephemeral, just for the purposes of typechecking. So + that's my justification. + */ + @alt a.node { + ast::carg_base { + fcx.ccx.tcx.sess.span_bug(a.span, + "check_constraints:\ + unexpected carg_base"); + } + ast::carg_lit(l) { + let tmp_node_id = fcx.ccx.tcx.sess.next_node_id(); + {id: tmp_node_id, node: ast::expr_lit(l), span: a.span} + } + ast::carg_ident(i) { + if i < num_args { + let p: ast::path_ = + {global: false, + idents: [args[i].ident], + types: []}; + let arg_occ_node_id = + fcx.ccx.tcx.sess.next_node_id(); + fcx.ccx.tcx.def_map.insert + (arg_occ_node_id, + ast::def_arg(args[i].id, args[i].mode)); + {id: arg_occ_node_id, + node: ast::expr_path(@respan(a.span, p)), + span: a.span} + } else { + fcx.ccx.tcx.sess.span_bug(a.span, + "check_constraints:\ + carg_ident index out of bounds"); + } + } + }]; + } + let p_op: ast::expr_ = ast::expr_path(c.node.path); + let oper: @ast::expr = @{id: c.node.id, node: p_op, span: c.span}; + // Another ephemeral expr + let call_expr_id = fcx.ccx.tcx.sess.next_node_id(); + let call_expr = + @{id: call_expr_id, + node: ast::expr_call(oper, c_args, false), + span: c.span}; + check_pred_expr(fcx, call_expr); + } +} + +fn check_fn(ccx: @crate_ctxt, + proto: ast::proto, + decl: ast::fn_decl, + body: ast::blk, + id: ast::node_id, + old_fcx: option<@fn_ctxt>) { + // If old_fcx is some(...), this is a block fn { |x| ... }. + // In that case, the purity is inherited from the context. + let purity = alt old_fcx { + none { decl.purity } + some(f) { assert decl.purity == ast::impure_fn; f.purity } + }; + + let gather_result = gather_locals(ccx, decl, body, id, old_fcx); + let fcx: @fn_ctxt = + @{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)), + purity: purity, + proto: proto, + var_bindings: gather_result.var_bindings, + locals: gather_result.locals, + next_var_id: gather_result.next_var_id, + ccx: ccx}; + + check_constraints(fcx, decl.constraints, decl.inputs); + check_block(fcx, body); + + // We unify the tail expr's type with the + // function result type, if there is a tail expr. + alt body.node.expr { + some(tail_expr) { + let tail_expr_ty = expr_ty(ccx.tcx, tail_expr); + demand::simple(fcx, tail_expr.span, fcx.ret_ty, tail_expr_ty); + } + none { } + } + + let args = ty::ty_fn_args(ty::node_id_to_type(ccx.tcx, id)); + let i = 0u; + for arg: ty::arg in args { + write_ty(ccx.tcx, decl.inputs[i].id, arg.ty); + i += 1u; + } + + // If we don't have any enclosing function scope, it is time to + // force any remaining type vars to be resolved. + // If we have an enclosing function scope, our type variables will be + // resolved when the enclosing scope finishes up. + if option::is_none(old_fcx) { + dict::resolve_in_block(fcx, body); + writeback::resolve_type_vars_in_block(fcx, body); + } +} + +fn check_method(ccx: @crate_ctxt, method: @ast::method) { + check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none); +} + +fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map { + let rslt = new_int_hash::<ty::t>(); + for m in members { + alt m.node.decl { + ast::instance_var(_,t,_,id) { + rslt.insert(id, ast_ty_to_ty(ccx.tcx, m_collect, t)); + } + ast::class_method(it) { + rslt.insert(it.id, ty_of_item(ccx.tcx, m_collect, it).ty); + } + } + } + rslt +} + +fn check_class_member(ccx: @crate_ctxt, cm: ast::class_member) { + alt cm { + ast::instance_var(_,t,_,_) { // ??? Not sure + } + // not right yet -- need a scope + ast::class_method(i) { check_item(ccx, i); } + } +} + +fn check_item(ccx: @crate_ctxt, it: @ast::item) { + alt it.node { + ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); } + ast::item_enum(vs, _) { check_enum_variants(ccx, it.span, vs, it.id); } + ast::item_fn(decl, tps, body) { + check_fn(ccx, ast::proto_bare, decl, body, it.id, none); + } + ast::item_res(decl, tps, body, dtor_id, _) { + check_fn(ccx, ast::proto_bare, decl, body, dtor_id, none); + } + ast::item_impl(tps, _, ty, ms) { + ccx.self_infos += [self_impl(ast_ty_to_ty(ccx.tcx, m_check, ty))]; + for m in ms { check_method(ccx, m); } + vec::pop(ccx.self_infos); + } + ast::item_class(tps, members, ctor_id, ctor_decl, ctor_body) { + let cid = some(it.id); + let members_info = class_types(ccx, members); + let class_ccx = @{enclosing_class_id:cid, + enclosing_class:members_info with *ccx}; + // typecheck the ctor + check_fn(class_ccx, ast::proto_bare, ctor_decl, ctor_body, ctor_id, + none); + // typecheck the members + for m in members { check_class_member(class_ccx, m.node.decl); } + } + _ {/* nothing to do */ } + } +} + +fn arg_is_argv_ty(_tcx: ty::ctxt, a: ty::arg) -> bool { + alt ty::get(a.ty).struct { + ty::ty_vec(mt) { + if mt.mutbl != ast::m_imm { ret false; } + alt ty::get(mt.ty).struct { + ty::ty_str { ret true; } + _ { ret false; } + } + } + _ { ret false; } + } +} + +fn check_main_fn_ty(tcx: ty::ctxt, main_id: ast::node_id, main_span: span) { + let main_t = ty::node_id_to_type(tcx, main_id); + alt ty::get(main_t).struct { + ty::ty_fn({proto: ast::proto_bare, inputs, output, + ret_style: ast::return_val, constraints}) { + alt tcx.items.find(main_id) { + some(ast_map::node_item(it,_)) { + alt it.node { + ast::item_fn(_,ps,_) if vec::is_not_empty(ps) { + tcx.sess.span_err(main_span, + "main function is not allowed to have type parameters"); + ret; + } + _ {} + } + } + _ {} + } + let ok = vec::len(constraints) == 0u; + ok &= ty::type_is_nil(output); + let num_args = vec::len(inputs); + ok &= num_args == 0u || num_args == 1u && + arg_is_argv_ty(tcx, inputs[0]); + if !ok { + tcx.sess.span_err(main_span, + "wrong type in main function: found `" + + ty_to_str(tcx, main_t) + "`"); + } + } + _ { + tcx.sess.span_bug(main_span, + "main has a non-function type: found `" + + ty_to_str(tcx, main_t) + "`"); + } + } +} + +fn check_for_main_fn(tcx: ty::ctxt, crate: @ast::crate) { + if !tcx.sess.building_library { + alt tcx.sess.main_fn { + some((id, sp)) { check_main_fn_ty(tcx, id, sp); } + none { tcx.sess.span_err(crate.span, "main function not found"); } + } + } +} + +mod dict { + fn has_iface_bounds(tps: [ty::param_bounds]) -> bool { + vec::any(tps, {|bs| + vec::any(*bs, {|b| + alt b { ty::bound_iface(_) { true } _ { false } } + }) + }) + } + + fn lookup_dicts(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + bounds: @[ty::param_bounds], tys: [ty::t]) + -> dict_res { + let tcx = fcx.ccx.tcx, result = [], i = 0u; + for ty in tys { + for bound in *bounds[i] { + alt bound { + ty::bound_iface(i_ty) { + let i_ty = ty::substitute_type_params(tcx, tys, i_ty); + result += [lookup_dict(fcx, isc, sp, ty, i_ty)]; + } + _ {} + } + } + i += 1u; + } + @result + } + + fn lookup_dict(fcx: @fn_ctxt, isc: resolve::iscopes, sp: span, + ty: ty::t, iface_ty: ty::t) -> dict_origin { + let tcx = fcx.ccx.tcx; + let (iface_id, iface_tps) = alt check ty::get(iface_ty).struct { + ty::ty_iface(did, tps) { (did, tps) } + }; + let ty = fixup_ty(fcx, sp, ty); + alt ty::get(ty).struct { + ty::ty_param(n, did) { + let n_bound = 0u; + for bound in *tcx.ty_param_bounds.get(did.node) { + alt bound { + ty::bound_iface(ity) { + alt check ty::get(ity).struct { + ty::ty_iface(idid, _) { + if iface_id == idid { ret dict_param(n, n_bound); } + } + } + n_bound += 1u; + } + _ {} + } + } + } + ty::ty_iface(did, _) if iface_id == did { + ret dict_iface(did); + } + _ { + let found = none; + std::list::iter(isc) {|impls| + if option::is_some(found) { ret; } + for im in *impls { + let match = alt ty::impl_iface(tcx, im.did) { + some(ity) { + alt check ty::get(ity).struct { + ty::ty_iface(id, _) { id == iface_id } + } + } + _ { false } + }; + if match { + let {n_tps, ty: self_ty} = impl_self_ty(tcx, im.did); + let {vars, ty: self_ty} = if n_tps > 0u { + bind_params(fcx, self_ty, n_tps) + } else { {vars: [], ty: self_ty} }; + let im_bs = ty::lookup_item_type(tcx, im.did).bounds; + alt unify::unify(fcx, ty, self_ty) { + ures_ok(_) { + if option::is_some(found) { + tcx.sess.span_err( + sp, "multiple applicable implementations \ + in scope"); + } else { + connect_iface_tps(fcx, sp, vars, iface_tps, + im.did); + let params = vec::map(vars, {|t| + fixup_ty(fcx, sp, t)}); + let subres = lookup_dicts(fcx, isc, sp, im_bs, + params); + found = some(dict_static(im.did, params, + subres)); + } + } + _ {} + } + } + } + } + alt found { + some(rslt) { ret rslt; } + _ {} + } + } + } + + tcx.sess.span_fatal( + sp, "failed to find an implementation of interface " + + ty_to_str(tcx, iface_ty) + " for " + + ty_to_str(tcx, ty)); + } + + fn fixup_ty(fcx: @fn_ctxt, sp: span, ty: ty::t) -> ty::t { + let tcx = fcx.ccx.tcx; + alt ty::unify::fixup_vars(tcx, some(sp), fcx.var_bindings, ty) { + fix_ok(new_type) { new_type } + fix_err(vid) { + tcx.sess.span_fatal(sp, "could not determine a type for a \ + bounded type parameter"); + } + } + } + + fn connect_iface_tps(fcx: @fn_ctxt, sp: span, impl_tys: [ty::t], + iface_tys: [ty::t], impl_did: ast::def_id) { + let tcx = fcx.ccx.tcx; + let ity = option::get(ty::impl_iface(tcx, impl_did)); + let iface_ty = ty::substitute_type_params(tcx, impl_tys, ity); + alt check ty::get(iface_ty).struct { + ty::ty_iface(_, tps) { + vec::iter2(tps, iface_tys, + {|a, b| demand::simple(fcx, sp, a, b);}); + } + } + } + + fn resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, v: visit::vt<@fn_ctxt>) { + let cx = fcx.ccx; + alt ex.node { + ast::expr_path(_) { + alt cx.tcx.node_type_substs.find(ex.id) { + some(ts) { + let did = ast_util::def_id_of_def(cx.tcx.def_map.get(ex.id)); + let item_ty = ty::lookup_item_type(cx.tcx, did); + if has_iface_bounds(*item_ty.bounds) { + let impls = cx.impl_map.get(ex.id); + cx.dict_map.insert(ex.id, lookup_dicts( + fcx, impls, ex.span, item_ty.bounds, ts)); + } + } + _ {} + } + } + // Must resolve bounds on methods with bounded params + ast::expr_field(_, _, _) | ast::expr_binary(_, _, _) | + ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) | + ast::expr_index(_, _) { + alt cx.method_map.find(ex.id) { + some(method_static(did)) { + let bounds = ty::lookup_item_type(cx.tcx, did).bounds; + if has_iface_bounds(*bounds) { + let callee_id = alt ex.node { + ast::expr_field(_, _, _) { ex.id } + _ { ast_util::op_expr_callee_id(ex) } + }; + let ts = ty::node_id_to_type_params(cx.tcx, callee_id); + let iscs = cx.impl_map.get(ex.id); + cx.dict_map.insert(callee_id, lookup_dicts( + fcx, iscs, ex.span, bounds, ts)); + } + } + _ {} + } + } + ast::expr_cast(src, _) { + let target_ty = expr_ty(cx.tcx, ex); + alt ty::get(target_ty).struct { + ty::ty_iface(_, _) { + let impls = cx.impl_map.get(ex.id); + let dict = lookup_dict(fcx, impls, ex.span, + expr_ty(cx.tcx, src), target_ty); + cx.dict_map.insert(ex.id, @[dict]); + } + _ {} + } + } + ast::expr_fn(p, _, _, _) if ast::is_blockish(p) {} + ast::expr_fn(_, _, _, _) { ret; } + _ {} + } + visit::visit_expr(ex, fcx, v); + } + + // Detect points where an interface-bounded type parameter is + // instantiated, resolve the impls for the parameters. + fn resolve_in_block(fcx: @fn_ctxt, bl: ast::blk) { + visit::visit_block(bl, fcx, visit::mk_vt(@{ + visit_expr: resolve_expr, + visit_item: fn@(_i: @ast::item, &&_e: @fn_ctxt, + _v: visit::vt<@fn_ctxt>) {} + with *visit::default_visitor() + })); + } +} + +fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map, + crate: @ast::crate) -> (method_map, dict_map) { + collect::collect_item_types(tcx, crate); + + let ccx = @{mutable self_infos: [], + impl_map: impl_map, + method_map: std::map::new_int_hash(), + dict_map: std::map::new_int_hash(), + enclosing_class_id: none, + enclosing_class: std::map::new_int_hash(), + tcx: tcx}; + let visit = visit::mk_simple_visitor(@{ + visit_item: bind check_item(ccx, _) + with *visit::default_simple_visitor() + }); + visit::visit_crate(*crate, (), visit); + check_for_main_fn(tcx, crate); + tcx.sess.abort_if_errors(); + (ccx.method_map, ccx.dict_map) +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc new file mode 100644 index 00000000000..8102e4d8cd8 --- /dev/null +++ b/src/rustc/rustc.rc @@ -0,0 +1,155 @@ +// -*- rust -*- + +#[link(name = "rustc", + vers = "0.1", + uuid = "0ce89b41-2f92-459e-bbc1-8f5fe32f16cf", + url = "http://rust-lang.org/src/rustc")]; + +#[comment = "The Rust compiler"]; +#[license = "MIT"]; +#[crate_type = "lib"]; + +use std (name = "std", + vers = "0.1", + url = "http://rust-lang.org/src/std"); + +mod middle { + mod trans { + mod common; + mod type_of; + mod build; + mod base; + mod alt; + mod uniq; + mod closure; + mod tvec; + mod impl; + mod native; + mod shape; + mod debuginfo; + } + mod inline; + mod ty; + mod ast_map; + mod resolve; + mod typeck; + mod fn_usage; + mod check_alt; + mod check_const; + mod lint; + mod mutbl; + mod alias; + mod last_use; + mod block_use; + mod kind; + mod freevars; + mod capture; + mod pat_util; + + mod tstate { + mod ck; + mod annotate; + #[path = "auxiliary.rs"] + mod aux; + mod bitvectors; + mod collect_locals; + mod pre_post_conditions; + mod states; + mod ann; + mod tritv; + } +} + + +mod syntax { + mod ast; + mod ast_util; + + mod fold; + mod visit; + mod codemap; + mod parse { + mod lexer; + mod parser; + mod token; + mod eval; + } + mod ext { + mod base; + mod expand; + mod qquote; + mod build; + + mod fmt; + mod env; + mod simplext; + mod concat_idents; + mod ident_to_str; + mod log_syntax; + } + mod print { + mod pprust; + mod pp; + } + mod util { + mod interner; + } +} + +mod front { + mod attr; + mod config; + mod test; + mod core_inject; +} + +mod back { + mod link; + mod abi; + mod upcall; + mod x86; + mod x86_64; + mod rpath; + mod target_strs; +} + +mod metadata { + export encoder; + export creader; + export cstore; + export csearch; + + mod common; + mod tyencode; + mod tydecode; + mod astencode; + mod astencode_gen; + mod encoder; + mod decoder; + mod creader; + mod cstore; + mod csearch; +} + +mod driver { + mod driver; + mod session; + mod diagnostic; +} + +mod util { + mod common; + mod ppaux; + mod filesearch; +} + +mod lib { + mod llvm; +} + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/syntax/ast.rs b/src/rustc/syntax/ast.rs new file mode 100644 index 00000000000..eea47191eef --- /dev/null +++ b/src/rustc/syntax/ast.rs @@ -0,0 +1,553 @@ +// The Rust abstract syntax tree. + +import codemap::{span, filename}; + +type spanned<T> = {node: T, span: span}; + +type ident = str; + +// Functions may or may not have names. +type fn_ident = option<ident>; + +// FIXME: with typestate constraint, could say +// idents and types are the same length, and are +// non-empty +type path_ = {global: bool, idents: [ident], types: [@ty]}; + +type path = spanned<path_>; + +type crate_num = int; +type node_id = int; +type def_id = {crate: crate_num, node: node_id}; + +const local_crate: crate_num = 0; +const crate_node_id: node_id = 0; + +enum ty_param_bound { + bound_copy, + bound_send, + bound_iface(@ty), +} + +type ty_param = {ident: ident, id: node_id, bounds: @[ty_param_bound]}; + +enum def { + def_fn(def_id, purity), + def_self(node_id), + def_mod(def_id), + def_native_mod(def_id), + def_const(def_id), + def_arg(node_id, mode), + def_local(node_id, bool /* is_mutbl */), + def_variant(def_id /* enum */, def_id /* variant */), + def_ty(def_id), + def_prim_ty(prim_ty), + def_ty_param(def_id, uint), + def_binding(node_id), + def_use(def_id), + def_upvar(node_id /* local id of closed over var */, + @def /* closed over def */, + node_id /* expr node that creates the closure */), + def_class(def_id), + // first def_id is for parent class + def_class_field(def_id, def_id), + // No purity allowed for now, I guess + // (simpler this way, b/c presumably methods read mutable state) + def_class_method(def_id, def_id) +} + +// The set of meta_items that define the compilation environment of the crate, +// used to drive conditional compilation +type crate_cfg = [@meta_item]; + +type crate = spanned<crate_>; + +type crate_ = + {directives: [@crate_directive], + module: _mod, + attrs: [attribute], + config: crate_cfg}; + +enum crate_directive_ { + cdir_src_mod(ident, [attribute]), + cdir_dir_mod(ident, [@crate_directive], [attribute]), + + // NB: cdir_view_item is *not* processed by the rest of the compiler, the + // attached view_items are sunk into the crate's module during parsing, + // and processed (resolved, imported, etc.) there. This enum-variant + // exists only to preserve the view items in order in case we decide to + // pretty-print crates in the future. + cdir_view_item(@view_item), + + cdir_syntax(@path), +} + +type crate_directive = spanned<crate_directive_>; + +type meta_item = spanned<meta_item_>; + +enum meta_item_ { + meta_word(ident), + meta_list(ident, [@meta_item]), + meta_name_value(ident, lit), +} + +type blk = spanned<blk_>; + +type blk_ = {view_items: [@view_item], stmts: [@stmt], expr: option<@expr>, + id: node_id, rules: blk_check_mode}; + +type pat = {id: node_id, node: pat_, span: span}; + +type field_pat = {ident: ident, pat: @pat}; + +enum pat_ { + pat_wild, + // A pat_ident may either be a new bound variable, + // or a nullary enum (in which case the second field + // is none). + // In the nullary enum case, the parser can't determine + // which it is. The resolver determines this, and + // records this pattern's node_id in an auxiliary + // set (of "pat_idents that refer to nullary enums") + pat_ident(@path, option<@pat>), + pat_enum(@path, [@pat]), + pat_rec([field_pat], bool), + pat_tup([@pat]), + pat_box(@pat), + pat_uniq(@pat), + pat_lit(@expr), + pat_range(@expr, @expr), +} + +enum mutability { m_mutbl, m_imm, m_const, } + +enum proto { + proto_bare, // native fn + proto_any, // fn + proto_uniq, // fn~ + proto_box, // fn@ + proto_block, // fn& +} + +pure fn is_blockish(p: ast::proto) -> bool { + alt p { + proto_any | proto_block { true } + proto_bare | proto_uniq | proto_box { false } + } +} + +enum binop { + add, + subtract, + mul, + div, + rem, + and, + or, + bitxor, + bitand, + bitor, + lsl, + lsr, + asr, + eq, + lt, + le, + ne, + ge, + gt, +} + +enum unop { + box(mutability), + uniq(mutability), + deref, not, neg, +} + +// Generally, after typeck you can get the inferred value +// using ty::resolved_T(...). +enum inferable<T> { + expl(T), infer(node_id) +} + +// "resolved" mode: the real modes. +enum rmode { by_ref, by_val, by_mutbl_ref, by_move, by_copy } + +// inferable mode. +type mode = inferable<rmode>; + +type stmt = spanned<stmt_>; + +enum stmt_ { + stmt_decl(@decl, node_id), + + // expr without trailing semi-colon (must have unit type): + stmt_expr(@expr, node_id), + + // expr with trailing semi-colon (may have any type): + stmt_semi(@expr, node_id), +} + +enum init_op { init_assign, init_move, } + +type initializer = {op: init_op, expr: @expr}; + +type local_ = // FIXME: should really be a refinement on pat + {is_mutbl: bool, ty: @ty, pat: @pat, + init: option<initializer>, id: node_id}; + +type local = spanned<local_>; + +type decl = spanned<decl_>; + +enum decl_ { decl_local([@local]), decl_item(@item), } + +type arm = {pats: [@pat], guard: option<@expr>, body: blk}; + +type field_ = {mutbl: mutability, ident: ident, expr: @expr}; + +type field = spanned<field_>; + +enum blk_check_mode { default_blk, unchecked_blk, unsafe_blk, } + +enum expr_check_mode { claimed_expr, checked_expr, } + +type expr = {id: node_id, node: expr_, span: span}; + +enum alt_mode { alt_check, alt_exhaustive, } + +enum expr_ { + expr_vec([@expr], mutability), + expr_rec([field], option<@expr>), + expr_call(@expr, [@expr], bool), + expr_tup([@expr]), + expr_bind(@expr, [option<@expr>]), + expr_binary(binop, @expr, @expr), + expr_unary(unop, @expr), + expr_lit(@lit), + expr_cast(@expr, @ty), + expr_if(@expr, blk, option<@expr>), + expr_while(@expr, blk), + expr_for(@local, @expr, blk), + expr_do_while(blk, @expr), + expr_alt(@expr, [arm], alt_mode), + expr_fn(proto, fn_decl, blk, @capture_clause), + expr_fn_block(fn_decl, blk), + expr_block(blk), + + /* + * FIXME: many of these @exprs should be constrained with + * is_lval once we have constrained types working. + */ + expr_copy(@expr), + expr_move(@expr, @expr), + expr_assign(@expr, @expr), + expr_swap(@expr, @expr), + expr_assign_op(binop, @expr, @expr), + expr_field(@expr, ident, [@ty]), + expr_index(@expr, @expr), + expr_path(@path), + expr_fail(option<@expr>), + expr_break, + expr_cont, + expr_ret(option<@expr>), + expr_be(@expr), + expr_log(int, @expr, @expr), + + /* just an assert, no significance to typestate */ + expr_assert(@expr), + + /* preds that typestate is aware of */ + expr_check(expr_check_mode, @expr), + + /* FIXME Would be nice if expr_check desugared + to expr_if_check. */ + expr_if_check(@expr, blk, option<@expr>), + expr_mac(mac), +} + +type capture_item = { + id: int, + name: ident, // Currently, can only capture a local var. + span: span +}; +type capture_clause = { + copies: [@capture_item], + moves: [@capture_item] +}; + +/* +// Says whether this is a block the user marked as +// "unchecked" +enum blk_sort { + blk_unchecked, // declared as "exception to effect-checking rules" + blk_checked, // all typing rules apply +} +*/ + +type mac = spanned<mac_>; + +type mac_arg = option::t<@expr>; + +type mac_body_ = {span: span}; +type mac_body = option::t<mac_body_>; + +enum mac_ { + mac_invoc(@path, mac_arg, mac_body), + mac_embed_type(@ty), + mac_embed_block(blk), + mac_ellipsis, + // the span is used by the quoter/anti-quoter ... + mac_aq(span /* span of quote */, @expr), // anti-quote + mac_var(uint) +} + +type lit = spanned<lit_>; + +enum lit_ { + lit_str(str), + lit_int(i64, int_ty), + lit_uint(u64, uint_ty), + lit_float(str, float_ty), + lit_nil, + lit_bool(bool), +} + +// NB: If you change this, you'll probably want to change the corresponding +// type structure in middle/ty.rs as well. +type mt = {ty: @ty, mutbl: mutability}; + +type ty_field_ = {ident: ident, mt: mt}; + +type ty_field = spanned<ty_field_>; + +type ty_method = {ident: ident, attrs: [attribute], + decl: fn_decl, tps: [ty_param], span: span}; + +enum int_ty { ty_i, ty_char, ty_i8, ty_i16, ty_i32, ty_i64, } + +enum uint_ty { ty_u, ty_u8, ty_u16, ty_u32, ty_u64, } + +enum float_ty { ty_f, ty_f32, ty_f64, } + +type ty = spanned<ty_>; + +// Not represented directly in the AST, referred to by name through a ty_path. +enum prim_ty { + ty_int(int_ty), + ty_uint(uint_ty), + ty_float(float_ty), + ty_str, + ty_bool, +} + +enum ty_ { + ty_nil, + ty_bot, /* bottom type */ + ty_box(mt), + ty_uniq(mt), + ty_vec(mt), + ty_ptr(mt), + ty_rec([ty_field]), + ty_fn(proto, fn_decl), + ty_tup([@ty]), + ty_path(@path, node_id), + ty_constr(@ty, [@ty_constr]), + ty_mac(mac), + // ty_infer means the type should be inferred instead of it having been + // specified. This should only appear at the "top level" of a type and not + // nested in one. + ty_infer, +} + + +/* +A constraint arg that's a function argument is referred to by its position +rather than name. This is so we could have higher-order functions that have +constraints (potentially -- right now there's no way to write that), and also +so that the typestate pass doesn't have to map a function name onto its decl. +So, the constr_arg type is parameterized: it's instantiated with uint for +declarations, and ident for uses. +*/ +enum constr_arg_general_<T> { carg_base, carg_ident(T), carg_lit(@lit), } + +type fn_constr_arg = constr_arg_general_<uint>; +type sp_constr_arg<T> = spanned<constr_arg_general_<T>>; +type ty_constr_arg = sp_constr_arg<@path>; +type constr_arg = spanned<fn_constr_arg>; + +// Constrained types' args are parameterized by paths, since +// we refer to paths directly and not by indices. +// The implicit root of such path, in the constraint-list for a +// constrained type, is * (referring to the base record) + +type constr_general_<ARG, ID> = + {path: @path, args: [@spanned<constr_arg_general_<ARG>>], id: ID}; + +// In the front end, constraints have a node ID attached. +// Typeck turns this to a def_id, using the output of resolve. +type constr_general<ARG> = spanned<constr_general_<ARG, node_id>>; +type constr_ = constr_general_<uint, node_id>; +type constr = spanned<constr_general_<uint, node_id>>; +type ty_constr_ = constr_general_<@path, node_id>; +type ty_constr = spanned<ty_constr_>; + +/* The parser generates ast::constrs; resolve generates + a mapping from each function to a list of ty::constr_defs, + corresponding to these. */ +type arg = {mode: mode, ty: @ty, ident: ident, id: node_id}; + +type fn_decl = + {inputs: [arg], + output: @ty, + purity: purity, + cf: ret_style, + constraints: [@constr]}; + +enum purity { + pure_fn, // declared with "pure fn" + unsafe_fn, // declared with "unsafe fn" + impure_fn, // declared with "fn" + crust_fn, // declared with "crust fn" +} + +enum ret_style { + noreturn, // functions with return type _|_ that always + // raise an error or exit (i.e. never return to the caller) + return_val, // everything else +} + +type method = {ident: ident, attrs: [attribute], + tps: [ty_param], decl: fn_decl, body: blk, + id: node_id, span: span}; + +type _mod = {view_items: [@view_item], items: [@item]}; + +enum native_abi { + native_abi_rust_intrinsic, + native_abi_cdecl, + native_abi_stdcall, +} + +type native_mod = + {view_items: [@view_item], + items: [@native_item]}; + +type variant_arg = {ty: @ty, id: node_id}; + +type variant_ = {name: ident, attrs: [attribute], args: [variant_arg], + id: node_id, disr_expr: option<@expr>}; + +type variant = spanned<variant_>; + + +// FIXME: May want to just use path here, which would allow things like +// 'import ::foo' +type simple_path = [ident]; + +type path_list_ident_ = {name: ident, id: node_id}; +type path_list_ident = spanned<path_list_ident_>; + +type view_path = spanned<view_path_>; +enum view_path_ { + + // quux = foo::bar::baz + // + // or just + // + // foo::bar::baz (with 'baz =' implicitly on the left) + view_path_simple(ident, @simple_path, node_id), + + // foo::bar::* + view_path_glob(@simple_path, node_id), + + // foo::bar::{a,b,c} + view_path_list(@simple_path, [path_list_ident], node_id) +} + +type view_item = spanned<view_item_>; +enum view_item_ { + view_item_use(ident, [@meta_item], node_id), + view_item_import([@view_path]), + view_item_export([@view_path]) +} + +// Meta-data associated with an item +type attribute = spanned<attribute_>; + + +// Distinguishes between attributes that decorate items and attributes that +// are contained as statements within items. These two cases need to be +// distinguished for pretty-printing. +enum attr_style { attr_outer, attr_inner, } + +type attribute_ = {style: attr_style, value: meta_item}; + +type item = {ident: ident, attrs: [attribute], + id: node_id, node: item_, span: span}; + +enum item_ { + item_const(@ty, @expr), + item_fn(fn_decl, [ty_param], blk), + item_mod(_mod), + item_native_mod(native_mod), + item_ty(@ty, [ty_param]), + item_enum([variant], [ty_param]), + item_res(fn_decl /* dtor */, [ty_param], blk, + node_id /* dtor id */, node_id /* ctor id */), + item_class([ty_param], /* ty params for class */ + [@class_item], /* methods, etc. */ + /* (not including ctor) */ + node_id, /* ctor id */ + fn_decl, /* ctor decl */ + blk /* ctor body */ + ), + item_iface([ty_param], [ty_method]), + item_impl([ty_param], option<@ty> /* iface */, + @ty /* self */, [@method]), +} + +type class_item_ = {privacy: privacy, decl: class_member}; +type class_item = spanned<class_item_>; + +enum class_member { + instance_var(ident, @ty, class_mutability, node_id), + class_method(@item) // FIXME: methods aren't allowed to be + // type-parametric. + // without constrained types, have to duplicate some stuff. or factor out + // item to separate out things with type params? +} + +enum class_mutability { class_mutable, class_immutable } + +enum privacy { priv, pub } + +type native_item = + {ident: ident, + attrs: [attribute], + node: native_item_, + id: node_id, + span: span}; + +enum native_item_ { + native_item_fn(fn_decl, [ty_param]), +} + +// The data we save and restore about an inlined item or method. This is not +// part of the AST that we parse from a file, but it becomes part of the tree +// that we trans. +enum inlined_item { + ii_item(@item), + ii_method(def_id /* impl id */, @method) +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/ast_util.rs b/src/rustc/syntax/ast_util.rs new file mode 100644 index 00000000000..04bd4236c9b --- /dev/null +++ b/src/rustc/syntax/ast_util.rs @@ -0,0 +1,443 @@ +import codemap::span; +import ast::*; + +fn respan<T: copy>(sp: span, t: T) -> spanned<T> { + ret {node: t, span: sp}; +} + +/* assuming that we're not in macro expansion */ +fn mk_sp(lo: uint, hi: uint) -> span { + ret {lo: lo, hi: hi, expn_info: none}; +} + +// make this a const, once the compiler supports it +fn dummy_sp() -> span { ret mk_sp(0u, 0u); } + +fn path_name(p: @path) -> str { path_name_i(p.node.idents) } + +fn path_name_i(idents: [ident]) -> str { str::connect(idents, "::") } + +fn local_def(id: node_id) -> def_id { ret {crate: local_crate, node: id}; } + +fn stmt_id(s: stmt) -> node_id { + alt s.node { + stmt_decl(_, id) { id } + stmt_expr(_, id) { id } + stmt_semi(_, id) { id } + } +} + +fn variant_def_ids(d: def) -> {enm: def_id, var: def_id} { + alt d { def_variant(enum_id, var_id) { + ret {enm: enum_id, var: var_id}; } + _ { fail "non-variant in variant_def_ids"; } } +} + +fn def_id_of_def(d: def) -> def_id { + alt d { + def_fn(id, _) | def_mod(id) | + def_native_mod(id) | def_const(id) | + def_variant(_, id) | def_ty(id) | def_ty_param(id, _) | + def_use(id) | + def_class(id) | def_class_field(_, id) | def_class_method(_, id) { id } + + def_self(id) | def_arg(id, _) | def_local(id, _) | + def_upvar(id, _, _) | def_binding(id) { + local_def(id) + } + + def_prim_ty(_) { fail; } + } +} + +fn binop_to_str(op: binop) -> str { + alt op { + add { ret "+"; } + subtract { ret "-"; } + mul { ret "*"; } + div { ret "/"; } + rem { ret "%"; } + and { ret "&&"; } + or { ret "||"; } + bitxor { ret "^"; } + bitand { ret "&"; } + bitor { ret "|"; } + lsl { ret "<<"; } + lsr { ret ">>"; } + asr { ret ">>>"; } + eq { ret "=="; } + lt { ret "<"; } + le { ret "<="; } + ne { ret "!="; } + ge { ret ">="; } + gt { ret ">"; } + } +} + +pure fn lazy_binop(b: binop) -> bool { + alt b { and { true } or { true } _ { false } } +} + +pure fn is_shift_binop(b: binop) -> bool { + alt b { + lsl { true } + lsr { true } + asr { true } + _ { false } + } +} + +fn unop_to_str(op: unop) -> str { + alt op { + box(mt) { if mt == m_mutbl { ret "@mut "; } ret "@"; } + uniq(mt) { if mt == m_mutbl { ret "~mut "; } ret "~"; } + deref { ret "*"; } + not { ret "!"; } + neg { ret "-"; } + } +} + +fn is_path(e: @expr) -> bool { + ret alt e.node { expr_path(_) { true } _ { false } }; +} + +fn int_ty_to_str(t: int_ty) -> str { + alt t { + ty_char { "u8" } // ??? + ty_i { "" } ty_i8 { "i8" } ty_i16 { "i16" } + ty_i32 { "i32" } ty_i64 { "i64" } + } +} + +fn int_ty_max(t: int_ty) -> u64 { + alt t { + ty_i8 { 0x80u64 } + ty_i16 { 0x800u64 } + ty_i | ty_char | ty_i32 { 0x80000000u64 } // actually ni about ty_i + ty_i64 { 0x8000000000000000u64 } + } +} + +fn uint_ty_to_str(t: uint_ty) -> str { + alt t { + ty_u { "u" } ty_u8 { "u8" } ty_u16 { "u16" } + ty_u32 { "u32" } ty_u64 { "u64" } + } +} + +fn uint_ty_max(t: uint_ty) -> u64 { + alt t { + ty_u8 { 0xffu64 } + ty_u16 { 0xffffu64 } + ty_u | ty_u32 { 0xffffffffu64 } // actually ni about ty_u + ty_u64 { 0xffffffffffffffffu64 } + } +} + +fn float_ty_to_str(t: float_ty) -> str { + alt t { ty_f { "" } ty_f32 { "f32" } ty_f64 { "f64" } } +} + +fn is_exported(i: ident, m: _mod) -> bool { + let local = false; + let parent_enum : option<ident> = none; + for it: @item in m.items { + if it.ident == i { local = true; } + alt it.node { + item_enum(variants, _) { + for v: variant in variants { + if v.node.name == i { + local = true; + parent_enum = some(it.ident); + } + } + } + _ { } + } + if local { break; } + } + let has_explicit_exports = false; + for vi: @view_item in m.view_items { + alt vi.node { + view_item_export(vps) { + has_explicit_exports = true; + for vp in vps { + alt vp.node { + ast::view_path_simple(id, _, _) { + if id == i { ret true; } + alt parent_enum { + some(parent_enum_id) { + if id == parent_enum_id { ret true; } + } + _ {} + } + } + + ast::view_path_list(path, ids, _) { + if vec::len(*path) == 1u { + if i == path[0] { ret true; } + for id in ids { + if id.node.name == i { ret true; } + } + } else { + fail "export of path-qualified list"; + } + } + + // FIXME: glob-exports aren't supported yet. + _ {} + } + } + } + _ {} + } + } + // If there are no declared exports then + // everything not imported is exported + // even if it's local (since it's explicit) + ret !has_explicit_exports && local; +} + +pure fn is_call_expr(e: @expr) -> bool { + alt e.node { expr_call(_, _, _) { true } _ { false } } +} + +fn is_constraint_arg(e: @expr) -> bool { + alt e.node { + expr_lit(_) { ret true; } + expr_path(_) { ret true; } + _ { ret false; } + } +} + +fn eq_ty(&&a: @ty, &&b: @ty) -> bool { ret box::ptr_eq(a, b); } + +fn hash_ty(&&t: @ty) -> uint { + let res = (t.span.lo << 16u) + t.span.hi; + ret res; +} + +fn hash_def_id(&&id: def_id) -> uint { + (id.crate as uint << 16u) + (id.node as uint) +} + +fn eq_def_id(&&a: def_id, &&b: def_id) -> bool { + a == b +} + +fn new_def_id_hash<T: copy>() -> std::map::hashmap<def_id, T> { + std::map::mk_hashmap(hash_def_id, eq_def_id) +} + +fn block_from_expr(e: @expr) -> blk { + let blk_ = default_block([], option::some::<@expr>(e), e.id); + ret {node: blk_, span: e.span}; +} + +fn default_block(stmts1: [@stmt], expr1: option<@expr>, id1: node_id) -> + blk_ { + {view_items: [], stmts: stmts1, expr: expr1, id: id1, rules: default_blk} +} + +// FIXME this doesn't handle big integer/float literals correctly (nor does +// the rest of our literal handling) +enum const_val { + const_float(float), + const_int(i64), + const_uint(u64), + const_str(str), +} + +// FIXME: issue #1417 +fn eval_const_expr(e: @expr) -> const_val { + fn fromb(b: bool) -> const_val { const_int(b as i64) } + alt e.node { + expr_unary(neg, inner) { + alt eval_const_expr(inner) { + const_float(f) { const_float(-f) } + const_int(i) { const_int(-i) } + const_uint(i) { const_uint(-i) } + _ { fail "eval_const_expr: bad neg argument"; } + } + } + expr_unary(not, inner) { + alt eval_const_expr(inner) { + const_int(i) { const_int(!i) } + const_uint(i) { const_uint(!i) } + _ { fail "eval_const_expr: bad not argument"; } + } + } + expr_binary(op, a, b) { + alt (eval_const_expr(a), eval_const_expr(b)) { + (const_float(a), const_float(b)) { + alt op { + add { const_float(a + b) } subtract { const_float(a - b) } + mul { const_float(a * b) } div { const_float(a / b) } + rem { const_float(a % b) } eq { fromb(a == b) } + lt { fromb(a < b) } le { fromb(a <= b) } ne { fromb(a != b) } + ge { fromb(a >= b) } gt { fromb(a > b) } + _ { fail "eval_const_expr: can't apply this binop to floats"; } + } + } + (const_int(a), const_int(b)) { + alt op { + add { const_int(a + b) } subtract { const_int(a - b) } + mul { const_int(a * b) } div { const_int(a / b) } + rem { const_int(a % b) } and | bitand { const_int(a & b) } + or | bitor { const_int(a | b) } bitxor { const_int(a ^ b) } + lsl { const_int(a << b) } lsr { const_int(a >> b) } + asr { const_int(a >>> b) } + eq { fromb(a == b) } lt { fromb(a < b) } + le { fromb(a <= b) } ne { fromb(a != b) } + ge { fromb(a >= b) } gt { fromb(a > b) } + _ { fail "eval_const_expr: can't apply this binop to ints"; } + } + } + (const_uint(a), const_uint(b)) { + alt op { + add { const_uint(a + b) } subtract { const_uint(a - b) } + mul { const_uint(a * b) } div { const_uint(a / b) } + rem { const_uint(a % b) } and | bitand { const_uint(a & b) } + or | bitor { const_uint(a | b) } bitxor { const_uint(a ^ b) } + lsl { const_int((a << b) as i64) } + lsr { const_int((a >> b) as i64) } + asr { const_int((a >>> b) as i64) } + eq { fromb(a == b) } lt { fromb(a < b) } + le { fromb(a <= b) } ne { fromb(a != b) } + ge { fromb(a >= b) } gt { fromb(a > b) } + _ { fail "eval_const_expr: can't apply this binop to uints"; } + } + } + _ { fail "eval_constr_expr: bad binary arguments"; } + } + } + expr_lit(lit) { lit_to_const(lit) } + // Precondition? + _ { + fail "eval_const_expr: non-constant expression"; + } + } +} + +fn lit_to_const(lit: @lit) -> const_val { + alt lit.node { + lit_str(s) { const_str(s) } + lit_int(n, _) { const_int(n) } + lit_uint(n, _) { const_uint(n) } + lit_float(n, _) { const_float(option::get(float::from_str(n))) } + lit_nil { const_int(0i64) } + lit_bool(b) { const_int(b as i64) } + } +} + +fn compare_const_vals(a: const_val, b: const_val) -> int { + alt (a, b) { + (const_int(a), const_int(b)) { + if a == b { + 0 + } else if a < b { + -1 + } else { + 1 + } + } + (const_uint(a), const_uint(b)) { + if a == b { + 0 + } else if a < b { + -1 + } else { + 1 + } + } + (const_float(a), const_float(b)) { + if a == b { + 0 + } else if a < b { + -1 + } else { + 1 + } + } + (const_str(a), const_str(b)) { + if a == b { + 0 + } else if a < b { + -1 + } else { + 1 + } + } + _ { + fail "compare_const_vals: ill-typed comparison"; + } + } +} + +fn compare_lit_exprs(a: @expr, b: @expr) -> int { + compare_const_vals(eval_const_expr(a), eval_const_expr(b)) +} + +fn lit_expr_eq(a: @expr, b: @expr) -> bool { compare_lit_exprs(a, b) == 0 } + +fn lit_eq(a: @lit, b: @lit) -> bool { + compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0 +} + +fn ident_to_path(s: span, i: ident) -> @path { + @respan(s, {global: false, idents: [i], types: []}) +} + +pure fn is_unguarded(&&a: arm) -> bool { + alt a.guard { + none { true } + _ { false } + } +} + +pure fn unguarded_pat(a: arm) -> option<[@pat]> { + if is_unguarded(a) { some(a.pats) } else { none } +} + +// Provides an extra node_id to hang callee information on, in case the +// operator is deferred to a user-supplied method. The parser is responsible +// for reserving this id. +fn op_expr_callee_id(e: @expr) -> node_id { e.id - 1 } + +pure fn class_item_ident(ci: @class_item) -> ident { + alt ci.node.decl { + instance_var(i,_,_,_) { i } + class_method(it) { it.ident } + } +} + +impl inlined_item_methods for inlined_item { + fn ident() -> ident { + alt self { + ii_item(i) { i.ident } + ii_method(_, m) { m.ident } + } + } + + fn id() -> ast::node_id { + alt self { + ii_item(i) { i.id } + ii_method(_, m) { m.id } + } + } + + fn accept<E>(e: E, v: visit::vt<E>) { + alt self { + ii_item(i) { v.visit_item(i, e, v) } + ii_method(_, m) { visit::visit_method_helper(m, e, v) } + } + } +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/syntax/codemap.rs b/src/rustc/syntax/codemap.rs new file mode 100644 index 00000000000..a7dbe574717 --- /dev/null +++ b/src/rustc/syntax/codemap.rs @@ -0,0 +1,204 @@ +type filename = str; + +type file_pos = {ch: uint, byte: uint}; + +/* A codemap is a thing that maps uints to file/line/column positions + * in a crate. This to make it possible to represent the positions + * with single-word things, rather than passing records all over the + * compiler. + */ + +enum file_substr { + fss_none, + fss_internal(span), + fss_external({filename: str, line: uint, col: uint}) +} + +type filemap = + @{name: filename, substr: file_substr, src: @str, + start_pos: file_pos, mutable lines: [file_pos]}; + +type codemap = @{mutable files: [filemap]}; + +type loc = {file: filemap, line: uint, col: uint}; + +fn new_codemap() -> codemap { @{mutable files: [] } } + +fn new_filemap_w_substr(filename: filename, substr: file_substr, + src: @str, + start_pos_ch: uint, start_pos_byte: uint) + -> filemap { + ret @{name: filename, substr: substr, src: src, + start_pos: {ch: start_pos_ch, byte: start_pos_byte}, + mutable lines: [{ch: start_pos_ch, byte: start_pos_byte}]}; +} + +fn new_filemap(filename: filename, src: @str, + start_pos_ch: uint, start_pos_byte: uint) + -> filemap { + ret new_filemap_w_substr(filename, fss_none, src, + start_pos_ch, start_pos_byte); +} + +fn mk_substr_filename(cm: codemap, sp: span) -> str +{ + let pos = lookup_char_pos(cm, sp.lo); + ret #fmt("<%s:%u:%u>", pos.file.name, pos.line, pos.col); +} + +fn next_line(file: filemap, chpos: uint, byte_pos: uint) { + file.lines += [{ch: chpos, byte: byte_pos}]; +} + +type lookup_fn = fn@(file_pos) -> uint; + +fn lookup_line(map: codemap, pos: uint, lookup: lookup_fn) + -> {fm: filemap, line: uint} +{ + let len = vec::len(map.files); + let a = 0u; + let b = len; + while b - a > 1u { + let m = (a + b) / 2u; + if lookup(map.files[m].start_pos) > pos { b = m; } else { a = m; } + } + if (a >= len) { + fail #fmt("position %u does not resolve to a source location", pos) + } + let f = map.files[a]; + a = 0u; + b = vec::len(f.lines); + while b - a > 1u { + let m = (a + b) / 2u; + if lookup(f.lines[m]) > pos { b = m; } else { a = m; } + } + ret {fm: f, line: a}; +} + +fn lookup_pos(map: codemap, pos: uint, lookup: lookup_fn) -> loc { + let {fm: f, line: a} = lookup_line(map, pos, lookup); + ret {file: f, line: a + 1u, col: pos - lookup(f.lines[a])}; +} + +fn lookup_char_pos(map: codemap, pos: uint) -> loc { + fn lookup(pos: file_pos) -> uint { ret pos.ch; } + ret lookup_pos(map, pos, lookup); +} + +fn lookup_byte_pos(map: codemap, pos: uint) -> loc { + fn lookup(pos: file_pos) -> uint { ret pos.byte; } + ret lookup_pos(map, pos, lookup); +} + +fn lookup_char_pos_adj(map: codemap, pos: uint) + -> {filename: str, line: uint, col: uint, file: option<filemap>} +{ + let loc = lookup_char_pos(map, pos); + alt (loc.file.substr) { + fss_none { + {filename: loc.file.name, line: loc.line, col: loc.col, + file: some(loc.file)} + } + fss_internal(sp) { + lookup_char_pos_adj(map, sp.lo + (pos - loc.file.start_pos.ch)) + } + fss_external(eloc) { + {filename: eloc.filename, + line: eloc.line + loc.line - 1u, + col: if loc.line == 1u {eloc.col + loc.col} else {loc.col}, + file: none} + } + } +} + +fn adjust_span(map: codemap, sp: span) -> span { + fn lookup(pos: file_pos) -> uint { ret pos.ch; } + let line = lookup_line(map, sp.lo, lookup); + alt (line.fm.substr) { + fss_none {sp} + fss_internal(s) { + adjust_span(map, {lo: s.lo + (sp.lo - line.fm.start_pos.ch), + hi: s.lo + (sp.hi - line.fm.start_pos.ch), + expn_info: sp.expn_info})} + fss_external(_) {sp} + } +} + +enum expn_info_ { + expanded_from({call_site: span, + callie: {name: str, span: option<span>}}) +} +type expn_info = option<@expn_info_>; +type span = {lo: uint, hi: uint, expn_info: expn_info}; + +fn span_to_str_no_adj(sp: span, cm: codemap) -> str { + let lo = lookup_char_pos(cm, sp.lo); + let hi = lookup_char_pos(cm, sp.hi); + ret #fmt("%s:%u:%u: %u:%u", lo.file.name, + lo.line, lo.col, hi.line, hi.col) +} + +fn span_to_str(sp: span, cm: codemap) -> str { + let lo = lookup_char_pos_adj(cm, sp.lo); + let hi = lookup_char_pos_adj(cm, sp.hi); + ret #fmt("%s:%u:%u: %u:%u", lo.filename, + lo.line, lo.col, hi.line, hi.col) +} + +type file_lines = {file: filemap, lines: [uint]}; + +fn span_to_lines(sp: span, cm: codemap::codemap) -> @file_lines { + let lo = lookup_char_pos(cm, sp.lo); + let hi = lookup_char_pos(cm, sp.hi); + let lines = []; + uint::range(lo.line - 1u, hi.line as uint) {|i| lines += [i]; }; + ret @{file: lo.file, lines: lines}; +} + +fn get_line(fm: filemap, line: int) -> str unsafe { + let begin: uint = fm.lines[line].byte - fm.start_pos.byte; + let end = alt str::find_char_from(*fm.src, '\n', begin) { + some(e) { e } + none { str::len(*fm.src) } + }; + str::slice(*fm.src, begin, end) +} + +fn lookup_byte_offset(cm: codemap::codemap, chpos: uint) + -> {fm: filemap, pos: uint} { + let {fm, line} = lookup_line(cm, chpos, {|pos| pos.ch}); + let line_offset = fm.lines[line].byte - fm.start_pos.byte; + let col = chpos - fm.lines[line].ch; + let col_offset = str::count_bytes(*fm.src, line_offset, col); + {fm: fm, pos: line_offset + col_offset} +} + +fn span_to_snippet(sp: span, cm: codemap::codemap) -> str { + let begin = lookup_byte_offset(cm, sp.lo); + let end = lookup_byte_offset(cm, sp.hi); + assert begin.fm == end.fm; + ret str::slice(*begin.fm.src, begin.pos, end.pos); +} + +fn get_snippet(cm: codemap::codemap, fidx: uint, lo: uint, hi: uint) -> str +{ + let fm = cm.files[fidx]; + ret str::slice(*fm.src, lo, hi) +} + +fn get_filemap(cm: codemap, filename: str) -> filemap { + for fm: filemap in cm.files { if fm.name == filename { ret fm; } } + //XXjdm the following triggers a mismatched type bug + // (or expected function, found _|_) + fail; // ("asking for " + filename + " which we don't know about"); +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/ext/base.rs b/src/rustc/syntax/ext/base.rs new file mode 100644 index 00000000000..d59591d849a --- /dev/null +++ b/src/rustc/syntax/ext/base.rs @@ -0,0 +1,157 @@ +import std::map::hashmap; +import driver::session::session; +import codemap::{span, expn_info, expanded_from}; +import std::map::new_str_hash; + +type syntax_expander_ = + fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> @ast::expr; +type syntax_expander = { + expander: syntax_expander_, + span: option<span>}; +type macro_def = {ident: str, ext: syntax_extension}; +type macro_definer = + fn@(ext_ctxt, span, ast::mac_arg, ast::mac_body) -> macro_def; + +enum syntax_extension { + normal(syntax_expander), + macro_defining(macro_definer), +} + +// A temporary hard-coded map of methods for expanding syntax extension +// AST nodes into full ASTs +fn syntax_expander_table() -> hashmap<str, syntax_extension> { + fn builtin(f: syntax_expander_) -> syntax_extension + {normal({expander: f, span: none})} + let syntax_expanders = new_str_hash::<syntax_extension>(); + syntax_expanders.insert("fmt", builtin(ext::fmt::expand_syntax_ext)); + syntax_expanders.insert("env", builtin(ext::env::expand_syntax_ext)); + syntax_expanders.insert("macro", + macro_defining(ext::simplext::add_new_extension)); + syntax_expanders.insert("concat_idents", + builtin(ext::concat_idents::expand_syntax_ext)); + syntax_expanders.insert("ident_to_str", + builtin(ext::ident_to_str::expand_syntax_ext)); + syntax_expanders.insert("log_syntax", + builtin(ext::log_syntax::expand_syntax_ext)); + syntax_expanders.insert("ast", + builtin(ext::qquote::expand_ast)); + ret syntax_expanders; +} + +iface ext_ctxt { + fn session() -> session; + fn print_backtrace(); + fn backtrace() -> expn_info; + fn bt_push(ei: codemap::expn_info_); + fn bt_pop(); + fn span_fatal(sp: span, msg: str) -> !; + fn span_err(sp: span, msg: str); + fn span_unimpl(sp: span, msg: str) -> !; + fn span_bug(sp: span, msg: str) -> !; + fn bug(msg: str) -> !; + fn next_id() -> ast::node_id; +} + +fn mk_ctxt(sess: session) -> ext_ctxt { + type ctxt_repr = {sess: session, + mutable backtrace: expn_info}; + impl of ext_ctxt for ctxt_repr { + fn session() -> session { self.sess } + fn print_backtrace() { } + fn backtrace() -> expn_info { self.backtrace } + fn bt_push(ei: codemap::expn_info_) { + alt ei { + expanded_from({call_site: cs, callie: callie}) { + self.backtrace = + some(@expanded_from({ + call_site: {lo: cs.lo, hi: cs.hi, + expn_info: self.backtrace}, + callie: callie})); + } + } + } + fn bt_pop() { + alt self.backtrace { + some(@expanded_from({call_site: {expn_info: prev, _}, _})) { + self.backtrace = prev + } + _ { self.bug("tried to pop without a push"); } + } + } + fn span_fatal(sp: span, msg: str) -> ! { + self.print_backtrace(); + self.sess.span_fatal(sp, msg); + } + fn span_err(sp: span, msg: str) { + self.print_backtrace(); + self.sess.span_err(sp, msg); + } + fn span_unimpl(sp: span, msg: str) -> ! { + self.print_backtrace(); + self.sess.span_unimpl(sp, msg); + } + fn span_bug(sp: span, msg: str) -> ! { + self.print_backtrace(); + self.sess.span_bug(sp, msg); + } + fn bug(msg: str) -> ! { self.print_backtrace(); self.sess.bug(msg); } + fn next_id() -> ast::node_id { ret self.sess.next_node_id(); } + } + let imp : ctxt_repr = {sess: sess, mutable backtrace: none}; + ret imp as ext_ctxt +} + +fn expr_to_str(cx: ext_ctxt, expr: @ast::expr, error: str) -> str { + alt expr.node { + ast::expr_lit(l) { + alt l.node { + ast::lit_str(s) { ret s; } + _ { cx.span_fatal(l.span, error); } + } + } + _ { cx.span_fatal(expr.span, error); } + } +} + +fn expr_to_ident(cx: ext_ctxt, expr: @ast::expr, error: str) -> ast::ident { + alt expr.node { + ast::expr_path(p) { + if vec::len(p.node.types) > 0u || vec::len(p.node.idents) != 1u { + cx.span_fatal(expr.span, error); + } else { ret p.node.idents[0]; } + } + _ { cx.span_fatal(expr.span, error); } + } +} + +fn make_new_lit(cx: ext_ctxt, sp: codemap::span, lit: ast::lit_) -> + @ast::expr { + let sp_lit = @{node: lit, span: sp}; + ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp}; +} + +fn get_mac_arg(cx: ext_ctxt, sp: span, arg: ast::mac_arg) -> @ast::expr { + alt (arg) { + some(expr) {expr} + none {cx.span_fatal(sp, "missing macro args")} + } +} + +fn get_mac_body(cx: ext_ctxt, sp: span, args: ast::mac_body) + -> ast::mac_body_ +{ + alt (args) { + some(body) {body} + none {cx.span_fatal(sp, "missing macro body")} + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/ext/build.rs b/src/rustc/syntax/ext/build.rs new file mode 100644 index 00000000000..5d615c4305d --- /dev/null +++ b/src/rustc/syntax/ext/build.rs @@ -0,0 +1,81 @@ +import codemap::span; +import syntax::ext::base::ext_ctxt; + +// NOTE: Moved from fmt.rs which had this fixme: +// FIXME: Cleanup the naming of these functions + +fn mk_lit(cx: ext_ctxt, sp: span, lit: ast::lit_) -> @ast::expr { + let sp_lit = @{node: lit, span: sp}; + ret @{id: cx.next_id(), node: ast::expr_lit(sp_lit), span: sp}; +} +fn mk_str(cx: ext_ctxt, sp: span, s: str) -> @ast::expr { + let lit = ast::lit_str(s); + ret mk_lit(cx, sp, lit); +} +fn mk_int(cx: ext_ctxt, sp: span, i: int) -> @ast::expr { + let lit = ast::lit_int(i as i64, ast::ty_i); + ret mk_lit(cx, sp, lit); +} +fn mk_uint(cx: ext_ctxt, sp: span, u: uint) -> @ast::expr { + let lit = ast::lit_uint(u as u64, ast::ty_u); + ret mk_lit(cx, sp, lit); +} +fn mk_binary(cx: ext_ctxt, sp: span, op: ast::binop, + lhs: @ast::expr, rhs: @ast::expr) + -> @ast::expr { + let binexpr = ast::expr_binary(op, lhs, rhs); + ret @{id: cx.next_id(), node: binexpr, span: sp}; +} +fn mk_unary(cx: ext_ctxt, sp: span, op: ast::unop, e: @ast::expr) + -> @ast::expr { + let expr = ast::expr_unary(op, e); + ret @{id: cx.next_id(), node: expr, span: sp}; +} +fn mk_path(cx: ext_ctxt, sp: span, idents: [ast::ident]) -> + @ast::expr { + let path = {global: false, idents: idents, types: []}; + let sp_path = @{node: path, span: sp}; + let pathexpr = ast::expr_path(sp_path); + ret @{id: cx.next_id(), node: pathexpr, span: sp}; +} +fn mk_access_(cx: ext_ctxt, sp: span, p: @ast::expr, m: ast::ident) + -> @ast::expr { + let expr = ast::expr_field(p, m, []); + ret @{id: cx.next_id(), node: expr, span: sp}; +} +fn mk_access(cx: ext_ctxt, sp: span, p: [ast::ident], m: ast::ident) + -> @ast::expr { + let pathexpr = mk_path(cx, sp, p); + ret mk_access_(cx, sp, pathexpr, m); +} +fn mk_call_(cx: ext_ctxt, sp: span, fn_expr: @ast::expr, + args: [@ast::expr]) -> @ast::expr { + let callexpr = ast::expr_call(fn_expr, args, false); + ret @{id: cx.next_id(), node: callexpr, span: sp}; +} +fn mk_call(cx: ext_ctxt, sp: span, fn_path: [ast::ident], + args: [@ast::expr]) -> @ast::expr { + let pathexpr = mk_path(cx, sp, fn_path); + ret mk_call_(cx, sp, pathexpr, args); +} +// e = expr, t = type +fn mk_vec_e(cx: ext_ctxt, sp: span, exprs: [@ast::expr]) -> + @ast::expr { + let vecexpr = ast::expr_vec(exprs, ast::m_imm); + ret @{id: cx.next_id(), node: vecexpr, span: sp}; +} +fn mk_rec_e(cx: ext_ctxt, sp: span, + fields: [{ident: ast::ident, ex: @ast::expr}]) -> + @ast::expr { + let astfields: [ast::field] = []; + for field: {ident: ast::ident, ex: @ast::expr} in fields { + let ident = field.ident; + let val = field.ex; + let astfield = + {node: {mutbl: ast::m_imm, ident: ident, expr: val}, span: sp}; + astfields += [astfield]; + } + let recexpr = ast::expr_rec(astfields, option::none::<@ast::expr>); + ret @{id: cx.next_id(), node: recexpr, span: sp}; +} + diff --git a/src/rustc/syntax/ext/concat_idents.rs b/src/rustc/syntax/ext/concat_idents.rs new file mode 100644 index 00000000000..4b9402f9cd9 --- /dev/null +++ b/src/rustc/syntax/ext/concat_idents.rs @@ -0,0 +1,24 @@ +import base::*; +import syntax::ast; + +fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg, + _body: ast::mac_body) -> @ast::expr { + let arg = get_mac_arg(cx,sp,arg); + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + cx.span_fatal(sp, "#concat_idents requires a vector argument .") + } + }; + let res: ast::ident = ""; + for e: @ast::expr in args { + res += expr_to_ident(cx, e, "expected an ident"); + } + + ret @{id: cx.next_id(), + node: ast::expr_path(@{node: {global: false, idents: [res], + types: []}, + span: sp}), + span: sp}; +} diff --git a/src/rustc/syntax/ext/env.rs b/src/rustc/syntax/ext/env.rs new file mode 100644 index 00000000000..cb9453ae062 --- /dev/null +++ b/src/rustc/syntax/ext/env.rs @@ -0,0 +1,45 @@ + +/* + * The compiler code necessary to support the #env extension. Eventually this + * should all get sucked into either the compiler syntax extension plugin + * interface. + */ +import std::generic_os; +import base::*; +export expand_syntax_ext; + +fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg, + _body: ast::mac_body) -> @ast::expr { + let arg = get_mac_arg(cx,sp,arg); + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + cx.span_fatal(sp, "#env requires arguments of the form `[...]`.") + } + }; + if vec::len::<@ast::expr>(args) != 1u { + cx.span_fatal(sp, "malformed #env call"); + } + // FIXME: if this was more thorough it would manufacture an + // option<str> rather than just an maybe-empty string. + + let var = expr_to_str(cx, args[0], "#env requires a string"); + alt generic_os::getenv(var) { + option::none { ret make_new_str(cx, sp, ""); } + option::some(s) { ret make_new_str(cx, sp, s); } + } +} + +fn make_new_str(cx: ext_ctxt, sp: codemap::span, s: str) -> @ast::expr { + ret make_new_lit(cx, sp, ast::lit_str(s)); +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/ext/expand.rs b/src/rustc/syntax/ext/expand.rs new file mode 100644 index 00000000000..104f49aebac --- /dev/null +++ b/src/rustc/syntax/ext/expand.rs @@ -0,0 +1,102 @@ +import driver::session; + +import std::map::hashmap; + +import syntax::ast::{crate, expr_, expr_mac, mac_invoc}; +import syntax::fold::*; +import syntax::ext::base::*; +import syntax::ext::qquote::{qq_helper}; +import syntax::parse::parser::parse_expr_from_source_str; + +import codemap::{span, expanded_from}; + +fn expand_expr(exts: hashmap<str, syntax_extension>, cx: ext_ctxt, + e: expr_, s: span, fld: ast_fold, + orig: fn@(expr_, span, ast_fold) -> (expr_, span)) + -> (expr_, span) +{ + ret alt e { + expr_mac(mac) { + alt mac.node { + mac_invoc(pth, args, body) { + assert (vec::len(pth.node.idents) > 0u); + let extname = pth.node.idents[0]; + alt exts.find(extname) { + none { + cx.span_fatal(pth.span, + #fmt["macro undefined: '%s'", extname]) + } + some(normal({expander: exp, span: exp_sp})) { + let expanded = exp(cx, pth.span, args, body); + + let info = {call_site: s, + callie: {name: extname, span: exp_sp}}; + cx.bt_push(expanded_from(info)); + //keep going, outside-in + let fully_expanded = fld.fold_expr(expanded).node; + cx.bt_pop(); + + (fully_expanded, s) + } + some(macro_defining(ext)) { + let named_extension = ext(cx, pth.span, args, body); + exts.insert(named_extension.ident, named_extension.ext); + (ast::expr_rec([], none), s) + } + } + } + _ { cx.span_bug(mac.span, "naked syntactic bit") } + } + } + _ { orig(e, s, fld) } + }; +} + +fn new_span(cx: ext_ctxt, sp: span) -> span { + /* this discards information in the case of macro-defining macros */ + ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; +} + +// FIXME: this is a terrible kludge to inject some macros into the default +// compilation environment. When the macro-definition system is substantially +// more mature, these should move from here, into a compiled part of libcore +// at very least. + +fn core_macros() -> str { + ret +"{ + #macro([#error[f, ...], log(core::error, #fmt[f, ...])]); + #macro([#warn[f, ...], log(core::warn, #fmt[f, ...])]); + #macro([#info[f, ...], log(core::info, #fmt[f, ...])]); + #macro([#debug[f, ...], log(core::debug, #fmt[f, ...])]); +}"; +} + +fn expand_crate(sess: session::session, c: @crate) -> @crate { + let exts = syntax_expander_table(); + let afp = default_ast_fold(); + let cx: ext_ctxt = mk_ctxt(sess); + let f_pre = + {fold_expr: bind expand_expr(exts, cx, _, _, _, afp.fold_expr), + new_span: bind new_span(cx, _) + with *afp}; + let f = make_fold(f_pre); + let cm = parse_expr_from_source_str("<core-macros>", + @core_macros(), + sess.opts.cfg, + sess.parse_sess); + + // This is run for its side-effects on the expander env, + // as it registers all the core macros as expanders. + f.fold_expr(cm); + + let res = @f.fold_crate(*c); + ret res; +} +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/syntax/ext/fmt.rs b/src/rustc/syntax/ext/fmt.rs new file mode 100644 index 00000000000..29b19a2d66c --- /dev/null +++ b/src/rustc/syntax/ext/fmt.rs @@ -0,0 +1,300 @@ + + +/* + * The compiler code necessary to support the #fmt extension. Eventually this + * should all get sucked into either the standard library extfmt module or the + * compiler syntax extension plugin interface. + */ +import extfmt::ct::*; +import base::*; +import codemap::span; +import syntax::ext::build::*; +export expand_syntax_ext; + +fn expand_syntax_ext(cx: ext_ctxt, sp: span, arg: ast::mac_arg, + _body: ast::mac_body) -> @ast::expr { + let arg = get_mac_arg(cx,sp,arg); + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + cx.span_fatal(sp, "#fmt requires arguments of the form `[...]`.") + } + }; + if vec::len::<@ast::expr>(args) == 0u { + cx.span_fatal(sp, "#fmt requires a format string"); + } + let fmt = + expr_to_str(cx, args[0], + "first argument to #fmt must be a string literal."); + let fmtspan = args[0].span; + #debug("Format string:"); + log(debug, fmt); + fn parse_fmt_err_(cx: ext_ctxt, sp: span, msg: str) -> ! { + cx.span_fatal(sp, msg); + } + let parse_fmt_err = bind parse_fmt_err_(cx, fmtspan, _); + let pieces = parse_fmt_string(fmt, parse_fmt_err); + ret pieces_to_expr(cx, sp, pieces, args); +} + +// FIXME: A lot of these functions for producing expressions can probably +// be factored out in common with other code that builds expressions. +// FIXME: Cleanup the naming of these functions +// NOTE: Moved many of the common ones to build.rs --kevina +fn pieces_to_expr(cx: ext_ctxt, sp: span, pieces: [piece], args: [@ast::expr]) + -> @ast::expr { + fn make_path_vec(_cx: ext_ctxt, ident: ast::ident) -> [ast::ident] { + ret ["extfmt", "rt", ident]; + } + fn make_rt_path_expr(cx: ext_ctxt, sp: span, ident: str) -> @ast::expr { + let path = make_path_vec(cx, ident); + ret mk_path(cx, sp, path); + } + // Produces an AST expression that represents a RT::conv record, + // which tells the RT::conv* functions how to perform the conversion + + fn make_rt_conv_expr(cx: ext_ctxt, sp: span, cnv: conv) -> @ast::expr { + fn make_flags(cx: ext_ctxt, sp: span, flags: [flag]) -> @ast::expr { + let flagexprs: [@ast::expr] = []; + for f: flag in flags { + let fstr; + alt f { + flag_left_justify { fstr = "flag_left_justify"; } + flag_left_zero_pad { fstr = "flag_left_zero_pad"; } + flag_space_for_sign { fstr = "flag_space_for_sign"; } + flag_sign_always { fstr = "flag_sign_always"; } + flag_alternate { fstr = "flag_alternate"; } + } + flagexprs += [make_rt_path_expr(cx, sp, fstr)]; + } + // FIXME: 0-length vectors can't have their type inferred + // through the rec that these flags are a member of, so + // this is a hack placeholder flag + + if vec::len::<@ast::expr>(flagexprs) == 0u { + flagexprs += [make_rt_path_expr(cx, sp, "flag_none")]; + } + ret mk_vec_e(cx, sp, flagexprs); + } + fn make_count(cx: ext_ctxt, sp: span, cnt: count) -> @ast::expr { + alt cnt { + count_implied { + ret make_rt_path_expr(cx, sp, "count_implied"); + } + count_is(c) { + let count_lit = mk_int(cx, sp, c); + let count_is_path = make_path_vec(cx, "count_is"); + let count_is_args = [count_lit]; + ret mk_call(cx, sp, count_is_path, count_is_args); + } + _ { cx.span_unimpl(sp, "unimplemented #fmt conversion"); } + } + } + fn make_ty(cx: ext_ctxt, sp: span, t: ty) -> @ast::expr { + let rt_type; + alt t { + ty_hex(c) { + alt c { + case_upper { rt_type = "ty_hex_upper"; } + case_lower { rt_type = "ty_hex_lower"; } + } + } + ty_bits { rt_type = "ty_bits"; } + ty_octal { rt_type = "ty_octal"; } + _ { rt_type = "ty_default"; } + } + ret make_rt_path_expr(cx, sp, rt_type); + } + fn make_conv_rec(cx: ext_ctxt, sp: span, flags_expr: @ast::expr, + width_expr: @ast::expr, precision_expr: @ast::expr, + ty_expr: @ast::expr) -> @ast::expr { + ret mk_rec_e(cx, sp, + [{ident: "flags", ex: flags_expr}, + {ident: "width", ex: width_expr}, + {ident: "precision", ex: precision_expr}, + {ident: "ty", ex: ty_expr}]); + } + let rt_conv_flags = make_flags(cx, sp, cnv.flags); + let rt_conv_width = make_count(cx, sp, cnv.width); + let rt_conv_precision = make_count(cx, sp, cnv.precision); + let rt_conv_ty = make_ty(cx, sp, cnv.ty); + ret make_conv_rec(cx, sp, rt_conv_flags, rt_conv_width, + rt_conv_precision, rt_conv_ty); + } + fn make_conv_call(cx: ext_ctxt, sp: span, conv_type: str, cnv: conv, + arg: @ast::expr) -> @ast::expr { + let fname = "conv_" + conv_type; + let path = make_path_vec(cx, fname); + let cnv_expr = make_rt_conv_expr(cx, sp, cnv); + let args = [cnv_expr, arg]; + ret mk_call(cx, arg.span, path, args); + } + fn make_new_conv(cx: ext_ctxt, sp: span, cnv: conv, arg: @ast::expr) -> + @ast::expr { + // FIXME: Extract all this validation into extfmt::ct + + fn is_signed_type(cnv: conv) -> bool { + alt cnv.ty { + ty_int(s) { + alt s { signed { ret true; } unsigned { ret false; } } + } + ty_float { ret true; } + _ { ret false; } + } + } + let unsupported = "conversion not supported in #fmt string"; + alt cnv.param { + option::none { } + _ { cx.span_unimpl(sp, unsupported); } + } + for f: flag in cnv.flags { + alt f { + flag_left_justify { } + flag_sign_always { + if !is_signed_type(cnv) { + cx.span_fatal(sp, + "+ flag only valid in " + + "signed #fmt conversion"); + } + } + flag_space_for_sign { + if !is_signed_type(cnv) { + cx.span_fatal(sp, + "space flag only valid in " + + "signed #fmt conversions"); + } + } + flag_left_zero_pad { } + _ { cx.span_unimpl(sp, unsupported); } + } + } + alt cnv.width { + count_implied { } + count_is(_) { } + _ { cx.span_unimpl(sp, unsupported); } + } + alt cnv.precision { + count_implied { } + count_is(_) { } + _ { cx.span_unimpl(sp, unsupported); } + } + alt cnv.ty { + ty_str { ret make_conv_call(cx, arg.span, "str", cnv, arg); } + ty_int(sign) { + alt sign { + signed { ret make_conv_call(cx, arg.span, "int", cnv, arg); } + unsigned { + ret make_conv_call(cx, arg.span, "uint", cnv, arg); + } + } + } + ty_bool { ret make_conv_call(cx, arg.span, "bool", cnv, arg); } + ty_char { ret make_conv_call(cx, arg.span, "char", cnv, arg); } + ty_hex(_) { ret make_conv_call(cx, arg.span, "uint", cnv, arg); } + ty_bits { ret make_conv_call(cx, arg.span, "uint", cnv, arg); } + ty_octal { ret make_conv_call(cx, arg.span, "uint", cnv, arg); } + ty_float { ret make_conv_call(cx, arg.span, "float", cnv, arg); } + ty_poly { ret make_conv_call(cx, arg.span, "poly", cnv, arg); } + _ { cx.span_unimpl(sp, unsupported); } + } + } + fn log_conv(c: conv) { + alt c.param { + some(p) { log(debug, "param: " + int::to_str(p, 10u)); } + _ { #debug("param: none"); } + } + for f: flag in c.flags { + alt f { + flag_left_justify { #debug("flag: left justify"); } + flag_left_zero_pad { #debug("flag: left zero pad"); } + flag_space_for_sign { #debug("flag: left space pad"); } + flag_sign_always { #debug("flag: sign always"); } + flag_alternate { #debug("flag: alternate"); } + } + } + alt c.width { + count_is(i) { log(debug, + "width: count is " + int::to_str(i, 10u)); } + count_is_param(i) { + log(debug, + "width: count is param " + int::to_str(i, 10u)); + } + count_is_next_param { #debug("width: count is next param"); } + count_implied { #debug("width: count is implied"); } + } + alt c.precision { + count_is(i) { log(debug, + "prec: count is " + int::to_str(i, 10u)); } + count_is_param(i) { + log(debug, + "prec: count is param " + int::to_str(i, 10u)); + } + count_is_next_param { #debug("prec: count is next param"); } + count_implied { #debug("prec: count is implied"); } + } + alt c.ty { + ty_bool { #debug("type: bool"); } + ty_str { #debug("type: str"); } + ty_char { #debug("type: char"); } + ty_int(s) { + alt s { + signed { #debug("type: signed"); } + unsigned { #debug("type: unsigned"); } + } + } + ty_bits { #debug("type: bits"); } + ty_hex(cs) { + alt cs { + case_upper { #debug("type: uhex"); } + case_lower { #debug("type: lhex"); } + } + } + ty_octal { #debug("type: octal"); } + ty_float { #debug("type: float"); } + ty_poly { #debug("type: poly"); } + } + } + let fmt_sp = args[0].span; + let n = 0u; + let tmp_expr = mk_str(cx, sp, ""); + let nargs = vec::len::<@ast::expr>(args); + for pc: piece in pieces { + alt pc { + piece_string(s) { + let s_expr = mk_str(cx, fmt_sp, s); + tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, s_expr); + } + piece_conv(conv) { + n += 1u; + if n >= nargs { + cx.span_fatal(sp, + "not enough arguments to #fmt " + + "for the given format string"); + } + #debug("Building conversion:"); + log_conv(conv); + let arg_expr = args[n]; + let c_expr = make_new_conv(cx, fmt_sp, conv, arg_expr); + tmp_expr = mk_binary(cx, fmt_sp, ast::add, tmp_expr, c_expr); + } + } + } + let expected_nargs = n + 1u; // n conversions + the fmt string + + if expected_nargs < nargs { + cx.span_fatal + (sp, #fmt["too many arguments to #fmt. found %u, expected %u", + nargs, expected_nargs]); + } + ret tmp_expr; +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/ext/ident_to_str.rs b/src/rustc/syntax/ext/ident_to_str.rs new file mode 100644 index 00000000000..2bb6dac7f2a --- /dev/null +++ b/src/rustc/syntax/ext/ident_to_str.rs @@ -0,0 +1,22 @@ +import base::*; +import syntax::ast; + +fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg, + _body: ast::mac_body) -> @ast::expr { + let arg = get_mac_arg(cx,sp,arg); + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + cx.span_fatal(sp, "#ident_to_str requires a vector argument .") + } + }; + if vec::len::<@ast::expr>(args) != 1u { + cx.span_fatal(sp, "malformed #ident_to_str call"); + } + + ret make_new_lit(cx, sp, + ast::lit_str(expr_to_ident(cx, args[0u], + "expected an ident"))); + +} diff --git a/src/rustc/syntax/ext/log_syntax.rs b/src/rustc/syntax/ext/log_syntax.rs new file mode 100644 index 00000000000..911cf9ff2eb --- /dev/null +++ b/src/rustc/syntax/ext/log_syntax.rs @@ -0,0 +1,13 @@ +import base::*; +import syntax::ast; +import std::io::writer_util; + +fn expand_syntax_ext(cx: ext_ctxt, sp: codemap::span, arg: ast::mac_arg, + _body: ast::mac_body) -> @ast::expr { + let arg = get_mac_arg(cx,sp,arg); + cx.print_backtrace(); + std::io::stdout().write_line(print::pprust::expr_to_str(arg)); + + //trivial expression + ret @{id: cx.next_id(), node: ast::expr_rec([], option::none), span: sp}; +} diff --git a/src/rustc/syntax/ext/qquote.rs b/src/rustc/syntax/ext/qquote.rs new file mode 100644 index 00000000000..7f7d5a387f0 --- /dev/null +++ b/src/rustc/syntax/ext/qquote.rs @@ -0,0 +1,338 @@ +import driver::session; + +import syntax::ast::{crate, expr_, mac_invoc, + mac_aq, mac_var}; +import syntax::fold::*; +import syntax::visit::*; +import syntax::ext::base::*; +import syntax::ext::build::*; +import syntax::parse::parser; +import syntax::parse::parser::{parser, parse_from_source_str}; + +import syntax::print::*; +import std::io::*; + +import codemap::span; + +type aq_ctxt = @{lo: uint, + mutable gather: [{lo: uint, hi: uint, + e: @ast::expr, + constr: str}]}; +enum fragment { + from_expr(@ast::expr), + from_ty(@ast::ty) +} + +iface qq_helper { + fn span() -> span; + fn visit(aq_ctxt, vt<aq_ctxt>); + fn extract_mac() -> option<ast::mac_>; + fn mk_parse_fn(ext_ctxt,span) -> @ast::expr; + fn get_fold_fn() -> str; +} + +impl of qq_helper for @ast::crate { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_crate(*self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_crate"]) + } + fn get_fold_fn() -> str {"fold_crate"} +} +impl of qq_helper for @ast::expr { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_expr(self, cx, v);} + fn extract_mac() -> option<ast::mac_> { + alt (self.node) { + ast::expr_mac({node: mac, _}) {some(mac)} + _ {none} + } + } + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "parse", "parser", "parse_expr"]) + } + fn get_fold_fn() -> str {"fold_expr"} +} +impl of qq_helper for @ast::ty { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_ty(self, cx, v);} + fn extract_mac() -> option<ast::mac_> { + alt (self.node) { + ast::ty_mac({node: mac, _}) {some(mac)} + _ {none} + } + } + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_ty"]) + } + fn get_fold_fn() -> str {"fold_ty"} +} +impl of qq_helper for @ast::item { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_item(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_item"]) + } + fn get_fold_fn() -> str {"fold_item"} +} +impl of qq_helper for @ast::stmt { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_stmt(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "ext", "qquote", "parse_stmt"]) + } + fn get_fold_fn() -> str {"fold_stmt"} +} +impl of qq_helper for @ast::pat { + fn span() -> span {self.span} + fn visit(cx: aq_ctxt, v: vt<aq_ctxt>) {visit_pat(self, cx, v);} + fn extract_mac() -> option<ast::mac_> {fail} + fn mk_parse_fn(cx: ext_ctxt, sp: span) -> @ast::expr { + mk_path(cx, sp, ["syntax", "parse", "parser", "parse_pat"]) + } + fn get_fold_fn() -> str {"fold_pat"} +} + +fn gather_anti_quotes<N: qq_helper>(lo: uint, node: N) -> aq_ctxt +{ + let v = @{visit_expr: visit_aq_expr, + visit_ty: visit_aq_ty + with *default_visitor()}; + let cx = @{lo:lo, mutable gather: []}; + node.visit(cx, mk_vt(v)); + ret cx; +} + +fn visit_aq<T:qq_helper>(node: T, constr: str, &&cx: aq_ctxt, v: vt<aq_ctxt>) +{ + alt (node.extract_mac()) { + some(mac_aq(sp, e)) { + cx.gather += [{lo: sp.lo - cx.lo, hi: sp.hi - cx.lo, + e: e, constr: constr}]; + } + _ {node.visit(cx, v);} + } +} +// FIXME: these are only here because I (kevina) couldn't figure out how to +// get bind to work in gather_anti_quotes +fn visit_aq_expr(node: @ast::expr, &&cx: aq_ctxt, v: vt<aq_ctxt>) { + visit_aq(node,"from_expr",cx,v); +} +fn visit_aq_ty(node: @ast::ty, &&cx: aq_ctxt, v: vt<aq_ctxt>) { + visit_aq(node,"from_ty",cx,v); +} + +fn is_space(c: char) -> bool { + syntax::parse::lexer::is_whitespace(c) +} + +fn expand_ast(ecx: ext_ctxt, _sp: span, + arg: ast::mac_arg, body: ast::mac_body) + -> @ast::expr +{ + let what = "expr"; + option::may(arg) {|arg| + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + ecx.span_fatal + (_sp, "#ast requires arguments of the form `[...]`.") + } + }; + if vec::len::<@ast::expr>(args) != 1u { + ecx.span_fatal(_sp, "#ast requires exactly one arg"); + } + alt (args[0].node) { + ast::expr_path(@{node: {idents: id, _},_}) if vec::len(id) == 1u + {what = id[0]} + _ {ecx.span_fatal(args[0].span, "expected an identifier");} + } + } + let body = get_mac_body(ecx,_sp,body); + + ret alt what { + "crate" {finish(ecx, body, parse_crate)} + "expr" {finish(ecx, body, parser::parse_expr)} + "ty" {finish(ecx, body, parse_ty)} + "item" {finish(ecx, body, parse_item)} + "stmt" {finish(ecx, body, parse_stmt)} + "pat" {finish(ecx, body, parser::parse_pat)} + _ {ecx.span_fatal(_sp, "unsupported ast type")} + }; +} + +fn parse_crate(p: parser) -> @ast::crate { + parser::parse_crate_mod(p, []) +} + +fn parse_ty(p: parser) -> @ast::ty { + parser::parse_ty(p, false) +} + +fn parse_stmt(p: parser) -> @ast::stmt { + parser::parse_stmt(p, []) +} + +fn parse_item(p: parser) -> @ast::item { + alt (parser::parse_item(p, [])) { + some(item) {item} + none {fail; /* FIXME: Error message, somehow */} + } +} + +fn finish<T: qq_helper> + (ecx: ext_ctxt, body: ast::mac_body_, f: fn (p: parser) -> T) + -> @ast::expr +{ + let cm = ecx.session().parse_sess.cm; + let str = @codemap::span_to_snippet(body.span, cm); + let fname = codemap::mk_substr_filename(cm, body.span); + let node = parse_from_source_str + (f, fname, codemap::fss_internal(body.span), str, + ecx.session().opts.cfg, ecx.session().parse_sess); + let loc = codemap::lookup_char_pos(cm, body.span.lo); + + let sp = node.span(); + let qcx = gather_anti_quotes(sp.lo, node); + let cx = qcx; + + // assert that the vector is sorted by position: + uint::range(1u, vec::len(cx.gather)) {|i| + assert cx.gather[i-1u].lo < cx.gather[i].lo; + } + + let str2 = ""; + enum state {active, skip(uint), blank}; + let state = active; + let i = 0u, j = 0u; + let g_len = vec::len(cx.gather); + str::chars_iter(*str) {|ch| + if (j < g_len && i == cx.gather[j].lo) { + assert ch == '$'; + let repl = #fmt("$%u ", j); + state = skip(str::char_len(repl)); + str2 += repl; + } + alt state { + active {str::push_char(str2, ch);} + skip(1u) {state = blank;} + skip(sk) {state = skip (sk-1u);} + blank if is_space(ch) {str::push_char(str2, ch);} + blank {str::push_char(str2, ' ');} + } + i += 1u; + if (j < g_len && i == cx.gather[j].hi) { + assert ch == ')'; + state = active; + j += 1u; + } + } + + let cx = ecx; + let session_call = bind mk_call_(cx,sp, + mk_access(cx,sp,["ext_cx"], "session"), + []); + let pcall = mk_call(cx,sp, + ["syntax", "parse", "parser", + "parse_from_source_str"], + [node.mk_parse_fn(cx,sp), + mk_str(cx,sp, fname), + mk_call(cx,sp, + ["syntax","ext","qquote", "mk_file_substr"], + [mk_str(cx,sp, loc.file.name), + mk_uint(cx,sp, loc.line), + mk_uint(cx,sp, loc.col)]), + mk_unary(cx,sp, ast::box(ast::m_imm), + mk_str(cx,sp, str2)), + mk_access_(cx,sp, + mk_access_(cx,sp, session_call(), "opts"), + "cfg"), + mk_access_(cx,sp, session_call(), "parse_sess")] + ); + let rcall = pcall; + if (g_len > 0u) { + rcall = mk_call(cx,sp, + ["syntax", "ext", "qquote", "replace"], + [pcall, + mk_vec_e(cx,sp, vec::map(copy qcx.gather) {|g| + mk_call(cx,sp, + ["syntax", "ext", "qquote", g.constr], + [g.e])}), + mk_path(cx,sp, + ["syntax", "ext", "qquote", + node.get_fold_fn()])]); + } + ret rcall; +} + +fn replace<T>(node: T, repls: [fragment], ff: fn (ast_fold, T) -> T) + -> T +{ + let aft = default_ast_fold(); + let f_pre = {fold_expr: bind replace_expr(repls, _, _, _, + aft.fold_expr), + fold_ty: bind replace_ty(repls, _, _, _, + aft.fold_ty) + with *aft}; + ret ff(make_fold(f_pre), node); +} +fn fold_crate(f: ast_fold, &&n: @ast::crate) -> @ast::crate { + @f.fold_crate(*n) +} +fn fold_expr(f: ast_fold, &&n: @ast::expr) -> @ast::expr {f.fold_expr(n)} +fn fold_ty(f: ast_fold, &&n: @ast::ty) -> @ast::ty {f.fold_ty(n)} +fn fold_item(f: ast_fold, &&n: @ast::item) -> @ast::item {f.fold_item(n)} +fn fold_stmt(f: ast_fold, &&n: @ast::stmt) -> @ast::stmt {f.fold_stmt(n)} +fn fold_pat(f: ast_fold, &&n: @ast::pat) -> @ast::pat {f.fold_pat(n)} + +fn replace_expr(repls: [fragment], + e: ast::expr_, s: span, fld: ast_fold, + orig: fn@(ast::expr_, span, ast_fold)->(ast::expr_, span)) + -> (ast::expr_, span) +{ + alt e { + ast::expr_mac({node: mac_var(i), _}) { + alt (repls[i]) { + from_expr(r) {(r.node, r.span)} + _ {fail /* fixme error message */}}} + _ {orig(e,s,fld)} + } +} + +fn replace_ty(repls: [fragment], + e: ast::ty_, s: span, fld: ast_fold, + orig: fn@(ast::ty_, span, ast_fold)->(ast::ty_, span)) + -> (ast::ty_, span) +{ + alt e { + ast::ty_mac({node: mac_var(i), _}) { + alt (repls[i]) { + from_ty(r) {(r.node, r.span)} + _ {fail /* fixme error message */}}} + _ {orig(e,s,fld)} + } +} + +fn print_expr(expr: @ast::expr) { + let stdout = std::io::stdout(); + let pp = pprust::rust_printer(stdout); + pprust::print_expr(pp, expr); + pp::eof(pp.s); + stdout.write_str("\n"); +} + +fn mk_file_substr(fname: str, line: uint, col: uint) -> codemap::file_substr { + codemap::fss_external({filename: fname, line: line, col: col}) +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/syntax/ext/simplext.rs b/src/rustc/syntax/ext/simplext.rs new file mode 100644 index 00000000000..1baf5f97d20 --- /dev/null +++ b/src/rustc/syntax/ext/simplext.rs @@ -0,0 +1,779 @@ +use std; + +import codemap::span; +import std::map::{hashmap, new_str_hash}; +import driver::session::session; + +import base::*; + +import fold::*; +import ast_util::respan; +import ast::{ident, path, ty, blk_, expr, path_, expr_path, + expr_vec, expr_mac, mac_invoc, node_id}; + +export add_new_extension; + +fn path_to_ident(pth: @path) -> option<ident> { + if vec::len(pth.node.idents) == 1u && vec::len(pth.node.types) == 0u { + ret some(pth.node.idents[0u]); + } + ret none; +} + +//a vec of binders might be a little big. +type clause = {params: binders, body: @expr}; + +/* logically, an arb_depth should contain only one kind of matchable */ +enum arb_depth<T> { leaf(T), seq(@[arb_depth<T>], span), } + + +enum matchable { + match_expr(@expr), + match_path(@path), + match_ident(ast::spanned<ident>), + match_ty(@ty), + match_block(ast::blk), + match_exact, /* don't bind anything, just verify the AST traversal */ +} + +/* for when given an incompatible bit of AST */ +fn match_error(cx: ext_ctxt, m: matchable, expected: str) -> ! { + alt m { + match_expr(x) { + cx.span_fatal(x.span, + "this argument is an expr, expected " + expected); + } + match_path(x) { + cx.span_fatal(x.span, + "this argument is a path, expected " + expected); + } + match_ident(x) { + cx.span_fatal(x.span, + "this argument is an ident, expected " + expected); + } + match_ty(x) { + cx.span_fatal(x.span, + "this argument is a type, expected " + expected); + } + match_block(x) { + cx.span_fatal(x.span, + "this argument is a block, expected " + expected); + } + match_exact { cx.bug("what is a match_exact doing in a bindings?"); } + } +} + +// We can't make all the matchables in a match_result the same type because +// idents can be paths, which can be exprs. + +// If we want better match failure error messages (like in Fortifying Syntax), +// we'll want to return something indicating amount of progress and location +// of failure instead of `none`. +type match_result = option<arb_depth<matchable>>; +type selector = fn@(matchable) -> match_result; + +fn elts_to_ell(cx: ext_ctxt, elts: [@expr]) -> + {pre: [@expr], rep: option<@expr>, post: [@expr]} { + let idx: uint = 0u; + let res = none; + for elt: @expr in elts { + alt elt.node { + expr_mac(m) { + alt m.node { + ast::mac_ellipsis { + if res != none { + cx.span_fatal(m.span, "only one ellipsis allowed"); + } + res = + some({pre: vec::slice(elts, 0u, idx - 1u), + rep: some(elts[idx - 1u]), + post: vec::slice(elts, idx + 1u, vec::len(elts))}); + } + _ { } + } + } + _ { } + } + idx += 1u; + } + ret alt res { + some(val) { val } + none { {pre: elts, rep: none, post: []} } + } +} + +fn option_flatten_map<T: copy, U: copy>(f: fn@(T) -> option<U>, v: [T]) -> + option<[U]> { + let res = []; + for elem: T in v { + alt f(elem) { none { ret none; } some(fv) { res += [fv]; } } + } + ret some(res); +} + +fn a_d_map(ad: arb_depth<matchable>, f: selector) -> match_result { + alt ad { + leaf(x) { ret f(x); } + seq(ads, span) { + alt option_flatten_map(bind a_d_map(_, f), *ads) { + none { ret none; } + some(ts) { ret some(seq(@ts, span)); } + } + } + } +} + +fn compose_sels(s1: selector, s2: selector) -> selector { + fn scomp(s1: selector, s2: selector, m: matchable) -> match_result { + ret alt s1(m) { + none { none } + some(matches) { a_d_map(matches, s2) } + } + } + ret bind scomp(s1, s2, _); +} + + + +type binders = + {real_binders: hashmap<ident, selector>, + mutable literal_ast_matchers: [selector]}; +type bindings = hashmap<ident, arb_depth<matchable>>; + +fn acumm_bindings(_cx: ext_ctxt, _b_dest: bindings, _b_src: bindings) { } + +/* these three functions are the big moving parts */ + +/* create the selectors needed to bind and verify the pattern */ + +fn pattern_to_selectors(cx: ext_ctxt, e: @expr) -> binders { + let res: binders = + {real_binders: new_str_hash::<selector>(), + mutable literal_ast_matchers: []}; + //this oughta return binders instead, but macro args are a sequence of + //expressions, rather than a single expression + fn trivial_selector(m: matchable) -> match_result { ret some(leaf(m)); } + p_t_s_rec(cx, match_expr(e), trivial_selector, res); + ret res; +} + + + +/* use the selectors on the actual arguments to the macro to extract +bindings. Most of the work is done in p_t_s, which generates the +selectors. */ + +fn use_selectors_to_bind(b: binders, e: @expr) -> option<bindings> { + let res = new_str_hash::<arb_depth<matchable>>(); + //need to do this first, to check vec lengths. + for sel: selector in b.literal_ast_matchers { + alt sel(match_expr(e)) { none { ret none; } _ { } } + } + let never_mind: bool = false; + b.real_binders.items {|key, val| + alt val(match_expr(e)) { + none { never_mind = true; } + some(mtc) { res.insert(key, mtc); } + } + }; + //HACK: `ret` doesn't work in `for each` + if never_mind { ret none; } + ret some(res); +} + +/* use the bindings on the body to generate the expanded code */ + +fn transcribe(cx: ext_ctxt, b: bindings, body: @expr) -> @expr { + let idx_path: @mutable [uint] = @mutable []; + fn new_id(_old: node_id, cx: ext_ctxt) -> node_id { ret cx.next_id(); } + fn new_span(cx: ext_ctxt, sp: span) -> span { + /* this discards information in the case of macro-defining macros */ + ret {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()}; + } + let afp = default_ast_fold(); + let f_pre = + {fold_ident: bind transcribe_ident(cx, b, idx_path, _, _), + fold_path: bind transcribe_path(cx, b, idx_path, _, _, _), + fold_expr: + bind transcribe_expr(cx, b, idx_path, _, _, _, afp.fold_expr), + fold_ty: bind transcribe_type(cx, b, idx_path, _, _, _, afp.fold_ty), + fold_block: + bind transcribe_block(cx, b, idx_path, _, _, _, afp.fold_block), + map_exprs: bind transcribe_exprs(cx, b, idx_path, _, _), + new_id: bind new_id(_, cx) + with *afp}; + let f = make_fold(f_pre); + let result = f.fold_expr(body); + ret result; +} + + +/* helper: descend into a matcher */ +fn follow(m: arb_depth<matchable>, idx_path: @mutable [uint]) -> + arb_depth<matchable> { + let res: arb_depth<matchable> = m; + for idx: uint in *idx_path { + alt res { + leaf(_) { ret res;/* end of the line */ } + seq(new_ms, _) { res = new_ms[idx]; } + } + } + ret res; +} + +fn follow_for_trans(cx: ext_ctxt, mmaybe: option<arb_depth<matchable>>, + idx_path: @mutable [uint]) -> option<matchable> { + alt mmaybe { + none { ret none } + some(m) { + ret alt follow(m, idx_path) { + seq(_, sp) { + cx.span_fatal(sp, + "syntax matched under ... but not " + + "used that way.") + } + leaf(m) { ret some(m) } + } + } + } + +} + +/* helper for transcribe_exprs: what vars from `b` occur in `e`? */ +fn free_vars(b: bindings, e: @expr, it: fn(ident)) { + let idents: hashmap<ident, ()> = new_str_hash::<()>(); + fn mark_ident(&&i: ident, _fld: ast_fold, b: bindings, + idents: hashmap<ident, ()>) -> ident { + if b.contains_key(i) { idents.insert(i, ()); } + ret i; + } + // using fold is a hack: we want visit, but it doesn't hit idents ) : + // solve this with macros + let f_pre = + {fold_ident: bind mark_ident(_, _, b, idents) + with *default_ast_fold()}; + let f = make_fold(f_pre); + f.fold_expr(e); // ignore result + idents.keys {|x| it(x); }; +} + + +/* handle sequences (anywhere in the AST) of exprs, either real or ...ed */ +fn transcribe_exprs(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + recur: fn@(&&@expr) -> @expr, exprs: [@expr]) -> [@expr] { + alt elts_to_ell(cx, exprs) { + {pre: pre, rep: repeat_me_maybe, post: post} { + let res = vec::map(pre, recur); + alt repeat_me_maybe { + none { } + some(repeat_me) { + let repeat: option<{rep_count: uint, name: ident}> = none; + /* we need to walk over all the free vars in lockstep, except for + the leaves, which are just duplicated */ + free_vars(b, repeat_me) {|fv| + let cur_pos = follow(b.get(fv), idx_path); + alt cur_pos { + leaf(_) { } + seq(ms, _) { + alt repeat { + none { + repeat = some({rep_count: vec::len(*ms), name: fv}); + } + some({rep_count: old_len, name: old_name}) { + let len = vec::len(*ms); + if old_len != len { + let msg = + #fmt["'%s' occurs %u times, but ", fv, len] + + #fmt["'%s' occurs %u times", old_name, + old_len]; + cx.span_fatal(repeat_me.span, msg); + } + } + } + } + } + }; + alt repeat { + none { + cx.span_fatal(repeat_me.span, + "'...' surrounds an expression without any" + + " repeating syntax variables"); + } + some({rep_count: rc, _}) { + /* Whew, we now know how how many times to repeat */ + let idx: uint = 0u; + while idx < rc { + *idx_path += [idx]; + res += [recur(repeat_me)]; // whew! + vec::pop(*idx_path); + idx += 1u; + } + } + } + } + } + res += vec::map(post, recur); + ret res; + } + } +} + + + +// substitute, in a position that's required to be an ident +fn transcribe_ident(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + &&i: ident, _fld: ast_fold) -> ident { + ret alt follow_for_trans(cx, b.find(i), idx_path) { + some(match_ident(a_id)) { a_id.node } + some(m) { match_error(cx, m, "an identifier") } + none { i } + } +} + + +fn transcribe_path(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + p: path_, s:span, _fld: ast_fold) -> (path_, span) { + // Don't substitute into qualified names. + if vec::len(p.types) > 0u || vec::len(p.idents) != 1u { ret (p, s); } + ret alt follow_for_trans(cx, b.find(p.idents[0]), idx_path) { + some(match_ident(id)) { + ({global: false, idents: [id.node], types: []}, id.span) + } + some(match_path(a_pth)) { (a_pth.node, a_pth.span) } + some(m) { match_error(cx, m, "a path") } + none { (p, s) } + } +} + + +fn transcribe_expr(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + e: ast::expr_, s: span, fld: ast_fold, + orig: fn@(ast::expr_, span, ast_fold)->(ast::expr_, span)) + -> (ast::expr_, span) +{ + ret alt e { + expr_path(p) { + // Don't substitute into qualified names. + if vec::len(p.node.types) > 0u || vec::len(p.node.idents) != 1u { + (e, s); + } + alt follow_for_trans(cx, b.find(p.node.idents[0]), idx_path) { + some(match_ident(id)) { + (expr_path(@respan(id.span, + {global: false, + idents: [id.node], + types: []})), id.span) + } + some(match_path(a_pth)) { (expr_path(a_pth), s) } + some(match_expr(a_exp)) { (a_exp.node, a_exp.span) } + some(m) { match_error(cx, m, "an expression") } + none { orig(e, s, fld) } + } + } + _ { orig(e, s, fld) } + } +} + +fn transcribe_type(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + t: ast::ty_, s: span, fld: ast_fold, + orig: fn@(ast::ty_, span, ast_fold) -> (ast::ty_, span)) + -> (ast::ty_, span) +{ + ret alt t { + ast::ty_path(pth, _) { + alt path_to_ident(pth) { + some(id) { + alt follow_for_trans(cx, b.find(id), idx_path) { + some(match_ty(ty)) { (ty.node, ty.span) } + some(m) { match_error(cx, m, "a type") } + none { orig(t, s, fld) } + } + } + none { orig(t, s, fld) } + } + } + _ { orig(t, s, fld) } + } +} + + +/* for parsing reasons, syntax variables bound to blocks must be used like +`{v}` */ + +fn transcribe_block(cx: ext_ctxt, b: bindings, idx_path: @mutable [uint], + blk: blk_, s: span, fld: ast_fold, + orig: fn@(blk_, span, ast_fold) -> (blk_, span)) + -> (blk_, span) +{ + ret alt block_to_ident(blk) { + some(id) { + alt follow_for_trans(cx, b.find(id), idx_path) { + some(match_block(new_blk)) { (new_blk.node, new_blk.span) } + + + + + + // possibly allow promotion of ident/path/expr to blocks? + some(m) { + match_error(cx, m, "a block") + } + none { orig(blk, s, fld) } + } + } + none { orig(blk, s, fld) } + } +} + + +/* traverse the pattern, building instructions on how to bind the actual +argument. ps accumulates instructions on navigating the tree.*/ +fn p_t_s_rec(cx: ext_ctxt, m: matchable, s: selector, b: binders) { + + //it might be possible to traverse only exprs, not matchables + alt m { + match_expr(e) { + alt e.node { + expr_path(p_pth) { p_t_s_r_path(cx, p_pth, s, b); } + expr_vec(p_elts, _) { + alt elts_to_ell(cx, p_elts) { + {pre: pre, rep: some(repeat_me), post: post} { + p_t_s_r_length(cx, vec::len(pre) + vec::len(post), true, s, + b); + if vec::len(pre) > 0u { + p_t_s_r_actual_vector(cx, pre, true, s, b); + } + p_t_s_r_ellipses(cx, repeat_me, vec::len(pre), s, b); + + if vec::len(post) > 0u { + cx.span_unimpl(e.span, + "matching after `...` not yet supported"); + } + } + {pre: pre, rep: none, post: post} { + if post != [] { + cx.bug("elts_to_ell provided an invalid result"); + } + p_t_s_r_length(cx, vec::len(pre), false, s, b); + p_t_s_r_actual_vector(cx, pre, false, s, b); + } + } + } + /* FIXME: handle embedded types and blocks, at least */ + expr_mac(mac) { + p_t_s_r_mac(cx, mac, s, b); + } + _ { + fn select(cx: ext_ctxt, m: matchable, pat: @expr) -> + match_result { + ret alt m { + match_expr(e) { + if e == pat { some(leaf(match_exact)) } else { none } + } + _ { cx.bug("broken traversal in p_t_s_r") } + } + } + b.literal_ast_matchers += [bind select(cx, _, e)]; + } + } + } + _ { + cx.session().bug("undocumented invariant in p_t_s_rec"); + } + } +} + + +/* make a match more precise */ +fn specialize_match(m: matchable) -> matchable { + ret alt m { + match_expr(e) { + alt e.node { + expr_path(pth) { + alt path_to_ident(pth) { + some(id) { match_ident(respan(pth.span, id)) } + none { match_path(pth) } + } + } + _ { m } + } + } + _ { m } + } +} + +/* pattern_to_selectors helper functions */ +fn p_t_s_r_path(cx: ext_ctxt, p: @path, s: selector, b: binders) { + alt path_to_ident(p) { + some(p_id) { + fn select(cx: ext_ctxt, m: matchable) -> match_result { + ret alt m { + match_expr(e) { some(leaf(specialize_match(m))) } + _ { cx.bug("broken traversal in p_t_s_r") } + } + } + if b.real_binders.contains_key(p_id) { + cx.span_fatal(p.span, "duplicate binding identifier"); + } + b.real_binders.insert(p_id, compose_sels(s, bind select(cx, _))); + } + none { } + } +} + +fn block_to_ident(blk: blk_) -> option<ident> { + if vec::len(blk.stmts) != 0u { ret none; } + ret alt blk.expr { + some(expr) { + alt expr.node { expr_path(pth) { path_to_ident(pth) } _ { none } } + } + none { none } + } +} + +fn p_t_s_r_mac(cx: ext_ctxt, mac: ast::mac, s: selector, b: binders) { + fn select_pt_1(cx: ext_ctxt, m: matchable, + fn_m: fn(ast::mac) -> match_result) -> match_result { + ret alt m { + match_expr(e) { + alt e.node { expr_mac(mac) { fn_m(mac) } _ { none } } + } + _ { cx.bug("broken traversal in p_t_s_r") } + } + } + fn no_des(cx: ext_ctxt, sp: span, syn: str) -> ! { + cx.span_fatal(sp, "destructuring " + syn + " is not yet supported"); + } + alt mac.node { + ast::mac_ellipsis { cx.span_fatal(mac.span, "misused `...`"); } + ast::mac_invoc(_, _, _) { no_des(cx, mac.span, "macro calls"); } + ast::mac_embed_type(ty) { + alt ty.node { + ast::ty_path(pth, _) { + alt path_to_ident(pth) { + some(id) { + /* look for an embedded type */ + fn select_pt_2(m: ast::mac) -> match_result { + ret alt m.node { + ast::mac_embed_type(t) { some(leaf(match_ty(t))) } + _ { none } + } + } + let final_step = bind select_pt_1(cx, _, select_pt_2); + b.real_binders.insert(id, compose_sels(s, final_step)); + } + none { no_des(cx, pth.span, "under `#<>`"); } + } + } + _ { no_des(cx, ty.span, "under `#<>`"); } + } + } + ast::mac_embed_block(blk) { + alt block_to_ident(blk.node) { + some(id) { + fn select_pt_2(m: ast::mac) -> match_result { + ret alt m.node { + ast::mac_embed_block(blk) { + some(leaf(match_block(blk))) + } + _ { none } + } + } + let final_step = bind select_pt_1(cx, _, select_pt_2); + b.real_binders.insert(id, compose_sels(s, final_step)); + } + none { no_des(cx, blk.span, "under `#{}`"); } + } + } + ast::mac_aq(_,_) { no_des(cx, mac.span, "antiquotes"); } + ast::mac_var(_) { no_des(cx, mac.span, "antiquote variables"); } + } +} + +fn p_t_s_r_ellipses(cx: ext_ctxt, repeat_me: @expr, offset: uint, s: selector, + b: binders) { + fn select(cx: ext_ctxt, repeat_me: @expr, offset: uint, m: matchable) -> + match_result { + ret alt m { + match_expr(e) { + alt e.node { + expr_vec(arg_elts, _) { + let elts = []; + let idx = offset; + while idx < vec::len(arg_elts) { + elts += [leaf(match_expr(arg_elts[idx]))]; + idx += 1u; + } + + // using repeat_me.span is a little wacky, but the + // error we want to report is one in the macro def + some(seq(@elts, repeat_me.span)) + } + _ { none } + } + } + _ { cx.bug("broken traversal in p_t_s_r") } + } + } + p_t_s_rec(cx, match_expr(repeat_me), + compose_sels(s, bind select(cx, repeat_me, offset, _)), b); +} + + +fn p_t_s_r_length(cx: ext_ctxt, len: uint, at_least: bool, s: selector, + b: binders) { + fn len_select(_cx: ext_ctxt, m: matchable, at_least: bool, len: uint) -> + match_result { + ret alt m { + match_expr(e) { + alt e.node { + expr_vec(arg_elts, _) { + let actual_len = vec::len(arg_elts); + if at_least && actual_len >= len || actual_len == len { + some(leaf(match_exact)) + } else { none } + } + _ { none } + } + } + _ { none } + } + } + b.literal_ast_matchers += + [compose_sels(s, bind len_select(cx, _, at_least, len))]; +} + +fn p_t_s_r_actual_vector(cx: ext_ctxt, elts: [@expr], _repeat_after: bool, + s: selector, b: binders) { + let idx: uint = 0u; + while idx < vec::len(elts) { + fn select(cx: ext_ctxt, m: matchable, idx: uint) -> match_result { + ret alt m { + match_expr(e) { + alt e.node { + expr_vec(arg_elts, _) { + some(leaf(match_expr(arg_elts[idx]))) + } + _ { none } + } + } + _ { cx.bug("broken traversal in p_t_s_r") } + } + } + p_t_s_rec(cx, match_expr(elts[idx]), + compose_sels(s, bind select(cx, _, idx)), b); + idx += 1u; + } +} + +fn add_new_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, + _body: ast::mac_body) -> base::macro_def { + let arg = get_mac_arg(cx,sp,arg); + let args: [@ast::expr] = + alt arg.node { + ast::expr_vec(elts, _) { elts } + _ { + cx.span_fatal(sp, + "#macro requires arguments of the form `[...]`.") + } + }; + + let macro_name: option<str> = none; + let clauses: [@clause] = []; + for arg: @expr in args { + alt arg.node { + expr_vec(elts, mutbl) { + if vec::len(elts) != 2u { + cx.span_fatal((*arg).span, + "extension clause must consist of [" + + "macro invocation, expansion body]"); + } + + + alt elts[0u].node { + expr_mac(mac) { + alt mac.node { + mac_invoc(pth, invoc_arg, body) { + alt path_to_ident(pth) { + some(id) { + alt macro_name { + none { macro_name = some(id); } + some(other_id) { + if id != other_id { + cx.span_fatal(pth.span, + "macro name must be " + + "consistent"); + } + } + } + } + none { + cx.span_fatal(pth.span, + "macro name must not be a path"); + } + } + clauses += + [@{params: pattern_to_selectors + (cx, get_mac_arg(cx,mac.span,invoc_arg)), + body: elts[1u]}]; + + // FIXME: check duplicates (or just simplify + // the macro arg situation) + } + _ { + cx.span_bug(mac.span, "undocumented invariant in \ + add_extension"); + } + } + } + _ { + cx.span_fatal(elts[0u].span, + "extension clause must" + + " start with a macro invocation."); + } + } + } + _ { + cx.span_fatal((*arg).span, + "extension must be [clause, " + " ...]"); + } + } + } + + let ext = bind generic_extension(_, _, _, _, clauses); + + ret {ident: + alt macro_name { + some(id) { id } + none { + cx.span_fatal(sp, + "macro definition must have " + + "at least one clause") + } + }, + ext: normal({expander: ext, span: some(arg.span)})}; + + fn generic_extension(cx: ext_ctxt, sp: span, arg: ast::mac_arg, + _body: ast::mac_body, clauses: [@clause]) -> @expr { + let arg = get_mac_arg(cx,sp,arg); + for c: @clause in clauses { + alt use_selectors_to_bind(c.params, arg) { + some(bindings) { ret transcribe(cx, bindings, c.body); } + none { cont; } + } + } + cx.span_fatal(sp, "no clauses match macro invocation"); + } +} + + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/fold.rs b/src/rustc/syntax/fold.rs new file mode 100644 index 00000000000..7ceae548769 --- /dev/null +++ b/src/rustc/syntax/fold.rs @@ -0,0 +1,761 @@ +import syntax::codemap::span; +import ast::*; + +export ast_fold_precursor; +export ast_fold; +export default_ast_fold; +export make_fold; +export noop_fold_crate; +export noop_fold_item; +export noop_fold_expr; +export noop_fold_pat; +export noop_fold_mod; +export noop_fold_ty; +export wrap; + +type ast_fold = @mutable a_f; + +// We may eventually want to be able to fold over type parameters, too + +type ast_fold_precursor = + //unlike the others, item_ is non-trivial + {fold_crate: fn@(crate_, span, ast_fold) -> (crate_, span), + fold_crate_directive: fn@(crate_directive_, span, + ast_fold) -> (crate_directive_, span), + fold_view_item: fn@(view_item_, ast_fold) -> view_item_, + fold_native_item: fn@(&&@native_item, ast_fold) -> @native_item, + fold_item: fn@(&&@item, ast_fold) -> @item, + fold_class_item: fn@(&&@class_item, ast_fold) -> @class_item, + fold_item_underscore: fn@(item_, ast_fold) -> item_, + fold_method: fn@(&&@method, ast_fold) -> @method, + fold_block: fn@(blk_, span, ast_fold) -> (blk_, span), + fold_stmt: fn@(stmt_, span, ast_fold) -> (stmt_, span), + fold_arm: fn@(arm, ast_fold) -> arm, + fold_pat: fn@(pat_, span, ast_fold) -> (pat_, span), + fold_decl: fn@(decl_, span, ast_fold) -> (decl_, span), + fold_expr: fn@(expr_, span, ast_fold) -> (expr_, span), + fold_ty: fn@(ty_, span, ast_fold) -> (ty_, span), + fold_constr: fn@(ast::constr_, span, ast_fold) -> (constr_, span), + fold_mod: fn@(_mod, ast_fold) -> _mod, + fold_native_mod: fn@(native_mod, ast_fold) -> native_mod, + fold_variant: fn@(variant_, span, ast_fold) -> (variant_, span), + fold_ident: fn@(&&ident, ast_fold) -> ident, + fold_path: fn@(path_, span, ast_fold) -> (path_, span), + fold_local: fn@(local_, span, ast_fold) -> (local_, span), + map_exprs: fn@(fn@(&&@expr) -> @expr, [@expr]) -> [@expr], + new_id: fn@(node_id) -> node_id, + new_span: fn@(span) -> span}; + +type a_f = + {fold_crate: fn@(crate) -> crate, + fold_crate_directive: fn@(&&@crate_directive) -> @crate_directive, + fold_view_item: fn@(&&@view_item) -> @view_item, + fold_native_item: fn@(&&@native_item) -> @native_item, + fold_item: fn@(&&@item) -> @item, + fold_class_item: fn@(&&@class_item) -> @class_item, + fold_item_underscore: fn@(item_) -> item_, + fold_method: fn@(&&@method) -> @method, + fold_block: fn@(blk) -> blk, + fold_stmt: fn@(&&@stmt) -> @stmt, + fold_arm: fn@(arm) -> arm, + fold_pat: fn@(&&@pat) -> @pat, + fold_decl: fn@(&&@decl) -> @decl, + fold_expr: fn@(&&@expr) -> @expr, + fold_ty: fn@(&&@ty) -> @ty, + fold_constr: fn@(&&@constr) -> @constr, + fold_mod: fn@(_mod) -> _mod, + fold_native_mod: fn@(native_mod) -> native_mod, + fold_variant: fn@(variant) -> variant, + fold_ident: fn@(&&ident) -> ident, + fold_path: fn@(&&@path) -> @path, + fold_local: fn@(&&@local) -> @local, + map_exprs: fn@(fn@(&&@expr) -> @expr, [@expr]) -> [@expr], + new_id: fn@(node_id) -> node_id, + new_span: fn@(span) -> span}; + + +//fn nf_dummy<T>(&T node) -> T { fail; } +fn nf_crate_dummy(_c: crate) -> crate { fail; } +fn nf_crate_directive_dummy(&&_c: @crate_directive) -> @crate_directive { + fail; +} +fn nf_view_item_dummy(&&_v: @view_item) -> @view_item { fail; } +fn nf_native_item_dummy(&&_n: @native_item) -> @native_item { fail; } +fn nf_item_dummy(&&_i: @item) -> @item { fail; } +fn nf_class_item_dummy(&&_ci: @class_item) -> @class_item { fail; } +fn nf_item_underscore_dummy(_i: item_) -> item_ { fail; } +fn nf_method_dummy(&&_m: @method) -> @method { fail; } +fn nf_blk_dummy(_b: blk) -> blk { fail; } +fn nf_stmt_dummy(&&_s: @stmt) -> @stmt { fail; } +fn nf_arm_dummy(_a: arm) -> arm { fail; } +fn nf_pat_dummy(&&_p: @pat) -> @pat { fail; } +fn nf_decl_dummy(&&_d: @decl) -> @decl { fail; } +fn nf_expr_dummy(&&_e: @expr) -> @expr { fail; } +fn nf_ty_dummy(&&_t: @ty) -> @ty { fail; } +fn nf_constr_dummy(&&_c: @constr) -> @constr { fail; } +fn nf_mod_dummy(_m: _mod) -> _mod { fail; } +fn nf_native_mod_dummy(_n: native_mod) -> native_mod { fail; } +fn nf_variant_dummy(_v: variant) -> variant { fail; } +fn nf_ident_dummy(&&_i: ident) -> ident { fail; } +fn nf_path_dummy(&&_p: @path) -> @path { fail; } +fn nf_local_dummy(&&_o: @local) -> @local { fail; } + +/* some little folds that probably aren't useful to have in ast_fold itself*/ + +//used in noop_fold_item and noop_fold_crate and noop_fold_crate_directive +fn fold_meta_item_(&&mi: @meta_item, fld: ast_fold) -> @meta_item { + ret @{node: + alt mi.node { + meta_word(id) { meta_word(fld.fold_ident(id)) } + meta_list(id, mis) { + let fold_meta_item = bind fold_meta_item_(_, fld); + meta_list(id, vec::map(mis, fold_meta_item)) + } + meta_name_value(id, s) { + meta_name_value(fld.fold_ident(id), s) + } + }, + span: mi.span}; +} +//used in noop_fold_item and noop_fold_crate +fn fold_attribute_(at: attribute, fmi: fn@(&&@meta_item) -> @meta_item) -> + attribute { + ret {node: {style: at.node.style, value: *fmi(@at.node.value)}, + span: at.span}; +} +//used in noop_fold_native_item and noop_fold_fn_decl +fn fold_arg_(a: arg, fld: ast_fold) -> arg { + ret {mode: a.mode, + ty: fld.fold_ty(a.ty), + ident: fld.fold_ident(a.ident), + id: fld.new_id(a.id)}; +} +//used in noop_fold_expr, and possibly elsewhere in the future +fn fold_mac_(m: mac, fld: ast_fold) -> mac { + ret {node: + alt m.node { + mac_invoc(pth, arg, body) { + mac_invoc(fld.fold_path(pth), + // FIXME: bind should work, but causes a crash + option::map(arg) {|arg| fld.fold_expr(arg)}, + body) + } + mac_embed_type(ty) { mac_embed_type(fld.fold_ty(ty)) } + mac_embed_block(blk) { mac_embed_block(fld.fold_block(blk)) } + mac_ellipsis { mac_ellipsis } + mac_aq(_,_) { /* fixme */ m.node } + mac_var(_) { /* fixme */ m.node } + }, + span: m.span}; +} + +fn fold_fn_decl(decl: ast::fn_decl, fld: ast_fold) -> ast::fn_decl { + ret {inputs: vec::map(decl.inputs, bind fold_arg_(_, fld)), + output: fld.fold_ty(decl.output), + purity: decl.purity, + cf: decl.cf, + constraints: vec::map(decl.constraints, fld.fold_constr)} +} + +fn fold_ty_param_bound(tpb: ty_param_bound, fld: ast_fold) -> ty_param_bound { + alt tpb { + bound_copy | bound_send { tpb } + bound_iface(ty) { bound_iface(fld.fold_ty(ty)) } + } +} + +fn fold_ty_param(tp: ty_param, fld: ast_fold) -> ty_param { + {ident: tp.ident, + id: fld.new_id(tp.id), + bounds: @vec::map(*tp.bounds, fold_ty_param_bound(_, fld))} +} + +fn fold_ty_params(tps: [ty_param], fld: ast_fold) -> [ty_param] { + vec::map(tps, fold_ty_param(_, fld)) +} + +fn noop_fold_crate(c: crate_, fld: ast_fold) -> crate_ { + let fold_meta_item = bind fold_meta_item_(_, fld); + let fold_attribute = bind fold_attribute_(_, fold_meta_item); + + ret {directives: vec::map(c.directives, fld.fold_crate_directive), + module: fld.fold_mod(c.module), + attrs: vec::map(c.attrs, fold_attribute), + config: vec::map(c.config, fold_meta_item)}; +} + +fn noop_fold_crate_directive(cd: crate_directive_, fld: ast_fold) -> + crate_directive_ { + ret alt cd { + cdir_src_mod(id, attrs) { + cdir_src_mod(fld.fold_ident(id), attrs) + } + cdir_dir_mod(id, cds, attrs) { + cdir_dir_mod(fld.fold_ident(id), + vec::map(cds, fld.fold_crate_directive), attrs) + } + cdir_view_item(vi) { cdir_view_item(fld.fold_view_item(vi)) } + cdir_syntax(_) { cd } + } +} + +fn noop_fold_view_item(vi: view_item_, _fld: ast_fold) -> view_item_ { + ret vi; +} + + +fn noop_fold_native_item(&&ni: @native_item, fld: ast_fold) -> @native_item { + let fold_arg = bind fold_arg_(_, fld); + let fold_meta_item = bind fold_meta_item_(_, fld); + let fold_attribute = bind fold_attribute_(_, fold_meta_item); + + ret @{ident: fld.fold_ident(ni.ident), + attrs: vec::map(ni.attrs, fold_attribute), + node: + alt ni.node { + native_item_fn(fdec, typms) { + native_item_fn({inputs: vec::map(fdec.inputs, fold_arg), + output: fld.fold_ty(fdec.output), + purity: fdec.purity, + cf: fdec.cf, + constraints: + vec::map(fdec.constraints, + fld.fold_constr)}, + fold_ty_params(typms, fld)) + } + }, + id: fld.new_id(ni.id), + span: fld.new_span(ni.span)}; +} + +fn noop_fold_item(&&i: @item, fld: ast_fold) -> @item { + let fold_meta_item = bind fold_meta_item_(_, fld); + let fold_attribute = bind fold_attribute_(_, fold_meta_item); + + ret @{ident: fld.fold_ident(i.ident), + attrs: vec::map(i.attrs, fold_attribute), + id: fld.new_id(i.id), + node: fld.fold_item_underscore(i.node), + span: fld.new_span(i.span)}; +} + +fn noop_fold_class_item(&&ci: @class_item, fld: ast_fold) + -> @class_item { + @{node: { + privacy:ci.node.privacy, + decl: + alt ci.node.decl { + instance_var(ident, t, cm, id) { + instance_var(ident, fld.fold_ty(t), cm, id) + } + class_method(i) { class_method(fld.fold_item(i)) } + }}, + span: ci.span} +} + +fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { + ret alt i { + item_const(t, e) { item_const(fld.fold_ty(t), fld.fold_expr(e)) } + item_fn(decl, typms, body) { + item_fn(fold_fn_decl(decl, fld), + fold_ty_params(typms, fld), + fld.fold_block(body)) + } + item_mod(m) { item_mod(fld.fold_mod(m)) } + item_native_mod(nm) { item_native_mod(fld.fold_native_mod(nm)) } + item_ty(t, typms) { item_ty(fld.fold_ty(t), + fold_ty_params(typms, fld)) } + item_enum(variants, typms) { + item_enum(vec::map(variants, fld.fold_variant), + fold_ty_params(typms, fld)) + } + item_class(typms, items, id, ctor_decl, ctor_body) { + item_class(fold_ty_params(typms, fld), + vec::map(items, fld.fold_class_item), + id, + fold_fn_decl(ctor_decl, fld), + fld.fold_block(ctor_body)) + } + item_impl(tps, ifce, ty, methods) { + item_impl(tps, option::map(ifce, fld.fold_ty), fld.fold_ty(ty), + vec::map(methods, fld.fold_method)) + } + item_iface(tps, methods) { item_iface(tps, methods) } + item_res(decl, typms, body, did, cid) { + item_res(fold_fn_decl(decl, fld), + fold_ty_params(typms, fld), + fld.fold_block(body), + did, + cid) + } + }; +} + +fn noop_fold_method(&&m: @method, fld: ast_fold) -> @method { + ret @{ident: fld.fold_ident(m.ident), + attrs: m.attrs, + tps: fold_ty_params(m.tps, fld), + decl: fold_fn_decl(m.decl, fld), + body: fld.fold_block(m.body), + id: fld.new_id(m.id), + span: fld.new_span(m.span)}; +} + + +fn noop_fold_block(b: blk_, fld: ast_fold) -> blk_ { + ret {view_items: vec::map(b.view_items, fld.fold_view_item), + stmts: vec::map(b.stmts, fld.fold_stmt), + expr: option::map(b.expr, fld.fold_expr), + id: fld.new_id(b.id), + rules: b.rules}; +} + +fn noop_fold_stmt(s: stmt_, fld: ast_fold) -> stmt_ { + ret alt s { + stmt_decl(d, nid) { stmt_decl(fld.fold_decl(d), fld.new_id(nid)) } + stmt_expr(e, nid) { stmt_expr(fld.fold_expr(e), fld.new_id(nid)) } + stmt_semi(e, nid) { stmt_semi(fld.fold_expr(e), fld.new_id(nid)) } + }; +} + +fn noop_fold_arm(a: arm, fld: ast_fold) -> arm { + ret {pats: vec::map(a.pats, fld.fold_pat), + guard: option::map(a.guard, fld.fold_expr), + body: fld.fold_block(a.body)}; +} + +fn noop_fold_pat(p: pat_, fld: ast_fold) -> pat_ { + ret alt p { + pat_wild { p } + pat_ident(pth, sub) { + pat_ident(fld.fold_path(pth), option::map(sub, fld.fold_pat)) + } + pat_lit(_) { p } + pat_enum(pth, pats) { + pat_enum(fld.fold_path(pth), vec::map(pats, fld.fold_pat)) + } + pat_rec(fields, etc) { + let fs = []; + for f: ast::field_pat in fields { + fs += [{ident: f.ident, pat: fld.fold_pat(f.pat)}]; + } + pat_rec(fs, etc) + } + pat_tup(elts) { pat_tup(vec::map(elts, fld.fold_pat)) } + pat_box(inner) { pat_box(fld.fold_pat(inner)) } + pat_uniq(inner) { pat_uniq(fld.fold_pat(inner)) } + pat_range(_, _) { p } + }; +} + +fn noop_fold_decl(d: decl_, fld: ast_fold) -> decl_ { + alt d { + decl_local(ls) { decl_local(vec::map(ls, fld.fold_local)) } + decl_item(it) { decl_item(fld.fold_item(it)) } + } +} + +fn wrap<T>(f: fn@(T, ast_fold) -> T) + -> fn@(T, span, ast_fold) -> (T, span) +{ + ret fn@(x: T, s: span, fld: ast_fold) -> (T, span) { + (f(x, fld), s) + } +} + +fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { + fn fold_field_(field: field, fld: ast_fold) -> field { + ret {node: + {mutbl: field.node.mutbl, + ident: fld.fold_ident(field.node.ident), + expr: fld.fold_expr(field.node.expr)}, + span: field.span}; + } + let fold_field = bind fold_field_(_, fld); + + let fold_mac = bind fold_mac_(_, fld); + + ret alt e { + expr_vec(exprs, mutt) { + expr_vec(fld.map_exprs(fld.fold_expr, exprs), mutt) + } + expr_rec(fields, maybe_expr) { + expr_rec(vec::map(fields, fold_field), + option::map(maybe_expr, fld.fold_expr)) + } + expr_tup(elts) { expr_tup(vec::map(elts, fld.fold_expr)) } + expr_call(f, args, blk) { + expr_call(fld.fold_expr(f), fld.map_exprs(fld.fold_expr, args), + blk) + } + expr_bind(f, args) { + let opt_map_se = bind option::map(_, fld.fold_expr); + expr_bind(fld.fold_expr(f), vec::map(args, opt_map_se)) + } + expr_binary(binop, lhs, rhs) { + expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs)) + } + expr_unary(binop, ohs) { expr_unary(binop, fld.fold_expr(ohs)) } + expr_lit(_) { e } + expr_cast(expr, ty) { expr_cast(fld.fold_expr(expr), ty) } + expr_if(cond, tr, fl) { + expr_if(fld.fold_expr(cond), fld.fold_block(tr), + option::map(fl, fld.fold_expr)) + } + expr_while(cond, body) { + expr_while(fld.fold_expr(cond), fld.fold_block(body)) + } + expr_for(decl, expr, blk) { + expr_for(fld.fold_local(decl), fld.fold_expr(expr), + fld.fold_block(blk)) + } + expr_do_while(blk, expr) { + expr_do_while(fld.fold_block(blk), fld.fold_expr(expr)) + } + expr_alt(expr, arms, mode) { + expr_alt(fld.fold_expr(expr), vec::map(arms, fld.fold_arm), mode) + } + expr_fn(proto, decl, body, captures) { + expr_fn(proto, fold_fn_decl(decl, fld), + fld.fold_block(body), captures) + } + expr_fn_block(decl, body) { + expr_fn_block(fold_fn_decl(decl, fld), fld.fold_block(body)) + } + expr_block(blk) { expr_block(fld.fold_block(blk)) } + expr_move(el, er) { + expr_move(fld.fold_expr(el), fld.fold_expr(er)) + } + expr_copy(e) { expr_copy(fld.fold_expr(e)) } + expr_assign(el, er) { + expr_assign(fld.fold_expr(el), fld.fold_expr(er)) + } + expr_swap(el, er) { + expr_swap(fld.fold_expr(el), fld.fold_expr(er)) + } + expr_assign_op(op, el, er) { + expr_assign_op(op, fld.fold_expr(el), fld.fold_expr(er)) + } + expr_field(el, id, tys) { + expr_field(fld.fold_expr(el), fld.fold_ident(id), + vec::map(tys, fld.fold_ty)) + } + expr_index(el, er) { + expr_index(fld.fold_expr(el), fld.fold_expr(er)) + } + expr_path(pth) { expr_path(fld.fold_path(pth)) } + expr_fail(e) { expr_fail(option::map(e, fld.fold_expr)) } + expr_break | expr_cont { e } + expr_ret(e) { expr_ret(option::map(e, fld.fold_expr)) } + expr_be(e) { expr_be(fld.fold_expr(e)) } + expr_log(i, lv, e) { expr_log(i, fld.fold_expr(lv), + fld.fold_expr(e)) } + expr_assert(e) { expr_assert(fld.fold_expr(e)) } + expr_check(m, e) { expr_check(m, fld.fold_expr(e)) } + expr_if_check(cond, tr, fl) { + expr_if_check(fld.fold_expr(cond), fld.fold_block(tr), + option::map(fl, fld.fold_expr)) + } + expr_mac(mac) { expr_mac(fold_mac(mac)) } + } +} + +fn noop_fold_ty(t: ty_, fld: ast_fold) -> ty_ { + let fold_mac = bind fold_mac_(_, fld); + fn fold_mt(mt: mt, fld: ast_fold) -> mt { + {ty: fld.fold_ty(mt.ty), mutbl: mt.mutbl} + } + fn fold_field(f: ty_field, fld: ast_fold) -> ty_field { + {node: {ident: fld.fold_ident(f.node.ident), + mt: fold_mt(f.node.mt, fld)}, + span: fld.new_span(f.span)} + } + alt t { + ty_nil | ty_bot {t} + ty_box(mt) {ty_box(fold_mt(mt, fld))} + ty_uniq(mt) {ty_uniq(fold_mt(mt, fld))} + ty_vec(mt) {ty_vec(fold_mt(mt, fld))} + ty_ptr(mt) {ty_ptr(fold_mt(mt, fld))} + ty_rec(fields) {ty_rec(vec::map(fields) {|f| fold_field(f, fld)})} + ty_fn(proto, decl) {ty_fn(proto, fold_fn_decl(decl, fld))} + ty_tup(tys) {ty_tup(vec::map(tys) {|ty| fld.fold_ty(ty)})} + ty_path(path, id) {ty_path(fld.fold_path(path), fld.new_id(id))} + // FIXME: constrs likely needs to be folded... + ty_constr(ty, constrs) {ty_constr(fld.fold_ty(ty), constrs)} + ty_mac(mac) {ty_mac(fold_mac(mac))} + ty_infer {t} + } +} + +fn noop_fold_constr(c: constr_, fld: ast_fold) -> constr_ { + {path: fld.fold_path(c.path), args: c.args, id: fld.new_id(c.id)} +} + +// ...nor do modules +fn noop_fold_mod(m: _mod, fld: ast_fold) -> _mod { + ret {view_items: vec::map(m.view_items, fld.fold_view_item), + items: vec::map(m.items, fld.fold_item)}; +} + +fn noop_fold_native_mod(nm: native_mod, fld: ast_fold) -> native_mod { + ret {view_items: vec::map(nm.view_items, fld.fold_view_item), + items: vec::map(nm.items, fld.fold_native_item)} +} + +fn noop_fold_variant(v: variant_, fld: ast_fold) -> variant_ { + fn fold_variant_arg_(va: variant_arg, fld: ast_fold) -> variant_arg { + ret {ty: fld.fold_ty(va.ty), id: fld.new_id(va.id)}; + } + let fold_variant_arg = bind fold_variant_arg_(_, fld); + let args = vec::map(v.args, fold_variant_arg); + + let fold_meta_item = bind fold_meta_item_(_, fld); + let fold_attribute = bind fold_attribute_(_, fold_meta_item); + let attrs = vec::map(v.attrs, fold_attribute); + + let de = alt v.disr_expr { + some(e) {some(fld.fold_expr(e))} + none {none} + }; + ret {name: v.name, + attrs: attrs, + args: args, id: fld.new_id(v.id), + disr_expr: de}; +} + +fn noop_fold_ident(&&i: ident, _fld: ast_fold) -> ident { ret i; } + +fn noop_fold_path(&&p: path_, fld: ast_fold) -> path_ { + ret {global: p.global, + idents: vec::map(p.idents, fld.fold_ident), + types: vec::map(p.types, fld.fold_ty)}; +} + +fn noop_fold_local(l: local_, fld: ast_fold) -> local_ { + ret {is_mutbl: l.is_mutbl, + ty: fld.fold_ty(l.ty), + pat: fld.fold_pat(l.pat), + init: + alt l.init { + option::none::<initializer> { l.init } + option::some::<initializer>(init) { + option::some::<initializer>({op: init.op, + expr: fld.fold_expr(init.expr)}) + } + }, + id: fld.new_id(l.id)}; +} + +/* temporarily eta-expand because of a compiler bug with using `fn<T>` as a + value */ +fn noop_map_exprs(f: fn@(&&@expr) -> @expr, es: [@expr]) -> [@expr] { + ret vec::map(es, f); +} + +fn noop_id(i: node_id) -> node_id { ret i; } + +fn noop_span(sp: span) -> span { ret sp; } + + +fn default_ast_fold() -> @ast_fold_precursor { + ret @{fold_crate: wrap(noop_fold_crate), + fold_crate_directive: wrap(noop_fold_crate_directive), + fold_view_item: noop_fold_view_item, + fold_native_item: noop_fold_native_item, + fold_item: noop_fold_item, + fold_class_item: noop_fold_class_item, + fold_item_underscore: noop_fold_item_underscore, + fold_method: noop_fold_method, + fold_block: wrap(noop_fold_block), + fold_stmt: wrap(noop_fold_stmt), + fold_arm: noop_fold_arm, + fold_pat: wrap(noop_fold_pat), + fold_decl: wrap(noop_fold_decl), + fold_expr: wrap(noop_fold_expr), + fold_ty: wrap(noop_fold_ty), + fold_constr: wrap(noop_fold_constr), + fold_mod: noop_fold_mod, + fold_native_mod: noop_fold_native_mod, + fold_variant: wrap(noop_fold_variant), + fold_ident: noop_fold_ident, + fold_path: wrap(noop_fold_path), + fold_local: wrap(noop_fold_local), + map_exprs: noop_map_exprs, + new_id: noop_id, + new_span: noop_span}; +} + +fn make_fold(afp: ast_fold_precursor) -> ast_fold { + // FIXME: Have to bind all the bare functions into shared functions + // because @mutable is invariant with respect to its contents + let result: ast_fold = + @mutable {fold_crate: bind nf_crate_dummy(_), + fold_crate_directive: bind nf_crate_directive_dummy(_), + fold_view_item: bind nf_view_item_dummy(_), + fold_native_item: bind nf_native_item_dummy(_), + fold_item: bind nf_item_dummy(_), + fold_class_item: bind nf_class_item_dummy(_), + fold_item_underscore: bind nf_item_underscore_dummy(_), + fold_method: bind nf_method_dummy(_), + fold_block: bind nf_blk_dummy(_), + fold_stmt: bind nf_stmt_dummy(_), + fold_arm: bind nf_arm_dummy(_), + fold_pat: bind nf_pat_dummy(_), + fold_decl: bind nf_decl_dummy(_), + fold_expr: bind nf_expr_dummy(_), + fold_ty: bind nf_ty_dummy(_), + fold_constr: bind nf_constr_dummy(_), + fold_mod: bind nf_mod_dummy(_), + fold_native_mod: bind nf_native_mod_dummy(_), + fold_variant: bind nf_variant_dummy(_), + fold_ident: bind nf_ident_dummy(_), + fold_path: bind nf_path_dummy(_), + fold_local: bind nf_local_dummy(_), + map_exprs: bind noop_map_exprs(_, _), + new_id: bind noop_id(_), + new_span: bind noop_span(_)}; + + /* naturally, a macro to write these would be nice */ + fn f_crate(afp: ast_fold_precursor, f: ast_fold, c: crate) -> crate { + let (n, s) = afp.fold_crate(c.node, c.span, f); + ret {node: n, span: afp.new_span(s)}; + } + fn f_crate_directive(afp: ast_fold_precursor, f: ast_fold, + &&c: @crate_directive) -> @crate_directive { + let (n, s) = afp.fold_crate_directive(c.node, c.span, f); + ret @{node: n, + span: afp.new_span(s)}; + } + fn f_view_item(afp: ast_fold_precursor, f: ast_fold, &&x: @view_item) -> + @view_item { + ret @{node: afp.fold_view_item(x.node, f), + span: afp.new_span(x.span)}; + } + fn f_native_item(afp: ast_fold_precursor, f: ast_fold, &&x: @native_item) + -> @native_item { + ret afp.fold_native_item(x, f); + } + fn f_item(afp: ast_fold_precursor, f: ast_fold, &&i: @item) -> @item { + ret afp.fold_item(i, f); + } + fn f_class_item(afp: ast_fold_precursor, f: ast_fold, + &&ci: @class_item) -> @class_item { + @{node: + {privacy:ci.node.privacy, + decl: + alt ci.node.decl { + instance_var(nm, t, mt, id) { + instance_var(nm, f_ty(afp, f, t), + mt, id) + } + class_method(i) { + class_method(afp.fold_item(i, f)) + } + }}, span: afp.new_span(ci.span)} + } + fn f_item_underscore(afp: ast_fold_precursor, f: ast_fold, i: item_) -> + item_ { + ret afp.fold_item_underscore(i, f); + } + fn f_method(afp: ast_fold_precursor, f: ast_fold, &&x: @method) + -> @method { + ret afp.fold_method(x, f); + } + fn f_block(afp: ast_fold_precursor, f: ast_fold, x: blk) -> blk { + let (n, s) = afp.fold_block(x.node, x.span, f); + ret {node: n, span: afp.new_span(s)}; + } + fn f_stmt(afp: ast_fold_precursor, f: ast_fold, &&x: @stmt) -> @stmt { + let (n, s) = afp.fold_stmt(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + fn f_arm(afp: ast_fold_precursor, f: ast_fold, x: arm) -> arm { + ret afp.fold_arm(x, f); + } + fn f_pat(afp: ast_fold_precursor, f: ast_fold, &&x: @pat) -> @pat { + let (n, s) = afp.fold_pat(x.node, x.span, f); + ret @{id: afp.new_id(x.id), + node: n, + span: afp.new_span(s)}; + } + fn f_decl(afp: ast_fold_precursor, f: ast_fold, &&x: @decl) -> @decl { + let (n, s) = afp.fold_decl(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + fn f_expr(afp: ast_fold_precursor, f: ast_fold, &&x: @expr) -> @expr { + let (n, s) = afp.fold_expr(x.node, x.span, f); + ret @{id: afp.new_id(x.id), + node: n, + span: afp.new_span(s)}; + } + fn f_ty(afp: ast_fold_precursor, f: ast_fold, &&x: @ty) -> @ty { + let (n, s) = afp.fold_ty(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + fn f_constr(afp: ast_fold_precursor, f: ast_fold, &&x: @ast::constr) -> + @ast::constr { + let (n, s) = afp.fold_constr(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + fn f_mod(afp: ast_fold_precursor, f: ast_fold, x: _mod) -> _mod { + ret afp.fold_mod(x, f); + } + fn f_native_mod(afp: ast_fold_precursor, f: ast_fold, x: native_mod) -> + native_mod { + ret afp.fold_native_mod(x, f); + } + fn f_variant(afp: ast_fold_precursor, f: ast_fold, x: variant) -> + variant { + let (n, s) = afp.fold_variant(x.node, x.span, f); + ret {node: n, span: afp.new_span(s)}; + } + fn f_ident(afp: ast_fold_precursor, f: ast_fold, &&x: ident) -> ident { + ret afp.fold_ident(x, f); + } + fn f_path(afp: ast_fold_precursor, f: ast_fold, &&x: @path) -> @path { + let (n, s) = afp.fold_path(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + fn f_local(afp: ast_fold_precursor, f: ast_fold, &&x: @local) -> @local { + let (n, s) = afp.fold_local(x.node, x.span, f); + ret @{node: n, span: afp.new_span(s)}; + } + + *result = + {fold_crate: bind f_crate(afp, result, _), + fold_crate_directive: bind f_crate_directive(afp, result, _), + fold_view_item: bind f_view_item(afp, result, _), + fold_native_item: bind f_native_item(afp, result, _), + fold_item: bind f_item(afp, result, _), + fold_class_item: bind f_class_item(afp, result, _), + fold_item_underscore: bind f_item_underscore(afp, result, _), + fold_method: bind f_method(afp, result, _), + fold_block: bind f_block(afp, result, _), + fold_stmt: bind f_stmt(afp, result, _), + fold_arm: bind f_arm(afp, result, _), + fold_pat: bind f_pat(afp, result, _), + fold_decl: bind f_decl(afp, result, _), + fold_expr: bind f_expr(afp, result, _), + fold_ty: bind f_ty(afp, result, _), + fold_constr: bind f_constr(afp, result, _), + fold_mod: bind f_mod(afp, result, _), + fold_native_mod: bind f_native_mod(afp, result, _), + fold_variant: bind f_variant(afp, result, _), + fold_ident: bind f_ident(afp, result, _), + fold_path: bind f_path(afp, result, _), + fold_local: bind f_local(afp, result, _), + map_exprs: afp.map_exprs, + new_id: afp.new_id, + new_span: afp.new_span}; + ret result; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/parse/eval.rs b/src/rustc/syntax/parse/eval.rs new file mode 100644 index 00000000000..06f26905550 --- /dev/null +++ b/src/rustc/syntax/parse/eval.rs @@ -0,0 +1,150 @@ + +import front::attr; +import std::{io, fs}; +import syntax::ast; +import syntax::parse::token; +import syntax::parse::parser::{parser, new_parser_from_file, + parse_inner_attrs_and_next, + parse_mod_items, SOURCE_FILE}; + +export eval_crate_directives_to_mod; + +type ctx = + @{p: parser, + sess: parser::parse_sess, + cfg: ast::crate_cfg}; + +fn eval_crate_directives(cx: ctx, cdirs: [@ast::crate_directive], prefix: str, + &view_items: [@ast::view_item], + &items: [@ast::item]) { + for sub_cdir: @ast::crate_directive in cdirs { + eval_crate_directive(cx, sub_cdir, prefix, view_items, items); + } +} + +fn eval_crate_directives_to_mod(cx: ctx, cdirs: [@ast::crate_directive], + prefix: str, suffix: option<str>) + -> (ast::_mod, [ast::attribute]) { + #debug("eval crate prefix: %s", prefix); + #debug("eval crate suffix: %s", + option::from_maybe("none", suffix)); + let (cview_items, citems, cattrs) + = parse_companion_mod(cx, prefix, suffix); + let view_items: [@ast::view_item] = []; + let items: [@ast::item] = []; + eval_crate_directives(cx, cdirs, prefix, view_items, items); + ret ({view_items: view_items + cview_items, + items: items + citems}, + cattrs); +} + +/* +The 'companion mod'. So .rc crates and directory mod crate directives define +modules but not a .rs file to fill those mods with stuff. The companion mod is +a convention for location a .rs file to go with them. For .rc files the +companion mod is a .rs file with the same name; for directory mods the +companion mod is a .rs file with the same name as the directory. + +We build the path to the companion mod by combining the prefix and the +optional suffix then adding the .rs extension. +*/ +fn parse_companion_mod(cx: ctx, prefix: str, suffix: option<str>) + -> ([@ast::view_item], [@ast::item], [ast::attribute]) { + + fn companion_file(prefix: str, suffix: option<str>) -> str { + ret alt suffix { + option::some(s) { fs::connect(prefix, s) } + option::none { prefix } + } + ".rs"; + } + + fn file_exists(path: str) -> bool { + // Crude, but there's no lib function for this and I'm not + // up to writing it just now + alt io::file_reader(path) { + result::ok(_) { true } + result::err(_) { false } + } + } + + let modpath = companion_file(prefix, suffix); + #debug("looking for companion mod %s", modpath); + if file_exists(modpath) { + #debug("found companion mod"); + let p0 = new_parser_from_file(cx.sess, cx.cfg, modpath, + SOURCE_FILE); + let inner_attrs = parse_inner_attrs_and_next(p0); + let first_item_outer_attrs = inner_attrs.next; + let m0 = parse_mod_items(p0, token::EOF, first_item_outer_attrs); + cx.sess.chpos = p0.reader.chpos; + cx.sess.byte_pos = cx.sess.byte_pos + p0.reader.pos; + ret (m0.view_items, m0.items, inner_attrs.inner); + } else { + ret ([], [], []); + } +} + +fn cdir_path_opt(id: str, attrs: [ast::attribute]) -> str { + alt attr::get_meta_item_value_str_by_name(attrs, "path") { + some(d) { + ret d; + } + none { ret id; } + } +} + +fn eval_crate_directive(cx: ctx, cdir: @ast::crate_directive, prefix: str, + &view_items: [@ast::view_item], + &items: [@ast::item]) { + alt cdir.node { + ast::cdir_src_mod(id, attrs) { + let file_path = cdir_path_opt(id + ".rs", attrs); + let full_path = + if std::fs::path_is_absolute(file_path) { + file_path + } else { prefix + std::fs::path_sep() + file_path }; + let p0 = + new_parser_from_file(cx.sess, cx.cfg, full_path, SOURCE_FILE); + let inner_attrs = parse_inner_attrs_and_next(p0); + let mod_attrs = attrs + inner_attrs.inner; + let first_item_outer_attrs = inner_attrs.next; + let m0 = parse_mod_items(p0, token::EOF, first_item_outer_attrs); + + let i = + syntax::parse::parser::mk_item(p0, cdir.span.lo, cdir.span.hi, id, + ast::item_mod(m0), mod_attrs); + // Thread defids, chpos and byte_pos through the parsers + cx.sess.chpos = p0.reader.chpos; + cx.sess.byte_pos = cx.sess.byte_pos + p0.reader.pos; + items += [i]; + } + ast::cdir_dir_mod(id, cdirs, attrs) { + let path = cdir_path_opt(id, attrs); + let full_path = + if std::fs::path_is_absolute(path) { + path + } else { prefix + std::fs::path_sep() + path }; + let (m0, a0) = eval_crate_directives_to_mod( + cx, cdirs, full_path, none); + let i = + @{ident: id, + attrs: attrs + a0, + id: cx.sess.next_id, + node: ast::item_mod(m0), + span: cdir.span}; + cx.sess.next_id += 1; + items += [i]; + } + ast::cdir_view_item(vi) { view_items += [vi]; } + ast::cdir_syntax(pth) { } + } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/parse/lexer.rs b/src/rustc/syntax/parse/lexer.rs new file mode 100644 index 00000000000..3350d7a7c95 --- /dev/null +++ b/src/rustc/syntax/parse/lexer.rs @@ -0,0 +1,748 @@ + +import std::io; +import io::reader_util; +import util::interner; +import util::interner::intern; +import driver::diagnostic; + +type reader = @{ + cm: codemap::codemap, + span_diagnostic: diagnostic::span_handler, + src: @str, + len: uint, + mutable col: uint, + mutable pos: uint, + mutable curr: char, + mutable chpos: uint, + mutable strs: [str], + filemap: codemap::filemap, + interner: @interner::interner<str> +}; + +impl reader for reader { + fn is_eof() -> bool { self.curr == -1 as char } + fn get_str_from(start: uint) -> str unsafe { + // I'm pretty skeptical about this subtraction. What if there's a + // multi-byte character before the mark? + ret str::slice(*self.src, start - 1u, self.pos - 1u); + } + fn next() -> char { + if self.pos < self.len { + ret str::char_at(*self.src, self.pos); + } else { ret -1 as char; } + } + fn bump() { + if self.pos < self.len { + self.col += 1u; + self.chpos += 1u; + if self.curr == '\n' { + codemap::next_line(self.filemap, self.chpos, self.pos + + self.filemap.start_pos.byte); + self.col = 0u; + } + let next = str::char_range_at(*self.src, self.pos); + self.pos = next.next; + self.curr = next.ch; + } else { + if (self.curr != -1 as char) { + self.col += 1u; + self.chpos += 1u; + self.curr = -1 as char; + } + } + } + fn fatal(m: str) -> ! { + self.span_diagnostic.span_fatal( + ast_util::mk_sp(self.chpos, self.chpos), + m) + } +} + +fn new_reader(cm: codemap::codemap, + span_diagnostic: diagnostic::span_handler, + filemap: codemap::filemap, + itr: @interner::interner<str>) -> reader { + let r = @{cm: cm, + span_diagnostic: span_diagnostic, + src: filemap.src, len: str::len(*filemap.src), + mutable col: 0u, mutable pos: 0u, mutable curr: -1 as char, + mutable chpos: filemap.start_pos.ch, mutable strs: [], + filemap: filemap, interner: itr}; + if r.pos < r.len { + let next = str::char_range_at(*r.src, r.pos); + r.pos = next.next; + r.curr = next.ch; + } + ret r; +} + +fn dec_digit_val(c: char) -> int { ret (c as int) - ('0' as int); } + +fn hex_digit_val(c: char) -> int { + if in_range(c, '0', '9') { ret (c as int) - ('0' as int); } + if in_range(c, 'a', 'f') { ret (c as int) - ('a' as int) + 10; } + if in_range(c, 'A', 'F') { ret (c as int) - ('A' as int) + 10; } + fail; +} + +fn bin_digit_value(c: char) -> int { if c == '0' { ret 0; } ret 1; } + +fn is_whitespace(c: char) -> bool { + ret c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +fn may_begin_ident(c: char) -> bool { ret is_alpha(c) || c == '_'; } + +fn in_range(c: char, lo: char, hi: char) -> bool { ret lo <= c && c <= hi; } + +fn is_alpha(c: char) -> bool { + ret in_range(c, 'a', 'z') || in_range(c, 'A', 'Z'); +} + +fn is_dec_digit(c: char) -> bool { ret in_range(c, '0', '9'); } + +fn is_alnum(c: char) -> bool { ret is_alpha(c) || is_dec_digit(c); } + +fn is_hex_digit(c: char) -> bool { + ret in_range(c, '0', '9') || in_range(c, 'a', 'f') || + in_range(c, 'A', 'F'); +} + +fn is_bin_digit(c: char) -> bool { ret c == '0' || c == '1'; } + +fn consume_whitespace_and_comments(rdr: reader) { + while is_whitespace(rdr.curr) { rdr.bump(); } + be consume_any_line_comment(rdr); +} + +fn consume_any_line_comment(rdr: reader) { + if rdr.curr == '/' { + alt rdr.next() { + '/' { + while rdr.curr != '\n' && !rdr.is_eof() { rdr.bump(); } + // Restart whitespace munch. + + be consume_whitespace_and_comments(rdr); + } + '*' { rdr.bump(); rdr.bump(); be consume_block_comment(rdr); } + _ { ret; } + } + } +} + +fn consume_block_comment(rdr: reader) { + let level: int = 1; + while level > 0 { + if rdr.is_eof() { rdr.fatal("unterminated block comment"); } + if rdr.curr == '/' && rdr.next() == '*' { + rdr.bump(); + rdr.bump(); + level += 1; + } else { + if rdr.curr == '*' && rdr.next() == '/' { + rdr.bump(); + rdr.bump(); + level -= 1; + } else { rdr.bump(); } + } + } + // restart whitespace munch. + + be consume_whitespace_and_comments(rdr); +} + +fn scan_exponent(rdr: reader) -> option<str> { + let c = rdr.curr; + let rslt = ""; + if c == 'e' || c == 'E' { + str::push_char(rslt, c); + rdr.bump(); + c = rdr.curr; + if c == '-' || c == '+' { + str::push_char(rslt, c); + rdr.bump(); + } + let exponent = scan_digits(rdr, 10u); + if str::len(exponent) > 0u { + ret some(rslt + exponent); + } else { rdr.fatal("scan_exponent: bad fp literal"); } + } else { ret none::<str>; } +} + +fn scan_digits(rdr: reader, radix: uint) -> str { + let rslt = ""; + while true { + let c = rdr.curr; + if c == '_' { rdr.bump(); cont; } + alt char::to_digit(c, radix) { + some(d) { + str::push_char(rslt, c); + rdr.bump(); + } + _ { break; } + } + } + ret rslt; +} + +fn scan_number(c: char, rdr: reader) -> token::token { + let num_str, base = 10u, c = c, n = rdr.next(); + if c == '0' && n == 'x' { + rdr.bump(); + rdr.bump(); + base = 16u; + } else if c == '0' && n == 'b' { + rdr.bump(); + rdr.bump(); + base = 2u; + } + num_str = scan_digits(rdr, base); + c = rdr.curr; + n = rdr.next(); + if c == 'u' || c == 'i' { + let signed = c == 'i', tp = if signed { either::left(ast::ty_i) } + else { either::right(ast::ty_u) }; + rdr.bump(); + c = rdr.curr; + if c == '8' { + rdr.bump(); + tp = if signed { either::left(ast::ty_i8) } + else { either::right(ast::ty_u8) }; + } + n = rdr.next(); + if c == '1' && n == '6' { + rdr.bump(); + rdr.bump(); + tp = if signed { either::left(ast::ty_i16) } + else { either::right(ast::ty_u16) }; + } else if c == '3' && n == '2' { + rdr.bump(); + rdr.bump(); + tp = if signed { either::left(ast::ty_i32) } + else { either::right(ast::ty_u32) }; + } else if c == '6' && n == '4' { + rdr.bump(); + rdr.bump(); + tp = if signed { either::left(ast::ty_i64) } + else { either::right(ast::ty_u64) }; + } + if str::len(num_str) == 0u { + rdr.fatal("no valid digits found for number"); + } + let parsed = option::get(u64::from_str(num_str, base as u64)); + alt tp { + either::left(t) { ret token::LIT_INT(parsed as i64, t); } + either::right(t) { ret token::LIT_UINT(parsed, t); } + } + } + let is_float = false; + if rdr.curr == '.' && !(is_alpha(rdr.next()) || rdr.next() == '_') { + is_float = true; + rdr.bump(); + let dec_part = scan_digits(rdr, 10u); + num_str += "." + dec_part; + } + alt scan_exponent(rdr) { + some(s) { + is_float = true; + num_str += s; + } + none {} + } + if rdr.curr == 'f' { + rdr.bump(); + c = rdr.curr; + n = rdr.next(); + if c == '3' && n == '2' { + rdr.bump(); + rdr.bump(); + ret token::LIT_FLOAT(intern(*rdr.interner, num_str), + ast::ty_f32); + } else if c == '6' && n == '4' { + rdr.bump(); + rdr.bump(); + ret token::LIT_FLOAT(intern(*rdr.interner, num_str), + ast::ty_f64); + /* FIXME: if this is out of range for either a 32-bit or + 64-bit float, it won't be noticed till the back-end */ + } else { + is_float = true; + } + } + if is_float { + ret token::LIT_FLOAT(interner::intern(*rdr.interner, num_str), + ast::ty_f); + } else { + if str::len(num_str) == 0u { + rdr.fatal("no valid digits found for number"); + } + let parsed = option::get(u64::from_str(num_str, base as u64)); + ret token::LIT_INT(parsed as i64, ast::ty_i); + } +} + +fn scan_numeric_escape(rdr: reader, n_hex_digits: uint) -> char { + let accum_int = 0, i = n_hex_digits; + while i != 0u { + let n = rdr.curr; + rdr.bump(); + if !is_hex_digit(n) { + rdr.fatal(#fmt["illegal numeric character escape: %d", n as int]); + } + accum_int *= 16; + accum_int += hex_digit_val(n); + i -= 1u; + } + ret accum_int as char; +} + +fn next_token(rdr: reader) -> {tok: token::token, chpos: uint, bpos: uint} { + consume_whitespace_and_comments(rdr); + let start_chpos = rdr.chpos; + let start_bpos = rdr.pos; + let tok = if rdr.is_eof() { token::EOF } else { next_token_inner(rdr) }; + ret {tok: tok, chpos: start_chpos, bpos: start_bpos}; +} + +fn next_token_inner(rdr: reader) -> token::token { + let accum_str = ""; + let c = rdr.curr; + if (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c == '_' + || (c > 'z' && char::is_XID_start(c)) { + while (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == '_' + || (c > 'z' && char::is_XID_continue(c)) { + str::push_char(accum_str, c); + rdr.bump(); + c = rdr.curr; + } + if str::eq(accum_str, "_") { ret token::UNDERSCORE; } + let is_mod_name = c == ':' && rdr.next() == ':'; + + // FIXME: perform NFKC normalization here. + ret token::IDENT(interner::intern::<str>(*rdr.interner, + accum_str), is_mod_name); + } + if is_dec_digit(c) { + ret scan_number(c, rdr); + } + fn binop(rdr: reader, op: token::binop) -> token::token { + rdr.bump(); + if rdr.curr == '=' { + rdr.bump(); + ret token::BINOPEQ(op); + } else { ret token::BINOP(op); } + } + alt c { + + + + + + // One-byte tokens. + ';' { rdr.bump(); ret token::SEMI; } + ',' { rdr.bump(); ret token::COMMA; } + '.' { + rdr.bump(); + if rdr.curr == '.' && rdr.next() == '.' { + rdr.bump(); + rdr.bump(); + ret token::ELLIPSIS; + } + ret token::DOT; + } + '(' { rdr.bump(); ret token::LPAREN; } + ')' { rdr.bump(); ret token::RPAREN; } + '{' { rdr.bump(); ret token::LBRACE; } + '}' { rdr.bump(); ret token::RBRACE; } + '[' { rdr.bump(); ret token::LBRACKET; } + ']' { rdr.bump(); ret token::RBRACKET; } + '@' { rdr.bump(); ret token::AT; } + '#' { + rdr.bump(); + if rdr.curr == '<' { rdr.bump(); ret token::POUND_LT; } + if rdr.curr == '{' { rdr.bump(); ret token::POUND_LBRACE; } + ret token::POUND; + } + '~' { rdr.bump(); ret token::TILDE; } + ':' { + rdr.bump(); + if rdr.curr == ':' { + rdr.bump(); + ret token::MOD_SEP; + } else { ret token::COLON; } + } + + '$' { + rdr.bump(); + if is_dec_digit(rdr.curr) { + let val = dec_digit_val(rdr.curr) as uint; + while is_dec_digit(rdr.next()) { + rdr.bump(); + val = val * 10u + (dec_digit_val(rdr.curr) as uint); + } + rdr.bump(); + ret token::DOLLAR_NUM(val); + } else if rdr.curr == '(' { + rdr.bump(); + ret token::DOLLAR_LPAREN; + } else { + rdr.fatal("expected digit"); + } + } + + + + + + // Multi-byte tokens. + '=' { + rdr.bump(); + if rdr.curr == '=' { + rdr.bump(); + ret token::EQEQ; + } else { ret token::EQ; } + } + '!' { + rdr.bump(); + if rdr.curr == '=' { + rdr.bump(); + ret token::NE; + } else { ret token::NOT; } + } + '<' { + rdr.bump(); + alt rdr.curr { + '=' { rdr.bump(); ret token::LE; } + '<' { ret binop(rdr, token::LSL); } + '-' { + rdr.bump(); + alt rdr.curr { + '>' { rdr.bump(); ret token::DARROW; } + _ { ret token::LARROW; } + } + } + _ { ret token::LT; } + } + } + '>' { + rdr.bump(); + alt rdr.curr { + '=' { rdr.bump(); ret token::GE; } + '>' { + if rdr.next() == '>' { + rdr.bump(); + ret binop(rdr, token::ASR); + } else { ret binop(rdr, token::LSR); } + } + _ { ret token::GT; } + } + } + '\'' { + rdr.bump(); + let c2 = rdr.curr; + rdr.bump(); + if c2 == '\\' { + let escaped = rdr.curr; + rdr.bump(); + alt escaped { + 'n' { c2 = '\n'; } + 'r' { c2 = '\r'; } + 't' { c2 = '\t'; } + '\\' { c2 = '\\'; } + '\'' { c2 = '\''; } + 'x' { c2 = scan_numeric_escape(rdr, 2u); } + 'u' { c2 = scan_numeric_escape(rdr, 4u); } + 'U' { c2 = scan_numeric_escape(rdr, 8u); } + c2 { + rdr.fatal(#fmt["unknown character escape: %d", c2 as int]); + } + } + } + if rdr.curr != '\'' { + rdr.fatal("unterminated character constant"); + } + rdr.bump(); // advance curr past token + ret token::LIT_INT(c2 as i64, ast::ty_char); + } + '"' { + let n = rdr.chpos; + rdr.bump(); + while rdr.curr != '"' { + if rdr.is_eof() { + rdr.fatal(#fmt["unterminated double quote string: %s", + rdr.get_str_from(n)]); + } + + let ch = rdr.curr; + rdr.bump(); + alt ch { + '\\' { + let escaped = rdr.curr; + rdr.bump(); + alt escaped { + 'n' { str::push_char(accum_str, '\n'); } + 'r' { str::push_char(accum_str, '\r'); } + 't' { str::push_char(accum_str, '\t'); } + '\\' { str::push_char(accum_str, '\\'); } + '"' { str::push_char(accum_str, '"'); } + '\n' { consume_whitespace(rdr); } + 'x' { + str::push_char(accum_str, scan_numeric_escape(rdr, 2u)); + } + 'u' { + str::push_char(accum_str, scan_numeric_escape(rdr, 4u)); + } + 'U' { + str::push_char(accum_str, scan_numeric_escape(rdr, 8u)); + } + c2 { + rdr.fatal(#fmt["unknown string escape: %d", c2 as int]); + } + } + } + _ { str::push_char(accum_str, ch); } + } + } + rdr.bump(); + ret token::LIT_STR(interner::intern::<str>(*rdr.interner, + accum_str)); + } + '-' { + if rdr.next() == '>' { + rdr.bump(); + rdr.bump(); + ret token::RARROW; + } else { ret binop(rdr, token::MINUS); } + } + '&' { + if rdr.next() == '&' { + rdr.bump(); + rdr.bump(); + ret token::ANDAND; + } else { ret binop(rdr, token::AND); } + } + '|' { + alt rdr.next() { + '|' { rdr.bump(); rdr.bump(); ret token::OROR; } + _ { ret binop(rdr, token::OR); } + } + } + '+' { ret binop(rdr, token::PLUS); } + '*' { ret binop(rdr, token::STAR); } + '/' { ret binop(rdr, token::SLASH); } + '^' { ret binop(rdr, token::CARET); } + '%' { ret binop(rdr, token::PERCENT); } + c { rdr.fatal(#fmt["unkown start of token: %d", c as int]); } + } +} + +enum cmnt_style { + isolated, // No code on either side of each line of the comment + trailing, // Code exists to the left of the comment + mixed, // Code before /* foo */ and after the comment + blank_line, // Just a manual blank line "\n\n", for layout +} + +type cmnt = {style: cmnt_style, lines: [str], pos: uint}; + +fn read_to_eol(rdr: reader) -> str { + let val = ""; + while rdr.curr != '\n' && !rdr.is_eof() { + str::push_char(val, rdr.curr); + rdr.bump(); + } + if rdr.curr == '\n' { rdr.bump(); } + ret val; +} + +fn read_one_line_comment(rdr: reader) -> str { + let val = read_to_eol(rdr); + assert (val[0] == '/' as u8 && val[1] == '/' as u8); + ret val; +} + +fn consume_whitespace(rdr: reader) { + while is_whitespace(rdr.curr) && !rdr.is_eof() { rdr.bump(); } +} + +fn consume_non_eol_whitespace(rdr: reader) { + while is_whitespace(rdr.curr) && rdr.curr != '\n' && !rdr.is_eof() { + rdr.bump(); + } +} + +fn push_blank_line_comment(rdr: reader, &comments: [cmnt]) { + #debug(">>> blank-line comment"); + let v: [str] = []; + comments += [{style: blank_line, lines: v, pos: rdr.chpos}]; +} + +fn consume_whitespace_counting_blank_lines(rdr: reader, &comments: [cmnt]) { + while is_whitespace(rdr.curr) && !rdr.is_eof() { + if rdr.col == 0u && rdr.curr == '\n' { + push_blank_line_comment(rdr, comments); + } + rdr.bump(); + } +} + +fn read_line_comments(rdr: reader, code_to_the_left: bool) -> cmnt { + #debug(">>> line comments"); + let p = rdr.chpos; + let lines: [str] = []; + while rdr.curr == '/' && rdr.next() == '/' { + let line = read_one_line_comment(rdr); + log(debug, line); + lines += [line]; + consume_non_eol_whitespace(rdr); + } + #debug("<<< line comments"); + ret {style: if code_to_the_left { trailing } else { isolated }, + lines: lines, + pos: p}; +} + +fn all_whitespace(s: str, begin: uint, end: uint) -> bool { + let i: uint = begin; + while i != end { if !is_whitespace(s[i] as char) { ret false; } i += 1u; } + ret true; +} + +fn trim_whitespace_prefix_and_push_line(&lines: [str], + s: str, col: uint) unsafe { + let s1; + if all_whitespace(s, 0u, col) { + if col < str::len(s) { + s1 = str::slice(s, col, str::len(s)); + } else { s1 = ""; } + } else { s1 = s; } + log(debug, "pushing line: " + s1); + lines += [s1]; +} + +fn read_block_comment(rdr: reader, code_to_the_left: bool) -> cmnt { + #debug(">>> block comment"); + let p = rdr.chpos; + let lines: [str] = []; + let col: uint = rdr.col; + rdr.bump(); + rdr.bump(); + let curr_line = "/*"; + let level: int = 1; + while level > 0 { + #debug("=== block comment level %d", level); + if rdr.is_eof() { rdr.fatal("unterminated block comment"); } + if rdr.curr == '\n' { + trim_whitespace_prefix_and_push_line(lines, curr_line, col); + curr_line = ""; + rdr.bump(); + } else { + str::push_char(curr_line, rdr.curr); + if rdr.curr == '/' && rdr.next() == '*' { + rdr.bump(); + rdr.bump(); + curr_line += "*"; + level += 1; + } else { + if rdr.curr == '*' && rdr.next() == '/' { + rdr.bump(); + rdr.bump(); + curr_line += "/"; + level -= 1; + } else { rdr.bump(); } + } + } + } + if str::len(curr_line) != 0u { + trim_whitespace_prefix_and_push_line(lines, curr_line, col); + } + let style = if code_to_the_left { trailing } else { isolated }; + consume_non_eol_whitespace(rdr); + if !rdr.is_eof() && rdr.curr != '\n' && vec::len(lines) == 1u { + style = mixed; + } + #debug("<<< block comment"); + ret {style: style, lines: lines, pos: p}; +} + +fn peeking_at_comment(rdr: reader) -> bool { + ret rdr.curr == '/' && rdr.next() == '/' || + rdr.curr == '/' && rdr.next() == '*'; +} + +fn consume_comment(rdr: reader, code_to_the_left: bool, &comments: [cmnt]) { + #debug(">>> consume comment"); + if rdr.curr == '/' && rdr.next() == '/' { + comments += [read_line_comments(rdr, code_to_the_left)]; + } else if rdr.curr == '/' && rdr.next() == '*' { + comments += [read_block_comment(rdr, code_to_the_left)]; + } else { fail; } + #debug("<<< consume comment"); +} + +fn is_lit(t: token::token) -> bool { + ret alt t { + token::LIT_INT(_, _) { true } + token::LIT_UINT(_, _) { true } + token::LIT_FLOAT(_, _) { true } + token::LIT_STR(_) { true } + token::LIT_BOOL(_) { true } + _ { false } + } +} + +type lit = {lit: str, pos: uint}; + +fn gather_comments_and_literals(cm: codemap::codemap, + span_diagnostic: diagnostic::span_handler, + path: str, + srdr: io::reader) -> + {cmnts: [cmnt], lits: [lit]} { + let src = @str::from_bytes(srdr.read_whole_stream()); + let itr = @interner::mk::<str>(str::hash, str::eq); + let rdr = new_reader(cm, span_diagnostic, + codemap::new_filemap(path, src, 0u, 0u), itr); + let comments: [cmnt] = []; + let literals: [lit] = []; + let first_read: bool = true; + while !rdr.is_eof() { + while true { + let code_to_the_left = !first_read; + consume_non_eol_whitespace(rdr); + if rdr.curr == '\n' { + code_to_the_left = false; + consume_whitespace_counting_blank_lines(rdr, comments); + } + while peeking_at_comment(rdr) { + consume_comment(rdr, code_to_the_left, comments); + consume_whitespace_counting_blank_lines(rdr, comments); + } + break; + } + let tok = next_token(rdr); + if is_lit(tok.tok) { + let s = rdr.get_str_from(tok.bpos); + literals += [{lit: s, pos: tok.chpos}]; + log(debug, "tok lit: " + s); + } else { + log(debug, "tok: " + token::to_str(rdr, tok.tok)); + } + first_read = false; + } + ret {cmnts: comments, lits: literals}; +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/parse/parser.rs b/src/rustc/syntax/parse/parser.rs new file mode 100644 index 00000000000..5837baf1ff9 --- /dev/null +++ b/src/rustc/syntax/parse/parser.rs @@ -0,0 +1,2747 @@ +import std::{io, fs}; +import either::{left, right}; +import std::map::{hashmap, new_str_hash}; +import token::can_begin_expr; +import codemap::{span,fss_none}; +import util::interner; +import ast::{node_id, spanned}; +import ast_util::{mk_sp, ident_to_path}; +import front::attr; +import lexer::reader; +import driver::diagnostic; + +enum restriction { + UNRESTRICTED, + RESTRICT_STMT_EXPR, + RESTRICT_NO_CALL_EXPRS, + RESTRICT_NO_BAR_OP, +} + +enum file_type { CRATE_FILE, SOURCE_FILE, } + +type parse_sess = @{ + cm: codemap::codemap, + mutable next_id: node_id, + span_diagnostic: diagnostic::span_handler, + // these two must be kept up to date + mutable chpos: uint, + mutable byte_pos: uint +}; + +fn next_node_id(sess: parse_sess) -> node_id { + let rv = sess.next_id; + sess.next_id += 1; + // ID 0 is reserved for the crate and doesn't actually exist in the AST + assert rv != 0; + ret rv; +} + +type parser = @{ + sess: parse_sess, + cfg: ast::crate_cfg, + file_type: file_type, + mutable token: token::token, + mutable span: span, + mutable last_span: span, + mutable buffer: [{tok: token::token, span: span}], + mutable restriction: restriction, + reader: reader, + precs: @[op_spec], + bad_expr_words: hashmap<str, ()> +}; + +impl parser for parser { + fn bump() { + self.last_span = self.span; + if vec::len(self.buffer) == 0u { + let next = lexer::next_token(self.reader); + self.token = next.tok; + self.span = ast_util::mk_sp(next.chpos, self.reader.chpos); + } else { + let next = vec::pop(self.buffer); + self.token = next.tok; + self.span = next.span; + } + } + fn swap(next: token::token, lo: uint, hi: uint) { + self.token = next; + self.span = ast_util::mk_sp(lo, hi); + } + fn look_ahead(distance: uint) -> token::token { + while vec::len(self.buffer) < distance { + let next = lexer::next_token(self.reader); + let sp = ast_util::mk_sp(next.chpos, self.reader.chpos); + self.buffer = [{tok: next.tok, span: sp}] + self.buffer; + } + ret self.buffer[distance - 1u].tok; + } + fn fatal(m: str) -> ! { + self.sess.span_diagnostic.span_fatal(self.span, m) + } + fn span_fatal(sp: span, m: str) -> ! { + self.sess.span_diagnostic.span_fatal(sp, m) + } + fn warn(m: str) { + self.sess.span_diagnostic.span_warn(self.span, m) + } + fn get_str(i: token::str_num) -> str { + interner::get(*self.reader.interner, i) + } + fn get_id() -> node_id { next_node_id(self.sess) } +} + +fn new_parser_from_file(sess: parse_sess, cfg: ast::crate_cfg, path: str, + ftype: file_type) -> + parser { + let src = alt io::read_whole_file_str(path) { + result::ok(src) { + // FIXME: This copy is unfortunate + @src + } + result::err(e) { + sess.span_diagnostic.handler().fatal(e) + } + }; + let filemap = codemap::new_filemap(path, src, + sess.chpos, sess.byte_pos); + sess.cm.files += [filemap]; + let itr = @interner::mk(str::hash, str::eq); + let rdr = lexer::new_reader(sess.cm, sess.span_diagnostic, filemap, itr); + ret new_parser(sess, cfg, rdr, ftype); +} + +fn new_parser_from_source_str(sess: parse_sess, cfg: ast::crate_cfg, + name: str, ss: codemap::file_substr, + source: @str) -> parser { + let ftype = SOURCE_FILE; + let filemap = codemap::new_filemap_w_substr + (name, ss, source, sess.chpos, sess.byte_pos); + sess.cm.files += [filemap]; + let itr = @interner::mk(str::hash, str::eq); + let rdr = lexer::new_reader(sess.cm, sess.span_diagnostic, + filemap, itr); + ret new_parser(sess, cfg, rdr, ftype); +} + +fn new_parser(sess: parse_sess, cfg: ast::crate_cfg, rdr: reader, + ftype: file_type) -> parser { + let tok0 = lexer::next_token(rdr); + let span0 = ast_util::mk_sp(tok0.chpos, rdr.chpos); + @{sess: sess, + cfg: cfg, + file_type: ftype, + mutable token: tok0.tok, + mutable span: span0, + mutable last_span: span0, + mutable buffer: [], + mutable restriction: UNRESTRICTED, + reader: rdr, + precs: prec_table(), + bad_expr_words: bad_expr_word_table()} +} + +// These are the words that shouldn't be allowed as value identifiers, +// because, if used at the start of a line, they will cause the line to be +// interpreted as a specific kind of statement, which would be confusing. +fn bad_expr_word_table() -> hashmap<str, ()> { + let words = new_str_hash(); + for word in ["alt", "assert", "be", "break", "check", "claim", + "class", "const", "cont", "copy", "do", "else", "enum", + "export", "fail", "fn", "for", "if", "iface", "impl", + "import", "let", "log", "mod", "mutable", "native", "pure", + "resource", "ret", "trait", "type", "unchecked", "unsafe", + "while", "crust", "mut"] { + words.insert(word, ()); + } + words +} + +fn unexpected(p: parser, t: token::token) -> ! { + let s: str = "unexpected token: '" + token::to_str(p.reader, t) + + "'"; + p.fatal(s); +} + +fn expect(p: parser, t: token::token) { + if p.token == t { + p.bump(); + } else { + let s: str = "expecting '"; + s += token::to_str(p.reader, t); + s += "' but found '"; + s += token::to_str(p.reader, p.token); + p.fatal(s + "'"); + } +} + +fn expect_gt(p: parser) { + if p.token == token::GT { + p.bump(); + } else if p.token == token::BINOP(token::LSR) { + p.swap(token::GT, p.span.lo + 1u, p.span.hi); + } else if p.token == token::BINOP(token::ASR) { + p.swap(token::BINOP(token::LSR), p.span.lo + 1u, p.span.hi); + } else { + let s: str = "expecting "; + s += token::to_str(p.reader, token::GT); + s += ", found "; + s += token::to_str(p.reader, p.token); + p.fatal(s); + } +} + +fn spanned<T: copy>(lo: uint, hi: uint, node: T) -> spanned<T> { + ret {node: node, span: ast_util::mk_sp(lo, hi)}; +} + +fn parse_ident(p: parser) -> ast::ident { + alt p.token { + token::IDENT(i, _) { p.bump(); ret p.get_str(i); } + _ { p.fatal("expecting ident, found " + + token::to_str(p.reader, p.token)); } + } +} + +fn parse_path_list_ident(p: parser) -> ast::path_list_ident { + let lo = p.span.lo; + let ident = parse_ident(p); + let hi = p.span.hi; + ret spanned(lo, hi, {name: ident, id: p.get_id()}); +} + +fn parse_value_ident(p: parser) -> ast::ident { + check_bad_word(p); + ret parse_ident(p); +} + +fn eat(p: parser, tok: token::token) -> bool { + ret if p.token == tok { p.bump(); true } else { false }; +} + +fn is_word(p: parser, word: str) -> bool { + ret alt p.token { + token::IDENT(sid, false) { str::eq(word, p.get_str(sid)) } + _ { false } + }; +} + +fn eat_word(p: parser, word: str) -> bool { + alt p.token { + token::IDENT(sid, false) { + if str::eq(word, p.get_str(sid)) { + p.bump(); + ret true; + } else { ret false; } + } + _ { ret false; } + } +} + +fn expect_word(p: parser, word: str) { + if !eat_word(p, word) { + p.fatal("expecting " + word + ", found " + + token::to_str(p.reader, p.token)); + } +} + +fn check_bad_word(p: parser) { + alt p.token { + token::IDENT(sid, false) { + let w = p.get_str(sid); + if p.bad_expr_words.contains_key(w) { + p.fatal("found " + w + " in expression position"); + } + } + _ { } + } +} + +fn parse_ty_fn(p: parser) -> ast::fn_decl { + fn parse_fn_input_ty(p: parser) -> ast::arg { + let mode = parse_arg_mode(p); + let name = if is_plain_ident(p) && p.look_ahead(1u) == token::COLON { + let name = parse_value_ident(p); + p.bump(); + name + } else { "" }; + ret {mode: mode, ty: parse_ty(p, false), ident: name, id: p.get_id()}; + } + let inputs = + parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + parse_fn_input_ty, p); + // FIXME: there's no syntax for this right now anyway + // auto constrs = parse_constrs(~[], p); + let constrs: [@ast::constr] = []; + let (ret_style, ret_ty) = parse_ret_ty(p); + ret {inputs: inputs.node, output: ret_ty, + purity: ast::impure_fn, cf: ret_style, + constraints: constrs}; +} + +fn parse_ty_methods(p: parser) -> [ast::ty_method] { + parse_seq(token::LBRACE, token::RBRACE, seq_sep_none(), {|p| + let attrs = parse_outer_attributes(p); + let flo = p.span.lo; + let pur = parse_fn_purity(p); + let ident = parse_method_name(p); + let tps = parse_ty_params(p); + let d = parse_ty_fn(p), fhi = p.last_span.hi; + expect(p, token::SEMI); + {ident: ident, attrs: attrs, decl: {purity: pur with d}, tps: tps, + span: ast_util::mk_sp(flo, fhi)} + }, p).node +} + +fn parse_mt(p: parser) -> ast::mt { + let mutbl = parse_mutability(p); + let t = parse_ty(p, false); + ret {ty: t, mutbl: mutbl}; +} + +fn parse_ty_field(p: parser) -> ast::ty_field { + let lo = p.span.lo; + let mutbl = parse_mutability(p); + let id = parse_ident(p); + expect(p, token::COLON); + let ty = parse_ty(p, false); + ret spanned(lo, ty.span.hi, {ident: id, mt: {ty: ty, mutbl: mutbl}}); +} + +// if i is the jth ident in args, return j +// otherwise, fail +fn ident_index(p: parser, args: [ast::arg], i: ast::ident) -> uint { + let j = 0u; + for a: ast::arg in args { if a.ident == i { ret j; } j += 1u; } + p.fatal("Unbound variable " + i + " in constraint arg"); +} + +fn parse_type_constr_arg(p: parser) -> @ast::ty_constr_arg { + let sp = p.span; + let carg = ast::carg_base; + expect(p, token::BINOP(token::STAR)); + if p.token == token::DOT { + // "*..." notation for record fields + p.bump(); + let pth = parse_path(p); + carg = ast::carg_ident(pth); + } + // No literals yet, I guess? + ret @{node: carg, span: sp}; +} + +fn parse_constr_arg(args: [ast::arg], p: parser) -> @ast::constr_arg { + let sp = p.span; + let carg = ast::carg_base; + if p.token == token::BINOP(token::STAR) { + p.bump(); + } else { + let i: ast::ident = parse_value_ident(p); + carg = ast::carg_ident(ident_index(p, args, i)); + } + ret @{node: carg, span: sp}; +} + +fn parse_ty_constr(fn_args: [ast::arg], p: parser) -> @ast::constr { + let lo = p.span.lo; + let path = parse_path(p); + let args: {node: [@ast::constr_arg], span: span} = + parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + {|p| parse_constr_arg(fn_args, p)}, p); + ret @spanned(lo, args.span.hi, + {path: path, args: args.node, id: p.get_id()}); +} + +fn parse_constr_in_type(p: parser) -> @ast::ty_constr { + let lo = p.span.lo; + let path = parse_path(p); + let args: [@ast::ty_constr_arg] = + parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + parse_type_constr_arg, p).node; + let hi = p.span.lo; + let tc: ast::ty_constr_ = {path: path, args: args, id: p.get_id()}; + ret @spanned(lo, hi, tc); +} + + +fn parse_constrs<T: copy>(pser: fn(parser) -> @ast::constr_general<T>, + p: parser) -> + [@ast::constr_general<T>] { + let constrs: [@ast::constr_general<T>] = []; + while true { + let constr = pser(p); + constrs += [constr]; + if p.token == token::COMMA { p.bump(); } else { break; } + } + constrs +} + +fn parse_type_constraints(p: parser) -> [@ast::ty_constr] { + ret parse_constrs(parse_constr_in_type, p); +} + +fn parse_ty_postfix(orig_t: ast::ty_, p: parser, colons_before_params: bool, + lo: uint) -> @ast::ty { + if colons_before_params && p.token == token::MOD_SEP { + p.bump(); + expect(p, token::LT); + } else if !colons_before_params && p.token == token::LT { + p.bump(); + } else { ret @spanned(lo, p.last_span.hi, orig_t); } + + // If we're here, we have explicit type parameter instantiation. + let seq = parse_seq_to_gt(some(token::COMMA), {|p| parse_ty(p, false)}, + p); + + alt orig_t { + ast::ty_path(pth, ann) { + ret @spanned(lo, p.last_span.hi, + ast::ty_path(@spanned(lo, p.last_span.hi, + {global: pth.node.global, + idents: pth.node.idents, + types: seq}), ann)); + } + _ { p.fatal("type parameter instantiation only allowed for paths"); } + } +} + +fn parse_ret_ty(p: parser) -> (ast::ret_style, @ast::ty) { + ret if eat(p, token::RARROW) { + let lo = p.span.lo; + if eat(p, token::NOT) { + (ast::noreturn, @spanned(lo, p.last_span.hi, ast::ty_bot)) + } else { (ast::return_val, parse_ty(p, false)) } + } else { + let pos = p.span.lo; + (ast::return_val, @spanned(pos, pos, ast::ty_nil)) + } +} + +fn parse_ty(p: parser, colons_before_params: bool) -> @ast::ty { + let lo = p.span.lo; + + alt have_dollar(p) { + some(e) {ret @spanned(lo, p.span.hi, + ast::ty_mac(spanned(lo, p.span.hi, e)))} + none {} + } + + let t = if p.token == token::LPAREN { + p.bump(); + if p.token == token::RPAREN { + p.bump(); + ast::ty_nil + } else { + let ts = [parse_ty(p, false)]; + while p.token == token::COMMA { + p.bump(); + ts += [parse_ty(p, false)]; + } + let t = if vec::len(ts) == 1u { ts[0].node } + else { ast::ty_tup(ts) }; + expect(p, token::RPAREN); + t + } + } else if p.token == token::AT { + p.bump(); + ast::ty_box(parse_mt(p)) + } else if p.token == token::TILDE { + p.bump(); + ast::ty_uniq(parse_mt(p)) + } else if p.token == token::BINOP(token::STAR) { + p.bump(); + ast::ty_ptr(parse_mt(p)) + } else if p.token == token::LBRACE { + let elems = + parse_seq(token::LBRACE, token::RBRACE, seq_sep_opt(token::COMMA), + parse_ty_field, p); + if vec::len(elems.node) == 0u { unexpected(p, token::RBRACE); } + let hi = elems.span.hi; + + let t = ast::ty_rec(elems.node); + if p.token == token::COLON { + p.bump(); + ast::ty_constr(@spanned(lo, hi, t), parse_type_constraints(p)) + } else { t } + } else if p.token == token::LBRACKET { + expect(p, token::LBRACKET); + let t = ast::ty_vec(parse_mt(p)); + expect(p, token::RBRACKET); + t + } else if eat_word(p, "fn") { + let proto = parse_fn_ty_proto(p); + alt proto { + ast::proto_bare { p.warn("fn is deprecated, use native fn"); } + _ { /* fallthrough */ } + } + ast::ty_fn(proto, parse_ty_fn(p)) + } else if eat_word(p, "native") { + expect_word(p, "fn"); + ast::ty_fn(ast::proto_bare, parse_ty_fn(p)) + } else if p.token == token::MOD_SEP || is_ident(p.token) { + let path = parse_path(p); + ast::ty_path(path, p.get_id()) + } else { p.fatal("expecting type"); }; + ret parse_ty_postfix(t, p, colons_before_params, lo); +} + +fn parse_arg_mode(p: parser) -> ast::mode { + if eat(p, token::BINOP(token::AND)) { + ast::expl(ast::by_mutbl_ref) + } else if eat(p, token::BINOP(token::MINUS)) { + ast::expl(ast::by_move) + } else if eat(p, token::ANDAND) { + ast::expl(ast::by_ref) + } else if eat(p, token::BINOP(token::PLUS)) { + if eat(p, token::BINOP(token::PLUS)) { + ast::expl(ast::by_val) + } else { + ast::expl(ast::by_copy) + } + } else { ast::infer(p.get_id()) } +} + +fn parse_arg(p: parser) -> ast::arg { + let m = parse_arg_mode(p); + let i = parse_value_ident(p); + expect(p, token::COLON); + let t = parse_ty(p, false); + ret {mode: m, ty: t, ident: i, id: p.get_id()}; +} + +fn parse_fn_block_arg(p: parser) -> ast::arg { + let m = parse_arg_mode(p); + let i = parse_value_ident(p); + let t = if eat(p, token::COLON) { + parse_ty(p, false) + } else { + @spanned(p.span.lo, p.span.hi, ast::ty_infer) + }; + ret {mode: m, ty: t, ident: i, id: p.get_id()}; +} + +fn parse_seq_to_before_gt<T: copy>(sep: option<token::token>, + f: fn(parser) -> T, + p: parser) -> [T] { + let first = true; + let v = []; + while p.token != token::GT && p.token != token::BINOP(token::LSR) && + p.token != token::BINOP(token::ASR) { + alt sep { + some(t) { if first { first = false; } else { expect(p, t); } } + _ { } + } + v += [f(p)]; + } + + ret v; +} + +fn parse_seq_to_gt<T: copy>(sep: option<token::token>, + f: fn(parser) -> T, p: parser) -> [T] { + let v = parse_seq_to_before_gt(sep, f, p); + expect_gt(p); + + ret v; +} + +fn parse_seq_lt_gt<T: copy>(sep: option<token::token>, + f: fn(parser) -> T, + p: parser) -> spanned<[T]> { + let lo = p.span.lo; + expect(p, token::LT); + let result = parse_seq_to_before_gt::<T>(sep, f, p); + let hi = p.span.hi; + expect_gt(p); + ret spanned(lo, hi, result); +} + +fn parse_seq_to_end<T: copy>(ket: token::token, sep: seq_sep, + f: fn(parser) -> T, p: parser) -> [T] { + let val = parse_seq_to_before_end(ket, sep, f, p); + p.bump(); + ret val; +} + +type seq_sep = { + sep: option<token::token>, + trailing_opt: bool // is trailing separator optional? +}; + +fn seq_sep(t: token::token) -> seq_sep { + ret {sep: option::some(t), trailing_opt: false}; +} +fn seq_sep_opt(t: token::token) -> seq_sep { + ret {sep: option::some(t), trailing_opt: true}; +} +fn seq_sep_none() -> seq_sep { + ret {sep: option::none, trailing_opt: false}; +} + +fn parse_seq_to_before_end<T: copy>(ket: token::token, + sep: seq_sep, + f: fn(parser) -> T, p: parser) -> [T] { + let first: bool = true; + let v: [T] = []; + while p.token != ket { + alt sep.sep { + some(t) { if first { first = false; } else { expect(p, t); } } + _ { } + } + if sep.trailing_opt && p.token == ket { break; } + v += [f(p)]; + } + ret v; +} + + +fn parse_seq<T: copy>(bra: token::token, ket: token::token, + sep: seq_sep, f: fn(parser) -> T, + p: parser) -> spanned<[T]> { + let lo = p.span.lo; + expect(p, bra); + let result = parse_seq_to_before_end::<T>(ket, sep, f, p); + let hi = p.span.hi; + p.bump(); + ret spanned(lo, hi, result); +} + +fn have_dollar(p: parser) -> option::t<ast::mac_> { + alt p.token { + token::DOLLAR_NUM(num) { + p.bump(); + some(ast::mac_var(num)) + } + token::DOLLAR_LPAREN { + let lo = p.span.lo; + p.bump(); + let e = parse_expr(p); + expect(p, token::RPAREN); + let hi = p.last_span.hi; + some(ast::mac_aq(ast_util::mk_sp(lo,hi), e)) + } + _ {none} + } +} + +fn lit_from_token(p: parser, tok: token::token) -> ast::lit_ { + alt tok { + token::LIT_INT(i, it) { ast::lit_int(i, it) } + token::LIT_UINT(u, ut) { ast::lit_uint(u, ut) } + token::LIT_FLOAT(s, ft) { ast::lit_float(p.get_str(s), ft) } + token::LIT_STR(s) { ast::lit_str(p.get_str(s)) } + token::LPAREN { expect(p, token::RPAREN); ast::lit_nil } + _ { unexpected(p, tok); } + } +} + +fn parse_lit(p: parser) -> ast::lit { + let sp = p.span; + let lit = if eat_word(p, "true") { + ast::lit_bool(true) + } else if eat_word(p, "false") { + ast::lit_bool(false) + } else { + let tok = p.token; + p.bump(); + lit_from_token(p, tok) + }; + ret {node: lit, span: sp}; +} + +fn is_ident(t: token::token) -> bool { + alt t { token::IDENT(_, _) { ret true; } _ { } } + ret false; +} + +fn is_plain_ident(p: parser) -> bool { + ret alt p.token { token::IDENT(_, false) { true } _ { false } }; +} + +fn parse_path(p: parser) -> @ast::path { + let lo = p.span.lo; + let global = eat(p, token::MOD_SEP), ids = [parse_ident(p)]; + while p.look_ahead(1u) != token::LT && eat(p, token::MOD_SEP) { + ids += [parse_ident(p)]; + } + ret @spanned(lo, p.last_span.hi, + {global: global, idents: ids, types: []}); +} + +fn parse_value_path(p: parser) -> @ast::path { + let pt = parse_path(p); + let last_word = pt.node.idents[vec::len(pt.node.idents)-1u]; + if p.bad_expr_words.contains_key(last_word) { + p.fatal("found " + last_word + " in expression position"); + } + pt +} + +fn parse_path_and_ty_param_substs(p: parser, colons: bool) -> @ast::path { + let lo = p.span.lo; + let path = parse_path(p); + let b = if colons { + eat(p, token::MOD_SEP) + } else { + p.token == token::LT + }; + if b { + let seq = parse_seq_lt_gt(some(token::COMMA), + {|p| parse_ty(p, false)}, p); + @spanned(lo, seq.span.hi, {types: seq.node with path.node}) + } else { path } +} + +fn parse_mutability(p: parser) -> ast::mutability { + if eat_word(p, "mutable") { + ast::m_mutbl + } else if eat_word(p, "mut") { + ast::m_mutbl + } else if eat_word(p, "const") { + ast::m_const + } else { + ast::m_imm + } +} + +fn parse_field(p: parser, sep: token::token) -> ast::field { + let lo = p.span.lo; + let m = parse_mutability(p); + let i = parse_ident(p); + expect(p, sep); + let e = parse_expr(p); + ret spanned(lo, e.span.hi, {mutbl: m, ident: i, expr: e}); +} + +fn mk_expr(p: parser, lo: uint, hi: uint, node: ast::expr_) -> @ast::expr { + ret @{id: p.get_id(), node: node, span: ast_util::mk_sp(lo, hi)}; +} + +fn mk_mac_expr(p: parser, lo: uint, hi: uint, m: ast::mac_) -> @ast::expr { + ret @{id: p.get_id(), + node: ast::expr_mac({node: m, span: ast_util::mk_sp(lo, hi)}), + span: ast_util::mk_sp(lo, hi)}; +} + +fn is_bar(t: token::token) -> bool { + alt t { token::BINOP(token::OR) | token::OROR { true } _ { false } } +} + +fn mk_lit_u32(p: parser, i: u32) -> @ast::expr { + let span = p.span; + let lv_lit = @{node: ast::lit_uint(i as u64, ast::ty_u32), + span: span}; + + ret @{id: p.get_id(), node: ast::expr_lit(lv_lit), span: span}; +} + +// We don't allow single-entry tuples in the true AST; that indicates a +// parenthesized expression. However, we preserve them temporarily while +// parsing because `(while{...})+3` parses differently from `while{...}+3`. +// +// To reflect the fact that the @ast::expr is not a true expr that should be +// part of the AST, we wrap such expressions in the pexpr enum. They +// can then be converted to true expressions by a call to `to_expr()`. +enum pexpr { + pexpr(@ast::expr), +} + +fn mk_pexpr(p: parser, lo: uint, hi: uint, node: ast::expr_) -> pexpr { + ret pexpr(mk_expr(p, lo, hi, node)); +} + +fn to_expr(e: pexpr) -> @ast::expr { + alt e.node { + ast::expr_tup(es) if vec::len(es) == 1u { es[0u] } + _ { *e } + } +} + +fn parse_bottom_expr(p: parser) -> pexpr { + let lo = p.span.lo; + let hi = p.span.hi; + + let ex: ast::expr_; + + alt have_dollar(p) { + some(x) {ret pexpr(mk_mac_expr(p, lo, p.span.hi, x));} + _ {} + } + + if p.token == token::LPAREN { + p.bump(); + if p.token == token::RPAREN { + hi = p.span.hi; + p.bump(); + let lit = @spanned(lo, hi, ast::lit_nil); + ret mk_pexpr(p, lo, hi, ast::expr_lit(lit)); + } + let es = [parse_expr(p)]; + while p.token == token::COMMA { p.bump(); es += [parse_expr(p)]; } + hi = p.span.hi; + expect(p, token::RPAREN); + + // Note: we retain the expr_tup() even for simple + // parenthesized expressions, but only for a "little while". + // This is so that wrappers around parse_bottom_expr() + // can tell whether the expression was parenthesized or not, + // which affects expr_is_complete(). + ret mk_pexpr(p, lo, hi, ast::expr_tup(es)); + } else if p.token == token::LBRACE { + p.bump(); + if is_word(p, "mut") || is_word(p, "mutable") || + is_plain_ident(p) && p.look_ahead(1u) == token::COLON { + let fields = [parse_field(p, token::COLON)]; + let base = none; + while p.token != token::RBRACE { + if eat_word(p, "with") { base = some(parse_expr(p)); break; } + expect(p, token::COMMA); + if p.token == token::RBRACE { + // record ends by an optional trailing comma + break; + } + fields += [parse_field(p, token::COLON)]; + } + hi = p.span.hi; + expect(p, token::RBRACE); + ex = ast::expr_rec(fields, base); + } else if is_bar(p.token) { + ret pexpr(parse_fn_block_expr(p)); + } else { + let blk = parse_block_tail(p, lo, ast::default_blk); + ret mk_pexpr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk)); + } + } else if eat_word(p, "if") { + ret pexpr(parse_if_expr(p)); + } else if eat_word(p, "for") { + ret pexpr(parse_for_expr(p)); + } else if eat_word(p, "while") { + ret pexpr(parse_while_expr(p)); + } else if eat_word(p, "do") { + ret pexpr(parse_do_while_expr(p)); + } else if eat_word(p, "alt") { + ret pexpr(parse_alt_expr(p)); + } else if eat_word(p, "fn") { + let proto = parse_fn_ty_proto(p); + alt proto { + ast::proto_bare { p.fatal("fn expr are deprecated, use fn@"); } + ast::proto_any { p.fatal("fn* cannot be used in an expression"); } + _ { /* fallthrough */ } + } + ret pexpr(parse_fn_expr(p, proto)); + } else if eat_word(p, "unchecked") { + ret pexpr(parse_block_expr(p, lo, ast::unchecked_blk)); + } else if eat_word(p, "unsafe") { + ret pexpr(parse_block_expr(p, lo, ast::unsafe_blk)); + } else if p.token == token::LBRACKET { + p.bump(); + let mutbl = parse_mutability(p); + let es = + parse_seq_to_end(token::RBRACKET, seq_sep(token::COMMA), + parse_expr, p); + ex = ast::expr_vec(es, mutbl); + } else if p.token == token::POUND_LT { + p.bump(); + let ty = parse_ty(p, false); + expect(p, token::GT); + + /* hack: early return to take advantage of specialized function */ + ret pexpr(mk_mac_expr(p, lo, p.span.hi, + ast::mac_embed_type(ty))); + } else if p.token == token::POUND_LBRACE { + p.bump(); + let blk = ast::mac_embed_block( + parse_block_tail(p, lo, ast::default_blk)); + ret pexpr(mk_mac_expr(p, lo, p.span.hi, blk)); + } else if p.token == token::ELLIPSIS { + p.bump(); + ret pexpr(mk_mac_expr(p, lo, p.span.hi, ast::mac_ellipsis)); + } else if eat_word(p, "bind") { + let e = parse_expr_res(p, RESTRICT_NO_CALL_EXPRS); + let es = + parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + parse_expr_or_hole, p); + hi = es.span.hi; + ex = ast::expr_bind(e, es.node); + } else if p.token == token::POUND { + let ex_ext = parse_syntax_ext(p); + hi = ex_ext.span.hi; + ex = ex_ext.node; + } else if eat_word(p, "fail") { + if can_begin_expr(p.token) { + let e = parse_expr(p); + hi = e.span.hi; + ex = ast::expr_fail(some(e)); + } else { ex = ast::expr_fail(none); } + } else if eat_word(p, "log") { + expect(p, token::LPAREN); + let lvl = parse_expr(p); + expect(p, token::COMMA); + let e = parse_expr(p); + ex = ast::expr_log(2, lvl, e); + hi = p.span.hi; + expect(p, token::RPAREN); + } else if eat_word(p, "assert") { + let e = parse_expr(p); + ex = ast::expr_assert(e); + hi = e.span.hi; + } else if eat_word(p, "check") { + /* Should be a predicate (pure boolean function) applied to + arguments that are all either slot variables or literals. + but the typechecker enforces that. */ + let e = parse_expr(p); + hi = e.span.hi; + ex = ast::expr_check(ast::checked_expr, e); + } else if eat_word(p, "claim") { + /* Same rules as check, except that if check-claims + is enabled (a command-line flag), then the parser turns + claims into check */ + + let e = parse_expr(p); + hi = e.span.hi; + ex = ast::expr_check(ast::claimed_expr, e); + } else if eat_word(p, "ret") { + if can_begin_expr(p.token) { + let e = parse_expr(p); + hi = e.span.hi; + ex = ast::expr_ret(some(e)); + } else { ex = ast::expr_ret(none); } + } else if eat_word(p, "break") { + ex = ast::expr_break; + hi = p.span.hi; + } else if eat_word(p, "cont") { + ex = ast::expr_cont; + hi = p.span.hi; + } else if eat_word(p, "be") { + let e = parse_expr(p); + + // FIXME: Is this the right place for this check? + if /*check*/ast_util::is_call_expr(e) { + hi = e.span.hi; + ex = ast::expr_be(e); + } else { p.fatal("Non-call expression in tail call"); } + } else if eat_word(p, "copy") { + let e = parse_expr(p); + ex = ast::expr_copy(e); + hi = e.span.hi; + } else if p.token == token::MOD_SEP || + is_ident(p.token) && !is_word(p, "true") && + !is_word(p, "false") { + check_bad_word(p); + let pth = parse_path_and_ty_param_substs(p, true); + hi = pth.span.hi; + ex = ast::expr_path(pth); + } else { + let lit = parse_lit(p); + hi = lit.span.hi; + ex = ast::expr_lit(@lit); + } + ret mk_pexpr(p, lo, hi, ex); +} + +fn parse_block_expr(p: parser, + lo: uint, + blk_mode: ast::blk_check_mode) -> @ast::expr { + expect(p, token::LBRACE); + let blk = parse_block_tail(p, lo, blk_mode); + ret mk_expr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk)); +} + +fn parse_syntax_ext(p: parser) -> @ast::expr { + let lo = p.span.lo; + expect(p, token::POUND); + ret parse_syntax_ext_naked(p, lo); +} + +fn parse_syntax_ext_naked(p: parser, lo: uint) -> @ast::expr { + alt p.token { + token::IDENT(_, _) {} + _ { p.fatal("expected a syntax expander name"); } + } + let pth = parse_path(p); + //temporary for a backwards-compatible cycle: + let sep = seq_sep(token::COMMA); + let e = none; + if (p.token == token::LPAREN || p.token == token::LBRACKET) { + let es = + if p.token == token::LPAREN { + parse_seq(token::LPAREN, token::RPAREN, + sep, parse_expr, p) + } else { + parse_seq(token::LBRACKET, token::RBRACKET, + sep, parse_expr, p) + }; + let hi = es.span.hi; + e = some(mk_expr(p, es.span.lo, hi, + ast::expr_vec(es.node, ast::m_imm))); + } + let b = none; + if p.token == token::LBRACE { + p.bump(); + let lo = p.span.lo; + let depth = 1u; + while (depth > 0u) { + alt (p.token) { + token::LBRACE {depth += 1u;} + token::RBRACE {depth -= 1u;} + token::EOF {p.fatal("unexpected EOF in macro body");} + _ {} + } + p.bump(); + } + let hi = p.last_span.lo; + b = some({span: mk_sp(lo,hi)}); + } + ret mk_mac_expr(p, lo, p.span.hi, ast::mac_invoc(pth, e, b)); +} + +fn parse_dot_or_call_expr(p: parser) -> pexpr { + let b = parse_bottom_expr(p); + parse_dot_or_call_expr_with(p, b) +} + +fn permits_call(p: parser) -> bool { + ret p.restriction != RESTRICT_NO_CALL_EXPRS; +} + +fn parse_dot_or_call_expr_with(p: parser, e0: pexpr) -> pexpr { + let e = e0; + let lo = e.span.lo; + let hi = e.span.hi; + while true { + // expr.f + if eat(p, token::DOT) { + alt p.token { + token::IDENT(i, _) { + hi = p.span.hi; + p.bump(); + let tys = if eat(p, token::MOD_SEP) { + expect(p, token::LT); + parse_seq_to_gt(some(token::COMMA), + {|p| parse_ty(p, false)}, p) + } else { [] }; + e = mk_pexpr(p, lo, hi, + ast::expr_field(to_expr(e), + p.get_str(i), + tys)); + } + t { unexpected(p, t); } + } + cont; + } + if expr_is_complete(p, e) { break; } + alt p.token { + // expr(...) + token::LPAREN if permits_call(p) { + let es_opt = + parse_seq(token::LPAREN, token::RPAREN, + seq_sep(token::COMMA), parse_expr_or_hole, p); + hi = es_opt.span.hi; + + let nd = + if vec::any(es_opt.node, {|e| option::is_none(e) }) { + ast::expr_bind(to_expr(e), es_opt.node) + } else { + let es = vec::map(es_opt.node) {|e| option::get(e) }; + ast::expr_call(to_expr(e), es, false) + }; + e = mk_pexpr(p, lo, hi, nd); + } + + // expr {|| ... } + token::LBRACE if is_bar(p.look_ahead(1u)) && permits_call(p) { + p.bump(); + let blk = parse_fn_block_expr(p); + alt e.node { + ast::expr_call(f, args, false) { + e = pexpr(@{node: ast::expr_call(f, args + [blk], true) + with *to_expr(e)}); + } + _ { + e = mk_pexpr(p, lo, p.last_span.hi, + ast::expr_call(to_expr(e), [blk], true)); + } + } + } + + // expr[...] + token::LBRACKET { + p.bump(); + let ix = parse_expr(p); + hi = ix.span.hi; + expect(p, token::RBRACKET); + p.get_id(); // see ast_util::op_expr_callee_id + e = mk_pexpr(p, lo, hi, ast::expr_index(to_expr(e), ix)); + } + + _ { ret e; } + } + } + ret e; +} + +fn parse_prefix_expr(p: parser) -> pexpr { + let lo = p.span.lo; + let hi = p.span.hi; + + let ex; + alt p.token { + token::NOT { + p.bump(); + let e = to_expr(parse_prefix_expr(p)); + hi = e.span.hi; + p.get_id(); // see ast_util::op_expr_callee_id + ex = ast::expr_unary(ast::not, e); + } + token::BINOP(b) { + alt b { + token::MINUS { + p.bump(); + let e = to_expr(parse_prefix_expr(p)); + hi = e.span.hi; + p.get_id(); // see ast_util::op_expr_callee_id + ex = ast::expr_unary(ast::neg, e); + } + token::STAR { + p.bump(); + let e = to_expr(parse_prefix_expr(p)); + hi = e.span.hi; + ex = ast::expr_unary(ast::deref, e); + } + _ { ret parse_dot_or_call_expr(p); } + } + } + token::AT { + p.bump(); + let m = parse_mutability(p); + let e = to_expr(parse_prefix_expr(p)); + hi = e.span.hi; + ex = ast::expr_unary(ast::box(m), e); + } + token::TILDE { + p.bump(); + let m = parse_mutability(p); + let e = to_expr(parse_prefix_expr(p)); + hi = e.span.hi; + ex = ast::expr_unary(ast::uniq(m), e); + } + _ { ret parse_dot_or_call_expr(p); } + } + ret mk_pexpr(p, lo, hi, ex); +} + +type op_spec = {tok: token::token, op: ast::binop, prec: int}; + + +// FIXME make this a const, don't store it in parser state +fn prec_table() -> @[op_spec] { + ret @[// 'as' sits between here with 12 + {tok: token::BINOP(token::STAR), op: ast::mul, prec: 11}, + {tok: token::BINOP(token::SLASH), op: ast::div, prec: 11}, + {tok: token::BINOP(token::PERCENT), op: ast::rem, prec: 11}, + {tok: token::BINOP(token::PLUS), op: ast::add, prec: 10}, + {tok: token::BINOP(token::MINUS), op: ast::subtract, prec: 10}, + {tok: token::BINOP(token::LSL), op: ast::lsl, prec: 9}, + {tok: token::BINOP(token::LSR), op: ast::lsr, prec: 9}, + {tok: token::BINOP(token::ASR), op: ast::asr, prec: 9}, + {tok: token::BINOP(token::AND), op: ast::bitand, prec: 8}, + {tok: token::BINOP(token::CARET), op: ast::bitxor, prec: 7}, + {tok: token::BINOP(token::OR), op: ast::bitor, prec: 6}, + {tok: token::LT, op: ast::lt, prec: 4}, + {tok: token::LE, op: ast::le, prec: 4}, + {tok: token::GE, op: ast::ge, prec: 4}, + {tok: token::GT, op: ast::gt, prec: 4}, + {tok: token::EQEQ, op: ast::eq, prec: 3}, + {tok: token::NE, op: ast::ne, prec: 3}, + {tok: token::ANDAND, op: ast::and, prec: 2}, + {tok: token::OROR, op: ast::or, prec: 1}]; +} + +fn parse_binops(p: parser) -> @ast::expr { + ret parse_more_binops(p, parse_prefix_expr(p), 0); +} + +const unop_prec: int = 100; + +const as_prec: int = 12; + +fn parse_more_binops(p: parser, plhs: pexpr, min_prec: int) -> + @ast::expr { + let lhs = to_expr(plhs); + if expr_is_complete(p, plhs) { ret lhs; } + let peeked = p.token; + if peeked == token::BINOP(token::OR) && + p.restriction == RESTRICT_NO_BAR_OP { ret lhs; } + for cur: op_spec in *p.precs { + if cur.prec > min_prec && cur.tok == peeked { + p.bump(); + let expr = parse_prefix_expr(p); + let rhs = parse_more_binops(p, expr, cur.prec); + p.get_id(); // see ast_util::op_expr_callee_id + let bin = mk_pexpr(p, lhs.span.lo, rhs.span.hi, + ast::expr_binary(cur.op, lhs, rhs)); + ret parse_more_binops(p, bin, min_prec); + } + } + if as_prec > min_prec && eat_word(p, "as") { + let rhs = parse_ty(p, true); + let _as = + mk_pexpr(p, lhs.span.lo, rhs.span.hi, ast::expr_cast(lhs, rhs)); + ret parse_more_binops(p, _as, min_prec); + } + ret lhs; +} + +fn parse_assign_expr(p: parser) -> @ast::expr { + let lo = p.span.lo; + let lhs = parse_binops(p); + alt p.token { + token::EQ { + p.bump(); + let rhs = parse_expr(p); + ret mk_expr(p, lo, rhs.span.hi, ast::expr_assign(lhs, rhs)); + } + token::BINOPEQ(op) { + p.bump(); + let rhs = parse_expr(p); + let aop = ast::add; + alt op { + token::PLUS { aop = ast::add; } + token::MINUS { aop = ast::subtract; } + token::STAR { aop = ast::mul; } + token::SLASH { aop = ast::div; } + token::PERCENT { aop = ast::rem; } + token::CARET { aop = ast::bitxor; } + token::AND { aop = ast::bitand; } + token::OR { aop = ast::bitor; } + token::LSL { aop = ast::lsl; } + token::LSR { aop = ast::lsr; } + token::ASR { aop = ast::asr; } + } + p.get_id(); // see ast_util::op_expr_callee_id + ret mk_expr(p, lo, rhs.span.hi, ast::expr_assign_op(aop, lhs, rhs)); + } + token::LARROW { + p.bump(); + let rhs = parse_expr(p); + ret mk_expr(p, lo, rhs.span.hi, ast::expr_move(lhs, rhs)); + } + token::DARROW { + p.bump(); + let rhs = parse_expr(p); + ret mk_expr(p, lo, rhs.span.hi, ast::expr_swap(lhs, rhs)); + } + _ {/* fall through */ } + } + ret lhs; +} + +fn parse_if_expr_1(p: parser) -> + {cond: @ast::expr, + then: ast::blk, + els: option<@ast::expr>, + lo: uint, + hi: uint} { + let lo = p.last_span.lo; + let cond = parse_expr(p); + let thn = parse_block(p); + let els: option<@ast::expr> = none; + let hi = thn.span.hi; + if eat_word(p, "else") { + let elexpr = parse_else_expr(p); + els = some(elexpr); + hi = elexpr.span.hi; + } + ret {cond: cond, then: thn, els: els, lo: lo, hi: hi}; +} + +fn parse_if_expr(p: parser) -> @ast::expr { + if eat_word(p, "check") { + let q = parse_if_expr_1(p); + ret mk_expr(p, q.lo, q.hi, ast::expr_if_check(q.cond, q.then, q.els)); + } else { + let q = parse_if_expr_1(p); + ret mk_expr(p, q.lo, q.hi, ast::expr_if(q.cond, q.then, q.els)); + } +} + +// Parses: +// +// CC := [copy ID*; move ID*] +// +// where any part is optional and trailing ; is permitted. +fn parse_capture_clause(p: parser) -> @ast::capture_clause { + fn expect_opt_trailing_semi(p: parser) { + if !eat(p, token::SEMI) { + if p.token != token::RBRACKET { + p.fatal("expecting ; or ]"); + } + } + } + + fn eat_ident_list(p: parser) -> [@ast::capture_item] { + let res = []; + while true { + alt p.token { + token::IDENT(_, _) { + let id = p.get_id(); + let sp = ast_util::mk_sp(p.span.lo, p.span.hi); + let ident = parse_ident(p); + res += [@{id:id, name:ident, span:sp}]; + if !eat(p, token::COMMA) { + ret res; + } + } + + _ { ret res; } + } + } + std::util::unreachable(); + } + + let copies = []; + let moves = []; + + if eat(p, token::LBRACKET) { + while !eat(p, token::RBRACKET) { + if eat_word(p, "copy") { + copies += eat_ident_list(p); + expect_opt_trailing_semi(p); + } else if eat_word(p, "move") { + moves += eat_ident_list(p); + expect_opt_trailing_semi(p); + } else { + let s: str = "expecting send, copy, or move clause"; + p.fatal(s); + } + } + } + + ret @{copies: copies, moves: moves}; +} + +fn parse_fn_expr(p: parser, proto: ast::proto) -> @ast::expr { + let lo = p.last_span.lo; + let capture_clause = parse_capture_clause(p); + let decl = parse_fn_decl(p, ast::impure_fn); + let body = parse_block(p); + ret mk_expr(p, lo, body.span.hi, + ast::expr_fn(proto, decl, body, capture_clause)); +} + +fn parse_fn_block_expr(p: parser) -> @ast::expr { + let lo = p.last_span.lo; + let decl = parse_fn_block_decl(p); + let body = parse_block_tail(p, lo, ast::default_blk); + ret mk_expr(p, lo, body.span.hi, ast::expr_fn_block(decl, body)); +} + +fn parse_else_expr(p: parser) -> @ast::expr { + if eat_word(p, "if") { + ret parse_if_expr(p); + } else { + let blk = parse_block(p); + ret mk_expr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk)); + } +} + +fn parse_for_expr(p: parser) -> @ast::expr { + let lo = p.last_span.lo; + let decl = parse_local(p, false, false); + expect_word(p, "in"); + let seq = parse_expr(p); + let body = parse_block_no_value(p); + let hi = body.span.hi; + ret mk_expr(p, lo, hi, ast::expr_for(decl, seq, body)); +} + +fn parse_while_expr(p: parser) -> @ast::expr { + let lo = p.last_span.lo; + let cond = parse_expr(p); + let body = parse_block_no_value(p); + let hi = body.span.hi; + ret mk_expr(p, lo, hi, ast::expr_while(cond, body)); +} + +fn parse_do_while_expr(p: parser) -> @ast::expr { + let lo = p.last_span.lo; + let body = parse_block_no_value(p); + expect_word(p, "while"); + let cond = parse_expr(p); + let hi = cond.span.hi; + ret mk_expr(p, lo, hi, ast::expr_do_while(body, cond)); +} + +fn parse_alt_expr(p: parser) -> @ast::expr { + let lo = p.last_span.lo; + let mode = if eat_word(p, "check") { ast::alt_check } + else { ast::alt_exhaustive }; + let discriminant = parse_expr(p); + expect(p, token::LBRACE); + let arms: [ast::arm] = []; + while p.token != token::RBRACE { + let pats = parse_pats(p); + let guard = none; + if eat_word(p, "if") { guard = some(parse_expr(p)); } + let blk = parse_block(p); + arms += [{pats: pats, guard: guard, body: blk}]; + } + let hi = p.span.hi; + p.bump(); + ret mk_expr(p, lo, hi, ast::expr_alt(discriminant, arms, mode)); +} + +fn parse_expr(p: parser) -> @ast::expr { + ret parse_expr_res(p, UNRESTRICTED); +} + +fn parse_expr_or_hole(p: parser) -> option<@ast::expr> { + alt p.token { + token::UNDERSCORE { p.bump(); ret none; } + _ { ret some(parse_expr(p)); } + } +} + +fn parse_expr_res(p: parser, r: restriction) -> @ast::expr { + let old = p.restriction; + p.restriction = r; + let e = parse_assign_expr(p); + p.restriction = old; + ret e; +} + +fn parse_initializer(p: parser) -> option<ast::initializer> { + alt p.token { + token::EQ { + p.bump(); + ret some({op: ast::init_assign, expr: parse_expr(p)}); + } + token::LARROW { + p.bump(); + ret some({op: ast::init_move, expr: parse_expr(p)}); + } + // Now that the the channel is the first argument to receive, + // combining it with an initializer doesn't really make sense. + // case (token::RECV) { + // p.bump(); + // ret some(rec(op = ast::init_recv, + // expr = parse_expr(p))); + // } + _ { + ret none; + } + } +} + +fn parse_pats(p: parser) -> [@ast::pat] { + let pats = []; + while true { + pats += [parse_pat(p)]; + if p.token == token::BINOP(token::OR) { p.bump(); } else { break; } + } + ret pats; +} + +fn parse_pat(p: parser) -> @ast::pat { + let lo = p.span.lo; + let hi = p.span.hi; + let pat; + alt p.token { + token::UNDERSCORE { p.bump(); pat = ast::pat_wild; } + token::AT { + p.bump(); + let sub = parse_pat(p); + pat = ast::pat_box(sub); + hi = sub.span.hi; + } + token::TILDE { + p.bump(); + let sub = parse_pat(p); + pat = ast::pat_uniq(sub); + hi = sub.span.hi; + } + token::LBRACE { + p.bump(); + let fields = []; + let etc = false; + let first = true; + while p.token != token::RBRACE { + if first { first = false; } else { expect(p, token::COMMA); } + + if p.token == token::UNDERSCORE { + p.bump(); + if p.token != token::RBRACE { + p.fatal("expecting }, found " + + token::to_str(p.reader, p.token)); + } + etc = true; + break; + } + + let lo1 = p.last_span.lo; + let fieldname = parse_ident(p); + let hi1 = p.last_span.lo; + let fieldpath = ast_util::ident_to_path(ast_util::mk_sp(lo1, hi1), + fieldname); + let subpat; + if p.token == token::COLON { + p.bump(); + subpat = parse_pat(p); + } else { + if p.bad_expr_words.contains_key(fieldname) { + p.fatal("found " + fieldname + " in binding position"); + } + subpat = @{id: p.get_id(), + node: ast::pat_ident(fieldpath, none), + span: ast_util::mk_sp(lo, hi)}; + } + fields += [{ident: fieldname, pat: subpat}]; + } + hi = p.span.hi; + p.bump(); + pat = ast::pat_rec(fields, etc); + } + token::LPAREN { + p.bump(); + if p.token == token::RPAREN { + hi = p.span.hi; + p.bump(); + let lit = @{node: ast::lit_nil, span: ast_util::mk_sp(lo, hi)}; + let expr = mk_expr(p, lo, hi, ast::expr_lit(lit)); + pat = ast::pat_lit(expr); + } else { + let fields = [parse_pat(p)]; + while p.token == token::COMMA { + p.bump(); + fields += [parse_pat(p)]; + } + if vec::len(fields) == 1u { expect(p, token::COMMA); } + hi = p.span.hi; + expect(p, token::RPAREN); + pat = ast::pat_tup(fields); + } + } + tok { + if !is_ident(tok) || is_word(p, "true") || is_word(p, "false") { + let val = parse_expr_res(p, RESTRICT_NO_BAR_OP); + if eat_word(p, "to") { + let end = parse_expr_res(p, RESTRICT_NO_BAR_OP); + hi = end.span.hi; + pat = ast::pat_range(val, end); + } else { + hi = val.span.hi; + pat = ast::pat_lit(val); + } + } else if is_plain_ident(p) && + alt p.look_ahead(1u) { + token::LPAREN | token::LBRACKET | token::LT { false } + _ { true } + } { + let name = parse_value_path(p); + let sub = if eat(p, token::AT) { some(parse_pat(p)) } + else { none }; + pat = ast::pat_ident(name, sub); + } else { + let enum_path = parse_path_and_ty_param_substs(p, true); + hi = enum_path.span.hi; + let args: [@ast::pat]; + alt p.token { + token::LPAREN { + let a = + parse_seq(token::LPAREN, token::RPAREN, + seq_sep(token::COMMA), parse_pat, p); + args = a.node; + hi = a.span.hi; + } + _ { args = []; } + } + // at this point, we're not sure whether it's a enum or a bind + if vec::len(args) == 0u && + vec::len(enum_path.node.idents) == 1u { + pat = ast::pat_ident(enum_path, none); + } + else { + pat = ast::pat_enum(enum_path, args); + } + } + } + } + ret @{id: p.get_id(), node: pat, span: ast_util::mk_sp(lo, hi)}; +} + +fn parse_local(p: parser, is_mutbl: bool, + allow_init: bool) -> @ast::local { + let lo = p.span.lo; + let pat = parse_pat(p); + let ty = @spanned(lo, lo, ast::ty_infer); + if eat(p, token::COLON) { ty = parse_ty(p, false); } + let init = if allow_init { parse_initializer(p) } else { none }; + ret @spanned(lo, p.last_span.hi, + {is_mutbl: is_mutbl, ty: ty, pat: pat, + init: init, id: p.get_id()}); +} + +fn parse_let(p: parser) -> @ast::decl { + let is_mutbl = eat_word(p, "mut"); + let lo = p.span.lo; + let locals = [parse_local(p, is_mutbl, true)]; + while eat(p, token::COMMA) { + locals += [parse_local(p, is_mutbl, true)]; + } + ret @spanned(lo, p.last_span.hi, ast::decl_local(locals)); +} + +fn parse_instance_var(p:parser) -> ast::class_member { + let is_mutbl = ast::class_immutable; + expect_word(p, "let"); + if eat_word(p, "mut") || eat_word(p, "mutable") { + is_mutbl = ast::class_mutable; + } + if !is_plain_ident(p) { + p.fatal("expecting ident"); + } + let name = parse_ident(p); + expect(p, token::COLON); + let ty = parse_ty(p, false); + ret ast::instance_var(name, ty, is_mutbl, p.get_id()); +} + +fn parse_stmt(p: parser, first_item_attrs: [ast::attribute]) -> @ast::stmt { + fn check_expected_item(p: parser, current_attrs: [ast::attribute]) { + // If we have attributes then we should have an item + if vec::is_not_empty(current_attrs) { + p.fatal("expected item"); + } + } + + let lo = p.span.lo; + if is_word(p, "let") { + check_expected_item(p, first_item_attrs); + expect_word(p, "let"); + let decl = parse_let(p); + ret @spanned(lo, decl.span.hi, ast::stmt_decl(decl, p.get_id())); + } else { + let item_attrs; + alt parse_outer_attrs_or_ext(p, first_item_attrs) { + none { item_attrs = []; } + some(left(attrs)) { item_attrs = attrs; } + some(right(ext)) { + ret @spanned(lo, ext.span.hi, ast::stmt_expr(ext, p.get_id())); + } + } + + let item_attrs = first_item_attrs + item_attrs; + + alt parse_item(p, item_attrs) { + some(i) { + let hi = i.span.hi; + let decl = @spanned(lo, hi, ast::decl_item(i)); + ret @spanned(lo, hi, ast::stmt_decl(decl, p.get_id())); + } + none() { /* fallthrough */ } + } + + check_expected_item(p, item_attrs); + + // Remainder are line-expr stmts. + let e = parse_expr_res(p, RESTRICT_STMT_EXPR); + ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id())); + } +} + +fn expr_is_complete(p: parser, e: pexpr) -> bool { + log(debug, ("expr_is_complete", p.restriction, + print::pprust::expr_to_str(*e), + expr_requires_semi_to_be_stmt(*e))); + ret p.restriction == RESTRICT_STMT_EXPR && + !expr_requires_semi_to_be_stmt(*e); +} + +fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool { + alt e.node { + ast::expr_if(_, _, _) | ast::expr_if_check(_, _, _) + | ast::expr_alt(_, _, _) | ast::expr_block(_) + | ast::expr_do_while(_, _) | ast::expr_while(_, _) + | ast::expr_for(_, _, _) + | ast::expr_call(_, _, true) { + false + } + _ { true } + } +} + +fn stmt_ends_with_semi(stmt: ast::stmt) -> bool { + alt stmt.node { + ast::stmt_decl(d, _) { + ret alt d.node { + ast::decl_local(_) { true } + ast::decl_item(_) { false } + } + } + ast::stmt_expr(e, _) { + ret expr_requires_semi_to_be_stmt(e); + } + ast::stmt_semi(e, _) { + ret false; + } + } +} + +fn parse_block(p: parser) -> ast::blk { + let (attrs, blk) = parse_inner_attrs_and_block(p, false); + assert vec::is_empty(attrs); + ret blk; +} + +fn parse_inner_attrs_and_block( + p: parser, parse_attrs: bool) -> ([ast::attribute], ast::blk) { + + fn maybe_parse_inner_attrs_and_next( + p: parser, parse_attrs: bool) -> + {inner: [ast::attribute], next: [ast::attribute]} { + if parse_attrs { + parse_inner_attrs_and_next(p) + } else { + {inner: [], next: []} + } + } + + let lo = p.span.lo; + if eat_word(p, "unchecked") { + expect(p, token::LBRACE); + let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs); + ret (inner, parse_block_tail_(p, lo, ast::unchecked_blk, next)); + } else if eat_word(p, "unsafe") { + expect(p, token::LBRACE); + let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs); + ret (inner, parse_block_tail_(p, lo, ast::unsafe_blk, next)); + } else { + expect(p, token::LBRACE); + let {inner, next} = maybe_parse_inner_attrs_and_next(p, parse_attrs); + ret (inner, parse_block_tail_(p, lo, ast::default_blk, next)); + } +} + +fn parse_block_no_value(p: parser) -> ast::blk { + // We parse blocks that cannot have a value the same as any other block; + // the type checker will make sure that the tail expression (if any) has + // unit type. + ret parse_block(p); +} + +// Precondition: already parsed the '{' or '#{' +// I guess that also means "already parsed the 'impure'" if +// necessary, and this should take a qualifier. +// some blocks start with "#{"... +fn parse_block_tail(p: parser, lo: uint, s: ast::blk_check_mode) -> ast::blk { + parse_block_tail_(p, lo, s, []) +} + +fn parse_block_tail_(p: parser, lo: uint, s: ast::blk_check_mode, + first_item_attrs: [ast::attribute]) -> ast::blk { + let stmts = []; + let expr = none; + let view_items = maybe_parse_view_import_only(p, first_item_attrs); + let initial_attrs = first_item_attrs; + + if p.token == token::RBRACE && !vec::is_empty(initial_attrs) { + p.fatal("expected item"); + } + + while p.token != token::RBRACE { + alt p.token { + token::SEMI { + p.bump(); // empty + } + _ { + let stmt = parse_stmt(p, initial_attrs); + initial_attrs = []; + alt stmt.node { + ast::stmt_expr(e, stmt_id) { // Expression without semicolon: + alt p.token { + token::SEMI { + p.bump(); + stmts += [@{node: ast::stmt_semi(e, stmt_id) with *stmt}]; + } + token::RBRACE { + expr = some(e); + } + t { + if stmt_ends_with_semi(*stmt) { + p.fatal("expected ';' or '}' after expression but \ + found '" + token::to_str(p.reader, t) + + "'"); + } + stmts += [stmt]; + } + } + } + + _ { // All other kinds of statements: + stmts += [stmt]; + + if stmt_ends_with_semi(*stmt) { + expect(p, token::SEMI); + } + } + } + } + } + } + let hi = p.span.hi; + p.bump(); + let bloc = {view_items: view_items, stmts: stmts, expr: expr, + id: p.get_id(), rules: s}; + ret spanned(lo, hi, bloc); +} + +fn parse_ty_param(p: parser) -> ast::ty_param { + let bounds = []; + let ident = parse_ident(p); + if eat(p, token::COLON) { + while p.token != token::COMMA && p.token != token::GT { + if eat_word(p, "send") { bounds += [ast::bound_send]; } + else if eat_word(p, "copy") { bounds += [ast::bound_copy]; } + else { bounds += [ast::bound_iface(parse_ty(p, false))]; } + } + } + ret {ident: ident, id: p.get_id(), bounds: @bounds}; +} + +fn parse_ty_params(p: parser) -> [ast::ty_param] { + if eat(p, token::LT) { + parse_seq_to_gt(some(token::COMMA), parse_ty_param, p) + } else { [] } +} + +fn parse_fn_decl(p: parser, purity: ast::purity) + -> ast::fn_decl { + let inputs: ast::spanned<[ast::arg]> = + parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + parse_arg, p); + // Use the args list to translate each bound variable + // mentioned in a constraint to an arg index. + // Seems weird to do this in the parser, but I'm not sure how else to. + let constrs = []; + if p.token == token::COLON { + p.bump(); + constrs = parse_constrs({|x| parse_ty_constr(inputs.node, x) }, p); + } + let (ret_style, ret_ty) = parse_ret_ty(p); + ret {inputs: inputs.node, + output: ret_ty, + purity: purity, + cf: ret_style, + constraints: constrs}; +} + +fn parse_fn_block_decl(p: parser) -> ast::fn_decl { + let inputs = if eat(p, token::OROR) { + [] + } else { + parse_seq(token::BINOP(token::OR), + token::BINOP(token::OR), + seq_sep(token::COMMA), + parse_fn_block_arg, p).node + }; + let output = if eat(p, token::RARROW) { + parse_ty(p, false) + } else { + @spanned(p.span.lo, p.span.hi, ast::ty_infer) + }; + ret {inputs: inputs, + output: output, + purity: ast::impure_fn, + cf: ast::return_val, + constraints: []}; +} + +fn parse_fn_header(p: parser) -> {ident: ast::ident, tps: [ast::ty_param]} { + let id = parse_value_ident(p); + let ty_params = parse_ty_params(p); + ret {ident: id, tps: ty_params}; +} + +fn mk_item(p: parser, lo: uint, hi: uint, ident: ast::ident, node: ast::item_, + attrs: [ast::attribute]) -> @ast::item { + ret @{ident: ident, + attrs: attrs, + id: p.get_id(), + node: node, + span: ast_util::mk_sp(lo, hi)}; +} + +fn parse_item_fn(p: parser, purity: ast::purity, + attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let t = parse_fn_header(p); + let decl = parse_fn_decl(p, purity); + let (inner_attrs, body) = parse_inner_attrs_and_block(p, true); + let attrs = attrs + inner_attrs; + ret mk_item(p, lo, body.span.hi, t.ident, + ast::item_fn(decl, t.tps, body), attrs); +} + +fn parse_method_name(p: parser) -> ast::ident { + alt p.token { + token::BINOP(op) { p.bump(); token::binop_to_str(op) } + token::NOT { p.bump(); "!" } + token::LBRACKET { p.bump(); expect(p, token::RBRACKET); "[]" } + _ { + let id = parse_value_ident(p); + if id == "unary" && eat(p, token::BINOP(token::MINUS)) { "unary-" } + else { id } + } + } +} + +fn parse_method(p: parser) -> @ast::method { + let attrs = parse_outer_attributes(p); + let lo = p.span.lo, pur = parse_fn_purity(p); + let ident = parse_method_name(p); + let tps = parse_ty_params(p); + let decl = parse_fn_decl(p, pur); + let (inner_attrs, body) = parse_inner_attrs_and_block(p, true); + let attrs = attrs + inner_attrs; + @{ident: ident, attrs: attrs, tps: tps, decl: decl, body: body, + id: p.get_id(), span: ast_util::mk_sp(lo, body.span.hi)} +} + +fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo, ident = parse_ident(p), + tps = parse_ty_params(p), meths = parse_ty_methods(p); + ret mk_item(p, lo, p.last_span.hi, ident, + ast::item_iface(tps, meths), attrs); +} + +// Parses three variants (with the initial params always optional): +// impl <T: copy> of to_str for [T] { ... } +// impl name<T> of to_str for [T] { ... } +// impl name<T> for [T] { ... } +fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + fn wrap_path(p: parser, pt: @ast::path) -> @ast::ty { + @{node: ast::ty_path(pt, p.get_id()), span: pt.span} + } + let (ident, tps) = if !is_word(p, "of") { + if p.token == token::LT { (none, parse_ty_params(p)) } + else { (some(parse_ident(p)), parse_ty_params(p)) } + } else { (none, []) }; + let ifce = if eat_word(p, "of") { + let path = parse_path_and_ty_param_substs(p, false); + if option::is_none(ident) { + ident = some(path.node.idents[vec::len(path.node.idents) - 1u]); + } + some(wrap_path(p, path)) + } else { none }; + let ident = alt ident { + some(name) { name } + none { expect_word(p, "of"); fail; } + }; + expect_word(p, "for"); + let ty = parse_ty(p, false), meths = []; + expect(p, token::LBRACE); + while !eat(p, token::RBRACE) { meths += [parse_method(p)]; } + ret mk_item(p, lo, p.last_span.hi, ident, + ast::item_impl(tps, ifce, ty, meths), attrs); +} + +fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let ident = parse_value_ident(p); + let ty_params = parse_ty_params(p); + expect(p, token::LPAREN); + let arg_ident = parse_value_ident(p); + expect(p, token::COLON); + let t = parse_ty(p, false); + expect(p, token::RPAREN); + let dtor = parse_block_no_value(p); + let decl = + {inputs: + [{mode: ast::expl(ast::by_ref), ty: t, + ident: arg_ident, id: p.get_id()}], + output: @spanned(lo, lo, ast::ty_nil), + purity: ast::impure_fn, + cf: ast::return_val, + constraints: []}; + ret mk_item(p, lo, dtor.span.hi, ident, + ast::item_res(decl, ty_params, dtor, p.get_id(), p.get_id()), + attrs); +} + +fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let class_name = parse_value_ident(p); + let class_path = ident_to_path(p.last_span, class_name); + let ty_params = parse_ty_params(p); + expect(p, token::LBRACE); + let items: [@ast::class_item] = []; + let ctor_id = p.get_id(); + let the_ctor : option<(ast::fn_decl, ast::blk)> = none; + while p.token != token::RBRACE { + alt parse_class_item(p, class_path) { + ctor_decl(a_fn_decl, blk) { + the_ctor = some((a_fn_decl, blk)); + } + plain_decl(a_decl) { + items += [@{node: {privacy: ast::pub, decl: a_decl}, + span: p.last_span}]; + } + priv_decls(some_decls) { + items += vec::map(some_decls, {|d| + @{node: {privacy: ast::priv, decl: d}, + span: p.last_span}}); + } + } + } + p.bump(); + alt the_ctor { + some((ct_d, ct_b)) { ret mk_item(p, lo, p.last_span.hi, class_name, + ast::item_class(ty_params, items, ctor_id, ct_d, ct_b), attrs); } + /* + Is it strange for the parser to check this? + */ + none { /* parse error */ fail "Class with no ctor"; } + } +} + +// lets us identify the constructor declaration at +// parse time +// we don't really want just the fn_decl... +enum class_contents { ctor_decl(ast::fn_decl, ast::blk), + // assumed to be public + plain_decl(ast::class_member), + // contents of a priv section -- + // parse_class_item ensures that + // none of these are a ctor decl + priv_decls([ast::class_member])} + + fn parse_class_item(p:parser, class_name:@ast::path) -> class_contents { + if eat_word(p, "new") { + // Can ctors have attrs? + // result type is always the type of the class + let decl_ = parse_fn_decl(p, ast::impure_fn); + let decl = {output: @{node: ast::ty_path(class_name, p.get_id()), + span: decl_.output.span} + with decl_}; + let body = parse_block(p); + ret ctor_decl(decl, body); + } + // FIXME: refactor + else if eat_word(p, "priv") { + expect(p, token::LBRACE); + let results = []; + while p.token != token::RBRACE { + alt parse_item(p, []) { + some(i) { + results += [ast::class_method(i)]; + } + _ { + let a_var = parse_instance_var(p); + expect(p, token::SEMI); + results += [a_var]; + } + } + } + p.bump(); + ret priv_decls(results); + } + else { + // Probably need to parse attrs + alt parse_item(p, []) { + some(i) { + ret plain_decl(ast::class_method(i)); + } + _ { + let a_var = parse_instance_var(p); + expect(p, token::SEMI); + ret plain_decl(a_var); + } + } + } +} + +fn parse_mod_items(p: parser, term: token::token, + first_item_attrs: [ast::attribute]) -> ast::_mod { + // Shouldn't be any view items since we've already parsed an item attr + let view_items = maybe_parse_view(p, first_item_attrs); + let items: [@ast::item] = []; + let initial_attrs = first_item_attrs; + while p.token != term { + let attrs = initial_attrs + parse_outer_attributes(p); + #debug["parse_mod_items: parse_item(attrs=%?)", attrs]; + alt parse_item(p, attrs) { + some(i) { items += [i]; } + _ { + p.fatal("expected item but found '" + + token::to_str(p.reader, p.token) + "'"); + } + } + #debug["parse_mod_items: attrs=%?", attrs]; + initial_attrs = []; + } + + if vec::is_not_empty(initial_attrs) { + // We parsed attributes for the first item but didn't find the item + p.fatal("expected item"); + } + + ret {view_items: view_items, items: items}; +} + +fn parse_item_const(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let id = parse_value_ident(p); + expect(p, token::COLON); + let ty = parse_ty(p, false); + expect(p, token::EQ); + let e = parse_expr(p); + let hi = p.span.hi; + expect(p, token::SEMI); + ret mk_item(p, lo, hi, id, ast::item_const(ty, e), attrs); +} + +fn parse_item_mod(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let id = parse_ident(p); + expect(p, token::LBRACE); + let inner_attrs = parse_inner_attrs_and_next(p); + let first_item_outer_attrs = inner_attrs.next; + let m = parse_mod_items(p, token::RBRACE, first_item_outer_attrs); + let hi = p.span.hi; + expect(p, token::RBRACE); + ret mk_item(p, lo, hi, id, ast::item_mod(m), attrs + inner_attrs.inner); +} + +fn parse_item_native_fn(p: parser, attrs: [ast::attribute], + purity: ast::purity) -> @ast::native_item { + let lo = p.last_span.lo; + let t = parse_fn_header(p); + let decl = parse_fn_decl(p, purity); + let hi = p.span.hi; + expect(p, token::SEMI); + ret @{ident: t.ident, + attrs: attrs, + node: ast::native_item_fn(decl, t.tps), + id: p.get_id(), + span: ast_util::mk_sp(lo, hi)}; +} + +fn parse_fn_purity(p: parser) -> ast::purity { + if eat_word(p, "fn") { ast::impure_fn } + else if eat_word(p, "pure") { expect_word(p, "fn"); ast::pure_fn } + else if eat_word(p, "unsafe") { expect_word(p, "fn"); ast::unsafe_fn } + else { unexpected(p, p.token); } +} + +fn parse_native_item(p: parser, attrs: [ast::attribute]) -> + @ast::native_item { + parse_item_native_fn(p, attrs, parse_fn_purity(p)) +} + +fn parse_native_mod_items(p: parser, first_item_attrs: [ast::attribute]) -> + ast::native_mod { + // Shouldn't be any view items since we've already parsed an item attr + let view_items = + if vec::len(first_item_attrs) == 0u { + parse_native_view(p) + } else { [] }; + let items: [@ast::native_item] = []; + let initial_attrs = first_item_attrs; + while p.token != token::RBRACE { + let attrs = initial_attrs + parse_outer_attributes(p); + initial_attrs = []; + items += [parse_native_item(p, attrs)]; + } + ret {view_items: view_items, + items: items}; +} + +fn parse_item_native_mod(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + expect_word(p, "mod"); + let id = parse_ident(p); + expect(p, token::LBRACE); + let more_attrs = parse_inner_attrs_and_next(p); + let inner_attrs = more_attrs.inner; + let first_item_outer_attrs = more_attrs.next; + let m = parse_native_mod_items(p, first_item_outer_attrs); + let hi = p.span.hi; + expect(p, token::RBRACE); + ret mk_item(p, lo, hi, id, ast::item_native_mod(m), attrs + inner_attrs); +} + +fn parse_type_decl(p: parser) -> {lo: uint, ident: ast::ident} { + let lo = p.last_span.lo; + let id = parse_ident(p); + ret {lo: lo, ident: id}; +} + +fn parse_item_type(p: parser, attrs: [ast::attribute]) -> @ast::item { + let t = parse_type_decl(p); + let tps = parse_ty_params(p); + expect(p, token::EQ); + let ty = parse_ty(p, false); + let hi = p.span.hi; + expect(p, token::SEMI); + ret mk_item(p, t.lo, hi, t.ident, ast::item_ty(ty, tps), attrs); +} + +fn parse_item_enum(p: parser, attrs: [ast::attribute]) -> @ast::item { + let lo = p.last_span.lo; + let id = parse_ident(p); + let ty_params = parse_ty_params(p); + let variants: [ast::variant] = []; + // Newtype syntax + if p.token == token::EQ { + if p.bad_expr_words.contains_key(id) { + p.fatal("found " + id + " in enum constructor position"); + } + p.bump(); + let ty = parse_ty(p, false); + expect(p, token::SEMI); + let variant = + spanned(ty.span.lo, ty.span.hi, + {name: id, + attrs: [], + args: [{ty: ty, id: p.get_id()}], + id: p.get_id(), + disr_expr: none}); + ret mk_item(p, lo, ty.span.hi, id, + ast::item_enum([variant], ty_params), attrs); + } + expect(p, token::LBRACE); + + let all_nullary = true, have_disr = false; + + while p.token != token::RBRACE { + let variant_attrs = parse_outer_attributes(p); + let vlo = p.span.lo; + let ident = parse_value_ident(p); + let args = [], disr_expr = none; + if p.token == token::LPAREN { + all_nullary = false; + let arg_tys = parse_seq(token::LPAREN, token::RPAREN, + seq_sep(token::COMMA), + {|p| parse_ty(p, false)}, p); + for ty in arg_tys.node { + args += [{ty: ty, id: p.get_id()}]; + } + } else if eat(p, token::EQ) { + have_disr = true; + disr_expr = some(parse_expr(p)); + } + + let vr = {name: ident, attrs: variant_attrs, + args: args, id: p.get_id(), + disr_expr: disr_expr}; + variants += [spanned(vlo, p.last_span.hi, vr)]; + + if !eat(p, token::COMMA) { break; } + } + expect(p, token::RBRACE); + if (have_disr && !all_nullary) { + p.fatal("discriminator values can only be used with a c-like enum"); + } + ret mk_item(p, lo, p.last_span.hi, id, + ast::item_enum(variants, ty_params), attrs); +} + +fn parse_fn_ty_proto(p: parser) -> ast::proto { + alt p.token { + token::AT { + p.bump(); + ast::proto_box + } + token::TILDE { + p.bump(); + ast::proto_uniq + } + token::BINOP(token::AND) { + p.bump(); + ast::proto_block + } + _ { + ast::proto_any + } + } +} + +fn fn_expr_lookahead(tok: token::token) -> bool { + alt tok { + token::LPAREN | token::AT | token::TILDE | token::BINOP(_) { + true + } + _ { + false + } + } +} + +fn parse_item(p: parser, attrs: [ast::attribute]) -> option<@ast::item> { + if eat_word(p, "const") { + ret some(parse_item_const(p, attrs)); + } else if is_word(p, "fn") && !fn_expr_lookahead(p.look_ahead(1u)) { + p.bump(); + ret some(parse_item_fn(p, ast::impure_fn, attrs)); + } else if eat_word(p, "pure") { + expect_word(p, "fn"); + ret some(parse_item_fn(p, ast::pure_fn, attrs)); + } else if is_word(p, "unsafe") && p.look_ahead(1u) != token::LBRACE { + p.bump(); + expect_word(p, "fn"); + ret some(parse_item_fn(p, ast::unsafe_fn, attrs)); + } else if eat_word(p, "crust") { + expect_word(p, "fn"); + ret some(parse_item_fn(p, ast::crust_fn, attrs)); + } else if eat_word(p, "mod") { + ret some(parse_item_mod(p, attrs)); + } else if eat_word(p, "native") { + ret some(parse_item_native_mod(p, attrs)); + } if eat_word(p, "type") { + ret some(parse_item_type(p, attrs)); + } else if eat_word(p, "enum") { + ret some(parse_item_enum(p, attrs)); + } else if eat_word(p, "iface") { + ret some(parse_item_iface(p, attrs)); + } else if eat_word(p, "impl") { + ret some(parse_item_impl(p, attrs)); + } else if eat_word(p, "resource") { + ret some(parse_item_res(p, attrs)); + } else if eat_word(p, "class") { + ret some(parse_item_class(p, attrs)); + } +else { ret none; } +} + +// A type to distingush between the parsing of item attributes or syntax +// extensions, which both begin with token.POUND +type attr_or_ext = option<either::t<[ast::attribute], @ast::expr>>; + +fn parse_outer_attrs_or_ext( + p: parser, + first_item_attrs: [ast::attribute]) -> attr_or_ext { + let expect_item_next = vec::is_not_empty(first_item_attrs); + if p.token == token::POUND { + let lo = p.span.lo; + if p.look_ahead(1u) == token::LBRACKET { + p.bump(); + let first_attr = parse_attribute_naked(p, ast::attr_outer, lo); + ret some(left([first_attr] + parse_outer_attributes(p))); + } else if !(p.look_ahead(1u) == token::LT + || p.look_ahead(1u) == token::LBRACKET + || expect_item_next) { + p.bump(); + ret some(right(parse_syntax_ext_naked(p, lo))); + } else { ret none; } + } else { ret none; } +} + +// Parse attributes that appear before an item +fn parse_outer_attributes(p: parser) -> [ast::attribute] { + let attrs: [ast::attribute] = []; + while p.token == token::POUND { + attrs += [parse_attribute(p, ast::attr_outer)]; + } + ret attrs; +} + +fn parse_attribute(p: parser, style: ast::attr_style) -> ast::attribute { + let lo = p.span.lo; + expect(p, token::POUND); + ret parse_attribute_naked(p, style, lo); +} + +fn parse_attribute_naked(p: parser, style: ast::attr_style, lo: uint) -> + ast::attribute { + expect(p, token::LBRACKET); + let meta_item = parse_meta_item(p); + expect(p, token::RBRACKET); + let hi = p.span.hi; + ret spanned(lo, hi, {style: style, value: *meta_item}); +} + +// Parse attributes that appear after the opening of an item, each terminated +// by a semicolon. In addition to a vector of inner attributes, this function +// also returns a vector that may contain the first outer attribute of the +// next item (since we can't know whether the attribute is an inner attribute +// of the containing item or an outer attribute of the first contained item +// until we see the semi). +fn parse_inner_attrs_and_next(p: parser) -> + {inner: [ast::attribute], next: [ast::attribute]} { + let inner_attrs: [ast::attribute] = []; + let next_outer_attrs: [ast::attribute] = []; + while p.token == token::POUND { + if p.look_ahead(1u) != token::LBRACKET { + // This is an extension + break; + } + let attr = parse_attribute(p, ast::attr_inner); + if p.token == token::SEMI { + p.bump(); + inner_attrs += [attr]; + } else { + // It's not really an inner attribute + let outer_attr = + spanned(attr.span.lo, attr.span.hi, + {style: ast::attr_outer, value: attr.node.value}); + next_outer_attrs += [outer_attr]; + break; + } + } + ret {inner: inner_attrs, next: next_outer_attrs}; +} + +fn parse_meta_item(p: parser) -> @ast::meta_item { + let lo = p.span.lo; + let ident = parse_ident(p); + alt p.token { + token::EQ { + p.bump(); + let lit = parse_lit(p); + let hi = p.span.hi; + ret @spanned(lo, hi, ast::meta_name_value(ident, lit)); + } + token::LPAREN { + let inner_items = parse_meta_seq(p); + let hi = p.span.hi; + ret @spanned(lo, hi, ast::meta_list(ident, inner_items)); + } + _ { + let hi = p.span.hi; + ret @spanned(lo, hi, ast::meta_word(ident)); + } + } +} + +fn parse_meta_seq(p: parser) -> [@ast::meta_item] { + ret parse_seq(token::LPAREN, token::RPAREN, seq_sep(token::COMMA), + parse_meta_item, p).node; +} + +fn parse_optional_meta(p: parser) -> [@ast::meta_item] { + alt p.token { token::LPAREN { ret parse_meta_seq(p); } _ { ret []; } } +} + +fn parse_use(p: parser) -> ast::view_item_ { + let ident = parse_ident(p); + let metadata = parse_optional_meta(p); + ret ast::view_item_use(ident, metadata, p.get_id()); +} + +fn parse_view_path(p: parser) -> @ast::view_path { + let lo = p.span.lo; + let first_ident = parse_ident(p); + let path = [first_ident]; + #debug("parsed view_path: %s", first_ident); + alt p.token { + token::EQ { + // x = foo::bar + p.bump(); + path = [parse_ident(p)]; + while p.token == token::MOD_SEP { + p.bump(); + let id = parse_ident(p); + path += [id]; + } + let hi = p.span.hi; + ret @spanned(lo, hi, + ast::view_path_simple(first_ident, + @path, p.get_id())); + } + + token::MOD_SEP { + // foo::bar or foo::{a,b,c} or foo::* + while p.token == token::MOD_SEP { + p.bump(); + + alt p.token { + + token::IDENT(i, _) { + p.bump(); + path += [p.get_str(i)]; + } + + // foo::bar::{a,b,c} + token::LBRACE { + let idents = + parse_seq(token::LBRACE, token::RBRACE, + seq_sep(token::COMMA), + parse_path_list_ident, p).node; + let hi = p.span.hi; + ret @spanned(lo, hi, + ast::view_path_list(@path, idents, + p.get_id())); + } + + // foo::bar::* + token::BINOP(token::STAR) { + p.bump(); + let hi = p.span.hi; + ret @spanned(lo, hi, + ast::view_path_glob(@path, + p.get_id())); + } + + _ { break; } + } + } + } + _ { } + } + let hi = p.span.hi; + let last = path[vec::len(path) - 1u]; + ret @spanned(lo, hi, + ast::view_path_simple(last, @path, + p.get_id())); +} + +fn parse_view_paths(p: parser) -> [@ast::view_path] { + let vp = [parse_view_path(p)]; + while p.token == token::COMMA { + p.bump(); + vp += [parse_view_path(p)]; + } + ret vp; +} + +fn parse_view_item(p: parser) -> @ast::view_item { + let lo = p.span.lo; + let the_item = + if eat_word(p, "use") { + parse_use(p) + } else if eat_word(p, "import") { + ast::view_item_import(parse_view_paths(p)) + } else if eat_word(p, "export") { + ast::view_item_export(parse_view_paths(p)) + } else { + fail + }; + let hi = p.span.lo; + expect(p, token::SEMI); + ret @spanned(lo, hi, the_item); +} + +fn is_view_item(p: parser) -> bool { + alt p.token { + token::IDENT(sid, false) { + let st = p.get_str(sid); + ret str::eq(st, "use") || str::eq(st, "import") || + str::eq(st, "export"); + } + _ { ret false; } + } +} + +fn maybe_parse_view( + p: parser, + first_item_attrs: [ast::attribute]) -> [@ast::view_item] { + + maybe_parse_view_while(p, first_item_attrs, is_view_item) +} + +fn maybe_parse_view_import_only( + p: parser, + first_item_attrs: [ast::attribute]) -> [@ast::view_item] { + + maybe_parse_view_while(p, first_item_attrs, bind is_word(_, "import")) +} + +fn maybe_parse_view_while( + p: parser, + first_item_attrs: [ast::attribute], + f: fn@(parser) -> bool) -> [@ast::view_item] { + + if vec::len(first_item_attrs) == 0u { + let items = []; + while f(p) { items += [parse_view_item(p)]; } + ret items; + } else { + // Shouldn't be any view items since we've already parsed an item attr + ret []; + } +} + +fn parse_native_view(p: parser) -> [@ast::view_item] { + maybe_parse_view_while(p, [], is_view_item) +} + +fn parse_crate_from_source_file(input: str, cfg: ast::crate_cfg, + sess: parse_sess) -> @ast::crate { + let p = new_parser_from_file(sess, cfg, input, SOURCE_FILE); + let r = parse_crate_mod(p, cfg); + sess.chpos = p.reader.chpos; + sess.byte_pos = sess.byte_pos + p.reader.pos; + ret r; +} + + +fn parse_expr_from_source_str(name: str, source: @str, cfg: ast::crate_cfg, + sess: parse_sess) -> @ast::expr { + let p = new_parser_from_source_str(sess, cfg, name, fss_none, source); + let r = parse_expr(p); + sess.chpos = p.reader.chpos; + sess.byte_pos = sess.byte_pos + p.reader.pos; + ret r; +} + +fn parse_from_source_str<T>(f: fn (p: parser) -> T, + name: str, ss: codemap::file_substr, + source: @str, cfg: ast::crate_cfg, + sess: parse_sess) + -> T +{ + let p = new_parser_from_source_str(sess, cfg, name, ss, source); + let r = f(p); + if !p.reader.is_eof() { + p.reader.fatal("expected end-of-string"); + } + sess.chpos = p.reader.chpos; + sess.byte_pos = sess.byte_pos + p.reader.pos; + ret r; +} + +fn parse_crate_from_source_str(name: str, source: @str, cfg: ast::crate_cfg, + sess: parse_sess) -> @ast::crate { + let p = new_parser_from_source_str(sess, cfg, name, fss_none, source); + let r = parse_crate_mod(p, cfg); + sess.chpos = p.reader.chpos; + sess.byte_pos = sess.byte_pos + p.reader.pos; + ret r; +} + +// Parses a source module as a crate +fn parse_crate_mod(p: parser, _cfg: ast::crate_cfg) -> @ast::crate { + let lo = p.span.lo; + let crate_attrs = parse_inner_attrs_and_next(p); + let first_item_outer_attrs = crate_attrs.next; + let m = parse_mod_items(p, token::EOF, first_item_outer_attrs); + ret @spanned(lo, p.span.lo, + {directives: [], + module: m, + attrs: crate_attrs.inner, + config: p.cfg}); +} + +fn parse_str(p: parser) -> str { + alt p.token { + token::LIT_STR(s) { p.bump(); p.get_str(s) } + _ { + p.fatal("expected string literal") + } + } +} + +// Logic for parsing crate files (.rc) +// +// Each crate file is a sequence of directives. +// +// Each directive imperatively extends its environment with 0 or more items. +fn parse_crate_directive(p: parser, first_outer_attr: [ast::attribute]) -> + ast::crate_directive { + + // Collect the next attributes + let outer_attrs = first_outer_attr + parse_outer_attributes(p); + // In a crate file outer attributes are only going to apply to mods + let expect_mod = vec::len(outer_attrs) > 0u; + + let lo = p.span.lo; + if expect_mod || is_word(p, "mod") { + expect_word(p, "mod"); + let id = parse_ident(p); + alt p.token { + // mod x = "foo.rs"; + token::SEMI { + let hi = p.span.hi; + p.bump(); + ret spanned(lo, hi, ast::cdir_src_mod(id, outer_attrs)); + } + // mod x = "foo_dir" { ...directives... } + token::LBRACE { + p.bump(); + let inner_attrs = parse_inner_attrs_and_next(p); + let mod_attrs = outer_attrs + inner_attrs.inner; + let next_outer_attr = inner_attrs.next; + let cdirs = + parse_crate_directives(p, token::RBRACE, next_outer_attr); + let hi = p.span.hi; + expect(p, token::RBRACE); + ret spanned(lo, hi, + ast::cdir_dir_mod(id, cdirs, mod_attrs)); + } + t { unexpected(p, t); } + } + } else if is_view_item(p) { + let vi = parse_view_item(p); + ret spanned(lo, vi.span.hi, ast::cdir_view_item(vi)); + } else { ret p.fatal("expected crate directive"); } +} + +fn parse_crate_directives(p: parser, term: token::token, + first_outer_attr: [ast::attribute]) -> + [@ast::crate_directive] { + + // This is pretty ugly. If we have an outer attribute then we can't accept + // seeing the terminator next, so if we do see it then fail the same way + // parse_crate_directive would + if vec::len(first_outer_attr) > 0u && p.token == term { + expect_word(p, "mod"); + } + + let cdirs: [@ast::crate_directive] = []; + let first_outer_attr = first_outer_attr; + while p.token != term { + let cdir = @parse_crate_directive(p, first_outer_attr); + cdirs += [cdir]; + first_outer_attr = []; + } + ret cdirs; +} + +fn parse_crate_from_crate_file(input: str, cfg: ast::crate_cfg, + sess: parse_sess) -> @ast::crate { + let p = new_parser_from_file(sess, cfg, input, CRATE_FILE); + let lo = p.span.lo; + let prefix = std::fs::dirname(p.reader.filemap.name); + let leading_attrs = parse_inner_attrs_and_next(p); + let crate_attrs = leading_attrs.inner; + let first_cdir_attr = leading_attrs.next; + let cdirs = parse_crate_directives(p, token::EOF, first_cdir_attr); + sess.chpos = p.reader.chpos; + sess.byte_pos = sess.byte_pos + p.reader.pos; + let cx = + @{p: p, + sess: sess, + cfg: p.cfg}; + let (companionmod, _) = fs::splitext(fs::basename(input)); + let (m, attrs) = eval::eval_crate_directives_to_mod( + cx, cdirs, prefix, option::some(companionmod)); + let hi = p.span.hi; + expect(p, token::EOF); + ret @spanned(lo, hi, + {directives: cdirs, + module: m, + attrs: crate_attrs + attrs, + config: p.cfg}); +} + +fn parse_crate_from_file(input: str, cfg: ast::crate_cfg, sess: parse_sess) -> + @ast::crate { + if str::ends_with(input, ".rc") { + parse_crate_from_crate_file(input, cfg, sess) + } else if str::ends_with(input, ".rs") { + parse_crate_from_source_file(input, cfg, sess) + } else { + sess.span_diagnostic.handler().fatal("unknown input file type: " + + input) + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/parse/token.rs b/src/rustc/syntax/parse/token.rs new file mode 100644 index 00000000000..60949f7793c --- /dev/null +++ b/src/rustc/syntax/parse/token.rs @@ -0,0 +1,199 @@ + +import util::interner; +import lexer::reader; + +type str_num = uint; + +enum binop { + PLUS, + MINUS, + STAR, + SLASH, + PERCENT, + CARET, + AND, + OR, + LSL, + LSR, + ASR, +} + +enum token { + /* Expression-operator symbols. */ + EQ, + LT, + LE, + EQEQ, + NE, + GE, + GT, + ANDAND, + OROR, + NOT, + TILDE, + BINOP(binop), + BINOPEQ(binop), + + /* Structural symbols */ + AT, + DOT, + ELLIPSIS, + COMMA, + SEMI, + COLON, + MOD_SEP, + RARROW, + LARROW, + DARROW, + LPAREN, + RPAREN, + LBRACKET, + RBRACKET, + LBRACE, + RBRACE, + POUND, + POUND_LBRACE, + POUND_LT, + + DOLLAR_LPAREN, + DOLLAR_NUM(uint), + + /* Literals */ + LIT_INT(i64, ast::int_ty), + LIT_UINT(u64, ast::uint_ty), + LIT_FLOAT(str_num, ast::float_ty), + LIT_STR(str_num), + LIT_BOOL(bool), + + /* Name components */ + IDENT(str_num, bool), + IDX(int), + UNDERSCORE, + BRACEQUOTE(str_num), + EOF, + +} + +fn binop_to_str(o: binop) -> str { + alt o { + PLUS { ret "+"; } + MINUS { ret "-"; } + STAR { ret "*"; } + SLASH { ret "/"; } + PERCENT { ret "%"; } + CARET { ret "^"; } + AND { ret "&"; } + OR { ret "|"; } + LSL { ret "<<"; } + LSR { ret ">>"; } + ASR { ret ">>>"; } + } +} + +fn to_str(r: reader, t: token) -> str { + alt t { + EQ { ret "="; } + LT { ret "<"; } + LE { ret "<="; } + EQEQ { ret "=="; } + NE { ret "!="; } + GE { ret ">="; } + GT { ret ">"; } + NOT { ret "!"; } + TILDE { ret "~"; } + OROR { ret "||"; } + ANDAND { ret "&&"; } + BINOP(op) { ret binop_to_str(op); } + BINOPEQ(op) { ret binop_to_str(op) + "="; } + + /* Structural symbols */ + AT { + ret "@"; + } + DOT { ret "."; } + ELLIPSIS { ret "..."; } + COMMA { ret ","; } + SEMI { ret ";"; } + COLON { ret ":"; } + MOD_SEP { ret "::"; } + RARROW { ret "->"; } + LARROW { ret "<-"; } + DARROW { ret "<->"; } + LPAREN { ret "("; } + RPAREN { ret ")"; } + LBRACKET { ret "["; } + RBRACKET { ret "]"; } + LBRACE { ret "{"; } + RBRACE { ret "}"; } + POUND { ret "#"; } + POUND_LBRACE { ret "#{"; } + POUND_LT { ret "#<"; } + + DOLLAR_LPAREN { ret "$("; } + DOLLAR_NUM(u) { + ret "$" + uint::to_str(u as uint, 10u); + } + + /* Literals */ + LIT_INT(c, ast::ty_char) { + // FIXME: escape. + let tmp = "'"; + str::push_char(tmp, c as char); + str::push_char(tmp, '\''); + ret tmp; + } + LIT_INT(i, t) { + ret int::to_str(i as int, 10u) + ast_util::int_ty_to_str(t); + } + LIT_UINT(u, t) { + ret uint::to_str(u as uint, 10u) + ast_util::uint_ty_to_str(t); + } + LIT_FLOAT(s, t) { + ret interner::get::<str>(*r.interner, s) + + ast_util::float_ty_to_str(t); + } + LIT_STR(s) { // FIXME: escape. + ret "\"" + interner::get::<str>(*r.interner, s) + "\""; + } + LIT_BOOL(b) { if b { ret "true"; } else { ret "false"; } } + + /* Name components */ + IDENT(s, _) { + ret interner::get::<str>(*r.interner, s); + } + IDX(i) { ret "_" + int::to_str(i, 10u); } + UNDERSCORE { ret "_"; } + BRACEQUOTE(_) { ret "<bracequote>"; } + EOF { ret "<eof>"; } + } +} + + +pure fn can_begin_expr(t: token) -> bool { + alt t { + LPAREN { true } + LBRACE { true } + LBRACKET { true } + IDENT(_, _) { true } + UNDERSCORE { true } + TILDE { true } + LIT_INT(_, _) { true } + LIT_UINT(_, _) { true } + LIT_FLOAT(_, _) { true } + LIT_STR(_) { true } + POUND { true } + AT { true } + NOT { true } + BINOP(MINUS) { true } + BINOP(STAR) { true } + MOD_SEP { true } + _ { false } + } +} + +// Local Variables: +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/syntax/print/pp.rs b/src/rustc/syntax/print/pp.rs new file mode 100644 index 00000000000..0b55bb8c356 --- /dev/null +++ b/src/rustc/syntax/print/pp.rs @@ -0,0 +1,526 @@ + +import std::io; +import io::writer_util; + +/* + * This pretty-printer is a direct reimplementation of Philip Karlton's + * Mesa pretty-printer, as described in appendix A of + * + * STAN-CS-79-770: "Pretty Printing", by Derek C. Oppen. + * Stanford Department of Computer Science, 1979. + * + * The algorithm's aim is to break a stream into as few lines as possible + * while respecting the indentation-consistency requirements of the enclosing + * block, and avoiding breaking at silly places on block boundaries, for + * example, between "x" and ")" in "x)". + * + * I am implementing this algorithm because it comes with 20 pages of + * documentation explaining its theory, and because it addresses the set of + * concerns I've seen other pretty-printers fall down on. Weirdly. Even though + * it's 32 years old and not written in Haskell. What can I say? + * + * Despite some redundancies and quirks in the way it's implemented in that + * paper, I've opted to keep the implementation here as similar as I can, + * changing only what was blatantly wrong, a typo, or sufficiently + * non-idiomatic rust that it really stuck out. + * + * In particular you'll see a certain amount of churn related to INTEGER vs. + * CARDINAL in the Mesa implementation. Mesa apparently interconverts the two + * somewhat readily? In any case, I've used uint for indices-in-buffers and + * ints for character-sizes-and-indentation-offsets. This respects the need + * for ints to "go negative" while carrying a pending-calculation balance, and + * helps differentiate all the numbers flying around internally (slightly). + * + * I also inverted the indentation arithmetic used in the print stack, since + * the Mesa implementation (somewhat randomly) stores the offset on the print + * stack in terms of margin-col rather than col itself. I store col. + * + * I also implemented a small change in the STRING token, in that I store an + * explicit length for the string. For most tokens this is just the length of + * the accompanying string. But it's necessary to permit it to differ, for + * encoding things that are supposed to "go on their own line" -- certain + * classes of comment and blank-line -- where relying on adjacent + * hardbreak-like BREAK tokens with long blankness indication doesn't actually + * work. To see why, consider when there is a "thing that should be on its own + * line" between two long blocks, say functions. If you put a hardbreak after + * each function (or before each) and the breaking algorithm decides to break + * there anyways (because the functions themselves are long) you wind up with + * extra blank lines. If you don't put hardbreaks you can wind up with the + * "thing which should be on its own line" not getting its own line in the + * rare case of "really small functions" or such. This re-occurs with comments + * and explicit blank lines. So in those cases we use a string with a payload + * we want isolated to a line and an explicit length that's huge, surrounded + * by two zero-length breaks. The algorithm will try its best to fit it on a + * line (which it can't) and so naturally place the content on its own line to + * avoid combining it with other lines and making matters even worse. + */ +enum breaks { consistent, inconsistent, } + +type break_t = {offset: int, blank_space: int}; + +type begin_t = {offset: int, breaks: breaks}; + +enum token { STRING(str, int), BREAK(break_t), BEGIN(begin_t), END, EOF, } + +fn tok_str(t: token) -> str { + alt t { + STRING(s, len) { ret #fmt["STR(%s,%d)", s, len]; } + BREAK(_) { ret "BREAK"; } + BEGIN(_) { ret "BEGIN"; } + END { ret "END"; } + EOF { ret "EOF"; } + } +} + +fn buf_str(toks: [mutable token], szs: [mutable int], left: uint, right: uint, + lim: uint) -> str { + let n = vec::len(toks); + assert (n == vec::len(szs)); + let i = left; + let L = lim; + let s = "["; + while i != right && L != 0u { + L -= 1u; + if i != left { s += ", "; } + s += #fmt["%d=%s", szs[i], tok_str(toks[i])]; + i += 1u; + i %= n; + } + s += "]"; + ret s; +} + +enum print_stack_break { fits, broken(breaks), } + +type print_stack_elt = {offset: int, pbreak: print_stack_break}; + +const size_infinity: int = 0xffff; + +fn mk_printer(out: io::writer, linewidth: uint) -> printer { + // Yes 3, it makes the ring buffers big enough to never + // fall behind. + let n: uint = 3u * linewidth; + #debug("mk_printer %u", linewidth); + let token: [mutable token] = vec::to_mut(vec::init_elt(n, EOF)); + let size: [mutable int] = vec::to_mut(vec::init_elt(n, 0)); + let scan_stack: [mutable uint] = vec::to_mut(vec::init_elt(n, 0u)); + let print_stack: [print_stack_elt] = []; + @{out: out, + buf_len: n, + mutable margin: linewidth as int, + mutable space: linewidth as int, + mutable left: 0u, + mutable right: 0u, + mutable token: token, + mutable size: size, + mutable left_total: 0, + mutable right_total: 0, + mutable scan_stack: scan_stack, + mutable scan_stack_empty: true, + mutable top: 0u, + mutable bottom: 0u, + mutable print_stack: print_stack, + mutable pending_indentation: 0} +} + + +/* + * In case you do not have the paper, here is an explanation of what's going + * on. + * + * There is a stream of input tokens flowing through this printer. + * + * The printer buffers up to 3N tokens inside itself, where N is linewidth. + * Yes, linewidth is chars and tokens are multi-char, but in the worst + * case every token worth buffering is 1 char long, so it's ok. + * + * Tokens are STRING, BREAK, and BEGIN/END to delimit blocks. + * + * BEGIN tokens can carry an offset, saying "how far to indent when you break + * inside here", as well as a flag indicating "consistent" or "inconsistent" + * breaking. Consistent breaking means that after the first break, no attempt + * will be made to flow subsequent breaks together onto lines. Inconsistent + * is the opposite. Inconsistent breaking example would be, say: + * + * foo(hello, there, good, friends) + * + * breaking inconsistently to become + * + * foo(hello, there + * good, friends); + * + * whereas a consistent breaking would yield: + * + * foo(hello, + * there + * good, + * friends); + * + * That is, in the consistent-break blocks we value vertical alignment + * more than the ability to cram stuff onto a line. But in all cases if it + * can make a block a one-liner, it'll do so. + * + * Carrying on with high-level logic: + * + * The buffered tokens go through a ring-buffer, 'tokens'. The 'left' and + * 'right' indices denote the active portion of the ring buffer as well as + * describing hypothetical points-in-the-infinite-stream at most 3N tokens + * apart (i.e. "not wrapped to ring-buffer boundaries"). The paper will switch + * between using 'left' and 'right' terms to denote the wrapepd-to-ring-buffer + * and point-in-infinite-stream senses freely. + * + * There is a parallel ring buffer, 'size', that holds the calculated size of + * each token. Why calculated? Because for BEGIN/END pairs, the "size" + * includes everything betwen the pair. That is, the "size" of BEGIN is + * actually the sum of the sizes of everything between BEGIN and the paired + * END that follows. Since that is arbitrarily far in the future, 'size' is + * being rewritten regularly while the printer runs; in fact most of the + * machinery is here to work out 'size' entries on the fly (and give up when + * they're so obviously over-long that "infinity" is a good enough + * approximation for purposes of line breaking). + * + * The "input side" of the printer is managed as an abstract process called + * SCAN, which uses 'scan_stack', 'scan_stack_empty', 'top' and 'bottom', to + * manage calculating 'size'. SCAN is, in other words, the process of + * calculating 'size' entries. + * + * The "output side" of the printer is managed by an abstract process called + * PRINT, which uses 'print_stack', 'margin' and 'space' to figure out what to + * do with each token/size pair it consumes as it goes. It's trying to consume + * the entire buffered window, but can't output anything until the size is >= + * 0 (sizes are set to negative while they're pending calculation). + * + * So SCAN takeks input and buffers tokens and pending calculations, while + * PRINT gobbles up completed calculations and tokens from the buffer. The + * theory is that the two can never get more than 3N tokens apart, because + * once there's "obviously" too much data to fit on a line, in a size + * calculation, SCAN will write "infinity" to the size and let PRINT consume + * it. + * + * In this implementation (following the paper, again) the SCAN process is + * the method called 'pretty_print', and the 'PRINT' process is the method + * called 'print'. + */ +type printer = @{ + out: io::writer, + buf_len: uint, + mutable margin: int, // width of lines we're constrained to + mutable space: int, // number of spaces left on line + mutable left: uint, // index of left side of input stream + mutable right: uint, // index of right side of input stream + mutable token: [mutable token], // ring-buffr stream goes through + mutable size: [mutable int], // ring-buffer of calculated sizes + mutable left_total: int, // running size of stream "...left" + mutable right_total: int, // running size of stream "...right" + // pseudo-stack, really a ring too. Holds the + // primary-ring-buffers index of the BEGIN that started the + // current block, possibly with the most recent BREAK after that + // BEGIN (if there is any) on top of it. Stuff is flushed off the + // bottom as it becomes irrelevant due to the primary ring-buffer + // advancing. + mutable scan_stack: [mutable uint], + mutable scan_stack_empty: bool, // top==bottom disambiguator + mutable top: uint, // index of top of scan_stack + mutable bottom: uint, // index of bottom of scan_stack + // stack of blocks-in-progress being flushed by print + mutable print_stack: [print_stack_elt], + // buffered indentation to avoid writing trailing whitespace + mutable pending_indentation: int +}; + +impl printer for printer { + fn last_token() -> token { self.token[self.right] } + // be very careful with this! + fn replace_last_token(t: token) { self.token[self.right] = t; } + fn pretty_print(t: token) { + #debug("pp [%u,%u]", self.left, self.right); + alt t { + EOF { + if !self.scan_stack_empty { + self.check_stack(0); + self.advance_left(self.token[self.left], + self.size[self.left]); + } + self.indent(0); + } + BEGIN(b) { + if self.scan_stack_empty { + self.left_total = 1; + self.right_total = 1; + self.left = 0u; + self.right = 0u; + } else { self.advance_right(); } + #debug("pp BEGIN/buffer [%u,%u]", self.left, self.right); + self.token[self.right] = t; + self.size[self.right] = -self.right_total; + self.scan_push(self.right); + } + END { + if self.scan_stack_empty { + #debug("pp END/print [%u,%u]", self.left, self.right); + self.print(t, 0); + } else { + #debug("pp END/buffer [%u,%u]", self.left, self.right); + self.advance_right(); + self.token[self.right] = t; + self.size[self.right] = -1; + self.scan_push(self.right); + } + } + BREAK(b) { + if self.scan_stack_empty { + self.left_total = 1; + self.right_total = 1; + self.left = 0u; + self.right = 0u; + } else { self.advance_right(); } + #debug("pp BREAK/buffer [%u,%u]", self.left, self.right); + self.check_stack(0); + self.scan_push(self.right); + self.token[self.right] = t; + self.size[self.right] = -self.right_total; + self.right_total += b.blank_space; + } + STRING(s, len) { + if self.scan_stack_empty { + #debug("pp STRING/print [%u,%u]", self.left, self.right); + self.print(t, len); + } else { + #debug("pp STRING/buffer [%u,%u]", self.left, self.right); + self.advance_right(); + self.token[self.right] = t; + self.size[self.right] = len; + self.right_total += len; + self.check_stream(); + } + } + } + } + fn check_stream() { + #debug("check_stream [%u, %u] with left_total=%d, right_total=%d", + self.left, self.right, self.left_total, self.right_total); + if self.right_total - self.left_total > self.space { + #debug("scan window is %d, longer than space on line (%d)", + self.right_total - self.left_total, self.space); + if !self.scan_stack_empty { + if self.left == self.scan_stack[self.bottom] { + #debug("setting %u to infinity and popping", self.left); + self.size[self.scan_pop_bottom()] = size_infinity; + } + } + self.advance_left(self.token[self.left], self.size[self.left]); + if self.left != self.right { self.check_stream(); } + } + } + fn scan_push(x: uint) { + #debug("scan_push %u", x); + if self.scan_stack_empty { + self.scan_stack_empty = false; + } else { + self.top += 1u; + self.top %= self.buf_len; + assert (self.top != self.bottom); + } + self.scan_stack[self.top] = x; + } + fn scan_pop() -> uint { + assert (!self.scan_stack_empty); + let x = self.scan_stack[self.top]; + if self.top == self.bottom { + self.scan_stack_empty = true; + } else { self.top += self.buf_len - 1u; self.top %= self.buf_len; } + ret x; + } + fn scan_top() -> uint { + assert (!self.scan_stack_empty); + ret self.scan_stack[self.top]; + } + fn scan_pop_bottom() -> uint { + assert (!self.scan_stack_empty); + let x = self.scan_stack[self.bottom]; + if self.top == self.bottom { + self.scan_stack_empty = true; + } else { self.bottom += 1u; self.bottom %= self.buf_len; } + ret x; + } + fn advance_right() { + self.right += 1u; + self.right %= self.buf_len; + assert (self.right != self.left); + } + fn advance_left(x: token, L: int) { + #debug("advnce_left [%u,%u], sizeof(%u)=%d", self.left, self.right, + self.left, L); + if L >= 0 { + self.print(x, L); + alt x { + BREAK(b) { self.left_total += b.blank_space; } + STRING(_, len) { assert (len == L); self.left_total += len; } + _ { } + } + if self.left != self.right { + self.left += 1u; + self.left %= self.buf_len; + self.advance_left(self.token[self.left], + self.size[self.left]); + } + } + } + fn check_stack(k: int) { + if !self.scan_stack_empty { + let x = self.scan_top(); + alt self.token[x] { + BEGIN(b) { + if k > 0 { + self.size[self.scan_pop()] = self.size[x] + + self.right_total; + self.check_stack(k - 1); + } + } + END { + // paper says + not =, but that makes no sense. + self.size[self.scan_pop()] = 1; + self.check_stack(k + 1); + } + _ { + self.size[self.scan_pop()] = self.size[x] + self.right_total; + if k > 0 { self.check_stack(k); } + } + } + } + } + fn print_newline(amount: int) { + #debug("NEWLINE %d", amount); + self.out.write_str("\n"); + self.pending_indentation = 0; + self.indent(amount); + } + fn indent(amount: int) { + #debug("INDENT %d", amount); + self.pending_indentation += amount; + } + fn get_top() -> print_stack_elt { + let n = vec::len(self.print_stack); + let top: print_stack_elt = {offset: 0, pbreak: broken(inconsistent)}; + if n != 0u { top = self.print_stack[n - 1u]; } + ret top; + } + fn write_str(s: str) { + while self.pending_indentation > 0 { + self.out.write_str(" "); + self.pending_indentation -= 1; + } + self.out.write_str(s); + } + fn print(x: token, L: int) { + #debug("print %s %d (remaining line space=%d)", tok_str(x), L, + self.space); + log(debug, buf_str(self.token, self.size, self.left, self.right, 6u)); + alt x { + BEGIN(b) { + if L > self.space { + let col = self.margin - self.space + b.offset; + #debug("print BEGIN -> push broken block at col %d", col); + self.print_stack += [{offset: col, pbreak: broken(b.breaks)}]; + } else { + #debug("print BEGIN -> push fitting block"); + self.print_stack += [{offset: 0, pbreak: fits}]; + } + } + END { + #debug("print END -> pop END"); + assert (vec::len(self.print_stack) != 0u); + vec::pop(self.print_stack); + } + BREAK(b) { + let top = self.get_top(); + alt top.pbreak { + fits { + #debug("print BREAK in fitting block"); + self.space -= b.blank_space; + self.indent(b.blank_space); + } + broken(consistent) { + #debug("print BREAK in consistent block"); + self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + } + broken(inconsistent) { + if L > self.space { + #debug("print BREAK w/ newline in inconsistent"); + self.print_newline(top.offset + b.offset); + self.space = self.margin - (top.offset + b.offset); + } else { + #debug("print BREAK w/o newline in inconsistent"); + self.indent(b.blank_space); + self.space -= b.blank_space; + } + } + } + } + STRING(s, len) { + #debug("print STRING"); + assert (L == len); + // assert L <= space; + self.space -= len; + self.write_str(s); + } + EOF { + // EOF should never get here. + fail; + } + } + } +} + +// Convenience functions to talk to the printer. +fn box(p: printer, indent: uint, b: breaks) { + p.pretty_print(BEGIN({offset: indent as int, breaks: b})); +} + +fn ibox(p: printer, indent: uint) { box(p, indent, inconsistent); } + +fn cbox(p: printer, indent: uint) { box(p, indent, consistent); } + +fn break_offset(p: printer, n: uint, off: int) { + p.pretty_print(BREAK({offset: off, blank_space: n as int})); +} + +fn end(p: printer) { p.pretty_print(END); } + +fn eof(p: printer) { p.pretty_print(EOF); } + +fn word(p: printer, wrd: str) { + p.pretty_print(STRING(wrd, str::len(wrd) as int)); +} + +fn huge_word(p: printer, wrd: str) { + p.pretty_print(STRING(wrd, size_infinity)); +} + +fn zero_word(p: printer, wrd: str) { p.pretty_print(STRING(wrd, 0)); } + +fn spaces(p: printer, n: uint) { break_offset(p, n, 0); } + +fn zerobreak(p: printer) { spaces(p, 0u); } + +fn space(p: printer) { spaces(p, 1u); } + +fn hardbreak(p: printer) { spaces(p, size_infinity as uint); } + +fn hardbreak_tok_offset(off: int) -> token { + ret BREAK({offset: off, blank_space: size_infinity}); +} + +fn hardbreak_tok() -> token { ret hardbreak_tok_offset(0); } + + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/print/pprust.rs b/src/rustc/syntax/print/pprust.rs new file mode 100644 index 00000000000..5f92b5d5beb --- /dev/null +++ b/src/rustc/syntax/print/pprust.rs @@ -0,0 +1,1793 @@ + +import std::io; +import parse::lexer; +import syntax::codemap::codemap; +import pp::{break_offset, word, printer, + space, zerobreak, hardbreak, breaks, consistent, + inconsistent, eof}; +import driver::diagnostic; + +// The ps is stored here to prevent recursive type. +enum ann_node { + node_block(ps, ast::blk), + node_item(ps, @ast::item), + node_expr(ps, @ast::expr), + node_pat(ps, @ast::pat), +} +type pp_ann = {pre: fn@(ann_node), post: fn@(ann_node)}; + +fn no_ann() -> pp_ann { + fn ignore(_node: ann_node) { } + ret {pre: ignore, post: ignore}; +} + +type ps = + @{s: pp::printer, + cm: option<codemap>, + comments: option<[lexer::cmnt]>, + literals: option<[lexer::lit]>, + mutable cur_cmnt: uint, + mutable cur_lit: uint, + mutable boxes: [pp::breaks], + ann: pp_ann}; + +fn ibox(s: ps, u: uint) { s.boxes += [pp::inconsistent]; pp::ibox(s.s, u); } + +fn end(s: ps) { vec::pop(s.boxes); pp::end(s.s); } + +fn rust_printer(writer: io::writer) -> ps { + let boxes: [pp::breaks] = []; + ret @{s: pp::mk_printer(writer, default_columns), + cm: none::<codemap>, + comments: none::<[lexer::cmnt]>, + literals: none::<[lexer::lit]>, + mutable cur_cmnt: 0u, + mutable cur_lit: 0u, + mutable boxes: boxes, + ann: no_ann()}; +} + +const indent_unit: uint = 4u; +const alt_indent_unit: uint = 2u; + +const default_columns: uint = 78u; + +// Requires you to pass an input filename and reader so that +// it can scan the input text for comments and literals to +// copy forward. +fn print_crate(cm: codemap, span_diagnostic: diagnostic::span_handler, + crate: @ast::crate, filename: str, in: io::reader, + out: io::writer, ann: pp_ann) { + let boxes: [pp::breaks] = []; + let r = lexer::gather_comments_and_literals(cm, span_diagnostic, filename, + in); + let s = + @{s: pp::mk_printer(out, default_columns), + cm: some(cm), + comments: some(r.cmnts), + literals: some(r.lits), + mutable cur_cmnt: 0u, + mutable cur_lit: 0u, + mutable boxes: boxes, + ann: ann}; + print_crate_(s, crate); +} + +fn print_crate_(s: ps, &&crate: @ast::crate) { + print_mod(s, crate.node.module, crate.node.attrs); + print_remaining_comments(s); + eof(s.s); +} + +fn ty_to_str(ty: @ast::ty) -> str { be to_str(ty, print_type); } + +fn pat_to_str(pat: @ast::pat) -> str { be to_str(pat, print_pat); } + +fn expr_to_str(e: @ast::expr) -> str { be to_str(e, print_expr); } + +fn stmt_to_str(s: ast::stmt) -> str { be to_str(s, print_stmt); } + +fn item_to_str(i: @ast::item) -> str { be to_str(i, print_item); } + +fn attr_to_str(i: ast::attribute) -> str { be to_str(i, print_attribute); } + +fn typarams_to_str(tps: [ast::ty_param]) -> str { + be to_str(tps, print_type_params) +} + +fn path_to_str(&&p: @ast::path) -> str { + be to_str(p, bind print_path(_, _, false)); +} + +fn fun_to_str(decl: ast::fn_decl, name: ast::ident, + params: [ast::ty_param]) -> str { + let buffer = io::mk_mem_buffer(); + let s = rust_printer(io::mem_buffer_writer(buffer)); + print_fn(s, decl, name, params); + end(s); // Close the head box + end(s); // Close the outer box + eof(s.s); + io::mem_buffer_str(buffer) +} + +#[test] +fn test_fun_to_str() { + let decl: ast::fn_decl = { + inputs: [], + output: @ast_util::respan(ast_util::dummy_sp(), ast::ty_nil), + purity: ast::impure_fn, + cf: ast::return_val, + constraints: [] + }; + assert fun_to_str(decl, "a", []) == "fn a()"; +} + +fn res_to_str(decl: ast::fn_decl, name: ast::ident, + params: [ast::ty_param]) -> str { + let buffer = io::mk_mem_buffer(); + let s = rust_printer(io::mem_buffer_writer(buffer)); + print_res(s, decl, name, params); + end(s); // Close the head box + end(s); // Close the outer box + eof(s.s); + io::mem_buffer_str(buffer) +} + +#[test] +fn test_res_to_str() { + let decl: ast::fn_decl = { + inputs: [{ + mode: ast::expl(ast::by_val), + ty: @ast_util::respan(ast_util::dummy_sp(), ast::ty_nil), + ident: "b", + id: 0 + }], + output: @ast_util::respan(ast_util::dummy_sp(), ast::ty_nil), + purity: ast::impure_fn, + cf: ast::return_val, + constraints: [] + }; + assert res_to_str(decl, "a", []) == "resource a(b: ())"; +} + +fn block_to_str(blk: ast::blk) -> str { + let buffer = io::mk_mem_buffer(); + let s = rust_printer(io::mem_buffer_writer(buffer)); + // containing cbox, will be closed by print-block at } + cbox(s, indent_unit); + // head-ibox, will be closed by print-block after { + ibox(s, 0u); + print_block(s, blk); + eof(s.s); + io::mem_buffer_str(buffer) +} + +fn meta_item_to_str(mi: ast::meta_item) -> str { + ret to_str(@mi, print_meta_item); +} + +fn attribute_to_str(attr: ast::attribute) -> str { + be to_str(attr, print_attribute); +} + +fn variant_to_str(var: ast::variant) -> str { + be to_str(var, print_variant); +} + +#[test] +fn test_variant_to_str() { + let var = ast_util::respan(ast_util::dummy_sp(), { + name: "principle_skinner", + attrs: [], + args: [], + id: 0, + disr_expr: none + }); + + let varstr = variant_to_str(var); + assert varstr == "principle_skinner"; +} + +fn cbox(s: ps, u: uint) { s.boxes += [pp::consistent]; pp::cbox(s.s, u); } + +fn box(s: ps, u: uint, b: pp::breaks) { s.boxes += [b]; pp::box(s.s, u, b); } + +fn nbsp(s: ps) { word(s.s, " "); } + +fn word_nbsp(s: ps, w: str) { word(s.s, w); nbsp(s); } + +fn word_space(s: ps, w: str) { word(s.s, w); space(s.s); } + +fn popen(s: ps) { word(s.s, "("); } + +fn pclose(s: ps) { word(s.s, ")"); } + +fn head(s: ps, w: str) { + // outer-box is consistent + cbox(s, indent_unit); + // head-box is inconsistent + ibox(s, str::len(w) + 1u); + // keyword that starts the head + word_nbsp(s, w); +} + +fn bopen(s: ps) { + word(s.s, "{"); + end(s); // close the head-box +} + +fn bclose_(s: ps, span: codemap::span, indented: uint) { + maybe_print_comment(s, span.hi); + break_offset_if_not_bol(s, 1u, -(indented as int)); + word(s.s, "}"); + end(s); // close the outer-box +} +fn bclose(s: ps, span: codemap::span) { bclose_(s, span, indent_unit); } + +fn is_begin(s: ps) -> bool { + alt s.s.last_token() { pp::BEGIN(_) { true } _ { false } } +} + +fn is_end(s: ps) -> bool { + alt s.s.last_token() { pp::END { true } _ { false } } +} + +fn is_bol(s: ps) -> bool { + ret s.s.last_token() == pp::EOF || + s.s.last_token() == pp::hardbreak_tok(); +} + +fn hardbreak_if_not_bol(s: ps) { if !is_bol(s) { hardbreak(s.s); } } +fn space_if_not_bol(s: ps) { if !is_bol(s) { space(s.s); } } +fn break_offset_if_not_bol(s: ps, n: uint, off: int) { + if !is_bol(s) { + break_offset(s.s, n, off); + } else { + if off != 0 && s.s.last_token() == pp::hardbreak_tok() { + // We do something pretty sketchy here: tuck the nonzero + // offset-adjustment we were going to deposit along with the + // break into the previous hardbreak. + s.s.replace_last_token(pp::hardbreak_tok_offset(off)); + } + } +} + +// Synthesizes a comment that was not textually present in the original source +// file. +fn synth_comment(s: ps, text: str) { + word(s.s, "/*"); + space(s.s); + word(s.s, text); + space(s.s); + word(s.s, "*/"); +} + +fn commasep<IN>(s: ps, b: breaks, elts: [IN], op: fn(ps, IN)) { + box(s, 0u, b); + let first = true; + for elt: IN in elts { + if first { first = false; } else { word_space(s, ","); } + op(s, elt); + } + end(s); +} + + +fn commasep_cmnt<IN>(s: ps, b: breaks, elts: [IN], op: fn(ps, IN), + get_span: fn(IN) -> codemap::span) { + box(s, 0u, b); + let len = vec::len::<IN>(elts); + let i = 0u; + for elt: IN in elts { + maybe_print_comment(s, get_span(elt).hi); + op(s, elt); + i += 1u; + if i < len { + word(s.s, ","); + maybe_print_trailing_comment(s, get_span(elt), + some(get_span(elts[i]).hi)); + space_if_not_bol(s); + } + } + end(s); +} + +fn commasep_exprs(s: ps, b: breaks, exprs: [@ast::expr]) { + fn expr_span(&&expr: @ast::expr) -> codemap::span { ret expr.span; } + commasep_cmnt(s, b, exprs, print_expr, expr_span); +} + +fn print_mod(s: ps, _mod: ast::_mod, attrs: [ast::attribute]) { + print_inner_attributes(s, attrs); + for vitem: @ast::view_item in _mod.view_items { + print_view_item(s, vitem); + } + for item: @ast::item in _mod.items { print_item(s, item); } +} + +fn print_native_mod(s: ps, nmod: ast::native_mod, attrs: [ast::attribute]) { + print_inner_attributes(s, attrs); + for vitem: @ast::view_item in nmod.view_items { + print_view_item(s, vitem); + } + for item: @ast::native_item in nmod.items { print_native_item(s, item); } +} + +fn print_type(s: ps, &&ty: @ast::ty) { + maybe_print_comment(s, ty.span.lo); + ibox(s, 0u); + alt ty.node { + ast::ty_nil { word(s.s, "()"); } + ast::ty_bot { word(s.s, "!"); } + ast::ty_box(mt) { word(s.s, "@"); print_mt(s, mt); } + ast::ty_uniq(mt) { word(s.s, "~"); print_mt(s, mt); } + ast::ty_vec(mt) { + word(s.s, "["); + alt mt.mutbl { + ast::m_mutbl { word_space(s, "mut"); } + ast::m_const { word_space(s, "const"); } + ast::m_imm { } + } + print_type(s, mt.ty); + word(s.s, "]"); + } + ast::ty_ptr(mt) { word(s.s, "*"); print_mt(s, mt); } + ast::ty_rec(fields) { + word(s.s, "{"); + fn print_field(s: ps, f: ast::ty_field) { + cbox(s, indent_unit); + print_mutability(s, f.node.mt.mutbl); + word(s.s, f.node.ident); + word_space(s, ":"); + print_type(s, f.node.mt.ty); + end(s); + } + fn get_span(f: ast::ty_field) -> codemap::span { ret f.span; } + commasep_cmnt(s, consistent, fields, print_field, get_span); + word(s.s, ",}"); + } + ast::ty_tup(elts) { + popen(s); + commasep(s, inconsistent, elts, print_type); + pclose(s); + } + ast::ty_fn(proto, d) { + print_ty_fn(s, some(proto), d, none, none); + } + ast::ty_path(path, _) { print_path(s, path, false); } + ast::ty_constr(t, cs) { + print_type(s, t); + space(s.s); + word(s.s, constrs_str(cs, ty_constr_to_str)); + } + ast::ty_mac(_) { + fail "print_type doesn't know how to print a ty_mac"; + } + ast::ty_infer { + fail "print_type shouldn't see a ty_infer"; + } + + } + end(s); +} + +fn print_native_item(s: ps, item: @ast::native_item) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, item.span.lo); + print_outer_attributes(s, item.attrs); + alt item.node { + ast::native_item_fn(decl, typarams) { + print_fn(s, decl, item.ident, typarams); + end(s); // end head-ibox + word(s.s, ";"); + end(s); // end the outer fn box + } + } +} + +fn print_item(s: ps, &&item: @ast::item) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, item.span.lo); + print_outer_attributes(s, item.attrs); + let ann_node = node_item(s, item); + s.ann.pre(ann_node); + alt item.node { + ast::item_const(ty, expr) { + head(s, "const"); + word_space(s, item.ident + ":"); + print_type(s, ty); + space(s.s); + end(s); // end the head-ibox + + word_space(s, "="); + print_expr(s, expr); + word(s.s, ";"); + end(s); // end the outer cbox + + } + ast::item_fn(decl, typarams, body) { + print_fn(s, decl, item.ident, typarams); + word(s.s, " "); + print_block_with_attrs(s, body, item.attrs); + } + ast::item_mod(_mod) { + head(s, "mod"); + word_nbsp(s, item.ident); + bopen(s); + print_mod(s, _mod, item.attrs); + bclose(s, item.span); + } + ast::item_native_mod(nmod) { + head(s, "native"); + word_nbsp(s, "mod"); + word_nbsp(s, item.ident); + bopen(s); + print_native_mod(s, nmod, item.attrs); + bclose(s, item.span); + } + ast::item_ty(ty, params) { + ibox(s, indent_unit); + ibox(s, 0u); + word_nbsp(s, "type"); + word(s.s, item.ident); + print_type_params(s, params); + end(s); // end the inner ibox + + space(s.s); + word_space(s, "="); + print_type(s, ty); + word(s.s, ";"); + end(s); // end the outer ibox + } + ast::item_enum(variants, params) { + let newtype = + vec::len(variants) == 1u && + str::eq(item.ident, variants[0].node.name) && + vec::len(variants[0].node.args) == 1u; + if newtype { + ibox(s, indent_unit); + word_space(s, "enum"); + } else { head(s, "enum"); } + word(s.s, item.ident); + print_type_params(s, params); + space(s.s); + if newtype { + word_space(s, "="); + print_type(s, variants[0].node.args[0].ty); + word(s.s, ";"); + end(s); + } else { + bopen(s); + for v: ast::variant in variants { + space_if_not_bol(s); + maybe_print_comment(s, v.span.lo); + print_outer_attributes(s, v.node.attrs); + ibox(s, indent_unit); + print_variant(s, v); + word(s.s, ","); + end(s); + maybe_print_trailing_comment(s, v.span, none::<uint>); + } + bclose(s, item.span); + } + } + ast::item_class(tps,items,_,ctor_decl,ctor_body) { + head(s, "class"); + word_nbsp(s, item.ident); + print_type_params(s, tps); + bopen(s); + hardbreak_if_not_bol(s); + head(s, "new"); + print_fn_args_and_ret(s, ctor_decl); + space(s.s); + print_block(s, ctor_body); + for ci in items { + /* + FIXME: collect all private items and print them + in a single "priv" section + */ + hardbreak_if_not_bol(s); + alt ci.node.privacy { + ast::priv { + head(s, "priv"); + bopen(s); + hardbreak_if_not_bol(s); + } + _ {} + } + alt ci.node.decl { + ast::instance_var(nm, t, mt, _) { + word_nbsp(s, "let"); + alt mt { + ast::class_mutable { word_nbsp(s, "mutable"); } + _ {} + } + word(s.s, nm); + word_nbsp(s, ":"); + print_type(s, t); + word(s.s, ";"); + } + ast::class_method(i) { + print_item(s, i); + } + } + alt ci.node.privacy { + ast::priv { bclose(s, ci.span); } + _ {} + } + } + } + ast::item_impl(tps, ifce, ty, methods) { + head(s, "impl"); + word(s.s, item.ident); + print_type_params(s, tps); + space(s.s); + alt ifce { + some(ty) { + word_nbsp(s, "of"); + print_type(s, ty); + space(s.s); + } + _ {} + } + word_nbsp(s, "for"); + print_type(s, ty); + space(s.s); + bopen(s); + for meth in methods { + hardbreak_if_not_bol(s); + maybe_print_comment(s, meth.span.lo); + print_outer_attributes(s, meth.attrs); + print_fn(s, meth.decl, meth.ident, meth.tps); + word(s.s, " "); + print_block_with_attrs(s, meth.body, meth.attrs); + } + bclose(s, item.span); + } + ast::item_iface(tps, methods) { + head(s, "iface"); + word(s.s, item.ident); + print_type_params(s, tps); + word(s.s, " "); + bopen(s); + for meth in methods { print_ty_method(s, meth); } + bclose(s, item.span); + } + ast::item_res(decl, tps, body, dt_id, ct_id) { + print_res(s, decl, item.ident, tps); + print_block(s, body); + } + } + s.ann.post(ann_node); +} + +fn print_res(s: ps, decl: ast::fn_decl, name: ast::ident, + typarams: [ast::ty_param]) { + head(s, "resource"); + word(s.s, name); + print_type_params(s, typarams); + popen(s); + word_space(s, decl.inputs[0].ident + ":"); + print_type(s, decl.inputs[0].ty); + pclose(s); + space(s.s); +} + +fn print_variant(s: ps, v: ast::variant) { + word(s.s, v.node.name); + if vec::len(v.node.args) > 0u { + popen(s); + fn print_variant_arg(s: ps, arg: ast::variant_arg) { + print_type(s, arg.ty); + } + commasep(s, consistent, v.node.args, print_variant_arg); + pclose(s); + } + alt v.node.disr_expr { + some(d) { + space(s.s); + word_space(s, "="); + print_expr(s, d); + } + _ {} + } +} + +fn print_ty_method(s: ps, m: ast::ty_method) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, m.span.lo); + print_outer_attributes(s, m.attrs); + print_ty_fn(s, none, m.decl, some(m.ident), some(m.tps)); + word(s.s, ";"); +} + +fn print_outer_attributes(s: ps, attrs: [ast::attribute]) { + let count = 0; + for attr: ast::attribute in attrs { + alt attr.node.style { + ast::attr_outer { print_attribute(s, attr); count += 1; } + _ {/* fallthrough */ } + } + } + if count > 0 { hardbreak_if_not_bol(s); } +} + +fn print_inner_attributes(s: ps, attrs: [ast::attribute]) { + let count = 0; + for attr: ast::attribute in attrs { + alt attr.node.style { + ast::attr_inner { + print_attribute(s, attr); + word(s.s, ";"); + count += 1; + } + _ {/* fallthrough */ } + } + } + if count > 0 { hardbreak_if_not_bol(s); } +} + +fn print_attribute(s: ps, attr: ast::attribute) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, attr.span.lo); + word(s.s, "#["); + print_meta_item(s, @attr.node.value); + word(s.s, "]"); +} + + +fn print_stmt(s: ps, st: ast::stmt) { + maybe_print_comment(s, st.span.lo); + alt st.node { + ast::stmt_decl(decl, _) { + print_decl(s, decl); + } + ast::stmt_expr(expr, _) { + space_if_not_bol(s); + print_expr(s, expr); + } + ast::stmt_semi(expr, _) { + space_if_not_bol(s); + print_expr(s, expr); + word(s.s, ";"); + } + } + if parse::parser::stmt_ends_with_semi(st) { word(s.s, ";"); } + maybe_print_trailing_comment(s, st.span, none::<uint>); +} + +fn print_block(s: ps, blk: ast::blk) { + print_possibly_embedded_block(s, blk, block_normal, indent_unit); +} + +fn print_block_with_attrs(s: ps, blk: ast::blk, attrs: [ast::attribute]) { + print_possibly_embedded_block_(s, blk, block_normal, indent_unit, attrs); +} + +enum embed_type { block_macro, block_block_fn, block_normal, } + +fn print_possibly_embedded_block(s: ps, blk: ast::blk, embedded: embed_type, + indented: uint) { + print_possibly_embedded_block_( + s, blk, embedded, indented, []); +} + +fn print_possibly_embedded_block_(s: ps, blk: ast::blk, embedded: embed_type, + indented: uint, attrs: [ast::attribute]) { + alt blk.node.rules { + ast::unchecked_blk { word(s.s, "unchecked"); } + ast::unsafe_blk { word(s.s, "unsafe"); } + ast::default_blk { } + } + maybe_print_comment(s, blk.span.lo); + let ann_node = node_block(s, blk); + s.ann.pre(ann_node); + alt embedded { + block_macro { word(s.s, "#{"); end(s); } + block_block_fn { end(s); } + block_normal { bopen(s); } + } + + print_inner_attributes(s, attrs); + + for vi in blk.node.view_items { print_view_item(s, vi); } + for st: @ast::stmt in blk.node.stmts { + print_stmt(s, *st); + } + alt blk.node.expr { + some(expr) { + space_if_not_bol(s); + print_expr(s, expr); + maybe_print_trailing_comment(s, expr.span, some(blk.span.hi)); + } + _ { } + } + bclose_(s, blk.span, indented); + s.ann.post(ann_node); +} + +// ret and fail, without arguments cannot appear is the discriminant of if, +// alt, do, & while unambiguously without being parenthesized +fn print_maybe_parens_discrim(s: ps, e: @ast::expr) { + let disambig = alt e.node { + ast::expr_ret(none) | ast::expr_fail(none) { true } + _ { false } + }; + if disambig { popen(s); } + print_expr(s, e); + if disambig { pclose(s); } +} + +fn print_if(s: ps, test: @ast::expr, blk: ast::blk, + elseopt: option<@ast::expr>, chk: bool) { + head(s, "if"); + if chk { word_nbsp(s, "check"); } + print_maybe_parens_discrim(s, test); + space(s.s); + print_block(s, blk); + fn do_else(s: ps, els: option<@ast::expr>) { + alt els { + some(_else) { + alt _else.node { + // "another else-if" + ast::expr_if(i, t, e) { + cbox(s, indent_unit - 1u); + ibox(s, 0u); + word(s.s, " else if "); + print_maybe_parens_discrim(s, i); + space(s.s); + print_block(s, t); + do_else(s, e); + } + // "final else" + ast::expr_block(b) { + cbox(s, indent_unit - 1u); + ibox(s, 0u); + word(s.s, " else "); + print_block(s, b); + } + // BLEAH, constraints would be great here + _ { + fail "print_if saw if with weird alternative"; + } + } + } + _ {/* fall through */ } + } + } + do_else(s, elseopt); +} + +fn print_mac(s: ps, m: ast::mac) { + alt m.node { + ast::mac_invoc(path, arg, body) { + word(s.s, "#"); + print_path(s, path, false); + alt arg { + some(@{node: ast::expr_vec(_, _), _}) { } + _ { word(s.s, " "); } + } + option::may(arg, bind print_expr(s, _)); + // FIXME: extension 'body' + } + ast::mac_embed_type(ty) { + word(s.s, "#<"); + print_type(s, ty); + word(s.s, ">"); + } + ast::mac_embed_block(blk) { + print_possibly_embedded_block(s, blk, block_normal, indent_unit); + } + ast::mac_ellipsis { word(s.s, "..."); } + ast::mac_var(v) { word(s.s, #fmt("$%u", v)); } + _ { /* fixme */ } + } +} + +fn print_expr(s: ps, &&expr: @ast::expr) { + maybe_print_comment(s, expr.span.lo); + ibox(s, indent_unit); + let ann_node = node_expr(s, expr); + s.ann.pre(ann_node); + alt expr.node { + ast::expr_vec(exprs, mutbl) { + ibox(s, indent_unit); + word(s.s, "["); + if mutbl == ast::m_mutbl { + word(s.s, "mutable"); + if vec::len(exprs) > 0u { nbsp(s); } + } + commasep_exprs(s, inconsistent, exprs); + word(s.s, "]"); + end(s); + } + ast::expr_rec(fields, wth) { + fn print_field(s: ps, field: ast::field) { + ibox(s, indent_unit); + if field.node.mutbl == ast::m_mutbl { word_nbsp(s, "mutable"); } + word(s.s, field.node.ident); + word_space(s, ":"); + print_expr(s, field.node.expr); + end(s); + } + fn get_span(field: ast::field) -> codemap::span { ret field.span; } + word(s.s, "{"); + commasep_cmnt(s, consistent, fields, print_field, get_span); + alt wth { + some(expr) { + if vec::len(fields) > 0u { space(s.s); } + ibox(s, indent_unit); + word_space(s, "with"); + print_expr(s, expr); + end(s); + } + _ { word(s.s, ","); } + } + word(s.s, "}"); + } + ast::expr_tup(exprs) { + popen(s); + commasep_exprs(s, inconsistent, exprs); + pclose(s); + } + ast::expr_call(func, args, has_block) { + print_expr_parens_if_not_bot(s, func); + let base_args = args, blk = none; + if has_block { blk = some(vec::pop(base_args)); } + if !has_block || vec::len(base_args) > 0u { + popen(s); + commasep_exprs(s, inconsistent, base_args); + pclose(s); + } + if has_block { + nbsp(s); + print_expr(s, option::get(blk)); + } + } + ast::expr_bind(func, args) { + fn print_opt(s: ps, expr: option<@ast::expr>) { + alt expr { + some(expr) { print_expr(s, expr); } + _ { word(s.s, "_"); } + } + } + + // "bind" keyword is only needed if there are no "_" arguments. + if !vec::any(args) {|arg| option::is_none(arg) } { + word_nbsp(s, "bind"); + } + + print_expr(s, func); + popen(s); + commasep(s, inconsistent, args, print_opt); + pclose(s); + } + ast::expr_binary(op, lhs, rhs) { + let prec = operator_prec(op); + print_op_maybe_parens(s, lhs, prec); + space(s.s); + word_space(s, ast_util::binop_to_str(op)); + print_op_maybe_parens(s, rhs, prec + 1); + } + ast::expr_unary(op, expr) { + word(s.s, ast_util::unop_to_str(op)); + print_op_maybe_parens(s, expr, parse::parser::unop_prec); + } + ast::expr_lit(lit) { print_literal(s, lit); } + ast::expr_cast(expr, ty) { + print_op_maybe_parens(s, expr, parse::parser::as_prec); + space(s.s); + word_space(s, "as"); + print_type(s, ty); + } + ast::expr_if(test, blk, elseopt) { + print_if(s, test, blk, elseopt, false); + } + ast::expr_if_check(test, blk, elseopt) { + print_if(s, test, blk, elseopt, true); + } + ast::expr_while(test, blk) { + head(s, "while"); + print_maybe_parens_discrim(s, test); + space(s.s); + print_block(s, blk); + } + ast::expr_for(decl, expr, blk) { + head(s, "for"); + print_for_decl(s, decl, expr); + space(s.s); + print_block(s, blk); + } + ast::expr_do_while(blk, expr) { + head(s, "do"); + space(s.s); + print_block(s, blk); + space(s.s); + word_space(s, "while"); + print_expr(s, expr); + } + ast::expr_alt(expr, arms, mode) { + cbox(s, alt_indent_unit); + ibox(s, 4u); + word_nbsp(s, "alt"); + if mode == ast::alt_check { word_nbsp(s, "check"); } + print_maybe_parens_discrim(s, expr); + space(s.s); + bopen(s); + for arm: ast::arm in arms { + space(s.s); + cbox(s, alt_indent_unit); + ibox(s, 0u); + let first = true; + for p: @ast::pat in arm.pats { + if first { + first = false; + } else { space(s.s); word_space(s, "|"); } + print_pat(s, p); + } + space(s.s); + alt arm.guard { + some(e) { word_space(s, "if"); print_expr(s, e); space(s.s); } + none { } + } + print_possibly_embedded_block(s, arm.body, block_normal, + alt_indent_unit); + } + bclose_(s, expr.span, alt_indent_unit); + } + ast::expr_fn(proto, decl, body, cap_clause) { + // containing cbox, will be closed by print-block at } + cbox(s, indent_unit); + // head-box, will be closed by print-block at start + ibox(s, 0u); + word(s.s, proto_to_str(proto)); + print_cap_clause(s, *cap_clause); + print_fn_args_and_ret(s, decl); + space(s.s); + print_block(s, body); + } + ast::expr_fn_block(decl, body) { + // containing cbox, will be closed by print-block at } + cbox(s, indent_unit); + // head-box, will be closed by print-block at start + ibox(s, 0u); + word(s.s, "{"); + print_fn_block_args(s, decl); + print_possibly_embedded_block(s, body, block_block_fn, indent_unit); + } + ast::expr_block(blk) { + // containing cbox, will be closed by print-block at } + cbox(s, indent_unit); + // head-box, will be closed by print-block after { + ibox(s, 0u); + print_block(s, blk); + } + ast::expr_copy(e) { word_space(s, "copy"); print_expr(s, e); } + ast::expr_move(lhs, rhs) { + print_expr(s, lhs); + space(s.s); + word_space(s, "<-"); + print_expr(s, rhs); + } + ast::expr_assign(lhs, rhs) { + print_expr(s, lhs); + space(s.s); + word_space(s, "="); + print_expr(s, rhs); + } + ast::expr_swap(lhs, rhs) { + print_expr(s, lhs); + space(s.s); + word_space(s, "<->"); + print_expr(s, rhs); + } + ast::expr_assign_op(op, lhs, rhs) { + print_expr(s, lhs); + space(s.s); + word(s.s, ast_util::binop_to_str(op)); + word_space(s, "="); + print_expr(s, rhs); + } + ast::expr_field(expr, id, tys) { + // Deal with '10.x' + if ends_in_lit_int(expr) { + popen(s); print_expr(s, expr); pclose(s); + } else { + print_expr_parens_if_not_bot(s, expr); + } + word(s.s, "."); + word(s.s, id); + if vec::len(tys) > 0u { + word(s.s, "::<"); + commasep(s, inconsistent, tys, print_type); + word(s.s, ">"); + } + } + ast::expr_index(expr, index) { + print_expr_parens_if_not_bot(s, expr); + word(s.s, "["); + print_expr(s, index); + word(s.s, "]"); + } + ast::expr_path(path) { print_path(s, path, true); } + ast::expr_fail(maybe_fail_val) { + word(s.s, "fail"); + alt maybe_fail_val { + some(expr) { word(s.s, " "); print_expr(s, expr); } + _ { } + } + } + ast::expr_break { word(s.s, "break"); } + ast::expr_cont { word(s.s, "cont"); } + ast::expr_ret(result) { + word(s.s, "ret"); + alt result { + some(expr) { word(s.s, " "); print_expr(s, expr); } + _ { } + } + } + ast::expr_be(result) { word_nbsp(s, "be"); print_expr(s, result); } + ast::expr_log(lvl, lexp, expr) { + alt check lvl { + 1 { word_nbsp(s, "log"); print_expr(s, expr); } + 0 { word_nbsp(s, "log_err"); print_expr(s, expr); } + 2 { + word_nbsp(s, "log"); + popen(s); + print_expr(s, lexp); + word(s.s, ","); + space_if_not_bol(s); + print_expr(s, expr); + pclose(s); + } + } + } + ast::expr_check(m, expr) { + alt m { + ast::claimed_expr { word_nbsp(s, "claim"); } + ast::checked_expr { word_nbsp(s, "check"); } + } + popen(s); + print_expr(s, expr); + pclose(s); + } + ast::expr_assert(expr) { + word_nbsp(s, "assert"); + print_expr(s, expr); + } + ast::expr_mac(m) { print_mac(s, m); } + } + s.ann.post(ann_node); + end(s); +} + +fn print_expr_parens_if_not_bot(s: ps, ex: @ast::expr) { + let parens = alt ex.node { + ast::expr_fail(_) | ast::expr_ret(_) | + ast::expr_binary(_, _, _) | ast::expr_unary(_, _) | + ast::expr_move(_, _) | ast::expr_copy(_) | + ast::expr_assign(_, _) | ast::expr_be(_) | + ast::expr_assign_op(_, _, _) | ast::expr_swap(_, _) | + ast::expr_log(_, _, _) | ast::expr_assert(_) | + ast::expr_call(_, _, true) | + ast::expr_check(_, _) { true } + _ { false } + }; + if parens { popen(s); } + print_expr(s, ex); + if parens { pclose(s); } +} + +fn print_local_decl(s: ps, loc: @ast::local) { + print_pat(s, loc.node.pat); + alt loc.node.ty.node { + ast::ty_infer { } + _ { word_space(s, ":"); print_type(s, loc.node.ty); } + } +} + +fn print_decl(s: ps, decl: @ast::decl) { + maybe_print_comment(s, decl.span.lo); + alt decl.node { + ast::decl_local(locs) { + space_if_not_bol(s); + ibox(s, indent_unit); + word_nbsp(s, "let"); + fn print_local(s: ps, &&loc: @ast::local) { + ibox(s, indent_unit); + print_local_decl(s, loc); + end(s); + alt loc.node.init { + some(init) { + nbsp(s); + alt init.op { + ast::init_assign { word_space(s, "="); } + ast::init_move { word_space(s, "<-"); } + } + print_expr(s, init.expr); + } + _ { } + } + } + commasep(s, consistent, locs, print_local); + end(s); + } + ast::decl_item(item) { print_item(s, item); } + } +} + +fn print_ident(s: ps, ident: ast::ident) { word(s.s, ident); } + +fn print_for_decl(s: ps, loc: @ast::local, coll: @ast::expr) { + print_local_decl(s, loc); + space(s.s); + word_space(s, "in"); + print_expr(s, coll); +} + +fn print_path(s: ps, &&path: @ast::path, colons_before_params: bool) { + maybe_print_comment(s, path.span.lo); + if path.node.global { word(s.s, "::"); } + let first = true; + for id: ast::ident in path.node.idents { + if first { first = false; } else { word(s.s, "::"); } + word(s.s, id); + } + if vec::len(path.node.types) > 0u { + if colons_before_params { word(s.s, "::"); } + word(s.s, "<"); + commasep(s, inconsistent, path.node.types, print_type); + word(s.s, ">"); + } +} + +fn print_pat(s: ps, &&pat: @ast::pat) { + maybe_print_comment(s, pat.span.lo); + let ann_node = node_pat(s, pat); + s.ann.pre(ann_node); + /* Pat isn't normalized, but the beauty of it + is that it doesn't matter */ + alt pat.node { + ast::pat_wild { word(s.s, "_"); } + ast::pat_ident(path, sub) { + print_path(s, path, true); + alt sub { + some(p) { word(s.s, "@"); print_pat(s, p); } + _ {} + } + } + ast::pat_enum(path, args) { + print_path(s, path, true); + if vec::len(args) > 0u { + popen(s); + commasep(s, inconsistent, args, print_pat); + pclose(s); + } else { } + } + ast::pat_rec(fields, etc) { + word(s.s, "{"); + fn print_field(s: ps, f: ast::field_pat) { + cbox(s, indent_unit); + word(s.s, f.ident); + word_space(s, ":"); + print_pat(s, f.pat); + end(s); + } + fn get_span(f: ast::field_pat) -> codemap::span { ret f.pat.span; } + commasep_cmnt(s, consistent, fields, print_field, get_span); + if etc { + if vec::len(fields) != 0u { word_space(s, ","); } + word(s.s, "_"); + } + word(s.s, "}"); + } + ast::pat_tup(elts) { + popen(s); + commasep(s, inconsistent, elts, print_pat); + pclose(s); + } + ast::pat_box(inner) { word(s.s, "@"); print_pat(s, inner); } + ast::pat_uniq(inner) { word(s.s, "~"); print_pat(s, inner); } + ast::pat_lit(e) { print_expr(s, e); } + ast::pat_range(begin, end) { + print_expr(s, begin); + space(s.s); + word_space(s, "to"); + print_expr(s, end); + } + } + s.ann.post(ann_node); +} + +fn print_fn(s: ps, decl: ast::fn_decl, name: ast::ident, + typarams: [ast::ty_param]) { + alt decl.purity { + ast::impure_fn { head(s, "fn"); } + ast::unsafe_fn { head(s, "unsafe fn"); } + ast::pure_fn { head(s, "pure fn"); } + ast::crust_fn { head(s, "crust fn"); } + } + word(s.s, name); + print_type_params(s, typarams); + print_fn_args_and_ret(s, decl); +} + +fn print_cap_clause(s: ps, cap_clause: ast::capture_clause) { + fn print_cap_item(s: ps, &&cap_item: @ast::capture_item) { + word(s.s, cap_item.name); + } + + let has_copies = vec::is_not_empty(cap_clause.copies); + let has_moves = vec::is_not_empty(cap_clause.moves); + if !has_copies && !has_moves { ret; } + + word(s.s, "["); + + if has_copies { + word_nbsp(s, "copy"); + commasep(s, inconsistent, cap_clause.copies, print_cap_item); + if has_moves { + word_space(s, ";"); + } + } + + if has_moves { + word_nbsp(s, "move"); + commasep(s, inconsistent, cap_clause.moves, print_cap_item); + } + + word(s.s, "]"); +} + +fn print_fn_args_and_ret(s: ps, decl: ast::fn_decl) { + popen(s); + fn print_arg(s: ps, x: ast::arg) { + ibox(s, indent_unit); + print_arg_mode(s, x.mode); + word_space(s, x.ident + ":"); + print_type(s, x.ty); + end(s); + } + commasep(s, inconsistent, decl.inputs, print_arg); + pclose(s); + word(s.s, constrs_str(decl.constraints, {|c| + ast_fn_constr_to_str(decl, c) + })); + + maybe_print_comment(s, decl.output.span.lo); + if decl.output.node != ast::ty_nil { + space_if_not_bol(s); + word_space(s, "->"); + print_type(s, decl.output); + } +} + +fn print_fn_block_args(s: ps, decl: ast::fn_decl) { + word(s.s, "|"); + fn print_arg(s: ps, x: ast::arg) { + ibox(s, indent_unit); + print_arg_mode(s, x.mode); + word(s.s, x.ident); + end(s); + } + commasep(s, inconsistent, decl.inputs, print_arg); + word(s.s, "|"); + if decl.output.node != ast::ty_infer { + space_if_not_bol(s); + word_space(s, "->"); + print_type(s, decl.output); + } + maybe_print_comment(s, decl.output.span.lo); +} + +fn mode_to_str(m: ast::mode) -> str { + alt m { + ast::expl(ast::by_mutbl_ref) { "&" } + ast::expl(ast::by_move) { "-" } + ast::expl(ast::by_ref) { "&&" } + ast::expl(ast::by_val) { "++" } + ast::expl(ast::by_copy) { "+" } + ast::infer(_) { "" } + } +} + +fn print_arg_mode(s: ps, m: ast::mode) { + let ms = mode_to_str(m); + if ms != "" { word(s.s, ms); } +} + +fn print_bounds(s: ps, bounds: @[ast::ty_param_bound]) { + if vec::len(*bounds) > 0u { + word(s.s, ":"); + for bound in *bounds { + nbsp(s); + alt bound { + ast::bound_copy { word(s.s, "copy"); } + ast::bound_send { word(s.s, "send"); } + ast::bound_iface(t) { print_type(s, t); } + } + } + } +} + +fn print_type_params(s: ps, &¶ms: [ast::ty_param]) { + if vec::len(params) > 0u { + word(s.s, "<"); + fn printParam(s: ps, param: ast::ty_param) { + word(s.s, param.ident); + print_bounds(s, param.bounds); + } + commasep(s, inconsistent, params, printParam); + word(s.s, ">"); + } +} + +fn print_meta_item(s: ps, &&item: @ast::meta_item) { + ibox(s, indent_unit); + alt item.node { + ast::meta_word(name) { word(s.s, name); } + ast::meta_name_value(name, value) { + word_space(s, name); + word_space(s, "="); + print_literal(s, @value); + } + ast::meta_list(name, items) { + word(s.s, name); + popen(s); + commasep(s, consistent, items, print_meta_item); + pclose(s); + } + } + end(s); +} + +fn print_simple_path(s: ps, path: ast::simple_path) { + let first = true; + for id in path { + if first { first = false; } else { word(s.s, "::"); } + word(s.s, id); + } +} + +fn print_view_path(s: ps, &&vp: @ast::view_path) { + alt vp.node { + ast::view_path_simple(ident, path, _) { + if path[vec::len(*path)-1u] != ident { + word_space(s, ident); + word_space(s, "="); + } + print_simple_path(s, *path); + } + + ast::view_path_glob(path, _) { + print_simple_path(s, *path); + word(s.s, "::*"); + } + + ast::view_path_list(path, idents, _) { + print_simple_path(s, *path); + word(s.s, "::{"); + commasep(s, inconsistent, idents) {|s, w| + word(s.s, w.node.name) + } + word(s.s, "}"); + } + } +} + +fn print_view_paths(s: ps, vps: [@ast::view_path]) { + commasep(s, inconsistent, vps, print_view_path); +} + +fn print_view_item(s: ps, item: @ast::view_item) { + hardbreak_if_not_bol(s); + maybe_print_comment(s, item.span.lo); + alt item.node { + ast::view_item_use(id, mta, _) { + head(s, "use"); + word(s.s, id); + if vec::len(mta) > 0u { + popen(s); + commasep(s, consistent, mta, print_meta_item); + pclose(s); + } + } + + ast::view_item_import(vps) { + head(s, "import"); + print_view_paths(s, vps); + } + + ast::view_item_export(vps) { + head(s, "export"); + print_view_paths(s, vps); + } + } + word(s.s, ";"); + end(s); // end inner head-block + end(s); // end outer head-block +} + + +// FIXME: The fact that this builds up the table anew for every call is +// not good. Eventually, table should be a const. +fn operator_prec(op: ast::binop) -> int { + for spec: parse::parser::op_spec in *parse::parser::prec_table() { + if spec.op == op { ret spec.prec; } + } + fail; +} + +fn need_parens(expr: @ast::expr, outer_prec: int) -> bool { + alt expr.node { + ast::expr_binary(op, _, _) { operator_prec(op) < outer_prec } + ast::expr_cast(_, _) { parse::parser::as_prec < outer_prec } + // This may be too conservative in some cases + ast::expr_assign(_, _) { true } + ast::expr_move(_, _) { true } + ast::expr_swap(_, _) { true } + ast::expr_assign_op(_, _, _) { true } + ast::expr_ret(_) { true } + ast::expr_be(_) { true } + ast::expr_assert(_) { true } + ast::expr_check(_, _) { true } + ast::expr_log(_, _, _) { true } + _ { !parse::parser::expr_requires_semi_to_be_stmt(expr) } + } +} + +fn print_op_maybe_parens(s: ps, expr: @ast::expr, outer_prec: int) { + let add_them = need_parens(expr, outer_prec); + if add_them { popen(s); } + print_expr(s, expr); + if add_them { pclose(s); } +} + +fn print_mutability(s: ps, mutbl: ast::mutability) { + alt mutbl { + ast::m_mutbl { word_nbsp(s, "mutable"); } + ast::m_const { word_nbsp(s, "const"); } + ast::m_imm {/* nothing */ } + } +} + +fn print_mt(s: ps, mt: ast::mt) { + print_mutability(s, mt.mutbl); + print_type(s, mt.ty); +} + +fn print_ty_fn(s: ps, opt_proto: option<ast::proto>, + decl: ast::fn_decl, id: option<ast::ident>, + tps: option<[ast::ty_param]>) { + ibox(s, indent_unit); + word(s.s, opt_proto_to_str(opt_proto)); + alt id { some(id) { word(s.s, " "); word(s.s, id); } _ { } } + alt tps { some(tps) { print_type_params(s, tps); } _ { } } + zerobreak(s.s); + popen(s); + fn print_arg(s: ps, input: ast::arg) { + print_arg_mode(s, input.mode); + if str::len(input.ident) > 0u { + word_space(s, input.ident + ":"); + } + print_type(s, input.ty); + } + commasep(s, inconsistent, decl.inputs, print_arg); + pclose(s); + maybe_print_comment(s, decl.output.span.lo); + if decl.output.node != ast::ty_nil { + space_if_not_bol(s); + ibox(s, indent_unit); + word_space(s, "->"); + if decl.cf == ast::noreturn { word_nbsp(s, "!"); } + else { print_type(s, decl.output); } + end(s); + } + word(s.s, constrs_str(decl.constraints, ast_ty_fn_constr_to_str)); + end(s); +} + +fn maybe_print_trailing_comment(s: ps, span: codemap::span, + next_pos: option<uint>) { + let cm; + alt s.cm { some(ccm) { cm = ccm; } _ { ret; } } + alt next_comment(s) { + some(cmnt) { + if cmnt.style != lexer::trailing { ret; } + let span_line = codemap::lookup_char_pos(cm, span.hi); + let comment_line = codemap::lookup_char_pos(cm, cmnt.pos); + let next = cmnt.pos + 1u; + alt next_pos { none { } some(p) { next = p; } } + if span.hi < cmnt.pos && cmnt.pos < next && + span_line.line == comment_line.line { + print_comment(s, cmnt); + s.cur_cmnt += 1u; + } + } + _ { } + } +} + +fn print_remaining_comments(s: ps) { + // If there aren't any remaining comments, then we need to manually + // make sure there is a line break at the end. + if option::is_none(next_comment(s)) { hardbreak(s.s); } + while true { + alt next_comment(s) { + some(cmnt) { print_comment(s, cmnt); s.cur_cmnt += 1u; } + _ { break; } + } + } +} + +fn in_cbox(s: ps) -> bool { + let len = vec::len(s.boxes); + if len == 0u { ret false; } + ret s.boxes[len - 1u] == pp::consistent; +} + +fn print_literal(s: ps, &&lit: @ast::lit) { + maybe_print_comment(s, lit.span.lo); + alt next_lit(s, lit.span.lo) { + some(lt) { + word(s.s, lt.lit); + ret; + } + _ {} + } + alt lit.node { + ast::lit_str(st) { print_string(s, st); } + ast::lit_int(ch, ast::ty_char) { + word(s.s, "'" + escape_str(str::from_char(ch as char), '\'') + "'"); + } + ast::lit_int(i, t) { + if i < 0_i64 { + word(s.s, + "-" + u64::to_str(-i as u64, 10u) + + ast_util::int_ty_to_str(t)); + } else { + word(s.s, + u64::to_str(i as u64, 10u) + + ast_util::int_ty_to_str(t)); + } + } + ast::lit_uint(u, t) { + word(s.s, + u64::to_str(u, 10u) + + ast_util::uint_ty_to_str(t)); + } + ast::lit_float(f, t) { + word(s.s, f + ast_util::float_ty_to_str(t)); + } + ast::lit_nil { word(s.s, "()"); } + ast::lit_bool(val) { + if val { word(s.s, "true"); } else { word(s.s, "false"); } + } + } +} + +fn lit_to_str(l: @ast::lit) -> str { be to_str(l, print_literal); } + +fn next_lit(s: ps, pos: uint) -> option<lexer::lit> { + alt s.literals { + some(lits) { + while s.cur_lit < vec::len(lits) { + let lt = lits[s.cur_lit]; + if lt.pos > pos { ret none; } + s.cur_lit += 1u; + if lt.pos == pos { ret some(lt); } + } + ret none; + } + _ { ret none; } + } +} + +fn maybe_print_comment(s: ps, pos: uint) { + while true { + alt next_comment(s) { + some(cmnt) { + if cmnt.pos < pos { + print_comment(s, cmnt); + s.cur_cmnt += 1u; + } else { break; } + } + _ { break; } + } + } +} + +fn print_comment(s: ps, cmnt: lexer::cmnt) { + alt cmnt.style { + lexer::mixed { + assert (vec::len(cmnt.lines) == 1u); + zerobreak(s.s); + word(s.s, cmnt.lines[0]); + zerobreak(s.s); + } + lexer::isolated { + pprust::hardbreak_if_not_bol(s); + for line: str in cmnt.lines { + // Don't print empty lines because they will end up as trailing + // whitespace + if str::is_not_empty(line) { word(s.s, line); } + hardbreak(s.s); + } + } + lexer::trailing { + word(s.s, " "); + if vec::len(cmnt.lines) == 1u { + word(s.s, cmnt.lines[0]); + hardbreak(s.s); + } else { + ibox(s, 0u); + for line: str in cmnt.lines { + if str::is_not_empty(line) { word(s.s, line); } + hardbreak(s.s); + } + end(s); + } + } + lexer::blank_line { + // We need to do at least one, possibly two hardbreaks. + let is_semi = + alt s.s.last_token() { + pp::STRING(s, _) { s == ";" } + _ { false } + }; + if is_semi || is_begin(s) || is_end(s) { hardbreak(s.s); } + hardbreak(s.s); + } + } +} + +fn print_string(s: ps, st: str) { + word(s.s, "\""); + word(s.s, escape_str(st, '"')); + word(s.s, "\""); +} + +fn escape_str(st: str, to_escape: char) -> str { + let out: str = ""; + let len = str::len(st); + let i = 0u; + while i < len { + alt st[i] as char { + '\n' { out += "\\n"; } + '\t' { out += "\\t"; } + '\r' { out += "\\r"; } + '\\' { out += "\\\\"; } + cur { + if cur == to_escape { out += "\\"; } + // FIXME some (or all?) non-ascii things should be escaped + + str::push_char(out, cur); + } + } + i += 1u; + } + ret out; +} + +fn to_str<T>(t: T, f: fn@(ps, T)) -> str { + let buffer = io::mk_mem_buffer(); + let s = rust_printer(io::mem_buffer_writer(buffer)); + f(s, t); + eof(s.s); + io::mem_buffer_str(buffer) +} + +fn next_comment(s: ps) -> option<lexer::cmnt> { + alt s.comments { + some(cmnts) { + if s.cur_cmnt < vec::len(cmnts) { + ret some(cmnts[s.cur_cmnt]); + } else { ret none::<lexer::cmnt>; } + } + _ { ret none::<lexer::cmnt>; } + } +} + +fn constr_args_to_str<T>(f: fn@(T) -> str, args: [@ast::sp_constr_arg<T>]) -> + str { + let comma = false; + let s = "("; + for a: @ast::sp_constr_arg<T> in args { + if comma { s += ", "; } else { comma = true; } + s += constr_arg_to_str::<T>(f, a.node); + } + s += ")"; + ret s; +} + +fn constr_arg_to_str<T>(f: fn@(T) -> str, c: ast::constr_arg_general_<T>) -> + str { + alt c { + ast::carg_base { ret "*"; } + ast::carg_ident(i) { ret f(i); } + ast::carg_lit(l) { ret lit_to_str(l); } + } +} + +// needed b/c constr_args_to_str needs +// something that takes an alias +// (argh) +fn uint_to_str(&&i: uint) -> str { ret uint::str(i); } + +fn ast_ty_fn_constr_to_str(&&c: @ast::constr) -> str { + ret path_to_str(c.node.path) + + constr_args_to_str(uint_to_str, c.node.args); +} + +fn ast_fn_constr_to_str(decl: ast::fn_decl, &&c: @ast::constr) -> str { + let arg_to_str = bind fn_arg_idx_to_str(decl, _); + ret path_to_str(c.node.path) + + constr_args_to_str(arg_to_str, c.node.args); +} + +fn ty_constr_to_str(&&c: @ast::ty_constr) -> str { + fn ty_constr_path_to_str(&&p: @ast::path) -> str { "*." + path_to_str(p) } + + ret path_to_str(c.node.path) + + constr_args_to_str::<@ast::path>(ty_constr_path_to_str, + c.node.args); +} + +fn constrs_str<T>(constrs: [T], elt: fn(T) -> str) -> str { + let s = "", colon = true; + for c in constrs { + if colon { s += " : "; colon = false; } else { s += ", "; } + s += elt(c); + } + ret s; +} + +fn fn_arg_idx_to_str(decl: ast::fn_decl, &&idx: uint) -> str { + decl.inputs[idx].ident +} + +fn opt_proto_to_str(opt_p: option<ast::proto>) -> str { + alt opt_p { + none { "fn" } + some(p) { proto_to_str(p) } + } +} + +fn proto_to_str(p: ast::proto) -> str { + ret alt p { + ast::proto_bare { "native fn" } + ast::proto_any { "fn" } + ast::proto_block { "fn&" } + ast::proto_uniq { "fn~" } + ast::proto_box { "fn@" } + }; +} + +fn ends_in_lit_int(ex: @ast::expr) -> bool { + alt ex.node { + ast::expr_lit(@{node: ast::lit_int(_, ast::ty_i), _}) { true } + ast::expr_binary(_, _, sub) | ast::expr_unary(_, sub) | + ast::expr_move(_, sub) | ast::expr_copy(sub) | + ast::expr_assign(_, sub) | ast::expr_be(sub) | + ast::expr_assign_op(_, _, sub) | ast::expr_swap(_, sub) | + ast::expr_log(_, _, sub) | ast::expr_assert(sub) | + ast::expr_check(_, sub) { ends_in_lit_int(sub) } + ast::expr_fail(osub) | ast::expr_ret(osub) { + alt osub { + some(ex) { ends_in_lit_int(ex) } + _ { false } + } + } + _ { false } + } +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/syntax/util/interner.rs b/src/rustc/syntax/util/interner.rs new file mode 100644 index 00000000000..c7f0d7ecb99 --- /dev/null +++ b/src/rustc/syntax/util/interner.rs @@ -0,0 +1,39 @@ +// An "interner" is a data structure that associates values with uint tags and +// allows bidirectional lookup; i.e. given a value, one can easily find the +// type, and vice versa. +import std::map; +import std::map::{hashmap, hashfn, eqfn}; + +type interner<T> = + {map: hashmap<T, uint>, + mutable vect: [T], + hasher: hashfn<T>, + eqer: eqfn<T>}; + +fn mk<T: copy>(hasher: hashfn<T>, eqer: eqfn<T>) -> interner<T> { + let m = map::mk_hashmap::<T, uint>(hasher, eqer); + ret {map: m, mutable vect: [], hasher: hasher, eqer: eqer}; +} + +fn intern<T: copy>(itr: interner<T>, val: T) -> uint { + alt itr.map.find(val) { + some(idx) { ret idx; } + none { + let new_idx = vec::len::<T>(itr.vect); + itr.map.insert(val, new_idx); + itr.vect += [val]; + ret new_idx; + } + } +} + +// |get| isn't "pure" in the traditional sense, because it can go from +// failing to returning a value as items are interned. But for typestate, +// where we first check a pred and then rely on it, ceasing to fail is ok. +pure fn get<T: copy>(itr: interner<T>, idx: uint) -> T { + unchecked { + itr.vect[idx] + } +} + +fn len<T>(itr: interner<T>) -> uint { ret vec::len(itr.vect); } diff --git a/src/rustc/syntax/visit.rs b/src/rustc/syntax/visit.rs new file mode 100644 index 00000000000..55958abd7af --- /dev/null +++ b/src/rustc/syntax/visit.rs @@ -0,0 +1,546 @@ + +import ast::*; +import codemap::span; + +// Context-passing AST walker. Each overridden visit method has full control +// over what happens with its node, it can do its own traversal of the node's +// children (potentially passing in different contexts to each), call +// visit::visit_* to apply the default traversal algorithm (again, it can +// override the context), or prevent deeper traversal by doing nothing. + +// Our typesystem doesn't do circular types, so the visitor record can not +// hold functions that take visitors. A vt enum is used to break the cycle. +enum vt<E> { mk_vt(visitor<E>), } + +enum fn_kind { + fk_item_fn(ident, [ty_param]), //< an item declared with fn() + fk_method(ident, [ty_param]), + fk_res(ident, [ty_param]), + fk_anon(proto), //< an anonymous function like fn@(...) + fk_fn_block, //< a block {||...} +} + +fn name_of_fn(fk: fn_kind) -> ident { + alt fk { + fk_item_fn(name, _) | fk_method(name, _) | fk_res(name, _) { name } + fk_anon(_) | fk_fn_block { "anon" } + } +} + +fn tps_of_fn(fk: fn_kind) -> [ty_param] { + alt fk { + fk_item_fn(_, tps) | fk_method(_, tps) | fk_res(_, tps) { tps } + fk_anon(_) | fk_fn_block { [] } + } +} + +type visitor<E> = + // takes the components so that one function can be + // generic over constr and ty_constr + @{visit_mod: fn@(_mod, span, node_id, E, vt<E>), + visit_view_item: fn@(@view_item, E, vt<E>), + visit_native_item: fn@(@native_item, E, vt<E>), + visit_item: fn@(@item, E, vt<E>), + visit_local: fn@(@local, E, vt<E>), + visit_block: fn@(ast::blk, E, vt<E>), + visit_stmt: fn@(@stmt, E, vt<E>), + visit_arm: fn@(arm, E, vt<E>), + visit_pat: fn@(@pat, E, vt<E>), + visit_decl: fn@(@decl, E, vt<E>), + visit_expr: fn@(@expr, E, vt<E>), + visit_ty: fn@(@ty, E, vt<E>), + visit_ty_params: fn@([ty_param], E, vt<E>), + visit_constr: fn@(@path, span, node_id, E, vt<E>), + visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt<E>), + visit_class_item: fn@(span, privacy, class_member, E, vt<E>)}; + +fn default_visitor<E>() -> visitor<E> { + ret @{visit_mod: bind visit_mod::<E>(_, _, _, _, _), + visit_view_item: bind visit_view_item::<E>(_, _, _), + visit_native_item: bind visit_native_item::<E>(_, _, _), + visit_item: bind visit_item::<E>(_, _, _), + visit_local: bind visit_local::<E>(_, _, _), + visit_block: bind visit_block::<E>(_, _, _), + visit_stmt: bind visit_stmt::<E>(_, _, _), + visit_arm: bind visit_arm::<E>(_, _, _), + visit_pat: bind visit_pat::<E>(_, _, _), + visit_decl: bind visit_decl::<E>(_, _, _), + visit_expr: bind visit_expr::<E>(_, _, _), + visit_ty: bind skip_ty::<E>(_, _, _), + visit_ty_params: bind visit_ty_params::<E>(_, _, _), + visit_constr: bind visit_constr::<E>(_, _, _, _, _), + visit_fn: bind visit_fn::<E>(_, _, _, _, _, _, _), + visit_class_item: bind visit_class_item::<E>(_,_,_,_,_)}; +} + +fn visit_crate<E>(c: crate, e: E, v: vt<E>) { + v.visit_mod(c.node.module, c.span, crate_node_id, e, v); +} + +fn visit_crate_directive<E>(cd: @crate_directive, e: E, v: vt<E>) { + alt cd.node { + cdir_src_mod(_, _) { } + cdir_dir_mod(_, cdirs, _) { + for cdir: @crate_directive in cdirs { + visit_crate_directive(cdir, e, v); + } + } + cdir_view_item(vi) { v.visit_view_item(vi, e, v); } + cdir_syntax(_) { } + } +} + +fn visit_mod<E>(m: _mod, _sp: span, _id: node_id, e: E, v: vt<E>) { + for vi: @view_item in m.view_items { v.visit_view_item(vi, e, v); } + for i: @item in m.items { v.visit_item(i, e, v); } +} + +fn visit_view_item<E>(_vi: @view_item, _e: E, _v: vt<E>) { } + +fn visit_local<E>(loc: @local, e: E, v: vt<E>) { + v.visit_pat(loc.node.pat, e, v); + v.visit_ty(loc.node.ty, e, v); + alt loc.node.init { none { } some(i) { v.visit_expr(i.expr, e, v); } } +} + +fn visit_item<E>(i: @item, e: E, v: vt<E>) { + alt i.node { + item_const(t, ex) { v.visit_ty(t, e, v); v.visit_expr(ex, e, v); } + item_fn(decl, tp, body) { + v.visit_fn(fk_item_fn(i.ident, tp), decl, body, i.span, i.id, e, v); + } + item_mod(m) { v.visit_mod(m, i.span, i.id, e, v); } + item_native_mod(nm) { + for vi: @view_item in nm.view_items { v.visit_view_item(vi, e, v); } + for ni: @native_item in nm.items { v.visit_native_item(ni, e, v); } + } + item_ty(t, tps) { v.visit_ty(t, e, v); v.visit_ty_params(tps, e, v); } + item_res(decl, tps, body, dtor_id, _) { + v.visit_fn(fk_res(i.ident, tps), decl, body, i.span, + dtor_id, e, v); + } + item_enum(variants, tps) { + v.visit_ty_params(tps, e, v); + for vr: variant in variants { + for va: variant_arg in vr.node.args { v.visit_ty(va.ty, e, v); } + } + } + item_impl(tps, ifce, ty, methods) { + v.visit_ty_params(tps, e, v); + alt ifce { some(ty) { v.visit_ty(ty, e, v); } _ {} } + v.visit_ty(ty, e, v); + for m in methods { + visit_method_helper(m, e, v) + } + } + item_class(tps, members, _, ctor_decl, ctor_blk) { + v.visit_ty_params(tps, e, v); + for m in members { + v.visit_class_item(m.span, m.node.privacy, m.node.decl, e, v); + } + visit_fn_decl(ctor_decl, e, v); + v.visit_block(ctor_blk, e, v); + } + item_iface(tps, methods) { + v.visit_ty_params(tps, e, v); + for m in methods { + for a in m.decl.inputs { v.visit_ty(a.ty, e, v); } + v.visit_ty(m.decl.output, e, v); + } + } + } +} + +fn visit_class_item<E>(_s: span, _p: privacy, cm: class_member, + e:E, v:vt<E>) { + alt cm { + instance_var(ident, t, mt, id) { + v.visit_ty(t, e, v); + } + class_method(i) { + v.visit_item(i, e, v); + } + } +} + +fn skip_ty<E>(_t: @ty, _e: E, _v: vt<E>) {} + +fn visit_ty<E>(t: @ty, e: E, v: vt<E>) { + alt t.node { + ty_box(mt) { v.visit_ty(mt.ty, e, v); } + ty_uniq(mt) { v.visit_ty(mt.ty, e, v); } + ty_vec(mt) { v.visit_ty(mt.ty, e, v); } + ty_ptr(mt) { v.visit_ty(mt.ty, e, v); } + ty_rec(flds) { + for f: ty_field in flds { v.visit_ty(f.node.mt.ty, e, v); } + } + ty_tup(ts) { for tt in ts { v.visit_ty(tt, e, v); } } + ty_fn(_, decl) { + for a in decl.inputs { v.visit_ty(a.ty, e, v); } + for c: @constr in decl.constraints { + v.visit_constr(c.node.path, c.span, c.node.id, e, v); + } + v.visit_ty(decl.output, e, v); + } + ty_path(p, _) { visit_path(p, e, v); } + ty_constr(t, cs) { + v.visit_ty(t, e, v); + for tc: @spanned<constr_general_<@path, node_id>> in cs { + v.visit_constr(tc.node.path, tc.span, tc.node.id, e, v); + } + } + _ {} + } +} + +fn visit_constr<E>(_operator: @path, _sp: span, _id: node_id, _e: E, + _v: vt<E>) { + // default +} + +fn visit_path<E>(p: @path, e: E, v: vt<E>) { + for tp: @ty in p.node.types { v.visit_ty(tp, e, v); } +} + +fn visit_pat<E>(p: @pat, e: E, v: vt<E>) { + alt p.node { + pat_enum(path, children) { + visit_path(path, e, v); + for child: @pat in children { v.visit_pat(child, e, v); } + } + pat_rec(fields, _) { + for f: field_pat in fields { v.visit_pat(f.pat, e, v); } + } + pat_tup(elts) { for elt in elts { v.visit_pat(elt, e, v); } } + pat_box(inner) | pat_uniq(inner) { + v.visit_pat(inner, e, v); + } + pat_ident(path, inner) { + visit_path(path, e, v); + option::may(inner, {|subpat| v.visit_pat(subpat, e, v)}); + } + _ { } + } +} + +fn visit_native_item<E>(ni: @native_item, e: E, v: vt<E>) { + alt ni.node { + native_item_fn(fd, tps) { + v.visit_ty_params(tps, e, v); + visit_fn_decl(fd, e, v); + } + } +} + +fn visit_ty_params<E>(tps: [ty_param], e: E, v: vt<E>) { + for tp in tps { + for bound in *tp.bounds { + alt bound { + bound_iface(t) { v.visit_ty(t, e, v); } + _ {} + } + } + } +} + +fn visit_fn_decl<E>(fd: fn_decl, e: E, v: vt<E>) { + for a: arg in fd.inputs { v.visit_ty(a.ty, e, v); } + for c: @constr in fd.constraints { + v.visit_constr(c.node.path, c.span, c.node.id, e, v); + } + v.visit_ty(fd.output, e, v); +} + +// Note: there is no visit_method() method in the visitor, instead override +// visit_fn() and check for fk_method(). I named this visit_method_helper() +// because it is not a default impl of any method, though I doubt that really +// clarifies anything. - Niko +fn visit_method_helper<E>(m: @method, e: E, v: vt<E>) { + v.visit_fn(fk_method(m.ident, m.tps), m.decl, m.body, m.span, + m.id, e, v); +} + +fn visit_fn<E>(fk: fn_kind, decl: fn_decl, body: blk, _sp: span, + _id: node_id, e: E, v: vt<E>) { + visit_fn_decl(decl, e, v); + v.visit_ty_params(tps_of_fn(fk), e, v); + v.visit_block(body, e, v); +} + +fn visit_block<E>(b: ast::blk, e: E, v: vt<E>) { + for vi in b.node.view_items { v.visit_view_item(vi, e, v); } + for s in b.node.stmts { v.visit_stmt(s, e, v); } + visit_expr_opt(b.node.expr, e, v); +} + +fn visit_stmt<E>(s: @stmt, e: E, v: vt<E>) { + alt s.node { + stmt_decl(d, _) { v.visit_decl(d, e, v); } + stmt_expr(ex, _) { v.visit_expr(ex, e, v); } + stmt_semi(ex, _) { v.visit_expr(ex, e, v); } + } +} + +fn visit_decl<E>(d: @decl, e: E, v: vt<E>) { + alt d.node { + decl_local(locs) { + for loc in locs { v.visit_local(loc, e, v); } + } + decl_item(it) { v.visit_item(it, e, v); } + } +} + +fn visit_expr_opt<E>(eo: option<@expr>, e: E, v: vt<E>) { + alt eo { none { } some(ex) { v.visit_expr(ex, e, v); } } +} + +fn visit_exprs<E>(exprs: [@expr], e: E, v: vt<E>) { + for ex: @expr in exprs { v.visit_expr(ex, e, v); } +} + +fn visit_mac<E>(m: mac, e: E, v: vt<E>) { + alt m.node { + ast::mac_invoc(pth, arg, body) { + option::map(arg) {|arg| visit_expr(arg, e, v)}; } + ast::mac_embed_type(ty) { v.visit_ty(ty, e, v); } + ast::mac_embed_block(blk) { v.visit_block(blk, e, v); } + ast::mac_ellipsis { } + ast::mac_aq(_, e) { /* FIXME: maybe visit */ } + ast::mac_var(_) { } + } +} + +fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) { + alt ex.node { + expr_vec(es, _) { visit_exprs(es, e, v); } + expr_rec(flds, base) { + for f: field in flds { v.visit_expr(f.node.expr, e, v); } + visit_expr_opt(base, e, v); + } + expr_tup(elts) { for el in elts { v.visit_expr(el, e, v); } } + expr_call(callee, args, _) { + visit_exprs(args, e, v); + v.visit_expr(callee, e, v); + } + expr_bind(callee, args) { + v.visit_expr(callee, e, v); + for eo: option<@expr> in args { visit_expr_opt(eo, e, v); } + } + expr_binary(_, a, b) { v.visit_expr(a, e, v); v.visit_expr(b, e, v); } + expr_unary(_, a) { v.visit_expr(a, e, v); } + expr_lit(_) { } + expr_cast(x, t) { v.visit_expr(x, e, v); v.visit_ty(t, e, v); } + expr_if(x, b, eo) { + v.visit_expr(x, e, v); + v.visit_block(b, e, v); + visit_expr_opt(eo, e, v); + } + expr_if_check(x, b, eo) { + v.visit_expr(x, e, v); + v.visit_block(b, e, v); + visit_expr_opt(eo, e, v); + } + expr_while(x, b) { v.visit_expr(x, e, v); v.visit_block(b, e, v); } + expr_for(dcl, x, b) { + v.visit_local(dcl, e, v); + v.visit_expr(x, e, v); + v.visit_block(b, e, v); + } + expr_do_while(b, x) { v.visit_block(b, e, v); v.visit_expr(x, e, v); } + expr_alt(x, arms, _) { + v.visit_expr(x, e, v); + for a: arm in arms { v.visit_arm(a, e, v); } + } + expr_fn(proto, decl, body, _) { + v.visit_fn(fk_anon(proto), decl, body, ex.span, ex.id, e, v); + } + expr_fn_block(decl, body) { + v.visit_fn(fk_fn_block, decl, body, ex.span, ex.id, e, v); + } + expr_block(b) { v.visit_block(b, e, v); } + expr_assign(a, b) { v.visit_expr(b, e, v); v.visit_expr(a, e, v); } + expr_copy(a) { v.visit_expr(a, e, v); } + expr_move(a, b) { v.visit_expr(b, e, v); v.visit_expr(a, e, v); } + expr_swap(a, b) { v.visit_expr(a, e, v); v.visit_expr(b, e, v); } + expr_assign_op(_, a, b) { + v.visit_expr(b, e, v); + v.visit_expr(a, e, v); + } + expr_field(x, _, tys) { + v.visit_expr(x, e, v); + for tp in tys { v.visit_ty(tp, e, v); } + } + expr_index(a, b) { v.visit_expr(a, e, v); v.visit_expr(b, e, v); } + expr_path(p) { visit_path(p, e, v); } + expr_fail(eo) { visit_expr_opt(eo, e, v); } + expr_break { } + expr_cont { } + expr_ret(eo) { visit_expr_opt(eo, e, v); } + expr_be(x) { v.visit_expr(x, e, v); } + expr_log(_, lv, x) { + v.visit_expr(lv, e, v); + v.visit_expr(x, e, v); + } + expr_check(_, x) { v.visit_expr(x, e, v); } + expr_assert(x) { v.visit_expr(x, e, v); } + expr_mac(mac) { visit_mac(mac, e, v); } + } +} + +fn visit_arm<E>(a: arm, e: E, v: vt<E>) { + for p: @pat in a.pats { v.visit_pat(p, e, v); } + visit_expr_opt(a.guard, e, v); + v.visit_block(a.body, e, v); +} + +// Simpler, non-context passing interface. Always walks the whole tree, simply +// calls the given functions on the nodes. + +type simple_visitor = + // takes the components so that one function can be + // generic over constr and ty_constr + @{visit_mod: fn@(_mod, span, node_id), + visit_view_item: fn@(@view_item), + visit_native_item: fn@(@native_item), + visit_item: fn@(@item), + visit_local: fn@(@local), + visit_block: fn@(ast::blk), + visit_stmt: fn@(@stmt), + visit_arm: fn@(arm), + visit_pat: fn@(@pat), + visit_decl: fn@(@decl), + visit_expr: fn@(@expr), + visit_ty: fn@(@ty), + visit_ty_params: fn@([ty_param]), + visit_constr: fn@(@path, span, node_id), + visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id), + visit_class_item: fn@(span, privacy, class_member)}; + +fn simple_ignore_ty(_t: @ty) {} + +fn default_simple_visitor() -> simple_visitor { + ret @{visit_mod: fn@(_m: _mod, _sp: span, _id: node_id) { }, + visit_view_item: fn@(_vi: @view_item) { }, + visit_native_item: fn@(_ni: @native_item) { }, + visit_item: fn@(_i: @item) { }, + visit_local: fn@(_l: @local) { }, + visit_block: fn@(_b: ast::blk) { }, + visit_stmt: fn@(_s: @stmt) { }, + visit_arm: fn@(_a: arm) { }, + visit_pat: fn@(_p: @pat) { }, + visit_decl: fn@(_d: @decl) { }, + visit_expr: fn@(_e: @expr) { }, + visit_ty: simple_ignore_ty, + visit_ty_params: fn@(_ps: [ty_param]) {}, + visit_constr: fn@(_p: @path, _sp: span, _id: node_id) { }, + visit_fn: fn@(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span, + _id: node_id) { }, + visit_class_item: fn@(_s: span, _p: privacy, _c: class_member) {} + }; +} + +fn mk_simple_visitor(v: simple_visitor) -> vt<()> { + fn v_mod(f: fn@(_mod, span, node_id), m: _mod, sp: span, id: node_id, + &&e: (), v: vt<()>) { + f(m, sp, id); + visit_mod(m, sp, id, e, v); + } + fn v_view_item(f: fn@(@view_item), vi: @view_item, &&e: (), v: vt<()>) { + f(vi); + visit_view_item(vi, e, v); + } + fn v_native_item(f: fn@(@native_item), ni: @native_item, &&e: (), + v: vt<()>) { + f(ni); + visit_native_item(ni, e, v); + } + fn v_item(f: fn@(@item), i: @item, &&e: (), v: vt<()>) { + f(i); + visit_item(i, e, v); + } + fn v_local(f: fn@(@local), l: @local, &&e: (), v: vt<()>) { + f(l); + visit_local(l, e, v); + } + fn v_block(f: fn@(ast::blk), bl: ast::blk, &&e: (), v: vt<()>) { + f(bl); + visit_block(bl, e, v); + } + fn v_stmt(f: fn@(@stmt), st: @stmt, &&e: (), v: vt<()>) { + f(st); + visit_stmt(st, e, v); + } + fn v_arm(f: fn@(arm), a: arm, &&e: (), v: vt<()>) { + f(a); + visit_arm(a, e, v); + } + fn v_pat(f: fn@(@pat), p: @pat, &&e: (), v: vt<()>) { + f(p); + visit_pat(p, e, v); + } + fn v_decl(f: fn@(@decl), d: @decl, &&e: (), v: vt<()>) { + f(d); + visit_decl(d, e, v); + } + fn v_expr(f: fn@(@expr), ex: @expr, &&e: (), v: vt<()>) { + f(ex); + visit_expr(ex, e, v); + } + fn v_ty(f: fn@(@ty), ty: @ty, &&e: (), v: vt<()>) { + f(ty); + visit_ty(ty, e, v); + } + fn v_ty_params(f: fn@([ty_param]), ps: [ty_param], &&e: (), v: vt<()>) { + f(ps); + visit_ty_params(ps, e, v); + } + fn v_constr(f: fn@(@path, span, node_id), pt: @path, sp: span, + id: node_id, &&e: (), v: vt<()>) { + f(pt, sp, id); + visit_constr(pt, sp, id, e, v); + } + fn v_fn(f: fn@(fn_kind, fn_decl, blk, span, node_id), + fk: fn_kind, decl: fn_decl, body: blk, sp: span, + id: node_id, &&e: (), v: vt<()>) { + f(fk, decl, body, sp, id); + visit_fn(fk, decl, body, sp, id, e, v); + } + let visit_ty = if v.visit_ty == simple_ignore_ty { + bind skip_ty(_, _, _) + } else { + bind v_ty(v.visit_ty, _, _, _) + }; + fn v_class_item(f: fn@(span, privacy, class_member), + s:span, p:privacy, cm: class_member, &&e: (), + v: vt<()>) { + f(s, p, cm); + visit_class_item(s, p, cm, e, v); + } + ret mk_vt(@{visit_mod: bind v_mod(v.visit_mod, _, _, _, _, _), + visit_view_item: bind v_view_item(v.visit_view_item, _, _, _), + visit_native_item: + bind v_native_item(v.visit_native_item, _, _, _), + visit_item: bind v_item(v.visit_item, _, _, _), + visit_local: bind v_local(v.visit_local, _, _, _), + visit_block: bind v_block(v.visit_block, _, _, _), + visit_stmt: bind v_stmt(v.visit_stmt, _, _, _), + visit_arm: bind v_arm(v.visit_arm, _, _, _), + visit_pat: bind v_pat(v.visit_pat, _, _, _), + visit_decl: bind v_decl(v.visit_decl, _, _, _), + visit_expr: bind v_expr(v.visit_expr, _, _, _), + visit_ty: visit_ty, + visit_ty_params: bind v_ty_params(v.visit_ty_params, _, _, _), + visit_constr: bind v_constr(v.visit_constr, _, _, _, _, _), + visit_fn: bind v_fn(v.visit_fn, _, _, _, _, _, _, _), + visit_class_item: bind v_class_item(v.visit_class_item, _, _, + _, _, _) + }); +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: diff --git a/src/rustc/util/common.rs b/src/rustc/util/common.rs new file mode 100644 index 00000000000..87a1f6145d6 --- /dev/null +++ b/src/rustc/util/common.rs @@ -0,0 +1,103 @@ +import math::{max, min}; +import std::map::hashmap; +import syntax::ast; +import ast::{ty, pat}; +import syntax::codemap::{span}; +import syntax::visit; +import syntax::print; + +type flag = hashmap<str, ()>; + +fn def_eq(a: ast::def_id, b: ast::def_id) -> bool { + ret a.crate == b.crate && a.node == b.node; +} + +fn hash_def(d: ast::def_id) -> uint { + let h = 5381u; + h = (h << 5u) + h ^ (d.crate as uint); + h = (h << 5u) + h ^ (d.node as uint); + ret h; +} + +fn new_def_hash<V: copy>() -> std::map::hashmap<ast::def_id, V> { + let hasher: std::map::hashfn<ast::def_id> = hash_def; + let eqer: std::map::eqfn<ast::def_id> = def_eq; + ret std::map::mk_hashmap::<ast::def_id, V>(hasher, eqer); +} + +fn field_expr(f: ast::field) -> @ast::expr { ret f.node.expr; } + +fn field_exprs(fields: [ast::field]) -> [@ast::expr] { + let es = []; + for f: ast::field in fields { es += [f.node.expr]; } + ret es; +} + +fn log_expr(e: ast::expr) { + log(debug, print::pprust::expr_to_str(@e)); +} + +fn log_expr_err(e: ast::expr) { + log(error, print::pprust::expr_to_str(@e)); +} + +fn log_ty_err(t: @ty) { + log(error, print::pprust::ty_to_str(t)); +} + +fn log_pat_err(p: @pat) { + log(error, print::pprust::pat_to_str(p)); +} + +fn log_block(b: ast::blk) { + log(debug, print::pprust::block_to_str(b)); +} + +fn log_block_err(b: ast::blk) { + log(error, print::pprust::block_to_str(b)); +} + +fn log_item_err(i: @ast::item) { + log(error, print::pprust::item_to_str(i)); +} +fn log_stmt(st: ast::stmt) { + log(debug, print::pprust::stmt_to_str(st)); +} + +fn log_stmt_err(st: ast::stmt) { + log(error, print::pprust::stmt_to_str(st)); +} + +fn has_nonlocal_exits(b: ast::blk) -> bool { + let has_exits = @mutable false; + fn visit_expr(flag: @mutable bool, e: @ast::expr) { + alt e.node { + ast::expr_break { *flag = true; } + ast::expr_cont { *flag = true; } + _ { } + } + } + let v = + visit::mk_simple_visitor(@{visit_expr: bind visit_expr(has_exits, _) + with *visit::default_simple_visitor()}); + visit::visit_block(b, (), v); + ret *has_exits; +} + +fn local_rhs_span(l: @ast::local, def: span) -> span { + alt l.node.init { some(i) { ret i.expr.span; } _ { ret def; } } +} + +fn is_main_name(path: middle::ast_map::path) -> bool { + option::get(vec::last(path)) == middle::ast_map::path_name("main") +} + +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/util/filesearch.rs b/src/rustc/util/filesearch.rs new file mode 100644 index 00000000000..3e23e09f8ea --- /dev/null +++ b/src/rustc/util/filesearch.rs @@ -0,0 +1,174 @@ +// A module for searching for libraries +// FIXME: I'm not happy how this module turned out. Should probably +// just be folded into cstore. + +import std::{fs, os, generic_os}; + +export filesearch; +export mk_filesearch; +export pick; +export pick_file; +export search; +export relative_target_lib_path; +export get_cargo_sysroot; +export get_cargo_root; +export get_cargo_root_nearest; +export libdir; + +type pick<T> = fn(path: fs::path) -> option<T>; + +fn pick_file(file: fs::path, path: fs::path) -> option<fs::path> { + if fs::basename(path) == file { option::some(path) } + else { option::none } +} + +iface filesearch { + fn sysroot() -> fs::path; + fn lib_search_paths() -> [fs::path]; + fn get_target_lib_path() -> fs::path; + fn get_target_lib_file_path(file: fs::path) -> fs::path; +} + +fn mk_filesearch(maybe_sysroot: option<fs::path>, + target_triple: str, + addl_lib_search_paths: [fs::path]) -> filesearch { + type filesearch_impl = {sysroot: fs::path, + addl_lib_search_paths: [fs::path], + target_triple: str}; + impl of filesearch for filesearch_impl { + fn sysroot() -> fs::path { self.sysroot } + fn lib_search_paths() -> [fs::path] { + self.addl_lib_search_paths + + [make_target_lib_path(self.sysroot, self.target_triple)] + + alt get_cargo_lib_path_nearest() { + result::ok(p) { [p] } + result::err(p) { [] } + } + + alt get_cargo_lib_path() { + result::ok(p) { [p] } + result::err(p) { [] } + } + } + fn get_target_lib_path() -> fs::path { + make_target_lib_path(self.sysroot, self.target_triple) + } + fn get_target_lib_file_path(file: fs::path) -> fs::path { + fs::connect(self.get_target_lib_path(), file) + } + } + + let sysroot = get_sysroot(maybe_sysroot); + #debug("using sysroot = %s", sysroot); + {sysroot: sysroot, + addl_lib_search_paths: addl_lib_search_paths, + target_triple: target_triple} as filesearch +} + +// FIXME #1001: This can't be an obj method +fn search<T: copy>(filesearch: filesearch, pick: pick<T>) -> option<T> { + for lib_search_path in filesearch.lib_search_paths() { + #debug("searching %s", lib_search_path); + for path in fs::list_dir(lib_search_path) { + #debug("testing %s", path); + let maybe_picked = pick(path); + if option::is_some(maybe_picked) { + #debug("picked %s", path); + ret maybe_picked; + } else { + #debug("rejected %s", path); + } + } + } + ret option::none; +} + +fn relative_target_lib_path(target_triple: str) -> [fs::path] { + [libdir(), "rustc", target_triple, libdir()] +} + +fn make_target_lib_path(sysroot: fs::path, + target_triple: str) -> fs::path { + let path = [sysroot] + relative_target_lib_path(target_triple); + let path = fs::connect_many(path); + ret path; +} + +fn get_default_sysroot() -> fs::path { + alt os::get_exe_path() { + option::some(p) { fs::normalize(fs::connect(p, "..")) } + option::none { + fail "can't determine value for sysroot"; + } + } +} + +fn get_sysroot(maybe_sysroot: option<fs::path>) -> fs::path { + alt maybe_sysroot { + option::some(sr) { sr } + option::none { get_default_sysroot() } + } +} + +fn get_cargo_sysroot() -> result::t<fs::path, str> { + let path = [get_default_sysroot(), libdir(), "cargo"]; + result::ok(fs::connect_many(path)) +} + +fn get_cargo_root() -> result::t<fs::path, str> { + alt generic_os::getenv("CARGO_ROOT") { + some(_p) { result::ok(_p) } + none { + alt fs::homedir() { + some(_q) { result::ok(fs::connect(_q, ".cargo")) } + none { result::err("no CARGO_ROOT or home directory") } + } + } + } +} + +fn get_cargo_root_nearest() -> result::t<fs::path, str> { + result::chain(get_cargo_root()) { |p| + let cwd = os::getcwd(); + let dirname = fs::dirname(cwd); + let dirpath = fs::split(dirname); + let cwd_cargo = fs::connect(cwd, ".cargo"); + let par_cargo = fs::connect(dirname, ".cargo"); + + if fs::path_is_dir(cwd_cargo) || cwd_cargo == p { + ret result::ok(cwd_cargo); + } + + while vec::is_not_empty(dirpath) && par_cargo != p { + if fs::path_is_dir(par_cargo) { + ret result::ok(par_cargo); + } + vec::pop(dirpath); + dirname = fs::dirname(dirname); + par_cargo = fs::connect(dirname, ".cargo"); + } + + result::ok(cwd_cargo) + } +} + +fn get_cargo_lib_path() -> result::t<fs::path, str> { + result::chain(get_cargo_root()) { |p| + result::ok(fs::connect(p, libdir())) + } +} + +fn get_cargo_lib_path_nearest() -> result::t<fs::path, str> { + result::chain(get_cargo_root_nearest()) { |p| + result::ok(fs::connect(p, libdir())) + } +} + +// The name of the directory rustc expects libraries to be located. +// On Unix should be "lib", on windows "bin" +fn libdir() -> str { + let libdir = #env("CFG_LIBDIR"); + if str::is_empty(libdir) { + fail "rustc compiled without CFG_LIBDIR environment variable"; + } + libdir +} diff --git a/src/rustc/util/ppaux.rs b/src/rustc/util/ppaux.rs new file mode 100644 index 00000000000..0ed630629b7 --- /dev/null +++ b/src/rustc/util/ppaux.rs @@ -0,0 +1,165 @@ +import middle::ty; +import middle::ty::*; +import metadata::encoder; +import syntax::print::pprust; +import syntax::print::pprust::{path_to_str, constr_args_to_str, proto_to_str, + mode_to_str}; +import syntax::{ast, ast_util}; +import middle::ast_map; +import driver::session::session; + +fn ty_to_str(cx: ctxt, typ: t) -> str { + fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) -> + str { + let {mode, ty} = input; + let modestr = alt canon_mode(cx, mode) { + ast::infer(_) { "" } + ast::expl(m) { + if !ty::type_has_vars(ty) && + m == ty::default_arg_mode_for_ty(ty) { + "" + } else { + mode_to_str(ast::expl(m)) + } + } + }; + modestr + ty_to_str(cx, ty) + } + fn fn_to_str(cx: ctxt, proto: ast::proto, ident: option<ast::ident>, + inputs: [arg], output: t, cf: ast::ret_style, + constrs: [@constr]) -> str { + let s = proto_to_str(proto); + alt ident { some(i) { s += " "; s += i; } _ { } } + s += "("; + let strs = []; + for a: arg in inputs { strs += [fn_input_to_str(cx, a)]; } + s += str::connect(strs, ", "); + s += ")"; + if ty::get(output).struct != ty_nil { + s += " -> "; + alt cf { + ast::noreturn { s += "!"; } + ast::return_val { s += ty_to_str(cx, output); } + } + } + s += constrs_str(constrs); + ret s; + } + fn method_to_str(cx: ctxt, m: method) -> str { + ret fn_to_str(cx, m.fty.proto, some(m.ident), m.fty.inputs, + m.fty.output, m.fty.ret_style, m.fty.constraints) + ";"; + } + fn field_to_str(cx: ctxt, f: field) -> str { + ret f.ident + ": " + mt_to_str(cx, f.mt); + } + fn mt_to_str(cx: ctxt, m: mt) -> str { + let mstr = alt m.mutbl { + ast::m_mutbl { "mut " } + ast::m_imm { "" } + ast::m_const { "const " } + }; + ret mstr + ty_to_str(cx, m.ty); + } + fn parameterized(cx: ctxt, base: str, tps: [ty::t]) -> str { + if vec::len(tps) > 0u { + let strs = vec::map(tps, {|t| ty_to_str(cx, t)}); + #fmt["%s<%s>", base, str::connect(strs, ",")] + } else { + base + } + } + + // if there is an id, print that instead of the structural type: + alt ty::type_def_id(typ) { + some(def_id) { + let cs = ast_map::path_to_str(ty::item_path(cx, def_id)); + ret alt ty::get(typ).struct { + ty_enum(_, tps) | ty_res(_, _, tps) { parameterized(cx, cs, tps) } + _ { cs } + }; + } + none { /* fallthrough */} + } + + // pretty print the structural type representation: + ret alt ty::get(typ).struct { + ty_nil { "()" } + ty_bot { "_|_" } + ty_bool { "bool" } + ty_int(ast::ty_i) { "int" } + ty_int(ast::ty_char) { "char" } + ty_int(t) { ast_util::int_ty_to_str(t) } + ty_uint(ast::ty_u) { "uint" } + ty_uint(t) { ast_util::uint_ty_to_str(t) } + ty_float(ast::ty_f) { "float" } + ty_float(t) { ast_util::float_ty_to_str(t) } + ty_str { "str" } + ty_box(tm) { "@" + mt_to_str(cx, tm) } + ty_uniq(tm) { "~" + mt_to_str(cx, tm) } + ty_ptr(tm) { "*" + mt_to_str(cx, tm) } + ty_vec(tm) { "[" + mt_to_str(cx, tm) + "]" } + ty_type { "type" } + ty_rec(elems) { + let strs: [str] = []; + for fld: field in elems { strs += [field_to_str(cx, fld)]; } + "{" + str::connect(strs, ",") + "}" + } + ty_tup(elems) { + let strs = []; + for elem in elems { strs += [ty_to_str(cx, elem)]; } + "(" + str::connect(strs, ",") + ")" + } + ty_fn(f) { + fn_to_str(cx, f.proto, none, f.inputs, f.output, f.ret_style, + f.constraints) + } + ty_var(v) { "<T" + int::str(v) + ">" } + ty_param(id, _) { + "'" + str::from_bytes([('a' as u8) + (id as u8)]) + } + ty_enum(did, tps) | ty_res(did, _, tps) | ty_class(did, tps) { + // Not sure why, but under some circumstances enum or resource types + // do not have an associated id. I didn't investigate enough to know + // if there is a good reason for this. - Niko, 2012-02-10 + let path = ty::item_path(cx, did); + let base = ast_map::path_to_str(path); + parameterized(cx, base, tps) + } + _ { ty_to_short_str(cx, typ) } + } +} + +fn ty_to_short_str(cx: ctxt, typ: t) -> str { + let s = encoder::encoded_ty(cx, typ); + if str::len(s) >= 32u { s = str::slice(s, 0u, 32u); } + ret s; +} + +fn constr_to_str(c: @constr) -> str { + ret path_to_str(c.node.path) + + pprust::constr_args_to_str(pprust::uint_to_str, c.node.args); +} + +fn constrs_str(constrs: [@constr]) -> str { + let s = ""; + let colon = true; + for c: @constr in constrs { + if colon { s += " : "; colon = false; } else { s += ", "; } + s += constr_to_str(c); + } + ret s; +} + +fn ty_constr_to_str<Q>(c: @ast::spanned<ast::constr_general_<@ast::path, Q>>) + -> str { + ret path_to_str(c.node.path) + + constr_args_to_str::<@ast::path>(path_to_str, c.node.args); +} + +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: |
