go_types/check/
interface.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// This file implements the collection of an interface's methods
13// without relying on partially computed types of methods or interfaces
14// for interface types declared at the package level.
15//
16// Because interfaces must not embed themselves, directly or indirectly,
17// the method set of a valid interface can always be computed independent
18// of any cycles that might exist via method signatures
19//
20// Except for blank method name and interface cycle errors, no errors
21// are reported. Affected methods or embedded interfaces are silently
22// dropped. Subsequent type-checking of the interface will check
23// signatures and embedded interfaces and report errors at that time.
24//
25// Only info_from_type_lit should be called directly from code outside this file
26// to compute an ifaceInfo.
27
28#![allow(dead_code)]
29use crate::SourceRead;
30
31use super::super::obj;
32use super::super::objects::{ObjKey, PackageKey, ScopeKey, TCObjects};
33use super::super::scope::Scope;
34use super::super::typ;
35use super::check::{Checker, FilesContext, RcIfaceInfo};
36use go_parser::ast::{self, Expr, Node};
37use go_parser::{AstObjects, FieldKey, IdentKey, Map, Pos};
38use std::borrow::Cow;
39use std::cell::RefCell;
40use std::collections::HashSet;
41use std::fmt;
42use std::fmt::Write;
43use std::rc::Rc;
44
45/// MethodInfo represents an interface method.
46/// At least one of src or fun must be non-None.
47/// (Methods declared in the current package have a non-None scope
48/// and src, and eventually a non-None fun field; imported and pre-
49/// declared methods have a None scope and src, and only a non-None
50/// fun field.)
51#[derive(Clone, Debug)]
52pub struct MethodInfo {
53    data: Rc<RefCell<MethodInfoData>>,
54}
55
56#[derive(Debug)]
57struct MethodInfoData {
58    // scope of interface method; or None
59    scope: Option<ScopeKey>,
60    // syntax tree representation of interface method; or None
61    src: Option<FieldKey>,
62    // corresponding fully type-checked method type(LangObj::Func); or None
63    func: Option<ObjKey>,
64}
65
66impl MethodInfo {
67    pub fn with_fun(fun: ObjKey) -> MethodInfo {
68        MethodInfo {
69            data: Rc::new(RefCell::new(MethodInfoData {
70                scope: None,
71                src: None,
72                func: Some(fun),
73            })),
74        }
75    }
76
77    pub fn with_scope_src(skey: ScopeKey, fkey: FieldKey) -> MethodInfo {
78        MethodInfo {
79            data: Rc::new(RefCell::new(MethodInfoData {
80                scope: Some(skey),
81                src: Some(fkey),
82                func: None,
83            })),
84        }
85    }
86
87    pub fn scope(&self) -> Option<ScopeKey> {
88        self.data.borrow().scope
89    }
90
91    pub fn src(&self) -> Option<FieldKey> {
92        self.data.borrow().src
93    }
94
95    pub fn func(&self) -> Option<ObjKey> {
96        self.data.borrow().func
97    }
98
99    pub fn set_func(&self, func: ObjKey) {
100        self.data.borrow_mut().func = Some(func);
101    }
102
103    pub fn fmt(
104        &self,
105        f: &mut fmt::Formatter<'_>,
106        tc_objs: &TCObjects,
107        ast_objs: &AstObjects,
108    ) -> fmt::Result {
109        let s = if let Some(okey) = self.func() {
110            tc_objs.lobjs[okey].name()
111        } else {
112            &ast_objs.idents[ast_objs.fields[self.src().unwrap()].names[0]].name
113        };
114        f.write_str(s)
115    }
116
117    pub fn pos(&self, tc_objs: &TCObjects, ast_objs: &AstObjects) -> Pos {
118        if let Some(okey) = self.func() {
119            tc_objs.lobjs[okey].pos()
120        } else {
121            self.src().unwrap().pos(ast_objs)
122        }
123    }
124
125    pub fn id<'a>(
126        &self,
127        pkey: PackageKey,
128        tc_objs: &'a TCObjects,
129        ast_objs: &'a AstObjects,
130    ) -> Cow<'a, str> {
131        if let Some(okey) = self.func() {
132            tc_objs.lobjs[okey].id(tc_objs)
133        } else {
134            let pkg = Some(&tc_objs.pkgs[pkey]);
135            let name = &ast_objs.idents[ast_objs.fields[self.src().unwrap()].names[0]].name;
136            obj::get_id(pkg, name)
137        }
138    }
139}
140
141/// IfaceInfo describes the method set for an interface.
142#[derive(Debug)]
143pub struct IfaceInfo {
144    pub explicits: usize,
145    pub methods: Vec<MethodInfo>,
146}
147
148impl IfaceInfo {
149    pub fn new(explicits: usize, methods: Vec<MethodInfo>) -> IfaceInfo {
150        IfaceInfo {
151            explicits: explicits,
152            methods: methods,
153        }
154    }
155
156    pub fn new_empty() -> IfaceInfo {
157        IfaceInfo::new(0, vec![])
158    }
159
160    pub fn is_empty(&self) -> bool {
161        self.methods.is_empty()
162    }
163
164    pub fn fmt(
165        &self,
166        f: &mut fmt::Formatter<'_>,
167        tc_objs: &TCObjects,
168        ast_objs: &AstObjects,
169    ) -> fmt::Result {
170        f.write_str("interface{")?;
171        for (i, m) in self.methods.iter().enumerate() {
172            if i > 0 {
173                f.write_char(' ')?;
174            }
175            m.fmt(f, tc_objs, ast_objs)?;
176        }
177        f.write_char('}')
178    }
179}
180
181impl<'a, S: SourceRead> Checker<'a, S> {
182    /// info_from_type_lit computes the method set for the given interface iface
183    /// declared in scope.
184    /// If a corresponding type name exists (tname is_some), it is used for
185    /// cycle detection and to cache the method set.
186    /// The result is the method set, or None if there is a cycle via embedded
187    /// interfaces. A is_some result doesn't mean that there were no errors,
188    /// but they were either reported (e.g., blank methods), or will be found
189    /// (again) when computing the interface's type.
190    /// If tname is not None it must be the last element in path.
191    pub fn info_from_type_lit(
192        &self,
193        skey: ScopeKey,
194        iface: &Rc<ast::InterfaceType>,
195        tname: Option<ObjKey>,
196        path: &Vec<ObjKey>,
197        fctx: &mut FilesContext<S>,
198    ) -> Option<RcIfaceInfo> {
199        if self.trace() {
200            let expr = Expr::Interface(iface.clone());
201            let ed = self.new_dis(&expr);
202            let pstr = self.obj_path_str(path);
203            let opstr = self.obj_path_str(&fctx.obj_path);
204            let msg = format!(
205                "-- collect methods for {} (path = {}, objPath = {})",
206                ed, pstr, opstr
207            );
208            self.trace_begin(iface.interface, &msg);
209        }
210
211        let end = |ret: Option<RcIfaceInfo>| {
212            if self.trace() {
213                let expr = Expr::Interface(iface.clone());
214                let ed = self.new_dis(&expr);
215                self.trace_end(iface.interface, &format!("=> {}", ed));
216            }
217            ret
218        };
219
220        // If the interface is named, check if we computed info already.
221        //
222        // This is not simply an optimization; we may run into stack
223        // overflow with recursive interface declarations. Example:
224        //
225        //      type T interface {
226        //              m() interface { T }
227        //      }
228        //
229        // (Since recursive definitions can only be expressed via names,
230        // it is sufficient to track named interfaces here.)
231        //
232        // While at it, use the same mechanism to detect cycles. (We still
233        // have the path-based cycle check because we want to report the
234        // entire cycle if present.)
235        if let Some(okey) = tname {
236            debug_assert!(path[path.len() - 1] == okey);
237            if let Some(info) = fctx.ifaces.get(&okey) {
238                let cloned_info = info.clone();
239                if info.is_none() {
240                    // We have a cycle and use check::has_cycle to report it.
241                    // We are guaranteed that check::has_cycle also finds the
242                    // cycle because when info_from_type_lit is called, any
243                    // tname that's already in FilesContext.ifaces was also
244                    // added to the path. (But the converse is not true:
245                    // A non-None tname is always the last element in path.)
246                    let yes = self.has_cycle(okey, path, true);
247                    assert!(yes);
248                }
249                return end(cloned_info);
250            } else {
251                // computation started but not complete
252                fctx.ifaces.insert(okey, None);
253            }
254        }
255
256        let iinfo = if iface.methods.list.len() == 0 {
257            Rc::new(IfaceInfo::new_empty())
258        } else {
259            let mut mset = Map::new();
260            let mut methods = vec![];
261            let mut embeddeds = vec![];
262            let mut positions = vec![];
263            for fkey in iface.methods.list.iter() {
264                let field = &self.ast_objs.fields[*fkey];
265                if field.names.len() > 0 {
266                    // We have a method with name f.Names[0].
267                    // (The parser ensures that there's only one method
268                    // and we don't care if a constructed AST has more.)
269
270                    // spec: "As with all method sets, in an interface type,
271                    // each method must have a unique non-blank name."
272                    let name = self.ast_ident(field.names[0]);
273                    if name.name == "_" {
274                        self.error_str(name.pos, "invalid method name _");
275                        continue; // ignore
276                    }
277
278                    let m = MethodInfo::with_scope_src(skey, *fkey);
279                    if self.declare_in_method_set(&mut mset, m.clone(), fkey.pos(self.ast_objs)) {
280                        methods.push(m);
281                    }
282                } else {
283                    // We have an embedded interface and f.Type is its
284                    // (possibly qualified) embedded type name. Collect
285                    // it if it's a valid interface.
286                    let e = match &field.typ {
287                        Expr::Ident(i) => self.info_from_type_name(skey, *i, path, fctx),
288                        Expr::Selector(sel) => self.info_from_qualified_type_mame(skey, sel),
289                        _ => {
290                            // The parser makes sure we only see one of the above.
291                            // Constructed ASTs may contain other (invalid) nodes;
292                            // we simply ignore them. The full type-checking pass
293                            // will report those as errors later.
294                            None
295                        }
296                    };
297                    if let Some(emb) = e {
298                        embeddeds.push(emb);
299                        positions.push(fkey.pos(self.ast_objs));
300                    }
301                }
302            }
303            let explicites = methods.len();
304            // collect methods of embedded interfaces
305            for (i, e) in embeddeds.into_iter().enumerate() {
306                let pos = positions[i];
307                for m in e.methods.iter() {
308                    if self.declare_in_method_set(&mut mset, m.clone(), pos) {
309                        methods.push(m.clone());
310                    }
311                }
312            }
313            Rc::new(IfaceInfo::new(explicites, methods))
314        };
315
316        // mark check.interfaces as complete
317        if let Some(okey) = tname {
318            fctx.ifaces.insert(okey, Some(iinfo.clone()));
319        }
320
321        end(Some(iinfo))
322    }
323
324    // info_from_type_name computes the method set for the given type name
325    // which must denote a type whose underlying type is an interface.
326    // The same result qualifications apply as for info_from_type_lit.
327    // info_from_type_name should only be called from info_from_type_lit.
328    fn info_from_type_name(
329        &self,
330        skey: ScopeKey,
331        name: IdentKey,
332        path: &Vec<ObjKey>,
333        fctx: &mut FilesContext<S>,
334    ) -> Option<RcIfaceInfo> {
335        // A single call of info_from_type_name handles a sequence of (possibly
336        // recursive) type declarations connected via unqualified type names.
337        // The general scenario looks like this:
338        //      ...
339        //      type Pn T        // previous declarations leading to T, path = [..., Pn]
340        //      type T interface { T0; ... }  // T0 leads to call of info_from_type_name
341        //
342        //      // info_from_type_name(name = T0, path = [..., Pn, T])
343        //      type T0 T1       // path = [..., Pn, T, T0]
344        //      type T1 T2  <-+  // path = [..., Pn, T, T0, T1]
345        //      type T2 ...   |  // path = [..., Pn, T, T0, T1, T2]
346        //      type Tn T1  --+  // path = [..., Pn, T, T0, T1, T2, Tn] and T1 is in path => cycle
347        // info_from_type_name returns nil when such a cycle is detected. But in
348        // contrast to cycles involving interfaces, we must not report the
349        // error for "type name only" cycles because they will be found again
350        // during type-checking of embedded interfaces. Reporting those cycles
351        // here would lead to double reporting. Cycles involving embedding are
352        // not reported again later because type-checking of interfaces relies
353        // on the IfaceInfos computed here which are cycle-free by design.
354        //
355        // Remember the path length to detect "type name only" cycles.
356        let start = path.len();
357
358        let mut cur_path = path.clone();
359        let mut ident = self.ast_ident(name);
360        loop {
361            let lookup = Scope::lookup_parent(&skey, &ident.name, self.octx.pos, self.tc_objs);
362            if lookup.is_none() {
363                break;
364            }
365            let tname = lookup.as_ref().unwrap().1;
366            let tname_val = self.lobj(tname);
367            if &obj::EntityType::TypeName != tname_val.entity_type() {
368                break;
369            }
370            // We have a type name. It may be predeclared (error type),
371            // imported (dot import), or declared by a type declaration.
372            // It may not be an interface (e.g., predeclared type int).
373            // Resolve it by analyzing each possible case.
374
375            // Abort but don't report an error if we have a "type name only"
376            // cycle (see big function comment).
377            if self.has_cycle(tname, &cur_path[start..], false) {
378                break;
379            }
380            // Abort and report an error if we have a general cycle.
381            if self.has_cycle(tname, &cur_path, true) {
382                break;
383            }
384
385            cur_path.push(tname);
386
387            // If tname is a package-level type declaration, it must be
388            // in the obj_map. Follow the RHS of that declaration if so.
389            // The RHS may be a literal type (likely case), or another
390            // (possibly parenthesized and/or qualified) type name.
391            // (The declaration may be an alias declaration, but it
392            // doesn't matter for the purpose of determining the under-
393            // lying interface.)
394            if let Some(decl_key) = self.obj_map.get(&tname) {
395                let decl = &self.tc_objs.decls[*decl_key].as_type();
396                let ty = Checker::<S>::unparen(&decl.typ);
397                match ty {
398                    Expr::Ident(i) => {
399                        // type tname T
400                        ident = self.ast_ident(*i);
401                    }
402                    Expr::Selector(sel) => {
403                        // type tname p.T
404                        return self.info_from_qualified_type_mame(decl.file_scope, sel);
405                    }
406                    Expr::Interface(iface) => {
407                        // type tname interface{...}
408                        return self.info_from_type_lit(
409                            decl.file_scope,
410                            iface,
411                            Some(tname),
412                            &cur_path,
413                            fctx,
414                        );
415                    }
416                    // type tname X // and X is not an interface type
417                    _ => break,
418                }
419            } else {
420                // If tname is not a package-level declaration, in a well-typed
421                // program it should be a predeclared (error type), imported (dot
422                // import), or function local declaration. Either way, it should
423                // have been fully declared before use, except if there is a direct
424                // cycle, and direct cycles will be caught above. Also, the denoted
425                // type should be an interface (e.g., int is not an interface).
426                if let Some(ty) = tname_val.typ() {
427                    let ty = typ::underlying_type(ty, self.tc_objs);
428                    if let typ::Type::Interface(i) = self.otype(ty) {
429                        return Some(self.info_from_type(i));
430                    }
431                }
432                break;
433            }
434        }
435        None
436    }
437
438    /// like Checker::declare_in_set but for method infos.
439    fn declare_in_method_set(
440        &self,
441        set: &mut Map<String, MethodInfo>,
442        mi: MethodInfo,
443        pos: Pos,
444    ) -> bool {
445        let id = mi.id(self.pkg, self.tc_objs, self.ast_objs);
446        if let Some(alt) = set.insert(id.to_string(), mi) {
447            let mi_ref = set.get(id.as_ref()).unwrap();
448            let md = self.new_dis(mi_ref);
449            self.error(pos, format!("{} redeclared", md));
450            let mpos = mi_ref.pos(self.tc_objs, self.ast_objs);
451            if mpos > 0 {
452                // We use "other" rather than "previous" here because
453                // the first declaration seen may not be textually
454                // earlier in the source.
455                let md = self.new_dis(&alt);
456                self.error(mpos, format!("\tother declaration of {}", md));
457            }
458            false
459        } else {
460            true
461        }
462    }
463
464    /// info_from_qualified_type_mame returns the method set for the given qualified
465    /// type name, or None.
466    fn info_from_qualified_type_mame(
467        &self,
468        skey: ScopeKey,
469        sel: &ast::SelectorExpr,
470    ) -> Option<RcIfaceInfo> {
471        if let Some(name) = sel.expr.try_as_ident() {
472            let ident = self.ast_ident(*name);
473            if let Some((_, obj1)) =
474                Scope::lookup_parent(&skey, &ident.name, self.octx.pos, self.tc_objs)
475            {
476                let obj_val = self.lobj(obj1);
477                if let obj::EntityType::PkgName(imported, _) = obj_val.entity_type() {
478                    debug_assert!(obj_val.pkg() == Some(self.pkg));
479                    let imported_val = &self.tc_objs.pkgs[*imported];
480                    let scope = &self.tc_objs.scopes[*imported_val.scope()];
481                    if let Some(obj2) = scope.lookup(&self.ast_ident(sel.sel).name) {
482                        let obj_val2 = self.lobj(*obj2);
483                        if !obj_val2.exported() {
484                            return None;
485                        }
486                        if let obj::EntityType::TypeName = obj_val2.entity_type() {
487                            let t = typ::underlying_type(obj_val2.typ().unwrap(), self.tc_objs);
488                            if let Some(iface) = self.otype(t).try_as_interface() {
489                                return Some(self.info_from_type(iface));
490                            }
491                        }
492                    }
493                }
494            }
495        }
496        None
497    }
498
499    /// info_from_type computes the method set for the given interface type.
500    fn info_from_type(&self, iface: &typ::InterfaceDetail) -> RcIfaceInfo {
501        let all_methods_ref = iface.all_methods();
502        let all_methods = all_methods_ref.as_ref().unwrap();
503        let all_methods_len = all_methods.len();
504
505        let mut mis = iface
506            .methods()
507            .iter()
508            .map(|x| MethodInfo::with_fun(*x))
509            .collect();
510        if all_methods_len == iface.methods().len() {
511            return Rc::new(IfaceInfo::new(all_methods_len, mis));
512        }
513
514        // there are embedded method, put them after explicite methods
515        let set: HashSet<ObjKey> = iface.methods().clone().into_iter().collect();
516        let mut embedded: Vec<MethodInfo> = all_methods
517            .iter()
518            .filter_map(|x| {
519                if set.contains(x) {
520                    None
521                } else {
522                    Some(MethodInfo::with_fun(*x))
523                }
524            })
525            .collect();
526        mis.append(&mut embedded);
527        Rc::new(IfaceInfo::new(iface.methods().len(), mis))
528    }
529}