Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/instruction/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,28 @@ impl Instruction for Block {

impl TypeCheck for Block {
fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType {
// FIXME: Refactor this iterator. Ugly
let mut ret_ty = None;
let last_type = self
.instructions
.iter_mut()
.map(|inst| inst.type_of(ctx))
.map(|ty| {
if ty == CheckedType::Later {
ret_ty = Some(CheckedType::Later)
}

ty
})
.last()
.unwrap_or(CheckedType::Void);

match &self.is_statement {
true => CheckedType::Void,
false => last_type,
match ret_ty {
Some(early_return_ty) => early_return_ty,
None => match &self.is_statement {
true => CheckedType::Void,
false => last_type,
},
}
}

Expand Down
23 changes: 12 additions & 11 deletions src/instruction/function_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct FunctionCall {
fn_name: String,
generics: Vec<TypeId>,
args: Vec<Box<dyn Instruction>>,
typechecked: bool,
cached_type: Option<CheckedType>,
}

impl FunctionCall {
Expand All @@ -29,7 +29,7 @@ impl FunctionCall {
fn_name,
generics,
args,
typechecked: false,
cached_type: None,
}
}

Expand Down Expand Up @@ -217,7 +217,9 @@ impl Instruction for FunctionCall {

impl TypeCheck for FunctionCall {
fn resolve_type(&mut self, ctx: &mut TypeCtx) -> CheckedType {
if !self.generics.is_empty() {
log!("typechecking call to {}", self.fn_name);

if !self.generics.is_empty() && !ctx.is_second_pass() {
return CheckedType::Later;
}

Expand All @@ -235,7 +237,7 @@ impl TypeCheck for FunctionCall {
};

// If the declaration contains generics but not the call, typecheck later still
if !function.generics().is_empty() {
if !function.generics().is_empty() && !ctx.is_second_pass() {
return CheckedType::Later;
}

Expand Down Expand Up @@ -279,14 +281,11 @@ impl TypeCheck for FunctionCall {
}

fn cached_type(&self) -> Option<&CheckedType> {
match self.typechecked {
true => Some(&CheckedType::Void),
false => None,
}
self.cached_type.as_ref()
}

fn set_cached_type(&mut self, _ty: CheckedType) {
self.typechecked = true;
fn set_cached_type(&mut self, ty: CheckedType) {
self.cached_type = Some(ty);
}
}

Expand Down Expand Up @@ -371,7 +370,9 @@ impl Generic for FunctionCall {
})
.collect();

self.fn_name = generics::mangle(self.name(), &self.generics);
self.fn_name = generic_name;
self.generics = vec![];
self.cached_type = None;
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/instruction/function_declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ impl TypeCheck for FunctionDec {
if let Some(b) = &mut self.block {
let block_ty = b.type_of(ctx);

// If the block's type cannot be determined yet, this is not an
// error: We simply have to wait for the next typechecking pass
if block_ty == CheckedType::Later {
return CheckedType::Later;
}

if block_ty != return_ty {
ctx.error(Error::new(ErrKind::TypeChecker).with_msg(format!(
"invalid type returned in function `{}`: expected type {}, found type {}",
Expand Down
21 changes: 15 additions & 6 deletions src/typechecker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,21 @@ pub trait TypeCheck {
fn type_of(&mut self, ctx: &mut TypeCtx) -> CheckedType {
// FIXME: Remove clones
match self.cached_type() {
None => {
let new_ty = self.resolve_type(ctx);
self.set_cached_type(new_ty.clone());

new_ty
}
None => match self.resolve_type(ctx) {
CheckedType::Resolved(new_ty) => {
self.set_cached_type(CheckedType::Resolved(new_ty.clone()));
CheckedType::Resolved(new_ty)
}
CheckedType::Void => {
self.set_cached_type(CheckedType::Void);
CheckedType::Void
}
CheckedType::Later => match ctx.is_second_pass() {
false => CheckedType::Later,
true => CheckedType::Error,
},
CheckedType::Error => CheckedType::Error,
},
Some(ty) => ty.clone(),
}
}
Expand Down
5 changes: 5 additions & 0 deletions tests/ft/generics/generics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ tests:
args:
- "tests/ft/generics/valid_method_call.jk"
exit_code: 0
- name: "Invalid typechecking on function call"
binary: "target/debug/jinko"
args:
- "tests/ft/generics/invalid_typechecking.jk"
exit_code: 1
9 changes: 9 additions & 0 deletions tests/ft/generics/invalid_typechecking.jk
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
func multi_generics[T, U](a: T, b: U, c: int) -> T {
a
}

type At;
type Bt;
multi_generics[At, Bt](At, Bt); // error: missing argument

@dump();
30 changes: 24 additions & 6 deletions tests/ft/generics/valid_multiple_ducktyping.jk
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,30 @@ incl generic_fns;

type Griffin;

func bark[Griffin](g: Griffin) {}
func eat[Griffin](g: Griffin) {}
func scream[Griffin](g: Griffin) {}
func run[Griffin](g: Griffin) {}
func swim[Griffin](g: Griffin) {}
func fly[Griffin](g: Griffin) {}
func bark[Griffin](g: Griffin) {
println("bark!")
}

func eat[Griffin](g: Griffin) {
println("nom")
}

func scream[Griffin](g: Griffin) {
println("AAAAAAAH")
}

func run[Griffin](g: Griffin) {
println("uf uf uf")
}

func swim[Griffin](g: Griffin) {
println("splish splosh")
}

func fly[Griffin](g: Griffin) {
println("wouuuuuuh")
}


griffin_instance = Griffin;
animal_full_suite[Griffin](griffin_instance);