Skip to content

Commit

Permalink
Get through typechecking! Exciting
Browse files Browse the repository at this point in the history
  • Loading branch information
Victorious3 committed Apr 14, 2024
1 parent 71e36da commit 81a6851
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 49 deletions.
9 changes: 7 additions & 2 deletions src/consteval.pr
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ export def walk_Def(node: &parser::Node, state: &typechecking::State) {
let impl = node.value.def_.impl
let has_yield = node.value.def_.has_yield

let prev_context = state.context
state.context = state.module.scope
defer state.context = prev_context

var body = node.value.def_.body
let imported = (share !int & parser::ShareMarker::IMPORT !int) != 0
let exported = (share !int & parser::ShareMarker::EXPORT !int) != 0
Expand Down Expand Up @@ -439,9 +443,10 @@ export def walk_Def(node: &parser::Node, state: &typechecking::State) {
let type_constructor = typechecking::get_type_constructor(first_param.tpe)
if type_constructor and type_constructor.cache {
let type_name = debug::type_to_str(first_param.tpe, full_name = true)
if not map::contains(type_constructor.cache, type_name) {
let ref = [module = state.get_context(), name = type_name] !typechecking::TypeRef
if not map::contains(type_constructor.cache, ref) {
typechecking::generate_concrete_functions(type_constructor, first_param.tpe, state)
type_constructor.cache(type_name) = first_param.tpe
type_constructor.cache(ref) = first_param.tpe
}
}
}
Expand Down
42 changes: 36 additions & 6 deletions src/scope.pr
Original file line number Diff line number Diff line change
Expand Up @@ -1145,21 +1145,35 @@ export def get_function_check(
var value = first_function

// Check for polymorphic instances
if not first_function or first_function.tpe.is_polymorph {
if is_global(scope) and (not first_function or first_function.tpe.is_polymorph) {
var root_scope = context.module.scope
var new_score = std::MAX_INT32

if root_scope.polymorphics {
// We need to check the path we took to get here and then see if
// there is a module under that path
var poly_value: &Value
if path.length > 0 {
let module_scope = get(root_scope, parser::make_identifier(@path.to_array()))
if module_scope and root_scope.polymorphics.contains(module_scope.module.filename) {
// If there is a module we can look into the polymorphic instances of that module
let poly_scope = root_scope.polymorphics(module_scope.module.filename)
if poly_scope {
let val = poly_scope.fields.get_or_default(name, null)
var new_score = std::MAX_INT32
let new_code, new_value = find_function(poly_scope, id, val, parameter_t, *new_score, dry_run, context)
if new_value {
if new_value and new_score < score {
return new_code, new_value
}
}
}
} else if first_function and first_function.tpe.is_polymorph {
if root_scope.polymorphics.contains(first_function.module.filename) {
// We found the function and we should only look into the polymorphs from this module
let poly_scope = root_scope.polymorphics(first_function.module.filename)
if poly_scope {
let val = poly_scope.fields.get_or_default(name, null)
let new_code, new_value = find_function(poly_scope, id, val, parameter_t, *new_score, dry_run, context)
if new_value and new_score < score {
return new_code, new_value
}
}
Expand All @@ -1170,9 +1184,8 @@ export def get_function_check(
let poly_scope = root_scope.polymorphics(module_name)
if poly_scope {
let val = poly_scope.fields.get_or_default(name, null)
var new_score = std::MAX_INT32
let new_code, new_value = find_function(poly_scope, id, val, parameter_t, *new_score, dry_run, context)
if new_value {
if new_value and new_score < score {
return new_code, new_value
}
}
Expand Down Expand Up @@ -1228,6 +1241,23 @@ export def get_function_check(
if value {
share = value.share
}

// Try to find function based on the first parameter. This is needed for using functions
// from another module while not being in that module's context. Without this calls like object.hash()
// don't work because you don't import object's module directly
if not value and vector::length(parameter_t) > 0 {
let first_param = parameter_t(0)
let module = typechecking::get_module(first_param.tpe)
if first_param.tpe and module and module != scope.module {
let scope = module.scope
var new_score = std::MAX_INT32 !int
let code2, new_value = find_function(scope, id, scope.fields.get_or_default(name, null), parameter_t, *new_score, dry_run, context)
if code2 { code = true }
if new_value {
value = new_value
}
}
}

if not value and context.module != scope.module and scope.module != builtins::builtins {
// Get value from context
Expand Down Expand Up @@ -1269,7 +1299,7 @@ export def get_function_check(
if root_scope.polymorphics {
let poly_scope = root_scope.polymorphics.get_or_default(module.filename, null)
if poly_scope {
let code, value = get_function_check(poly_scope, id_tail, parameter_t, force_compile, only_function, context = context, path = path)
let code, value = get_function_check(poly_scope, id_tail, parameter_t, dry_run, force_compile, only_function, context = context, path = path)
if value {
return code, value
}
Expand Down
2 changes: 1 addition & 1 deletion src/serialize.pr
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ def deserialize_type(deserialize: &Deserialize, fp: File, tpe: &typechecking::Ty
let node = deserialize_type_node(deserialize, loc)
tpe.tc_node = node
}
tpe.cache = map::make(type &typechecking::Type)
tpe.cache = map::make(type typechecking::TypeRef, type &typechecking::Type)
case typechecking::TypeKind::GENERIC
tpe._tpe = deserialize_type(deserialize, fp)
case typechecking::TypeKind::BOOL
Expand Down
92 changes: 52 additions & 40 deletions src/typechecking.pr
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,21 @@ export type StructuralTypeMember = struct {
return_t: &Vector(&Type)
}

export type TypeRef = struct {
module: weak_ref(toolchain::Module)
name: Str
}

export def == (a: TypeRef, b: TypeRef) -> bool {
return a.module == b.module and a.name == b.name
}
export def != (a: TypeRef, b: TypeRef) -> bool {
return not (a == b)
}
export def hash(v: TypeRef) -> uint64 {
return combine_hashes(hash(v.module), hash(v.name))
}

// TODO Really, types should not be copied. There should only be one instance for every given type.
// This would make comparing them so much easier but changing that at this point might be too much work.
// It would also make the whole TypeMeta thing useless in the process.
Expand Down Expand Up @@ -142,7 +157,7 @@ export type Type = struct {
// Type constructors save a template of a type
tc_node: weak_ref(parser::Node)
// Cache of specialized type constructor
cache: &SMap(&Type)
cache: &Map(TypeRef, &Type)
// Type constructor instances have a type and arguments
tc_tpe: weak_ref(Type)
// Vector of Type, also used by GENERIC
Expand Down Expand Up @@ -343,11 +358,8 @@ export def has_user_defined_copy_constructor(tpe: &Type) -> bool {
if tpe.kind == TypeKind::STATIC_ARRAY { res = has_user_defined_copy_constructor(tpe.tpe) }
else if tpe.module {
let args = vector::make(NamedParameter)
let arg = [
_tpe = pointer(tpe)
] !NamedParameter
args.push(arg)
args.push(arg)
args.push([ _tpe = pointer(tpe)] !NamedParameter)
args.push([ _tpe = pointer(tpe)] !NamedParameter)

var module = tpe.module
if tpe.tc_tpe { module = tpe.tc_tpe.module }
Expand Down Expand Up @@ -525,6 +537,11 @@ export type State = struct {
context: &scope::Scope
}

export def get_context(state: &State) -> &toolchain::Module {
if state.context { return state.context.module }
return state.module
}

var lambda_counter = 0 // TODO Why is this global state?

def current_value(state: &State) -> &scope::Value {
Expand Down Expand Up @@ -1003,7 +1020,7 @@ export type TypeEntryMember = struct {
}

export def create_type_entry(tpe: &Type) -> &TypeEntry {
if is_polymorph(tpe) { return null }
//if is_polymorph(tpe) { return null }
let name = debug::type_to_str(tpe, full_name = true)
var type_entry = types_map.get_or_default(name, null)
if not type_entry {
Expand Down Expand Up @@ -1060,18 +1077,23 @@ export def copy_return_t(return_t: &Vector(&Type)) -> &Vector(&Type) {
return new
}

def copy_types(a: &Vector(&Type)) -> &Vector(&Type) {
let res = vector::make(type &Type)
for var i in 0..vector::length(a) {
let arg = a(i)
res.push(copy(arg))
}
return res
}

// TODO Not a deep copy
export def copy(a: &Type) -> &Type {
if not a { return null }
var t: &Type = @a
t._hash = 0

if a.tc_args {
t.tc_args = vector::make(type &Type)
for var i in 0..vector::length(a.tc_args) {
let arg = a.tc_args(i)
t.tc_args.push(copy(arg))
}
t.tc_args = copy_types(a.tc_args)
}

if a.kind == TypeKind::FUNCTION {
Expand Down Expand Up @@ -1898,8 +1920,8 @@ def replace_parameter(tpe: &Type, types: &SMap(&Type)) -> &Type {
tpe.tc_args(i) = replace_parameter(arg, types)
}
} else if is_box(tpe) {
if tpe._tpe {
tpe._tpe = replace_parameter(tpe._tpe, types)
if tpe.tpe {
tpe._tpe = replace_parameter(tpe.tpe, types)
}
} else if tpe.kind == TypeKind::FUNCTION or tpe.kind == TypeKind::CLOSURE {
for var i in 0..vector::length(tpe.parameter_t) {
Expand Down Expand Up @@ -1938,9 +1960,9 @@ export def replace_type_defs(
}

for var i in 0..vector::length(param_a) {
let left = param_a(i)
let left = param_a.get(i)

let ltpe = left.tpe
let ltpe = copy(left.tpe)
if not ltpe { continue }

var right: *NamedParameter = null
Expand Down Expand Up @@ -1968,7 +1990,7 @@ export def replace_type_defs(

let intf = get_interface(ltpe)
if intf and replace {
@ltpe = @rtpe
left._tpe = rtpe
continue
}

Expand All @@ -1979,6 +2001,7 @@ export def replace_type_defs(
}
} else {
find_type_defs(module, ltpe, rtpe, types_map, replace)
left._tpe = ltpe
}

for var j in (i + 1)..vector::length(param_a) {
Expand Down Expand Up @@ -2366,16 +2389,19 @@ def lookup_type_constructor(tc: &parser::Node, node: &parser::Node, state: &Stat
tpe.tc_node = node
tpe.tc_node.module = state.module
tpe.module = state.module
tpe.cache = map::make(type &Type)
tpe.cache = map::make(TypeRef, type &Type)
return tpe
}

// This generates concrete functons when refering to type constructors
export def generate_concrete_functions(type_constructor: &Type, tpe: &Type, state: &State) {
if consteval::is_static { return }
if is_polymorph(tpe) { return }

let functions = get_member_functions(tpe)
for var j in 0..vector::length(functions) {
let function = functions(j)

let ftpe = copy(function.function)
if ftpe.kind != TypeKind::FUNCTION { continue }

Expand All @@ -2384,26 +2410,11 @@ export def generate_concrete_functions(type_constructor: &Type, tpe: &Type, stat

replace_type_defs(ftpe.parameter_t, ftpe.return_t, pars, null, state.module)
ftpe.type_name = mangle_function_name(append_module(ftpe.name, ftpe.module.module), ftpe.parameter_t, context = ftpe.context)
if not is_polymorph(tpe) and not is_polymorph(ftpe) {

if not is_polymorph(ftpe) {
let new_entry = create_type_entry(tpe, function.exported, ftpe, function.module)
if new_entry {
let state2 = consteval::copy_state(state)
state2.scope = ftpe.node.module.scope
state2.module = ftpe.node.module
state2.function_stack = vector::make(type &compiler::Function)
if state.function_stack.length > 0 {
state2.function_stack.push(state.function_stack(0))
}

if not scope::has_function(state.scope, ftpe) {
if consteval::is_static {
let value = scope::get_function(state2.scope, parser::make_identifier(ftpe.name), pars, false, true, force_compile = false)
consteval::compile_function(value, state2.scope, pars)
} else {
walk_Def_with_type_argument(ftpe.node, pars, state.scope, state2)
}
}
let score, value = scope::get_function_check(state.scope, parser::make_identifier(ftpe.name), pars, false, true, force_compile = true, context = state.context)
}
}
}
Expand All @@ -2418,10 +2429,11 @@ export def type_lookup(node: &parser::Node, state: &State, current_type: &Type =
if tpe and type_constructor and type_constructor.kind != TypeKind::STUB {
let type_name = debug::type_to_str(tpe, full_name = true)

if not map::contains(type_constructor.cache, type_name) {
type_constructor.cache(type_name) = null
let ref = [module = state.get_context(), name = type_name] !typechecking::TypeRef
if not map::contains(type_constructor.cache, ref) {
type_constructor.cache(ref) = null
generate_concrete_functions(type_constructor, tpe, state)
type_constructor.cache(type_name) = (tpe if tpe.tc_tpe !* == type_constructor !* else null !&Type)
type_constructor.cache(ref) = (tpe if tpe.tc_tpe !* == type_constructor !* else null !&Type)
}
}
if tpe and (tpe.kind == TypeKind::STRUCT or tpe.kind == TypeKind::UNION) {
Expand Down Expand Up @@ -5319,7 +5331,7 @@ def walk_IfExpr(node: &parser::Node, state: &State) {
} else if if_false.tpe and if_false.tpe.kind == TypeKind::NULL and (is_ref_or_weak(if_true.tpe) or is_pointer(if_true.tpe)) {
node.tpe = if_true.tpe
} else if not equals(if_true.tpe, if_false.tpe) {
errors::errorn(node, "If expression needs to have a single type")
errors::errorn(node, "If expression needs to have a single type. Types were ", debug::type_to_str(if_true.tpe), " and ", debug::type_to_str(if_false.tpe))
} else {
node.tpe = if_true.tpe
}
Expand Down

0 comments on commit 81a6851

Please sign in to comment.