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}