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