go_types/check/
conversion.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::objects::TypeKey;
17use super::super::operand::{Operand, OperandMode};
18use super::super::typ::{self, BasicType, Type};
19use super::check::{Checker, FilesContext};
20use std::char;
21
22impl<'a, S: SourceRead> Checker<'a, S> {
23    pub fn conversion(&mut self, x: &mut Operand, t: TypeKey, fctx: &mut FilesContext<S>) {
24        let constv = match &mut x.mode {
25            OperandMode::Constant(v) => Some(v),
26            _ => None,
27        };
28        let const_arg = constv.is_some();
29
30        let o = &self.tc_objs;
31        let xtype = x.typ.unwrap();
32        let ok = if const_arg && typ::is_const_type(t, o) {
33            // constant conversion
34            let v = constv.unwrap();
35            let tval = self.otype(t).underlying_val(o);
36            let basic = tval.try_as_basic().unwrap();
37            let clone = v.clone();
38            if clone.representable(basic, Some(v)) {
39                true
40            } else if typ::is_integer(xtype, o) && tval.is_string(o) {
41                let mut s = "\u{FFFD}".to_owned();
42                let (i, exact) = v.int_as_i64();
43                if exact {
44                    if let Some(c) = char::from_u32(i as u32) {
45                        s = c.to_string()
46                    }
47                }
48                *v = Value::with_str(s);
49                true
50            } else {
51                false
52            }
53        } else if self.convertable_to(x, t, fctx) {
54            // non-constant conversion
55            x.mode = OperandMode::Value;
56            true
57        } else {
58            false
59        };
60
61        if !ok {
62            let xd = self.new_dis(x);
63            let td = self.new_dis(&t);
64            self.error(xd.pos(), format!("cannot convert {} to {}", xd, td));
65            x.mode = OperandMode::Invalid;
66            return;
67        }
68
69        // The conversion argument types are final. For untyped values the
70        // conversion provides the type, per the spec: "A constant may be
71        // given a type explicitly by a constant declaration or conversion,...".
72        if typ::is_untyped(xtype, self.tc_objs) {
73            // - For conversions to interfaces, use the argument's default type.
74            // - For conversions of untyped constants to non-constant types, also
75            //   use the default type (e.g., []byte("foo") should report string
76            //   not []byte as type for the constant "foo").
77            // - Keep untyped nil for untyped nil arguments.
78            // - For integer to string conversions, keep the argument type.
79            let o = &self.tc_objs;
80            let final_t = if typ::is_interface(t, o) || const_arg && !typ::is_const_type(t, o) {
81                typ::untyped_default_type(xtype, o)
82            } else if typ::is_integer(xtype, o) && typ::is_string(t, o) {
83                xtype
84            } else {
85                t
86            };
87            self.update_expr_type(x.expr.as_ref().unwrap(), final_t, true, fctx);
88        }
89
90        x.typ = Some(t);
91    }
92
93    // convertible_to returns if x is convertable to t.
94    // The check parameter may be nil if convertibleTo is invoked through an
95    // exported API call, i.e., when all methods have been type-checked.
96    pub fn convertable_to(&mut self, x: &Operand, t: TypeKey, fctx: &mut FilesContext<S>) -> bool {
97        // "x is assignable to t"
98        if x.assignable_to(t, None, self, fctx) {
99            return true;
100        }
101
102        let o = &self.tc_objs;
103        // "x's type and t have identical underlying types if tags are ignored"
104        let v = x.typ.unwrap();
105        let vu = typ::underlying_type(v, o);
106        let tu = typ::underlying_type(t, o);
107        if typ::identical_ignore_tags_o(Some(vu), Some(tu), o) {
108            return true;
109        }
110
111        let vval = self.otype(v);
112        let tval = self.otype(t);
113        let vuval = self.otype(vu);
114        let tuval = self.otype(tu);
115        // "x's type and t are unnamed pointer types and their pointer base types
116        // have identical underlying types if tags are ignored"
117        if let Some(vdetail) = vval.try_as_pointer() {
118            if let Some(tdetail) = tval.try_as_pointer() {
119                let vu = typ::underlying_type(vdetail.base(), o);
120                let tu = typ::underlying_type(tdetail.base(), o);
121                if typ::identical_ignore_tags_o(Some(vu), Some(tu), o) {
122                    return true;
123                }
124            }
125        }
126
127        // "x's type and t are both integer or floating point types"
128        if (vval.is_integer(o) || vval.is_float(o)) && (tval.is_integer(o) || tval.is_float(o)) {
129            return true;
130        }
131
132        // "x's type and t are both complex types"
133        if vval.is_complex(o) || tval.is_complex(o) {
134            return true;
135        }
136
137        // "x is an integer or a slice of bytes or runes and t is a string type"
138        if (vval.is_integer(o) || self.is_bytes_or_runes(vuval)) && tval.is_string(o) {
139            return true;
140        }
141
142        // "x is a string and T is a slice of bytes or runes"
143        if vval.is_string(o) && self.is_bytes_or_runes(tuval) {
144            return true;
145        }
146
147        // package unsafe:
148        // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
149        if (self.is_pointer(vuval) || self.is_uintptr(vuval)) && self.is_unsafe_pointer(tval) {
150            return true;
151        }
152
153        // "and vice versa"
154        if (self.is_pointer(tuval) || self.is_uintptr(tuval)) && self.is_unsafe_pointer(vval) {
155            return true;
156        }
157
158        false
159    }
160
161    fn is_uintptr(&self, t: &Type) -> bool {
162        if let Some(detail) = t.underlying_val(self.tc_objs).try_as_basic() {
163            return detail.typ() == BasicType::Uintptr;
164        }
165        false
166    }
167
168    fn is_unsafe_pointer(&self, t: &Type) -> bool {
169        if let Some(detail) = t.underlying_val(self.tc_objs).try_as_basic() {
170            return detail.typ() == BasicType::UnsafePointer;
171        }
172        false
173    }
174
175    fn is_pointer(&self, t: &Type) -> bool {
176        t.underlying_val(self.tc_objs).try_as_pointer().is_some()
177    }
178
179    fn is_bytes_or_runes(&self, t: &Type) -> bool {
180        if let Some(detail) = t.try_as_slice() {
181            if let Some(b) = self
182                .otype(detail.elem())
183                .underlying_val(self.tc_objs)
184                .try_as_basic()
185            {
186                return b.typ() == BasicType::Byte || b.typ() == BasicType::Rune;
187            }
188        }
189        false
190    }
191}