Commit 8e41f67f authored by Yorick Peterse's avatar Yorick Peterse

Rewrite the compiler type system

The new type system is much simpler, and this time there are actually
tests for the more important parts of the type system.
parent ff8f878a
Pipeline #22929452 passed with stage
in 4 minutes and 5 seconds
......@@ -12,11 +12,12 @@ require 'inkoc/source_file'
require 'inkoc/source_location'
require 'inkoc/token'
require 'inkoc/lexer'
require 'inkoc/message_context'
require 'inkoc/parser'
require 'inkoc/type_scope'
require 'inkoc/equality'
require 'inkoc/module_paths_cache'
require 'inkoc/visitor_methods'
require 'inkoc/type_pass'
require 'inkoc/ast/type_operations'
require 'inkoc/ast/predicates'
require 'inkoc/ast/type_cast'
......@@ -25,6 +26,7 @@ require 'inkoc/ast/send'
require 'inkoc/ast/block_type'
require 'inkoc/ast/lambda_type'
require 'inkoc/ast/constant'
require 'inkoc/ast/type_name'
require 'inkoc/ast/global'
require 'inkoc/ast/keyword_argument'
require 'inkoc/ast/string'
......@@ -57,6 +59,7 @@ require 'inkoc/ast/module_documentation'
require 'inkoc/ast/method_requirement'
require 'inkoc/ast/dereference'
require 'inkoc/config'
require 'inkoc/constant_resolver'
require 'inkoc/state'
require 'inkoc/diagnostics'
require 'inkoc/diagnostic'
......@@ -70,8 +73,9 @@ require 'inkoc/pass/track_module'
require 'inkoc/pass/add_implicit_import_symbols'
require 'inkoc/pass/compile_imported_modules'
require 'inkoc/pass/desugar_object'
require 'inkoc/pass/define_types'
require 'inkoc/pass/validate_constraints'
require 'inkoc/pass/define_this_module_type'
require 'inkoc/pass/define_import_types'
require 'inkoc/pass/define_type'
require 'inkoc/pass/validate_throw'
require 'inkoc/pass/optimize_keyword_arguments'
require 'inkoc/pass/generate_tir'
......@@ -80,7 +84,6 @@ require 'inkoc/pass/define_module_type'
require 'inkoc/pass/code_generation'
require 'inkoc/pass/code_writer'
require 'inkoc/pass/collect_imports'
require 'inkoc/pass/dead_code'
require 'inkoc/pass/tail_call_elimination'
require 'inkoc/pass/setup_symbol_tables'
require 'inkoc/compiler'
......@@ -125,23 +128,26 @@ require 'inkoc/tir/module'
require 'inkoc/tir/qualified_name'
require 'inkoc/tir/virtual_register'
require 'inkoc/tir/virtual_registers'
require 'inkoc/type/predicates'
require 'inkoc/type/object_operations'
require 'inkoc/type/generic_type_operations'
require 'inkoc/type/type_compatibility'
require 'inkoc/type/object'
require 'inkoc/type/boolean'
require 'inkoc/type/nil'
require 'inkoc/type/block'
require 'inkoc/type/database'
require 'inkoc/type/dynamic'
require 'inkoc/type/constraint'
require 'inkoc/type/trait'
require 'inkoc/type/type_parameter'
require 'inkoc/type/type_parameter_table'
require 'inkoc/type/optional'
require 'inkoc/type/self_type'
require 'inkoc/type/void'
require 'inkoc/type_system/type'
require 'inkoc/type_system/type_with_prototype'
require 'inkoc/type_system/type_with_attributes'
require 'inkoc/type_system/generic_type'
require 'inkoc/type_system/generic_type_with_instances'
require 'inkoc/type_system/without_empty_type_parameters'
require 'inkoc/type_system/new_instance'
require 'inkoc/type_system/type_name'
require 'inkoc/type_system/type_parameter'
require 'inkoc/type_system/type_parameter_table'
require 'inkoc/type_system/type_parameter_instances'
require 'inkoc/type_system/object'
require 'inkoc/type_system/trait'
require 'inkoc/type_system/block'
require 'inkoc/type_system/dynamic'
require 'inkoc/type_system/optional'
require 'inkoc/type_system/self_type'
require 'inkoc/type_system/error'
require 'inkoc/type_system/void'
require 'inkoc/type_system/database'
require 'inkoc/codegen/compiled_code'
require 'inkoc/codegen/instruction'
require 'inkoc/codegen/literals'
......
......@@ -33,13 +33,17 @@ module Inkoc
end
def visitor_method
:on_block
lambda? ? :on_lambda : :on_block
end
def block_type
type
end
def closure?
true
end
def block?
true
end
......
......@@ -39,6 +39,10 @@ module Inkoc
def empty?
@expressions.empty?
end
def location_of_last_expression
last_expression&.location || location
end
end
end
end
......@@ -8,7 +8,6 @@ module Inkoc
include Inspect
attr_reader :name, :location, :receiver
attr_accessor :type_parameters, :optional
# name - The name of the constant as a String.
# location - The SourceLocation of the constant.
......@@ -17,16 +16,24 @@ module Inkoc
@name = name
@receiver = receiver
@location = location
@type_parameters = []
@optional = false
end
def constant?
true
end
def optional?
@optional
def qualified_name
segments = []
source = self
while source
segments << source.name
source = source.receiver
end
segments
.reverse
.join(Config::MODULE_SEPARATOR)
end
def self_type?
......
......@@ -15,6 +15,14 @@ module Inkoc
@location = location
@required_traits = []
end
def type_name
name
end
def visitor_method
:on_define_type_parameter
end
end
end
end
......@@ -27,7 +27,11 @@ module Inkoc
end
def visitor_method
:on_define_variable
if value_type
:on_define_variable_with_explicit_type
else
:on_define_variable
end
end
def variable_definition?
......
......@@ -20,6 +20,10 @@ module Inkoc
def visitor_method
:on_import_glob
end
def expose?
true
end
end
end
end
......@@ -33,6 +33,10 @@ module Inkoc
type
end
def closure?
true
end
def lambda?
true
end
......
......@@ -8,14 +8,14 @@ module Inkoc
include Inspect
attr_reader :name, :arguments, :type_parameters, :returns, :throws,
:type_requirements, :body, :location
:method_bounds, :body, :location
# name - The name of the method.
# args - The arguments of the method.
# returns - The return type of the method.
# throws - The type being thrown by this method.
# required - If the method is a required method in a trait.
# type_requirements - Any method type requirements that are defined.
# method_bounds - Additional type requirements for this method.
# body - The body of the method.
# loc - The SourceLocation of this method.
def initialize(
......@@ -25,7 +25,7 @@ module Inkoc
returns,
throws,
required,
type_requirements,
method_bounds,
body,
loc
)
......@@ -34,7 +34,7 @@ module Inkoc
@type_parameters = targs
@returns = returns
@throws = throws
@type_requirements = type_requirements
@method_bounds = method_bounds
@body = body
@location = loc
@required = required
......@@ -46,7 +46,7 @@ module Inkoc
end
def visitor_method
:on_method
required? ? :on_required_method : :on_method
end
def method?
......
......@@ -75,6 +75,10 @@ module Inkoc
false
end
def closure?
false
end
def self_type?
false
end
......@@ -86,6 +90,10 @@ module Inkoc
def void_type?
false
end
def send?
false
end
end
end
end
......@@ -23,6 +23,10 @@ module Inkoc
def return?
true
end
def value_location
value ? value.location : location
end
end
end
end
......@@ -24,6 +24,10 @@ module Inkoc
@method_type = nil
end
def send?
true
end
def visitor_method
if raw_instruction?
:on_raw_instruction
......
......@@ -7,17 +7,17 @@ module Inkoc
include Predicates
include Inspect
attr_reader :trait_name, :object_names, :body, :location
attr_reader :trait_name, :object_name, :body, :location
attr_accessor :block_type
# trait_name - The name of the trait being implemented.
# object_names - The names of the objects to implement the trait for.
# object_name - The name of the object to implement the trait for.
# body - The body of the implementation.
# location - The SourceLocation of the implementation.
def initialize(trait_name, object_names, body, location)
def initialize(trait_name, object_name, body, location)
@trait_name = trait_name
@object_names = object_names
@object_name = object_name
@body = body
@location = location
@block_type = nil
......
......@@ -39,36 +39,15 @@ module Inkoc
else_argument&.name
end
def type_scope_for_else(self_type)
scope = TypeScope.new(self_type, else_block_type, else_body.locals)
scope.define_self_local
scope
end
def define_else_argument_type
return unless (arg_name = else_argument_name)
type = throw_type || Type::Dynamic.new
else_block_type.define_required_argument(arg_name, type)
else_body.locals.define(arg_name, type)
end
def throw_type
btype =
if expression.throw?
try_block_type
else
expression.block_type
end
if btype&.physical_type?
# For method calls (e.g. "try foo") we want the thrown type to resolve
# to the type thrown my the "fo" method.
btype.throws
else
btype
if expression.throw?
expression.type
elsif expression.send?
expression.block_type&.throw_type
elsif expression.identifier?
# The identifier might be a local variable, in which case "block_type"
# is not set.
expression.block_type&.throw_type
end
end
end
......
# frozen_string_literal: true
module Inkoc
module AST
class TypeName
include TypeOperations
include Predicates
include Inspect
attr_reader :name, :location, :type_parameters
attr_accessor :optional
# name - The name of the type.
# location - The SourceLocation of the type.
def initialize(name, type_parameters, location)
@name = name
@location = location
@type_parameters = type_parameters
@optional = false
end
def type_name
name.name
end
def optional?
@optional
end
def qualified_name
name.qualified_name
end
def visitor_method
case name.name
when Config::SELF_TYPE
:on_self_type
when Config::DYNAMIC_TYPE
:on_dynamic_type
when Config::VOID_TYPE
:on_void_type
else
:on_type_name
end
end
end
end
end
......@@ -6,7 +6,7 @@ module Inkoc
attr_accessor :type
def block_type
Type::Void.new
TypeSystem::Void.new
end
end
end
......
......@@ -15,12 +15,12 @@ module Inkoc
Pass::AddImplicitImportSymbols,
Pass::CompileImportedModules,
Pass::SetupSymbolTables,
Pass::DefineTypes,
Pass::ValidateConstraints,
Pass::DefineThisModuleType,
Pass::DefineImportTypes,
Pass::DefineType,
Pass::ValidateThrow,
Pass::OptimizeKeywordArguments,
Pass::GenerateTir,
Pass::DeadCode,
Pass::TailCallElimination,
Pass::CodeGeneration,
Pass::CodeWriter
......
......@@ -26,6 +26,9 @@ module Inkoc
# module.
GLOBALS_MODULE = 'globals'
MARKER_MODULE = 'std::marker'
INKO_CONST = 'Inko'
OBJECT_CONST = 'Object'
TRAIT_CONST = 'Trait'
ARRAY_CONST = 'Array'
......@@ -41,6 +44,8 @@ module Inkoc
FILE_CONST = '<primitive File>'
HASHER_CONST = '<primitive Hasher>'
ARRAY_TYPE_PARAMETER = 'T'
OPTIONAL_CONST = 'Optional'
COMPATIBLE_CONST = 'Compatible'
MODULE_TYPE = 'Module'
SELF_TYPE = 'Self'
......@@ -70,7 +75,13 @@ module Inkoc
INIT_MESSAGE = 'init'
RESERVED_CONSTANTS = Set.new(
[MODULE_GLOBAL, RAW_INSTRUCTION_RECEIVER, SELF_TYPE]
[
MODULE_GLOBAL,
RAW_INSTRUCTION_RECEIVER,
SELF_TYPE,
VOID_TYPE,
DYNAMIC_TYPE
]
).freeze
attr_reader :source_directories, :mode, :target
......
# frozen_string_literal: true
module Inkoc
class ConstantResolver
attr_reader :diagnostics
def initialize(diagnostics)
@diagnostics = diagnostics
end
def resolve(node, scope)
type = resolve_without_error(node, scope)
if type.error?
diagnostics.undefined_constant_error(node.qualified_name, node.location)
end
type
end
def resolve_without_error(node, scope)
source =
node.receiver ? resolve_without_error(node.receiver, scope) : scope
source.lookup_type(node.name) || TypeSystem::Error.new
end
end
end
......@@ -22,6 +22,10 @@ module Inkoc
@level == :error
end
def warning?
@level == :warning
end
def line
location.line
end
......
......@@ -26,6 +26,10 @@ module Inkoc
@entries.any?(&:error?)
end
def warnings?
@entries.any?(&:warning?)
end
def each(&block)
@entries.each(&block)
end
......@@ -39,23 +43,35 @@ module Inkoc
end
def reassign_immutable_local_error(name, location)
error("Cannot reassign immutable local variable #{name}", location)
error(
"Cannot reassign immutable local variable #{name.inspect}",
location
)
end
def reassign_immutable_attribute_error(name, location)
error("Cannot reassign immutable attribute #{name}", location)
error("Cannot reassign immutable attribute #{name.inspect}", location)
end
def reassign_undefined_local_error(name, location)
error("Cannot reassign undefined local variable #{name}", location)
error(
"Cannot reassign undefined local variable #{name.inspect}",
location
)
TypeSystem::Error.new
end
def reassign_undefined_attribute_error(name, location)
error("Cannot reassign undefined attribute #{name}", location)
TypeSystem::Error.new
end
def redefine_existing_local_error(name, location)
error("The local variable #{name} has already been defined", location)
TypeSystem::Error.new
end
def undefined_local_error(name, location)
......@@ -64,10 +80,14 @@ module Inkoc
def redefine_existing_attribute_error(name, location)
error("The attribute #{name} has already been defined", location)
TypeSystem::Error.new
end
def redefine_existing_constant_error(name, location)
error("The constant #{name} has already been defined", location)
TypeSystem::Error.new
end
def undefined_attribute_error(receiver, name, location)
......@@ -87,22 +107,24 @@ module Inkoc
"The type #{tname} does not respond to the message #{msg}",
location
)
TypeSystem::Error.new
end
def undefined_constant_error(name, location)
error("The constant #{name} is undefined", location)
TypeSystem::Error.new
end
def unknown_raw_instruction_error(name, location)
error("The raw instruction #{name} does not exist", location)
end
def unreachable_code_warning(location)
warn('This and any following expressions are unreachable', location)
end
def reopen_invalid_object_error(name, location)
error("Cannot reopen #{name} since it's not an object", location)
TypeSystem::Error.new
end
def define_required_method_on_non_trait_error(location)
......@@ -117,6 +139,8 @@ module Inkoc
"Expected a value of type #{exp_name} instead of #{found_name}",
location
)
TypeSystem::Error.new
end
def return_type_error(expected, found, location)
......@@ -165,22 +189,6 @@ module Inkoc
)
end
def type_parameter_not_implemented_error(param, type, location)
missing = []
name = type.type_name.inspect
param.required_traits.each do |t|
missing << t.type_name unless type.implements_trait?(t)
end
traits = missing.join(', ')
error(
"The type #{name} does not implement the following trait(s): #{traits}",
location
)
end
def argument_count_error(given, range, location)
given_word = given == 1 ? 'was' : 'were'
......@@ -198,6 +206,17 @@ module Inkoc
"but #{given} #{given_word} given",
location
)
TypeSystem::Error.new
end
def type_parameter_count_error(given, exp, location)
error(
"This type requires #{exp} type parameters, but #{given} were given",
location
)
TypeSystem::Error.new
end
def undefined_keyword_argument_error(name, type, location)
......@@ -207,6 +226,8 @@ module Inkoc
"The type #{tname} does not define the argument #{name.inspect}",
location
)
TypeSystem::Error.new
end
def redefine_reserved_constant_error(name, location)
......@@ -221,7 +242,7 @@ module Inkoc
error(
"cannot throw a value of type #{tname} because the enclosing " \
'block does not define a type to throw',
'method does not define a type to throw',
location
)
end
......@@ -252,12 +273,18 @@ module Inkoc
)
end
def redundant_try_warning(location)
warn('This expression will never throw a value', location)
end
def define_instance_attribute_error(name, location)
error(
"Instance attributes such as #{name.inspect} can only be " \
'defined in a constructor method',
location
)
TypeSystem::Error.new
end
def import_undefined_symbol_error(mname, sname, location)
......@@ -273,7 +300,7 @@ module Inkoc
def invalid_type_parameters(type, given, location)
name = type.name.inspect
ex = type.type_parameter_names.join(', ')
ex = type.type_parameters.map(&:name).join(', ')
got = given.join(', ')