diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 300a0592e39..d0b33945f40 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -104,7 +104,7 @@ pub(super) fn abi_type_from_hir_type(context: &Context, typ: &Type) -> AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 3492df73103..a1ae349b537 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -117,7 +117,7 @@ pub enum UnresolvedTypeData { ), // The type of quoted code for metaprogramming - Expr, + Quoted(crate::QuotedType), Unspecified, // This is for when the user declares a variable without specifying it's type Error, @@ -216,7 +216,7 @@ impl std::fmt::Display for UnresolvedTypeData { } } MutableReference(element) => write!(f, "&mut {element}"), - Expr => write!(f, "Expr"), + Quoted(quoted) => write!(f, "{}", quoted), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/compiler/noirc_frontend/src/elaborator/expressions.rs b/compiler/noirc_frontend/src/elaborator/expressions.rs index a57690878dc..d6ad752b67a 100644 --- a/compiler/noirc_frontend/src/elaborator/expressions.rs +++ b/compiler/noirc_frontend/src/elaborator/expressions.rs @@ -28,7 +28,7 @@ use crate::{ MethodCallExpression, PrefixExpression, }, node_interner::{DefinitionKind, ExprId, FuncId}, - Shared, StructType, Type, + QuotedType, Shared, StructType, Type, }; use super::Elaborator; @@ -650,7 +650,7 @@ impl<'context> Elaborator<'context> { let mut unquoted_exprs = Vec::new(); self.find_unquoted_exprs_in_block(&mut block, &mut unquoted_exprs); let quoted = HirQuoted { quoted_block: block, unquoted_exprs }; - (HirExpression::Quote(quoted), Type::Expr) + (HirExpression::Quote(quoted), Type::Quoted(QuotedType::Expr)) } fn elaborate_comptime_block(&mut self, block: BlockExpression, span: Span) -> (ExprId, Type) { @@ -716,9 +716,8 @@ impl<'context> Elaborator<'context> { location: Location, return_type: Type, ) -> Option<(HirExpression, Type)> { - self.unify(&return_type, &Type::Expr, || TypeCheckError::MacroReturningNonExpr { - typ: return_type.clone(), - span: location.span, + self.unify(&return_type, &Type::Quoted(QuotedType::Expr), || { + TypeCheckError::MacroReturningNonExpr { typ: return_type.clone(), span: location.span } }); let function = match self.try_get_comptime_function(func, location) { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 0b7e5a58849..053de6e1735 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ ast::{FunctionKind, UnresolvedTraitConstraint}, hir::{ - comptime::{self, Interpreter}, + comptime::{self, Interpreter, Value}, def_collector::{ dc_crate::{ filter_literal_globals, CompilationError, ImplMap, UnresolvedGlobal, @@ -205,10 +205,22 @@ impl<'context> Elaborator<'context> { pub fn elaborate( context: &'context mut Context, crate_id: CrateId, - mut items: CollectedItems, + items: CollectedItems, ) -> Vec<(CompilationError, FileId)> { let mut this = Self::new(context, crate_id); + // Filter out comptime items to execute their functions first if needed. + // This step is why comptime items can only refer to other comptime items + // in the same crate, but can refer to any item in dependencies. Trying to + // run these at the same time as other items would lead to them seeing empty + // function bodies from functions that have yet to be elaborated. + let (comptime_items, runtime_items) = Self::filter_comptime_items(items); + this.elaborate_items(comptime_items); + this.elaborate_items(runtime_items); + this.errors + } + + fn elaborate_items(&mut self, mut items: CollectedItems) { // We must first resolve and intern the globals before we can resolve any stmts inside each function. // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope // @@ -216,58 +228,56 @@ impl<'context> Elaborator<'context> { // the values of integer globals as numeric generics. let (literal_globals, non_literal_globals) = filter_literal_globals(items.globals); for global in non_literal_globals { - this.unresolved_globals.insert(global.global_id, global); + self.unresolved_globals.insert(global.global_id, global); } for global in literal_globals { - this.elaborate_global(global); + self.elaborate_global(global); } for (alias_id, alias) in items.type_aliases { - this.define_type_alias(alias_id, alias); + self.define_type_alias(alias_id, alias); } - this.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); - this.collect_traits(items.traits); + self.define_function_metas(&mut items.functions, &mut items.impls, &mut items.trait_impls); + self.collect_traits(items.traits); // Must resolve structs before we resolve globals. - this.collect_struct_definitions(items.types); + self.collect_struct_definitions(items.types); // Before we resolve any function symbols we must go through our impls and // re-collect the methods within into their proper module. This cannot be // done during def collection since we need to be able to resolve the type of // the impl since that determines the module we should collect into. for ((_self_type, module), impls) in &mut items.impls { - this.collect_impls(*module, impls); + self.collect_impls(*module, impls); } // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { - this.collect_trait_impl(trait_impl); + self.collect_trait_impl(trait_impl); } // We must wait to resolve non-literal globals until after we resolve structs since struct // globals will need to reference the struct type they're initialized to ensure they are valid. - while let Some((_, global)) = this.unresolved_globals.pop_first() { - this.elaborate_global(global); + while let Some((_, global)) = self.unresolved_globals.pop_first() { + self.elaborate_global(global); } for functions in items.functions { - this.elaborate_functions(functions); + self.elaborate_functions(functions); } for impls in items.impls.into_values() { - this.elaborate_impls(impls); + self.elaborate_impls(impls); } for trait_impl in items.trait_impls { - this.elaborate_trait_impl(trait_impl); + self.elaborate_trait_impl(trait_impl); } - let cycle_errors = this.interner.check_for_dependency_cycles(); - this.errors.extend(cycle_errors); - this.errors + self.errors.extend(self.interner.check_for_dependency_cycles()); } /// Runs `f` and if it modifies `self.generics`, `self.generics` is truncated @@ -1085,15 +1095,20 @@ impl<'context> Elaborator<'context> { // Resolve each field in each struct. // Each struct should already be present in the NodeInterner after def collection. - for (type_id, typ) in structs { + for (type_id, mut typ) in structs { self.file = typ.file_id; self.local_module = typ.module_id; + + let attributes = std::mem::take(&mut typ.struct_def.attributes); + let span = typ.struct_def.span; let (generics, fields) = self.resolve_struct_fields(typ.struct_def, type_id); self.interner.update_struct(type_id, |struct_def| { struct_def.set_fields(fields); struct_def.generics = generics; }); + + self.run_comptime_attributes_on_struct(attributes, type_id, span); } // Check whether the struct fields have nested slices @@ -1117,6 +1132,38 @@ impl<'context> Elaborator<'context> { } } + fn run_comptime_attributes_on_struct( + &mut self, + attributes: Vec, + struct_id: StructId, + span: Span, + ) { + for attribute in attributes { + if let SecondaryAttribute::Custom(name) = attribute { + match self.lookup_global(Path::from_single(name, span)) { + Ok(id) => { + let definition = self.interner.definition(id); + if let DefinitionKind::Function(function) = &definition.kind { + let function = *function; + let mut interpreter = + Interpreter::new(self.interner, &mut self.comptime_scopes); + + let location = Location::new(span, self.file); + let arguments = vec![(Value::TypeDefinition(struct_id), location)]; + let result = interpreter.call_function(function, arguments, location); + if let Err(error) = result { + self.errors.push(error.into_compilation_error_pair()); + } + } else { + self.push_err(ResolverError::NonFunctionInAnnotation { span }); + } + } + Err(_) => self.push_err(ResolverError::UnknownAnnotation { span }), + } + } + } + } + pub fn resolve_struct_fields( &mut self, unresolved: NoirStruct, @@ -1261,4 +1308,52 @@ impl<'context> Elaborator<'context> { }); } } + + /// Filters out comptime items from non-comptime items. + /// Returns a pair of (comptime items, non-comptime items) + fn filter_comptime_items(mut items: CollectedItems) -> (CollectedItems, CollectedItems) { + let mut function_sets = Vec::with_capacity(items.functions.len()); + let mut comptime_function_sets = Vec::new(); + + for function_set in items.functions { + let mut functions = Vec::with_capacity(function_set.functions.len()); + let mut comptime_functions = Vec::new(); + + for function in function_set.functions { + if function.2.def.is_comptime { + comptime_functions.push(function); + } else { + functions.push(function); + } + } + + let file_id = function_set.file_id; + let self_type = function_set.self_type; + let trait_id = function_set.trait_id; + + if !comptime_functions.is_empty() { + comptime_function_sets.push(UnresolvedFunctions { + functions: comptime_functions, + file_id, + trait_id, + self_type: self_type.clone(), + }); + } + + function_sets.push(UnresolvedFunctions { functions, file_id, trait_id, self_type }); + } + + let comptime = CollectedItems { + functions: comptime_function_sets, + types: BTreeMap::new(), + type_aliases: BTreeMap::new(), + traits: BTreeMap::new(), + trait_impls: Vec::new(), + globals: Vec::new(), + impls: std::collections::HashMap::new(), + }; + + items.functions = function_sets; + (comptime, items) + } } diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index e3f198e68bd..fcb7ac94c26 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -72,7 +72,7 @@ impl<'context> Elaborator<'context> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Expr => Type::Expr, + Quoted(quoted) => Type::Quoted(quoted), Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1398,7 +1398,7 @@ impl<'context> Elaborator<'context> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 07da3398593..21b6897f5dd 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -104,18 +104,18 @@ impl<'a> Interpreter<'a> { "array_len" => builtin::array_len(&arguments), "as_slice" => builtin::as_slice(arguments), _ => { - let item = format!("Evaluation for builtin function {builtin}"); + let item = format!("Comptime evaluation for builtin function {builtin}"); Err(InterpreterError::Unimplemented { item, location }) } } } else if let Some(foreign) = func_attrs.foreign() { - let item = format!("Evaluation for foreign functions like {foreign}"); + let item = format!("Comptime evaluation for foreign functions like {foreign}"); Err(InterpreterError::Unimplemented { item, location }) } else if let Some(oracle) = func_attrs.oracle() { if oracle == "print" { self.print_oracle(arguments) } else { - let item = format!("Evaluation for oracle functions like {oracle}"); + let item = format!("Comptime evaluation for oracle functions like {oracle}"); Err(InterpreterError::Unimplemented { item, location }) } } else { diff --git a/compiler/noirc_frontend/src/hir/comptime/value.rs b/compiler/noirc_frontend/src/hir/comptime/value.rs index 5d91b151218..a75d4fd2b24 100644 --- a/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -12,9 +12,10 @@ use crate::{ hir_def::expr::{HirArrayLiteral, HirConstructorExpression, HirIdent, HirLambda, ImplKind}, macros_api::{ Expression, ExpressionKind, HirExpression, HirLiteral, Literal, NodeInterner, Path, + StructId, }, node_interner::{ExprId, FuncId}, - Shared, Type, + QuotedType, Shared, Type, }; use rustc_hash::FxHashMap as HashMap; @@ -42,6 +43,7 @@ pub enum Value { Array(Vector, Type), Slice(Vector, Type), Code(Rc), + TypeDefinition(StructId), } impl Value { @@ -70,7 +72,8 @@ impl Value { Value::Struct(_, typ) => return Cow::Borrowed(typ), Value::Array(_, typ) => return Cow::Borrowed(typ), Value::Slice(_, typ) => return Cow::Borrowed(typ), - Value::Code(_) => Type::Expr, + Value::Code(_) => Type::Quoted(QuotedType::Expr), + Value::TypeDefinition(_) => Type::Quoted(QuotedType::TypeDefinition), Value::Pointer(element) => { let element = element.borrow().get_type().into_owned(); Type::MutableReference(Box::new(element)) @@ -172,7 +175,7 @@ impl Value { ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(elements))) } Value::Code(block) => ExpressionKind::Block(unwrap_rc(block)), - Value::Pointer(_) => { + Value::Pointer(_) | Value::TypeDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -273,7 +276,7 @@ impl Value { HirExpression::Literal(HirLiteral::Slice(HirArrayLiteral::Standard(elements))) } Value::Code(block) => HirExpression::Unquote(unwrap_rc(block)), - Value::Pointer(_) => { + Value::Pointer(_) | Value::TypeDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } }; @@ -348,7 +351,8 @@ impl Display for Value { let values = vecmap(values, ToString::to_string); write!(f, "&[{}]", values.join(", ")) } - Value::Code(_) => todo!(), + Value::Code(block) => write!(f, "quote {block}"), + Value::TypeDefinition(_) => write!(f, "(type definition)"), } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 2c6461cb77c..dad0149ec73 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -106,6 +106,10 @@ pub enum ResolverError { InvalidSyntaxInMacroCall { span: Span }, #[error("Macros must be comptime functions")] MacroIsNotComptime { span: Span }, + #[error("Annotation name must refer to a comptime function")] + NonFunctionInAnnotation { span: Span }, + #[error("Unknown annotation")] + UnknownAnnotation { span: Span }, } impl ResolverError { @@ -411,13 +415,27 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *span, ) }, - ResolverError::MacroIsNotComptime{ span } => { + ResolverError::MacroIsNotComptime { span } => { Diagnostic::simple_error( "This macro call is to a non-comptime function".into(), "Macro calls must be to comptime functions".into(), *span, ) }, + ResolverError::NonFunctionInAnnotation { span } => { + Diagnostic::simple_error( + "Unknown annotation".into(), + "The name of an annotation must refer to a comptime function".into(), + *span, + ) + }, + ResolverError::UnknownAnnotation { span } => { + Diagnostic::simple_warning( + "Unknown annotation".into(), + "No matching comptime function found in scope".into(), + *span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f8db9730f88..1dcace82372 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -570,7 +570,7 @@ impl<'a> Resolver<'a> { let fields = self.resolve_type_inner(*fields); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } - Expr => Type::Expr, + Quoted(quoted) => Type::Quoted(quoted), Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, @@ -1159,7 +1159,7 @@ impl<'a> Resolver<'a> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Forall(_, _) => (), Type::TraitAsType(_, _, args) => { diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 503bbbf79be..46e8db8f5ff 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -307,7 +307,7 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } - HirExpression::Quote(_) => Type::Expr, + HirExpression::Quote(_) => Type::Quoted(crate::QuotedType::Expr), HirExpression::Comptime(block) => self.check_block(block), // Unquote should be inserted & removed by the comptime interpreter. diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a90bd931496..ce36a22cf88 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -105,7 +105,7 @@ pub enum Type { Constant(u32), /// The type of quoted code in macros. This is always a comptime-only type - Expr, + Quoted(QuotedType), /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with @@ -147,7 +147,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } @@ -187,6 +187,14 @@ impl Type { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub enum QuotedType { + Expr, + TypeDefinition, + TopLevelItem, + Type, +} + /// A list of TypeVariableIds to bind to a type. Storing the /// TypeVariable in addition to the matching TypeVariableId allows /// the binding to later be undone if needed. @@ -644,7 +652,7 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::Forall(_, _) - | Type::Expr => false, + | Type::Quoted(_) => false, Type::TraitAsType(_, _, args) => { args.iter().any(|generic| generic.contains_numeric_typevar(target_id)) @@ -710,7 +718,7 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::Expr + | Type::Quoted(_) | Type::Slice(_) | Type::TraitAsType(..) => false, @@ -759,7 +767,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) // TODO: probably can allow code as it is all compile time - | Type::Expr + | Type::Quoted(_) | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { @@ -922,7 +930,7 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } - Type::Expr => write!(f, "Expr"), + Type::Quoted(quoted) => write!(f, "{}", quoted), } } } @@ -954,6 +962,17 @@ impl std::fmt::Display for TypeBinding { } } +impl std::fmt::Display for QuotedType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QuotedType::Expr => write!(f, "Expr"), + QuotedType::TypeDefinition => write!(f, "TypeDefinition"), + QuotedType::TopLevelItem => write!(f, "TopLevelItem"), + QuotedType::Type => write!(f, "Type"), + } + } +} + pub struct UnificationError; impl Type { @@ -1643,7 +1662,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Expr + | Type::Quoted(_) | Type::Unit => self.clone(), } } @@ -1686,7 +1705,7 @@ impl Type { | Type::Bool | Type::Constant(_) | Type::Error - | Type::Expr + | Type::Quoted(_) | Type::Unit => false, } } @@ -1743,7 +1762,9 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Expr | Error => self.clone(), + FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Quoted(_) | Error => { + self.clone() + } } } @@ -1878,7 +1899,7 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } - Type::Expr => unreachable!(), + Type::Quoted(_) => unreachable!(), } } } @@ -1963,7 +1984,7 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } - Type::Expr => write!(f, "Expr"), + Type::Quoted(quoted) => write!(f, "{}", quoted), } } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index d204191796c..ec589a816f6 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -865,8 +865,11 @@ pub enum Keyword { String, Struct, Super, + TopLevelItem, Trait, Type, + TypeType, + TypeDefinition, Unchecked, Unconstrained, Use, @@ -910,8 +913,11 @@ impl fmt::Display for Keyword { Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), Keyword::Super => write!(f, "super"), + Keyword::TopLevelItem => write!(f, "TopLevelItem"), Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), + Keyword::TypeType => write!(f, "Type"), + Keyword::TypeDefinition => write!(f, "TypeDefinition"), Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), @@ -958,8 +964,11 @@ impl Keyword { "str" => Keyword::String, "struct" => Keyword::Struct, "super" => Keyword::Super, + "TopLevelItem" => Keyword::TopLevelItem, "trait" => Keyword::Trait, "type" => Keyword::Type, + "Type" => Keyword::TypeType, + "TypeDefinition" => Keyword::TypeDefinition, "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index c6cc1344aae..821ae7e7c4c 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1012,7 +1012,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } - HirType::Expr => unreachable!("Tried to translate Code type into runtime code"), + HirType::Quoted(_) => unreachable!("Tried to translate Code type into runtime code"), }) } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5fdce80087a..915093df514 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -17,6 +17,7 @@ use crate::hir::comptime; use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::QuotedType; use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; @@ -1805,7 +1806,7 @@ enum TypeMethodKey { Tuple, Function, Generic, - Code, + Quoted(QuotedType), } fn get_type_method_key(typ: &Type) -> Option { @@ -1825,7 +1826,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), Type::NamedGeneric(_, _) => Some(Generic), - Type::Expr => Some(Code), + Type::Quoted(quoted) => Some(Quoted(*quoted)), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 40edc9936d1..a961b32f9d7 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -3,6 +3,7 @@ use super::{ ParserErrorReason, Precedence, }; use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; +use crate::QuotedType; use crate::parser::labels::ParsingRuleLabel; use crate::token::{Keyword, Token}; @@ -23,6 +24,9 @@ pub(super) fn parse_type_inner<'a>( bool_type(), string_type(), expr_type(), + type_definition_type(), + top_level_item_type(), + quoted_type(), format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), @@ -69,7 +73,29 @@ pub(super) fn bool_type() -> impl NoirParser { /// This is the type `Expr` - the type of a quoted, untyped expression object used for macros pub(super) fn expr_type() -> impl NoirParser { - keyword(Keyword::Expr).map_with_span(|_, span| UnresolvedTypeData::Expr.with_span(span)) + keyword(Keyword::Expr) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Expr).with_span(span)) +} + +/// This is the type `TypeDefinition` - the type of a quoted type definition +pub(super) fn type_definition_type() -> impl NoirParser { + keyword(Keyword::TypeDefinition).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::TypeDefinition).with_span(span) + }) +} + +/// This is the type `TopLevelItem` - the type of a quoted statement in the top level. +/// E.g. a type definition, trait definition, trait impl, function, etc. +fn top_level_item_type() -> impl NoirParser { + keyword(Keyword::TopLevelItem).map_with_span(|_, span| { + UnresolvedTypeData::Quoted(QuotedType::TopLevelItem).with_span(span) + }) +} + +/// This is the type `Type` - the type of a quoted noir type. +fn quoted_type() -> impl NoirParser { + keyword(Keyword::TypeType) + .map_with_span(|_, span| UnresolvedTypeData::Quoted(QuotedType::Type).with_span(span)) } pub(super) fn string_type() -> impl NoirParser { diff --git a/test_programs/compile_failure/type_definition_annotation/Nargo.toml b/test_programs/compile_failure/type_definition_annotation/Nargo.toml new file mode 100644 index 00000000000..dc90816e16b --- /dev/null +++ b/test_programs/compile_failure/type_definition_annotation/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "type_definition_annotation" +type = "bin" +authors = [""] +compiler_version = ">=0.31.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/type_definition_annotation/src/main.nr b/test_programs/compile_failure/type_definition_annotation/src/main.nr new file mode 100644 index 00000000000..91f9c3a52f4 --- /dev/null +++ b/test_programs/compile_failure/type_definition_annotation/src/main.nr @@ -0,0 +1,8 @@ +#[fail_assert] +struct Foo { x: Field } + +comptime fn fail_assert(_typ: TypeDefinition) { + assert(false); +} + +fn main() {} diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index ec750236806..43c277ba03e 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -61,7 +61,8 @@ const IGNORED_BRILLIG_TESTS: [&str; 11] = [ /// Certain features are only available in the elaborator. /// We skip these tests for non-elaborator code since they are not /// expected to work there. This can be removed once the old code is removed. -const IGNORED_NEW_FEATURE_TESTS: [&str; 2] = ["macros", "wildcard_type"]; +const IGNORED_NEW_FEATURE_TESTS: [&str; 3] = + ["macros", "wildcard_type", "type_definition_annotation"]; fn generate_execution_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "execution_success"; @@ -291,7 +292,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa }; let test_dir = &test_dir.path(); - let comptime_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { "\n#[ignore]" } else { "" @@ -300,7 +301,7 @@ fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Pa write!( test_file, r#" -#[test]{comptime_ignored} +#[test]{new_feature_ignored} fn compile_success_empty_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); @@ -417,10 +418,16 @@ fn generate_compile_failure_tests(test_file: &mut File, test_data_dir: &Path) { }; let test_dir = &test_dir.path(); + let new_feature_ignored = if IGNORED_NEW_FEATURE_TESTS.contains(&test_name.as_str()) { + "\n#[ignore]" + } else { + "" + }; + write!( test_file, r#" -#[test] +#[test]{new_feature_ignored} fn compile_failure_legacy_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 3ad1d9302be..3eb398346c3 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -64,7 +64,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::String(_) | UnresolvedTypeData::FormatString(_, _) - | UnresolvedTypeData::Expr + | UnresolvedTypeData::Quoted(_) | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), UnresolvedTypeData::Error => unreachable!(), } diff --git a/tooling/nargo_fmt/tests/expected/impl.nr b/tooling/nargo_fmt/tests/expected/impl.nr index 1c0d4564b5e..ec734b57970 100644 --- a/tooling/nargo_fmt/tests/expected/impl.nr +++ b/tooling/nargo_fmt/tests/expected/impl.nr @@ -1,10 +1,10 @@ -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type { +impl MyType { fn method(self) {} fn method(mut self) {} @@ -12,10 +12,10 @@ impl Type { fn method(&mut self) {} } -impl Type { +impl MyType { fn method(self) {} } -impl Type { +impl MyType { fn method(self) {} } diff --git a/tooling/nargo_fmt/tests/input/impl.nr b/tooling/nargo_fmt/tests/input/impl.nr index 1f111371a43..ea909dfad44 100644 --- a/tooling/nargo_fmt/tests/input/impl.nr +++ b/tooling/nargo_fmt/tests/input/impl.nr @@ -1,10 +1,10 @@ -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type {} +impl MyType {} -impl Type { +impl MyType { fn method(self) {} fn method(mut self) {} @@ -12,10 +12,10 @@ impl Type { fn method(&mut self) {} } -impl Type { +impl MyType { fn method(self) {} } -impl Type { +impl MyType { fn method(self) {} -} \ No newline at end of file +}