1use ast::*;
4use super::*;
5use visit::ASTVisitorMut;
6use visit::NameCtxt;
7
8use std::collections::HashSet;
9
10pub 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 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 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 Type::Variant => match *to {
139 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 Type::Array(ref targetty, ArrayBounds::Dynamic(dims)) =>
155 targetty == basety && bounds.dims() == dims,
156
157 Type::Variant => {
158 match **basety {
159 Type::Struct(_) => false,
161 _ => true,
162 }
163 },
164
165 _ => false,
166 },
167
168 Type::VarArgsArray => false,
169
170 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 _ => 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 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 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 Type::Array(ref basety, _) => match *to {
236 Type::Variant => {
237 match **basety {
238 Type::Struct(_) => false,
240 _ => true,
241 }
242 },
243
244 _ => false,
245 },
246
247 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 _ => panic!("dumpster fire: we haven't figured out the rules for \
267 this type yet."),
268 }
269}
270
271macro_rules! try_type {
273 ($ex:expr) => {
274 match $ex.ty {
275 Some(ref ty) => ty,
276 None => return,
277 }
278 }
279}
280
281macro_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 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 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 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 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 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 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 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 self.walk_expr(expr, module, function);
512
513 expr.ty = match expr.data {
516 ExprKind::Lit(ref lit) => Some(lit.ty()),
517
518 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 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 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 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 },
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 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 _ => {
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 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 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 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 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 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 },
1406
1407 _ => { },
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 _ => { },
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 _ => { },
1507 }
1508 },
1509
1510 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 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, ¶m.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 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 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, ¶m.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, ¶m.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}