diff --git a/compiler/noirc_frontend/src/hir/comptime/display.rs b/compiler/noirc_frontend/src/hir/comptime/display.rs new file mode 100644 index 00000000000..869e5517d6c --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/display.rs @@ -0,0 +1,864 @@ +use std::{fmt::Display, rc::Rc}; + +use iter_extended::vecmap; +use noirc_errors::Span; + +use crate::{ + ast::{ + ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, + CastExpression, ConstrainStatement, ConstructorExpression, Expression, ExpressionKind, + ForLoopStatement, ForRange, GenericTypeArgs, IfExpression, IndexExpression, + InfixExpression, LValue, Lambda, LetStatement, Literal, MemberAccessExpression, + MethodCallExpression, Pattern, PrefixExpression, Statement, StatementKind, UnresolvedType, + UnresolvedTypeData, + }, + hir_def::traits::TraitConstraint, + macros_api::NodeInterner, + node_interner::InternedStatementKind, + token::{Keyword, Token}, + Type, +}; + +use super::{ + value::{ExprValue, TypedExpr}, + Value, +}; + +pub(super) fn display_quoted( + tokens: &[Token], + indent: usize, + interner: &NodeInterner, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + if tokens.is_empty() { + write!(f, "quote {{ }}") + } else { + writeln!(f, "quote {{")?; + let indent = indent + 1; + write!(f, "{}", " ".repeat(indent * 4))?; + display_tokens(tokens, interner, indent, f)?; + writeln!(f)?; + let indent = indent - 1; + write!(f, "{}", " ".repeat(indent * 4))?; + write!(f, "}}") + } +} + +struct TokensPrettyPrinter<'tokens, 'interner> { + tokens: &'tokens [Token], + interner: &'interner NodeInterner, +} + +impl<'tokens, 'interner> Display for TokensPrettyPrinter<'tokens, 'interner> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + display_tokens(self.tokens, self.interner, 0, f) + } +} + +fn display_tokens( + tokens: &[Token], + interner: &NodeInterner, + indent: usize, + f: &mut std::fmt::Formatter<'_>, +) -> std::fmt::Result { + let mut token_printer = TokenPrettyPrinter::new(interner, indent); + for token in tokens { + token_printer.print(token, f)?; + } + Ok(()) +} + +pub(super) fn tokens_to_string(tokens: Rc>, interner: &NodeInterner) -> String { + let tokens: Vec = tokens.iter().cloned().collect(); + TokensPrettyPrinter { tokens: &tokens, interner }.to_string() +} + +/// Tries to print tokens in a way that it'll be easier for the user to understand a +/// stream of tokens without having to format it themselves. +/// +/// The gist is: +/// - Keep track of the current indent level +/// - When '{' is found, a newline is inserted and the indent is incremented +/// - When '}' is found, a newline is inserted and the indent is decremented +/// - When ';' is found a newline is inserted +/// - When interned values are encountered, they are turned into strings and indented +/// according to the current indentation. +/// +/// There are a few more details that needs to be taken into account: +/// - two consecutive words shouldn't be glued together (as they are separate tokens) +/// - inserting spaces when needed +/// - not inserting extra newlines if possible +/// - ';' shouldn't always insert newlines (this is when it's something like `[Field; 2]`) +struct TokenPrettyPrinter<'interner> { + interner: &'interner NodeInterner, + indent: usize, + last_was_alphanumeric: bool, + last_was_right_brace: bool, + last_was_semicolon: bool, +} + +impl<'interner> TokenPrettyPrinter<'interner> { + fn new(interner: &'interner NodeInterner, indent: usize) -> Self { + Self { + interner, + indent, + last_was_alphanumeric: false, + last_was_right_brace: false, + last_was_semicolon: false, + } + } + + fn print(&mut self, token: &Token, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let last_was_alphanumeric = self.last_was_alphanumeric; + self.last_was_alphanumeric = false; + + // After `}` we usually want a newline... but not always! + if self.last_was_right_brace { + self.last_was_right_brace = false; + + match token { + Token::Keyword(Keyword::Else) => { + // If we have `} else` we don't want a newline + write!(f, " else")?; + self.last_was_alphanumeric = true; + return Ok(()); + } + Token::RightBrace => { + // Because we insert a newline right before `}`, if we have two + // (or more) in a row we don't want extra newlines. + } + _ => { + writeln!(f)?; + self.write_indent(f)?; + } + } + } + + // Heuristic: if we have `; 2` then we assume we are inside something like `[Field; 2]` + // and don't include a newline. + // The only consequence of getting this wrong is that we'll end with two consecutive + // statements in a single line (not a big deal). + if self.last_was_semicolon { + self.last_was_semicolon = false; + + match token { + Token::Int(..) => { + write!(f, " ")?; + } + Token::Ident(ident) => { + if ident.chars().all(|char| char.is_ascii_uppercase()) { + write!(f, " ")?; + } else { + writeln!(f)?; + self.write_indent(f)?; + } + } + Token::RightBrace => { + // We don't want an extra newline in this case + } + _ => { + writeln!(f)?; + self.write_indent(f)?; + } + } + } + + match token { + Token::QuotedType(id) => write!(f, "{}", self.interner.get_quoted_type(*id)), + Token::InternedExpr(id) => { + let value = Value::expression(ExpressionKind::Interned(*id)); + self.print_value(&value, f) + } + Token::InternedStatement(id) => { + let value = Value::statement(StatementKind::Interned(*id)); + self.print_value(&value, f) + } + Token::InternedLValue(id) => { + let value = Value::lvalue(LValue::Interned(*id, Span::default())); + self.print_value(&value, f) + } + Token::InternedUnresolvedTypeData(id) => { + let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); + self.print_value(&value, f) + } + Token::InternedPattern(id) => { + let value = Value::pattern(Pattern::Interned(*id, Span::default())); + self.print_value(&value, f) + } + Token::UnquoteMarker(id) => { + let value = Value::TypedExpr(TypedExpr::ExprId(*id)); + self.print_value(&value, f) + } + Token::Keyword(..) + | Token::Ident(..) + | Token::IntType(..) + | Token::Int(..) + | Token::Bool(..) => { + if last_was_alphanumeric { + write!(f, " ")?; + } + self.last_was_alphanumeric = true; + write!(f, "{token}") + } + Token::Comma => { + write!(f, "{token} ") + } + Token::LeftBrace => { + writeln!(f, " {{")?; + self.indent += 1; + self.write_indent(f) + } + Token::RightBrace => { + self.last_was_right_brace = true; + writeln!(f)?; + self.indent -= 1; + self.write_indent(f)?; + write!(f, "}}") + } + Token::Semicolon => { + self.last_was_semicolon = true; + write!(f, ";") + } + Token::Quote(tokens) => { + if last_was_alphanumeric { + write!(f, " ")?; + } + let tokens = vecmap(&tokens.0, |spanned_token| spanned_token.clone().into_token()); + display_quoted(&tokens, self.indent, self.interner, f) + } + Token::Colon => { + write!(f, "{token} ") + } + Token::Less + | Token::LessEqual + | Token::Greater + | Token::GreaterEqual + | Token::Equal + | Token::NotEqual + | Token::Plus + | Token::Minus + | Token::Star + | Token::Slash + | Token::Percent + | Token::Ampersand + | Token::ShiftLeft + | Token::ShiftRight + | Token::Assign + | Token::Arrow => write!(f, " {token} "), + Token::LeftParen + | Token::RightParen + | Token::LeftBracket + | Token::RightBracket + | Token::Dot + | Token::DoubleColon + | Token::DoubleDot + | Token::Caret + | Token::Pound + | Token::Pipe + | Token::Bang + | Token::DollarSign => { + write!(f, "{token}") + } + Token::Str(..) + | Token::RawStr(..) + | Token::FmtStr(..) + | Token::Whitespace(_) + | Token::LineComment(..) + | Token::BlockComment(..) + | Token::Attribute(..) + | Token::InnerAttribute(..) + | Token::Invalid(_) => { + if last_was_alphanumeric { + write!(f, " ")?; + } + write!(f, "{token}") + } + Token::EOF => Ok(()), + } + } + + fn print_value(&mut self, value: &Value, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let string = value.display(self.interner).to_string(); + for (index, line) in string.lines().enumerate() { + if index > 0 { + writeln!(f)?; + self.write_indent(f)?; + } + line.fmt(f)?; + } + + self.last_was_alphanumeric = string.bytes().all(|byte| byte.is_ascii_alphanumeric()); + self.last_was_right_brace = string.ends_with('}'); + self.last_was_semicolon = string.ends_with(';'); + + Ok(()) + } + + fn write_indent(&mut self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", " ".repeat(self.indent * 4)) + } +} + +impl Value { + pub fn display<'value, 'interner>( + &'value self, + interner: &'interner NodeInterner, + ) -> ValuePrinter<'value, 'interner> { + ValuePrinter { value: self, interner } + } +} + +pub struct ValuePrinter<'value, 'interner> { + value: &'value Value, + interner: &'interner NodeInterner, +} + +impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.value { + Value::Unit => write!(f, "()"), + Value::Bool(value) => { + let msg = if *value { "true" } else { "false" }; + write!(f, "{msg}") + } + Value::Field(value) => write!(f, "{value}"), + Value::I8(value) => write!(f, "{value}"), + Value::I16(value) => write!(f, "{value}"), + Value::I32(value) => write!(f, "{value}"), + Value::I64(value) => write!(f, "{value}"), + Value::U1(value) => write!(f, "{value}"), + Value::U8(value) => write!(f, "{value}"), + Value::U16(value) => write!(f, "{value}"), + Value::U32(value) => write!(f, "{value}"), + Value::U64(value) => write!(f, "{value}"), + Value::String(value) => write!(f, "{value}"), + Value::CtString(value) => write!(f, "{value}"), + Value::FormatString(value, _) => write!(f, "{value}"), + Value::Function(..) => write!(f, "(function)"), + Value::Closure(..) => write!(f, "(closure)"), + Value::Tuple(fields) => { + let fields = vecmap(fields, |field| field.display(self.interner).to_string()); + write!(f, "({})", fields.join(", ")) + } + Value::Struct(fields, typ) => { + let typename = match typ.follow_bindings() { + Type::Struct(def, _) => def.borrow().name.to_string(), + other => other.to_string(), + }; + let fields = vecmap(fields, |(name, value)| { + format!("{}: {}", name, value.display(self.interner)) + }); + write!(f, "{typename} {{ {} }}", fields.join(", ")) + } + Value::Pointer(value, _) => write!(f, "&mut {}", value.borrow().display(self.interner)), + Value::Array(values, _) => { + let values = vecmap(values, |value| value.display(self.interner).to_string()); + write!(f, "[{}]", values.join(", ")) + } + Value::Slice(values, _) => { + let values = vecmap(values, |value| value.display(self.interner).to_string()); + write!(f, "&[{}]", values.join(", ")) + } + Value::Quoted(tokens) => display_quoted(tokens, 0, self.interner, f), + Value::StructDefinition(id) => { + let def = self.interner.get_struct(*id); + let def = def.borrow(); + write!(f, "{}", def.name) + } + Value::TraitConstraint(trait_id, generics) => { + let trait_ = self.interner.get_trait(*trait_id); + write!(f, "{}{generics}", trait_.name) + } + Value::TraitDefinition(trait_id) => { + let trait_ = self.interner.get_trait(*trait_id); + write!(f, "{}", trait_.name) + } + Value::TraitImpl(trait_impl_id) => { + let trait_impl = self.interner.get_trait_implementation(*trait_impl_id); + let trait_impl = trait_impl.borrow(); + + let generic_string = + vecmap(&trait_impl.trait_generics, ToString::to_string).join(", "); + let generic_string = if generic_string.is_empty() { + generic_string + } else { + format!("<{}>", generic_string) + }; + + let where_clause = vecmap(&trait_impl.where_clause, |trait_constraint| { + display_trait_constraint(self.interner, trait_constraint) + }); + let where_clause = where_clause.join(", "); + let where_clause = if where_clause.is_empty() { + where_clause + } else { + format!(" where {}", where_clause) + }; + + write!( + f, + "impl {}{} for {}{}", + trait_impl.ident, generic_string, trait_impl.typ, where_clause + ) + } + Value::FunctionDefinition(function_id) => { + write!(f, "{}", self.interner.function_name(function_id)) + } + Value::ModuleDefinition(module_id) => { + if let Some(attributes) = self.interner.try_module_attributes(module_id) { + write!(f, "{}", &attributes.name) + } else { + write!(f, "(crate root)") + } + } + Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), + Value::Type(typ) => write!(f, "{}", typ), + Value::Expr(ExprValue::Expression(expr)) => { + let expr = remove_interned_in_expression_kind(self.interner, expr.clone()); + write!(f, "{}", expr) + } + Value::Expr(ExprValue::Statement(statement)) => { + write!(f, "{}", remove_interned_in_statement_kind(self.interner, statement.clone())) + } + Value::Expr(ExprValue::LValue(lvalue)) => { + write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) + } + Value::Expr(ExprValue::Pattern(pattern)) => { + write!(f, "{}", remove_interned_in_pattern(self.interner, pattern.clone())) + } + Value::TypedExpr(TypedExpr::ExprId(id)) => { + let hir_expr = self.interner.expression(id); + let expr = hir_expr.to_display_ast(self.interner, Span::default()); + write!(f, "{}", expr.kind) + } + Value::TypedExpr(TypedExpr::StmtId(id)) => { + let hir_statement = self.interner.statement(id); + let stmt = hir_statement.to_display_ast(self.interner, Span::default()); + write!(f, "{}", stmt.kind) + } + Value::UnresolvedType(typ) => { + write!(f, "{}", remove_interned_in_unresolved_type_data(self.interner, typ.clone())) + } + } + } +} + +impl Token { + pub fn display<'token, 'interner>( + &'token self, + interner: &'interner NodeInterner, + ) -> TokenPrinter<'token, 'interner> { + TokenPrinter { token: self, interner } + } +} + +pub struct TokenPrinter<'token, 'interner> { + token: &'token Token, + interner: &'interner NodeInterner, +} + +impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.token { + Token::QuotedType(id) => { + write!(f, "{}", self.interner.get_quoted_type(*id)) + } + Token::InternedExpr(id) => { + let value = Value::expression(ExpressionKind::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::InternedStatement(id) => { + let value = Value::statement(StatementKind::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::InternedLValue(id) => { + let value = Value::lvalue(LValue::Interned(*id, Span::default())); + value.display(self.interner).fmt(f) + } + Token::InternedUnresolvedTypeData(id) => { + let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); + value.display(self.interner).fmt(f) + } + Token::InternedPattern(id) => { + let value = Value::pattern(Pattern::Interned(*id, Span::default())); + value.display(self.interner).fmt(f) + } + Token::UnquoteMarker(id) => { + let value = Value::TypedExpr(TypedExpr::ExprId(*id)); + value.display(self.interner).fmt(f) + } + other => write!(f, "{other}"), + } + } +} + +fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String { + let trait_ = interner.get_trait(trait_constraint.trait_id); + format!("{}: {}{}", trait_constraint.typ, trait_.name, trait_constraint.trait_generics) +} + +// Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. +fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { + Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } +} + +// Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. +fn remove_interned_in_expression_kind( + interner: &NodeInterner, + expr: ExpressionKind, +) -> ExpressionKind { + match expr { + ExpressionKind::Literal(literal) => { + ExpressionKind::Literal(remove_interned_in_literal(interner, literal)) + } + ExpressionKind::Block(block) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Block(BlockExpression { statements }) + } + ExpressionKind::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { + rhs: remove_interned_in_expression(interner, prefix.rhs), + ..*prefix + })), + ExpressionKind::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { + collection: remove_interned_in_expression(interner, index.collection), + index: remove_interned_in_expression(interner, index.index), + })), + ExpressionKind::Call(call) => ExpressionKind::Call(Box::new(CallExpression { + func: Box::new(remove_interned_in_expression(interner, *call.func)), + arguments: vecmap(call.arguments, |arg| remove_interned_in_expression(interner, arg)), + ..*call + })), + ExpressionKind::MethodCall(call) => { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: remove_interned_in_expression(interner, call.object), + arguments: vecmap(call.arguments, |arg| { + remove_interned_in_expression(interner, arg) + }), + ..*call + })) + } + ExpressionKind::Constructor(constructor) => { + ExpressionKind::Constructor(Box::new(ConstructorExpression { + fields: vecmap(constructor.fields, |(name, expr)| { + (name, remove_interned_in_expression(interner, expr)) + }), + ..*constructor + })) + } + ExpressionKind::MemberAccess(member_access) => { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: remove_interned_in_expression(interner, member_access.lhs), + ..*member_access + })) + } + ExpressionKind::Cast(cast) => ExpressionKind::Cast(Box::new(CastExpression { + lhs: remove_interned_in_expression(interner, cast.lhs), + ..*cast + })), + ExpressionKind::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { + lhs: remove_interned_in_expression(interner, infix.lhs), + rhs: remove_interned_in_expression(interner, infix.rhs), + ..*infix + })), + ExpressionKind::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { + condition: remove_interned_in_expression(interner, if_expr.condition), + consequence: remove_interned_in_expression(interner, if_expr.consequence), + alternative: if_expr + .alternative + .map(|alternative| remove_interned_in_expression(interner, alternative)), + })), + ExpressionKind::Variable(_) => expr, + ExpressionKind::Tuple(expressions) => ExpressionKind::Tuple(vecmap(expressions, |expr| { + remove_interned_in_expression(interner, expr) + })), + ExpressionKind::Lambda(lambda) => ExpressionKind::Lambda(Box::new(Lambda { + body: remove_interned_in_expression(interner, lambda.body), + ..*lambda + })), + ExpressionKind::Parenthesized(expr) => { + ExpressionKind::Parenthesized(Box::new(remove_interned_in_expression(interner, *expr))) + } + ExpressionKind::Quote(_) => expr, + ExpressionKind::Unquote(expr) => { + ExpressionKind::Unquote(Box::new(remove_interned_in_expression(interner, *expr))) + } + ExpressionKind::Comptime(block, span) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Comptime(BlockExpression { statements }, span) + } + ExpressionKind::Unsafe(block, span) => { + let statements = + vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); + ExpressionKind::Unsafe(BlockExpression { statements }, span) + } + ExpressionKind::AsTraitPath(mut path) => { + path.typ = remove_interned_in_unresolved_type(interner, path.typ); + path.trait_generics = + remove_interned_in_generic_type_args(interner, path.trait_generics); + ExpressionKind::AsTraitPath(path) + } + ExpressionKind::TypePath(mut path) => { + path.typ = remove_interned_in_unresolved_type(interner, path.typ); + path.turbofish = remove_interned_in_generic_type_args(interner, path.turbofish); + ExpressionKind::TypePath(path) + } + ExpressionKind::Resolved(id) => { + let expr = interner.expression(&id); + expr.to_display_ast(interner, Span::default()).kind + } + ExpressionKind::Interned(id) => { + let expr = interner.get_expression_kind(id).clone(); + remove_interned_in_expression_kind(interner, expr) + } + ExpressionKind::Error => expr, + ExpressionKind::InternedStatement(id) => remove_interned_in_statement_expr(interner, id), + } +} + +fn remove_interned_in_statement_expr( + interner: &NodeInterner, + id: InternedStatementKind, +) -> ExpressionKind { + let expr = match interner.get_statement_kind(id).clone() { + StatementKind::Expression(expr) | StatementKind::Semi(expr) => expr.kind, + StatementKind::Interned(id) => remove_interned_in_statement_expr(interner, id), + _ => ExpressionKind::Error, + }; + remove_interned_in_expression_kind(interner, expr) +} + +fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Literal { + match literal { + Literal::Array(array_literal) => { + Literal::Array(remove_interned_in_array_literal(interner, array_literal)) + } + Literal::Slice(array_literal) => { + Literal::Array(remove_interned_in_array_literal(interner, array_literal)) + } + Literal::Bool(_) + | Literal::Integer(_, _) + | Literal::Str(_) + | Literal::RawStr(_, _) + | Literal::FmtStr(_) + | Literal::Unit => literal, + } +} + +fn remove_interned_in_array_literal( + interner: &NodeInterner, + literal: ArrayLiteral, +) -> ArrayLiteral { + match literal { + ArrayLiteral::Standard(expressions) => { + ArrayLiteral::Standard(vecmap(expressions, |expr| { + remove_interned_in_expression(interner, expr) + })) + } + ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { + repeated_element: Box::new(remove_interned_in_expression(interner, *repeated_element)), + length: Box::new(remove_interned_in_expression(interner, *length)), + }, + } +} + +// Returns a new Statement where all Interned statements have been turned into non-interned StatementKind. +fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { + Statement { + kind: remove_interned_in_statement_kind(interner, statement.kind), + span: statement.span, + } +} + +// Returns a new StatementKind where all Interned statements have been turned into non-interned StatementKind. +fn remove_interned_in_statement_kind( + interner: &NodeInterner, + statement: StatementKind, +) -> StatementKind { + match statement { + StatementKind::Let(let_statement) => StatementKind::Let(LetStatement { + pattern: remove_interned_in_pattern(interner, let_statement.pattern), + expression: remove_interned_in_expression(interner, let_statement.expression), + r#type: remove_interned_in_unresolved_type(interner, let_statement.r#type), + ..let_statement + }), + StatementKind::Constrain(constrain) => StatementKind::Constrain(ConstrainStatement( + remove_interned_in_expression(interner, constrain.0), + constrain.1.map(|expr| remove_interned_in_expression(interner, expr)), + constrain.2, + )), + StatementKind::Expression(expr) => { + StatementKind::Expression(remove_interned_in_expression(interner, expr)) + } + StatementKind::Assign(assign) => StatementKind::Assign(AssignStatement { + lvalue: assign.lvalue, + expression: remove_interned_in_expression(interner, assign.expression), + }), + StatementKind::For(for_loop) => StatementKind::For(ForLoopStatement { + range: match for_loop.range { + ForRange::Range(from, to) => ForRange::Range( + remove_interned_in_expression(interner, from), + remove_interned_in_expression(interner, to), + ), + ForRange::Array(expr) => { + ForRange::Array(remove_interned_in_expression(interner, expr)) + } + }, + block: remove_interned_in_expression(interner, for_loop.block), + ..for_loop + }), + StatementKind::Comptime(statement) => { + StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) + } + StatementKind::Semi(expr) => { + StatementKind::Semi(remove_interned_in_expression(interner, expr)) + } + StatementKind::Interned(id) => { + let statement = interner.get_statement_kind(id).clone(); + remove_interned_in_statement_kind(interner, statement) + } + StatementKind::Break | StatementKind::Continue | StatementKind::Error => statement, + } +} + +// Returns a new LValue where all Interned LValues have been turned into LValue. +fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { + match lvalue { + LValue::Ident(_) => lvalue, + LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { + object: Box::new(remove_interned_in_lvalue(interner, *object)), + field_name, + span, + }, + LValue::Index { array, index, span } => LValue::Index { + array: Box::new(remove_interned_in_lvalue(interner, *array)), + index: remove_interned_in_expression(interner, index), + span, + }, + LValue::Dereference(lvalue, span) => { + LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) + } + LValue::Interned(id, span) => { + let lvalue = interner.get_lvalue(id, span); + remove_interned_in_lvalue(interner, lvalue) + } + } +} + +fn remove_interned_in_unresolved_type( + interner: &NodeInterner, + typ: UnresolvedType, +) -> UnresolvedType { + UnresolvedType { + typ: remove_interned_in_unresolved_type_data(interner, typ.typ), + span: typ.span, + } +} + +fn remove_interned_in_unresolved_type_data( + interner: &NodeInterner, + typ: UnresolvedTypeData, +) -> UnresolvedTypeData { + match typ { + UnresolvedTypeData::Array(expr, typ) => UnresolvedTypeData::Array( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Slice(typ) => { + UnresolvedTypeData::Slice(Box::new(remove_interned_in_unresolved_type(interner, *typ))) + } + UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( + expr, + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Parenthesized(typ) => UnresolvedTypeData::Parenthesized(Box::new( + remove_interned_in_unresolved_type(interner, *typ), + )), + UnresolvedTypeData::Named(path, generic_type_args, is_synthesized) => { + UnresolvedTypeData::Named( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + is_synthesized, + ) + } + UnresolvedTypeData::TraitAsType(path, generic_type_args) => { + UnresolvedTypeData::TraitAsType( + path, + remove_interned_in_generic_type_args(interner, generic_type_args), + ) + } + UnresolvedTypeData::MutableReference(typ) => UnresolvedTypeData::MutableReference( + Box::new(remove_interned_in_unresolved_type(interner, *typ)), + ), + UnresolvedTypeData::Tuple(types) => UnresolvedTypeData::Tuple(vecmap(types, |typ| { + remove_interned_in_unresolved_type(interner, typ) + })), + UnresolvedTypeData::Function(arg_types, ret_type, env_type, unconstrained) => { + UnresolvedTypeData::Function( + vecmap(arg_types, |typ| remove_interned_in_unresolved_type(interner, typ)), + Box::new(remove_interned_in_unresolved_type(interner, *ret_type)), + Box::new(remove_interned_in_unresolved_type(interner, *env_type)), + unconstrained, + ) + } + UnresolvedTypeData::AsTraitPath(as_trait_path) => { + UnresolvedTypeData::AsTraitPath(Box::new(AsTraitPath { + typ: remove_interned_in_unresolved_type(interner, as_trait_path.typ), + trait_generics: remove_interned_in_generic_type_args( + interner, + as_trait_path.trait_generics, + ), + ..*as_trait_path + })) + } + UnresolvedTypeData::Interned(id) => interner.get_unresolved_type_data(id).clone(), + UnresolvedTypeData::FieldElement + | UnresolvedTypeData::Integer(_, _) + | UnresolvedTypeData::Bool + | UnresolvedTypeData::Unit + | UnresolvedTypeData::String(_) + | UnresolvedTypeData::Resolved(_) + | UnresolvedTypeData::Quoted(_) + | UnresolvedTypeData::Expression(_) + | UnresolvedTypeData::Unspecified + | UnresolvedTypeData::Error => typ, + } +} + +fn remove_interned_in_generic_type_args( + interner: &NodeInterner, + args: GenericTypeArgs, +) -> GenericTypeArgs { + GenericTypeArgs { + ordered_args: vecmap(args.ordered_args, |typ| { + remove_interned_in_unresolved_type(interner, typ) + }), + named_args: vecmap(args.named_args, |(name, typ)| { + (name, remove_interned_in_unresolved_type(interner, typ)) + }), + } +} + +// Returns a new Pattern where all Interned Patterns have been turned into Pattern. +fn remove_interned_in_pattern(interner: &NodeInterner, pattern: Pattern) -> Pattern { + match pattern { + Pattern::Identifier(_) => pattern, + Pattern::Mutable(pattern, span, is_synthesized) => Pattern::Mutable( + Box::new(remove_interned_in_pattern(interner, *pattern)), + span, + is_synthesized, + ), + Pattern::Tuple(patterns, span) => Pattern::Tuple( + vecmap(patterns, |pattern| remove_interned_in_pattern(interner, pattern)), + span, + ), + Pattern::Struct(path, patterns, span) => { + let patterns = vecmap(patterns, |(name, pattern)| { + (name, remove_interned_in_pattern(interner, pattern)) + }); + Pattern::Struct(path, patterns, span) + } + Pattern::Interned(id, _) => interner.get_pattern(id).clone(), + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/errors.rs b/compiler/noirc_frontend/src/hir/comptime/errors.rs index 0cb2b37e273..5217bbd1e71 100644 --- a/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -5,12 +5,10 @@ use crate::{ ast::TraitBound, hir::{def_collector::dc_crate::CompilationError, type_check::NoMatchingImplFoundError}, parser::ParserError, - token::Token, Type, }; use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement}; use fm::FileId; -use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Location}; /// The possible errors that can halt the interpreter. @@ -145,7 +143,7 @@ pub enum InterpreterError { }, FailedToParseMacro { error: ParserError, - tokens: Rc>, + tokens: String, rule: &'static str, file: FileId, }, @@ -488,10 +486,9 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { InterpreterError::DebugEvaluateComptime { diagnostic, .. } => diagnostic.clone(), InterpreterError::FailedToParseMacro { error, tokens, rule, file: _ } => { let message = format!("Failed to parse macro's token stream into {rule}"); - let tokens = vecmap(tokens.iter(), ToString::to_string).join(" "); - // 10 is an aribtrary number of tokens here chosen to fit roughly onto one line - let token_stream = if tokens.len() > 10 { + // If it's less than 48 chars, the error message fits in a single line (less than 80 chars total) + let token_stream = if tokens.len() <= 48 && !tokens.contains('\n') { format!("The resulting token stream was: {tokens}") } else { format!( diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 595ca5dedc0..e9c8fbe99a5 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -146,7 +146,7 @@ impl<'local, 'context> Interpreter<'local, 'context> { "modulus_le_bits" => modulus_le_bits(arguments, location), "modulus_le_bytes" => modulus_le_bytes(arguments, location), "modulus_num_bits" => modulus_num_bits(arguments, location), - "quoted_as_expr" => quoted_as_expr(arguments, return_type, location), + "quoted_as_expr" => quoted_as_expr(interner, arguments, return_type, location), "quoted_as_module" => quoted_as_module(self, arguments, return_type, location), "quoted_as_trait_constraint" => quoted_as_trait_constraint(self, arguments, location), "quoted_as_type" => quoted_as_type(self, arguments, location), @@ -672,6 +672,7 @@ fn slice_insert( // fn as_expr(quoted: Quoted) -> Option fn quoted_as_expr( + interner: &NodeInterner, arguments: Vec<(Value, Location)>, return_type: Type, location: Location, @@ -684,7 +685,7 @@ fn quoted_as_expr( let parser = choice((expr_parser, statement_parser, lvalue_parser)); let parser = parser.then_ignore(just(Token::Semicolon).or_not()); - let expr = parse(argument, parser, "an expression").ok(); + let expr = parse(interner, argument, parser, "an expression").ok(); option(return_type, expr) } @@ -698,7 +699,9 @@ fn quoted_as_module( ) -> IResult { let argument = check_one_argument(arguments, location)?; - let path = parse(argument, parser::path_no_turbofish(), "a path").ok(); + let path = + parse(interpreter.elaborator.interner, argument, parser::path_no_turbofish(), "a path") + .ok(); let option_value = path.and_then(|path| { let module = interpreter .elaborate_in_function(interpreter.current_function, |elaborator| { @@ -717,7 +720,12 @@ fn quoted_as_trait_constraint( location: Location, ) -> IResult { let argument = check_one_argument(arguments, location)?; - let trait_bound = parse(argument, parser::trait_bound(), "a trait constraint")?; + let trait_bound = parse( + interpreter.elaborator.interner, + argument, + parser::trait_bound(), + "a trait constraint", + )?; let bound = interpreter .elaborate_in_function(interpreter.current_function, |elaborator| { elaborator.resolve_trait_bound(&trait_bound, Type::Unit) @@ -734,7 +742,7 @@ fn quoted_as_type( location: Location, ) -> IResult { let argument = check_one_argument(arguments, location)?; - let typ = parse(argument, parser::parse_type(), "a type")?; + let typ = parse(interpreter.elaborator.interner, argument, parser::parse_type(), "a type")?; let typ = interpreter .elaborate_in_function(interpreter.current_function, |elab| elab.resolve_type(typ)); Ok(Value::Type(typ)) @@ -2301,6 +2309,7 @@ fn function_def_set_parameters( )?; let parameter_type = get_type((tuple.pop().unwrap(), parameters_argument_location))?; let parameter_pattern = parse( + interpreter.elaborator.interner, (tuple.pop().unwrap(), parameters_argument_location), parser::pattern(), "a pattern", @@ -2403,7 +2412,8 @@ fn module_add_item( let module_data = interpreter.elaborator.get_module(module_id); let parser = parser::top_level_items(); - let top_level_statements = parse(item, parser, "a top-level item")?; + let top_level_statements = + parse(interpreter.elaborator.interner, item, parser, "a top-level item")?; interpreter.elaborate_in_module(module_id, module_data.location.file, |elaborator| { let mut generated_items = CollectedItems::default(); diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 5fe5447da6a..20303e49e15 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -4,6 +4,7 @@ use std::{hash::Hasher, rc::Rc}; use acvm::FieldElement; use noirc_errors::Location; +use crate::hir::comptime::display::tokens_to_string; use crate::lexer::Lexer; use crate::{ ast::{ @@ -403,6 +404,7 @@ pub(super) fn lex(input: &str) -> Vec { } pub(super) fn parse( + interner: &NodeInterner, (value, location): (Value, Location), parser: impl NoirParser, rule: &'static str, @@ -410,18 +412,20 @@ pub(super) fn parse( let parser = parser.then_ignore(chumsky::primitive::end()); let tokens = get_quoted((value, location))?; let quoted = add_token_spans(tokens.clone(), location.span); - parse_tokens(tokens, quoted, location, parser, rule) + parse_tokens(tokens, quoted, interner, location, parser, rule) } pub(super) fn parse_tokens( tokens: Rc>, quoted: Tokens, + interner: &NodeInterner, location: Location, parser: impl NoirParser, rule: &'static str, ) -> IResult { parser.parse(quoted).map_err(|mut errors| { let error = errors.swap_remove(0); + let tokens = tokens_to_string(tokens, interner); InterpreterError::FailedToParseMacro { error, tokens, rule, file: location.file } }) } diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs index 16090c64174..2e2753001b4 100644 --- a/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1,3 +1,4 @@ +mod display; mod errors; mod hir_to_display_ast; mod interpreter; diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 4d0787628cd..f01e188e498 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt::Display, rc::Rc, vec}; +use std::{borrow::Cow, rc::Rc, vec}; use acvm::{AcirField, FieldElement}; use chumsky::Parser; @@ -9,29 +9,26 @@ use strum_macros::Display; use crate::{ ast::{ - ArrayLiteral, AsTraitPath, AssignStatement, BlockExpression, CallExpression, - CastExpression, ConstrainStatement, ConstructorExpression, ForLoopStatement, ForRange, - GenericTypeArgs, Ident, IfExpression, IndexExpression, InfixExpression, IntegerBitSize, - LValue, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, Pattern, - PrefixExpression, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, + ArrayLiteral, BlockExpression, ConstructorExpression, Ident, IntegerBitSize, LValue, + Pattern, Signedness, Statement, StatementKind, UnresolvedType, UnresolvedTypeData, }, hir::{def_map::ModuleId, type_check::generics::TraitGenerics}, - hir_def::{ - expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, - traits::TraitConstraint, - }, + hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, StructId, }, - node_interner::{ExprId, FuncId, InternedStatementKind, StmtId, TraitId, TraitImplId}, + node_interner::{ExprId, FuncId, StmtId, TraitId, TraitImplId}, parser::{self, NoirParser, TopLevelStatement}, token::{SpannedToken, Token, Tokens}, Kind, QuotedType, Shared, Type, TypeBindings, }; use rustc_hash::FxHashMap as HashMap; -use super::errors::{IResult, InterpreterError}; +use super::{ + display::tokens_to_string, + errors::{IResult, InterpreterError}, +}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Value { @@ -270,6 +267,7 @@ impl Value { let error = errors.swap_remove(0); let file = location.file; let rule = "an expression"; + let tokens = tokens_to_string(tokens, interner); Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) } }; @@ -526,8 +524,11 @@ impl Value { location: Location, interner: &NodeInterner, ) -> IResult> { + let parser = parser::top_level_items(); match self { - Value::Quoted(tokens) => parse_tokens(tokens, parser::top_level_items(), location), + Value::Quoted(tokens) => { + parse_tokens(tokens, interner, parser, location, "top-level item") + } _ => { let typ = self.get_type().into_owned(); let value = self.display(interner).to_string(); @@ -535,13 +536,6 @@ impl Value { } } } - - pub fn display<'value, 'interner>( - &'value self, - interner: &'interner NodeInterner, - ) -> ValuePrinter<'value, 'interner> { - ValuePrinter { value: self, interner } - } } /// Unwraps an Rc value without cloning the inner value if the reference count is 1. Clones otherwise. @@ -551,16 +545,18 @@ pub(crate) fn unwrap_rc(rc: Rc) -> T { fn parse_tokens( tokens: Rc>, + interner: &NodeInterner, parser: impl NoirParser, location: Location, + rule: &'static str, ) -> IResult { let parser = parser.then_ignore(chumsky::primitive::end()); match parser.parse(add_token_spans(tokens.clone(), location.span)) { Ok(expr) => Ok(expr), Err(mut errors) => { let error = errors.swap_remove(0); - let rule = "an expression"; let file = location.file; + let tokens = tokens_to_string(tokens, interner); Err(InterpreterError::FailedToParseMacro { error, file, tokens, rule }) } } @@ -570,564 +566,3 @@ pub(crate) fn add_token_spans(tokens: Rc>, span: Span) -> Tokens { let tokens = unwrap_rc(tokens); Tokens(vecmap(tokens, |token| SpannedToken::new(token, span))) } - -pub struct ValuePrinter<'value, 'interner> { - value: &'value Value, - interner: &'interner NodeInterner, -} - -impl<'value, 'interner> Display for ValuePrinter<'value, 'interner> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.value { - Value::Unit => write!(f, "()"), - Value::Bool(value) => { - let msg = if *value { "true" } else { "false" }; - write!(f, "{msg}") - } - Value::Field(value) => write!(f, "{value}"), - Value::I8(value) => write!(f, "{value}"), - Value::I16(value) => write!(f, "{value}"), - Value::I32(value) => write!(f, "{value}"), - Value::I64(value) => write!(f, "{value}"), - Value::U1(value) => write!(f, "{value}"), - Value::U8(value) => write!(f, "{value}"), - Value::U16(value) => write!(f, "{value}"), - Value::U32(value) => write!(f, "{value}"), - Value::U64(value) => write!(f, "{value}"), - Value::String(value) => write!(f, "{value}"), - Value::CtString(value) => write!(f, "{value}"), - Value::FormatString(value, _) => write!(f, "{value}"), - Value::Function(..) => write!(f, "(function)"), - Value::Closure(..) => write!(f, "(closure)"), - Value::Tuple(fields) => { - let fields = vecmap(fields, |field| field.display(self.interner).to_string()); - write!(f, "({})", fields.join(", ")) - } - Value::Struct(fields, typ) => { - let typename = match typ.follow_bindings() { - Type::Struct(def, _) => def.borrow().name.to_string(), - other => other.to_string(), - }; - let fields = vecmap(fields, |(name, value)| { - format!("{}: {}", name, value.display(self.interner)) - }); - write!(f, "{typename} {{ {} }}", fields.join(", ")) - } - Value::Pointer(value, _) => write!(f, "&mut {}", value.borrow().display(self.interner)), - Value::Array(values, _) => { - let values = vecmap(values, |value| value.display(self.interner).to_string()); - write!(f, "[{}]", values.join(", ")) - } - Value::Slice(values, _) => { - let values = vecmap(values, |value| value.display(self.interner).to_string()); - write!(f, "&[{}]", values.join(", ")) - } - Value::Quoted(tokens) => { - write!(f, "quote {{")?; - for token in tokens.iter() { - write!(f, " ")?; - token.display(self.interner).fmt(f)?; - } - write!(f, " }}") - } - Value::StructDefinition(id) => { - let def = self.interner.get_struct(*id); - let def = def.borrow(); - write!(f, "{}", def.name) - } - Value::TraitConstraint(trait_id, generics) => { - let trait_ = self.interner.get_trait(*trait_id); - write!(f, "{}{generics}", trait_.name) - } - Value::TraitDefinition(trait_id) => { - let trait_ = self.interner.get_trait(*trait_id); - write!(f, "{}", trait_.name) - } - Value::TraitImpl(trait_impl_id) => { - let trait_impl = self.interner.get_trait_implementation(*trait_impl_id); - let trait_impl = trait_impl.borrow(); - - let generic_string = - vecmap(&trait_impl.trait_generics, ToString::to_string).join(", "); - let generic_string = if generic_string.is_empty() { - generic_string - } else { - format!("<{}>", generic_string) - }; - - let where_clause = vecmap(&trait_impl.where_clause, |trait_constraint| { - display_trait_constraint(self.interner, trait_constraint) - }); - let where_clause = where_clause.join(", "); - let where_clause = if where_clause.is_empty() { - where_clause - } else { - format!(" where {}", where_clause) - }; - - write!( - f, - "impl {}{} for {}{}", - trait_impl.ident, generic_string, trait_impl.typ, where_clause - ) - } - Value::FunctionDefinition(function_id) => { - write!(f, "{}", self.interner.function_name(function_id)) - } - Value::ModuleDefinition(module_id) => { - if let Some(attributes) = self.interner.try_module_attributes(module_id) { - write!(f, "{}", &attributes.name) - } else { - write!(f, "(crate root)") - } - } - Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), - Value::Type(typ) => write!(f, "{}", typ), - Value::Expr(ExprValue::Expression(expr)) => { - write!(f, "{}", remove_interned_in_expression_kind(self.interner, expr.clone())) - } - Value::Expr(ExprValue::Statement(statement)) => { - write!(f, "{}", remove_interned_in_statement_kind(self.interner, statement.clone())) - } - Value::Expr(ExprValue::LValue(lvalue)) => { - write!(f, "{}", remove_interned_in_lvalue(self.interner, lvalue.clone())) - } - Value::Expr(ExprValue::Pattern(pattern)) => { - write!(f, "{}", remove_interned_in_pattern(self.interner, pattern.clone())) - } - Value::TypedExpr(TypedExpr::ExprId(id)) => { - let hir_expr = self.interner.expression(id); - let expr = hir_expr.to_display_ast(self.interner, Span::default()); - write!(f, "{}", expr.kind) - } - Value::TypedExpr(TypedExpr::StmtId(id)) => { - let hir_statement = self.interner.statement(id); - let stmt = hir_statement.to_display_ast(self.interner, Span::default()); - write!(f, "{}", stmt.kind) - } - Value::UnresolvedType(typ) => { - write!(f, "{}", remove_interned_in_unresolved_type_data(self.interner, typ.clone())) - } - } - } -} - -impl Token { - pub fn display<'token, 'interner>( - &'token self, - interner: &'interner NodeInterner, - ) -> TokenPrinter<'token, 'interner> { - TokenPrinter { token: self, interner } - } -} - -pub struct TokenPrinter<'token, 'interner> { - token: &'token Token, - interner: &'interner NodeInterner, -} - -impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.token { - Token::QuotedType(id) => { - write!(f, "{}", self.interner.get_quoted_type(*id)) - } - Token::InternedExpr(id) => { - let value = Value::expression(ExpressionKind::Interned(*id)); - value.display(self.interner).fmt(f) - } - Token::InternedStatement(id) => { - let value = Value::statement(StatementKind::Interned(*id)); - value.display(self.interner).fmt(f) - } - Token::InternedLValue(id) => { - let value = Value::lvalue(LValue::Interned(*id, Span::default())); - value.display(self.interner).fmt(f) - } - Token::InternedUnresolvedTypeData(id) => { - let value = Value::UnresolvedType(UnresolvedTypeData::Interned(*id)); - value.display(self.interner).fmt(f) - } - Token::InternedPattern(id) => { - let value = Value::pattern(Pattern::Interned(*id, Span::default())); - value.display(self.interner).fmt(f) - } - Token::UnquoteMarker(id) => { - let value = Value::TypedExpr(TypedExpr::ExprId(*id)); - value.display(self.interner).fmt(f) - } - other => write!(f, "{other}"), - } - } -} - -fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String { - let trait_ = interner.get_trait(trait_constraint.trait_id); - format!("{}: {}{}", trait_constraint.typ, trait_.name, trait_constraint.trait_generics) -} - -// Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. -fn remove_interned_in_expression(interner: &NodeInterner, expr: Expression) -> Expression { - Expression { kind: remove_interned_in_expression_kind(interner, expr.kind), span: expr.span } -} - -// Returns a new ExpressionKind where all Interned and Resolved expressions have been turned into non-interned ExpressionKind. -fn remove_interned_in_expression_kind( - interner: &NodeInterner, - expr: ExpressionKind, -) -> ExpressionKind { - match expr { - ExpressionKind::Literal(literal) => { - ExpressionKind::Literal(remove_interned_in_literal(interner, literal)) - } - ExpressionKind::Block(block) => { - let statements = - vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Block(BlockExpression { statements }) - } - ExpressionKind::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { - rhs: remove_interned_in_expression(interner, prefix.rhs), - ..*prefix - })), - ExpressionKind::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { - collection: remove_interned_in_expression(interner, index.collection), - index: remove_interned_in_expression(interner, index.index), - })), - ExpressionKind::Call(call) => ExpressionKind::Call(Box::new(CallExpression { - func: Box::new(remove_interned_in_expression(interner, *call.func)), - arguments: vecmap(call.arguments, |arg| remove_interned_in_expression(interner, arg)), - ..*call - })), - ExpressionKind::MethodCall(call) => { - ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object: remove_interned_in_expression(interner, call.object), - arguments: vecmap(call.arguments, |arg| { - remove_interned_in_expression(interner, arg) - }), - ..*call - })) - } - ExpressionKind::Constructor(constructor) => { - ExpressionKind::Constructor(Box::new(ConstructorExpression { - fields: vecmap(constructor.fields, |(name, expr)| { - (name, remove_interned_in_expression(interner, expr)) - }), - ..*constructor - })) - } - ExpressionKind::MemberAccess(member_access) => { - ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: remove_interned_in_expression(interner, member_access.lhs), - ..*member_access - })) - } - ExpressionKind::Cast(cast) => ExpressionKind::Cast(Box::new(CastExpression { - lhs: remove_interned_in_expression(interner, cast.lhs), - ..*cast - })), - ExpressionKind::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { - lhs: remove_interned_in_expression(interner, infix.lhs), - rhs: remove_interned_in_expression(interner, infix.rhs), - ..*infix - })), - ExpressionKind::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { - condition: remove_interned_in_expression(interner, if_expr.condition), - consequence: remove_interned_in_expression(interner, if_expr.consequence), - alternative: if_expr - .alternative - .map(|alternative| remove_interned_in_expression(interner, alternative)), - })), - ExpressionKind::Variable(_) => expr, - ExpressionKind::Tuple(expressions) => ExpressionKind::Tuple(vecmap(expressions, |expr| { - remove_interned_in_expression(interner, expr) - })), - ExpressionKind::Lambda(lambda) => ExpressionKind::Lambda(Box::new(Lambda { - body: remove_interned_in_expression(interner, lambda.body), - ..*lambda - })), - ExpressionKind::Parenthesized(expr) => { - ExpressionKind::Parenthesized(Box::new(remove_interned_in_expression(interner, *expr))) - } - ExpressionKind::Quote(_) => expr, - ExpressionKind::Unquote(expr) => { - ExpressionKind::Unquote(Box::new(remove_interned_in_expression(interner, *expr))) - } - ExpressionKind::Comptime(block, span) => { - let statements = - vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Comptime(BlockExpression { statements }, span) - } - ExpressionKind::Unsafe(block, span) => { - let statements = - vecmap(block.statements, |stmt| remove_interned_in_statement(interner, stmt)); - ExpressionKind::Unsafe(BlockExpression { statements }, span) - } - ExpressionKind::AsTraitPath(mut path) => { - path.typ = remove_interned_in_unresolved_type(interner, path.typ); - path.trait_generics = - remove_interned_in_generic_type_args(interner, path.trait_generics); - ExpressionKind::AsTraitPath(path) - } - ExpressionKind::TypePath(mut path) => { - path.typ = remove_interned_in_unresolved_type(interner, path.typ); - path.turbofish = remove_interned_in_generic_type_args(interner, path.turbofish); - ExpressionKind::TypePath(path) - } - ExpressionKind::Resolved(id) => { - let expr = interner.expression(&id); - expr.to_display_ast(interner, Span::default()).kind - } - ExpressionKind::Interned(id) => { - let expr = interner.get_expression_kind(id).clone(); - remove_interned_in_expression_kind(interner, expr) - } - ExpressionKind::Error => expr, - ExpressionKind::InternedStatement(id) => remove_interned_in_statement_expr(interner, id), - } -} - -fn remove_interned_in_statement_expr( - interner: &NodeInterner, - id: InternedStatementKind, -) -> ExpressionKind { - let expr = match interner.get_statement_kind(id).clone() { - StatementKind::Expression(expr) | StatementKind::Semi(expr) => expr.kind, - StatementKind::Interned(id) => remove_interned_in_statement_expr(interner, id), - _ => ExpressionKind::Error, - }; - remove_interned_in_expression_kind(interner, expr) -} - -fn remove_interned_in_literal(interner: &NodeInterner, literal: Literal) -> Literal { - match literal { - Literal::Array(array_literal) => { - Literal::Array(remove_interned_in_array_literal(interner, array_literal)) - } - Literal::Slice(array_literal) => { - Literal::Array(remove_interned_in_array_literal(interner, array_literal)) - } - Literal::Bool(_) - | Literal::Integer(_, _) - | Literal::Str(_) - | Literal::RawStr(_, _) - | Literal::FmtStr(_) - | Literal::Unit => literal, - } -} - -fn remove_interned_in_array_literal( - interner: &NodeInterner, - literal: ArrayLiteral, -) -> ArrayLiteral { - match literal { - ArrayLiteral::Standard(expressions) => { - ArrayLiteral::Standard(vecmap(expressions, |expr| { - remove_interned_in_expression(interner, expr) - })) - } - ArrayLiteral::Repeated { repeated_element, length } => ArrayLiteral::Repeated { - repeated_element: Box::new(remove_interned_in_expression(interner, *repeated_element)), - length: Box::new(remove_interned_in_expression(interner, *length)), - }, - } -} - -// Returns a new Statement where all Interned statements have been turned into non-interned StatementKind. -fn remove_interned_in_statement(interner: &NodeInterner, statement: Statement) -> Statement { - Statement { - kind: remove_interned_in_statement_kind(interner, statement.kind), - span: statement.span, - } -} - -// Returns a new StatementKind where all Interned statements have been turned into non-interned StatementKind. -fn remove_interned_in_statement_kind( - interner: &NodeInterner, - statement: StatementKind, -) -> StatementKind { - match statement { - StatementKind::Let(let_statement) => StatementKind::Let(LetStatement { - pattern: remove_interned_in_pattern(interner, let_statement.pattern), - expression: remove_interned_in_expression(interner, let_statement.expression), - r#type: remove_interned_in_unresolved_type(interner, let_statement.r#type), - ..let_statement - }), - StatementKind::Constrain(constrain) => StatementKind::Constrain(ConstrainStatement( - remove_interned_in_expression(interner, constrain.0), - constrain.1.map(|expr| remove_interned_in_expression(interner, expr)), - constrain.2, - )), - StatementKind::Expression(expr) => { - StatementKind::Expression(remove_interned_in_expression(interner, expr)) - } - StatementKind::Assign(assign) => StatementKind::Assign(AssignStatement { - lvalue: assign.lvalue, - expression: remove_interned_in_expression(interner, assign.expression), - }), - StatementKind::For(for_loop) => StatementKind::For(ForLoopStatement { - range: match for_loop.range { - ForRange::Range(from, to) => ForRange::Range( - remove_interned_in_expression(interner, from), - remove_interned_in_expression(interner, to), - ), - ForRange::Array(expr) => { - ForRange::Array(remove_interned_in_expression(interner, expr)) - } - }, - block: remove_interned_in_expression(interner, for_loop.block), - ..for_loop - }), - StatementKind::Comptime(statement) => { - StatementKind::Comptime(Box::new(remove_interned_in_statement(interner, *statement))) - } - StatementKind::Semi(expr) => { - StatementKind::Semi(remove_interned_in_expression(interner, expr)) - } - StatementKind::Interned(id) => { - let statement = interner.get_statement_kind(id).clone(); - remove_interned_in_statement_kind(interner, statement) - } - StatementKind::Break | StatementKind::Continue | StatementKind::Error => statement, - } -} - -// Returns a new LValue where all Interned LValues have been turned into LValue. -fn remove_interned_in_lvalue(interner: &NodeInterner, lvalue: LValue) -> LValue { - match lvalue { - LValue::Ident(_) => lvalue, - LValue::MemberAccess { object, field_name, span } => LValue::MemberAccess { - object: Box::new(remove_interned_in_lvalue(interner, *object)), - field_name, - span, - }, - LValue::Index { array, index, span } => LValue::Index { - array: Box::new(remove_interned_in_lvalue(interner, *array)), - index: remove_interned_in_expression(interner, index), - span, - }, - LValue::Dereference(lvalue, span) => { - LValue::Dereference(Box::new(remove_interned_in_lvalue(interner, *lvalue)), span) - } - LValue::Interned(id, span) => { - let lvalue = interner.get_lvalue(id, span); - remove_interned_in_lvalue(interner, lvalue) - } - } -} - -fn remove_interned_in_unresolved_type( - interner: &NodeInterner, - typ: UnresolvedType, -) -> UnresolvedType { - UnresolvedType { - typ: remove_interned_in_unresolved_type_data(interner, typ.typ), - span: typ.span, - } -} - -fn remove_interned_in_unresolved_type_data( - interner: &NodeInterner, - typ: UnresolvedTypeData, -) -> UnresolvedTypeData { - match typ { - UnresolvedTypeData::Array(expr, typ) => UnresolvedTypeData::Array( - expr, - Box::new(remove_interned_in_unresolved_type(interner, *typ)), - ), - UnresolvedTypeData::Slice(typ) => { - UnresolvedTypeData::Slice(Box::new(remove_interned_in_unresolved_type(interner, *typ))) - } - UnresolvedTypeData::FormatString(expr, typ) => UnresolvedTypeData::FormatString( - expr, - Box::new(remove_interned_in_unresolved_type(interner, *typ)), - ), - UnresolvedTypeData::Parenthesized(typ) => UnresolvedTypeData::Parenthesized(Box::new( - remove_interned_in_unresolved_type(interner, *typ), - )), - UnresolvedTypeData::Named(path, generic_type_args, is_synthesized) => { - UnresolvedTypeData::Named( - path, - remove_interned_in_generic_type_args(interner, generic_type_args), - is_synthesized, - ) - } - UnresolvedTypeData::TraitAsType(path, generic_type_args) => { - UnresolvedTypeData::TraitAsType( - path, - remove_interned_in_generic_type_args(interner, generic_type_args), - ) - } - UnresolvedTypeData::MutableReference(typ) => UnresolvedTypeData::MutableReference( - Box::new(remove_interned_in_unresolved_type(interner, *typ)), - ), - UnresolvedTypeData::Tuple(types) => UnresolvedTypeData::Tuple(vecmap(types, |typ| { - remove_interned_in_unresolved_type(interner, typ) - })), - UnresolvedTypeData::Function(arg_types, ret_type, env_type, unconstrained) => { - UnresolvedTypeData::Function( - vecmap(arg_types, |typ| remove_interned_in_unresolved_type(interner, typ)), - Box::new(remove_interned_in_unresolved_type(interner, *ret_type)), - Box::new(remove_interned_in_unresolved_type(interner, *env_type)), - unconstrained, - ) - } - UnresolvedTypeData::AsTraitPath(as_trait_path) => { - UnresolvedTypeData::AsTraitPath(Box::new(AsTraitPath { - typ: remove_interned_in_unresolved_type(interner, as_trait_path.typ), - trait_generics: remove_interned_in_generic_type_args( - interner, - as_trait_path.trait_generics, - ), - ..*as_trait_path - })) - } - UnresolvedTypeData::Interned(id) => interner.get_unresolved_type_data(id).clone(), - UnresolvedTypeData::FieldElement - | UnresolvedTypeData::Integer(_, _) - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Unit - | UnresolvedTypeData::String(_) - | UnresolvedTypeData::Resolved(_) - | UnresolvedTypeData::Quoted(_) - | UnresolvedTypeData::Expression(_) - | UnresolvedTypeData::Unspecified - | UnresolvedTypeData::Error => typ, - } -} - -fn remove_interned_in_generic_type_args( - interner: &NodeInterner, - args: GenericTypeArgs, -) -> GenericTypeArgs { - GenericTypeArgs { - ordered_args: vecmap(args.ordered_args, |typ| { - remove_interned_in_unresolved_type(interner, typ) - }), - named_args: vecmap(args.named_args, |(name, typ)| { - (name, remove_interned_in_unresolved_type(interner, typ)) - }), - } -} - -// Returns a new Pattern where all Interned Patterns have been turned into Pattern. -fn remove_interned_in_pattern(interner: &NodeInterner, pattern: Pattern) -> Pattern { - match pattern { - Pattern::Identifier(_) => pattern, - Pattern::Mutable(pattern, span, is_synthesized) => Pattern::Mutable( - Box::new(remove_interned_in_pattern(interner, *pattern)), - span, - is_synthesized, - ), - Pattern::Tuple(patterns, span) => Pattern::Tuple( - vecmap(patterns, |pattern| remove_interned_in_pattern(interner, pattern)), - span, - ), - Pattern::Struct(path, patterns, span) => { - let patterns = vecmap(patterns, |(name, pattern)| { - (name, remove_interned_in_pattern(interner, pattern)) - }); - Pattern::Struct(path, patterns, span) - } - Pattern::Interned(id, _) => interner.get_pattern(id).clone(), - } -}