go_types/check/
check.rs

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