trashcan/analysis/
typecheck.rs

1//! trashcan's type judgment and checking tools
2
3use ast::*;
4use super::*;
5use visit::ASTVisitorMut;
6use visit::NameCtxt;
7
8use std::collections::HashSet;
9
10/// Typecheck a dumpster
11pub fn typecheck(dumpster: &mut Dumpster, symtab: &SymbolTable)
12  -> AnalysisResultMany<()> {
13    let mut v = TypecheckVisitor {
14        symtab,
15        errors: Vec::new(),
16    };
17
18    v.visit_dumpster(dumpster);
19
20    if v.errors.is_empty() {
21        Ok(())
22    } else {
23        Err(v.errors)
24    }
25}
26
27pub fn is_constexpr(expr: &Expr, symtab: &SymbolTable, module: &Ident,
28  function: Option<&Ident>) -> AnalysisResult<bool> {
29      match expr.data {
30          ExprKind::Lit(_) => Ok(true),
31
32          ExprKind::Name(ref path) => {
33              match *symtab.symbol_at_path(path,
34                NameCtxt::Value(module, function, Access::Private),
35                &expr.loc)? {
36                  Symbol::Const(_, _) => Ok(true),
37                  _ => Ok(false),
38              }
39          },
40
41          _ => Ok(false),
42      }
43}
44
45pub fn upper_bound_type(lhs: &Type, rhs: &Type) -> Option<Type> {
46    // TODO: revisit this for object types
47    if lhs == rhs {
48        Some(lhs.clone())
49    } else if may_coerce(rhs, lhs) {
50        Some(lhs.clone())
51    } else if may_coerce(lhs, rhs) {
52        Some(rhs.clone())
53    } else {
54        None
55    }
56}
57
58pub fn may_coerce(from: &Type, to: &Type) -> bool {
59    if let Type::Deferred(ref path) = *to {
60        panic!("dumpster fire: attempt to coerce-check deferred type {}", path);
61    }
62
63    match *from {
64        Type::Bool => match *to {
65            Type::Bool
66          | Type::Variant => true,
67            _ => false,
68        },
69
70        Type::UInt8 => match *to {
71            Type::UInt8
72          | Type::Int16
73          | Type::Int32
74          | Type::IntPtr
75          | Type::Float32
76          | Type::Float64
77          | Type::Currency
78          | Type::Variant => true,
79            _ => false,
80        },
81
82        Type::Int16 => match *to {
83            Type::Int16
84          | Type::Int32
85          | Type::IntPtr
86          | Type::Float32
87          | Type::Float64
88          | Type::Currency
89          | Type::Variant => true,
90            _ => false,
91        },
92
93        Type::Int32 => match *to {
94            Type::Int32
95          | Type::IntPtr
96          | Type::Float32
97          | Type::Float64
98          | Type::Currency
99          | Type::Variant => true,
100            _ => false,
101        },
102
103        Type::IntPtr => match *to {
104            Type::IntPtr
105          | Type::Float64
106          | Type::Variant => true,
107            _ => false,
108        },
109
110        // TODO: allow currency to float coercion?
111
112        Type::Float32 => match *to {
113            Type::Float32
114          | Type::Float64
115          | Type::Variant => true,
116            _ => false,
117        },
118
119        Type::Float64 => match *to {
120            Type::Float64
121          | Type::Variant => true,
122            _ => false,
123        },
124
125        Type::String => match *to {
126            Type::String
127          | Type::Variant => true,
128            _ => false,
129        },
130
131        Type::Currency => match *to {
132            Type::Currency
133          | Type::Variant => true,
134            _ => false,
135        },
136
137        // TODO: do we want to allow this? coercion to var may fail at runtime
138        Type::Variant => match *to {
139            // can't assign to statically-dimensioned array
140            Type::Array(_, ArrayBounds::Static(_)) => false,
141            Type::Void => false,
142            _ => true,
143        },
144
145        Type::Obj => match *to {
146            Type::Obj
147          | Type::Variant
148          | Type::Object(_) => true,
149            _ => false,
150        },
151
152        Type::Array(ref basety, ref bounds) => match *to {
153            // can't assign to statically-dimensioned array
154            Type::Array(ref targetty, ArrayBounds::Dynamic(dims)) =>
155                targetty == basety && bounds.dims() == dims,
156
157            Type::Variant => {
158                match **basety {
159                    // can't put structs into Arrays inside Variants :/
160                    Type::Struct(_) => false,
161                    _ => true,
162                }
163            },
164
165            _ => false,
166        },
167
168        Type::VarArgsArray => false,
169
170        // TODO: thread the symbol table through here
171        //   and check actual subtyping info
172        Type::Object(_) => match *to {
173            Type::Obj
174          | Type::Variant
175          | Type::Object(_) => true,
176            _ => false,
177        },
178
179        Type::Struct(ref path) => match *to {
180            Type::Struct(ref path2) => path == path2,
181            _ => false,
182        },
183
184        Type::Enum(ref path) => match *to {
185            Type::Enum(ref path2) => path == path2,
186            Type::Int32 => true,
187            _ => false,
188        },
189
190        Type::Deferred(ref path) => panic!("dumpster fire: \
191            attempt to coerce-check deferred type {}", path),
192
193        Type::Void => false,
194
195        // TODO: handle currency, date, etc
196        _ => panic!("dumpster fire: we haven't figured out the rules for \
197          this type yet."),
198    }
199}
200
201pub fn may_cast(from: &Type, to: &Type) -> bool {
202    if let Type::Deferred(ref path) = *to {
203        panic!("dumpster fire: attempt to coerce-check deferred type {}", path);
204    }
205
206    match *from {
207        // as 1 or 0
208        Type::Bool => to.might_be_numeric() || *to == Type::Bool,
209
210        Type::UInt8
211      | Type::Int16
212      | Type::Int32
213      | Type::IntPtr
214      | Type::Float32
215      | Type::Float64
216      | Type::Currency => to.might_be_numeric(),
217
218        Type::String => to.might_be_string() || to.might_be_numeric(),
219
220        Type::Variant => match *to {
221            // can't assign to statically-dimensioned array
222            Type::Array(_, ArrayBounds::Static(_)) => false,
223            Type::Void => false,
224            _ => true,
225        },
226
227        Type::Obj => match *to {
228            Type::Obj
229          | Type::Variant
230          | Type::Object(_) => true,
231            _ => false,
232        },
233
234        // for now, don't allow cast array-to-array (even if exact type!)
235        Type::Array(ref basety, _) => match *to {
236            Type::Variant => {
237                match **basety {
238                    // can't put structs into Arrays inside Variants :/
239                    Type::Struct(_) => false,
240                    _ => true,
241                }
242            },
243
244            _ => false,
245        },
246
247        // TODO: thread the symbol table through here
248        //   and check actual subtyping info
249        Type::Object(_) => match *to {
250            Type::Obj
251          | Type::Variant
252          | Type::Object(_) => true,
253            _ => false,
254        },
255
256        Type::Struct(_) => false,
257
258        Type::Enum(_) => to.might_be_numeric(),
259
260        Type::Deferred(ref path) => panic!("dumpster fire: \
261            attempt to coerce-check deferred type {}", path),
262
263        Type::Void => false,
264
265        // TODO: handle currency, date, etc
266        _ => panic!("dumpster fire: we haven't figured out the rules for \
267          this type yet."),
268    }
269}
270
271// used to fail a shallow check if subexprs previously failed typecheck
272macro_rules! try_type {
273    ($ex:expr) => {
274        match $ex.ty {
275            Some(ref ty) => ty,
276            None => return,
277        }
278    }
279}
280
281// used to fail a shallow check if e.g. symbol lookup fails
282macro_rules! try_collect {
283    ($e:expr => $errs:expr) => {
284        match $e {
285            Ok(r) => r,
286            Err(e) => {
287                $errs.push(e);
288                return;
289            },
290        }
291    }
292}
293
294struct TypecheckVisitor<'a> {
295    symtab: &'a SymbolTable,
296    errors: Vec<AnalysisError>,
297}
298
299impl<'a> ASTVisitorMut for TypecheckVisitor<'a> {
300    fn visit_fundef(&mut self, def: &mut FunDef, m: &Ident) {
301        self.walk_fundef(def, m);
302
303        // private-in-public check: a pub fn may not have a private return type
304        if def.access == Access::Public {
305            if let Ok(Access::Private) =
306              self.symtab.type_access(&def.ret, m, &def.loc) {
307                self.errors.push(AnalysisError {
308                    kind: AnalysisErrorKind::PrivateInPublic,
309                    regarding: Some(format!("return type {} of pub fn {}::{} \
310                      is private to mod {}", def.ret, m, def.name, m)),
311                    loc: def.loc.clone(),
312                });
313            }
314        }
315    }
316
317    fn visit_funparam(&mut self, p: &mut FunParam, m: &Ident, f: &Ident) {
318        self.walk_funparam(p, m, f);
319
320        match p.mode {
321            ParamMode::ByRef => match p.ty {
322                Type::Array(_, ArrayBounds::Static(_)) =>
323                    self.errors.push(AnalysisError {
324                        kind: AnalysisErrorKind::FnCallError,
325                        regarding: Some(String::from("array types cannot \
326                          specify static bounds when used as parameters.")),
327                        loc: p.loc.clone(),
328                    }),
329                _ => { },
330            },
331
332            ParamMode::ByVal => match p.ty {
333                Type::Array(_, _) => self.errors.push(AnalysisError {
334                    kind: AnalysisErrorKind::FnCallError,
335                    regarding: Some(String::from("array types cannot \
336                      be passed by value")),
337                    loc: p.loc.clone(),
338                }),
339
340                Type::Struct(_) => self.errors.push(AnalysisError {
341                    kind: AnalysisErrorKind::FnCallError,
342                    regarding: Some(String::from("struct types cannot \
343                      be passed by value")),
344                    loc: p.loc.clone(),
345                }),
346
347                _ => { },
348            },
349        };
350
351        // these should already be gensymmed away
352        if &p.name == f {
353            panic!("dumpster fire: \
354              parameter {} has same name as function", p.name);
355        }
356    }
357
358    fn visit_optparam(&mut self, p: &mut (FunParam, Literal),
359      m: &Ident, f: &Ident) {
360        // run defaults to typecheck param, literal
361        self.walk_optparam(p, m, f);
362
363        let (ref p, ref default) = *p;
364
365        if !p.ty.is_scalar() {
366            self.errors.push(AnalysisError {
367                kind: AnalysisErrorKind::FnCallError,
368                regarding: Some(format!("non-scalar type {} cannot be used \
369                  as an optional parameter", p.ty)),
370                loc: p.loc.clone(),
371            });
372        }
373
374        let lit_ty = default.ty();
375
376        match p.mode {
377            ParamMode::ByRef =>
378                if p.ty != lit_ty {
379                    self.errors.push(AnalysisError {
380                        kind: AnalysisErrorKind::TypeError,
381                        regarding: Some(format!(
382                          "optional parameter {} has type &{}; \
383                            default of type {} provided",
384                          p.name, p.ty, lit_ty)),
385                        loc: p.loc.clone(),
386                    });
387                },
388
389            ParamMode::ByVal =>
390                if !may_coerce(&lit_ty, &p.ty) {
391                    self.errors.push(AnalysisError {
392                        kind: AnalysisErrorKind::TypeError,
393                        regarding: Some(format!(
394                          "optional parameter {} has type {}; \
395                            default of type {} provided",
396                          p.name, p.ty, lit_ty)),
397                        loc: p.loc.clone(),
398                    });
399                },
400        }
401    }
402
403    fn visit_static(&mut self, s: &mut Static, m: &Ident) {
404        self.walk_static(s, m);
405
406        // private-in-public check: a pub static may not have a private type
407        if s.access == Access::Public {
408            if let Ok(Access::Private) =
409              self.symtab.type_access(&s.ty, m, &s.loc) {
410                self.errors.push(AnalysisError {
411                    kind: AnalysisErrorKind::PrivateInPublic,
412                    regarding: Some(format!("type {} of pub static {}::{} \
413                      is private to mod {}", s.ty, m, s.name, m)),
414                    loc: s.loc.clone(),
415                });
416            }
417        }
418
419        match s.init {
420            Some(ref lit) => if !may_coerce(&lit.ty(), &s.ty) {
421                self.errors.push(AnalysisError {
422                    kind: AnalysisErrorKind::TypeError,
423                    regarding: Some(format!(
424                      "static {}::{} has type {}; initializer of type {} \
425                        provided", m, s.name, s.ty, lit.ty())),
426                    loc: s.loc.clone(),
427                });
428            },
429
430            None => { },
431        };
432    }
433
434    fn visit_constant(&mut self, c: &mut Constant, m: &Ident) {
435        self.walk_constant(c, m);
436
437        // no private-in-public check: all constable types are public
438
439        if !may_coerce(&c.value.ty(), &c.ty) {
440            self.errors.push(AnalysisError {
441                kind: AnalysisErrorKind::TypeError,
442                regarding: Some(format!(
443                  "const {}::{} has type {}; initializer of type {} \
444                    provided", m, c.name, c.ty, c.value.ty())),
445                loc: c.loc.clone(),
446            });
447        }
448    }
449
450    fn visit_structmem(&mut self, mem: &mut StructMem, m: &Ident, st: &Ident) {
451        self.walk_structmem(mem, m, st);
452
453        // TODO: also recurse into nested types
454        // recursive type check
455        if mem.ty == Type::Struct(Path(Some(m.clone()), st.clone())) {
456            self.errors.push(AnalysisError {
457                kind: AnalysisErrorKind::RecursiveType,
458                regarding: Some(format!("member {} of struct {}::{} makes type \
459                  recursive", mem.name, m, st)),
460                loc: mem.loc.clone(),
461            })
462        }
463
464        // private-in-public check: a pub struct may not have a
465        //   private member type
466        let st_access = match *self.symtab.symbol_at_path(
467          &Path(None, st.clone()), NameCtxt::Type(m, Access::Private),
468          &mem.loc).expect(
469              "dumpster fire: couldn't retrieve struct definition") {
470            Symbol::Struct { ref def, .. } => def.access,
471            _ => panic!("dumpster fire: struct wasn't a struct"),
472        };
473
474        if st_access == Access::Public {
475            if let Ok(Access::Private) =
476              self.symtab.type_access(&mem.ty, m, &mem.loc) {
477                self.errors.push(AnalysisError {
478                    kind: AnalysisErrorKind::PrivateInPublic,
479                    regarding: Some(format!("type {} of member {} \
480                      of struct {} is private to mod {}",
481                      mem.ty, mem.name, st, m)),
482                    loc: mem.loc.clone(),
483                });
484            }
485        }
486    }
487
488    fn visit_stmt(&mut self, stmt: &mut Stmt, m: &Ident, f: &Ident) {
489        self.walk_stmt(stmt, m, f);
490        self.typecheck_stmt_shallow(stmt, m, f);
491    }
492
493    fn visit_expr(&mut self, expr: &mut Expr, module: &Ident,
494      function: Option<&Ident>) {
495        // ok, here's the reasoning.
496        // our goal is to:
497        //   * typecheck every expr
498        //   * typecheck every expr once
499        //   * not repeat errors when we recurse in/out of exprs
500        //   * avoid weird Option<Result<<Type>>> types
501        //
502        // so, to typecheck an expr:
503        //   * walk all subexpressions and typecheck
504        //   * if any subexprs are not typed, they hit errors,
505        //   *   so done (don't double up)
506        //   * apply shallow typecheck to expr, given any
507        //       subexpr types
508        //   * report shallow errors if any
509
510        // first, walk subexprs and typecheck
511        self.walk_expr(expr, module, function);
512
513        // now do a shallow check on our type, iff all subexprs
514        //   were successfully typed
515        expr.ty = match expr.data {
516            ExprKind::Lit(ref lit) => Some(lit.ty()),
517
518            // qualified::name (must denote a module item)
519            ExprKind::Name(ref path) => {
520                match *try_collect!(self.symtab.symbol_at_path(
521                  path,
522                  NameCtxt::Value(module, function, Access::Private),
523                  &expr.loc) => self.errors) {
524                    Symbol::Const(ref ty, _) => Some(ty.clone()),
525                    Symbol::Value(ref ty, _, _) => Some(ty.clone()),
526                    _ => panic!("dumpster fire: non-value slipped past \
527                      lookup typecheck"),
528                }
529            },
530
531            ExprKind::Index(ref expr, ref indices) => {
532                let expr_t = try_type!(expr);
533
534                for index in indices {
535                    let index_t = try_type!(index);
536                    if !may_coerce(index_t, &Type::Int32) {
537                        self.errors.push(AnalysisError {
538                            kind: AnalysisErrorKind::TypeError,
539                            regarding: Some(String::from("index not coercible to i32")),
540                            loc: index.loc.clone(),
541                        });
542                    }
543                }
544
545                match *expr_t {
546                    Type::Array(ref base_t, ref bounds) => {
547                        if bounds.dims() == indices.len() {
548                            Some((**base_t).clone())
549                        } else {
550                            self.errors.push(AnalysisError {
551                                kind: AnalysisErrorKind::TypeError,
552                                regarding: Some(format!("expression indexed with \
553                                  {} dimensions; {} required", indices.len(),
554                                  bounds.dims())),
555                                loc: expr.loc.clone(),
556                            });
557                            None
558                        }
559                    },
560
561                    Type::VarArgsArray => {
562                        if indices.len() == 1 {
563                            Some(Type::Variant)
564                        } else {
565                            self.errors.push(AnalysisError {
566                                kind: AnalysisErrorKind::TypeError,
567                                regarding: Some(format!("variadic arguments \
568                                  array indexed with {} dimensions; 1 required",
569                                  indices.len())),
570                                loc: expr.loc.clone(),
571                            });
572                            None
573                        }
574                    },
575
576                    // not a lot else we can do
577                    Type::Variant => Some(Type::Variant),
578
579                    _ => {
580                        self.errors.push(AnalysisError {
581                            kind: AnalysisErrorKind::TypeError,
582                            regarding: Some(String::from("indexed expression \
583                              not of indexible type")),
584                            loc: expr.loc.clone(),
585                        });
586                        None
587                    }
588                }
589            },
590
591            ExprKind::Call(ref path, ref args, ref optargs) => {
592                let fun = match *try_collect!(self.symtab.symbol_at_path(
593                  path,
594                  NameCtxt::Function(module, Access::Private),
595                  &expr.loc) => self.errors) {
596                    Symbol::Fun { ref def, .. } => def,
597                    _ => panic!("dumpster fire: non-function \
598                      slipped past lookup typecheck"),
599                };
600
601                self.typecheck_fn_call(fun, args, optargs, path, &expr.loc);
602                Some(fun.ret.clone())
603            },
604
605            ExprKind::Member(ref expr, ref mem) => {
606                match *try_type!(expr) {
607                    // for now
608                    Type::Variant | Type::Obj | Type::Object(_) =>
609                        Some(Type::Variant),
610
611                    Type::Struct(ref path) => {
612                        let members = match *try_collect!(
613                          self.symtab.symbol_at_path(
614                              path,
615                              NameCtxt::Type(module, Access::Private),
616                              &expr.loc) => self.errors) {
617                            Symbol::Struct { ref members, .. } => members,
618                            _ => panic!("dumpster fire: non-struct slipped \
619                              past lookup typecheck"),
620                        };
621
622                        match members.get(&mem.0).cloned() {
623                            Some(ty) => Some(ty),
624                            None => {
625                                self.errors.push(AnalysisError {
626                                    kind: AnalysisErrorKind::NotDefined,
627                                    regarding: Some(format!(
628                                      "member {} of struct {}", mem, path)),
629                                    loc: expr.loc.clone(),
630                                });
631                                None
632                            }
633                        }
634                    },
635
636                    Type::Deferred(ref path) => panic!("dumpster fire:
637                      deferred type {} in type checking pass", path),
638
639                    ref ty => {
640                        self.errors.push(AnalysisError {
641                            kind: AnalysisErrorKind::TypeError,
642                            regarding: Some(format!("attempt to access member \
643                              of type {} (not struct, class, obj, or var)", ty)),
644                            loc: expr.loc.clone(),
645                        });
646                        None
647                    }
648                }
649            },
650
651            ExprKind::MemberInvoke(ref expr, ref _mem, ref _args) => {
652                match *try_type!(expr) {
653                    // for now
654                    Type::Variant | Type::Obj | Type::Object(_) =>
655                        Some(Type::Variant),
656
657                    ref ty => {
658                        self.errors.push(AnalysisError {
659                            kind: AnalysisErrorKind::TypeError,
660                            regarding: Some(format!("attempt to invoke member \
661                              function of type {} (not class, obj, or var)", ty)),
662                            loc: expr.loc.clone(),
663                        });
664                        None
665                    }
666                }
667
668                // eventually use typecheck_fn_call
669            },
670
671            ExprKind::UnOpApp(ref expr, ref op) => {
672                let expr_ty = try_type!(expr);
673                match *op {
674                    UnOp::Negate => {
675                        if !expr_ty.might_be_numeric() {
676                            self.errors.push(AnalysisError {
677                                kind: AnalysisErrorKind::TypeError,
678                                regarding: Some(String::from("unary negation of \
679                                  non-numeric expression")),
680                                loc: expr.loc.clone(),
681                            });
682                            None
683                        } else {
684                            Some(expr_ty.clone())
685                        }
686                    },
687
688                    UnOp::BitNot => {
689                        if !expr_ty.might_be_bitwise() {
690                            self.errors.push(AnalysisError {
691                                kind: AnalysisErrorKind::TypeError,
692                                regarding: Some(String::from("bitwise complement \
693                                  of non-bitwise expression")),
694                                loc: expr.loc.clone(),
695                            });
696                            None
697                        } else {
698                            Some(expr_ty.clone())
699                        }
700                    },
701
702                    UnOp::LogNot => {
703                        if !may_coerce(&expr_ty, &Type::Bool) {
704                            self.errors.push(AnalysisError {
705                                kind: AnalysisErrorKind::TypeError,
706                                regarding: Some(String::from("logical complement \
707                                  of non-boolean expression")),
708                                loc: expr.loc.clone(),
709                            });
710                            None
711                        } else {
712                            Some(Type::Bool)
713                        }
714                    },
715
716                    UnOp::AddressOf => {
717                        if !expr.is_lvalue() {
718                            self.errors.push(AnalysisError {
719                                kind: AnalysisErrorKind::InvalidExpr,
720                                regarding: Some(String::from("attempt to take \
721                                  address of non-lvalue")),
722                                loc: expr.loc.clone(),
723                            });
724                            None
725                        } else {
726                            Some(Type::IntPtr)
727                        }
728                    },
729                }
730            },
731
732            ExprKind::BinOpApp(ref lhs, ref rhs, ref op) => {
733                let lhs_ty = try_type!(lhs);
734                let rhs_ty = try_type!(rhs);
735                let ub_ty = match upper_bound_type(lhs_ty, rhs_ty) {
736                    None => {
737                        self.errors.push(AnalysisError {
738                            kind: AnalysisErrorKind::TypeError,
739                            regarding: Some(format!("no common type for {} and {}",
740                              lhs_ty, rhs_ty)),
741                            loc: expr.loc.clone(),
742                        });
743                        return;
744                    },
745
746                    Some(ty) => ty,
747                };
748
749                match *op {
750                    BinOp::Add
751                  | BinOp::Sub
752                  | BinOp::Mul
753                  | BinOp::Div
754                  | BinOp::Mod 
755                  | BinOp::Pow => {
756                        if !lhs_ty.might_be_numeric()
757                          || !rhs_ty.might_be_numeric() {
758                            self.errors.push(AnalysisError {
759                                kind: AnalysisErrorKind::TypeError,
760                                regarding: Some(String::from("non-numeric type in \
761                                  numeric operation")),
762                                loc: expr.loc.clone(),
763                            });
764                            None
765                        } else {
766                            Some(ub_ty)
767                        }
768                    },
769
770                    BinOp::StrCat => {
771                        if !lhs_ty.might_be_string()
772                          || !rhs_ty.might_be_string() {
773                            self.errors.push(AnalysisError {
774                                kind: AnalysisErrorKind::TypeError,
775                                regarding: Some(String::from("non-string type in \
776                                  string operation")),
777                                loc: expr.loc.clone(),
778                            });
779                            None
780                        } else {
781                            Some(ub_ty)
782                        }
783                    },
784
785                    BinOp::Eq | BinOp::NotEq => {
786                        if !lhs_ty.is_scalar() || !rhs_ty.is_scalar() {
787                            self.errors.push(AnalysisError {
788                                kind: AnalysisErrorKind::TypeError,
789                                regarding: Some(String::from("non-scalar type in \
790                                  equality test")),
791                                loc: expr.loc.clone(),
792                            });
793                            None
794                        } else {
795                            Some(Type::Bool)
796                        }
797                    },
798
799                    // TODO: we might codegen this as an addressof comparison,
800                    //   eventually
801                    BinOp::IdentEq | BinOp::NotIdentEq => {
802                        let mut both_maybe_object = true;
803
804                        if let Some(false) = lhs_ty.is_object() {
805                            self.errors.push(AnalysisError {
806                                kind: AnalysisErrorKind::TypeError,
807                                regarding: Some(String::from("non-object type in \
808                                  object identity test")),
809                                loc: lhs.loc.clone(),
810                            });
811                            both_maybe_object = false;
812                        }
813
814                        if let Some(false) = rhs_ty.is_object() {
815                            self.errors.push(AnalysisError {
816                                kind: AnalysisErrorKind::TypeError,
817                                regarding: Some(String::from("non-object type in \
818                                  object identity test")),
819                                loc: rhs.loc.clone(),
820                            });
821                            both_maybe_object = false;
822                        }
823
824                        if both_maybe_object {
825                            Some(Type::Bool)
826                        } else {
827                            None
828                        }
829                    },
830
831                    BinOp::Lt | BinOp::Gt | BinOp::LtEq | BinOp::GtEq => {
832                        if !(lhs_ty.might_be_numeric()
833                             || lhs_ty.might_be_string())
834                          || !(rhs_ty.might_be_numeric()
835                             || rhs_ty.might_be_string()) {
836                            self.errors.push(AnalysisError {
837                                kind: AnalysisErrorKind::TypeError,
838                                regarding: Some(String::from("non-comparable type \
839                                  in comparison operation")),
840                                loc: expr.loc.clone(),
841                            });
842                            None
843                        } else {
844                            Some(Type::Bool)
845                        }
846                    },
847
848                    BinOp::BitAnd | BinOp::BitOr => {
849                        if !lhs_ty.might_be_bitwise()
850                          || !rhs_ty.might_be_bitwise() {
851                            self.errors.push(AnalysisError {
852                                kind: AnalysisErrorKind::TypeError,
853                                regarding: Some(String::from("non-bitwise type in \
854                                  bitwise operation")),
855                                loc: expr.loc.clone(),
856                            });
857                            None
858                        } else {
859                            Some(ub_ty)
860                        }
861                    },
862
863                    BinOp::LogAnd | BinOp::LogOr => {
864                        if !may_coerce(&lhs_ty, &Type::Bool)
865                          || !may_coerce(&rhs_ty, &Type::Bool) {
866                            self.errors.push(AnalysisError {
867                                kind: AnalysisErrorKind::TypeError,
868                                regarding: Some(String::from("non-boolean type in \
869                                  logical operation")),
870                                loc: expr.loc.clone(),
871                            });
872                            None
873                        } else {
874                            Some(ub_ty)
875                        }
876                    },
877                }
878            },
879
880            ExprKind::CondExpr { ref cond, ref if_expr, ref else_expr } => {
881                if !may_coerce(try_type!(cond), &Type::Bool) {
882                    self.errors.push(AnalysisError {
883                        kind: AnalysisErrorKind::TypeError,
884                        regarding: Some(String::from("non-boolean expression as \
885                          conditional-expression condition")),
886                        loc: cond.loc.clone(),
887                    });
888                }
889
890                let if_ty = try_type!(if_expr);
891                let else_ty = try_type!(else_expr);
892                let ub_ty = upper_bound_type(&if_ty, &else_ty);
893                match ub_ty {
894                    None => {
895                        self.errors.push(AnalysisError {
896                            kind: AnalysisErrorKind::TypeError,
897                            regarding: Some(format!("no common type for {} and {}",
898                              if_ty, else_ty)),
899                            loc: expr.loc.clone(),
900                        });
901                        None
902                    },
903
904                    Some(ub_ty) => Some(ub_ty),
905                }
906            },
907
908            ExprKind::ExtentExpr(ref expr, _, dim) => {
909                let expr_ty = try_type!(expr);
910                match *expr_ty {
911                    Type::Array(_, ref bounds) => {
912                        if dim < bounds.dims() {
913                            Some(Type::Int32)
914                        } else {
915                            self.errors.push(AnalysisError {
916                                kind: AnalysisErrorKind::TypeError,
917                                regarding: Some(format!("dimension {} not \
918                                  valid for type {}", dim, expr_ty)),
919                                loc: expr.loc.clone(),
920                            });
921                            None
922                        }
923                    },
924
925                    Type::VarArgsArray => {
926                        if dim == 0 {
927                            Some(Type::Int32)
928                        } else {
929                            self.errors.push(AnalysisError {
930                                kind: AnalysisErrorKind::TypeError,
931                                regarding: Some(format!("dimension {} not \
932                                  valid for variadic arguments array", dim)),
933                                loc: expr.loc.clone(),
934                            });
935                            None
936                        }
937                    },
938
939                    // TODO: maybe allow variants here (checked at runtime)?
940
941                    _ => {
942                        self.errors.push(AnalysisError {
943                            kind: AnalysisErrorKind::TypeError,
944                            regarding: Some(format!("cannot get extents for \
945                              non-array type {}", expr_ty)),
946                            loc: expr.loc.clone(),
947                        });
948                        None
949                    },
950                }
951            },
952
953            ExprKind::Cast(ref expr, ref ty) => {
954                let expr_ty = try_type!(expr);
955                if may_cast(expr_ty, ty) {
956                    Some(ty.clone())
957                } else {
958                    self.errors.push(AnalysisError {
959                        kind: AnalysisErrorKind::TypeError,
960                        regarding: Some(format!("cannot cast expression of type {} \
961                          to type {}", expr_ty, ty)),
962                        loc: expr.loc.clone(),
963                    });
964                    None
965                }
966            },
967
968            // could be anything
969            ExprKind::VbExpr(_) => Some(Type::Variant),
970        };
971    }
972
973    fn visit_allocextent(&mut self, extent: &mut AllocExtent, m: &Ident,
974      f: &Ident, loc: &SrcLoc) {
975        self.walk_allocextent(extent, m, f, loc);
976        self.typecheck_allocextent(extent, loc);
977    }
978}
979
980impl<'a> TypecheckVisitor<'a> {
981
982    fn typecheck_stmt_shallow(&mut self, stmt: &Stmt, module: &Ident,
983      function: &Ident) {
984        match stmt.data {
985            StmtKind::ExprStmt(ref expr) => {
986                match expr.data {
987                    ExprKind::Call(_, _, _)
988                  | ExprKind::MemberInvoke(_, _, _)
989                  | ExprKind::VbExpr(_) => {},
990
991                    _ => self.errors.push(AnalysisError {
992                        kind: AnalysisErrorKind::InvalidStmt,
993                        regarding: None,
994                        loc: expr.loc.clone(),
995                    })
996                };
997            },
998
999            StmtKind::VarDecl(ref decls) => {
1000                for &(ref ident, ref ty, ref init) in decls {
1001                    // these should already be gensymmed away
1002                    if ident == function {
1003                        panic!("dumpster fire: \
1004                          variable {} has same name as function", ident);
1005                    }
1006
1007                    if let Some(ref init) = *init {
1008                        let init_ty = try_type!(init);
1009                        if !may_coerce(&init_ty, &ty) {
1010                            self.errors.push(AnalysisError {
1011                                kind: AnalysisErrorKind::TypeError,
1012                                regarding: Some(format!("initializer (of type {}) \
1013                                  not coercible to declared type {}", init_ty, ty)),
1014                                loc: stmt.loc.clone(),
1015                            });
1016                        }
1017                    }
1018                }
1019            },
1020
1021            StmtKind::Assign(ref lhs, ref op, ref rhs) => { 
1022                if !lhs.is_lvalue() {
1023                    self.errors.push(AnalysisError {
1024                        kind: AnalysisErrorKind::InvalidStmt,
1025                        regarding: Some(String::from("assignment to non-lvalue \
1026                          expression")),
1027                        loc: lhs.loc.clone(),
1028                    });
1029                    return;
1030                }
1031
1032                let lhs_ty = try_type!(lhs);
1033                let rhs_ty = try_type!(rhs);
1034
1035                if try_collect!(is_constexpr(
1036                  lhs, &self.symtab, module, Some(function)) => self.errors) {
1037                    self.errors.push(AnalysisError {
1038                        kind: AnalysisErrorKind::InvalidStmt,
1039                        regarding: Some(String::from("assignment to const \
1040                          expression")),
1041                        loc: lhs.loc.clone(),
1042                    });
1043                    return;
1044                }
1045
1046                match *op {
1047                    AssignOp::Assign => { },
1048
1049                    AssignOp::AddAssign
1050                  | AssignOp::SubAssign
1051                  | AssignOp::MulAssign
1052                  | AssignOp::DivAssign
1053                  | AssignOp::PowAssign
1054                  | AssignOp::SubAssign
1055                    if !lhs_ty.might_be_numeric()
1056                      || !rhs_ty.might_be_numeric() => {
1057                        self.errors.push(AnalysisError {
1058                            kind: AnalysisErrorKind::TypeError,
1059                            regarding: Some(String::from("non-numeric \
1060                              expression in numeric-operation assignment")),
1061                            loc: stmt.loc.clone(),
1062                        });
1063                    },
1064
1065                    AssignOp::StrCatAssign
1066                    if !lhs_ty.might_be_string()
1067                      || !rhs_ty.might_be_string() => {
1068                        self.errors.push(AnalysisError {
1069                            kind: AnalysisErrorKind::TypeError,
1070                            regarding: Some(String::from("non-string \
1071                              expression in string-operation assignment")),
1072                            loc: stmt.loc.clone(),
1073                        });
1074                    },
1075
1076                    AssignOp::BitAndAssign
1077                  | AssignOp::BitOrAssign
1078                    if !lhs_ty.might_be_string()
1079                      || !rhs_ty.might_be_string() => {
1080                        self.errors.push(AnalysisError {
1081                            kind: AnalysisErrorKind::TypeError,
1082                            regarding: Some(String::from("non-bitwise \
1083                              expression in bitwise-operation assignment")),
1084                            loc: stmt.loc.clone(),
1085                        });
1086                    },
1087
1088                    AssignOp::LogAndAssign
1089                  | AssignOp::LogOrAssign
1090                    if !may_coerce(&lhs_ty, &Type::Bool)
1091                      || !may_coerce(&rhs_ty, &Type::Bool) => {
1092                        self.errors.push(AnalysisError {
1093                            kind: AnalysisErrorKind::TypeError,
1094                            regarding: Some(String::from("non-boolean \
1095                              expression in logical-operation assignment")),
1096                            loc: stmt.loc.clone(),
1097                        });
1098                    },
1099
1100                    _ => {},
1101                }
1102
1103                // TODO: do we like this rule in every case?
1104                if !may_coerce(&rhs_ty, &lhs_ty) {
1105                    self.errors.push(AnalysisError {
1106                        kind: AnalysisErrorKind::TypeError,
1107                        regarding: Some(format!("expression not \
1108                          coercible to {}", lhs_ty)),
1109                        loc: stmt.loc.clone(),
1110                    });
1111                }
1112            },
1113
1114            StmtKind::Return(ref expr) => {
1115                let fun_path = Path(Some(module.clone()), function.clone());
1116                // TODO: symbol_at_ident needed here
1117                if let Symbol::Fun { ref def, .. } = *try_collect!(
1118                  self.symtab.symbol_at_path(
1119                      &fun_path,
1120                      NameCtxt::Function(module, Access::Private),
1121                      &stmt.loc) => self.errors) {
1122                    match def.ret {
1123                        Type::Void => if expr.is_some() {
1124                            self.errors.push(AnalysisError {
1125                                kind: AnalysisErrorKind::InvalidStmt,
1126                                regarding: Some(String::from("return with \
1127                                  value from non-void function")),
1128                                loc: stmt.loc.clone(),
1129                            });
1130                        },
1131
1132                        ref ret_ty => match *expr {
1133                            None => self.errors.push(AnalysisError {
1134                                kind: AnalysisErrorKind::InvalidStmt,
1135                                regarding: Some(String::from("return without \
1136                                  expression from non-void function")),
1137                                loc: stmt.loc.clone(),
1138                            }),
1139
1140                            Some(ref expr) => {
1141                                let expr_ty = try_type!(expr);
1142                                if !may_coerce(&expr_ty, ret_ty) {
1143                                    self.errors.push(AnalysisError {
1144                                        kind: AnalysisErrorKind::TypeError,
1145                                        regarding: Some(format!("return value not \
1146                                          coercible to {}", ret_ty)),
1147                                        loc: stmt.loc.clone(),
1148                                    });
1149                                }
1150                            }
1151                        }
1152                    }
1153                } else {
1154                    panic!("dumpster fire: fn definition not \
1155                      found in symbol table.");
1156                }
1157            },
1158
1159            StmtKind::IfStmt { ref cond, ref elsifs, .. } => {
1160                let cond_ty = try_type!(cond);
1161                if !may_coerce(&cond_ty, &Type::Bool) {
1162                    self.errors.push(AnalysisError {
1163                        kind: AnalysisErrorKind::TypeError,
1164                        regarding: Some(String::from(
1165                          "condition not coercible to bool")),
1166                        loc: cond.loc.clone(),
1167                    });
1168                }
1169
1170                for &(ref cond, _) in elsifs {
1171                    let cond_ty = try_type!(cond);
1172                    if !may_coerce(&cond_ty, &Type::Bool) {
1173                        self.errors.push(AnalysisError {
1174                            kind: AnalysisErrorKind::TypeError,
1175                            regarding: Some(String::from(
1176                              "condition not coercible to bool")),
1177                            loc: cond.loc.clone(),
1178                        });
1179                    }
1180                }
1181            },
1182
1183            StmtKind::WhileLoop { ref cond, .. } => {
1184                let cond_ty = try_type!(cond);
1185                if !may_coerce(cond_ty, &Type::Bool) {
1186                    self.errors.push(AnalysisError {
1187                        kind: AnalysisErrorKind::TypeError,
1188                        regarding: Some(String::from(
1189                          "condition not coercible to bool")),
1190                        loc: cond.loc.clone(),
1191                    });
1192                }
1193            },
1194
1195            StmtKind::ForLoop { var: (ref var, ref ty, ref mode), ref spec, .. } => {
1196                match *spec {
1197                    ForSpec::Range(ref from, ref to, ref step) => {
1198                        if *mode == ParamMode::ByRef {
1199                            self.errors.push(AnalysisError {
1200                                kind: AnalysisErrorKind::InvalidStmt,
1201                                regarding: Some(format!("reference variable \
1202                                  {} cannot be used with for loop over range",
1203                                  var)),
1204                                loc: stmt.loc.clone(),
1205                            });
1206                        }
1207
1208                        if !ty.might_be_numeric() {
1209                            self.errors.push(AnalysisError {
1210                                kind: AnalysisErrorKind::TypeError,
1211                                regarding: Some(String::from("non-numeric control \
1212                                  variable in range-based for loop.")),
1213                                loc: stmt.loc.clone(),
1214                            });
1215                        }
1216
1217                        let to_ty = try_type!(to);
1218                        let from_ty = try_type!(from);
1219
1220                        if !from_ty.might_be_numeric()
1221                          || !to_ty.might_be_numeric() {
1222                            self.errors.push(AnalysisError {
1223                                kind: AnalysisErrorKind::TypeError,
1224                                regarding: Some(String::from("non-numeric range \
1225                                  expression in range-based for loop.")),
1226                                loc: stmt.loc.clone(),
1227                            });
1228                        }
1229
1230                        if !may_coerce(&from_ty, &to_ty) {
1231                            self.errors.push(AnalysisError {
1232                                kind: AnalysisErrorKind::TypeError,
1233                                regarding: Some(String::from("bounds have \
1234                                  incompatible types in range-based for loop.")),
1235                                loc: stmt.loc.clone(),
1236                            });
1237                        }
1238
1239                        if let Some(ref step) = *step {
1240                            let step_ty = try_type!(step);
1241                            if !step_ty.might_be_numeric() {
1242                                self.errors.push(AnalysisError {
1243                                    kind: AnalysisErrorKind::TypeError,
1244                                    regarding: Some(String::from("non-numeric step \
1245                                      expression in range-based for loop.")),
1246                                    loc: stmt.loc.clone(),
1247                                });
1248                            }
1249
1250                            if !may_coerce(&step_ty, &to_ty) {
1251                                self.errors.push(AnalysisError {
1252                                    kind: AnalysisErrorKind::TypeError,
1253                                    regarding: Some(String::from("step has \
1254                                      incompatible type in range-based for loop.")),
1255                                    loc: stmt.loc.clone(),
1256                                });
1257                            }
1258                        }
1259                    },
1260
1261                    ForSpec::Each(ref expr) => {
1262                        match *try_type!(expr) {
1263                            Type::Array(ref base, _) => {
1264                                match *mode {
1265                                    ParamMode::ByVal => {
1266                                        if !may_coerce(base, ty) {
1267                                            self.errors.push(AnalysisError {
1268                                                kind: AnalysisErrorKind::TypeError,
1269                                                regarding: Some(format!(
1270                                                  "element type {} not coercible \
1271                                                    to variable type {}",
1272                                                  base, ty)),
1273                                                loc: stmt.loc.clone(),
1274                                            });
1275                                        }
1276                                    },
1277
1278                                    ParamMode::ByRef => {
1279                                        if **base != *ty {
1280                                            self.errors.push(AnalysisError {
1281                                                kind: AnalysisErrorKind::TypeError,
1282                                                regarding: Some(format!(
1283                                                  "loop variable {} has type &{}; \
1284                                                  element type {} provided",
1285                                                  var, ty, base)),
1286                                                loc: stmt.loc.clone(),
1287                                            });
1288                                        }
1289                                    },
1290                                }
1291                            },
1292
1293                            Type::VarArgsArray => {
1294                                match *mode {
1295                                    ParamMode::ByVal => {
1296                                        if !may_coerce(&Type::Variant, ty) {
1297                                            self.errors.push(AnalysisError {
1298                                                kind: AnalysisErrorKind::TypeError,
1299                                                regarding: Some(format!(
1300                                                  "element type var not coercible \
1301                                                    to variable type {}", ty)),
1302                                                loc: stmt.loc.clone(),
1303                                            });
1304                                        }
1305                                    },
1306
1307                                    ParamMode::ByRef => {
1308                                        if *ty != Type::Variant {
1309                                            self.errors.push(AnalysisError {
1310                                                kind: AnalysisErrorKind::TypeError,
1311                                                regarding: Some(format!(
1312                                                  "loop variable {} has type &{}; \
1313                                                  element type var provided",
1314                                                  var, ty)),
1315                                                loc: stmt.loc.clone(),
1316                                            });
1317                                        }
1318                                    },
1319                                }
1320                            },
1321
1322                            Type::Variant
1323                          | Type::Obj
1324                          | Type::Object(_) => {
1325                                if *mode == ParamMode::ByRef {
1326                                    self.errors.push(AnalysisError {
1327                                        kind: AnalysisErrorKind::TypeError,
1328                                        regarding: Some(format!("reference \
1329                                          variable {} cannot be used to loop over \
1330                                          expression of object or var type (for now)",
1331                                          var)),
1332                                        loc: stmt.loc.clone(),
1333                                    });
1334                                }
1335                            },
1336
1337                            ref expr_ty => self.errors.push(AnalysisError {
1338                                kind: AnalysisErrorKind::TypeError,
1339                                regarding: Some(format!("for-each loop \
1340                                  iteration expression must have array or object \
1341                                  type; found type {}", expr_ty)),
1342                                loc: stmt.loc.clone(),
1343                            })
1344
1345                        }
1346                    },
1347
1348                };
1349            },
1350
1351            StmtKind::ForAlong { ref vars, ref along, .. } => {
1352                let dims = match *try_type!(along) {
1353                    Type::Array(_, ref bounds) => bounds.dims(),
1354
1355                    Type::VarArgsArray => 1,
1356
1357                    // TODO: maybe allow variants (checked at runtime)?
1358
1359                    ref along_ty => {
1360                        self.errors.push(AnalysisError {
1361                            kind: AnalysisErrorKind::TypeError,
1362                            regarding: Some(format!("for-along loop over \
1363                              non-array expression of type {}", along_ty)),
1364                            loc: stmt.loc.clone(),
1365                        });
1366                        return;
1367                    }
1368                };
1369
1370                if vars.len() > dims {
1371                    self.errors.push(AnalysisError {
1372                        kind: AnalysisErrorKind::TypeError,
1373                        regarding: Some(format!("for-along loop iteration \
1374                          variable count ({}) exceeds iterated array \
1375                          dimension ({})", vars.len(), dims)),
1376                        loc: stmt.loc.clone(),
1377                    });
1378                };
1379            },
1380
1381            StmtKind::Alloc(ref expr, ref extents) => {
1382                match *try_type!(expr) {
1383                    Type::Array(_, ArrayBounds::Dynamic(dims)) => {
1384                        let extent_dims = extents.len();
1385
1386                        if extent_dims == 1 {
1387                            match extents[0] {
1388                                AllocExtent::Along(ref other) => {
1389                                    match *try_type!(other) {
1390                                        Type::Array(_, ref bounds) => {
1391                                            if bounds.dims() < dims {
1392                                                self.errors.push(AnalysisError {
1393                                                    kind: AnalysisErrorKind::TypeError,
1394                                                    regarding: Some(format!(
1395                                                      "along expression does not \
1396                                                      have enough dimensions ({}) \
1397                                                      for alloc expression extents \
1398                                                      ({})", bounds.dims(), dims)),
1399                                                    loc: stmt.loc.clone(),
1400                                                });
1401                                            }
1402
1403                                            // otherwise, we have a single
1404                                            // big-enough array
1405                                        },
1406
1407                                        // handled by allocextent visitor
1408                                        _ => { },
1409                                    }
1410                                },
1411
1412                                AllocExtent::Range(_, _) => { },
1413                            }
1414                        } else {
1415                            for (dim, extent) in extents.iter().enumerate() {
1416                                match *extent {
1417                                    AllocExtent::Along(ref other) => {
1418                                        match *try_type!(other) {
1419                                            Type::Array(_, ref bounds) => {
1420                                                if bounds.dims() <= dim {
1421                                                    self.errors.push(AnalysisError {
1422                                                        kind: AnalysisErrorKind::TypeError,
1423                                                        regarding: Some(format!(
1424                                                          "along expression does not \
1425                                                          have enough dimensions ({}) \
1426                                                          for dimension {} of alloc \
1427                                                          expression extents",
1428                                                          bounds.dims(), dim)),
1429                                                        loc: stmt.loc.clone(),
1430                                                    });
1431                                                }
1432                                            },
1433
1434                                            // handled by allocextent visitor
1435                                            _ => { },
1436                                        }
1437                                    },
1438
1439                                    AllocExtent::Range(_, _) => { },
1440                                }
1441                            }
1442                        }
1443
1444                        if dims != extent_dims {
1445                            self.errors.push(AnalysisError {
1446                                kind: AnalysisErrorKind::TypeError,
1447                                regarding: Some(format!("extents dimensions ({}) \
1448                                  do not match array dimensions ({}) in alloc",
1449                                  extent_dims, dims)),
1450                                loc: stmt.loc.clone(),
1451                            });
1452                        }
1453                    },
1454
1455                    Type::Array(_, ArrayBounds::Static(_)) => {
1456                        self.errors.push(AnalysisError {
1457                            kind: AnalysisErrorKind::InvalidStmt,
1458                            regarding: Some(String::from("attempt to allocate \
1459                              statically-dimensioned array")),
1460                            loc: stmt.loc.clone(),
1461                        });
1462                    },
1463
1464                    Type::VarArgsArray => {
1465                        self.errors.push(AnalysisError {
1466                            kind: AnalysisErrorKind::InvalidStmt,
1467                            regarding: Some(String::from("attempt to allocate \
1468                              variadic arguments array")),
1469                            loc: stmt.loc.clone(),
1470                        });
1471                    },
1472
1473                    ref ty => {
1474                        self.errors.push(AnalysisError {
1475                            kind: AnalysisErrorKind::InvalidStmt,
1476                            regarding: Some(format!("attempt to allocate \
1477                              expression of non-array type {}", ty)),
1478                            loc: stmt.loc.clone(),
1479                        });
1480                    },
1481                }
1482            },
1483
1484            StmtKind::ReAlloc(ref expr, preserved, ref extent) => {
1485                match *try_type!(expr) {
1486                    Type::Array(_, ArrayBounds::Dynamic(dims)) => {
1487                        match *extent {
1488                            AllocExtent::Along(ref expr) => {
1489                                let expr_ty = try_type!(expr);
1490                                match *expr_ty {
1491                                    Type::Array(_, ref bounds) => {
1492                                        let dims = bounds.dims();
1493                                        if dims < preserved + 1 {
1494                                            self.errors.push(AnalysisError {
1495                                                kind: AnalysisErrorKind::TypeError,
1496                                                regarding: Some(format!("along \
1497                                                  expression of type {} does not \
1498                                                  have enough dimensions in \
1499                                                  realloc", expr_ty)),
1500                                                loc: stmt.loc.clone(),
1501                                            });
1502                                        }
1503                                    },
1504
1505                                    // checked in allocextent visitor
1506                                    _ => { },
1507                                }
1508                            },
1509
1510                            // checked in allocextent visitor
1511                            AllocExtent::Range(_, _) => { },
1512                        };
1513
1514                        if dims != preserved + 1 {
1515                            self.errors.push(AnalysisError {
1516                                kind: AnalysisErrorKind::TypeError,
1517                                regarding: Some(format!("extents dimensions ({}) \
1518                                  do not match array dimensions ({}) in realloc",
1519                                  preserved + 1, dims)),
1520                                loc: stmt.loc.clone(),
1521                            });
1522                        }
1523                    },
1524
1525                    Type::Array(_, ArrayBounds::Static(_)) => {
1526                        self.errors.push(AnalysisError {
1527                            kind: AnalysisErrorKind::InvalidStmt,
1528                            regarding: Some(String::from("attempt to \
1529                              reallocate statically-dimensioned array")),
1530                            loc: stmt.loc.clone(),
1531                        });
1532                    },
1533
1534                    Type::VarArgsArray => {
1535                        self.errors.push(AnalysisError {
1536                            kind: AnalysisErrorKind::InvalidStmt,
1537                            regarding: Some(String::from("attempt to reallocate \
1538                              variadic arguments array")),
1539                            loc: stmt.loc.clone(),
1540                        });
1541                    },
1542
1543                    ref ty => {
1544                        self.errors.push(AnalysisError {
1545                            kind: AnalysisErrorKind::InvalidStmt,
1546                            regarding: Some(format!("attempt to reallocate \
1547                              expression of non-array type {}", ty)),
1548                            loc: stmt.loc.clone(),
1549                        });
1550                    },
1551                }
1552            },
1553
1554            StmtKind::DeAlloc(ref expr) => {
1555                match *try_type!(expr) {
1556                    Type::Array(_, ArrayBounds::Dynamic(_)) => { },
1557
1558                    Type::Array(_, ArrayBounds::Static(_)) => {
1559                        self.errors.push(AnalysisError {
1560                            kind: AnalysisErrorKind::InvalidStmt,
1561                            regarding: Some(String::from("attempt to \
1562                              deallocate statically-dimensioned array")),
1563                            loc: stmt.loc.clone(),
1564                        });
1565                    },
1566
1567                    Type::VarArgsArray => {
1568                        self.errors.push(AnalysisError {
1569                            kind: AnalysisErrorKind::InvalidStmt,
1570                            regarding: Some(String::from("attempt to deallocate \
1571                              variadic arguments array")),
1572                            loc: stmt.loc.clone(),
1573                        });
1574                    },
1575
1576                    ref ty => {
1577                        self.errors.push(AnalysisError {
1578                            kind: AnalysisErrorKind::InvalidStmt,
1579                            regarding: Some(format!("attempt to deallocate \
1580                              expression of non-array type {}", ty)),
1581                            loc: stmt.loc.clone(),
1582                        });
1583                    },
1584                }
1585            },
1586
1587            StmtKind::Print(ref exprs) => {
1588                for expr in exprs {
1589                    match *try_type!(expr) {
1590                        Type::Void => self.errors.push(AnalysisError {
1591                            kind: AnalysisErrorKind::TypeError,
1592                            regarding: Some(String::from("void function \
1593                              invocation in print statement")),
1594                            loc: stmt.loc.clone(),
1595                        }),
1596
1597                        _ => { },
1598                    }
1599                }
1600            },
1601        }
1602    }
1603
1604    fn typecheck_allocextent(&mut self, extent: &AllocExtent, loc: &SrcLoc) {
1605        // TODO: maybe allow variants (checked at runtime)?
1606
1607        match *extent {
1608            AllocExtent::Along(ref expr) => {
1609                match *try_type!(expr) {
1610                    Type::Array(_, _) => { },
1611
1612                    Type::VarArgsArray => { },
1613
1614                    ref ty => self.errors.push(AnalysisError {
1615                        kind: AnalysisErrorKind::TypeError,
1616                        regarding: Some(format!("along expression of non-array \
1617                          type {}", ty)),
1618                        loc: loc.clone(),
1619                    }),
1620                }
1621            },
1622
1623            AllocExtent::Range(ref lb, ref ub) => {
1624                if let Some(ref lb) = *lb {
1625                    let extent_ty = try_type!(lb);
1626                    if !may_coerce(&extent_ty, &Type::Int32) {
1627                        self.errors.push(AnalysisError {
1628                            kind: AnalysisErrorKind::TypeError,
1629                            regarding: Some(String::from(
1630                              "array extent bound not \
1631                                coercible to i32")),
1632                            loc: loc.clone(),
1633                        });
1634                    }
1635                }
1636
1637                let extent_ty = try_type!(ub);
1638                if !may_coerce(&extent_ty, &Type::Int32) {
1639                    self.errors.push(AnalysisError {
1640                        kind: AnalysisErrorKind::TypeError,
1641                        regarding: Some(String::from(
1642                          "array extent bound not coercible \
1643                          to i32")),
1644                        loc: loc.clone(),
1645                    });
1646                }
1647            },
1648        }
1649    }
1650
1651    fn typecheck_fn_call(&mut self, fun: &FunDef, args: &Vec<Expr>,
1652      optargs: &Vec<(Ident, Expr)>, invoke_path: &Path,
1653      invoke_loc: &SrcLoc) {
1654        if args.len() < fun.params.len() {
1655            self.errors.push(AnalysisError {
1656                kind: AnalysisErrorKind::FnCallError,
1657                regarding: Some(format!("{} requires {} arguments; \
1658                  {} were provided", invoke_path, fun.params.len(), args.len())),
1659                loc: invoke_loc.clone(),
1660            });
1661            return;
1662        }
1663
1664        if !optargs.is_empty() && args.len() > fun.params.len() {
1665            self.errors.push(AnalysisError {
1666                kind: AnalysisErrorKind::FnCallError,
1667                regarding: Some(String::from("positional optional arguments \
1668                  cannot be mixed with by-name optional arguments (sorry!)")),
1669                loc: invoke_loc.clone(),
1670            });
1671            return;
1672        }
1673
1674        if let Some(max_optargs) =
1675          fun.optparams.as_ref().map(|o| o.max_len()).unwrap_or(Some(0)) {
1676            if args.len() > fun.params.len() + max_optargs {
1677                self.errors.push(AnalysisError {
1678                    kind: AnalysisErrorKind::FnCallError,
1679                    regarding: Some(format!("{} requires {} arguments{}; \
1680                      {} were provided", invoke_path,
1681                      fun.params.len(),
1682                      if max_optargs != 0 {
1683                          format!(" (+ {} optional)", max_optargs)
1684                      } else {
1685                          String::from("")
1686                      },
1687                      args.len())),
1688                    loc: invoke_loc.clone(),
1689                });
1690            }
1691        }
1692
1693        for (i, param) in fun.params.iter().enumerate() {
1694            let arg_type = try_type!(args[i]).decay();
1695
1696            match param.mode {
1697                ParamMode::ByRef =>
1698                    if param.ty != arg_type {
1699                        self.errors.push(AnalysisError {
1700                            kind: AnalysisErrorKind::TypeError,
1701                            regarding: Some(format!(
1702                              "parameter {} has type &{}; type {} provided",
1703                              param.name, param.ty, arg_type)),
1704                            loc: args[i].loc.clone(),
1705                        });
1706                    },
1707                ParamMode::ByVal =>
1708                    if !may_coerce(&arg_type, &param.ty) {
1709                        self.errors.push(AnalysisError {
1710                            kind: AnalysisErrorKind::TypeError,
1711                            regarding: Some(format!(
1712                              "parameter {} has type {}; type {} provided",
1713                              param.name, param.ty, arg_type)),
1714                            loc: args[i].loc.clone(),
1715                        });
1716                    },
1717            }
1718        }
1719
1720        if optargs.is_empty() {
1721            // any optional arguments are positional
1722            let optargs = &args[fun.params.len()..];
1723            match fun.optparams {
1724                Some(FunOptParams::Named(ref optparams)) => {
1725                    for (i, &(ref param, _)) in optparams.iter().enumerate() {
1726                        // argument was provided
1727                        if i < optargs.len() {
1728                            let arg_ty = try_type!(optargs[i]).decay();
1729
1730                            match param.mode {
1731                                ParamMode::ByRef =>
1732                                    if param.ty != arg_ty {
1733                                        self.errors.push(AnalysisError {
1734                                            kind: AnalysisErrorKind::TypeError,
1735                                            regarding: Some(format!(
1736                                              "parameter {} has type &{}; type {} \
1737                                                provided",
1738                                              param.name, param.ty, arg_ty)),
1739                                            loc: optargs[i].loc.clone(),
1740                                        });
1741                                    },
1742                                ParamMode::ByVal =>
1743                                    if !may_coerce(&arg_ty, &param.ty) {
1744                                        self.errors.push(AnalysisError {
1745                                            kind: AnalysisErrorKind::TypeError,
1746                                            regarding: Some(format!(
1747                                              "parameter {} has type {}; type {} \
1748                                                provided",
1749                                              param.name, param.ty, arg_ty)),
1750                                            loc: optargs[i].loc.clone(),
1751                                        });
1752                                    },
1753                            }
1754                        }
1755                    }
1756                },
1757
1758                Some(FunOptParams::VarArgs(_, _)) => {
1759                    for arg in optargs.iter() {
1760                        let arg_ty = try_type!(arg).decay();
1761                        if !may_coerce(&arg_ty, &Type::Variant) {
1762                            self.errors.push(AnalysisError {
1763                                kind: AnalysisErrorKind::TypeError,
1764                                regarding: Some(format!(
1765                                    "variadic optional argument has type {}; not \
1766                                      coercible to var", arg_ty)),
1767                                loc: arg.loc.clone(),
1768                            });
1769                        }
1770                    }
1771                },
1772
1773                None => { },
1774            }
1775        } else {
1776            let optparams = match fun.optparams {
1777                Some(FunOptParams::Named(ref optparams)) => optparams,
1778                _ => {
1779                    self.errors.push(AnalysisError {
1780                        kind: AnalysisErrorKind::FnCallError,
1781                        regarding: Some(format!("fn {} called with \
1782                          named optional arguments; none specified in \
1783                          function definition",
1784                          invoke_path)),
1785                        loc: optargs[0].1.loc.clone(),
1786                    });
1787                    return;
1788                },
1789            };
1790
1791            let mut seen = HashSet::new();
1792
1793            for &(ref argname, ref arg) in optargs {
1794                let which = optparams.iter().enumerate()
1795                    .find(|&(_, &(ref param, _))| {
1796                        match param.name {
1797                            Ident(ref name, None) => *name == argname.0,
1798                            Ident(_, Some(ref prev)) => *prev == argname.0,
1799                        }
1800                    });
1801
1802                match which {
1803                    Some((i, _)) => {
1804                        if seen.contains(&i) {
1805                            self.errors.push(AnalysisError {
1806                                kind: AnalysisErrorKind::DuplicateSymbol,
1807                                regarding: Some(format!("optional argument {} to \
1808                                  {} was duplicated", argname, invoke_path)),
1809                                loc: arg.loc.clone(),
1810                            });
1811                        }
1812                        seen.insert(i);
1813
1814                        let param = &optparams[i].0;
1815                        let arg_ty = try_type!(arg).decay();
1816                        match param.mode {
1817                            ParamMode::ByRef =>
1818                                if param.ty != arg_ty {
1819                                    self.errors.push(AnalysisError {
1820                                        kind: AnalysisErrorKind::TypeError,
1821                                        regarding: Some(format!(
1822                                          "parameter {} has type &{}; type {} \
1823                                            provided",
1824                                          param.name, param.ty, arg_ty)),
1825                                        loc: arg.loc.clone(),
1826                                    });
1827                                },
1828                            ParamMode::ByVal =>
1829                                if !may_coerce(&arg_ty, &param.ty) {
1830                                    self.errors.push(AnalysisError {
1831                                        kind: AnalysisErrorKind::TypeError,
1832                                        regarding: Some(format!(
1833                                          "parameter {} has type {}; type {} \
1834                                            provided",
1835                                          param.name, param.ty, arg_ty)),
1836                                        loc: arg.loc.clone(),
1837                                    });
1838                                },
1839                        }
1840                    },
1841
1842                    None => self.errors.push(AnalysisError {
1843                        kind: AnalysisErrorKind::FnCallError,
1844                        regarding: Some(format!("{} has no optional argument {}",
1845                          invoke_path, argname)),
1846                        loc: arg.loc.clone(),
1847                    }),
1848                }
1849            }
1850        }
1851    }
1852}