From e4ef386c44adf0b225bf8ad548a6bd10b0b5dc95 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 14 Aug 2023 17:19:24 -0400 Subject: [PATCH] feat(tipo): inference for and/or chains --- crates/aiken-lang/src/ast.rs | 6 ++- crates/aiken-lang/src/expr.rs | 5 ++- crates/aiken-lang/src/tipo/error.rs | 19 ++++++++- crates/aiken-lang/src/tipo/expr.rs | 61 +++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 684acf81..243a2d0e 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -6,7 +6,11 @@ use crate::{ }; use miette::Diagnostic; use owo_colors::{OwoColorize, Stream::Stdout}; -use std::{fmt, ops::Range, sync::Arc}; +use std::{ + fmt::{self, Display}, + ops::Range, + sync::Arc, +}; use vec1::Vec1; pub const ASSERT_VARIABLE: &str = "_try"; diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index bac25db3..6a5da8c8 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -5,8 +5,9 @@ use vec1::Vec1; use crate::{ ast::{ self, Annotation, Arg, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, - DefinitionLocation, IfBranch, ParsedCallArg, Pattern, RecordUpdateSpread, Span, TraceKind, - TypedClause, TypedRecordUpdateArg, UnOp, UntypedClause, UntypedRecordUpdateArg, + DefinitionLocation, IfBranch, LogicalOpChainKind, ParsedCallArg, Pattern, + RecordUpdateSpread, Span, TraceKind, TypedClause, TypedRecordUpdateArg, UnOp, + UntypedClause, UntypedRecordUpdateArg, }, builtins::void, parser::token::Base, diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index d1886e26..b909e2d7 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1,6 +1,6 @@ use super::Type; use crate::{ - ast::{Annotation, BinOp, CallArg, Span, UntypedPattern}, + ast::{Annotation, BinOp, CallArg, LogicalOpChainKind, Span, UntypedPattern}, expr::{self, UntypedExpr}, format::Formatter, levenshtein, @@ -55,7 +55,22 @@ impl Diagnostic for UnknownLabels { #[derive(Debug, thiserror::Error, Diagnostic, Clone)] pub enum Error { - #[error("I discovered a type cast from Data without an annotation")] + #[error("I discovered an {} chain with less than 2 expressions.", op.if_supports_color(Stdout, |s| s.purple()))] + #[diagnostic(code("illegal::logical_op_chain"))] + #[diagnostic(help( + "Logical {}/{} chains require at least 2 expressions. You are missing {}.", + "and".if_supports_color(Stdout, |s| s.purple()), + "or".if_supports_color(Stdout, |s| s.purple()), + missing + ))] + LogicalOpChainMissingExpr { + op: LogicalOpChainKind, + #[label] + location: Span, + missing: u8, + }, + + #[error("I discovered a type cast from Data without an annotation.")] #[diagnostic(code("illegal::type_cast"))] #[diagnostic(help("Try adding an annotation...\n\n{}", format_suggestion(value)))] CastDataNoAnn { diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index ac5b6f91..0d1c6507 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -4,10 +4,10 @@ use vec1::Vec1; use crate::{ ast::{ Annotation, Arg, ArgName, AssignmentKind, BinOp, ByteArrayFormatPreference, CallArg, - ClauseGuard, Constant, IfBranch, RecordUpdateSpread, Span, TraceKind, Tracing, TypedArg, - TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, TypedPattern, - TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, UntypedIfBranch, - UntypedPattern, UntypedRecordUpdateArg, + ClauseGuard, Constant, IfBranch, LogicalOpChainKind, RecordUpdateSpread, Span, TraceKind, + Tracing, TypedArg, TypedCallArg, TypedClause, TypedClauseGuard, TypedIfBranch, + TypedPattern, TypedRecordUpdateArg, UnOp, UntypedArg, UntypedClause, UntypedClauseGuard, + UntypedIfBranch, UntypedPattern, UntypedRecordUpdateArg, }, builtins::{bool, byte_array, function, int, list, string, tuple}, expr::{FnStyle, TypedExpr, UntypedExpr}, @@ -213,6 +213,12 @@ impl<'a, 'b> ExprTyper<'a, 'b> { location, value, .. } => Ok(self.infer_string(value, location)), + UntypedExpr::LogicalOpChain { + kind, + expressions, + location, + } => self.infer_logical_op_chain(kind, expressions, location), + UntypedExpr::PipeLine { expressions, .. } => self.infer_pipeline(expressions), UntypedExpr::Fn { @@ -1571,6 +1577,52 @@ impl<'a, 'b> ExprTyper<'a, 'b> { } } + fn infer_logical_op_chain( + &mut self, + kind: LogicalOpChainKind, + expressions: Vec, + location: Span, + ) -> Result { + let mut typed_expressions = vec![]; + + for expression in expressions { + let typed_expression = self.infer(expression)?; + + self.unify( + bool(), + typed_expression.tipo(), + typed_expression.location(), + false, + )?; + + typed_expressions.push(typed_expression); + } + + if typed_expressions.len() < 2 { + return Err(Error::LogicalOpChainMissingExpr { + op: kind, + location, + missing: 2 - typed_expressions.len() as u8, + }); + } + + let name: BinOp = kind.into(); + + let chain = typed_expressions + .into_iter() + .rev() + .reduce(|acc, typed_expression| TypedExpr::BinOp { + location: Span::empty(), + tipo: bool(), + name, + left: typed_expression.into(), + right: acc.into(), + }) + .expect("should have at least two"); + + Ok(chain) + } + fn infer_pipeline(&mut self, expressions: Vec1) -> Result { PipeTyper::infer(self, expressions) } @@ -1927,6 +1979,7 @@ fn assert_no_assignment(expr: &UntypedExpr) -> Result<(), Error> { | UntypedExpr::TupleIndex { .. } | UntypedExpr::UnOp { .. } | UntypedExpr::Var { .. } + | UntypedExpr::LogicalOpChain { .. } | UntypedExpr::TraceIfFalse { .. } | UntypedExpr::When { .. } => Ok(()), }