go_types/
operand.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 crate::SourceRead;
14
15use super::check::{Checker, FilesContext};
16use super::constant;
17use super::lookup::missing_method;
18use super::objects::{TCObjects, TypeKey};
19use super::typ;
20use super::typ::{fmt_type, BasicType, Type};
21use super::universe::{Builtin, Universe};
22use go_parser::ast::*;
23use go_parser::visitor::{walk_expr, ExprVisitor};
24use go_parser::{AstObjects, FuncTypeKey, IdentKey, Pos, Token};
25use std::fmt;
26use std::fmt::Display;
27use std::fmt::Write;
28
29/// An OperandMode specifies the (addressing) mode of an operand.
30#[derive(Clone, Debug, PartialEq)]
31pub enum OperandMode {
32    Invalid,                   // operand is invalid
33    NoValue,                   // operand represents no value (result of a function call w/o result)
34    Builtin(Builtin),          // operand is a built-in function
35    TypeExpr,                  // operand is a type
36    Constant(constant::Value), // operand is a constant; the operand's typ is a Basic type
37    Variable,                  // operand is an addressable variable
38    MapIndex, // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
39    Value,    // operand is a computed value
40    CommaOk,  // like value, but operand may be used in a comma,ok expression
41}
42
43impl OperandMode {
44    pub fn constant_val(&self) -> Option<&constant::Value> {
45        match self {
46            OperandMode::Constant(v) => Some(v),
47            _ => None,
48        }
49    }
50
51    pub fn builtin_id(&self) -> Option<Builtin> {
52        match self {
53            OperandMode::Builtin(id) => Some(*id),
54            _ => None,
55        }
56    }
57}
58
59impl fmt::Display for OperandMode {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        f.write_str(match self {
62            OperandMode::Invalid => "invalid operand",
63            OperandMode::NoValue => "no value",
64            OperandMode::Builtin(_) => "built-in",
65            OperandMode::TypeExpr => "type",
66            OperandMode::Constant(_) => "constant",
67            OperandMode::Variable => "variable",
68            OperandMode::MapIndex => "map index expression",
69            OperandMode::Value => "value",
70            OperandMode::CommaOk => "comma, ok expression",
71        })
72    }
73}
74
75/// An Operand represents an intermediate value during type checking.
76/// Operands have an (addressing) mode, the expression evaluating to
77/// the operand, the operand's type, a value for constants, and an id
78/// for built-in functions.
79#[derive(Clone, Debug)]
80pub struct Operand {
81    pub mode: OperandMode,
82    pub expr: Option<Expr>,
83    pub typ: Option<TypeKey>,
84}
85
86impl Operand {
87    pub fn new() -> Operand {
88        Operand::with(OperandMode::Invalid, None, None)
89    }
90
91    pub fn with(mode: OperandMode, expr: Option<Expr>, typ: Option<TypeKey>) -> Operand {
92        Operand {
93            mode: mode,
94            expr: expr,
95            typ: typ,
96        }
97    }
98
99    pub fn invalid(&self) -> bool {
100        self.mode == OperandMode::Invalid
101    }
102
103    pub fn pos(&self, ast_objs: &AstObjects) -> Pos {
104        if let Some(e) = &self.expr {
105            e.pos(ast_objs)
106        } else {
107            0
108        }
109    }
110
111    pub fn set_const(&mut self, t: &Token, u: &Universe) {
112        let bt = match t {
113            Token::INT(_) => BasicType::UntypedInt,
114            Token::FLOAT(_) => BasicType::UntypedFloat,
115            Token::IMAG(_) => BasicType::UntypedComplex,
116            Token::CHAR(_) => BasicType::UntypedRune,
117            Token::STRING(_) => BasicType::UntypedString,
118            _ => unreachable!(),
119        };
120        self.mode = OperandMode::Constant(constant::Value::with_literal(t));
121        self.typ = Some(u.types()[&bt]);
122    }
123
124    pub fn is_nil(&self, u: &Universe) -> bool {
125        match self.mode {
126            OperandMode::Value => self.typ == Some(u.types()[&BasicType::UntypedNil]),
127            _ => false,
128        }
129    }
130
131    /// assignable_to returns whether self is assignable to a variable of type 't'.
132    /// If the result is false and a non-None reason is provided, it may be set
133    /// to a more detailed explanation of the failure.
134    pub fn assignable_to<S: SourceRead>(
135        &self,
136        t: TypeKey,
137        reason: Option<&mut String>,
138        checker: &mut Checker<S>,
139        fctx: &mut FilesContext<S>,
140    ) -> bool {
141        let objs = &checker.tc_objs;
142        let u = objs.universe();
143        if self.invalid() || t == u.types()[&BasicType::Invalid] {
144            return true; // avoid spurious errors
145        }
146
147        if typ::identical(self.typ.unwrap(), t, objs) {
148            return true;
149        }
150
151        let (k_left, k_right) = (t, self.typ.unwrap());
152        let t_left = &objs.types[k_left];
153        let t_right = &objs.types[k_right];
154        let ut_key_left = typ::underlying_type(k_left, objs);
155        let ut_key_right = typ::underlying_type(k_right, objs);
156        let ut_left = &objs.types[ut_key_left];
157        let ut_right = &objs.types[ut_key_right];
158
159        if ut_right.is_untyped(objs) {
160            match ut_left {
161                Type::Basic(detail) => {
162                    if self.is_nil(u) && detail.typ() == BasicType::UnsafePointer {
163                        return true;
164                    }
165                    if let OperandMode::Constant(val) = &self.mode {
166                        return val.representable(detail, None);
167                    }
168                    // The result of a comparison is an untyped boolean,
169                    // but may not be a constant.
170                    if detail.typ() == BasicType::Bool {
171                        return ut_right.try_as_basic().unwrap().typ() == BasicType::UntypedBool;
172                    }
173                }
174                Type::Interface(detail) => return self.is_nil(u) || detail.is_empty(),
175                Type::Pointer(_)
176                | Type::Signature(_)
177                | Type::Slice(_)
178                | Type::Map(_)
179                | Type::Chan(_) => return self.is_nil(u),
180                _ => {}
181            }
182        }
183
184        // 'right' is typed
185        // self's type 'right' and 'left' have identical underlying types
186        // and at least one of 'right' or 'left' is not a named type
187        if typ::identical(ut_key_right, ut_key_left, objs)
188            && (!t_right.is_named() || !t_left.is_named())
189        {
190            return true;
191        }
192
193        // 'left' is an interface and 'right' implements 'left'
194        if ut_left.try_as_interface().is_some() {
195            if let Some((m, wrong_type)) = missing_method(k_right, ut_key_left, true, checker, fctx)
196            {
197                if let Some(re) = reason {
198                    let msg = if wrong_type {
199                        "wrong type for method"
200                    } else {
201                        "missing method"
202                    };
203                    *re = format!("{} {}", msg, checker.tc_objs.lobjs[m].name());
204                }
205                return false;
206            }
207            return true;
208        }
209
210        // 'right' is a bidirectional channel value, 'left' is a channel
211        // type, they have identical element types,
212        // and at least one of 'right' or 'left' is not a named type
213        if let Some(cr) = ut_right.try_as_chan() {
214            if cr.dir() == typ::ChanDir::SendRecv {
215                if let Some(cl) = ut_left.try_as_chan() {
216                    if typ::identical(cr.elem(), cl.elem(), objs) {
217                        return !t_right.is_named() || !t_left.is_named();
218                    }
219                }
220            }
221        }
222
223        false
224    }
225
226    /// Operand string formats
227    /// (not all "untyped" cases can appear due to the type system)
228    ///
229    /// mode       format
230    ///
231    /// invalid    <expr> (               <mode>                    )
232    /// novalue    <expr> (               <mode>                    )
233    /// builtin    <expr> (               <mode>                    )
234    /// typexpr    <expr> (               <mode>                    )
235    ///
236    /// constant   <expr> (<untyped kind> <mode>                    )
237    /// constant   <expr> (               <mode>       of type <typ>)
238    /// constant   <expr> (<untyped kind> <mode> <val>              )
239    /// constant   <expr> (               <mode> <val> of type <typ>)
240    ///
241    /// variable   <expr> (<untyped kind> <mode>                    )
242    /// variable   <expr> (               <mode>       of type <typ>)
243    ///
244    /// mapindex   <expr> (<untyped kind> <mode>                    )
245    /// mapindex   <expr> (               <mode>       of type <typ>)
246    ///
247    /// value      <expr> (<untyped kind> <mode>                    )
248    /// value      <expr> (               <mode>       of type <typ>)
249    ///
250    /// commaok    <expr> (<untyped kind> <mode>                    )
251    /// commaok    <expr> (               <mode>       of type <typ>)
252    pub fn fmt(
253        &self,
254        f: &mut fmt::Formatter<'_>,
255        tc_objs: &TCObjects,
256        ast_objs: &AstObjects,
257    ) -> fmt::Result {
258        let universe = tc_objs.universe();
259        let mut has_expr = true;
260
261        // <expr> (
262        if let Some(expr) = &self.expr {
263            fmt_expr(&expr, f, ast_objs)?;
264        } else {
265            match &self.mode {
266                OperandMode::Builtin(bi) => {
267                    f.write_str(universe.builtins()[bi].name)?;
268                }
269                OperandMode::TypeExpr => {
270                    fmt_type(self.typ, f, tc_objs)?;
271                }
272                OperandMode::Constant(val) => {
273                    f.write_str(&val.to_string())?;
274                }
275                _ => has_expr = false,
276            }
277        }
278        if has_expr {
279            f.write_str(" (")?;
280        }
281
282        // <untyped kind>
283        let has_type = match self.mode {
284            OperandMode::Invalid
285            | OperandMode::NoValue
286            | OperandMode::Builtin(_)
287            | OperandMode::TypeExpr => false,
288            _ => {
289                let tval = &tc_objs.types[self.typ.unwrap()];
290                if tval.is_untyped(tc_objs) {
291                    f.write_str(tval.try_as_basic().unwrap().name())?;
292                    f.write_char(' ')?;
293                    false
294                } else {
295                    true
296                }
297            }
298        };
299
300        // <mode>
301        self.mode.fmt(f)?;
302
303        // <val>
304        if let OperandMode::Constant(val) = &self.mode {
305            if self.expr.is_some() {
306                f.write_char(' ')?;
307                f.write_str(&val.to_string())?;
308            }
309        }
310
311        // <typ>
312        if has_type {
313            if self.typ != Some(universe.types()[&BasicType::Invalid]) {
314                f.write_str(" of type ")?;
315                fmt_type(self.typ, f, tc_objs)?;
316            } else {
317                f.write_str(" with invalid type")?;
318            }
319        }
320
321        // )
322        if has_expr {
323            f.write_char(')')?;
324        }
325        Ok(())
326    }
327}
328
329/// fmt_expr formats the (possibly shortened) string representation for 'expr'.
330/// Shortened representations are suitable for user interfaces but may not
331/// necessarily follow Go syntax.
332pub fn fmt_expr(expr: &Expr, f: &mut fmt::Formatter<'_>, ast_objs: &AstObjects) -> fmt::Result {
333    // The AST preserves source-level parentheses so there is
334    // no need to introduce them here to correct for different
335    // operator precedences. (This assumes that the AST was
336    // generated by a Go parser.)
337    ExprFormater {
338        f: f,
339        ast_objs: ast_objs,
340    }
341    .visit_expr(expr)
342}
343
344struct ExprFormater<'a, 'b> {
345    f: &'a mut fmt::Formatter<'b>,
346    ast_objs: &'a AstObjects,
347}
348
349impl<'a, 'b> ExprVisitor for ExprFormater<'a, 'b> {
350    type Result = fmt::Result;
351
352    fn visit_expr_ident(&mut self, _: &Expr, ident: &IdentKey) -> Self::Result {
353        self.fmt_ident(ident)
354    }
355
356    fn visit_expr_ellipsis(&mut self, _: &Expr, els: &Option<Expr>) -> Self::Result {
357        self.f.write_str("...")?;
358        if let Some(e) = els {
359            self.visit_expr(e)?;
360        }
361        Ok(())
362    }
363
364    fn visit_expr_basic_lit(&mut self, _: &Expr, blit: &BasicLit) -> Self::Result {
365        blit.token.fmt(self.f)
366    }
367
368    fn visit_expr_func_lit(&mut self, this: &Expr, flit: &FuncLit) -> Self::Result {
369        self.f.write_char('(')?;
370        self.visit_expr_func_type(this, &flit.typ)?;
371        self.f.write_str(" literal)")
372    }
373
374    fn visit_expr_composit_lit(&mut self, _: &Expr, clit: &CompositeLit) -> Self::Result {
375        self.f.write_char('(')?;
376        match &clit.typ {
377            Some(t) => {
378                self.visit_expr(t)?;
379            }
380            None => {
381                self.f.write_str("(bad expr)")?;
382            }
383        }
384        self.f.write_str(" literal)")
385    }
386
387    fn visit_expr_paren(&mut self, _: &Expr, expr: &Expr) -> Self::Result {
388        self.f.write_char('(')?;
389        self.visit_expr(expr)?;
390        self.f.write_char(')')
391    }
392
393    fn visit_expr_selector(&mut self, this: &Expr, expr: &Expr, ident: &IdentKey) -> Self::Result {
394        self.visit_expr(expr)?;
395        self.f.write_char('.')?;
396        self.visit_expr_ident(this, ident)
397    }
398
399    fn visit_expr_index(&mut self, _: &Expr, expr: &Expr, index: &Expr) -> Self::Result {
400        self.visit_expr(expr)?;
401        self.f.write_char('[')?;
402        self.visit_expr(index)?;
403        self.f.write_char(']')
404    }
405
406    fn visit_expr_slice(
407        &mut self,
408        _: &Expr,
409        expr: &Expr,
410        low: &Option<Expr>,
411        high: &Option<Expr>,
412        max: &Option<Expr>,
413    ) -> Self::Result {
414        self.visit_expr(expr)?;
415        self.f.write_char('[')?;
416        if let Some(l) = low {
417            self.visit_expr(l)?;
418        }
419        self.f.write_char(':')?;
420        if let Some(h) = high {
421            self.visit_expr(h)?;
422        }
423        if let Some(m) = max {
424            self.f.write_char(':')?;
425            self.visit_expr(m)?;
426        }
427        self.f.write_char(']')
428    }
429
430    fn visit_expr_type_assert(
431        &mut self,
432        _: &Expr,
433        expr: &Expr,
434        typ: &Option<Expr>,
435    ) -> Self::Result {
436        self.visit_expr(expr)?;
437        self.f.write_str(".(")?;
438        match &typ {
439            Some(t) => {
440                self.visit_expr(t)?;
441            }
442            None => {
443                self.f.write_str("(bad expr)")?;
444            }
445        }
446        self.f.write_char(')')
447    }
448
449    fn visit_expr_call(
450        &mut self,
451        _: &Expr,
452        func: &Expr,
453        args: &Vec<Expr>,
454        ellipsis: bool,
455    ) -> Self::Result {
456        self.visit_expr(func)?;
457        self.f.write_char('(')?;
458        for (i, arg) in args.iter().enumerate() {
459            if i > 0 {
460                self.f.write_str(", ")?;
461            }
462            self.visit_expr(arg)?;
463        }
464        if ellipsis {
465            self.f.write_str("...")?;
466        }
467        self.f.write_char(')')
468    }
469
470    fn visit_expr_star(&mut self, _: &Expr, expr: &Expr) -> Self::Result {
471        self.f.write_char('*')?;
472        self.visit_expr(expr)
473    }
474
475    fn visit_expr_unary(&mut self, _: &Expr, expr: &Expr, op: &Token) -> Self::Result {
476        op.fmt(self.f)?;
477        self.visit_expr(expr)
478    }
479
480    fn visit_expr_binary(
481        &mut self,
482        _: &Expr,
483        left: &Expr,
484        op: &Token,
485        right: &Expr,
486    ) -> Self::Result {
487        self.visit_expr(left)?;
488        self.f.write_fmt(format_args!(" {} ", op))?;
489        self.visit_expr(right)
490    }
491
492    fn visit_expr_key_value(&mut self, _: &Expr, _: &Expr, _: &Expr) -> Self::Result {
493        self.f.write_str("(bad expr)")
494    }
495
496    fn visit_expr_array_type(&mut self, _: &Expr, len: &Option<Expr>, elm: &Expr) -> Self::Result {
497        self.f.write_char('[')?;
498        if let Some(l) = len {
499            self.visit_expr(l)?;
500        }
501        self.f.write_char(']')?;
502        self.visit_expr(elm)
503    }
504
505    fn visit_expr_struct_type(&mut self, _: &Expr, s: &StructType) -> Self::Result {
506        self.f.write_str("struct{")?;
507        self.fmt_fields(&s.fields, "; ", false)?;
508        self.f.write_char('}')
509    }
510
511    fn visit_expr_func_type(&mut self, _: &Expr, s: &FuncTypeKey) -> Self::Result {
512        self.f.write_str("func")?;
513        self.fmt_sig(&self.ast_objs.ftypes[*s])
514    }
515
516    fn visit_expr_interface_type(&mut self, _: &Expr, s: &InterfaceType) -> Self::Result {
517        self.f.write_str("interface{")?;
518        self.fmt_fields(&s.methods, "; ", true)?;
519        self.f.write_char('}')
520    }
521
522    fn visit_map_type(&mut self, _: &Expr, key: &Expr, val: &Expr, _: &Expr) -> Self::Result {
523        self.f.write_str("map[")?;
524        self.visit_expr(key)?;
525        self.f.write_char(']')?;
526        self.visit_expr(val)
527    }
528
529    fn visit_chan_type(&mut self, _: &Expr, chan: &Expr, dir: &ChanDir) -> Self::Result {
530        let s = match dir {
531            ChanDir::Send => "chan<- ",
532            ChanDir::Recv => "<-chan ",
533            ChanDir::SendRecv => "chan ",
534        };
535        self.f.write_str(s)?;
536        self.visit_expr(chan)
537    }
538
539    fn visit_bad_expr(&mut self, _: &Expr, _: &BadExpr) -> Self::Result {
540        self.f.write_str("(bad expr)")
541    }
542}
543
544impl<'a, 'b> ExprFormater<'a, 'b> {
545    fn visit_expr(&mut self, expr: &Expr) -> fmt::Result {
546        walk_expr(self, expr)
547    }
548
549    fn fmt_sig(&mut self, sig: &FuncType) -> fmt::Result {
550        self.f.write_char('(')?;
551        self.fmt_fields(&sig.params, ", ", false)?;
552        self.f.write_char(')')?;
553        if let Some(re) = &sig.results {
554            self.f.write_char(' ')?;
555            if re.list.len() == 1 {
556                let field = &self.ast_objs.fields[re.list[0]];
557                if field.names.len() == 0 {
558                    self.visit_expr(&field.typ)?;
559                }
560            } else {
561                self.f.write_char('(')?;
562                self.fmt_fields(&re, ", ", false)?;
563                self.f.write_char(')')?;
564            }
565        }
566        Ok(())
567    }
568
569    fn fmt_fields(&mut self, fields: &FieldList, sep: &str, iface: bool) -> fmt::Result {
570        for (i, fkey) in fields.list.iter().enumerate() {
571            if i > 0 {
572                self.f.write_str(sep)?;
573            }
574            let field = &self.ast_objs.fields[*fkey];
575            for (i, name) in field.names.iter().enumerate() {
576                if i > 0 {
577                    self.f.write_str(", ")?;
578                    self.fmt_ident(name)?;
579                }
580            }
581            // types of interface methods consist of signatures only
582            if iface {
583                match &field.typ {
584                    Expr::Func(sig) => {
585                        self.fmt_sig(&self.ast_objs.ftypes[*sig])?;
586                    }
587                    _ => {}
588                }
589            } else {
590                // named fields are separated with a blank from the field type
591                if field.names.len() > 0 {
592                    self.f.write_char(' ')?;
593                }
594                self.visit_expr(&field.typ)?;
595            }
596        }
597        Ok(())
598    }
599
600    fn fmt_ident(&mut self, ident: &IdentKey) -> fmt::Result {
601        self.f.write_str(&self.ast_objs.idents[*ident].name)
602    }
603}