Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for usage of super in import paths #5502

Merged
merged 13 commits into from
Jul 15, 2024
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ pub enum PathKind {
Crate,
Dep,
Plain,
Super,
}

#[derive(Debug, PartialEq, Eq, Clone)]
Expand Down Expand Up @@ -748,6 +749,7 @@ impl Display for PathKind {
match self {
PathKind::Crate => write!(f, "crate"),
PathKind::Dep => write!(f, "dep"),
PathKind::Super => write!(f, "super"),
PathKind::Plain => write!(f, "plain"),
}
}
Expand Down
6 changes: 1 addition & 5 deletions compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@
}
}
TraitItem::Type { name } => {
// TODO(nickysn or alexvitkov): implement context.def_interner.push_empty_type_alias and get an id, instead of using TypeAliasId::dummy_id()

Check warning on line 512 in compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (nickysn)

Check warning on line 512 in compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (alexvitkov)
if let Err((first_def, second_def)) = self.def_collector.def_map.modules
[trait_id.0.local_id.0]
.declare_type_alias(name.clone(), TypeAliasId::dummy_id())
Expand Down Expand Up @@ -695,7 +695,7 @@
// if it's an inline module, or the first char of a the file if it's an external module.
// - `location` will always point to the token "foo" in `mod foo` regardless of whether
// it's inline or external.
// Eventually the location put in `ModuleData` is used for codelenses about `contract`s,

Check warning on line 698 in compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (codelenses)
// so we keep using `location` so that it continues to work as usual.
let location = Location::new(mod_name.span(), mod_location.file);
let new_module = ModuleData::new(parent, location, is_contract);
Expand Down Expand Up @@ -732,11 +732,7 @@

context.def_interner.add_module_attributes(
mod_id,
ModuleAttributes {
name: mod_name.0.contents.clone(),
location: mod_location,
parent: self.module_id,
},
ModuleAttributes { name: mod_name.0.contents.clone(), location: mod_location },
);
}

Expand Down
24 changes: 24 additions & 0 deletions compiler/noirc_frontend/src/hir/resolution/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub enum PathResolutionError {
Unresolved(Ident),
#[error("{0} is private and not visible from the current module")]
Private(Ident),
#[error("There is no super module")]
NoSuper(Span),
}

#[derive(Debug)]
Expand Down Expand Up @@ -73,6 +75,9 @@ impl<'a> From<&'a PathResolutionError> for CustomDiagnostic {
format!("{ident} is private"),
ident.span(),
),
PathResolutionError::NoSuper(span) => {
CustomDiagnostic::simple_error(error.to_string(), String::new(), *span)
}
}
}
}
Expand Down Expand Up @@ -187,6 +192,25 @@ fn resolve_path_to_ns(
path_references,
importing_crate,
),

crate::ast::PathKind::Super => {
if let Some(parent_module_id) =
def_maps[&crate_id].modules[import_directive.module_id.0].parent
{
resolve_name_in_module(
crate_id,
importing_crate,
import_path,
parent_module_id,
def_maps,
path_references,
)
} else {
let span_start = import_directive.path.span().start();
let span = Span::from(span_start..span_start + 5); // 5 == "super".len()
Err(PathResolutionError::NoSuper(span))
}
}
}
}

Expand Down
1 change: 0 additions & 1 deletion compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
pub struct ModuleAttributes {
pub name: String,
pub location: Location,
pub parent: LocalModuleId,
}

type StructAttributes = Vec<SecondaryAttribute>;
Expand Down Expand Up @@ -194,7 +193,7 @@
/// Stores the [Location] of a [Type] reference
pub(crate) type_ref_locations: Vec<(Type, Location)>,

/// In Noir's metaprogramming, a noir type has the type `Type`. When these are spliced

Check warning on line 196 in compiler/noirc_frontend/src/node_interner.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (metaprogramming)
/// into `quoted` expressions, we preserve the original type by assigning it a unique id
/// and creating a `Token::QuotedType(id)` from this id. We cannot create a token holding
/// the actual type since types do not implement Send or Sync.
Expand Down
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/parser/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(super) fn path() -> impl NoirParser<Path> {
choice((
path_kind(Keyword::Crate, PathKind::Crate),
path_kind(Keyword::Dep, PathKind::Dep),
path_kind(Keyword::Super, PathKind::Super),
idents().map_with_span(make_path(PathKind::Plain)),
))
}
Expand Down Expand Up @@ -64,6 +65,7 @@ mod test {
("std", PathKind::Plain),
("hash::collections", PathKind::Plain),
("crate::std::hash", PathKind::Crate),
("super::foo", PathKind::Super),
];

for (src, expected_path_kind) in cases {
Expand Down
118 changes: 74 additions & 44 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@
get_program(src, false).2
}

fn assert_no_errors(src: &str) {
let errors = get_program_errors(src);
if !errors.is_empty() {
panic!("Expected no errors, got: {:?}", errors);
}
}

