use super::*;
fn type_uses_name(checked_type: &Type, name: &str) -> bool {
match checked_type {
Type::Named(type_name, arguments) => {
type_name == name
|| arguments.iter().any(|argument| match argument {
GenericArgument::Type(inner) => type_uses_name(inner, name),
GenericArgument::Const(constant) => const_uses_name(constant, name),
GenericArgument::Lifetime(_) => false,
})
}
Type::Generic(generic_name) => generic_name == name,
Type::Pointer(_, _, inner) | Type::Reference(_, _, inner) | Type::Slice(_, inner) => {
type_uses_name(inner, name)
}
Type::Array(inner, size) | Type::Vector(inner, size) => {
type_uses_name(inner, name) || const_uses_name(size, name)
}
Type::Tuple(fields) | Type::Enum(fields) => {
fields.iter().any(|field| type_uses_name(field, name))
}
Type::Function(signature) => {
signature
.parameters
.iter()
.any(|parameter| type_uses_name(parameter, name))
|| type_uses_name(&signature.return_type, name)
}
_ => false,
}
}
fn const_uses_name(expression: &ConstExpression, name: &str) -> bool {
match expression {
ConstExpression::Named(constant_name) => constant_name == name,
ConstExpression::Integer(_) => false,
ConstExpression::Call(_, arguments) => arguments
.iter()
.any(|argument| const_uses_name(argument, name)),
ConstExpression::Field(inner, _) => const_uses_name(inner, name),
ConstExpression::Construction(fields) => {
fields.iter().any(|(_, value)| const_uses_name(value, name))
}
binary => {
let children: Vec<&ConstExpression> = match binary {
ConstExpression::Add(l, r)
| ConstExpression::Subtract(l, r)
| ConstExpression::Multiply(l, r)
| ConstExpression::Divide(l, r)
| ConstExpression::Modulo(l, r)
| ConstExpression::BitwiseAnd(l, r)
| ConstExpression::BitwiseOr(l, r)
| ConstExpression::BitwiseXor(l, r)
| ConstExpression::ShiftLeft(l, r)
| ConstExpression::ShiftRight(l, r) => vec![l, r],
_ => vec![],
};
children.iter().any(|child| const_uses_name(child, name))
}
}
}
fn const_uses_name_in_type(checked_type: &Type, name: &str) -> bool {
match checked_type {
Type::Array(inner, size) | Type::Vector(inner, size) => {
const_uses_name(size, name) || const_uses_name_in_type(inner, name)
}
Type::Named(_, arguments) => arguments.iter().any(|argument| match argument {
GenericArgument::Const(constant) => const_uses_name(constant, name),
GenericArgument::Type(inner) => const_uses_name_in_type(inner, name),
GenericArgument::Lifetime(_) => false,
}),
Type::Pointer(_, _, inner) | Type::Reference(_, _, inner) | Type::Slice(_, inner) => {
const_uses_name_in_type(inner, name)
}
Type::Tuple(fields) | Type::Enum(fields) => fields
.iter()
.any(|field| const_uses_name_in_type(field, name)),
_ => false,
}
}
fn extract_params_from_type(
inner_type: &Type,
span: Span,
generic_params: &HashSet<String>,
) -> Result<Option<Vec<Parameter>>> {
match inner_type {
t if t.is_unit() => Ok(Some(vec![])),
Type::Named(name, _) if !generic_params.contains(name) => Ok(Some(vec![Parameter {
name: name.clone(),
parameter_type: inner_type.clone(),
}])),
Type::Tuple(fields) => {
let mut params = Vec::new();
for field in fields {
match field {
Type::Named(name, _) if !generic_params.contains(name) => {
params.push(Parameter {
name: name.clone(),
parameter_type: field.clone(),
})
}
_ => {
return Err(Error {
message: "fn parameter tuple fields must be named types".into(),
span: Some(span),
});
}
}
}
Ok(Some(params))
}
_ => Ok(Some(vec![Parameter {
name: "it".into(),
parameter_type: inner_type.clone(),
}])),
}
}
fn param_types_from_inner(inner_type: &Type) -> Vec<Type> {
match inner_type {
Type::Tuple(fields) => fields.clone(),
t if t.is_unit() => vec![],
other => vec![other.clone()],
}
}
impl Parser {
pub(super) fn parse_function(&mut self, public: bool) -> Result<Vec<Declaration>> {
self.parse_function_inner(public, false)
}
pub(super) fn parse_function_inner(
&mut self,
public: bool,
const_fn: bool,
) -> Result<Vec<Declaration>> {
self.advance();
let name = self.expect_identifier()?;
let generic_parameters = self.parse_generic_parameters()?;
let added_generic_params: Vec<String> = generic_parameters
.iter()
.filter(|p| matches!(p.kind, GenericParameterKind::Type))
.map(|p| p.name.clone())
.collect();
for param_name in &added_generic_params {
self.context
.active_generic_params
.insert(param_name.clone());
}
if const_fn {
return self.parse_const_fn_body(
name,
generic_parameters,
added_generic_params,
public,
);
}
let inner_type = if self.at(&Token::Arrow) || self.at(&Token::LeftBrace) {
Type::unit()
} else {
self.parse_type_expression_raw()?
};
if let Type::Tuple(fields) = &inner_type {
let mut seen = HashSet::new();
for field in fields {
let key = format!("{field:?}");
if !seen.insert(key) {
return Err(Error {
message: format!(
"duplicate parameter type in '{name}' — use distinct newtypes"
),
span: Some(self.span()),
});
}
}
}
let return_type = if self.at(&Token::Arrow) {
self.advance();
self.parse_type_expression()?
} else {
Type::unit()
};
for generic_parameter in &generic_parameters {
let used = match &generic_parameter.kind {
GenericParameterKind::Type => {
type_uses_name(&inner_type, &generic_parameter.name)
|| type_uses_name(&return_type, &generic_parameter.name)
}
GenericParameterKind::Const(_) => {
const_uses_name_in_type(&inner_type, &generic_parameter.name)
|| const_uses_name_in_type(&return_type, &generic_parameter.name)
}
};
if !used {
let kind_label = match &generic_parameter.kind {
GenericParameterKind::Type => "type",
GenericParameterKind::Const(_) => "const",
};
return Err(Error {
message: format!("unused {kind_label} parameter '{}'", generic_parameter.name),
span: Some(self.span()),
});
}
}
if self.context.newtypes.contains_key(&name) {
return Err(Error {
message: format!("'{name}' is already declared as a type"),
span: Some(self.span()),
});
}
let parameters = extract_params_from_type(
&inner_type,
self.span(),
&self.context.active_generic_params,
)?;
self.context
.return_types
.insert(name.clone(), return_type.clone());
self.context
.function_params
.insert(name.clone(), param_types_from_inner(&inner_type));
if !generic_parameters.is_empty() {
self.context.generic_functions.insert(name.clone());
self.context.generic_parameter_kinds.insert(
name.clone(),
generic_parameters.iter().map(|p| p.kind.clone()).collect(),
);
}
self.context.push_scope();
if let Some(ref params) = parameters {
for parameter in params {
self.context
.define_variable(parameter.name.clone(), parameter.parameter_type.clone());
if matches!(parameter.parameter_type, Type::Pointer(..)) {
self.context.mark_auto_ref(¶meter.name);
}
}
}
let body = self.parse_block()?;
self.context.pop_scope();
for param_name in &added_generic_params {
self.context.active_generic_params.remove(param_name);
}
self.context
.newtypes
.insert(name.clone(), inner_type.clone());
let mut declarations = Vec::new();
declarations.push(Declaration::Type(Newtype {
name: name.clone(),
generic_parameters: generic_parameters.clone(),
inner_type,
public,
}));
let target_type = if generic_parameters.is_empty() {
Type::Named(name, vec![])
} else {
Type::Named(
name,
generic_parameters
.iter()
.map(|p| match &p.kind {
GenericParameterKind::Type => {
GenericArgument::Type(Type::Generic(p.name.clone()))
}
GenericParameterKind::Const(_) => {
GenericArgument::Const(ConstExpression::Named(p.name.clone()))
}
})
.collect(),
)
};
declarations.push(Declaration::Impl(ImplBlock {
generic_parameters,
target_type,
callable: Callable {
return_type,
body,
parameters,
},
const_fn: false,
}));
Ok(declarations)
}
fn parse_const_fn_body(
&mut self,
name: String,
generic_parameters: Vec<GenericParameter>,
added_generic_params: Vec<String>,
_public: bool,
) -> Result<Vec<Declaration>> {
self.expect(Token::LeftParen)?;
let parameters = self.parse_parameter_list()?;
self.expect(Token::RightParen)?;
if parameters.len() > 1 {
let mut seen_types = HashSet::new();
for parameter in ¶meters {
if !seen_types.insert(format!("{:?}", parameter.parameter_type)) {
return Err(Error {
message: format!(
"duplicate parameter type in '{name}' — use distinct newtypes"
),
span: Some(self.span()),
});
}
}
}
let return_type = if self.at(&Token::Arrow) {
self.advance();
self.parse_type_expression()?
} else {
Type::unit()
};
for generic_parameter in &generic_parameters {
let used = match &generic_parameter.kind {
GenericParameterKind::Type => {
parameters
.iter()
.any(|p| type_uses_name(&p.parameter_type, &generic_parameter.name))
|| type_uses_name(&return_type, &generic_parameter.name)
}
GenericParameterKind::Const(_) => {
parameters.iter().any(|p| {
const_uses_name_in_type(&p.parameter_type, &generic_parameter.name)
}) || const_uses_name_in_type(&return_type, &generic_parameter.name)
}
};
if !used {
let kind_label = match &generic_parameter.kind {
GenericParameterKind::Type => "type",
GenericParameterKind::Const(_) => "const",
};
return Err(Error {
message: format!("unused {kind_label} parameter '{}'", generic_parameter.name),
span: Some(self.span()),
});
}
}
if self.context.newtypes.contains_key(&name) {
return Err(Error {
message: format!("'{name}' is already declared as a type"),
span: Some(self.span()),
});
}
self.context
.return_types
.insert(name.clone(), return_type.clone());
self.context.function_params.insert(
name.clone(),
parameters
.iter()
.map(|parameter| parameter.parameter_type.clone())
.collect(),
);
if !generic_parameters.is_empty() {
self.context.generic_functions.insert(name.clone());
self.context.generic_parameter_kinds.insert(
name.clone(),
generic_parameters.iter().map(|p| p.kind.clone()).collect(),
);
}
self.context.push_scope();
for parameter in ¶meters {
self.context
.define_variable(parameter.name.clone(), parameter.parameter_type.clone());
if matches!(parameter.parameter_type, Type::Pointer(..)) {
self.context.mark_auto_ref(¶meter.name);
}
}
let body = self.parse_block()?;
self.context.pop_scope();
for param_name in &added_generic_params {
self.context.active_generic_params.remove(param_name);
}
let inner_type = match parameters.len() {
0 => Type::unit(),
1 => parameters[0].parameter_type.clone(),
_ => Type::Tuple(
parameters
.iter()
.map(|p| p.parameter_type.clone())
.collect(),
),
};
self.context.newtypes.insert(name.clone(), inner_type);
let target_type = if generic_parameters.is_empty() {
Type::Named(name, vec![])
} else {
Type::Named(
name,
generic_parameters
.iter()
.map(|p| match &p.kind {
GenericParameterKind::Type => {
GenericArgument::Type(Type::Generic(p.name.clone()))
}
GenericParameterKind::Const(_) => {
GenericArgument::Const(ConstExpression::Named(p.name.clone()))
}
})
.collect(),
)
};
Ok(vec![Declaration::Impl(ImplBlock {
generic_parameters,
target_type,
callable: Callable {
return_type,
body,
parameters: Some(parameters),
},
const_fn: true,
})])
}
pub(super) fn parse_impl(&mut self) -> Result<Vec<Declaration>> {
self.advance();
let generic_parameters = self.parse_generic_parameters()?;
let impl_generic_params: Vec<String> = generic_parameters
.iter()
.filter(|p| matches!(p.kind, GenericParameterKind::Type))
.map(|p| p.name.clone())
.collect();
for param_name in &impl_generic_params {
self.context
.active_generic_params
.insert(param_name.clone());
}
let target_type = self.parse_type_expression()?;
self.expect(Token::Arrow)?;
let return_type = self.parse_type_expression()?;
if let Type::Named(name, _) = &target_type
&& !generic_parameters.is_empty()
{
self.context.generic_parameter_kinds.insert(
name.clone(),
generic_parameters.iter().map(|p| p.kind.clone()).collect(),
);
for anonymous_type in &mut self.anonymous_types {
if let Declaration::Type(newtype) = anonymous_type
&& newtype.name == *name
&& newtype.generic_parameters.is_empty()
{
newtype.generic_parameters.clone_from(&generic_parameters);
}
}
}
self.context.push_scope();
self.context
.define_variable("it".into(), target_type.clone());
let body = self.parse_block()?;
self.context.pop_scope();
for param_name in &impl_generic_params {
self.context.active_generic_params.remove(param_name);
}
Ok(vec![Declaration::Impl(ImplBlock {
generic_parameters,
target_type,
callable: Callable {
return_type,
body,
parameters: None,
},
const_fn: false,
})])
}
fn parse_parameter_list(&mut self) -> Result<Vec<Parameter>> {
let mut parameters = Vec::new();
if self.at(&Token::RightParen) || self.at(&Token::Ellipsis) {
return Ok(parameters);
}
parameters.push(self.parse_parameter()?);
while self.at(&Token::Comma) {
self.advance();
if self.at(&Token::RightParen) || self.at(&Token::Ellipsis) {
break;
}
parameters.push(self.parse_parameter()?);
}
Ok(parameters)
}
fn parse_parameter(&mut self) -> Result<Parameter> {
let name = self.expect_identifier()?;
self.expect(Token::Colon)?;
let parameter_type = self.parse_type_expression()?;
Ok(Parameter {
name,
parameter_type,
})
}
pub(super) fn parse_extern(&mut self) -> Result<Vec<Declaration>> {
self.advance();
self.expect(Token::Fn)?;
let name = self.expect_identifier()?;
self.expect(Token::LeftParen)?;
let parameters = self.parse_parameter_list()?;
let variadic = if self.at(&Token::Ellipsis) {
self.advance();
true
} else {
false
};
self.expect(Token::RightParen)?;
let return_type = if self.at(&Token::Arrow) {
self.advance();
self.parse_type_expression()?
} else {
Type::unit()
};
self.expect(Token::Semicolon)?;
self.context
.return_types
.insert(name.clone(), return_type.clone());
self.context.function_params.insert(
name.clone(),
parameters
.iter()
.map(|parameter| parameter.parameter_type.clone())
.collect(),
);
let parameter_list = if variadic {
ParameterList::Variadic(parameters)
} else {
ParameterList::Fixed(parameters)
};
Ok(vec![Declaration::Extern(ExternFunction {
name,
parameters: parameter_list,
return_type,
})])
}
pub(super) fn parse_type_declarations(&mut self, public: bool) -> Result<Vec<Declaration>> {
self.advance();
let mut names = vec![self.expect_identifier()?];
while self.at(&Token::Comma) {
self.advance();
names.push(self.expect_identifier()?);
}
if self.at(&Token::Semicolon) {
return Ok(self.finish_implicit_generic_types(names, public));
}
let generic_parameters = self.parse_generic_parameters()?;
let type_generic_params: Vec<String> = generic_parameters
.iter()
.filter(|p| matches!(p.kind, GenericParameterKind::Type))
.map(|p| p.name.clone())
.collect();
for param_name in &type_generic_params {
self.context
.active_generic_params
.insert(param_name.clone());
}
self.expect(Token::Equal)?;
let inner_type = self.parse_type_expression_raw()?;
for param_name in &type_generic_params {
self.context.active_generic_params.remove(param_name);
}
if inner_type.is_unit() {
return Err(Error {
message:
"type declarations cannot use void/unit type; omit the return type instead"
.into(),
span: Some(self.span()),
});
}
let compound_fields = match &inner_type {
Type::Tuple(fields) => Some(("tuple", "field", fields)),
Type::Enum(variants) => Some(("enum", "variant", variants)),
_ => None,
};
if let Some((kind, element_name, fields)) = compound_fields {
let mut seen = HashSet::new();
for field in fields {
if let Type::Named(name, _) = field {
if !seen.insert(name) {
return Err(Error {
message: format!("duplicate {element_name} type '{name}' in {kind}"),
span: Some(self.span()),
});
}
} else {
return Err(Error {
message: format!(
"{kind} {element_name}s must be newtypes, not primitive types"
),
span: Some(self.span()),
});
}
}
}
if generic_parameters.len() == 1
&& matches!(generic_parameters[0].kind, GenericParameterKind::Type)
&& matches!(&inner_type, Type::Named(name, args) if name == &generic_parameters[0].name && args.is_empty())
{
return Err(Error {
message: format!(
"'type {}[{}] = {};' is equivalent to 'type {};' — use the shorthand",
names[0], generic_parameters[0].name, generic_parameters[0].name, names[0]
),
span: Some(self.span()),
});
}
for parameter in &generic_parameters {
let used = match ¶meter.kind {
GenericParameterKind::Type => type_uses_name(&inner_type, ¶meter.name),
GenericParameterKind::Const(_) => {
const_uses_name_in_type(&inner_type, ¶meter.name)
}
};
if !used {
let kind_label = match ¶meter.kind {
GenericParameterKind::Type => "type",
GenericParameterKind::Const(_) => "const",
};
return Err(Error {
message: format!("unused {kind_label} parameter '{}'", parameter.name),
span: Some(self.span()),
});
}
}
self.expect(Token::Semicolon)?;
let mut declarations = Vec::new();
for name in names {
if self.context.return_types.contains_key(&name) {
return Err(Error {
message: format!("'{name}' is already declared as a function"),
span: Some(self.span()),
});
}
self.context
.newtypes
.insert(name.clone(), inner_type.clone());
if !generic_parameters.is_empty() {
self.context.generic_parameter_kinds.insert(
name.clone(),
generic_parameters.iter().map(|p| p.kind.clone()).collect(),
);
}
declarations.push(Declaration::Type(Newtype {
name,
generic_parameters: generic_parameters.clone(),
inner_type: inner_type.clone(),
public,
}));
}
Ok(declarations)
}
fn finish_implicit_generic_types(
&mut self,
names: Vec<String>,
public: bool,
) -> Vec<Declaration> {
self.advance();
let generic_parameters = vec![GenericParameter {
name: "T".into(),
kind: GenericParameterKind::Type,
}];
let inner_type = Type::Named("T".into(), vec![]);
let mut declarations = Vec::new();
for name in names {
self.context
.newtypes
.insert(name.clone(), inner_type.clone());
self.context.generic_parameter_kinds.insert(
name.clone(),
generic_parameters.iter().map(|p| p.kind.clone()).collect(),
);
declarations.push(Declaration::Type(Newtype {
name,
generic_parameters: generic_parameters.clone(),
inner_type: inner_type.clone(),
public,
}));
}
declarations
}
pub(super) fn parse_constant(&mut self, public: bool) -> Result<Vec<Declaration>> {
self.advance();
if *self.peek() == Token::Fn {
return self.parse_function_inner(public, true);
}
let name = self.expect_identifier()?;
self.expect(Token::Colon)?;
let constant_type = self.parse_type_expression()?;
self.expect(Token::Equal)?;
let value = self.parse_expression()?;
self.expect(Token::Semicolon)?;
self.context
.constants
.insert(name.clone(), constant_type.clone());
Ok(vec![Declaration::Constant(Constant {
name,
constant_type,
value,
public,
})])
}
pub(super) fn parse_import(&mut self) -> Result<Declaration> {
self.advance();
let Token::String(path) = self.peek().clone() else {
return Err(Error {
message: "expected string after import".into(),
span: Some(self.span()),
});
};
self.advance();
self.expect(Token::Semicolon)?;
Ok(Declaration::Import(String::from_utf8(path).map_err(
|_| Error {
message: "import: invalid UTF-8 path".into(),
span: Some(self.span()),
},
)?))
}
}