goscript_types/check/
check.rs

1#![allow(dead_code)]
2use super::super::constant::Value;
3use super::super::importer::{Config, ImportKey, Importer};
4use super::super::objects::{DeclInfoKey, ObjKey, PackageKey, ScopeKey, TCObjects, TypeKey};
5use super::super::operand::OperandMode;
6use super::super::selection::Selection;
7use super::interface::IfaceInfo;
8use goscript_parser::ast;
9use goscript_parser::ast::Node;
10use goscript_parser::ast::{Expr, NodeId};
11use goscript_parser::errors::{ErrorList, FilePosErrors};
12use goscript_parser::objects::{IdentKey, Objects as AstObjects};
13use goscript_parser::position::Pos;
14use goscript_parser::FileSet;
15use std::cell::RefCell;
16use std::collections::{HashMap, HashSet};
17use std::rc::Rc;
18
19/// TypeAndValue reports the type and value (for constants, stored in 'mode')
20/// of the corresponding expression.
21#[derive(Debug, Clone)]
22pub struct TypeAndValue {
23    pub mode: OperandMode,
24    pub typ: TypeKey,
25}
26
27impl TypeAndValue {
28    pub fn get_const_val(&self) -> Option<&Value> {
29        match &self.mode {
30            OperandMode::Constant(v) => Some(v),
31            _ => None,
32        }
33    }
34}
35
36/// An Initializer describes a package-level variable, or a list of variables in case
37/// of a multi-valued initialization expression, and the corresponding initialization
38/// expression.
39#[derive(Debug)]
40pub struct Initializer {
41    pub lhs: Vec<ObjKey>,
42    pub rhs: Expr,
43}
44
45/// Types info holds the results of Type Checking
46#[derive(Debug)]
47pub struct TypeInfo {
48    /// 'types' maps expressions to their types, and for constant
49    /// expressions, also their values. Invalid expressions are
50    /// omitted.
51    ///
52    /// For (possibly parenthesized) identifiers denoting built-in
53    /// functions, the recorded signatures are call-site specific:
54    /// if the call result is not a constant, the recorded type is
55    /// an argument-specific signature. Otherwise, the recorded type
56    /// is invalid.
57    ///
58    /// 'types' does not record the type of every identifier,
59    /// only those that appear where an arbitrary expression is
60    /// permitted. For instance, the identifier f in a selector
61    /// expression x.f is found only in the Selections map, the
62    /// identifier z in a variable declaration 'var z int' is found
63    /// only in the Defs map, and identifiers denoting packages in
64    /// qualified identifiers are collected in the Uses map.
65    pub types: HashMap<NodeId, TypeAndValue>,
66    /// 'defs' maps identifiers to the objects they define (including
67    /// package names, dots "." of dot-imports, and blank "_" identifiers).
68    /// For identifiers that do not denote objects (e.g., the package name
69    /// in package clauses, or symbolic variables t in t := x.(type) of
70    /// type switch headers), the corresponding objects are None.
71    ///
72    /// For an embedded field, Defs returns the field it defines.
73    ///
74    /// Invariant: defs[id] == None || defs[id].pos() == id.pos()
75    pub defs: HashMap<IdentKey, Option<ObjKey>>,
76    /// 'uses' maps identifiers to the objects they denote.
77    ///
78    /// For an embedded field, 'uses' returns the TypeName it denotes.
79    ///
80    /// Invariant: uses[id].pos() != id.pos()
81    pub uses: HashMap<IdentKey, ObjKey>,
82    /// 'implicits' maps nodes to their implicitly declared objects, if any.
83    /// The following node and object types may appear:
84    ///     node               declared object
85    ///     ImportSpec    PkgName for imports without renames
86    ///     CaseClause    type-specific Object::Var for each type switch case clause (incl. default)
87    ///     Field         anonymous parameter Object::Var
88    pub implicits: HashMap<NodeId, ObjKey>,
89    /// 'selections' maps selector expressions (excluding qualified identifiers)
90    /// to their corresponding selections.
91    pub selections: HashMap<NodeId, Selection>,
92    /// 'scopes' maps ast::Nodes to the scopes they define. Package scopes are not
93    /// associated with a specific node but with all files belonging to a package.
94    /// Thus, the package scope can be found in the type-checked Package object.
95    /// Scopes nest, with the Universe scope being the outermost scope, enclosing
96    /// the package scope, which contains (one or more) files scopes, which enclose
97    /// function scopes which in turn enclose statement and function literal scopes.
98    /// Note that even though package-level functions are declared in the package
99    /// scope, the function scopes are embedded in the file scope of the file
100    /// containing the function declaration.
101    ///
102    /// The following node types may appear in Scopes:
103    ///     File
104    ///     FuncType
105    ///     BlockStmt
106    ///     IfStmt
107    ///     SwitchStmt
108    ///     TypeSwitchStmt
109    ///     CaseClause
110    ///     CommClause
111    ///     ForStmt
112    ///     RangeStmt
113    pub scopes: HashMap<NodeId, ScopeKey>,
114    /// 'init_order' is the list of package-level initializers in the order in which
115    /// they must be executed. Initializers referring to variables related by an
116    /// initialization dependency appear in topological order, the others appear
117    /// in source order. Variables without an initialization expression do not
118    /// appear in this list.
119    pub init_order: Vec<Initializer>,
120    /// oxfeeefeee: parse result of the package, to be used by code gen
121    pub ast_files: Vec<ast::File>,
122}
123
124impl TypeInfo {
125    pub fn new() -> TypeInfo {
126        TypeInfo {
127            types: HashMap::new(),
128            defs: HashMap::new(),
129            uses: HashMap::new(),
130            implicits: HashMap::new(),
131            selections: HashMap::new(),
132            scopes: HashMap::new(),
133            init_order: Vec::new(),
134            ast_files: Vec::new(),
135        }
136    }
137}
138
139/// ExprInfo stores information about an untyped expression.
140#[derive(Debug)]
141pub struct ExprInfo {
142    pub is_lhs: bool,
143    pub mode: OperandMode,
144    pub typ: Option<TypeKey>,
145}
146
147// ObjContext is context within which the current object is type-checked
148// (valid only for the duration of type-checking a specific object)
149#[derive(Clone)]
150pub struct ObjContext {
151    // package-level declaration whose init expression/function body is checked
152    pub decl: Option<DeclInfoKey>,
153    // top-most scope for lookups
154    pub scope: Option<ScopeKey>,
155    // if valid, identifiers are looked up as if at position pos (used by Eval)
156    pub pos: Option<Pos>,
157    // value of iota in a constant declaration; None otherwise
158    pub iota: Option<Value>,
159    // function signature if inside a function; None otherwise
160    pub sig: Option<TypeKey>,
161    // set of panic call ids (used for termination check)
162    pub panics: Option<HashSet<NodeId>>,
163    // set if a function makes use of labels (only ~1% of functions); unused outside functions
164    pub has_label: bool,
165    // set if an expression contains a function call or channel receive operation
166    pub has_call_or_recv: bool,
167}
168
169type DelayedAction = Box<dyn FnOnce(&mut Checker, &mut FilesContext)>;
170
171pub type RcIfaceInfo = Rc<IfaceInfo>;
172
173/// FilesContext contains information collected during type-checking
174/// of a set of package files
175pub struct FilesContext<'a> {
176    // package files
177    pub files: &'a Vec<ast::File>,
178    // positions of unused dot-imported packages for each file scope
179    pub unused_dot_imports: HashMap<ScopeKey, HashMap<PackageKey, Pos>>,
180    // maps package scope type names(LangObj::TypeName) to associated
181    // non-blank, non-interface methods(LangObj::Func)
182    pub methods: HashMap<ObjKey, Vec<ObjKey>>,
183    // maps interface(LangObj::TypeName) type names to corresponding
184    // interface infos
185    pub ifaces: HashMap<ObjKey, Option<RcIfaceInfo>>,
186    // map of expressions(ast::Expr) without final type
187    pub untyped: HashMap<NodeId, ExprInfo>,
188    // stack of delayed actions
189    pub delayed: Vec<DelayedAction>,
190    // path of object dependencies during type inference (for cycle reporting)
191    pub obj_path: Vec<ObjKey>,
192}
193
194pub struct Checker<'a> {
195    // object container for type checker
196    pub tc_objs: &'a mut TCObjects,
197    // object container for AST
198    pub ast_objs: &'a mut AstObjects,
199    // errors
200    errors: &'a ErrorList,
201    // files in this package
202    pub fset: &'a mut FileSet,
203    // all packages checked so far
204    pub all_pkgs: &'a mut HashMap<String, PackageKey>,
205    // all results, i.e. including results collected from
206    // previously created Checker instances
207    all_results: &'a mut HashMap<PackageKey, TypeInfo>,
208    // this package
209    pub pkg: PackageKey,
210    // maps package-level objects and (non-interface) methods to declaration info
211    pub obj_map: HashMap<ObjKey, DeclInfoKey>,
212    // maps (import path, source directory) to (complete or fake) package
213    pub imp_map: HashMap<ImportKey, PackageKey>,
214    // object context
215    pub octx: ObjContext,
216    // import config
217    config: &'a Config,
218    // result of type checking
219    pub result: TypeInfo,
220    // for debug
221    pub indent: Rc<RefCell<usize>>,
222}
223
224impl ObjContext {
225    pub fn new() -> ObjContext {
226        ObjContext {
227            decl: None,
228            scope: None,
229            pos: None,
230            iota: None,
231            sig: None,
232            panics: None,
233            has_label: false,
234            has_call_or_recv: false,
235        }
236    }
237}
238
239impl FilesContext<'_> {
240    pub fn new(files: &Vec<ast::File>) -> FilesContext<'_> {
241        FilesContext {
242            files: files,
243            unused_dot_imports: HashMap::new(),
244            methods: HashMap::new(),
245            ifaces: HashMap::new(),
246            untyped: HashMap::new(),
247            delayed: Vec::new(),
248            obj_path: Vec::new(),
249        }
250    }
251
252    /// file_name returns a filename suitable for debugging output.
253    pub fn file_name(&self, index: usize, checker: &Checker) -> String {
254        let file = &self.files[index];
255        let pos = file.pos(checker.ast_objs);
256        if pos > 0 {
257            checker.fset.file(pos).unwrap().name().to_owned()
258        } else {
259            format!("file[{}]", index)
260        }
261    }
262
263    pub fn add_unused_dot_import(&mut self, scope: &ScopeKey, pkg: &PackageKey, pos: Pos) {
264        if !self.unused_dot_imports.contains_key(scope) {
265            self.unused_dot_imports.insert(*scope, HashMap::new());
266        }
267        self.unused_dot_imports
268            .get_mut(scope)
269            .unwrap()
270            .insert(*pkg, pos);
271    }
272
273    pub fn remember_untyped(&mut self, e: &Expr, ex_info: ExprInfo) {
274        self.untyped.insert(e.id(), ex_info);
275    }
276
277    /// later pushes f on to the stack of actions that will be processed later;
278    /// either at the end of the current statement, or in case of a local constant
279    /// or variable declaration, before the constant or variable is in scope
280    /// (so that f still sees the scope before any new declarations).
281    pub fn later(&mut self, action: DelayedAction) {
282        self.delayed.push(action);
283    }
284
285    pub fn delayed_count(&self) -> usize {
286        self.delayed.len()
287    }
288
289    pub fn process_delayed(&mut self, top: usize, checker: &mut Checker) {
290        let fs: Vec<DelayedAction> = self.delayed.drain(top..).into_iter().collect();
291        for f in fs {
292            f(checker, self);
293        }
294    }
295
296    /// push pushes obj to obj_path and returns it's index
297    pub fn push(&mut self, obj: ObjKey) -> usize {
298        self.obj_path.push(obj);
299        self.obj_path.len() - 1
300    }
301
302    pub fn pop(&mut self) -> ObjKey {
303        self.obj_path.pop().unwrap()
304    }
305}
306
307impl TypeAndValue {
308    fn new(mode: OperandMode, typ: TypeKey) -> TypeAndValue {
309        TypeAndValue {
310            mode: mode,
311            typ: typ,
312        }
313    }
314}
315
316impl TypeInfo {
317    pub fn record_type_and_value(&mut self, e: &Expr, mode: OperandMode, typ: TypeKey) {
318        self.record_type_and_value_with_id(e.id(), mode, typ);
319    }
320
321    pub fn record_type_and_value_with_id(&mut self, id: NodeId, mode: OperandMode, typ: TypeKey) {
322        if let OperandMode::Invalid = mode {
323            return;
324        }
325        self.types.insert(id, TypeAndValue::new(mode, typ));
326    }
327
328    pub fn record_builtin_type(&mut self, mode: &OperandMode, e: &Expr, sig: TypeKey) {
329        let mut expr = e;
330        // expr must be a (possibly parenthesized) identifier denoting a built-in
331        // (built-ins in package unsafe always produce a constant result and
332        // we don't record their signatures, so we don't see qualified idents
333        // here): record the signature for f and possible children.
334        loop {
335            self.record_type_and_value(expr, mode.clone(), sig);
336            match expr {
337                Expr::Ident(_) => break,
338                Expr::Paren(p) => expr = &(*p).expr,
339                _ => unreachable!(),
340            }
341        }
342    }
343
344    pub fn record_comma_ok_types(
345        &mut self,
346        e: &Expr,
347        t: &[TypeKey; 2],
348        tc_objs: &mut TCObjects,
349        ast_objs: &AstObjects,
350        pkg: PackageKey,
351    ) {
352        let pos = e.pos(ast_objs);
353        let mut expr = e;
354        loop {
355            let tv = self.types.get_mut(&expr.id()).unwrap();
356            tv.typ = Checker::comma_ok_type(tc_objs, pos, pkg, t);
357            match expr {
358                Expr::Paren(p) => expr = &(*p).expr,
359                _ => break,
360            }
361        }
362    }
363
364    pub fn record_def(&mut self, id: IdentKey, obj: Option<ObjKey>) {
365        self.defs.insert(id, obj);
366    }
367
368    pub fn record_use(&mut self, id: IdentKey, obj: ObjKey) {
369        self.uses.insert(id, obj);
370    }
371
372    pub fn record_implicit(&mut self, node: &impl Node, obj: ObjKey) {
373        self.implicits.insert(node.id(), obj);
374    }
375
376    pub fn record_selection(&mut self, expr: &ast::SelectorExpr, sel: Selection) {
377        self.record_use(expr.sel, sel.obj());
378        self.selections.insert(expr.id(), sel);
379    }
380
381    pub fn record_scope(&mut self, node: &impl Node, scope: ScopeKey) {
382        self.scopes.insert(node.id(), scope);
383    }
384
385    pub fn record_init_order(&mut self, init_order: Vec<Initializer>) {
386        self.init_order = init_order;
387    }
388}
389
390impl<'a> Checker<'a> {
391    pub fn new(
392        tc_objs: &'a mut TCObjects,
393        ast_objs: &'a mut AstObjects,
394        fset: &'a mut FileSet,
395        errors: &'a ErrorList,
396        pkgs: &'a mut HashMap<String, PackageKey>,
397        all_results: &'a mut HashMap<PackageKey, TypeInfo>,
398        pkg: PackageKey,
399        cfg: &'a Config,
400    ) -> Checker<'a> {
401        Checker {
402            tc_objs: tc_objs,
403            ast_objs: ast_objs,
404            fset: fset,
405            errors: errors,
406            all_pkgs: pkgs,
407            all_results: all_results,
408            pkg: pkg,
409            obj_map: HashMap::new(),
410            imp_map: HashMap::new(),
411            octx: ObjContext::new(),
412            config: cfg,
413            result: TypeInfo::new(),
414            indent: Rc::new(RefCell::new(0)),
415        }
416    }
417
418    pub fn check(mut self, mut files: Vec<ast::File>) -> Result<PackageKey, ()> {
419        self.check_files_pkg_name(&files)?;
420        let fctx = &mut FilesContext::new(&files);
421        self.collect_objects(fctx);
422        self.package_objects(fctx);
423        fctx.process_delayed(0, &mut self);
424        self.init_order();
425        self.unused_imports(fctx);
426        self.record_untyped(fctx);
427
428        std::mem::swap(&mut self.result.ast_files, &mut files);
429        self.all_results.insert(self.pkg, self.result);
430        Ok(self.pkg)
431    }
432
433    fn record_untyped(&mut self, fctx: &mut FilesContext) {
434        for (id, info) in fctx.untyped.drain().into_iter() {
435            if info.mode != OperandMode::Invalid {
436                self.result
437                    .record_type_and_value_with_id(id.clone(), info.mode, info.typ.unwrap());
438            }
439        }
440    }
441
442    pub fn errors(&self) -> &ErrorList {
443        self.errors
444    }
445
446    pub fn config(&self) -> &Config {
447        &self.config
448    }
449
450    pub fn new_importer(&mut self, pos: Pos) -> Importer {
451        Importer::new(
452            self.config,
453            self.fset,
454            self.all_pkgs,
455            self.all_results,
456            self.ast_objs,
457            self.tc_objs,
458            self.errors,
459            pos,
460        )
461    }
462
463    /// check files' package name
464    fn check_files_pkg_name(&mut self, files: &Vec<ast::File>) -> Result<(), ()> {
465        let mut pkg_name: Option<String> = None;
466        for f in files.iter() {
467            let ident = &self.ast_objs.idents[f.name];
468            if pkg_name.is_none() {
469                if ident.name == "_" {
470                    self.error(ident.pos, "invalid package name _".to_string());
471                    return Err(());
472                } else {
473                    pkg_name = Some(ident.name.clone());
474                }
475            } else if &ident.name != pkg_name.as_ref().unwrap() {
476                self.error(
477                    f.package,
478                    format!(
479                        "package {}; expected {}",
480                        ident.name,
481                        pkg_name.as_ref().unwrap()
482                    ),
483                );
484                return Err(());
485            }
486        }
487        self.tc_objs.pkgs[self.pkg].set_name(pkg_name.unwrap());
488        Ok(())
489    }
490
491    pub fn error(&self, pos: Pos, err: String) {
492        self.error_impl(pos, err, false);
493    }
494
495    pub fn error_str(&self, pos: Pos, err: &str) {
496        self.error_impl(pos, err.to_string(), false);
497    }
498
499    pub fn soft_error(&self, pos: Pos, err: String) {
500        self.error_impl(pos, err, true);
501    }
502
503    pub fn soft_error_str(&self, pos: Pos, err: &str) {
504        self.error_impl(pos, err.to_string(), true);
505    }
506
507    fn error_impl(&self, pos: Pos, err: String, soft: bool) {
508        let file = self.fset.file(pos).unwrap();
509        FilePosErrors::new(file, self.errors).add(pos, err, soft);
510    }
511}