#[test]
fn check_trait_implemented_for_all_t() {
let src = "
Expand Down Expand Up @@ -141,10 +148,7 @@
fn main(a: Foo) -> pub bool {
a.is_default()
}";

let errors = get_program_errors(src);
errors.iter().for_each(|err| println!("{:?}", err));
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -767,9 +771,7 @@
self
}
}";
let errors = get_program_errors(src);
errors.iter().for_each(|err| println!("{:?}", err));
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -794,10 +796,7 @@
fn main(a: Foo) -> pub bool {
test_eq(a)
}";

let errors = get_program_errors(src);
errors.iter().for_each(|err| println!("{:?}", err));
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -830,10 +829,7 @@
fn main(a: Foo, b: u64) -> pub bool {
test_eq(a, b)
}";

let errors = get_program_errors(src);
errors.iter().for_each(|err| println!("{:?}", err));
assert!(errors.is_empty());
assert_no_errors(src);
}

fn get_program_captures(src: &str) -> Vec<Vec<String>> {
Expand Down Expand Up @@ -898,7 +894,7 @@

}
";
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}
#[test]
fn resolve_basic_function() {
Expand All @@ -908,7 +904,7 @@
assert(y == x);
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}
#[test]
fn resolve_unused_var() {
Expand Down Expand Up @@ -981,7 +977,7 @@
assert(y == x);
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -1028,7 +1024,7 @@
let _y = -x;
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1040,7 +1036,7 @@
};
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1054,7 +1050,7 @@
x
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1071,7 +1067,7 @@
x
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1082,7 +1078,7 @@
closure(x)
}
"#;
assert!(get_program_errors(src).is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -1133,7 +1129,7 @@
a + b + c + closure_with_transitive_captures(6)
}
"#;
assert!(get_program_errors(src).is_empty(), "there should be no errors");
assert_no_errors(src);

let expected_captures = vec![
vec![],
Expand Down Expand Up @@ -1636,8 +1632,7 @@
let src = r#"
fn foo<let N: u8>(arr: [Field; N]) -> [Field; N] { arr }
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -1749,12 +1744,14 @@
inner: [u64; N],
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
fn numeric_generic_used_in_trait() {
// We want to make sure that `N` in `impl<let N: u64, T> Deserialize<N, T>` does
// not trigger `expected type, found numeric generic parameter N` as the trait
// does in fact expect a numeric generic.
let src = r#"
asterite marked this conversation as resolved.
Show resolved Hide resolved
struct MyType<T> {
a: Field,
Expand All @@ -1773,11 +1770,7 @@
fn deserialize(fields: [Field; N], other: T) -> Self;
}
"#;
let errors = get_program_errors(src);
// We want to make sure that `N` in `impl<let N: u64, T> Deserialize<N, T>` does
// not trigger `expected type, found numeric generic parameter N` as the trait
// does in fact expect a numeric generic.
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -1808,8 +1801,7 @@
fn deserialize(fields: [Field; N]) -> Self;
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1827,8 +1819,7 @@
T::deserialize(fields)
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1845,8 +1836,7 @@
assert(double::<7 + 8>() == 30);
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand All @@ -1866,8 +1856,7 @@
}
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);
}

#[test]
Expand Down Expand Up @@ -2002,7 +1991,7 @@
}

#[test]
fn underflowing_u8() {

Check warning on line 1994 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (underflowing)
let src = r#"
fn main() {
let _: u8 = -1;
Expand Down Expand Up @@ -2040,7 +2029,7 @@
}

#[test]
fn underflowing_i8() {

Check warning on line 2032 in compiler/noirc_frontend/src/tests.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (underflowing)
let src = r#"
fn main() {
let _: i8 = -129;
Expand Down Expand Up @@ -2076,8 +2065,7 @@
let _ = bar::<M>();
}
"#;
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_no_errors(src);

// Check for turbofish numeric generics used with method calls
let src = r#"
Expand Down Expand Up @@ -2107,6 +2095,48 @@
let _ = bar::<M>();
}
"#;
assert_no_errors(src);
}

#[test]
fn use_super() {
let src = r#"
fn some_func() {}

mod foo {
use super::some_func;
}
"#;
assert_no_errors(src);
}

#[test]
fn use_super_in_path() {
let src = r#"
fn some_func() {}

mod foo {
fn func() {
super::some_func();
}
}
"#;
assert_no_errors(src);
}

#[test]
fn no_super() {
let src = "use super::some_func;";
let errors = get_program_errors(src);
assert!(errors.is_empty());
assert_eq!(errors.len(), 1);

let CompilationError::DefinitionError(DefCollectorErrorKind::PathResolutionError(
PathResolutionError::NoSuper(span),
)) = &errors[0].0
else {
panic!("Expected a 'no super' error, got {:?}", errors[0].0);
};

assert_eq!(span.start(), 4);
assert_eq!(span.end(), 9);
}
Loading
Loading