Skip to content

Commit

Permalink
Error on unsupported turbofish usage
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Jul 25, 2024
1 parent e129ff0 commit 18d1dde
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 3 deletions.
4 changes: 3 additions & 1 deletion compiler/noirc_frontend/src/ast/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,13 @@ impl Path {
pub struct PathSegment {
pub ident: Ident,
pub generics: Option<Vec<UnresolvedType>>,
pub span: Span,
}

impl From<Ident> for PathSegment {
fn from(ident: Ident) -> PathSegment {
PathSegment { ident, generics: None }
let span = ident.span();
PathSegment { ident, generics: None, span }
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ impl<'context> Elaborator<'context> {
&mut self,
constructor: ConstructorExpression,
) -> (HirExpression, Type) {
let exclude_last_segment = false;
self.check_unsupported_turbofish_usage(&constructor.type_name, exclude_last_segment);

let span = constructor.type_name.span();
let last_segment = constructor.type_name.last_ident();
let is_self_type = last_segment.is_self_type_name();
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,9 @@ impl<'context> Elaborator<'context> {
}

pub(super) fn elaborate_variable(&mut self, variable: Path) -> (ExprId, Type) {
let exclude_last_segment = true;
self.check_unsupported_turbofish_usage(&variable, exclude_last_segment);

let unresolved_turbofish = variable.segments.last().unwrap().generics.clone();

let span = variable.span;
Expand Down
14 changes: 14 additions & 0 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,20 @@ impl<'context> Elaborator<'context> {
let context = context.expect("The function_context stack should always be non-empty");
context.trait_constraints.push((constraint, expr_id));
}

pub fn check_unsupported_turbofish_usage(&mut self, path: &Path, exclude_last_segment: bool) {
for (index, segment) in path.segments.iter().enumerate() {
if exclude_last_segment && index == path.segments.len() - 1 {
continue;
}

if segment.generics.is_some() {
// From "foo::<T>", create a span for just "::<T>"
let span = Span::from(segment.ident.span().end()..segment.span.end());
self.push_err(TypeCheckError::UnsupportedTurbofishUsage { span })
}
}
}
}

/// Gives an error if a user tries to create a mutable reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ impl HirExpression {
let kind = match self {
HirExpression::Ident(ident, generics) => {
let ident = ident.to_display_ast(interner);
let span = ident.span();
let segment = PathSegment {
ident,
generics: generics.as_ref().map(|option| {
option.iter().map(|generic| generic.to_display_ast()).collect()
}),
span,
};

let path =
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ pub enum TypeCheckError {
StringIndexAssign { span: Span },
#[error("Macro calls may only return `Quoted` values")]
MacroReturningNonExpr { typ: Type, span: Span },
#[error("turbofish (`::<_>`) usage at this position isn't supported yet")]
UnsupportedTurbofishUsage { span: Span },
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -350,6 +352,10 @@ impl<'a> From<&'a TypeCheckError> for Diagnostic {
"Macro calls must return quoted values, otherwise there is no code to insert".into(),
*span,
),
TypeCheckError::UnsupportedTurbofishUsage { span } => {
let msg = "turbofish (`::<_>`) usage at this position isn't supported yet";
Diagnostic::simple_error(msg.to_string(), "".to_string(), *span)
},
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/parser/parser/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub(super) fn token_kind(token_kind: TokenKind) -> impl NoirParser<Token> {
pub(super) fn path_segment() -> impl NoirParser<PathSegment> {
ident()
.then(turbofish(super::parse_type()))
.map(|(ident, generics)| PathSegment { ident, generics })
.map_with_span(|(ident, generics), span| PathSegment { ident, generics, span })
}

pub(super) fn path_segment_no_turbofish() -> impl NoirParser<PathSegment> {
Expand Down
46 changes: 46 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2537,3 +2537,49 @@ fn trait_constraint_on_tuple_type() {
fn main() {}"#;
assert_no_errors(src);
}

#[test]
fn turbofish_in_constructor_unsupported_yet() {
let src = r#"
struct Foo<T> {
x: T
}
fn main() {
let _ = Foo::<i32> { x: 1 };
}
"#;
let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

assert!(matches!(
errors[0].0,
CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }),
));
}

#[test]
fn turbofish_in_middle_of_variable_unsupported_yet() {
let src = r#"
struct Foo<T> {
x: T
}
impl <T> Foo<T> {
fn new(x: T) -> Self {
Foo { x }
}
}
fn main() {
let _ = Foo::<i32>::new(1);
}
"#;
let errors = get_program_errors(src);
assert_eq!(errors.len(), 1);

assert!(matches!(
errors[0].0,
CompilationError::TypeError(TypeCheckError::UnsupportedTurbofishUsage { .. }),
));
}

0 comments on commit 18d1dde

Please sign in to comment.