go_types/check/
util.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
12use crate::SourceRead;
13
14use super::super::display::{Display, Displayer};
15use super::super::obj;
16use super::super::objects::{DeclInfoKey, ObjKey, PackageKey, ScopeKey, TCObjects, TypeKey};
17use super::super::operand::{Operand, OperandMode};
18use super::super::package::Package;
19use super::super::scope::Scope;
20use super::super::typ::{self, BasicType, Type};
21use super::super::universe::{Builtin, BuiltinInfo};
22use super::check::{Checker, FilesContext};
23use super::resolver::DeclInfo;
24use std::cmp::Ordering;
25
26use go_parser::{ast, ast::Expr, FilePos, IdentKey, Map, Pos};
27
28macro_rules! error_operand {
29    ($x:ident, $fmt:expr, $checker:ident) => {
30        let xd = $checker.new_dis(&$x);
31        $checker.error(xd.pos(), format!($fmt, xd));
32    };
33}
34
35#[derive(Debug)]
36pub enum UnpackResult<'a> {
37    Tuple(Option<Expr>, Vec<Option<TypeKey>>, Ordering), // rhs is a tuple
38    CommaOk(Option<Expr>, [TypeKey; 2]),                 // rhs returns comma_ok
39    Mutliple(&'a Vec<Expr>, Ordering),                   // N to N
40    Single(Operand, Ordering),                           // 1 to 1
41    Nothing(Ordering),                                   // nothing to unpack
42    Error,                                               // errors when trying to unpack
43}
44
45impl<'a> UnpackResult<'a> {
46    pub fn get<S: SourceRead>(
47        &self,
48        checker: &mut Checker<S>,
49        x: &mut Operand,
50        i: usize,
51        fctx: &mut FilesContext<S>,
52    ) {
53        match self {
54            UnpackResult::Tuple(expr, types, _) => {
55                x.mode = OperandMode::Value;
56                x.expr = expr.clone();
57                x.typ = types[i];
58            }
59            UnpackResult::CommaOk(expr, types) => {
60                x.mode = OperandMode::Value;
61                x.expr = expr.clone();
62                x.typ = Some(types[i]);
63            }
64            UnpackResult::Mutliple(exprs, _) => {
65                checker.multi_expr(x, &exprs[i], fctx);
66            }
67            UnpackResult::Single(sx, _) => {
68                x.mode = sx.mode.clone();
69                x.expr = sx.expr.clone();
70                x.typ = sx.typ;
71            }
72            UnpackResult::Nothing(_) => unreachable!(),
73            UnpackResult::Error => unreachable!(),
74        }
75    }
76
77    // rhs_count return the var count of rhs, and if it less than or equal to or matchs
78    // the required count
79    pub fn rhs_count(&self) -> (usize, Ordering) {
80        match self {
81            UnpackResult::Tuple(_, types, ord) => (types.len(), *ord),
82            UnpackResult::CommaOk(_, types) => (types.len(), Ordering::Equal),
83            UnpackResult::Mutliple(exprs, ord) => (exprs.len(), *ord),
84            UnpackResult::Single(_, ord) => (1, *ord),
85            UnpackResult::Nothing(ord) => (0, *ord),
86            UnpackResult::Error => unreachable!(),
87        }
88    }
89
90    pub fn use_<S: SourceRead>(
91        &self,
92        checker: &mut Checker<S>,
93        from: usize,
94        fctx: &mut FilesContext<S>,
95    ) {
96        let exprs = match self {
97            UnpackResult::Mutliple(exprs, _) => exprs,
98            _ => {
99                return;
100            }
101        };
102
103        let mut x = Operand::new();
104        for i in from..exprs.len() {
105            checker.multi_expr(&mut x, &exprs[i], fctx);
106        }
107    }
108
109    pub fn is_err(&self) -> bool {
110        match self {
111            UnpackResult::Error => true,
112            _ => false,
113        }
114    }
115}
116
117#[derive(Debug)]
118pub struct UnpackedResultLeftovers<'a> {
119    pub leftovers: &'a UnpackResult<'a>,
120    pub consumed: Option<&'a Vec<Operand>>,
121}
122
123impl<'a> UnpackedResultLeftovers<'a> {
124    pub fn new(
125        re: &'a UnpackResult<'a>,
126        consumed: Option<&'a Vec<Operand>>,
127    ) -> UnpackedResultLeftovers<'a> {
128        UnpackedResultLeftovers {
129            leftovers: re,
130            consumed: consumed,
131        }
132    }
133
134    pub fn use_all<S: SourceRead>(&self, checker: &mut Checker<S>, fctx: &mut FilesContext<S>) {
135        let from = if self.consumed.is_none() {
136            0
137        } else {
138            self.consumed.unwrap().len()
139        };
140        self.leftovers.use_(checker, from, fctx);
141    }
142
143    pub fn get<S: SourceRead>(
144        &self,
145        checker: &mut Checker<S>,
146        x: &mut Operand,
147        i: usize,
148        fctx: &mut FilesContext<S>,
149    ) {
150        if self.consumed.is_none() {
151            self.leftovers.get(checker, x, i, fctx);
152            return;
153        }
154        let consumed = self.consumed.unwrap();
155        if i < consumed.len() {
156            let c = &consumed[i];
157            x.mode = c.mode.clone();
158            x.expr = c.expr.clone();
159            x.typ = c.typ;
160        } else {
161            self.leftovers.get(checker, x, i, fctx);
162        }
163    }
164}
165
166impl<'a, S: SourceRead> Checker<'a, S> {
167    pub fn unparen(x: &Expr) -> &Expr {
168        if let Expr::Paren(p) = x {
169            Checker::<S>::unparen(&p.expr)
170        } else {
171            x
172        }
173    }
174
175    /// invalid_ast helps to report ast error
176    pub fn invalid_ast(&self, pos: Pos, err: &str) {
177        self.error(pos, format!("invalid AST: {}", err));
178    }
179
180    pub fn invalid_arg(&self, pos: Pos, err: &str) {
181        self.error(pos, format!("invalid argument: {}", err));
182    }
183
184    pub fn invalid_op(&self, pos: Pos, err: &str) {
185        self.error(pos, format!("invalid operation: {}", err));
186    }
187
188    pub fn obj_path_str(&self, path: &Vec<ObjKey>) -> String {
189        let names: Vec<&str> = path.iter().map(|p| self.lobj(*p).name().as_str()).collect();
190        names[..].join("->")
191    }
192
193    pub fn dump(&self, pos: Option<Pos>, msg: &str) {
194        if let Some(p) = pos {
195            let p = self.fset.position(p);
196            print!("checker dump({}):{}\n", p.unwrap_or(FilePos::null()), msg);
197        } else {
198            print!("checker dump:{}\n", msg);
199        }
200    }
201
202    pub fn print_trace(&self, pos: Pos, msg: &str) {
203        let p = self.fset.position(pos);
204        print!(
205            "{}:\t{}{}\n",
206            p.unwrap_or(FilePos::null()),
207            ".  ".repeat(*self.indent.borrow()),
208            msg
209        );
210    }
211
212    pub fn trace_begin(&self, pos: Pos, msg: &str) {
213        self.print_trace(pos, msg);
214        *self.indent.borrow_mut() += 1;
215    }
216
217    pub fn trace_end(&self, pos: Pos, msg: &str) {
218        *self.indent.borrow_mut() -= 1;
219        self.print_trace(pos, msg);
220    }
221
222    // has_cycle reports whether obj appears in path or not.
223    // If it does, and report is set, it also reports a cycle error.
224    pub fn has_cycle(&self, okey: ObjKey, path: &[ObjKey], report: bool) -> bool {
225        if let Some((i, _)) = path.iter().enumerate().find(|(_, &x)| x == okey) {
226            if report {
227                let obj_val = self.lobj(okey);
228                self.error(
229                    obj_val.pos(),
230                    format!("illegal cycle in declaration of {}", obj_val.name()),
231                );
232                // print cycle
233                for o in path[i..].iter() {
234                    let oval = self.lobj(*o);
235                    self.error(oval.pos(), format!("\t{} refers to", oval.name()));
236                }
237                self.error(obj_val.pos(), format!("\t{}", obj_val.name()));
238            }
239            return true;
240        }
241        false
242    }
243
244    pub fn comma_ok_type(
245        tc_objs: &mut TCObjects,
246        pos: usize,
247        pkg: PackageKey,
248        t: &[TypeKey; 2],
249    ) -> TypeKey {
250        let vars = vec![
251            tc_objs.lobjs.insert(obj::LangObj::new_var(
252                pos,
253                Some(pkg),
254                String::new(),
255                Some(t[0]),
256            )),
257            tc_objs.lobjs.insert(obj::LangObj::new_var(
258                pos,
259                Some(pkg),
260                String::new(),
261                Some(t[1]),
262            )),
263        ];
264        tc_objs.new_t_tuple(vars)
265    }
266
267    pub fn unpack<'b>(
268        &mut self,
269        rhs: &'b Vec<Expr>,
270        lhs_len: usize,
271        allow_comma_ok: bool,
272        variadic: bool,
273        fctx: &mut FilesContext<S>,
274    ) -> UnpackResult<'b> {
275        let do_match = |rhs_len: usize| {
276            let order = rhs_len.cmp(&lhs_len);
277            if variadic && order == Ordering::Greater {
278                Ordering::Equal
279            } else {
280                order
281            }
282        };
283        if rhs.len() != 1 {
284            let matching = do_match(rhs.len());
285            return if rhs.len() == 0 {
286                UnpackResult::Nothing(matching)
287            } else {
288                UnpackResult::Mutliple(rhs, matching)
289            };
290        }
291
292        let mut x = Operand::new();
293        self.multi_expr(&mut x, &rhs[0], fctx);
294        if x.invalid() {
295            return UnpackResult::Error;
296        }
297
298        if let Some(t) = self.otype(x.typ.unwrap()).try_as_tuple() {
299            let types: Vec<Option<TypeKey>> =
300                t.vars().iter().map(|x| self.lobj(*x).typ()).collect();
301            let matching = do_match(types.len());
302            return UnpackResult::Tuple(x.expr.clone(), types, matching);
303        } else if x.mode == OperandMode::MapIndex || x.mode == OperandMode::CommaOk {
304            if allow_comma_ok {
305                let types = [x.typ.unwrap(), self.basic_type(BasicType::UntypedBool)];
306                return UnpackResult::CommaOk(x.expr.clone(), types);
307            }
308            x.mode = OperandMode::Value;
309        }
310
311        UnpackResult::Single(x, do_match(1))
312    }
313
314    pub fn use_exprs(&mut self, exprs: &Vec<Expr>, fctx: &mut FilesContext<S>) {
315        let x = &mut Operand::new();
316        for e in exprs.iter() {
317            self.raw_expr(x, &e, None, fctx);
318        }
319    }
320
321    // use_lhs is like use_exprs, but doesn't "use" top-level identifiers.
322    // It should be called instead of use_exprs if the arguments are
323    // expressions on the lhs of an assignment.
324    // The arguments must not be nil.
325    pub fn use_lhs(&mut self, lhs: &Vec<Expr>, fctx: &mut FilesContext<S>) {
326        let x = &mut Operand::new();
327        for e in lhs.iter() {
328            let v = match Checker::<S>::unparen(e) {
329                Expr::Ident(ikey) => match &self.ast_ident(*ikey).name {
330                    s if s == "_" => continue,
331                    s => Scope::lookup_parent(
332                        self.octx.scope.as_ref().unwrap(),
333                        s,
334                        None,
335                        self.tc_objs,
336                    )
337                    .map(|(_, okey)| okey)
338                    .map(|okey| {
339                        let lobj = self.lobj(okey);
340                        match lobj.entity_type() {
341                            obj::EntityType::Var(vp) => match lobj.pkg() == Some(self.pkg) {
342                                true => Some((okey, vp.used)),
343                                false => None,
344                            },
345                            _ => None,
346                        }
347                    }),
348                },
349                _ => None,
350            }
351            .flatten();
352
353            self.raw_expr(x, &e, None, fctx);
354
355            if let Some((okey, used)) = v {
356                match self.lobj_mut(okey).entity_type_mut() {
357                    obj::EntityType::Var(vp) => vp.used = used,
358                    _ => unreachable!(),
359                }
360            }
361        }
362    }
363
364    pub fn lookup(&self, name: &str) -> Option<ObjKey> {
365        Scope::lookup_parent(
366            self.octx.scope.as_ref().unwrap(),
367            name,
368            self.octx.pos,
369            self.tc_objs,
370        )
371        .map(|(_, okey)| okey)
372    }
373
374    pub fn add_decl_dep(&mut self, to: ObjKey) {
375        if self.octx.decl.is_none() {
376            // not in a package-level init expression
377            return;
378        }
379        if !self.obj_map.contains_key(&to) {
380            return;
381        }
382        self.tc_objs.decls[self.octx.decl.unwrap()].add_dep(to);
383    }
384
385    pub fn insert_obj_to_set(&self, set: &mut Map<String, ObjKey>, okey: ObjKey) -> Option<ObjKey> {
386        let obj_val = self.lobj(okey);
387        let id = obj_val.id(self.tc_objs).to_string();
388        set.insert(id, okey)
389    }
390
391    pub fn ast_ident(&self, key: IdentKey) -> &ast::Ident {
392        &self.ast_objs.idents[key]
393    }
394
395    pub fn lobj(&self, key: ObjKey) -> &obj::LangObj {
396        &self.tc_objs.lobjs[key]
397    }
398
399    pub fn lobj_mut(&mut self, key: ObjKey) -> &mut obj::LangObj {
400        &mut self.tc_objs.lobjs[key]
401    }
402
403    pub fn otype(&self, key: TypeKey) -> &Type {
404        &self.tc_objs.types[key]
405    }
406
407    pub fn otype_mut(&mut self, key: TypeKey) -> &mut Type {
408        &mut self.tc_objs.types[key]
409    }
410
411    pub fn otype_interface(&self, key: TypeKey) -> &typ::InterfaceDetail {
412        self.otype(key).try_as_interface().unwrap()
413    }
414
415    pub fn otype_signature(&self, key: TypeKey) -> &typ::SignatureDetail {
416        self.otype(key).try_as_signature().unwrap()
417    }
418
419    pub fn otype_interface_mut(&mut self, key: TypeKey) -> &mut typ::InterfaceDetail {
420        self.otype_mut(key).try_as_interface_mut().unwrap()
421    }
422
423    pub fn otype_signature_mut(&mut self, key: TypeKey) -> &mut typ::SignatureDetail {
424        self.otype_mut(key).try_as_signature_mut().unwrap()
425    }
426
427    pub fn package(&self, key: PackageKey) -> &Package {
428        &self.tc_objs.pkgs[key]
429    }
430
431    pub fn package_mut(&mut self, key: PackageKey) -> &mut Package {
432        &mut self.tc_objs.pkgs[key]
433    }
434
435    pub fn scope(&self, key: ScopeKey) -> &Scope {
436        &self.tc_objs.scopes[key]
437    }
438
439    pub fn decl_info(&self, key: DeclInfoKey) -> &DeclInfo {
440        &self.tc_objs.decls[key]
441    }
442
443    pub fn position(&self, pos: Pos) -> FilePos {
444        self.fset.file(pos).unwrap().position(pos)
445    }
446
447    pub fn builtin_info(&self, id: Builtin) -> &BuiltinInfo {
448        &self.tc_objs.universe().builtins()[&id]
449    }
450
451    pub fn basic_type(&self, t: typ::BasicType) -> TypeKey {
452        self.tc_objs.universe().types()[&t]
453    }
454
455    pub fn invalid_type(&self) -> TypeKey {
456        self.basic_type(typ::BasicType::Invalid)
457    }
458
459    pub fn new_dis<'b>(&'b self, x: &'b impl Display) -> Displayer<'b> {
460        Displayer::new(x, Some(self.ast_objs), Some(self.tc_objs))
461    }
462
463    pub fn new_td_o<'t>(&'t self, t: &'t Option<TypeKey>) -> Displayer<'t> {
464        Displayer::new(t.as_ref().unwrap(), Some(self.ast_objs), Some(self.tc_objs))
465    }
466}