|
@@ -253,6 +253,9 @@ pub enum TypeCheckError {
|
|
|
|
|
|
#[error("Incompatible types in comparison operator")]
|
|
|
IncompatibleTypesInComparison(Span, Type, Span, Type, Span),
|
|
|
+
|
|
|
+ #[error("This name is already used by {0}")]
|
|
|
+ NameAlreadyUsedBy(&'static str, Span, Option<Span>),
|
|
|
}
|
|
|
|
|
|
type Result<T> = std::result::Result<T, TypeCheckError>;
|
|
@@ -357,15 +360,9 @@ fn evaluate_const_expr(expr: &typed_ast::Expression) -> Result<Exponent> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-#[derive(Clone, PartialEq)]
|
|
|
-enum IdentifierKind {
|
|
|
- Variable,
|
|
|
- Other,
|
|
|
-}
|
|
|
-
|
|
|
#[derive(Clone, Default)]
|
|
|
pub struct TypeChecker {
|
|
|
- identifiers: HashMap<String, (Type, IdentifierKind)>,
|
|
|
+ identifiers: HashMap<String, (Type, Option<Span>)>,
|
|
|
function_signatures: HashMap<
|
|
|
String,
|
|
|
(
|
|
@@ -380,15 +377,14 @@ pub struct TypeChecker {
|
|
|
}
|
|
|
|
|
|
impl TypeChecker {
|
|
|
- fn identifier_type_and_kind(&self, span: Span, name: &str) -> Result<&(Type, IdentifierKind)> {
|
|
|
- self.identifiers.get(name).ok_or_else(|| {
|
|
|
- let suggestion = suggestion::did_you_mean(self.identifiers.keys(), name);
|
|
|
- TypeCheckError::UnknownIdentifier(span, name.into(), suggestion)
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
fn identifier_type(&self, span: Span, name: &str) -> Result<&Type> {
|
|
|
- self.identifier_type_and_kind(span, name).map(|(t, _)| t)
|
|
|
+ self.identifiers
|
|
|
+ .get(name)
|
|
|
+ .ok_or_else(|| {
|
|
|
+ let suggestion = suggestion::did_you_mean(self.identifiers.keys(), name);
|
|
|
+ TypeCheckError::UnknownIdentifier(span, name.into(), suggestion)
|
|
|
+ })
|
|
|
+ .map(|(type_, _)| type_)
|
|
|
}
|
|
|
|
|
|
pub(crate) fn check_expression(&self, ast: &ast::Expression) -> Result<typed_ast::Expression> {
|
|
@@ -797,10 +793,8 @@ impl TypeChecker {
|
|
|
ast::Statement::Expression(expr) => {
|
|
|
let checked_expr = self.check_expression(expr)?;
|
|
|
for &identifier in LAST_RESULT_IDENTIFIERS {
|
|
|
- self.identifiers.insert(
|
|
|
- identifier.into(),
|
|
|
- (checked_expr.get_type(), IdentifierKind::Variable),
|
|
|
- );
|
|
|
+ self.identifiers
|
|
|
+ .insert(identifier.into(), (checked_expr.get_type(), None));
|
|
|
}
|
|
|
typed_ast::Statement::Expression(checked_expr)
|
|
|
}
|
|
@@ -810,6 +804,16 @@ impl TypeChecker {
|
|
|
expr,
|
|
|
type_annotation,
|
|
|
} => {
|
|
|
+ // Make sure that identifier does not clash with a function name. We do not
|
|
|
+ // check for clashes with unit names, as this is handled by the prefix parser.
|
|
|
+ if let Some(entry) = self.function_signatures.get(identifier) {
|
|
|
+ return Err(TypeCheckError::NameAlreadyUsedBy(
|
|
|
+ "a function",
|
|
|
+ *identifier_span,
|
|
|
+ Some(entry.0),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
let expr_checked = self.check_expression(expr)?;
|
|
|
let type_deduced = expr_checked.get_type();
|
|
|
|
|
@@ -856,7 +860,7 @@ impl TypeChecker {
|
|
|
|
|
|
self.identifiers.insert(
|
|
|
identifier.clone(),
|
|
|
- (type_deduced.clone(), IdentifierKind::Variable),
|
|
|
+ (type_deduced.clone(), Some(*identifier_span)),
|
|
|
);
|
|
|
|
|
|
typed_ast::Statement::DefineVariable(
|
|
@@ -869,7 +873,7 @@ impl TypeChecker {
|
|
|
type_deduced,
|
|
|
)
|
|
|
}
|
|
|
- ast::Statement::DefineBaseUnit(_span, unit_name, type_annotation, decorators) => {
|
|
|
+ ast::Statement::DefineBaseUnit(span, unit_name, type_annotation, decorators) => {
|
|
|
let type_specified = if let Some(dexpr) = type_annotation {
|
|
|
self.registry
|
|
|
.get_base_representation(dexpr)
|
|
@@ -886,10 +890,7 @@ impl TypeChecker {
|
|
|
for (name, _) in decorator::name_and_aliases(unit_name, decorators) {
|
|
|
self.identifiers.insert(
|
|
|
name.clone(),
|
|
|
- (
|
|
|
- Type::Dimension(type_specified.clone()),
|
|
|
- IdentifierKind::Other,
|
|
|
- ),
|
|
|
+ (Type::Dimension(type_specified.clone()), Some(*span)),
|
|
|
);
|
|
|
}
|
|
|
typed_ast::Statement::DefineBaseUnit(
|
|
@@ -944,7 +945,10 @@ impl TypeChecker {
|
|
|
for (name, _) in decorator::name_and_aliases(identifier, decorators) {
|
|
|
self.identifiers.insert(
|
|
|
name.clone(),
|
|
|
- (Type::Dimension(type_deduced.clone()), IdentifierKind::Other),
|
|
|
+ (
|
|
|
+ Type::Dimension(type_deduced.clone()),
|
|
|
+ Some(*identifier_span),
|
|
|
+ ),
|
|
|
);
|
|
|
}
|
|
|
typed_ast::Statement::DefineDerivedUnit(
|
|
@@ -966,6 +970,16 @@ impl TypeChecker {
|
|
|
return_type_annotation_span,
|
|
|
return_type_annotation,
|
|
|
} => {
|
|
|
+ // Make sure that function name does not clash with an identifier. We do not
|
|
|
+ // check for clashes with unit names, as this is handled by the prefix parser.
|
|
|
+ if let Some((_, span)) = self.identifiers.get(function_name) {
|
|
|
+ return Err(TypeCheckError::NameAlreadyUsedBy(
|
|
|
+ "a constant",
|
|
|
+ *function_name_span,
|
|
|
+ span.clone(),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
let mut typechecker_fn = self.clone();
|
|
|
let is_ffi_function = body.is_none();
|
|
|
let mut type_parameters = type_parameters.clone();
|
|
@@ -1020,7 +1034,7 @@ impl TypeChecker {
|
|
|
|
|
|
typechecker_fn.identifiers.insert(
|
|
|
parameter.clone(),
|
|
|
- (parameter_type.clone(), IdentifierKind::Variable),
|
|
|
+ (parameter_type.clone(), Some(*parameter_span)),
|
|
|
);
|
|
|
typed_parameters.push((
|
|
|
*parameter_span,
|