Skip to content

Commit

Permalink
fix: check for previously existing type parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
junlarsen committed Feb 15, 2025
1 parent 982c991 commit 58fbc5c
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 31 deletions.
2 changes: 1 addition & 1 deletion compiler/eight-middle/src/hir/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ impl<'hir> HirBuilder {
name,
name_span,
signature,
type_parameter_substitutions: BTreeMap::new(),
}
}

Expand All @@ -93,7 +94,6 @@ impl<'hir> HirBuilder {
type_arguments,
members,
signature,
type_parameter_substitutions: BTreeMap::new(),
}
}

Expand Down
8 changes: 2 additions & 6 deletions compiler/eight-middle/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ pub struct HirFunction<'hir> {
/// // or if the module is printed before type inference
/// fn foo<T>(x: T) -> T {}
/// ```
pub type_parameter_substitutions: BTreeMap<&'hir str, &'hir HirTy<'hir>>,
pub type_parameter_substitutions: BTreeMap<&'hir str, (&'hir HirTy<'hir>, Span)>,
pub linkage_type: LinkageType,
}

Expand All @@ -344,6 +344,7 @@ pub struct HirTrait<'hir> {
pub name: &'hir str,
pub name_span: Span,
pub signature: &'hir HirTraitSignature<'hir>,
pub type_parameter_substitutions: BTreeMap<&'hir str, (&'hir HirTy<'hir>, Span)>,
}

#[derive(Debug)]
Expand All @@ -362,11 +363,6 @@ pub struct HirInstance<'hir> {
pub type_arguments: Vec<&'hir HirTy<'hir>>,
pub members: Vec<HirFunction<'hir>>,
pub signature: &'hir HirInstanceSignature<'hir>,
/// See [`HirFunction::type_parameter_substitutions`] for explanation.
///
/// These are instantiations from the trait itself. Separate ones will be created for each
/// member.
pub type_parameter_substitutions: BTreeMap<&'hir str, &'hir HirTy<'hir>>,
}

