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, ¶ms, 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), ¶ms, 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}