|
@@ -247,6 +247,9 @@ pub enum TypeCheckError {
|
|
|
|
|
|
|
|
#[error("Incompatible types in {0}")]
|
|
#[error("Incompatible types in {0}")]
|
|
|
IncompatibleTypesInAnnotation(String, Span, Type, Span, Type, Span),
|
|
IncompatibleTypesInAnnotation(String, 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>;
|
|
type Result<T> = std::result::Result<T, TypeCheckError>;
|
|
@@ -358,15 +361,9 @@ fn evaluate_const_expr(expr: &typed_ast::Expression) -> Result<Exponent> {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-#[derive(Clone, PartialEq)]
|
|
|
|
|
-enum IdentifierKind {
|
|
|
|
|
- Variable,
|
|
|
|
|
- Other,
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
#[derive(Clone, Default)]
|
|
#[derive(Clone, Default)]
|
|
|
pub struct TypeChecker {
|
|
pub struct TypeChecker {
|
|
|
- identifiers: HashMap<String, (Type, IdentifierKind)>,
|
|
|
|
|
|
|
+ identifiers: HashMap<String, (Type, Option<Span>)>,
|
|
|
function_signatures: HashMap<
|
|
function_signatures: HashMap<
|
|
|
String,
|
|
String,
|
|
|
(
|
|
(
|
|
@@ -381,15 +378,14 @@ pub struct TypeChecker {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl 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> {
|
|
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> {
|
|
pub(crate) fn check_expression(&self, ast: &ast::Expression) -> Result<typed_ast::Expression> {
|
|
@@ -773,10 +769,8 @@ impl TypeChecker {
|
|
|
ast::Statement::Expression(expr) => {
|
|
ast::Statement::Expression(expr) => {
|
|
|
let checked_expr = self.check_expression(expr)?;
|
|
let checked_expr = self.check_expression(expr)?;
|
|
|
for &identifier in LAST_RESULT_IDENTIFIERS {
|
|
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)
|
|
typed_ast::Statement::Expression(checked_expr)
|
|
|
}
|
|
}
|
|
@@ -786,6 +780,16 @@ impl TypeChecker {
|
|
|
expr,
|
|
expr,
|
|
|
type_annotation,
|
|
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 expr_checked = self.check_expression(expr)?;
|
|
|
let type_deduced = expr_checked.get_type();
|
|
let type_deduced = expr_checked.get_type();
|
|
|
|
|
|
|
@@ -832,7 +836,7 @@ impl TypeChecker {
|
|
|
|
|
|
|
|
self.identifiers.insert(
|
|
self.identifiers.insert(
|
|
|
identifier.clone(),
|
|
identifier.clone(),
|
|
|
- (type_deduced.clone(), IdentifierKind::Variable),
|
|
|
|
|
|
|
+ (type_deduced.clone(), Some(*identifier_span)),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
typed_ast::Statement::DefineVariable(
|
|
typed_ast::Statement::DefineVariable(
|
|
@@ -845,7 +849,7 @@ impl TypeChecker {
|
|
|
type_deduced,
|
|
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 {
|
|
let type_specified = if let Some(dexpr) = type_annotation {
|
|
|
self.registry
|
|
self.registry
|
|
|
.get_base_representation(dexpr)
|
|
.get_base_representation(dexpr)
|
|
@@ -862,10 +866,7 @@ impl TypeChecker {
|
|
|
for (name, _) in decorator::name_and_aliases(unit_name, decorators) {
|
|
for (name, _) in decorator::name_and_aliases(unit_name, decorators) {
|
|
|
self.identifiers.insert(
|
|
self.identifiers.insert(
|
|
|
name.clone(),
|
|
name.clone(),
|
|
|
- (
|
|
|
|
|
- Type::Dimension(type_specified.clone()),
|
|
|
|
|
- IdentifierKind::Other,
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ (Type::Dimension(type_specified.clone()), Some(*span)),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
typed_ast::Statement::DefineBaseUnit(
|
|
typed_ast::Statement::DefineBaseUnit(
|
|
@@ -920,7 +921,10 @@ impl TypeChecker {
|
|
|
for (name, _) in decorator::name_and_aliases(identifier, decorators) {
|
|
for (name, _) in decorator::name_and_aliases(identifier, decorators) {
|
|
|
self.identifiers.insert(
|
|
self.identifiers.insert(
|
|
|
name.clone(),
|
|
name.clone(),
|
|
|
- (Type::Dimension(type_deduced.clone()), IdentifierKind::Other),
|
|
|
|
|
|
|
+ (
|
|
|
|
|
+ Type::Dimension(type_deduced.clone()),
|
|
|
|
|
+ Some(*identifier_span),
|
|
|
|
|
+ ),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
typed_ast::Statement::DefineDerivedUnit(
|
|
typed_ast::Statement::DefineDerivedUnit(
|
|
@@ -942,6 +946,16 @@ impl TypeChecker {
|
|
|
return_type_annotation_span,
|
|
return_type_annotation_span,
|
|
|
return_type_annotation,
|
|
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 mut typechecker_fn = self.clone();
|
|
|
let is_ffi_function = body.is_none();
|
|
let is_ffi_function = body.is_none();
|
|
|
let mut type_parameters = type_parameters.clone();
|
|
let mut type_parameters = type_parameters.clone();
|
|
@@ -999,7 +1013,7 @@ impl TypeChecker {
|
|
|
parameter.clone(),
|
|
parameter.clone(),
|
|
|
(
|
|
(
|
|
|
Type::Dimension(parameter_type.clone()),
|
|
Type::Dimension(parameter_type.clone()),
|
|
|
- IdentifierKind::Variable,
|
|
|
|
|
|
|
+ Some(*parameter_span),
|
|
|
),
|
|
),
|
|
|
);
|
|
);
|
|
|
typed_parameters.push((
|
|
typed_parameters.push((
|