1use rustc_hash::FxHashMap;
2use serde::Serialize;
3use std::{collections::HashSet, mem};
4
5use crate::arena::Arena;
6use crate::typing::{Record, Type};
7use crate::{
8 App, Attrs, Binary, ExprRef, Field, Query, Raw, RecRef, Source, SourceKind, StrRef, Value,
9 error::AnalysisError, token::Operator,
10};
11
12#[derive(Debug, Clone, Serialize)]
22pub struct Typed {
23 pub project: Type,
28
29 #[serde(skip)]
34 pub scope: Scope,
35
36 pub aggregate: bool,
38}
39
40pub type AnalysisResult<A> = Result<A, AnalysisError>;
45
46#[derive(Default)]
52pub struct AnalysisOptions {
53 pub default_scope: Scope,
55 pub default_event_type: Type,
57 pub custom_types: HashSet<StrRef>,
63
64 pub data_sources: FxHashMap<StrRef, Type>,
70}
71
72#[derive(Default, Clone, Serialize, Debug)]
78pub struct Scope {
79 #[serde(skip_serializing)]
80 entries: FxHashMap<StrRef, Type>,
81}
82
83impl Scope {
84 pub fn is_empty(&self) -> bool {
86 self.entries.is_empty()
87 }
88
89 pub fn declare(&mut self, name: StrRef, tpe: Type) -> bool {
94 self.entries.insert(name, tpe).is_none()
95 }
96
97 pub fn get(&self, name: StrRef) -> Option<Type> {
101 self.entries.get(&name).copied()
102 }
103
104 pub fn get_mut(&mut self, name: StrRef) -> Option<&mut Type> {
108 self.entries.get_mut(&name)
109 }
110
111 pub fn exists(&self, name: StrRef) -> bool {
113 self.entries.contains_key(&name)
114 }
115}
116
117#[derive(Default)]
118struct CheckContext {
119 use_agg_func: bool,
120 use_source_based: bool,
121}
122
123#[derive(Default)]
128pub struct AnalysisContext {
129 pub allow_agg_func: bool,
135
136 pub use_agg_funcs: bool,
138}
139
140pub struct Analysis<'a> {
145 arena: &'a mut Arena,
146 options: &'a AnalysisOptions,
148 prev_scopes: Vec<Scope>,
150 scope: Scope,
152}
153
154impl<'a> Analysis<'a> {
155 pub fn new(arena: &'a mut Arena, options: &'a AnalysisOptions) -> Self {
157 Self {
158 arena,
159 options,
160 prev_scopes: Default::default(),
161 scope: Scope::default(),
162 }
163 }
164
165 pub fn scope(&self) -> &Scope {
173 &self.scope
174 }
175
176 pub fn scope_mut(&mut self) -> &mut Scope {
184 &mut self.scope
185 }
186
187 fn enter_scope(&mut self) {
188 if self.scope.is_empty() {
189 return;
190 }
191
192 let prev = mem::take(&mut self.scope);
193 self.prev_scopes.push(prev);
194 }
195
196 fn exit_scope(&mut self) -> Scope {
197 if let Some(prev) = self.prev_scopes.pop() {
198 mem::replace(&mut self.scope, prev)
199 } else {
200 mem::take(&mut self.scope)
201 }
202 }
203
204 #[cfg(test)]
205 pub fn test_declare(&mut self, name: &str, tpe: Type) -> bool {
206 let name = self.arena.strings.alloc_no_case(name);
207 self.scope.declare(name, tpe)
208 }
209
210 pub fn analyze_query(&mut self, query: Query<Raw>) -> AnalysisResult<Query<Typed>> {
237 self.enter_scope();
238
239 let mut sources = Vec::with_capacity(query.sources.len());
240 let mut ctx = AnalysisContext::default();
241
242 for source in query.sources {
243 sources.push(self.analyze_source(source)?);
244 }
245
246 if let Some(expr) = query.predicate.as_ref().copied() {
247 self.analyze_expr(&mut ctx, expr, Type::Bool)?;
248 }
249
250 if let Some(group_by) = &query.group_by {
251 let node = self.arena.exprs.get(group_by.expr);
252 if !matches!(node.value, Value::Access(_) | Value::Id(_)) {
253 return Err(AnalysisError::ExpectFieldLiteral(
254 node.attrs.pos.line,
255 node.attrs.pos.col,
256 ));
257 }
258
259 self.analyze_expr(&mut ctx, group_by.expr, Type::Unspecified)?;
260
261 if let Some(expr) = group_by.predicate {
262 ctx.allow_agg_func = true;
263 ctx.use_agg_funcs = true;
264
265 self.analyze_expr(&mut ctx, expr, Type::Bool)?;
266
267 let node = self.arena.exprs.get(expr);
268 if !self.expect_agg_expr(expr)? {
269 return Err(AnalysisError::ExpectAggExpr(
270 node.attrs.pos.line,
271 node.attrs.pos.col,
272 ));
273 }
274 }
275
276 ctx.allow_agg_func = true;
277 ctx.use_agg_funcs = true;
278 }
279
280 let project = self.analyze_projection(&mut ctx, query.projection)?;
281
282 if let Some(order_by) = &query.order_by {
283 self.analyze_expr(&mut ctx, order_by.expr, Type::Unspecified)?;
284 let node = self.arena.exprs.get(order_by.expr);
285 if query.group_by.is_none() && !matches!(node.value, Value::Access(_) | Value::Id(_)) {
286 return Err(AnalysisError::ExpectFieldLiteral(
287 node.attrs.pos.line,
288 node.attrs.pos.col,
289 ));
290 } else if query.group_by.is_some() {
291 self.expect_agg_func(order_by.expr)?;
292 }
293 }
294
295 let scope = self.exit_scope();
296
297 Ok(Query {
298 attrs: query.attrs,
299 sources,
300 predicate: query.predicate,
301 group_by: query.group_by,
302 order_by: query.order_by,
303 limit: query.limit,
304 projection: query.projection,
305 distinct: query.distinct,
306 meta: Typed {
307 project,
308 scope,
309 aggregate: ctx.use_agg_funcs,
310 },
311 })
312 }
313
314 fn analyze_source(&mut self, source: Source<Raw>) -> AnalysisResult<Source<Typed>> {
315 let kind = self.analyze_source_kind(source.kind)?;
316 let tpe = match &kind {
317 SourceKind::Name(name) => {
318 let tpe = if let Some(tpe) = self.options.data_sources.get(name).copied() {
319 tpe
320 } else {
321 self.options.default_event_type
322 };
323
324 self.arena.types.alloc_type(tpe)
325 }
326
327 SourceKind::Subject(_) => self.arena.types.alloc_type(self.options.default_event_type),
328
329 SourceKind::Subquery(query) => self.projection_type(query),
330 };
331
332 if !self.scope.declare(source.binding.name, tpe) {
333 return Err(AnalysisError::BindingAlreadyExists(
334 source.binding.pos.line,
335 source.binding.pos.col,
336 self.arena.strings.get(source.binding.name).to_owned(),
337 ));
338 }
339
340 Ok(Source {
341 binding: source.binding,
342 kind,
343 })
344 }
345
346 fn analyze_source_kind(&mut self, kind: SourceKind<Raw>) -> AnalysisResult<SourceKind<Typed>> {
347 match kind {
348 SourceKind::Name(n) => Ok(SourceKind::Name(n)),
349 SourceKind::Subject(s) => Ok(SourceKind::Subject(s)),
350 SourceKind::Subquery(query) => {
351 let query = self.analyze_query(*query)?;
352 Ok(SourceKind::Subquery(Box::new(query)))
353 }
354 }
355 }
356
357 fn analyze_projection(
358 &mut self,
359 ctx: &mut AnalysisContext,
360 expr: ExprRef,
361 ) -> AnalysisResult<Type> {
362 let node = self.arena.exprs.get(expr);
363 match node.value {
364 Value::Record(record) => {
365 if self.arena.exprs.rec(record).is_empty() {
366 return Err(AnalysisError::EmptyRecord(
367 node.attrs.pos.line,
368 node.attrs.pos.col,
369 ));
370 }
371
372 ctx.allow_agg_func = true;
373 let tpe = self.analyze_expr(ctx, expr, Type::Unspecified)?;
374 let mut chk_ctx = CheckContext {
375 use_agg_func: ctx.use_agg_funcs,
376 ..Default::default()
377 };
378
379 self.check_projection_on_record(&mut chk_ctx, record)?;
380 Ok(tpe)
381 }
382
383 Value::App(app) => {
384 ctx.allow_agg_func = true;
385
386 let tpe = self.analyze_expr(ctx, expr, Type::Unspecified)?;
387
388 if ctx.use_agg_funcs {
389 let mut chk_ctx = CheckContext {
390 use_agg_func: ctx.use_agg_funcs,
391 ..Default::default()
392 };
393
394 self.check_projection_on_field_expr(&mut chk_ctx, expr)?;
395 } else {
396 self.reject_constant_func(node.attrs, &app)?;
397 }
398
399 Ok(tpe)
400 }
401
402 Value::Id(_) if ctx.use_agg_funcs => Err(AnalysisError::ExpectAggExpr(
403 node.attrs.pos.line,
404 node.attrs.pos.col,
405 )),
406
407 Value::Id(id) => {
408 if let Some(tpe) = self.scope.get(id) {
409 Ok(tpe)
410 } else {
411 Err(AnalysisError::VariableUndeclared(
412 node.attrs.pos.line,
413 node.attrs.pos.col,
414 self.arena.strings.get(id).to_owned(),
415 ))
416 }
417 }
418
419 Value::Access(_) if ctx.use_agg_funcs => Err(AnalysisError::ExpectAggExpr(
420 node.attrs.pos.line,
421 node.attrs.pos.col,
422 )),
423
424 Value::Access(access) => {
425 let mut current = self.arena.exprs.get(access.target);
426
427 loop {
428 match current.value {
429 Value::Id(name) => {
430 if !self.scope.exists(name) {
431 return Err(AnalysisError::VariableUndeclared(
432 current.attrs.pos.line,
433 current.attrs.pos.col,
434 self.arena.strings.get(name).to_owned(),
435 ));
436 }
437
438 break;
439 }
440
441 Value::Access(next) => current = self.arena.exprs.get(next.target),
442 _ => unreachable!(),
443 }
444 }
445
446 self.analyze_expr(ctx, expr, Type::Unspecified)
447 }
448
449 _ => {
450 let tpe = self.project_type(expr);
451
452 Err(AnalysisError::ExpectRecordOrSourcedProperty(
453 node.attrs.pos.line,
454 node.attrs.pos.col,
455 display_type(self.arena, tpe),
456 ))
457 }
458 }
459 }
460
461 fn check_projection_on_record(
462 &mut self,
463 ctx: &mut CheckContext,
464 record: RecRef,
465 ) -> AnalysisResult<()> {
466 for idx in 0..self.arena.exprs.rec(record).len() {
467 let field = self.arena.exprs.rec_get(record, idx);
468
469 self.check_projection_on_field(ctx, &field)?;
470 }
471
472 Ok(())
473 }
474
475 fn check_projection_on_field(
476 &mut self,
477 ctx: &mut CheckContext,
478 field: &Field,
479 ) -> AnalysisResult<()> {
480 self.check_projection_on_field_expr(ctx, field.expr)
481 }
482
483 fn check_projection_on_field_expr(
484 &mut self,
485 ctx: &mut CheckContext,
486 expr: ExprRef,
487 ) -> AnalysisResult<()> {
488 let node = self.arena.exprs.get(expr);
489 match node.value {
490 Value::Number(_) | Value::String(_) | Value::Bool(_) => Ok(()),
491
492 Value::Id(id) => {
493 if self.scope.exists(id) {
494 if ctx.use_agg_func {
495 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
496 node.attrs.pos.line,
497 node.attrs.pos.col,
498 ));
499 }
500
501 ctx.use_source_based = true;
502 }
503
504 Ok(())
505 }
506
507 Value::Array(exprs) => {
508 for idx in self.arena.exprs.vec_idxes(exprs) {
509 let expr = self.arena.exprs.vec_get(exprs, idx);
510
511 self.check_projection_on_field_expr(ctx, expr)?;
512 }
513
514 Ok(())
515 }
516
517 Value::Record(fields) => {
518 for idx in self.arena.exprs.rec_idxes(fields) {
519 let field = self.arena.exprs.rec_get(fields, idx);
520
521 self.check_projection_on_field(ctx, &field)?;
522 }
523
524 Ok(())
525 }
526
527 Value::Access(access) => self.check_projection_on_field_expr(ctx, access.target),
528
529 Value::App(app) => {
530 if let Some(Type::App { aggregate, .. }) = self.options.default_scope.get(app.func)
531 {
532 ctx.use_agg_func |= aggregate;
533
534 if ctx.use_agg_func && ctx.use_source_based {
535 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
536 node.attrs.pos.line,
537 node.attrs.pos.col,
538 ));
539 }
540
541 if aggregate {
542 return self.expect_agg_func(expr);
543 }
544
545 for idx in self.arena.exprs.vec_idxes(app.args) {
546 let arg = self.arena.exprs.vec_get(app.args, idx);
547
548 self.invalidate_agg_func_usage(arg)?;
549 }
550 }
551
552 Ok(())
553 }
554
555 Value::Binary(binary) => {
556 self.check_projection_on_field_expr(ctx, binary.lhs)?;
557 self.check_projection_on_field_expr(ctx, binary.rhs)
558 }
559
560 Value::Unary(unary) => self.check_projection_on_field_expr(ctx, unary.expr),
561 Value::Group(expr) => self.check_projection_on_field_expr(ctx, expr),
562 }
563 }
564
565 fn expect_agg_func(&self, expr: ExprRef) -> AnalysisResult<()> {
566 let node = self.arena.exprs.get(expr);
567 if let Value::App(app) = node.value
568 && let Some(Type::App {
569 aggregate: true, ..
570 }) = self.options.default_scope.get(app.func)
571 {
572 for idx in 0..self.arena.exprs.vec(app.args).len() {
573 let arg = self.arena.exprs.vec_get(app.args, idx);
574
575 self.ensure_agg_param_is_source_bound(arg)?;
576 self.invalidate_agg_func_usage(arg)?;
577 }
578
579 return Ok(());
580 }
581
582 Err(AnalysisError::ExpectAggExpr(
583 node.attrs.pos.line,
584 node.attrs.pos.col,
585 ))
586 }
587
588 fn expect_agg_expr(&self, expr: ExprRef) -> AnalysisResult<bool> {
589 let node = self.arena.exprs.get(expr);
590 match node.value {
591 Value::Id(id) => {
592 if self.scope.exists(id) {
593 return Err(AnalysisError::UnallowedAggFuncUsageWithSrcField(
594 node.attrs.pos.line,
595 node.attrs.pos.col,
596 ));
597 }
598
599 Ok(false)
600 }
601 Value::Group(expr) => self.expect_agg_expr(expr),
602 Value::Binary(binary) => {
603 let lhs = self.expect_agg_expr(binary.lhs)?;
604 let rhs = self.expect_agg_expr(binary.rhs)?;
605
606 if !lhs && !rhs {
607 return Err(AnalysisError::ExpectAggExpr(
608 node.attrs.pos.line,
609 node.attrs.pos.col,
610 ));
611 }
612
613 Ok(true)
614 }
615 Value::Unary(unary) => self.expect_agg_expr(unary.expr),
616 Value::App(_) => {
617 self.expect_agg_func(expr)?;
618 Ok(true)
619 }
620
621 _ => Ok(false),
622 }
623 }
624
625 fn ensure_agg_param_is_source_bound(&self, expr: ExprRef) -> AnalysisResult<()> {
626 let node = self.arena.exprs.get(expr);
627 match node.value {
628 Value::Id(id) if !self.options.default_scope.exists(id) => Ok(()),
629 Value::Access(access) => self.ensure_agg_param_is_source_bound(access.target),
630 Value::Binary(binary) => self.ensure_agg_binary_op_is_source_bound(node.attrs, binary),
631 Value::Unary(unary) => self.ensure_agg_param_is_source_bound(unary.expr),
632
633 _ => Err(AnalysisError::ExpectSourceBoundProperty(
634 node.attrs.pos.line,
635 node.attrs.pos.col,
636 )),
637 }
638 }
639
640 fn ensure_agg_binary_op_is_source_bound(
641 &self,
642 attrs: Attrs,
643 binary: Binary,
644 ) -> AnalysisResult<()> {
645 if !self.ensure_agg_binary_op_branch_is_source_bound(binary.lhs)
646 && !self.ensure_agg_binary_op_branch_is_source_bound(binary.rhs)
647 {
648 return Err(AnalysisError::ExpectSourceBoundProperty(
649 attrs.pos.line,
650 attrs.pos.col,
651 ));
652 }
653
654 Ok(())
655 }
656
657 fn ensure_agg_binary_op_branch_is_source_bound(&self, expr: ExprRef) -> bool {
658 let node = self.arena.exprs.get(expr);
659 match node.value {
660 Value::Id(id) => !self.options.default_scope.exists(id),
661 Value::Array(exprs) => {
662 if self.arena.exprs.vec(exprs).is_empty() {
663 return false;
664 }
665
666 for idx in 0..self.arena.exprs.vec(exprs).len() {
667 let expr = self.arena.exprs.vec_get(exprs, idx);
668
669 if !self.ensure_agg_binary_op_branch_is_source_bound(expr) {
670 return false;
671 }
672 }
673
674 true
675 }
676 Value::Record(fields) => {
677 if self.arena.exprs.rec(fields).is_empty() {
678 return false;
679 }
680
681 for idx in 0..self.arena.exprs.rec(fields).len() {
682 let field = self.arena.exprs.rec_get(fields, idx);
683
684 if !self.ensure_agg_binary_op_branch_is_source_bound(field.expr) {
685 return false;
686 }
687 }
688
689 true
690 }
691
692 Value::Access(access) => {
693 self.ensure_agg_binary_op_branch_is_source_bound(access.target)
694 }
695
696 Value::Binary(binary) => self
697 .ensure_agg_binary_op_is_source_bound(node.attrs, binary)
698 .is_ok(),
699 Value::Unary(unary) => self.ensure_agg_binary_op_branch_is_source_bound(unary.expr),
700 Value::Group(expr) => self.ensure_agg_binary_op_branch_is_source_bound(expr),
701
702 Value::Number(_) | Value::String(_) | Value::Bool(_) | Value::App(_) => false,
703 }
704 }
705
706 fn invalidate_agg_func_usage(&self, expr: ExprRef) -> AnalysisResult<()> {
707 let node = self.arena.exprs.get(expr);
708 match node.value {
709 Value::Number(_)
710 | Value::String(_)
711 | Value::Bool(_)
712 | Value::Id(_)
713 | Value::Access(_) => Ok(()),
714
715 Value::Array(exprs) => {
716 for idx in 0..self.arena.exprs.vec(exprs).len() {
717 let expr = self.arena.exprs.vec_get(exprs, idx);
718
719 self.invalidate_agg_func_usage(expr)?;
720 }
721
722 Ok(())
723 }
724
725 Value::Record(fields) => {
726 for idx in 0..self.arena.exprs.rec(fields).len() {
727 let field = self.arena.exprs.rec_get(fields, idx);
728
729 self.invalidate_agg_func_usage(field.expr)?;
730 }
731
732 Ok(())
733 }
734
735 Value::App(app) => {
736 if let Some(Type::App { aggregate, .. }) = self.options.default_scope.get(app.func)
737 && aggregate
738 {
739 return Err(AnalysisError::WrongAggFunUsage(
740 node.attrs.pos.line,
741 node.attrs.pos.col,
742 self.arena.strings.get(app.func).to_owned(),
743 ));
744 }
745
746 for idx in 0..self.arena.exprs.vec(app.args).len() {
747 let arg = self.arena.exprs.vec_get(app.args, idx);
748 self.invalidate_agg_func_usage(arg)?;
749 }
750
751 Ok(())
752 }
753
754 Value::Binary(binary) => {
755 self.invalidate_agg_func_usage(binary.lhs)?;
756 self.invalidate_agg_func_usage(binary.rhs)
757 }
758
759 Value::Unary(unary) => self.invalidate_agg_func_usage(unary.expr),
760 Value::Group(expr) => self.invalidate_agg_func_usage(expr),
761 }
762 }
763
764 fn reject_constant_func(&self, attrs: Attrs, app: &App) -> AnalysisResult<()> {
765 if self.arena.exprs.vec(app.args).is_empty() {
766 return Err(AnalysisError::ConstantExprInProjectIntoClause(
767 attrs.pos.line,
768 attrs.pos.col,
769 ));
770 }
771
772 let mut errored = None;
773 for idx in 0..self.arena.exprs.vec(app.args).len() {
774 let arg = self.arena.exprs.vec_get(app.args, idx);
775
776 if let Err(e) = self.reject_constant_expr(arg) {
777 if errored.is_none() {
778 errored = Some(e);
779 }
780
781 continue;
782 }
783
784 return Ok(());
786 }
787
788 Err(errored.expect("to be defined at that point"))
789 }
790
791 fn reject_constant_expr(&self, expr: ExprRef) -> AnalysisResult<()> {
792 let node = self.arena.exprs.get(expr);
793 match node.value {
794 Value::Id(id) if self.scope.exists(id) => Ok(()),
795 Value::Array(exprs) => {
796 let mut errored = None;
797 for idx in 0..self.arena.exprs.vec(exprs).len() {
798 let expr = self.arena.exprs.vec_get(exprs, idx);
799
800 if let Err(e) = self.reject_constant_expr(expr) {
801 if errored.is_none() {
802 errored = Some(e);
803 }
804
805 continue;
806 }
807
808 return Ok(());
810 }
811
812 Err(errored.expect("to be defined at that point"))
813 }
814
815 Value::Record(fields) => {
816 let mut errored = None;
817 for idx in 0..self.arena.exprs.rec(fields).len() {
818 let field = self.arena.exprs.rec_get(fields, idx);
819
820 if let Err(e) = self.reject_constant_expr(field.expr) {
821 if errored.is_none() {
822 errored = Some(e);
823 }
824
825 continue;
826 }
827
828 return Ok(());
830 }
831
832 Err(errored.expect("to be defined at that point"))
833 }
834
835 Value::Binary(binary) => self
836 .reject_constant_expr(binary.lhs)
837 .or_else(|e| self.reject_constant_expr(binary.rhs).map_err(|_| e)),
838
839 Value::Access(access) => self.reject_constant_expr(access.target),
840 Value::App(app) => self.reject_constant_func(node.attrs, &app),
841 Value::Unary(unary) => self.reject_constant_expr(unary.expr),
842 Value::Group(expr) => self.reject_constant_expr(expr),
843
844 _ => Err(AnalysisError::ConstantExprInProjectIntoClause(
845 node.attrs.pos.line,
846 node.attrs.pos.col,
847 )),
848 }
849 }
850
851 pub fn analyze_expr(
879 &mut self,
880 ctx: &mut AnalysisContext,
881 expr: ExprRef,
882 mut expect: Type,
883 ) -> AnalysisResult<Type> {
884 let node = self.arena.exprs.get(expr);
885 match node.value {
886 Value::Number(_) => self.arena.type_check(node.attrs, expect, Type::Number),
887 Value::String(_) => self.arena.type_check(node.attrs, expect, Type::String),
888 Value::Bool(_) => self.arena.type_check(node.attrs, expect, Type::Bool),
889
890 Value::Id(id) => {
891 if let Some(tpe) = self.options.default_scope.get(id) {
892 self.arena.type_check(node.attrs, expect, tpe)
893 } else if let Some(tpe) = self.scope.get_mut(id) {
894 *tpe = self.arena.type_check(node.attrs, mem::take(tpe), expect)?;
895
896 Ok(*tpe)
897 } else {
898 Err(AnalysisError::VariableUndeclared(
899 node.attrs.pos.line,
900 node.attrs.pos.col,
901 self.arena.strings.get(id).to_owned(),
902 ))
903 }
904 }
905
906 Value::Array(exprs) => {
907 if matches!(expect, Type::Unspecified) {
908 for idx in self.arena.exprs.vec_idxes(exprs) {
909 let expr = self.arena.exprs.vec_get(exprs, idx);
910
911 expect = self.analyze_expr(ctx, expr, expect)?;
912 }
913
914 return Ok(self.arena.types.alloc_array_of(expect));
915 }
916
917 match expect {
918 Type::Array(expect) => {
919 let mut expect = self.arena.types.get_type(expect);
920 for idx in 0..self.arena.exprs.vec(exprs).len() {
921 let expr = self.arena.exprs.vec_get(exprs, idx);
922 expect = self.analyze_expr(ctx, expr, expect)?;
923 }
924
925 Ok(self.arena.types.alloc_array_of(expect))
926 }
927
928 expect => {
929 let tpe = self.project_type(expr);
930
931 Err(AnalysisError::TypeMismatch(
932 node.attrs.pos.line,
933 node.attrs.pos.col,
934 display_type(self.arena, expect),
935 display_type(self.arena, tpe),
936 ))
937 }
938 }
939 }
940
941 Value::Record(fields) => {
942 if matches!(expect, Type::Unspecified) {
943 let mut record = FxHashMap::default();
944
945 for idx in 0..self.arena.exprs.rec(fields).len() {
946 let field = self.arena.exprs.rec_get(fields, idx);
947
948 record.insert(
949 field.name,
950 self.analyze_expr(ctx, field.expr, Type::Unspecified)?,
951 );
952 }
953
954 return Ok(Type::Record(self.arena.types.alloc_record(record)));
955 }
956
957 if let Type::Record(rec) = expect
958 && self.arena.types.record_len(rec) == self.arena.exprs.rec(fields).len()
959 {
960 for idx in self.arena.exprs.rec_idxes(fields) {
961 let field = self.arena.exprs.rec_get(fields, idx);
962
963 if let Some(tpe) = self.arena.types.record_get(rec, field.name) {
964 let new_tpe = self.analyze_expr(ctx, field.expr, tpe)?;
965 self.arena.types.record_set(rec, field.name, new_tpe);
966 continue;
967 }
968
969 return Err(AnalysisError::FieldUndeclared(
970 field.attrs.pos.line,
971 field.attrs.pos.col,
972 self.arena.strings.get(field.name).to_owned(),
973 ));
974 }
975
976 return Ok(expect);
977 }
978
979 let tpe = self.project_type(expr);
980
981 Err(AnalysisError::TypeMismatch(
982 node.attrs.pos.line,
983 node.attrs.pos.col,
984 display_type(self.arena, expect),
985 display_type(self.arena, tpe),
986 ))
987 }
988
989 Value::Access(_) => Ok(self.analyze_access(node.attrs, expr, expect)?),
990
991 Value::App(app) => {
992 if let Some(tpe) = self.options.default_scope.get(app.func)
993 && let Type::App {
994 args,
995 result,
996 aggregate,
997 } = tpe
998 {
999 let args_actual_len = self.arena.exprs.vec(app.args).len();
1000 let args_decl_len = self.arena.types.get_args(args.values).len();
1001
1002 if !(args_actual_len >= args.needed && args_actual_len <= args_decl_len) {
1003 return Err(AnalysisError::FunWrongArgumentCount(
1004 node.attrs.pos.line,
1005 node.attrs.pos.col,
1006 self.arena.strings.get(app.func).to_owned(),
1007 ));
1008 }
1009
1010 if aggregate && !ctx.allow_agg_func {
1011 return Err(AnalysisError::WrongAggFunUsage(
1012 node.attrs.pos.line,
1013 node.attrs.pos.col,
1014 self.arena.strings.get(app.func).to_owned(),
1015 ));
1016 }
1017
1018 if aggregate && ctx.allow_agg_func {
1019 ctx.use_agg_funcs = true;
1020 }
1021
1022 let arg_types = self.arena.types.args_idxes(args.values);
1023 let args_idxes = self.arena.exprs.vec_idxes(app.args);
1024 for (val_idx, tpe_idx) in args_idxes.zip(arg_types) {
1025 let arg = self.arena.exprs.vec_get(app.args, val_idx);
1026 let tpe = self.arena.types.args_get(args.values, tpe_idx);
1027
1028 self.analyze_expr(ctx, arg, tpe)?;
1029 }
1030
1031 if matches!(expect, Type::Unspecified) {
1032 Ok(self.arena.types.get_type(result))
1033 } else {
1034 self.arena
1035 .type_check(node.attrs, expect, self.arena.types.get_type(result))
1036 }
1037 } else {
1038 Err(AnalysisError::FuncUndeclared(
1039 node.attrs.pos.line,
1040 node.attrs.pos.col,
1041 self.arena.strings.get(app.func).to_owned(),
1042 ))
1043 }
1044 }
1045
1046 Value::Binary(binary) => match binary.operator {
1047 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => {
1048 self.analyze_expr(ctx, binary.lhs, Type::Number)?;
1049 self.analyze_expr(ctx, binary.rhs, Type::Number)?;
1050 self.arena.type_check(node.attrs, expect, Type::Number)
1051 }
1052
1053 Operator::Eq
1054 | Operator::Neq
1055 | Operator::Lt
1056 | Operator::Lte
1057 | Operator::Gt
1058 | Operator::Gte => {
1059 let lhs_expect = self.analyze_expr(ctx, binary.lhs, Type::Unspecified)?;
1060 let rhs_expect = self.analyze_expr(ctx, binary.rhs, lhs_expect)?;
1061
1062 if matches!(lhs_expect, Type::Unspecified)
1065 && !matches!(rhs_expect, Type::Unspecified)
1066 {
1067 self.analyze_expr(ctx, binary.lhs, rhs_expect)?;
1068 }
1069
1070 self.arena.type_check(node.attrs, expect, Type::Bool)
1071 }
1072
1073 Operator::Contains => {
1074 let new_expect = self.arena.types.alloc_array_of(Type::Unspecified);
1075 let lhs_expect = self.analyze_expr(ctx, binary.lhs, new_expect)?;
1076
1077 let lhs_assumption = match lhs_expect {
1078 Type::Array(inner) => self.arena.types.get_type(inner),
1079 other => {
1080 return Err(AnalysisError::ExpectArray(
1081 node.attrs.pos.line,
1082 node.attrs.pos.col,
1083 display_type(self.arena, other),
1084 ));
1085 }
1086 };
1087
1088 let rhs_expect = self.analyze_expr(ctx, binary.rhs, lhs_assumption)?;
1089
1090 if matches!(lhs_assumption, Type::Unspecified)
1093 && !matches!(rhs_expect, Type::Unspecified)
1094 {
1095 let new_expect = self.arena.types.alloc_array_of(rhs_expect);
1096 self.analyze_expr(ctx, binary.lhs, new_expect)?;
1097 }
1098
1099 self.arena.type_check(node.attrs, expect, Type::Bool)
1100 }
1101
1102 Operator::And | Operator::Or | Operator::Xor => {
1103 self.analyze_expr(ctx, binary.lhs, Type::Bool)?;
1104 self.analyze_expr(ctx, binary.rhs, Type::Bool)?;
1105 self.arena.type_check(node.attrs, expect, Type::Bool)
1106 }
1107
1108 Operator::As => {
1109 let rhs = self.arena.exprs.get(binary.rhs);
1110 if let Value::Id(name) = rhs.value {
1111 return if let Some(tpe) = resolve_type(self.arena, self.options, name) {
1112 Ok(tpe)
1114 } else {
1115 Err(AnalysisError::UnsupportedCustomType(
1116 rhs.attrs.pos.line,
1117 rhs.attrs.pos.col,
1118 self.arena.strings.get(name).to_owned(),
1119 ))
1120 };
1121 }
1122
1123 unreachable!(
1124 "we already made sure during parsing that we can only have an ID symbol at this point"
1125 )
1126 }
1127
1128 Operator::Not => unreachable!(),
1129 },
1130
1131 Value::Unary(unary) => match unary.operator {
1132 Operator::Add | Operator::Sub => {
1133 self.analyze_expr(ctx, unary.expr, Type::Number)?;
1134 self.arena.type_check(node.attrs, expect, Type::Number)
1135 }
1136
1137 Operator::Not => {
1138 self.analyze_expr(ctx, unary.expr, Type::Bool)?;
1139 self.arena.type_check(node.attrs, expect, Type::Bool)
1140 }
1141
1142 _ => unreachable!(),
1143 },
1144
1145 Value::Group(expr) => Ok(self.analyze_expr(ctx, expr, expect)?),
1146 }
1147 }
1148
1149 fn analyze_access(
1150 &mut self,
1151 attrs: Attrs,
1152 access: ExprRef,
1153 expect: Type,
1154 ) -> AnalysisResult<Type> {
1155 struct State {
1156 depth: u8,
1157 dynamic: bool,
1159 definition: Def,
1160 }
1161
1162 impl State {
1163 fn new(definition: Def) -> Self {
1164 Self {
1165 depth: 0,
1166 dynamic: false,
1167 definition,
1168 }
1169 }
1170 }
1171
1172 #[derive(Copy, Clone)]
1173 struct Parent {
1174 record: Record,
1175 field: Option<StrRef>,
1176 }
1177
1178 enum Def {
1179 User { parent: Parent, tpe: Type },
1180 System(Type),
1181 }
1182
1183 fn go<'global>(
1184 scope: &mut Scope,
1185 arena: &'global mut Arena,
1186 sys: &'global AnalysisOptions,
1187 expr: ExprRef,
1188 ) -> AnalysisResult<State> {
1189 let node = arena.exprs.get(expr);
1190 match node.value {
1191 Value::Id(id) => {
1192 if let Some(tpe) = sys.default_scope.get(id) {
1193 if matches!(tpe, Type::Record(_)) {
1194 Ok(State::new(Def::System(tpe)))
1195 } else {
1196 Err(AnalysisError::ExpectRecord(
1197 node.attrs.pos.line,
1198 node.attrs.pos.col,
1199 display_type(arena, tpe),
1200 ))
1201 }
1202 } else if let Some(tpe) = scope.get_mut(id) {
1203 if matches!(tpe, Type::Unspecified) {
1204 let record = arena.types.instantiate_record();
1205 *tpe = Type::Record(record);
1206
1207 Ok(State::new(Def::User {
1208 parent: Parent {
1209 record,
1210 field: None,
1211 },
1212 tpe: Type::Record(record),
1213 }))
1214 } else if let Type::Record(record) = *tpe {
1215 Ok(State::new(Def::User {
1216 parent: Parent {
1217 record,
1218 field: None,
1219 },
1220 tpe: *tpe,
1221 }))
1222 } else {
1223 Err(AnalysisError::ExpectRecord(
1224 node.attrs.pos.line,
1225 node.attrs.pos.col,
1226 display_type(arena, *tpe),
1227 ))
1228 }
1229 } else {
1230 Err(AnalysisError::VariableUndeclared(
1231 node.attrs.pos.line,
1232 node.attrs.pos.col,
1233 arena.strings.get(id).to_owned(),
1234 ))
1235 }
1236 }
1237 Value::Access(access) => {
1238 let mut state = go(scope, arena, sys, access.target)?;
1239
1240 let is_data_field =
1242 state.depth == 0 && arena.strings.get(access.field) == "data";
1243
1244 if !state.dynamic && is_data_field {
1248 state.dynamic = true;
1249 }
1250
1251 match state.definition {
1252 Def::User { parent, tpe } => {
1253 if matches!(tpe, Type::Unspecified) && state.dynamic {
1254 let record = arena.types.instantiate_record();
1255 arena
1256 .types
1257 .record_set(record, access.field, Type::Unspecified);
1258
1259 if let Some(field) = parent.field {
1261 arena.types.record_set(
1262 parent.record,
1263 field,
1264 Type::Record(record),
1265 );
1266 }
1267
1268 return Ok(State {
1269 depth: state.depth + 1,
1270 definition: Def::User {
1271 parent: Parent {
1272 record,
1273 field: Some(access.field),
1274 },
1275 tpe: Type::Unspecified,
1276 },
1277 ..state
1278 });
1279 } else if let Type::Record(record) = tpe {
1280 return if let Some(tpe) =
1281 arena.types.record_get(record, access.field)
1282 {
1283 Ok(State {
1284 depth: state.depth + 1,
1285 definition: Def::User {
1286 parent: Parent {
1287 record,
1288 field: Some(access.field),
1289 },
1290 tpe,
1291 },
1292 ..state
1293 })
1294 } else {
1295 if state.dynamic || is_data_field {
1297 arena.types.record_set(
1298 record,
1299 access.field,
1300 Type::Unspecified,
1301 );
1302 return Ok(State {
1303 depth: state.depth + 1,
1304 definition: Def::User {
1305 parent: Parent {
1306 record,
1307 field: Some(access.field),
1308 },
1309 tpe: Type::Unspecified,
1310 },
1311 ..state
1312 });
1313 }
1314
1315 Err(AnalysisError::FieldUndeclared(
1316 node.attrs.pos.line,
1317 node.attrs.pos.col,
1318 arena.strings.get(access.field).to_owned(),
1319 ))
1320 };
1321 }
1322
1323 Err(AnalysisError::ExpectRecord(
1324 node.attrs.pos.line,
1325 node.attrs.pos.col,
1326 display_type(arena, tpe),
1327 ))
1328 }
1329
1330 Def::System(tpe) => {
1331 if matches!(tpe, Type::Unspecified) && state.dynamic {
1332 return Ok(State {
1333 depth: state.depth + 1,
1334 definition: Def::System(Type::Unspecified),
1335 ..state
1336 });
1337 }
1338
1339 if let Type::Record(rec) = tpe {
1340 if let Some(field) = arena.types.record_get(rec, access.field) {
1341 return Ok(State {
1342 depth: state.depth + 1,
1343 definition: Def::System(field),
1344 ..state
1345 });
1346 }
1347
1348 return Err(AnalysisError::FieldUndeclared(
1349 node.attrs.pos.line,
1350 node.attrs.pos.col,
1351 arena.strings.get(access.field).to_owned(),
1352 ));
1353 }
1354
1355 Err(AnalysisError::ExpectRecord(
1356 node.attrs.pos.line,
1357 node.attrs.pos.col,
1358 display_type(arena, tpe),
1359 ))
1360 }
1361 }
1362 }
1363 Value::Number(_)
1364 | Value::String(_)
1365 | Value::Bool(_)
1366 | Value::Array(_)
1367 | Value::Record(_)
1368 | Value::App(_)
1369 | Value::Binary(_)
1370 | Value::Unary(_)
1371 | Value::Group(_) => unreachable!(),
1372 }
1373 }
1374
1375 let state = go(&mut self.scope, self.arena, self.options, access)?;
1376
1377 match state.definition {
1378 Def::User { parent, tpe } => {
1379 let new_tpe = self.arena.type_check(attrs, tpe, expect)?;
1380
1381 if let Some(field) = parent.field {
1382 self.arena.types.record_set(parent.record, field, new_tpe);
1383 }
1384
1385 Ok(new_tpe)
1386 }
1387
1388 Def::System(tpe) => self.arena.type_check(attrs, tpe, expect),
1389 }
1390 }
1391
1392 fn projection_type(&mut self, query: &Query<Typed>) -> Type {
1393 self.project_type(query.projection)
1394 }
1395
1396 fn project_type(&mut self, node: ExprRef) -> Type {
1397 match self.arena.exprs.get(node).value {
1398 Value::Number(_) => Type::Number,
1399 Value::String(_) => Type::String,
1400 Value::Bool(_) => Type::Bool,
1401 Value::Id(id) => {
1402 if let Some(tpe) = self.options.default_scope.get(id) {
1403 tpe
1404 } else if let Some(tpe) = self.scope.get(id) {
1405 tpe
1406 } else {
1407 Type::Unspecified
1408 }
1409 }
1410 Value::Array(exprs) => {
1411 let mut project = Type::Unspecified;
1412
1413 for idx in self.arena.exprs.vec_idxes(exprs) {
1414 let expr = self.arena.exprs.vec_get(exprs, idx);
1415 let tmp = self.project_type(expr);
1416
1417 if !matches!(tmp, Type::Unspecified) {
1418 project = tmp;
1419 break;
1420 }
1421 }
1422
1423 self.arena.types.alloc_array_of(project)
1424 }
1425 Value::Record(fields) => {
1426 let mut props = FxHashMap::default();
1427
1428 for idx in self.arena.exprs.rec_idxes(fields) {
1429 let field = self.arena.exprs.rec_get(fields, idx);
1430 let tpe = self.project_type(field.expr);
1431 props.insert(field.name, tpe);
1432 }
1433
1434 Type::Record(self.arena.types.alloc_record(props))
1435 }
1436 Value::Access(access) => {
1437 let tpe = self.project_type(access.target);
1438 if let Type::Record(record) = tpe {
1439 self.arena
1440 .types
1441 .record_get(record, access.field)
1442 .unwrap_or_default()
1443 } else {
1444 Type::Unspecified
1445 }
1446 }
1447 Value::App(app) => self.options.default_scope.get(app.func).unwrap_or_default(),
1448 Value::Binary(binary) => match binary.operator {
1449 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => Type::Number,
1450 Operator::As => {
1451 if let Value::Id(n) = self.arena.exprs.get(binary.rhs).value
1452 && let Some(tpe) = resolve_type(self.arena, self.options, n)
1453 {
1454 tpe
1455 } else {
1456 Type::Unspecified
1457 }
1458 }
1459 Operator::Eq
1460 | Operator::Neq
1461 | Operator::Lt
1462 | Operator::Lte
1463 | Operator::Gt
1464 | Operator::Gte
1465 | Operator::And
1466 | Operator::Or
1467 | Operator::Xor
1468 | Operator::Not
1469 | Operator::Contains => Type::Bool,
1470 },
1471 Value::Unary(unary) => match unary.operator {
1472 Operator::Add | Operator::Sub => Type::Number,
1473 Operator::Mul
1474 | Operator::Div
1475 | Operator::Eq
1476 | Operator::Neq
1477 | Operator::Lt
1478 | Operator::Lte
1479 | Operator::Gt
1480 | Operator::Gte
1481 | Operator::And
1482 | Operator::Or
1483 | Operator::Xor
1484 | Operator::Not
1485 | Operator::Contains
1486 | Operator::As => unreachable!(),
1487 },
1488 Value::Group(expr) => self.project_type(expr),
1489 }
1490 }
1491}
1492
1493impl Arena {
1494 fn type_check(&mut self, attrs: Attrs, this: Type, other: Type) -> Result<Type, AnalysisError> {
1499 match (this, other) {
1500 (Type::Unspecified, other) => Ok(other),
1501 (this, Type::Unspecified) => Ok(this),
1502 (Type::Subject, Type::Subject) => Ok(Type::Subject),
1503
1504 (Type::Subject, Type::String) => Ok(Type::String),
1508 (Type::String, Type::Subject) => Ok(Type::String),
1509
1510 (Type::Number, Type::Number) => Ok(Type::Number),
1511 (Type::String, Type::String) => Ok(Type::String),
1512 (Type::Bool, Type::Bool) => Ok(Type::Bool),
1513 (Type::Date, Type::Date) => Ok(Type::Date),
1514 (Type::Time, Type::Time) => Ok(Type::Time),
1515 (Type::DateTime, Type::DateTime) => Ok(Type::DateTime),
1516
1517 (Type::DateTime, Type::Date) => Ok(Type::Date),
1519 (Type::Date, Type::DateTime) => Ok(Type::Date),
1520 (Type::DateTime, Type::Time) => Ok(Type::Time),
1521 (Type::Time, Type::DateTime) => Ok(Type::Time),
1522 (Type::Custom(a), Type::Custom(b)) if self.strings.eq_ignore_ascii_case(a, b) => {
1523 Ok(Type::Custom(a))
1524 }
1525 (Type::Array(a), Type::Array(b)) => {
1526 let a = self.types.get_type(a);
1527 let b = self.types.get_type(b);
1528 let tpe = self.type_check(attrs, a, b)?;
1529
1530 Ok(self.types.alloc_array_of(tpe))
1531 }
1532
1533 (Type::Record(a), Type::Record(b)) if self.types.records_have_same_keys(a, b) => {
1534 let mut map_a = mem::take(&mut self.types.records[a.0]);
1535 let mut map_b = mem::take(&mut self.types.records[b.0]);
1536
1537 for (bk, bv) in map_b.iter_mut() {
1538 let av = map_a.get_mut(bk).unwrap();
1539 let new_tpe = self.type_check(attrs, *av, *bv)?;
1540
1541 *av = new_tpe;
1542 *bv = new_tpe;
1543 }
1544
1545 self.types.records[a.0] = map_a;
1546 self.types.records[b.0] = map_b;
1547
1548 Ok(Type::Record(a))
1549 }
1550
1551 (
1552 Type::App {
1553 args: a_args,
1554 result: a_res,
1555 aggregate: a_agg,
1556 },
1557 Type::App {
1558 args: b_args,
1559 result: b_res,
1560 aggregate: b_agg,
1561 },
1562 ) if self.types.get_args(a_args.values).len()
1563 == self.types.get_args(b_args.values).len()
1564 && a_agg == b_agg =>
1565 {
1566 if self.types.get_args(a_args.values).is_empty() {
1567 let a = self.types.get_type(a_res);
1568 let b = self.types.get_type(b_res);
1569 let new_res = self.type_check(attrs, a, b)?;
1570
1571 return Ok(Type::App {
1572 args: a_args,
1573 result: self.types.register_type(new_res),
1574 aggregate: a_agg,
1575 });
1576 }
1577
1578 let mut vec_a = mem::take(&mut self.types.args[a_args.values.0]);
1579 let mut vec_b = mem::take(&mut self.types.args[b_args.values.0]);
1580
1581 for (a, b) in vec_a.iter_mut().zip(vec_b.iter_mut()) {
1582 let new_tpe = self.type_check(attrs, *a, *b)?;
1583 *a = new_tpe;
1584 *b = new_tpe;
1585 }
1586
1587 self.types.args[a_args.values.0] = vec_a;
1588 self.types.args[b_args.values.0] = vec_b;
1589
1590 let res_a = self.types.get_type(a_res);
1591 let res_b = self.types.get_type(b_res);
1592 let new_tpe = self.type_check(attrs, res_a, res_b)?;
1593
1594 Ok(Type::App {
1595 args: a_args,
1596 result: self.types.register_type(new_tpe),
1597 aggregate: a_agg,
1598 })
1599 }
1600
1601 (this, other) => Err(AnalysisError::TypeMismatch(
1602 attrs.pos.line,
1603 attrs.pos.col,
1604 display_type(self, this),
1605 display_type(self, other),
1606 )),
1607 }
1608 }
1609}
1610
1611pub(crate) fn resolve_type_from_str(
1633 arena: &Arena,
1634 opts: &AnalysisOptions,
1635 name: &str,
1636) -> Option<Type> {
1637 if name.eq_ignore_ascii_case("string") {
1638 Some(Type::String)
1639 } else if name.eq_ignore_ascii_case("int")
1640 || name.eq_ignore_ascii_case("float64")
1641 || name.eq_ignore_ascii_case("number")
1642 {
1643 Some(Type::Number)
1644 } else if name.eq_ignore_ascii_case("boolean") || name.eq_ignore_ascii_case("bool") {
1645 Some(Type::Bool)
1646 } else if name.eq_ignore_ascii_case("date") {
1647 Some(Type::Date)
1648 } else if name.eq_ignore_ascii_case("time") {
1649 Some(Type::Time)
1650 } else if name.eq_ignore_ascii_case("datetime") {
1651 Some(Type::DateTime)
1652 } else if let Some(str_ref) = arena.strings.str_ref_no_case(name)
1653 && opts.custom_types.contains(&str_ref)
1654 {
1655 Some(Type::Custom(str_ref))
1656 } else {
1657 None
1658 }
1659}
1660
1661pub(crate) fn resolve_type(
1662 arena: &Arena,
1663 opts: &AnalysisOptions,
1664 name_ref: StrRef,
1665) -> Option<Type> {
1666 let name = arena.strings.get(name_ref);
1667 resolve_type_from_str(arena, opts, name)
1668}
1669
1670pub(crate) fn display_type(arena: &Arena, tpe: Type) -> String {
1671 fn go(buffer: &mut String, arena: &Arena, tpe: Type) {
1672 match tpe {
1673 Type::Unspecified => buffer.push_str("Any"),
1674 Type::Number => buffer.push_str("Number"),
1675 Type::String => buffer.push_str("String"),
1676 Type::Bool => buffer.push_str("Bool"),
1677 Type::Subject => buffer.push_str("Subject"),
1678 Type::Date => buffer.push_str("Date"),
1679 Type::Time => buffer.push_str("Time"),
1680 Type::DateTime => buffer.push_str("DateTime"),
1681 Type::Custom(n) => buffer.push_str(arena.strings.get(n)),
1682
1683 Type::Array(tpe) => {
1684 buffer.push_str("[]");
1685 go(buffer, arena, arena.types.get_type(tpe));
1686 }
1687
1688 Type::Record(map) => {
1689 let map = arena.types.get_record(map);
1690
1691 buffer.push_str("{ ");
1692
1693 for (idx, (name, value)) in map.iter().enumerate() {
1694 if idx != 0 {
1695 buffer.push_str(", ");
1696 }
1697
1698 buffer.push_str(arena.strings.get(*name));
1699 buffer.push_str(": ");
1700
1701 go(buffer, arena, *value);
1702 }
1703
1704 buffer.push_str(" }");
1705 }
1706
1707 Type::App {
1708 args,
1709 result,
1710 aggregate,
1711 } => {
1712 let fun_args = arena.types.get_args(args.values);
1713 buffer.push('(');
1714
1715 for (idx, arg) in fun_args.iter().copied().enumerate() {
1716 if idx != 0 {
1717 buffer.push_str(", ");
1718 }
1719
1720 go(buffer, arena, arg);
1721
1722 if idx + 1 > args.needed {
1723 buffer.push('?');
1724 }
1725 }
1726
1727 buffer.push(')');
1728
1729 if aggregate {
1730 buffer.push_str(" => ");
1731 } else {
1732 buffer.push_str(" -> ");
1733 }
1734
1735 go(buffer, arena, arena.types.get_type(result));
1736 }
1737 }
1738 }
1739
1740 let mut buffer = String::new();
1741 go(&mut buffer, arena, tpe);
1742
1743 buffer
1744}