go_types/check/
builtin.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::super::constant::Value;
16use super::super::lookup::{self, LookupResult};
17use super::super::objects::{ObjKey, TCObjects, TypeKey};
18use super::super::operand::{Operand, OperandMode};
19use super::super::selection::{Selection, SelectionKind};
20use super::super::typ::{self, untyped_default_type, BasicInfo, BasicType, Type};
21use super::super::universe::Builtin;
22use super::check::{Checker, FilesContext};
23use super::util::{UnpackResult, UnpackedResultLeftovers};
24use go_parser::ast::{CallExpr, Expr, Node};
25use go_parser::Token;
26use std::cmp::Ordering;
27use std::collections::HashSet;
28use std::rc::Rc;
29
30impl<'a, S: SourceRead> Checker<'a, S> {
31    /// builtin type-checks a call to the built-in specified by id and
32    /// reports whether the call is valid, with *x holding the result;
33    /// but x.expr is not set. If the call is invalid, the result is
34    /// false, and *x is undefined.
35    pub fn builtin(
36        &mut self,
37        x: &mut Operand,
38        call: &Rc<CallExpr>,
39        id: Builtin,
40        fctx: &mut FilesContext<S>,
41    ) -> bool {
42        // append is the only built-in that permits the use of ... for the last argument
43        let binfo = self.tc_objs.universe().builtins()[&id];
44        if call.ellipsis.is_some() && id != Builtin::Append {
45            self.invalid_op(
46                call.ellipsis.unwrap(),
47                &format!("invalid use of ... with built-in {}", binfo.name),
48            );
49            self.use_exprs(&call.args, fctx);
50            return false;
51        }
52
53        // For len(x) and cap(x) we need to know if x contains any function calls or
54        // receive operations. Save/restore current setting and set has_call_or_recv to
55        // false for the evaluation of x so that we can check it afterwards.
56        // Note: We must do this _before_ calling unpack because unpack evaluates the
57        //       first argument before we even call arg(x, 0)!
58        let hcor_backup = if id == Builtin::Len || id == Builtin::Cap {
59            Some(self.octx.has_call_or_recv)
60        } else {
61            None
62        };
63        self.octx.has_call_or_recv = false;
64
65        let report_mismatch = |checker: &Checker<S>, ord, rcount| {
66            let msg = match ord {
67                std::cmp::Ordering::Less => "not enough",
68                std::cmp::Ordering::Greater => "too many",
69                std::cmp::Ordering::Equal => return,
70            };
71
72            let expr = Expr::Call(call.clone());
73            let ed = checker.new_dis(&expr);
74            checker.invalid_op(
75                call.r_paren,
76                &format!(
77                    "{} arguments for {} (expected {}, found {})",
78                    msg, &ed, binfo.arg_count, rcount
79                ),
80            );
81        };
82
83        // determine actual arguments
84        let mut nargs = call.args.len();
85        let unpack_result = match id {
86            // arguments require special handling
87            Builtin::Make | Builtin::New | Builtin::Offsetof | Builtin::Trace | Builtin::Ffi => {
88                let ord = nargs.cmp(&binfo.arg_count);
89                let ord = if binfo.variadic && ord == Ordering::Greater {
90                    Ordering::Equal
91                } else {
92                    ord
93                };
94                if ord != std::cmp::Ordering::Equal {
95                    report_mismatch(self, ord, nargs);
96                    return false;
97                }
98                None
99            }
100            _ => {
101                let result = self.unpack(&call.args, binfo.arg_count, false, binfo.variadic, fctx);
102                if result.is_err() {
103                    return false;
104                }
105                let (count, ord) = result.rhs_count();
106                nargs = count;
107                if ord != std::cmp::Ordering::Equal {
108                    report_mismatch(self, ord, count);
109                    return false;
110                }
111                match result {
112                    UnpackResult::Tuple(_, _, _)
113                    | UnpackResult::Mutliple(_, _)
114                    | UnpackResult::Single(_, _) => {
115                        result.get(self, x, 0, fctx);
116                        if x.invalid() {
117                            return false;
118                        }
119                    }
120                    UnpackResult::Nothing(_) => {} // do nothing
121                    UnpackResult::CommaOk(_, _) => unreachable!(),
122                    UnpackResult::Error => unreachable!(),
123                }
124                Some(result)
125            }
126        };
127
128        let invalid_type = self.invalid_type();
129        let om_builtin = &OperandMode::Builtin(id);
130        let record =
131            |c: &mut Checker<S>, res: Option<TypeKey>, args: &[TypeKey], variadic: bool| {
132                let sig = make_sig(c.tc_objs, res, args, variadic);
133                c.result.record_builtin_type(om_builtin, &call.func, sig);
134            };
135        let record_with_sig = |c: &mut Checker<S>, sig: TypeKey| {
136            c.result.record_builtin_type(om_builtin, &call.func, sig);
137        };
138        match id {
139            Builtin::Append => {
140                // append(s S, x ...T) S, where T is the element type of S
141                // spec: "The variadic function append appends zero or more values x to s of type
142                // S, which must be a slice type, and returns the resulting slice, also of type S.
143                // The values x are passed to a parameter of type ...T where T is the element type
144                // of S and the respective parameter passing rules apply."
145                let slice = x.typ.unwrap();
146                let telem = if let Some(detail) = self
147                    .otype(typ::underlying_type(slice, self.tc_objs))
148                    .try_as_slice()
149                {
150                    detail.elem()
151                } else {
152                    let xd = self.new_dis(x);
153                    self.invalid_arg(xd.pos(), &format!("{} is not a slice", xd));
154                    return false;
155                };
156
157                // the first arg is already evaluated
158                let mut alist = vec![x.clone()];
159
160                // spec: "As a special case, append also accepts a first argument assignable
161                // to type []byte with a second argument of string type followed by ... .
162                // This form appends the bytes of the string.
163                if nargs == 2
164                    && call.ellipsis.is_some()
165                    && x.assignable_to(*self.tc_objs.universe().slice_of_bytes(), None, self, fctx)
166                {
167                    unpack_result.as_ref().unwrap().get(self, x, 1, fctx);
168                    if x.invalid() {
169                        return false;
170                    }
171                    let stype = x.typ.unwrap();
172                    if typ::is_string(stype, self.tc_objs) {
173                        record(self, Some(slice), &vec![slice, stype], true);
174
175                        x.mode = OperandMode::Value;
176                        x.typ = Some(slice);
177                        return true;
178                    }
179                    alist.push(x.clone());
180                }
181
182                // check general case by creating custom signature
183                let tslice = self.tc_objs.new_t_slice(telem);
184                let sig = make_sig(self.tc_objs, Some(slice), &vec![slice, tslice], true);
185                let re = UnpackedResultLeftovers {
186                    leftovers: unpack_result.as_ref().unwrap(),
187                    consumed: Some(&alist),
188                };
189                self.arguments(x, call, sig, &re, nargs, fctx);
190                // ok to continue even if check.arguments reported errors
191
192                x.mode = OperandMode::Value;
193                x.typ = Some(slice);
194                record_with_sig(self, sig);
195            }
196            Builtin::Cap | Builtin::Len => {
197                // cap(x)
198                // len(x)
199                let ty = typ::underlying_type(x.typ.unwrap(), self.tc_objs);
200                let ty = implicit_array_deref(ty, self.tc_objs);
201                let mode = match self.otype(ty) {
202                    Type::Basic(detail) => {
203                        if detail.info() == BasicInfo::IsString {
204                            if let OperandMode::Constant(v) = &x.mode {
205                                OperandMode::Constant(Value::with_u64(
206                                    v.str_as_string().len() as u64
207                                ))
208                            } else {
209                                OperandMode::Value
210                            }
211                        } else {
212                            OperandMode::Invalid
213                        }
214                    }
215                    Type::Array(detail) => {
216                        if self.octx.has_call_or_recv {
217                            OperandMode::Value
218                        } else {
219                            // spec: "The expressions len(s) and cap(s) are constants
220                            // if the type of s is an array or pointer to an array and
221                            // the expression s does not contain channel receives or
222                            // function calls; in this case s is not evaluated."
223                            OperandMode::Constant(if let Some(len) = detail.len() {
224                                Value::with_u64(len)
225                            } else {
226                                Value::Unknown
227                            })
228                        }
229                    }
230                    Type::Slice(_) | Type::Chan(_) => OperandMode::Value,
231                    Type::Map(_) => {
232                        if id == Builtin::Len {
233                            OperandMode::Value
234                        } else {
235                            OperandMode::Invalid
236                        }
237                    }
238                    _ => OperandMode::Invalid,
239                };
240
241                self.octx.has_call_or_recv = hcor_backup.unwrap();
242
243                if mode == OperandMode::Invalid && ty != invalid_type {
244                    let dis = self.new_dis(x);
245                    self.invalid_arg(dis.pos(), &format!("{} for {}", dis, binfo.name));
246                    return false;
247                }
248
249                x.mode = mode;
250                x.typ = Some(self.basic_type(BasicType::Int));
251                match &x.mode {
252                    OperandMode::Constant(_) => {}
253                    _ => record(self, x.typ, &vec![ty], false),
254                }
255            }
256            Builtin::Close => {
257                // close(c)
258                let tkey = typ::underlying_type(x.typ.unwrap(), self.tc_objs);
259                if let Some(detail) = self.otype(tkey).try_as_chan() {
260                    if detail.dir() == typ::ChanDir::RecvOnly {
261                        let dis = self.new_dis(x);
262                        self.invalid_arg(
263                            dis.pos(),
264                            &format!("{} must not be a receive-only channel", dis),
265                        );
266                        return false;
267                    }
268                    x.mode = OperandMode::NoValue;
269
270                    record(self, None, &vec![tkey], false);
271                } else {
272                    let dis = self.new_dis(x);
273                    self.invalid_arg(dis.pos(), &format!("{} is not a channel", dis));
274                    return false;
275                }
276            }
277            Builtin::Complex => {
278                // complex(x, y floatT) complexT
279                let mut y = Operand::new();
280                unpack_result.as_ref().unwrap().get(self, &mut y, 1, fctx);
281                if y.invalid() {
282                    return false;
283                }
284
285                // convert or check untyped arguments
286                let x_untyped = typ::is_untyped(x.typ.unwrap(), self.tc_objs);
287                let y_untyped = typ::is_untyped(y.typ.unwrap(), self.tc_objs);
288                match (x_untyped, y_untyped) {
289                    (false, false) => {} // x and y are typed => nothing to do
290                    // only x is untyped => convert to type of y
291                    (true, false) => self.convert_untyped(x, y.typ.unwrap(), fctx),
292                    // only y is untyped => convert to type of x
293                    (false, true) => self.convert_untyped(&mut y, x.typ.unwrap(), fctx),
294                    (true, true) => {
295                        // x and y are untyped =>
296                        // 1) if both are constants, convert them to untyped
297                        //    floating-point numbers if possible,
298                        // 2) if one of them is not constant (possible because
299                        //    it contains a shift that is yet untyped), convert
300                        //    both of them to float64 since they must have the
301                        //    same type to succeed (this will result in an error
302                        //    because shifts of floats are not permitted)
303                        match (&x.mode, &y.mode) {
304                            (OperandMode::Constant(vx), OperandMode::Constant(vy)) => {
305                                let to_float =
306                                    |xtype: &mut Option<TypeKey>, v: &Value, objs: &TCObjects| {
307                                        if typ::is_numeric(xtype.unwrap(), objs)
308                                            && v.imag().sign() == 0
309                                        {
310                                            *xtype = Some(self.basic_type(BasicType::UntypedFloat));
311                                        }
312                                    };
313                                to_float(&mut x.typ, vx, self.tc_objs);
314                                to_float(&mut y.typ, vy, self.tc_objs);
315                            }
316                            _ => {
317                                let tf64 = self.basic_type(BasicType::Float64);
318                                self.convert_untyped(x, tf64, fctx);
319                                self.convert_untyped(&mut y, tf64, fctx);
320                                // x and y should be invalid now, but be conservative
321                                // and check below
322                            }
323                        }
324                    }
325                }
326                if x.invalid() || y.invalid() {
327                    return false;
328                }
329
330                // both argument types must be identical
331                if !typ::identical_o(x.typ, y.typ, self.tc_objs) {
332                    self.invalid_arg(
333                        x.pos(self.ast_objs),
334                        &format!(
335                            "mismatched types {} and {}",
336                            self.new_dis(x.typ.as_ref().unwrap()),
337                            self.new_dis(y.typ.as_ref().unwrap())
338                        ),
339                    );
340                    return false;
341                }
342
343                // the argument types must be of floating-point type
344                if !typ::is_float(x.typ.unwrap(), self.tc_objs) {
345                    self.invalid_arg(
346                        x.pos(self.ast_objs),
347                        &format!(
348                            "arguments have type {}, expected floating-point",
349                            self.new_dis(x.typ.as_ref().unwrap())
350                        ),
351                    );
352                    return false;
353                }
354
355                // if both arguments are constants, the result is a constant
356                match (&mut x.mode, &y.mode) {
357                    (OperandMode::Constant(vx), OperandMode::Constant(vy)) => {
358                        *vx = Value::binary_op(vx, &Token::ADD, &vy.to_float().make_imag());
359                    }
360                    _ => {
361                        x.mode = OperandMode::Value;
362                    }
363                }
364
365                // determine result type
366                let res = match self
367                    .otype(x.typ.unwrap())
368                    .underlying_val(self.tc_objs)
369                    .try_as_basic()
370                    .unwrap()
371                    .typ()
372                {
373                    BasicType::Float32 => BasicType::Complex64,
374                    BasicType::Float64 => BasicType::Complex128,
375                    BasicType::UntypedFloat => BasicType::UntypedComplex,
376                    _ => unreachable!(),
377                };
378                let res_type = self.basic_type(res);
379
380                match &x.mode {
381                    OperandMode::Constant(_) => {}
382                    _ => record(
383                        self,
384                        Some(res_type),
385                        &vec![x.typ.unwrap(), x.typ.unwrap()],
386                        false,
387                    ),
388                }
389
390                x.typ = Some(res_type);
391            }
392            Builtin::Copy => {
393                // copy(x, y []T) int
394                let dst = self
395                    .otype(x.typ.unwrap())
396                    .underlying_val(self.tc_objs)
397                    .try_as_slice()
398                    .map(|x| x.elem());
399
400                let mut y = Operand::new();
401                unpack_result.as_ref().unwrap().get(self, &mut y, 1, fctx);
402                if y.invalid() {
403                    return false;
404                }
405                let ytype = self.otype(y.typ.unwrap());
406                let src = match ytype.underlying_val(self.tc_objs) {
407                    Type::Basic(detail) => {
408                        if detail.info() == BasicInfo::IsString {
409                            Some(*self.tc_objs.universe().byte())
410                        } else {
411                            None
412                        }
413                    }
414                    Type::Slice(detail) => Some(detail.elem()),
415                    _ => None,
416                };
417
418                if dst.is_none() || src.is_none() {
419                    let (xd, yd) = (self.new_dis(x), self.new_dis(&y));
420                    self.invalid_arg(
421                        xd.pos(),
422                        &format!("copy expects slice arguments; found {} and {}", xd, yd),
423                    );
424                    return false;
425                }
426
427                if !typ::identical_o(dst, src, self.tc_objs) {
428                    let (xd, yd) = (self.new_dis(x), self.new_dis(&y));
429                    let (txd, tyd) = (self.new_td_o(&dst), self.new_td_o(&src));
430                    self.invalid_arg(
431                        xd.pos(),
432                        &format!(
433                            "arguments to copy {} and {} have different element types {} and {}",
434                            xd, yd, txd, tyd
435                        ),
436                    );
437                    return false;
438                }
439
440                record(
441                    self,
442                    Some(self.basic_type(BasicType::Int)),
443                    &vec![x.typ.unwrap(), y.typ.unwrap()],
444                    false,
445                );
446
447                x.mode = OperandMode::Value;
448                x.typ = Some(self.basic_type(BasicType::Int));
449            }
450            Builtin::Delete => {
451                // delete(m, k)
452                let mtype = x.typ.unwrap();
453                match self.otype(mtype).underlying_val(self.tc_objs).try_as_map() {
454                    Some(detail) => {
455                        let key = detail.key();
456                        unpack_result.as_ref().unwrap().get(self, x, 1, fctx);
457                        if x.invalid() {
458                            return false;
459                        }
460                        if !x.assignable_to(key, None, self, fctx) {
461                            let xd = self.new_dis(x);
462                            let td = self.new_dis(&key);
463                            self.invalid_arg(
464                                xd.pos(),
465                                &format!("{} is not assignable to {}", xd, td),
466                            );
467                            return false;
468                        }
469                        x.mode = OperandMode::NoValue;
470                        record(self, None, &vec![mtype, key], false);
471                    }
472                    None => {
473                        let xd = self.new_dis(x);
474                        self.invalid_arg(xd.pos(), &format!("{} is not a map", xd));
475                        return false;
476                    }
477                }
478            }
479            Builtin::Imag | Builtin::Real => {
480                // imag(complexT) floatT
481                // real(complexT) floatT
482
483                // convert or check untyped argument
484                if typ::is_untyped(x.typ.unwrap(), self.tc_objs) {
485                    if let OperandMode::Constant(_) = &x.mode {
486                        // an untyped constant number can alway be considered
487                        // as a complex constant
488                        if typ::is_numeric(x.typ.unwrap(), self.tc_objs) {
489                            x.typ = Some(self.basic_type(BasicType::UntypedComplex));
490                        }
491                    } else {
492                        // an untyped non-constant argument may appear if
493                        // it contains a (yet untyped non-constant) shift
494                        // expression: convert it to complex128 which will
495                        // result in an error (shift of complex value)
496                        self.convert_untyped(x, self.basic_type(BasicType::Complex128), fctx);
497                        // x should be invalid now, but be conservative and check
498                        if x.invalid() {
499                            return false;
500                        }
501                    }
502                }
503
504                // the argument must be of complex type
505                if !typ::is_complex(x.typ.unwrap(), self.tc_objs) {
506                    let xd = self.new_dis(x);
507                    self.invalid_arg(
508                        xd.pos(),
509                        &format!("argument has type {}, expected complex type", xd),
510                    );
511                    return false;
512                }
513
514                // if the argument is a constant, the result is a constant
515                if let OperandMode::Constant(v) = &mut x.mode {
516                    *v = match id {
517                        Builtin::Real => v.real(),
518                        Builtin::Imag => v.imag(),
519                        _ => unreachable!(),
520                    };
521                } else {
522                    x.mode = OperandMode::Value;
523                }
524
525                // determine result type
526                let res = match self
527                    .otype(x.typ.unwrap())
528                    .underlying_val(self.tc_objs)
529                    .try_as_basic()
530                    .unwrap()
531                    .typ()
532                {
533                    BasicType::Complex64 => BasicType::Float32,
534                    BasicType::Complex128 => BasicType::Float64,
535                    BasicType::UntypedComplex => BasicType::UntypedFloat,
536                    _ => unreachable!(),
537                };
538                let res_type = self.basic_type(res);
539
540                match &x.mode {
541                    OperandMode::Constant(_) => {}
542                    _ => record(self, Some(res_type), &vec![x.typ.unwrap()], false),
543                }
544
545                x.typ = Some(res_type);
546            }
547            Builtin::Make => {
548                // make(T, n)
549                // make(T, n, m)
550                // (no argument evaluated yet)
551                let arg0 = &call.args[0];
552                let arg0t = self.type_expr(arg0, fctx);
553                if arg0t == invalid_type {
554                    return false;
555                }
556
557                let min = match self.otype(arg0t).underlying_val(self.tc_objs) {
558                    Type::Slice(_) => 2,
559                    Type::Map(_) | Type::Chan(_) => 1,
560                    _ => {
561                        let ed = self.new_dis(arg0);
562                        self.invalid_arg(
563                            ed.pos(),
564                            &format!("cannot make {}; type must be slice, map, or channel", ed),
565                        );
566                        return false;
567                    }
568                };
569                if nargs < min || min + 1 < nargs {
570                    let expr = Expr::Call(call.clone());
571                    let ed = self.new_dis(&expr);
572                    self.error(
573                        ed.pos(),
574                        format!(
575                            "{} expects {} or {} arguments; found {}",
576                            ed,
577                            min,
578                            min + 1,
579                            nargs
580                        ),
581                    );
582                    return false;
583                }
584
585                // constant integer arguments, if any
586                let sizes: Vec<u64> = call.args[1..]
587                    .iter()
588                    .filter_map(|x| {
589                        if let Ok(i) = self.index(x, None, fctx) {
590                            return i;
591                        }
592                        None
593                    })
594                    .collect();
595                if sizes.len() == 2 && sizes[0] > sizes[1] {
596                    let pos = call.args[1].pos(self.ast_objs);
597                    self.invalid_arg(pos, "length and capacity swapped");
598                    // safe to continue
599                }
600                x.mode = OperandMode::Value;
601                x.typ = Some(arg0t);
602
603                let int_type = self.basic_type(BasicType::Int);
604                record(
605                    self,
606                    x.typ,
607                    &[arg0t, int_type, int_type][..1 + sizes.len()],
608                    false,
609                );
610            }
611            Builtin::New => {
612                // new(T)
613                // (no argument evaluated yet)
614                let arg0 = &call.args[0];
615                let argt = self.type_expr(arg0, fctx);
616                if argt == invalid_type {
617                    return false;
618                }
619
620                x.mode = OperandMode::Value;
621                x.typ = Some(self.tc_objs.new_t_pointer(argt));
622                record(self, x.typ, &vec![argt], false);
623            }
624            Builtin::Panic => {
625                // panic(x)
626                // record panic call if inside a function with result parameters
627                // (for use in Checker.isTerminating)
628                if let Some(sig) = self.octx.sig {
629                    if self
630                        .otype(sig)
631                        .try_as_signature()
632                        .unwrap()
633                        .results_count(self.tc_objs)
634                        > 0
635                    {
636                        if self.octx.panics.is_none() {
637                            self.octx.panics = Some(HashSet::new());
638                        }
639                        self.octx.panics.as_mut().unwrap().insert(call.id());
640                    }
641                }
642
643                let iempty = self.tc_objs.new_t_empty_interface();
644                self.assignment(x, Some(iempty), "argument to panic", fctx);
645                if x.invalid() {
646                    return false;
647                }
648
649                x.mode = OperandMode::NoValue;
650                record(self, None, &vec![iempty], false);
651            }
652            Builtin::Print | Builtin::Println => {
653                // print(x, y, ...)
654                // println(x, y, ...)
655                let mut params = vec![];
656                for i in 0..nargs {
657                    if i > 0 {
658                        // first argument already evaluated
659                        unpack_result.as_ref().unwrap().get(self, x, i, fctx);
660                    }
661                    let msg = format!("argument to {}", self.builtin_info(id).name);
662                    self.assignment(x, None, &msg, fctx);
663                    if x.invalid() {
664                        return false;
665                    }
666                    params.push(x.typ.unwrap());
667                }
668
669                x.mode = OperandMode::NoValue;
670                // note: not variadic
671                record(self, None, &params, false);
672            }
673            Builtin::Recover => {
674                // recover() interface{}
675                x.mode = OperandMode::Value;
676                x.typ = Some(self.tc_objs.new_t_empty_interface());
677                record(self, x.typ, &vec![], false);
678            }
679            Builtin::Alignof => {
680                // unsafe.Alignof(x T) uintptr
681                self.assignment(x, None, "argument to unsafe.Alignof", fctx);
682                if x.invalid() {
683                    return false;
684                }
685                // todo: not sure if Alignof will ever be used in goscript
686                let align = Value::with_i64(0); // set Alignof to zero
687                x.mode = OperandMode::Constant(align);
688                x.typ = Some(self.basic_type(BasicType::Uintptr));
689            }
690            Builtin::Offsetof => {
691                // unsafe.Offsetof(x T) uintptr, where x must be a selector
692                // (no argument evaluated yet)
693                let arg0 = &call.args[0];
694                if let Expr::Selector(selx) = Checker::<S>::unparen(arg0) {
695                    self.expr(x, &selx.expr, fctx);
696                    if x.invalid() {
697                        return false;
698                    }
699                    let base = lookup::deref_struct_ptr(x.typ.unwrap(), self.tc_objs);
700                    let sel = &self.ast_ident(selx.sel).name;
701                    let result = lookup::lookup_field_or_method(
702                        base,
703                        false,
704                        Some(self.pkg),
705                        sel,
706                        self.tc_objs,
707                    );
708                    let (obj, indices) = match result {
709                        LookupResult::Ambiguous(_)
710                        | LookupResult::NotFound
711                        | LookupResult::BadMethodReceiver => {
712                            let td = self.new_dis(&base);
713                            let msg = if result == LookupResult::BadMethodReceiver {
714                                format!("field {} is embedded via a pointer in {}", sel, td)
715                            } else {
716                                format!("{} has no single field {}", td, sel)
717                            };
718                            self.invalid_arg(x.pos(self.ast_objs), &msg);
719                            return false;
720                        }
721                        LookupResult::Entry(okey, indices, _) => {
722                            if self.lobj(okey).entity_type().is_func() {
723                                let ed = self.new_dis(arg0);
724                                self.invalid_arg(ed.pos(), &format!("{} is a method value", ed));
725                            }
726                            (okey, indices)
727                        }
728                    };
729
730                    let selection = Selection::new(
731                        SelectionKind::FieldVal,
732                        Some(base),
733                        obj,
734                        indices,
735                        false,
736                        self.tc_objs,
737                    );
738                    self.result.record_selection(selx, selection);
739
740                    // todo: not sure if Offsetof will ever be used in goscript
741                    let offs = Value::with_i64(0); // set Offsetof to zero
742                    x.mode = OperandMode::Constant(offs);
743                    x.typ = Some(self.basic_type(BasicType::Uintptr));
744                } else {
745                    let ed = self.new_dis(arg0);
746                    self.invalid_arg(ed.pos(), &format!("{} is not a selector expression", ed));
747                    self.use_exprs(&vec![arg0.clone()], fctx);
748                    return false;
749                }
750                // result is constant - no need to record signature
751            }
752            Builtin::Sizeof => {
753                // unsafe.Sizeof(x T) uintptr
754                self.assignment(x, None, "argument to unsafe.Sizeof", fctx);
755                if x.invalid() {
756                    return false;
757                }
758                let size = Value::with_u64(typ::size_of(&x.typ.unwrap(), self.tc_objs) as u64);
759                x.mode = OperandMode::Constant(size);
760                x.typ = Some(self.basic_type(BasicType::Uintptr));
761                // result is constant - no need to record signature
762            }
763            Builtin::Assert => {
764                // assert(pred) causes a typechecker error if pred is false.
765                // The result of assert is the value of pred if there is no error.
766                // oxfeefeee: minor change to make it work at runtime
767                let default_err = || {
768                    let xd = self.new_dis(x);
769                    self.invalid_arg(xd.pos(), &format!("{} is not a boolean", xd));
770                    false
771                };
772                match &x.mode {
773                    OperandMode::Constant(v) => {
774                        if !typ::is_boolean(x.typ.unwrap(), self.tc_objs) {
775                            return default_err();
776                        }
777                        match v {
778                            Value::Bool(b) => {
779                                if !*b {
780                                    let expr = Expr::Call(call.clone());
781                                    let ed = self.new_dis(&expr);
782                                    self.error(ed.pos(), format!("{} failed", ed))
783                                    // compile-time assertion failure - safe to continue
784                                }
785                            }
786                            _ => {
787                                let xd = self.new_dis(x);
788                                let msg = format!(
789                                    "internal error: value of {} should be a boolean constant",
790                                    xd
791                                );
792                                self.error(xd.pos(), msg);
793                                return false;
794                            }
795                        }
796                    }
797                    _ => {
798                        let tkey = x.typ.unwrap();
799                        if !typ::is_boolean(tkey, self.tc_objs) {
800                            return default_err();
801                        }
802                        // only record when the argument is not constant
803                        x.mode = OperandMode::NoValue;
804                        record(self, None, &vec![tkey], false);
805                    }
806                }
807            }
808            Builtin::Trace => {
809                // trace(x, y, z, ...) dumps the positions, expressions, and
810                // values of its arguments. The result of trace is the value
811                // of the first argument.
812                // Note: trace is only available in self-test mode.
813                // (no argument evaluated yet)
814                if nargs == 0 {
815                    let expr = Expr::Call(call.clone());
816                    let ed = self.new_dis(&expr);
817                    self.dump(Some(ed.pos()), "trace() without arguments");
818                    x.mode = OperandMode::NoValue;
819                    return true;
820                }
821                let mut x_temp = Operand::new(); // only used for dumping
822                let mut cur_x = x;
823                for arg in call.args.iter() {
824                    self.raw_expr(cur_x, arg, None, fctx); // permit trace for types, e.g.: new(trace(T))
825                    let xd = self.new_dis(cur_x);
826                    self.dump(Some(xd.pos()), &format!("{}", xd));
827                    cur_x = &mut x_temp;
828                }
829                // x contains info of the first argument
830                // trace is only available in test mode - no need to record signature
831            }
832            Builtin::Ffi => {
833                // native(I, string_id, params...)
834                // (no argument evaluated yet)
835                let arg0 = &call.args[0];
836                let arg0t = self.type_expr(arg0, fctx);
837                if arg0t == invalid_type {
838                    return false;
839                }
840
841                if self
842                    .otype(arg0t)
843                    .underlying_val(self.tc_objs)
844                    .try_as_interface()
845                    .is_none()
846                {
847                    let ed = self.new_dis(arg0);
848                    self.invalid_arg(
849                        ed.pos(),
850                        &format!("cannot create native type as {}; must be interface", ed),
851                    );
852                    return false;
853                }
854
855                self.expr(x, &call.args[1], fctx);
856                if x.invalid() {
857                    return false;
858                }
859                let stype = x.typ.unwrap();
860                if !typ::is_string(stype, self.tc_objs) {
861                    let xd = self.new_dis(x);
862                    self.invalid_arg(xd.pos(), &format!("{} is not a string", xd));
863                    return false;
864                }
865
866                let params = vec![arg0t, stype];
867
868                // Disable extra arguments
869                // for i in 2..nargs {
870                //     self.expr(x, &call.args[i], fctx);
871                //     if x.invalid() {
872                //         return false;
873                //     }
874                //     let msg = format!("argument to {}", self.builtin_info(id).name);
875                //     self.assignment(x, None, &msg, fctx);
876                //     if x.invalid() {
877                //         return false;
878                //     }
879                //     params.push(x.typ.unwrap());
880                // }
881
882                x.mode = OperandMode::Value;
883                x.typ = Some(arg0t);
884                // recorded as non-variadic
885                record(self, Some(arg0t), &params, false);
886            }
887        }
888        true
889    }
890}
891
892/// make_sig makes a signature for the given argument and result types.
893/// Default types are used for untyped arguments, and res may be nil.
894fn make_sig(
895    objs: &mut TCObjects,
896    res: Option<TypeKey>,
897    args: &[TypeKey],
898    variadic: bool,
899) -> TypeKey {
900    let list: Vec<ObjKey> = args
901        .iter()
902        .map(|&x| {
903            let ty = Some(untyped_default_type(x, objs));
904            objs.new_var(0, None, "".to_owned(), ty)
905        })
906        .collect();
907    let params = objs.new_t_tuple(list);
908    let rlist = res.map_or(vec![], |x| {
909        vec![objs.new_var(0, None, "".to_owned(), Some(x))]
910    });
911    let results = objs.new_t_tuple(rlist);
912    objs.new_t_signature(None, None, params, results, variadic)
913}
914
915/// implicit_array_deref returns A if typ is of the form *A and A is an array;
916/// otherwise it returns typ.
917fn implicit_array_deref(t: TypeKey, objs: &TCObjects) -> TypeKey {
918    let ty = &objs.types[t];
919    if let Some(detail) = ty.try_as_pointer() {
920        let base = typ::underlying_type(detail.base(), objs);
921        if objs.types[base].try_as_array().is_some() {
922            return base;
923        }
924    }
925    t
926}