go_types/check/
decl.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;
16use super::super::obj::{type_name_is_alias, EntityType, ObjColor};
17use super::super::objects::{DeclInfoKey, ObjKey, ScopeKey, TypeKey};
18use super::super::operand::Operand;
19use super::super::scope::Scope;
20use super::super::typ::{self};
21use super::check::{Checker, FilesContext, ObjContext};
22use super::stmt::BodyContainer;
23use go_parser::ast::{self, Expr, Node};
24use go_parser::{IdentKey, Map, Pos, Token};
25
26impl<'a, S: SourceRead> Checker<'a, S> {
27    pub fn report_alt_decl(&self, okey: ObjKey) {
28        let lobj = self.lobj(okey);
29        let pos = lobj.pos();
30        if pos > 0 {
31            self.error(pos, format!("\tother declaration of {}", lobj.name()));
32        }
33    }
34
35    pub fn declare(&mut self, skey: ScopeKey, ikey: Option<IdentKey>, okey: ObjKey, pos: Pos) {
36        // spec: "The blank identifier, represented by the underscore
37        // character _, may be used in a declaration like any other
38        // identifier but the declaration does not introduce a new
39        // binding."
40        if self.lobj(okey).name() != "_" {
41            let alt = Scope::insert(skey, okey, self.tc_objs).map(|x| x.clone());
42            if let Some(o) = alt {
43                let lobj = self.lobj(okey);
44                self.error(
45                    lobj.pos(),
46                    format!("{} redeclared in this block", lobj.name()),
47                );
48                self.report_alt_decl(o);
49                return;
50            }
51            self.lobj_mut(okey).set_scope_pos(pos);
52        }
53        if ikey.is_some() {
54            self.result.record_def(ikey.unwrap(), Some(okey));
55        }
56    }
57
58    pub fn obj_decl(&mut self, okey: ObjKey, def: Option<TypeKey>, fctx: &mut FilesContext<S>) {
59        let trace_end_data = if self.trace() {
60            let lobj = self.lobj(okey);
61            let pos = lobj.pos();
62            let obj_display = self.new_dis(&okey);
63            let bmsg = format!(
64                "-- checking {} {} (objPath = {})",
65                lobj.color(),
66                obj_display,
67                self.obj_path_str(&fctx.obj_path)
68            );
69            let end = format!("=> {}", obj_display);
70            self.trace_begin(pos, &bmsg);
71            Some((pos, end))
72        } else {
73            None
74        };
75
76        // Checking the declaration of obj means inferring its type
77        // (and possibly its value, for constants).
78        // An object's type (and thus the object) may be in one of
79        // three states which are expressed by colors:
80        //
81        // - an object whose type is not yet known is painted white (initial color)
82        // - an object whose type is in the process of being inferred is painted grey
83        // - an object whose type is fully inferred is painted black
84        //
85        // During type inference, an object's color changes from white to grey
86        // to black (pre-declared objects are painted black from the start).
87        // A black object (i.e., its type) can only depend on (refer to) other black
88        // ones. White and grey objects may depend on white and black objects.
89        // A dependency on a grey object indicates a cycle which may or may not be
90        // valid.
91        //
92        // When objects turn grey, they are pushed on the object path (a stack);
93        // they are popped again when they turn black. Thus, if a grey object (a
94        // cycle) is encountered, it is on the object path, and all the objects
95        // it depends on are the remaining objects on that path. Color encoding
96        // is such that the color value of a grey object indicates the index of
97        // that object in the object path.
98
99        // During type-checking, white objects may be assigned a type without
100        // traversing through objDecl; e.g., when initializing constants and
101        // variables. Update the colors of those objects here (rather than
102        // everywhere where we set the type) to satisfy the color invariants.
103
104        let lobj = &mut self.tc_objs.lobjs[okey];
105        if lobj.color() == ObjColor::White && lobj.typ().is_some() {
106            lobj.set_color(ObjColor::Black);
107
108            if let Some((p, m)) = &trace_end_data {
109                self.trace_end(*p, m);
110            }
111            return;
112        }
113
114        match lobj.color() {
115            ObjColor::White => {
116                assert!(lobj.typ().is_none());
117                lobj.set_color(ObjColor::Gray(fctx.push(okey)));
118
119                let dkey = self.obj_map[&okey];
120                let d = &self.tc_objs.decls[dkey];
121                // create a new octx for the checker
122                let mut octx = ObjContext::new();
123                octx.scope = Some(*d.file_scope());
124                std::mem::swap(&mut self.octx, &mut octx);
125
126                let lobj = &self.tc_objs.lobjs[okey];
127                match lobj.entity_type() {
128                    EntityType::Const(_) => {
129                        self.octx.decl = Some(dkey);
130                        let cd = d.as_const();
131                        let (typ, init) = (cd.typ.clone(), cd.init.clone());
132                        self.const_decl(okey, &typ, &init, fctx);
133                    }
134                    EntityType::Var(_) => {
135                        self.octx.decl = Some(dkey);
136                        let cd = d.as_var();
137                        let (lhs, typ, init) = (cd.lhs.clone(), cd.typ.clone(), cd.init.clone());
138                        self.var_decl(okey, lhs.as_ref(), &typ, &init, fctx);
139                    }
140                    EntityType::TypeName => {
141                        let cd = d.as_type();
142                        let (typ, alias) = (cd.typ.clone(), cd.alias);
143                        self.type_decl(okey, &typ, def, alias, fctx);
144                    }
145                    EntityType::Func(_) => {
146                        self.func_decl(okey, dkey, fctx);
147                    }
148                    _ => unreachable!(),
149                }
150
151                // handled defered actions:
152                std::mem::swap(&mut self.octx, &mut octx); // restore octx
153                self.lobj_mut(fctx.pop()).set_color(ObjColor::Black);
154            }
155            ObjColor::Black => {
156                assert!(lobj.typ().is_some());
157            }
158            ObjColor::Gray(_) => {
159                // We have a cycle.
160                // In the existing code, this is marked by a non-nil type
161                // for the object except for constants and variables whose
162                // type may be non-nil (known), or nil if it depends on the
163                // not-yet known initialization value.
164                // In the former case, set the type to Typ[Invalid] because
165                // we have an initialization cycle. The cycle error will be
166                // reported later, when determining initialization order.
167                let lobj = &self.tc_objs.lobjs[okey];
168                let invalid_type = self.invalid_type();
169                match lobj.entity_type() {
170                    EntityType::Const(_) | EntityType::Var(_) => {
171                        if self.invalid_type_cycle(okey, fctx) || lobj.typ().is_none() {
172                            self.tc_objs.lobjs[okey].set_type(Some(invalid_type));
173                        }
174                    }
175                    EntityType::TypeName => {
176                        if self.invalid_type_cycle(okey, fctx) {
177                            self.tc_objs.lobjs[okey].set_type(Some(invalid_type));
178                        }
179                    }
180                    EntityType::Func(_) => {
181                        if self.invalid_type_cycle(okey, fctx) {
182                            // Don't set obj.typ to Typ[Invalid] here
183                            // because plenty of code type-asserts that
184                            // functions have a Signature type. Grey
185                            // functions have their type set to an empty
186                            // signature which makes it impossible to
187                            // initialize a variable with the function.
188                        }
189                    }
190                    _ => unreachable!(),
191                }
192                let lobj = self.lobj(okey); // make the borrow checker happy
193                assert!(lobj.typ().is_some());
194            }
195        }
196
197        if let Some((p, m)) = &trace_end_data {
198            self.trace_end(*p, m);
199        }
200    }
201
202    /// invalid_type_cycle returns true if the cycle starting with obj is invalid and
203    /// reports an error.
204    pub fn invalid_type_cycle(&self, okey: ObjKey, fctx: &mut FilesContext<S>) -> bool {
205        // Given the number of constants and variables (nval) in the cycle
206        // and the cycle length (ncycle = number of named objects in the cycle),
207        // we distinguish between cycles involving only constants and variables
208        // (nval = ncycle), cycles involving types (and functions) only
209        // (nval == 0), and mixed cycles (nval != 0 && nval != ncycle).
210        // We ignore functions at the moment (taking them into account correctly
211        // is complicated and it doesn't improve error reporting significantly).
212        //
213        // A cycle must have at least one indirection and one type definition
214        // to be permitted: If there is no indirection, the size of the type
215        // cannot be computed (it's either infinite or 0); if there is no type
216        // definition, we have a sequence of alias type names which will expand
217        // ad infinitum.
218        let lobj = self.lobj(okey);
219        let mut has_indir = false;
220        let mut has_type_def = false;
221        let mut nval = 0;
222        let start = match lobj.color() {
223            ObjColor::Gray(v) => v,
224            _ => unreachable!(),
225        };
226        let cycle = &fctx.obj_path[start..];
227        let mut ncycle = cycle.len(); // including indirections
228        for o in cycle {
229            let oval = self.lobj(*o);
230            match oval.entity_type() {
231                EntityType::Const(_) | EntityType::Var(_) => {
232                    nval += 1;
233                }
234                EntityType::TypeName => {
235                    if o == self.tc_objs.universe().indir() {
236                        ncycle -= 1; // don't count (indirections are not objects)
237                        has_indir = true;
238                    } else {
239                        // Determine if the type name is an alias or not. For
240                        // package-level objects, use the object map which
241                        // provides syntactic information (which doesn't rely
242                        // on the order in which the objects are set up). For
243                        // local objects, we can rely on the order, so use
244                        // the object's predicate.
245                        let alias = if let Some(d) = self.obj_map.get(o) {
246                            // package-level object
247                            self.decl_info(*d).as_type().alias
248                        } else {
249                            // function local object
250                            type_name_is_alias(*o, self.tc_objs)
251                        };
252                        if !alias {
253                            has_type_def = true;
254                        }
255                    }
256                }
257                EntityType::Func(_) => {} // ignored for now
258                _ => unreachable!(),
259            }
260        }
261
262        // A cycle involving only constants and variables is invalid but we
263        // ignore them here because they are reported via the initialization
264        // cycle check.
265        if nval == ncycle {
266            return false;
267        }
268
269        // A cycle involving only types (and possibly functions) must have at
270        // least one indirection and one type definition to be permitted: If
271        // there is no indirection, the size of the type cannot be computed
272        // (it's either infinite or 0); if there is no type definition, we
273        // have a sequence of alias type names which will expand ad infinitum.
274        if nval == 0 && has_indir && has_type_def {
275            return false; // cycle is permitted
276        }
277
278        // report error
279        let pos = lobj.pos();
280        self.error(
281            pos,
282            format!("illegal cycle in declaration of {}", lobj.name()),
283        );
284        for o in cycle {
285            if o == self.tc_objs.universe().indir() {
286                continue;
287            }
288            self.error(pos, format!("\t{} refers to", self.lobj(*o).name()));
289        }
290        self.error(pos, format!("\t{} refers to", lobj.name()));
291
292        true
293    }
294
295    pub fn const_decl(
296        &mut self,
297        okey: ObjKey,
298        typ: &Option<Expr>,
299        init: &Option<Expr>,
300        fctx: &mut FilesContext<S>,
301    ) {
302        let lobj = self.lobj(okey);
303        assert!(lobj.typ().is_none());
304        self.octx.iota = Some(lobj.const_val().clone());
305
306        // provide valid constant value under all circumstances
307        self.lobj_mut(okey).set_const_val(constant::Value::Unknown);
308        // determine type, if any
309        if let Some(e) = typ {
310            let t = self.type_expr(e, fctx);
311            let tval = &self.tc_objs.types[t];
312            if !tval.is_const_type(self.tc_objs) {
313                let invalid_type = self.invalid_type();
314                if tval.underlying().unwrap_or(t) != invalid_type {
315                    self.error(
316                        e.pos(self.ast_objs),
317                        format!("invalid constant type {}", self.new_dis(&t)),
318                    );
319                }
320                self.lobj_mut(okey).set_type(Some(invalid_type));
321
322                // clear iota
323                self.octx.iota = None;
324                return;
325            }
326            self.lobj_mut(okey).set_type(Some(t));
327        }
328
329        let mut x = Operand::new();
330        if let Some(expr) = init {
331            self.expr(&mut x, expr, fctx);
332        }
333        self.init_const(okey, &mut x, fctx);
334
335        // clear iota
336        self.octx.iota = None;
337    }
338
339    pub fn var_decl(
340        &mut self,
341        okey: ObjKey,
342        lhs: Option<&Vec<ObjKey>>,
343        typ: &Option<Expr>,
344        init: &Option<Expr>,
345        fctx: &mut FilesContext<S>,
346    ) {
347        debug_assert!(self.lobj(okey).typ().is_none());
348
349        // determine type, if any
350        if let Some(texpr) = typ {
351            let t = self.type_expr(texpr, fctx);
352            self.lobj_mut(okey).set_type(Some(t));
353            // We cannot spread the type to all lhs variables if there
354            // are more than one since that would mark them as checked
355            // (see Checker::obj_decl) and the assignment of init exprs,
356            // if any, would not be checked.
357        }
358
359        // check initialization
360        if init.is_none() {
361            if typ.is_none() {
362                // error reported before by arityMatch
363                let invalid = self.invalid_type();
364                self.lobj_mut(okey).set_type(Some(invalid));
365            }
366            return;
367        }
368
369        if lhs.is_none() || lhs.as_ref().unwrap().len() == 1 {
370            assert!(lhs.is_none() || lhs.as_ref().unwrap()[0] == okey);
371            let mut x = Operand::new();
372            self.expr(&mut x, init.as_ref().unwrap(), fctx);
373            self.init_var(okey, &mut x, "variable declaration", fctx);
374            return;
375        }
376
377        debug_assert!(lhs.as_ref().unwrap().iter().find(|&&x| x == okey).is_some());
378
379        // We have multiple variables on the lhs and one init expr.
380        // Make sure all variables have been given the same type if
381        // one was specified, otherwise they assume the type of the
382        // init expression values
383        if typ.is_some() {
384            let t = self.lobj(okey).typ();
385            for o in lhs.as_ref().unwrap().iter() {
386                self.lobj_mut(*o).set_type(t);
387            }
388        }
389
390        self.init_vars(
391            lhs.as_ref().unwrap(),
392            &vec![init.clone().unwrap()],
393            None,
394            fctx,
395        );
396    }
397
398    pub fn type_decl(
399        &mut self,
400        okey: ObjKey,
401        typ: &Expr,
402        def: Option<TypeKey>,
403        alias: bool,
404        fctx: &mut FilesContext<S>,
405    ) {
406        debug_assert!(self.lobj(okey).typ().is_none());
407        if alias {
408            let invalid = self.invalid_type();
409            self.lobj_mut(okey).set_type(Some(invalid));
410            let t = self.type_expr(typ, fctx);
411            self.lobj_mut(okey).set_type(Some(t));
412        } else {
413            let named_key = self.tc_objs.new_t_named(Some(okey), None, vec![]);
414            if let Some(d) = def {
415                self.tc_objs.types[d]
416                    .try_as_named_mut()
417                    .unwrap()
418                    .set_underlying(named_key);
419            }
420            // make sure recursive type declarations terminate
421            self.lobj_mut(okey).set_type(Some(named_key));
422
423            // determine underlying type of named
424            self.defined_type(typ, Some(named_key), fctx);
425
426            // The underlying type of named may be itself a named type that is
427            // incomplete:
428            //
429            //	type (
430            //		A B
431            //		B *C
432            //		C A
433            //	)
434            //
435            // The type of C is the (named) type of A which is incomplete,
436            // and which has as its underlying type the named type B.
437            // Determine the (final, unnamed) underlying type by resolving
438            // any forward chain (they always end in an unnamed type).
439            let underlying = typ::deep_underlying_type(named_key, self.tc_objs);
440            self.tc_objs.types[named_key]
441                .try_as_named_mut()
442                .unwrap()
443                .set_underlying(underlying);
444        }
445        self.add_method_decls(okey, fctx);
446    }
447
448    pub fn func_decl(&mut self, okey: ObjKey, dkey: DeclInfoKey, fctx: &mut FilesContext<S>) {
449        debug_assert!(self.lobj(okey).typ().is_none());
450        // func declarations cannot use iota
451        debug_assert!(self.octx.iota.is_none());
452
453        let guard_sig = Some(*self.tc_objs.universe().guard_sig());
454        self.lobj_mut(okey).set_type(guard_sig);
455
456        let d = &self.tc_objs.decls[dkey].as_func();
457        let fdecl_key = d.fdecl;
458        let fdecl = &self.ast_objs.fdecls[fdecl_key];
459        let (recv, typ) = (fdecl.recv.clone(), fdecl.typ);
460        let sig_key = self.func_type(recv.as_ref(), typ, fctx);
461        self.lobj_mut(okey).set_type(Some(sig_key));
462
463        // check for 'init' func
464        let fdecl = &self.ast_objs.fdecls[fdecl_key];
465        let sig = &self.tc_objs.types[sig_key].try_as_signature().unwrap();
466        let lobj = &self.tc_objs.lobjs[okey];
467        if sig.recv().is_none()
468            && lobj.name() == "init"
469            && (sig.params_count(self.tc_objs) > 0 || sig.results_count(self.tc_objs) > 0)
470        {
471            self.error(
472                fdecl.pos(self.ast_objs),
473                "func init must have no arguments and no return values".to_owned(),
474            );
475            // ok to continue
476        }
477
478        if let Some(_) = &fdecl.body {
479            let name = lobj.name().clone();
480            let body = BodyContainer::FuncDecl(fdecl_key);
481            let f = move |checker: &mut Checker<S>, fctx: &mut FilesContext<S>| {
482                checker.func_body(Some(dkey), &name, sig_key, body, None, fctx);
483            };
484            fctx.later(Box::new(f));
485        }
486    }
487
488    pub fn add_method_decls(&mut self, okey: ObjKey, fctx: &mut FilesContext<S>) {
489        // get associated methods
490        // (Checker.collect_objects only collects methods with non-blank names;
491        // Checker.resolve_base_type_name ensures that obj is not an alias name
492        // if it has attached methods.)
493        if !fctx.methods.contains_key(&okey) {
494            return;
495        }
496        let methods = fctx.methods.remove(&okey).unwrap();
497        // don't use TypeName.is_alias (requires fully set up object)
498        debug_assert!(!self.decl_info(self.obj_map[&okey]).as_type().alias);
499
500        let mut mset: Map<String, ObjKey> = Map::new();
501        let type_key = self.lobj(okey).typ().unwrap();
502        // type could be invalid
503        if let Some(named) = self.otype(type_key).try_as_named() {
504            if let Some(struc) = self.otype(named.underlying()).try_as_struct() {
505                for f in struc.fields().iter() {
506                    if self.lobj(*f).name() != "_" {
507                        assert!(self.insert_obj_to_set(&mut mset, *f).is_none());
508                    }
509                }
510            }
511            // if we allow Check.check be called multiple times; additional package files
512            // may add methods to already type-checked types. Add pre-existing methods
513            // so that we can detect redeclarations.
514            for m in named.methods().iter() {
515                let lobj = self.lobj(*m);
516                assert!(lobj.name() != "_");
517                assert!(self.insert_obj_to_set(&mut mset, *m).is_none());
518            }
519        }
520
521        // get valid methods
522        let mut valids: Vec<ObjKey> = methods
523            .into_iter()
524            .filter(|m| {
525                // spec: "For a base type, the non-blank names of methods bound
526                // to it must be unique."
527                let mobj = self.lobj(*m);
528                assert!(mobj.name() != "_");
529                let alt = self.insert_obj_to_set(&mut mset, *m);
530                if let Some(alt) = alt {
531                    let alt_obj = self.lobj(alt);
532                    match alt_obj.entity_type() {
533                        EntityType::Var(_) => self.error(
534                            mobj.pos(),
535                            format!("field and method with the same name {}", mobj.name()),
536                        ),
537                        EntityType::Func(_) => {
538                            self.error(
539                                mobj.pos(),
540                                format!(
541                                    "method {} already declared for {}",
542                                    mobj.name(),
543                                    self.new_dis(m)
544                                ),
545                            );
546                        }
547                        _ => unreachable!(),
548                    }
549                    self.report_alt_decl(alt);
550                    false
551                } else {
552                    true
553                }
554            })
555            .collect();
556        // append valid methods
557        self.tc_objs.types[type_key]
558            .try_as_named_mut()
559            .and_then::<(), _>(|x| {
560                x.methods_mut().append(&mut valids);
561                None
562            });
563    }
564
565    pub fn decl_stmt(&mut self, decl: ast::Decl, fctx: &mut FilesContext<S>) {
566        match decl {
567            ast::Decl::Bad(_) => { /*ignore*/ }
568            ast::Decl::Func(_) => {
569                self.invalid_ast(decl.pos(self.ast_objs), "unknown ast.FuncDecl node")
570            }
571            ast::Decl::Gen(gdecl) => {
572                let mut last_full_const_spec: Option<ast::Spec> = None;
573                let specs = &(*gdecl).specs;
574                for (iota, spec_key) in specs.iter().enumerate() {
575                    let spec = &self.ast_objs.specs[*spec_key].clone();
576                    let spec_pos = spec.pos(self.ast_objs);
577                    let spec_end = spec.end(self.ast_objs);
578                    match spec {
579                        ast::Spec::Value(vs) => {
580                            let vspec = &**vs;
581                            let top = fctx.delayed_count();
582                            let mut current_vspec = None;
583                            let lhs: Vec<ObjKey> = match gdecl.token {
584                                Token::CONST => {
585                                    if vspec.typ.is_some() || vspec.values.len() > 0 {
586                                        last_full_const_spec = Some(spec.clone());
587                                        current_vspec = Some(vspec);
588                                    } else {
589                                        // no ValueSpec with type or init exprs,
590                                        // try get the last one
591                                        if let Some(spec) = &last_full_const_spec {
592                                            match spec {
593                                                ast::Spec::Value(v) => {
594                                                    current_vspec = Some(&*v);
595                                                }
596                                                _ => unreachable!(),
597                                            }
598                                        }
599                                    }
600
601                                    // all lhs
602                                    vspec
603                                        .names
604                                        .clone()
605                                        .into_iter()
606                                        .enumerate()
607                                        .map(|(i, name)| {
608                                            let ident = &self.ast_objs.idents[name];
609                                            let okey = self.tc_objs.new_const(
610                                                ident.pos,
611                                                Some(self.pkg),
612                                                ident.name.clone(),
613                                                None,
614                                                constant::Value::with_i64(iota as i64),
615                                            );
616                                            let init = if current_vspec.is_some()
617                                                && i < current_vspec.unwrap().values.len()
618                                            {
619                                                Some(current_vspec.unwrap().values[i].clone())
620                                            } else {
621                                                None
622                                            };
623                                            let typ =
624                                                current_vspec.map(|x| x.typ.clone()).flatten();
625                                            self.const_decl(okey, &typ, &init, fctx);
626                                            okey
627                                        })
628                                        .collect()
629                                }
630                                Token::VAR => {
631                                    let vars: Vec<ObjKey> = vspec
632                                        .names
633                                        .iter()
634                                        .map(|x| {
635                                            let ident = &self.ast_objs.idents[*x];
636                                            self.tc_objs.new_var(
637                                                ident.pos,
638                                                Some(self.pkg),
639                                                ident.name.clone(),
640                                                None,
641                                            )
642                                        })
643                                        .collect();
644                                    let n_to_1 = vspec.values.len() == 1 && vspec.names.len() > 1;
645                                    if n_to_1 {
646                                        self.var_decl(
647                                            vars[0],
648                                            Some(&vars),
649                                            &vspec.typ.clone(),
650                                            &Some(vspec.values[0].clone()),
651                                            fctx,
652                                        );
653                                    } else {
654                                        for (i, okey) in vars.iter().enumerate() {
655                                            self.var_decl(
656                                                *okey,
657                                                None,
658                                                &vspec.typ.clone(),
659                                                &vspec.values.get(i).map(|x| x.clone()),
660                                                fctx,
661                                            );
662                                        }
663                                    }
664                                    vars
665                                }
666                                _ => {
667                                    self.invalid_ast(
668                                        spec_pos,
669                                        &format!("invalid token {}", gdecl.token),
670                                    );
671                                    vec![]
672                                }
673                            };
674
675                            self.arity_match(vspec, gdecl.token == Token::CONST, current_vspec);
676
677                            // process function literals in init expressions before scope changes
678                            fctx.process_delayed(top, self);
679
680                            // spec: "The scope of a constant or variable identifier declared
681                            // inside a function begins at the end of the ConstSpec or VarSpec
682                            // (ShortVarDecl for short variable declarations) and ends at the
683                            // end of the innermost containing block."
684                            for (i, name) in vspec.names.iter().enumerate() {
685                                self.declare(
686                                    self.octx.scope.unwrap(),
687                                    Some(*name),
688                                    lhs[i],
689                                    spec_end,
690                                );
691                            }
692                        }
693                        ast::Spec::Type(ts) => {
694                            let ident = self.ast_ident(ts.name);
695                            let (pos, name) = (ident.pos, ident.name.clone());
696                            let okey = self.tc_objs.new_type_name(pos, Some(self.pkg), name, None);
697                            // spec: "The scope of a type identifier declared inside a function
698                            // begins at the identifier in the TypeSpec and ends at the end of
699                            // the innermost containing block."
700                            self.declare(self.octx.scope.unwrap(), Some(ts.name), okey, pos);
701                            // mark and unmark type before calling Checker.type_decl;
702                            // its type is still nil (see Checker.obj_decl)
703                            self.lobj_mut(okey)
704                                .set_color(ObjColor::Gray(fctx.push(okey)));
705                            self.type_decl(okey, &ts.typ.clone(), None, ts.assign > 0, fctx);
706                            self.lobj_mut(fctx.pop()).set_color(ObjColor::Black);
707                        }
708                        _ => self.invalid_ast(spec_pos, "const, type, or var declaration expected"),
709                    }
710                }
711            }
712        }
713    }
714}