Skip to content

Commit aedc983

Browse files
asteritejfecher
andauthored
feat: check unconstrained trait impl method matches (#6057)
# Description ## Problem Fixes #5717 ## Summary In a previous PR we started allowing `unconstrained` and `comptime` in trait impls. But two things remained: 1. Defining an `unconstrained` trait impl method would produce a warning. 2. We should error if a trait method is unconstrained but the trait impl method is not, or the other way around. This PR will also include "unconstrained" in trait imp method stubs in LSP. ## Additional Context I initially also errored if there's a mismatch in `comptime`, but that's probably not good as we have some implementation of `Append` be comptime and some not. This made me wonder what's the purpose of `comptime` in functions, if non-comptime functions can also be interpreted. ## Documentation Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher <[email protected]>
1 parent f476c4b commit aedc983

File tree

7 files changed

+35
-6
lines changed

7 files changed

+35
-6
lines changed

compiler/noirc_frontend/src/elaborator/traits.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ pub(crate) fn check_trait_impl_method_matches_declaration(
229229
function: FuncId,
230230
) -> Vec<TypeCheckError> {
231231
let meta = interner.function_meta(&function);
232+
let modifiers = interner.function_modifiers(&function);
232233
let method_name = interner.function_name(&function);
233234
let mut errors = Vec::new();
234235

@@ -267,6 +268,14 @@ pub(crate) fn check_trait_impl_method_matches_declaration(
267268
// issue an error for it here.
268269
if let Some(trait_fn_id) = trait_info.method_ids.get(method_name) {
269270
let trait_fn_meta = interner.function_meta(trait_fn_id);
271+
let trait_fn_modifiers = interner.function_modifiers(trait_fn_id);
272+
273+
if modifiers.is_unconstrained != trait_fn_modifiers.is_unconstrained {
274+
let expected = trait_fn_modifiers.is_unconstrained;
275+
let span = meta.name.location.span;
276+
let item = method_name.to_string();
277+
errors.push(TypeCheckError::UnconstrainedMismatch { item, expected, span });
278+
}
270279

271280
if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() {
272281
let expected = trait_fn_meta.direct_generics.len();

compiler/noirc_frontend/src/hir/type_check/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ pub enum TypeCheckError {
6161
ParameterCountMismatch { expected: usize, found: usize, span: Span },
6262
#[error("{item} expects {expected} generics but {found} were given")]
6363
GenericCountMismatch { item: String, expected: usize, found: usize, span: Span },
64+
#[error("{item} has incompatible `unconstrained`")]
65+
UnconstrainedMismatch { item: String, expected: bool, span: Span },
6466
#[error("Only integer and Field types may be casted to")]
6567
UnsupportedCast { span: Span },
6668
#[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")]
@@ -264,6 +266,14 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic {
264266
let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given");
265267
Diagnostic::simple_error(msg, String::new(), *span)
266268
}
269+
TypeCheckError::UnconstrainedMismatch { item, expected, span } => {
270+
let msg = if *expected {
271+
format!("{item} is expected to be unconstrained")
272+
} else {
273+
format!("{item} is not expected to be unconstrained")
274+
};
275+
Diagnostic::simple_error(msg, String::new(), *span)
276+
}
267277
TypeCheckError::InvalidCast { span, .. }
268278
| TypeCheckError::ExpectedFunction { span, .. }
269279
| TypeCheckError::AccessUnknownMember { span, .. }

compiler/noirc_frontend/src/parser/errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ pub enum ParserErrorReason {
4545
PatternInTraitFunctionParameter,
4646
#[error("Patterns aren't allowed in a trait impl's associated constants")]
4747
PatternInAssociatedConstant,
48-
#[error("Modifiers are ignored on a trait impl method")]
49-
TraitImplFunctionModifiers,
48+
#[error("Visibility is ignored on a trait impl method")]
49+
TraitImplVisibilityIgnored,
5050
#[error("comptime keyword is deprecated")]
5151
ComptimeDeprecated,
5252
#[error("{0} are experimental and aren't fully supported yet")]
@@ -202,7 +202,7 @@ impl<'a> From<&'a ParserError> for Diagnostic {
202202
ParserErrorReason::ExperimentalFeature(_) => {
203203
Diagnostic::simple_warning(reason.to_string(), "".into(), error.span)
204204
}
205-
ParserErrorReason::TraitImplFunctionModifiers => {
205+
ParserErrorReason::TraitImplVisibilityIgnored => {
206206
Diagnostic::simple_warning(reason.to_string(), "".into(), error.span)
207207
}
208208
ParserErrorReason::ExpectedPatternButFoundType(ty) => Diagnostic::simple_error(

compiler/noirc_frontend/src/parser/parser/traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ pub(super) fn trait_implementation() -> impl NoirParser<TopLevelStatementKind> {
187187

188188
fn trait_implementation_body() -> impl NoirParser<Vec<Documented<TraitImplItem>>> {
189189
let function = function::function_definition(true).validate(|mut f, span, emit| {
190-
if f.def().is_unconstrained || f.def().visibility != ItemVisibility::Private {
191-
emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span));
190+
if f.def().visibility != ItemVisibility::Private {
191+
emit(ParserError::with_reason(ParserErrorReason::TraitImplVisibilityIgnored, span));
192192
}
193193
// Trait impl functions are always public
194194
f.def_mut().visibility = ItemVisibility::Public;

tooling/lsp/src/requests/code_action/implement_missing_members.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,12 @@ impl<'a> CodeActionFinder<'a> {
101101

102102
for (name, func_id) in method_ids {
103103
let func_meta = self.interner.function_meta(func_id);
104+
let modifiers = self.interner.function_modifiers(func_id);
104105

105106
let mut generator = TraitImplMethodStubGenerator::new(
106107
name,
107108
func_meta,
109+
modifiers,
108110
trait_,
109111
noir_trait_impl,
110112
self.interner,

tooling/lsp/src/requests/completion.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,10 +899,12 @@ impl<'a> NodeFinder<'a> {
899899
}
900900

901901
let func_meta = self.interner.function_meta(&func_id);
902+
let modifiers = self.interner.function_modifiers(&func_id);
902903

903904
let mut generator = TraitImplMethodStubGenerator::new(
904905
&name,
905906
func_meta,
907+
modifiers,
906908
trait_,
907909
noir_trait_impl,
908910
self.interner,

tooling/lsp/src/trait_impl_method_stub_generator.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use noirc_frontend::{
99
},
1010
hir_def::{function::FuncMeta, stmt::HirPattern, traits::Trait},
1111
macros_api::{ModuleDefId, NodeInterner},
12-
node_interner::ReferenceId,
12+
node_interner::{FunctionModifiers, ReferenceId},
1313
Kind, ResolvedGeneric, Type, TypeVariableKind,
1414
};
1515

@@ -18,6 +18,7 @@ use crate::modules::relative_module_id_path;
1818
pub(crate) struct TraitImplMethodStubGenerator<'a> {
1919
name: &'a str,
2020
func_meta: &'a FuncMeta,
21+
modifiers: &'a FunctionModifiers,
2122
trait_: &'a Trait,
2223
noir_trait_impl: &'a NoirTraitImpl,
2324
interner: &'a NodeInterner,
@@ -33,6 +34,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> {
3334
pub(crate) fn new(
3435
name: &'a str,
3536
func_meta: &'a FuncMeta,
37+
modifiers: &'a FunctionModifiers,
3638
trait_: &'a Trait,
3739
noir_trait_impl: &'a NoirTraitImpl,
3840
interner: &'a NodeInterner,
@@ -43,6 +45,7 @@ impl<'a> TraitImplMethodStubGenerator<'a> {
4345
Self {
4446
name,
4547
func_meta,
48+
modifiers,
4649
trait_,
4750
noir_trait_impl,
4851
interner,
@@ -63,6 +66,9 @@ impl<'a> TraitImplMethodStubGenerator<'a> {
6366
let indent_string = " ".repeat(self.indent);
6467

6568
self.string.push_str(&indent_string);
69+
if self.modifiers.is_unconstrained {
70+
self.string.push_str("unconstrained ");
71+
}
6672
self.string.push_str("fn ");
6773
self.string.push_str(self.name);
6874
self.append_resolved_generics(&self.func_meta.direct_generics);

0 commit comments

Comments
 (0)