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}