/// A module containing all the types and functions defined in a program.
Expand Down
2 changes: 1 addition & 1 deletion compiler/eight-middle/src/passes/hir_textual_pass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ impl<'a> HirModuleTextualPass<'a> {
.append(self.arena.intersperse(
function.signature.type_parameters.iter().map(|p| {
match function.type_parameter_substitutions.get(p.name) {
Some(t) => self.visit_ty(t),
Some((t, _)) => self.visit_ty(t),
None => self.arena.text(p.name),
}
}),
Expand Down
24 changes: 20 additions & 4 deletions compiler/eight-middle/src/passes/hir_type_check_pass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::hir::{
};
use crate::HirResult;
use eight_support::errors::hir::{
HirError, InvalidReferenceError, TypeFieldInfiniteRecursionError, UnknownTypeError,
WrongTraitTypeArgumentCount,
DuplicateTypeParameterError, HirError, InvalidReferenceError, TypeFieldInfiniteRecursionError,
UnknownTypeError, WrongTraitTypeArgumentCount,
};
use eight_support::ice;
use eight_support::span::Span;
Expand Down Expand Up @@ -155,9 +155,16 @@ impl HirModuleTypeCheckerPass {
cx.enter_type_binding_scope();
// Instantiate all the HirTy::Variable types from the function's type variables
for type_parameter in node.signature.type_parameters.iter() {
if let Some((_, def)) = node.type_parameter_substitutions.get(type_parameter.name) {
return Err(HirError::from(DuplicateTypeParameterError {
name: type_parameter.name.to_owned(),
span: type_parameter.name_span,
previous: *def,
}));
}
let ty = cx.instantiate_type_parameter(type_parameter.ty);
node.type_parameter_substitutions
.insert(type_parameter.name, ty);
.insert(type_parameter.name, (ty, type_parameter.name_span));
}

// Propagate the parameter and return types. If any of these are HirTy::Variable, they are
Expand Down Expand Up @@ -260,7 +267,16 @@ impl HirModuleTypeCheckerPass {
// this makes the `T` in `trait Foo<T> {}` visible to the trait body. While there are no let
// bindings in traits because they are ambient, methods can still use these types.
for type_parameter in node.signature.type_parameters.iter() {
cx.instantiate_type_parameter(type_parameter.ty);
if let Some((_, def)) = node.type_parameter_substitutions.get(type_parameter.name) {
return Err(HirError::from(DuplicateTypeParameterError {
name: type_parameter.name.to_owned(),
span: type_parameter.name_span,
previous: *def,
}));
}
let ty = cx.instantiate_type_parameter(type_parameter.ty);
node.type_parameter_substitutions
.insert(type_parameter.name, (ty, type_parameter.name_span));
}

// Iterate through the ambient method declarations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use eight_support::errors::hir::{
DuplicateLetBindingInSameScopeError, FunctionTypeMismatchError, HirError,
InvalidFieldReferenceOfNonStructError, InvalidStructFieldReferenceError, MissingFieldError,
SelfReferentialTypeError, TraitDoesNotExistError, TraitInstanceMissingFnError,
TraitMethodDoesNotExistError, TraitMissingInstanceError, TypeMismatchError,
TypeParameterShadowsExisting, UnknownFieldError, WrongFunctionTypeArgumentCount,
TraitMethodDoesNotExistError, TraitMissingInstanceError, TypeMismatchError, UnknownFieldError,
WrongFunctionTypeArgumentCount,
};
use eight_support::ice;
use eight_support::span::Span;
Expand Down Expand Up @@ -119,19 +119,6 @@ impl<'hir> TypingContext<'hir> {
span: Span,
ty: &'hir HirTy<'hir>,
) -> HirResult<()> {
let current_depth = self.type_binding_context.depth();
if self
.type_binding_context
.find_within_depth(&name, current_depth)
.is_some()
{
return Err(HirError::TypeParameterShadowsExisting(
TypeParameterShadowsExisting {
name: name.to_owned(),
span,
},
));
}
self.type_binding_context.add(name, (ty, span));
Ok(())
}
Expand Down
10 changes: 6 additions & 4 deletions compiler/eight-support/src/errors/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ declare_error_type! {
TraitMethodDoesNotExist(TraitMethodDoesNotExistError),
TraitMissingInstance(TraitMissingInstanceError),
WrongTraitTypeArgumentCount(WrongTraitTypeArgumentCount),
TypeParameterShadowsExisting(TypeParameterShadowsExisting),
DuplicateTypeParameter(DuplicateTypeParameterError),
DuplicateLetBindingInSameScope(DuplicateLetBindingInSameScopeError),
ConstructingNonStructType(ConstructingNonStructTypeError),
ConstructingPointerType(ConstructingPointerTypeError),
Expand Down Expand Up @@ -223,10 +223,12 @@ pub struct WrongTraitTypeArgumentCount {
}

#[derive(Error, Diagnostic, Debug)]
#[diagnostic(code(sema::type_parameter_shadows_existing))]
#[error("type parameter {name} shadows existing type parameter")]
pub struct TypeParameterShadowsExisting {
#[diagnostic(code(sema::duplicate_type_parameter))]
#[error("type parameter with name {name} has already been defined for this item")]
pub struct DuplicateTypeParameterError {
pub name: String,
#[label = "previous declared here"]
pub previous: Span,
#[label = "the type parameter {name} shadows an existing type with the same name"]
pub span: Span,
}
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/hir/duplicate-trait-type-parameter.eight
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// RUN: not %eightc %s 2>&1 | %regtest test %s

trait From<T, T> {}
10 changes: 10 additions & 0 deletions tests/ui/hir/duplicate-trait-type-parameter.eight.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Error: sema::duplicate_type_parameter

× type parameter with name T has already been defined for this item
╭─[duplicate-trait-type-parameter.eight:3:12]
2
3trait From<T, T> {}
· ┬ ┬
· │ ╰── the type parameter T shadows an existing type with the same name
· ╰── previous declared here
╰────
3 changes: 3 additions & 0 deletions tests/ui/hir/duplicate-type-parameter.eight
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// RUN: not %eightc %s 2>&1 | %regtest test %s

fn foo<T, T>() {}
10 changes: 10 additions & 0 deletions tests/ui/hir/duplicate-type-parameter.eight.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Error: sema::duplicate_type_parameter

× type parameter with name T has already been defined for this item
╭─[duplicate-type-parameter.eight:3:8]
2
3fn foo<T, T>() {}
· ┬ ┬
· │ ╰── the type parameter T shadows an existing type with the same name
· ╰── previous declared here
╰────

0 comments on commit 58fbc5c

Please sign in to comment.