summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2016-06-10 14:07:27 +0800
committerJohn Hodge <tpg@mutabah.net>2016-06-10 14:07:27 +0800
commit0c7bc726eccd68e7dac8533ceaf6eeebe81c0314 (patch)
tree4453633f37c19008e735108870d623b50e498075 /src
parent91248863bcfba4cc97ae1d3a196fd3f0b2512551 (diff)
downloadmrust-0c7bc726eccd68e7dac8533ceaf6eeebe81c0314.tar.gz
HIR Typecheck - Split expr.cpp into two files - context and enum
Diffstat (limited to 'src')
-rw-r--r--src/hir_typeck/expr.cpp1661
-rw-r--r--src/hir_typeck/expr.hpp187
-rw-r--r--src/hir_typeck/expr_context.cpp1522
3 files changed, 1741 insertions, 1629 deletions
diff --git a/src/hir_typeck/expr.cpp b/src/hir_typeck/expr.cpp
index 19361b0c..4a1a729f 100644
--- a/src/hir_typeck/expr.cpp
+++ b/src/hir_typeck/expr.cpp
@@ -6,7 +6,9 @@
#include <hir/visitor.hpp>
#include <algorithm> // std::find_if
-namespace {
+#include "expr.hpp"
+
+namespace typeck {
bool monomorphise_type_needed(const ::HIR::TypeRef& tpl);
@@ -84,8 +86,6 @@ namespace {
)
throw "";
}
- typedef ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> t_cb_generic;
- ::HIR::TypeRef monomorphise_type_with(const Span& sp, const ::HIR::TypeRef& tpl, t_cb_generic callback, bool allow_infer=true);
::HIR::PathParams monomorphise_path_params_with(const Span& sp, const ::HIR::PathParams& tpl, t_cb_generic callback, bool allow_infer)
{
@@ -200,1637 +200,38 @@ namespace {
return params.m_types[e.binding];
}, false);
}
-
-
- struct IVar
- {
- unsigned int alias; // If not ~0, this points to another ivar
- ::std::unique_ptr< ::HIR::TypeRef> type; // Type (only nullptr if alias!=0)
-
- IVar():
- alias(~0u),
- type(new ::HIR::TypeRef())
- {}
- bool is_alias() const { return alias != ~0u; }
- };
- static const ::std::string EMPTY_STRING;
- struct Variable
- {
- ::std::string name;
- ::HIR::TypeRef type;
-
- Variable()
- {}
- Variable(const ::std::string& name, ::HIR::TypeRef type):
- name( name ),
- type( mv$(type) )
- {}
- Variable(Variable&&) = default;
-
- Variable& operator=(Variable&&) = default;
- };
-
- class TypecheckContext
- {
- public:
- const ::HIR::Crate& m_crate;
- ::std::vector< ::std::pair< const ::HIR::SimplePath*, const ::HIR::Trait* > > m_traits;
- private:
- ::std::vector< Variable> m_locals;
- ::std::vector< IVar> m_ivars;
- bool m_has_changed;
-
- const ::HIR::GenericParams* m_impl_params;
- const ::HIR::GenericParams* m_item_params;
-
- public:
- TypecheckContext(const ::HIR::Crate& crate, const ::HIR::GenericParams* impl_params, const ::HIR::GenericParams* item_params):
- m_crate(crate),
- m_has_changed(false),
- m_impl_params( impl_params ),
- m_item_params( item_params )
- {
- }
-
- void dump() const {
- DEBUG("TypecheckContext - " << m_ivars.size() << " ivars, " << m_locals.size() << " locals");
- unsigned int i = 0;
- for(const auto& v : m_ivars) {
- if(v.is_alias()) {
- DEBUG("#" << i << " = " << v.alias);
- }
- else {
- DEBUG("#" << i << " = " << *v.type);
- }
- i ++ ;
- }
- i = 0;
- for(const auto& v : m_locals) {
- DEBUG("VAR " << i << " '"<<v.name<<"' = " << v.type);
- i ++;
- }
- }
-
- bool take_changed() {
- bool rv = m_has_changed;
- m_has_changed = false;
- return rv;
- }
- void mark_change() {
- DEBUG("- CHANGE");
- m_has_changed = true;
- }
-
- /// Adds a local variable binding (type is mutable so it can be inferred if required)
- void add_local(unsigned int index, const ::std::string& name, ::HIR::TypeRef type)
- {
- if( m_locals.size() <= index )
- m_locals.resize(index+1);
- m_locals[index] = Variable(name, mv$(type));
- }
-
- const ::HIR::TypeRef& get_var_type(const Span& sp, unsigned int index)
- {
- if( index >= m_locals.size() ) {
- this->dump();
- BUG(sp, "Local index out of range " << index << " >= " << m_locals.size());
- }
- return m_locals.at(index).type;
- }
- /// Add (and bind) all '_' types in `type`
- void add_ivars(::HIR::TypeRef& type)
- {
- TU_MATCH(::HIR::TypeRef::Data, (type.m_data), (e),
- (Infer,
- if( e.index == ~0u ) {
- e.index = this->new_ivar();
- this->m_ivars[e.index].type->m_data.as_Infer().ty_class = e.ty_class;
- }
- ),
- (Diverge,
- ),
- (Primitive,
- ),
- (Path,
- // Iterate all arguments
- TU_MATCH(::HIR::Path::Data, (e.path.m_data), (e2),
- (Generic,
- this->add_ivars_params(e2.m_params);
- ),
- (UfcsKnown,
- this->add_ivars(*e2.type);
- this->add_ivars_params(e2.trait.m_params);
- this->add_ivars_params(e2.params);
- ),
- (UfcsUnknown,
- this->add_ivars(*e2.type);
- this->add_ivars_params(e2.params);
- ),
- (UfcsInherent,
- this->add_ivars(*e2.type);
- this->add_ivars_params(e2.params);
- )
- )
- ),
- (Generic,
- ),
- (TraitObject,
- // Iterate all paths
- ),
- (Array,
- add_ivars(*e.inner);
- ),
- (Slice,
- add_ivars(*e.inner);
- ),
- (Tuple,
- for(auto& ty : e)
- add_ivars(ty);
- ),
- (Borrow,
- add_ivars(*e.inner);
- ),
- (Pointer,
- add_ivars(*e.inner);
- ),
- (Function,
- // No ivars allowed
- // TODO: Check?
- ),
- (Closure,
- // Shouldn't be possible
- )
- )
- }
- void add_ivars_params(::HIR::PathParams& params)
- {
- for(auto& arg : params.m_types)
- add_ivars(arg);
- }
-
-
- void add_pattern_binding(const ::HIR::PatternBinding& pb, ::HIR::TypeRef type)
+ void check_type_class_primitive(const Span& sp, const ::HIR::TypeRef& type, ::HIR::InferClass ic, ::HIR::CoreType ct)
+ {
+ switch(ic)
{
- assert( pb.is_valid() );
- switch( pb.m_type )
+ case ::HIR::InferClass::None:
+ break;
+ case ::HIR::InferClass::Float:
+ switch(ct)
{
- case ::HIR::PatternBinding::Type::Move:
- this->add_local( pb.m_slot, pb.m_name, mv$(type) );
- break;
- case ::HIR::PatternBinding::Type::Ref:
- this->add_local( pb.m_slot, pb.m_name, ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, mv$(type)) );
+ case ::HIR::CoreType::F32:
+ case ::HIR::CoreType::F64:
break;
- case ::HIR::PatternBinding::Type::MutRef:
- this->add_local( pb.m_slot, pb.m_name, ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Unique, mv$(type)) );
- break;
- }
- }
-
- void add_binding(const Span& sp, ::HIR::Pattern& pat, ::HIR::TypeRef& type)
- {
- TRACE_FUNCTION_F("pat = " << pat << ", type = " << type);
-
- if( pat.m_binding.is_valid() ) {
- this->add_pattern_binding(pat.m_binding, type.clone());
- // TODO: Can there be bindings within a bound pattern?
- //return ;
- }
-
- //
- TU_MATCH(::HIR::Pattern::Data, (pat.m_data), (e),
- (Any,
- // Just leave it, the pattern says nothing
- ),
- (Value,
- //TODO(sp, "Value pattern");
- ),
- (Range,
- //TODO(sp, "Range pattern");
- ),
- (Box,
- TODO(sp, "Box pattern");
- ),
- (Ref,
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Borrow({ e.type, box$(this->new_ivar_tr()) });
- }
- // Type must be a &-ptr
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Borrow,
- if( te.type != e.type ) {
- // TODO: Type mismatch
- }
- this->add_binding(sp, *e.sub, *te.inner );
- )
- )
- ),
- (Tuple,
- if( type.m_data.is_Infer() ) {
- ::std::vector< ::HIR::TypeRef> sub_types;
- for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
- sub_types.push_back( this->new_ivar_tr() );
- type.m_data = ::HIR::TypeRef::Data::make_Tuple( mv$(sub_types) );
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Tuple,
- if( te.size() != e.sub_patterns.size() ) {
- // TODO: Type mismatch
- }
- for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
- this->add_binding(sp, e.sub_patterns[i], te[i] );
- )
- )
- ),
- (Slice,
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Slice( {box$(this->new_ivar_tr())} );
- this->mark_change();
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw""; ),
- (Slice,
- for(auto& sub : e.sub_patterns)
- this->add_binding(sp, sub, *te.inner );
- )
- )
- ),
- (SplitSlice,
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Slice( {box$(this->new_ivar_tr())} );
- this->mark_change();
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Slice,
- for(auto& sub : e.leading)
- this->add_binding( sp, sub, *te.inner );
- for(auto& sub : e.trailing)
- this->add_binding( sp, sub, *te.inner );
- if( e.extra_bind.is_valid() ) {
- this->add_local( e.extra_bind.m_slot, e.extra_bind.m_name, type.clone() );
- }
- )
- )
- ),
-
- // - Enums/Structs
- (StructTuple,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
- }
- assert(e.binding);
- const auto& str = *e.binding;
- // - assert check from earlier pass
- assert( str.m_data.is_Tuple() );
- const auto& sd = str.m_data.as_Tuple();
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
- ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
- }
- // NOTE: Must be Generic for the above to have passed
- auto& gp = te.path.m_data.as_Generic();
-
- if( e.sub_patterns.size() != sd.size() ) {
- ERROR(sp, E0000, "Tuple struct pattern with an incorrect number of fields");
- }
- for( unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
- {
- const auto& field_type = sd[i].ent;
- if( monomorphise_type_needed(field_type) ) {
- auto var_ty = monomorphise_type(sp, str.m_params, gp.m_params, field_type);
- this->add_binding(sp, e.sub_patterns[i], var_ty);
- }
- else {
- // SAFE: Can't have _ as monomorphise_type_needed checks for that
- this->add_binding(sp, e.sub_patterns[i], const_cast< ::HIR::TypeRef&>(field_type));
- }
- }
- )
- )
- ),
- (StructTupleWildcard,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
- }
- assert(e.binding);
- const auto& str = *e.binding;
- // - assert check from earlier pass
- assert( str.m_data.is_Tuple() );
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
- ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
- }
- )
- )
- ),
- (Struct,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
- }
- assert(e.binding);
- const auto& str = *e.binding;
- // - assert check from earlier pass
- assert( str.m_data.is_Named() );
- const auto& sd = str.m_data.as_Named();
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
- ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
- }
- // NOTE: Must be Generic for the above to have passed
- auto& gp = te.path.m_data.as_Generic();
- for( auto& field_pat : e.sub_patterns )
- {
- unsigned int f_idx = ::std::find_if( sd.begin(), sd.end(), [&](const auto& x){ return x.first == field_pat.first; } ) - sd.begin();
- if( f_idx == sd.size() ) {
- ERROR(sp, E0000, "Struct " << e.path << " doesn't have a field " << field_pat.first);
- }
- const ::HIR::TypeRef& field_type = sd[f_idx].second.ent;
- if( monomorphise_type_needed(field_type) ) {
- auto field_type_mono = monomorphise_type(sp, str.m_params, gp.m_params, field_type);
- this->add_binding(sp, field_pat.second, field_type_mono);
- }
- else {
- // SAFE: Can't have _ as monomorphise_type_needed checks for that
- this->add_binding(sp, field_pat.second, const_cast< ::HIR::TypeRef&>(field_type));
- }
- }
- )
- )
- ),
- (EnumTuple,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- auto path = e.path.clone();
- path.m_path.m_components.pop_back();
- type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
- }
- assert(e.binding_ptr);
- const auto& enm = *e.binding_ptr;
- const auto& var = enm.m_variants[e.binding_idx].second;
- assert(var.is_Tuple());
- const auto& tup_var = var.as_Tuple();
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
- ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
- }
- // NOTE: Must be Generic for the above to have passed
- auto& gp = te.path.m_data.as_Generic();
- if( e.sub_patterns.size() != tup_var.size() ) {
- ERROR(sp, E0000, "Enum pattern with an incorrect number of fields - " << e.path << " - expected " << tup_var.size() << ", got " << e.sub_patterns.size());
- }
- for( unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
- {
- if( monomorphise_type_needed(tup_var[i]) ) {
- auto var_ty = monomorphise_type(sp, enm.m_params, gp.m_params, tup_var[i]);
- this->add_binding(sp, e.sub_patterns[i], var_ty);
- }
- else {
- // SAFE: Can't have a _ (monomorphise_type_needed checks for that)
- this->add_binding(sp, e.sub_patterns[i], const_cast< ::HIR::TypeRef&>(tup_var[i]));
- }
- }
- )
- )
- ),
- (EnumTupleWildcard,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- auto path = e.path.clone();
- path.m_path.m_components.pop_back();
- type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
- }
- assert(e.binding_ptr);
- const auto& enm = *e.binding_ptr;
- const auto& var = enm.m_variants[e.binding_idx].second;
- assert(var.is_Tuple());
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
- ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
- }
- )
- )
- ),
- (EnumStruct,
- this->add_ivars_params( e.path.m_params );
- if( type.m_data.is_Infer() ) {
- auto path = e.path.clone();
- path.m_path.m_components.pop_back();
- type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
- }
- assert(e.binding_ptr);
- const auto& enm = *e.binding_ptr;
- const auto& var = enm.m_variants[e.binding_idx].second;
- assert(var.is_Struct());
- const auto& tup_var = var.as_Struct();
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw ""; ),
- (Path,
- if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
- ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
- }
- // NOTE: Must be Generic for the above to have passed
- auto& gp = te.path.m_data.as_Generic();
-
- for( auto& field_pat : e.sub_patterns )
- {
- unsigned int f_idx = ::std::find_if( tup_var.begin(), tup_var.end(), [&](const auto& x){ return x.first == field_pat.first; } ) - tup_var.begin();
- if( f_idx == tup_var.size() ) {
- ERROR(sp, E0000, "Enum variant " << e.path << " doesn't have a field " << field_pat.first);
- }
- const ::HIR::TypeRef& field_type = tup_var[f_idx].second;
- if( monomorphise_type_needed(field_type) ) {
- auto field_type_mono = monomorphise_type(sp, enm.m_params, gp.m_params, field_type);
- this->add_binding(sp, field_pat.second, field_type_mono);
- }
- else {
- // SAFE: Can't have _ as monomorphise_type_needed checks for that
- this->add_binding(sp, field_pat.second, const_cast< ::HIR::TypeRef&>(field_type));
- }
- }
- )
- )
- )
- )
- }
-
- /// Run inferrence using a pattern
- void apply_pattern(const ::HIR::Pattern& pat, ::HIR::TypeRef& type)
- {
- static Span _sp;
- const Span& sp = _sp;
- // TODO: Should this do an equality on the binding?
-
- auto& ty = this->get_type(type);
-
- TU_MATCH(::HIR::Pattern::Data, (pat.m_data), (e),
- (Any,
- // Just leave it, the pattern says nothing about the type
- ),
- (Value,
- TODO(sp, "Value pattern");
- ),
- (Range,
- TODO(sp, "Range pattern");
- ),
- // - Pointer destructuring
- (Box,
- // Type must be box-able
- TODO(sp, "Box patterns");
- ),
- (Ref,
- if( ty.m_data.is_Infer() ) {
- BUG(sp, "Infer type hit that should already have been fixed");
- }
- // Type must be a &-ptr
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Borrow,
- if( te.type != e.type ) {
- // TODO: Type mismatch
- }
- this->apply_pattern( *e.sub, *te.inner );
- )
- )
- ),
- (Tuple,
- if( ty.m_data.is_Infer() ) {
- BUG(sp, "Infer type hit that should already have been fixed");
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Tuple,
- if( te.size() != e.sub_patterns.size() ) {
- // TODO: Type mismatch
- }
- for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
- this->apply_pattern( e.sub_patterns[i], te[i] );
- )
- )
- ),
- // --- Slices
- (Slice,
- if( ty.m_data.is_Infer() ) {
- BUG(sp, "Infer type hit that should already have been fixed");
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Slice,
- for(const auto& sp : e.sub_patterns )
- this->apply_pattern( sp, *te.inner );
- )
- )
- ),
- (SplitSlice,
- if( ty.m_data.is_Infer() ) {
- BUG(sp, "Infer type hit that should already have been fixed");
- }
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Slice,
- for(const auto& sp : e.leading)
- this->apply_pattern( sp, *te.inner );
- for(const auto& sp : e.trailing)
- this->apply_pattern( sp, *te.inner );
- // TODO: extra_bind? (see comment at start of function)
- )
- )
- ),
-
- // - Enums/Structs
- (StructTuple,
- if( ty.m_data.is_Infer() ) {
- ty.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
- this->mark_change();
- }
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Path,
- // TODO: Does anything need to happen here? This can only introduce equalities?
- )
- )
- ),
- (StructTupleWildcard,
- ),
- (Struct,
- if( ty.m_data.is_Infer() ) {
- //TODO: Does this lead to issues with generic parameters?
- ty.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
- this->mark_change();
- }
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Path,
- // TODO: Does anything need to happen here? This can only introduce equalities?
- )
- )
- ),
- (EnumTuple,
- if( ty.m_data.is_Infer() ) {
- TODO(sp, "EnumTuple - infer");
- this->mark_change();
- }
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Path,
- )
- )
- ),
- (EnumTupleWildcard,
- ),
- (EnumStruct,
- if( ty.m_data.is_Infer() ) {
- TODO(sp, "EnumStruct - infer");
- this->mark_change();
- }
-
- TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
- (
- // TODO: Type mismatch
- ),
- (Infer, throw "";),
- (Path,
- )
- )
- )
- )
- }
- // Adds a rule that two types must be equal
- // - NOTE: The ordering does matter, as the righthand side will get unsizing/deref coercions applied if possible
- /// \param sp Span for reporting errors
- /// \param left Lefthand type (destination for coercions)
- /// \param right Righthand type (source for coercions)
- /// \param node_ptr Pointer to ExprNodeP, updated with new nodes for coercions
- void apply_equality(const Span& sp, const ::HIR::TypeRef& left, const ::HIR::TypeRef& right, ::HIR::ExprNodeP* node_ptr_ptr = nullptr)
- {
- apply_equality(sp, left, [](const auto& x)->const auto&{return x;}, right, [](const auto& x)->const auto&{return x;}, node_ptr_ptr);
- }
-
- const ::HIR::TypeRef& expand_associated_types_to(const Span& sp, const ::HIR::TypeRef& t, ::HIR::TypeRef& tmp_t) const {
- TU_IFLET(::HIR::TypeRef::Data, t.m_data, Path, e,
- if( e.path.m_data.is_Generic() )
- return t;
- else {
- tmp_t = this->expand_associated_types(sp, t.clone());
- DEBUG("Expanded " << t << " into " << tmp_t);
- return tmp_t;
- }
- )
- else {
- return t;
+ default:
+ ERROR(sp, E0000, "Type unificiation of integer literal with non-integer - " << type);
}
- }
- void apply_equality(const Span& sp, const ::HIR::TypeRef& left, t_cb_generic cb_left, const ::HIR::TypeRef& right, t_cb_generic cb_right, ::HIR::ExprNodeP* node_ptr_ptr)
- {
- TRACE_FUNCTION_F(left << ", " << right);
- assert( ! left.m_data.is_Infer() || left.m_data.as_Infer().index != ~0u );
- assert( !right.m_data.is_Infer() || right.m_data.as_Infer().index != ~0u );
- // - Convert left/right types into resolved versions (either root ivar, or generic replacement)
- const auto& l_t1 = left.m_data.is_Generic() ? cb_left (left ) : this->get_type(left );
- const auto& r_t1 = right.m_data.is_Generic() ? cb_right(right) : this->get_type(right);
- if( l_t1 == r_t1 ) {
- return ;
- }
-
- ::HIR::TypeRef left_tmp;
- const auto& l_t = this->expand_associated_types_to(sp, l_t1, left_tmp);
- ::HIR::TypeRef right_tmp;
- const auto& r_t = this->expand_associated_types_to(sp, r_t1, right_tmp);
-
- DEBUG("- l_t = " << l_t << ", r_t = " << r_t);
- TU_IFLET(::HIR::TypeRef::Data, r_t.m_data, Infer, r_e,
- TU_IFLET(::HIR::TypeRef::Data, l_t.m_data, Infer, l_e,
- // If both are infer, unify the two ivars (alias right to point to left)
- this->ivar_unify(l_e.index, r_e.index);
- )
- else {
- // Righthand side is infer, alias it to the left
- // TODO: that `true` should be `false` if the callback isn't unity (for bug checking)
- this->set_ivar_to(r_e.index, monomorphise_type_with(sp, left, cb_left, true));
- }
- )
- else {
- TU_IFLET(::HIR::TypeRef::Data, l_t.m_data, Infer, l_e,
- // Lefthand side is infer, alias it to the right
- // TODO: that `true` should be `false` if the callback isn't unity (for bug checking)
- this->set_ivar_to(l_e.index, monomorphise_type_with(sp, right, cb_right, true));
- )
- else {
- // If generic replacement happened, clear the callback
- if( left.m_data.is_Generic() ) {
- cb_left = [](const auto& x)->const auto&{return x;};
- }
- if( right.m_data.is_Generic() ) {
- cb_right = [](const auto& x)->const auto&{return x;};
- }
-
- // Neither are infer - both should be of the same form
- // - If either side is `!`, return early (diverging type, matches anything)
- if( l_t.m_data.is_Diverge() || r_t.m_data.is_Diverge() ) {
- // TODO: Should diverge check be done elsewhere? what happens if a ! ends up in an ivar?
- return ;
- }
-
- // Helper function for Path and TraitObject
- auto equality_typeparams = [&](const ::HIR::PathParams& l, const ::HIR::PathParams& r) {
- if( l.m_types.size() != r.m_types.size() ) {
- ERROR(sp, E0000, "Type mismatch in type params `" << l << "` and `" << r << "`");
- }
- for(unsigned int i = 0; i < l.m_types.size(); i ++)
- {
- this->apply_equality(sp, l.m_types[i], cb_left, r.m_types[i], cb_right, nullptr);
- }
- };
-
- if( l_t.m_data.is_Pointer() && r_t.m_data.is_Borrow() ) {
- const auto& l_e = l_t.m_data.as_Pointer();
- const auto& r_e = r_t.m_data.as_Borrow();
- if( l_e.type != r_e.type ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " (pointer type mismatch)");
- }
- // 1. Equate inner types
- this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
-
- // 2. If that succeeds, add a coerce
- if( node_ptr_ptr != nullptr )
- {
- auto& node_ptr = *node_ptr_ptr;
- auto span = node_ptr->span();
- // - Override the existing result type (to prevent infinite recursion)
- node_ptr->m_res_type = r_t.clone();
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), l_t.clone() ));
- //node_ptr->m_res_type = l_t.clone(); // < Set by _Cast
-
- DEBUG("- Borrow->Pointer cast added - " << l_t << " <- " << r_t);
- this->mark_change();
- return ;
- }
- else
- {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " (can't coerce)");
- }
- }
-
- // - If tags don't match, error
- if( l_t.m_data.tag() != r_t.m_data.tag() ) {
- // Type error
- this->dump();
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- TU_MATCH(::HIR::TypeRef::Data, (l_t.m_data, r_t.m_data), (l_e, r_e),
- (Infer,
- throw "";
- ),
- (Diverge,
- TODO(sp, "Handle !");
- ),
- (Primitive,
- if( l_e != r_e ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- ),
- (Path,
- if( l_e.path.m_data.tag() != r_e.path.m_data.tag() ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- TU_MATCH(::HIR::Path::Data, (l_e.path.m_data, r_e.path.m_data), (lpe, rpe),
- (Generic,
- if( lpe.m_path != rpe.m_path ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- equality_typeparams(lpe.m_params, rpe.m_params);
- ),
- (UfcsInherent,
- equality_typeparams(lpe.params, rpe.params);
- if( lpe.item != rpe.item )
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
- ),
- (UfcsKnown,
- equality_typeparams(lpe.trait.m_params, rpe.trait.m_params);
- equality_typeparams(lpe.params, rpe.params);
- if( lpe.item != rpe.item )
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
- ),
- (UfcsUnknown,
- // TODO: If the type is fully known, locate a suitable trait item
- equality_typeparams(lpe.params, rpe.params);
- if( lpe.item != rpe.item )
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
- )
- )
- ),
- (Generic,
- if( l_e.binding != r_e.binding ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- ),
- (TraitObject,
- if( l_e.m_traits.size() != r_e.m_traits.size() ) {
- // TODO: Possibly allow inferrence reducing the set?
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - trait counts differ");
- }
- // NOTE: Lifetime is ignored
- // TODO: Is this list sorted in any way? (if it's not sorted, this could fail when source does Send+Any instead of Any+Send)
- for(unsigned int i = 0; i < l_e.m_traits.size(); i ++ )
- {
- auto& l_p = l_e.m_traits[i];
- auto& r_p = r_e.m_traits[i];
- if( l_p.m_path != r_p.m_path ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- equality_typeparams(l_p.m_params, r_p.m_params);
- }
- ),
- (Array,
- this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
- if( l_e.size_val != r_e.size_val ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - sizes differ");
- }
- ),
- (Slice,
- this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
- ),
- (Tuple,
- if( l_e.size() != r_e.size() ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Tuples are of different length");
- }
- for(unsigned int i = 0; i < l_e.size(); i ++)
- {
- this->apply_equality(sp, l_e[i], cb_left, r_e[i], cb_right, nullptr);
- }
- ),
- (Borrow,
- if( l_e.type != r_e.type ) {
- // TODO: This could be allowed if left == Shared && right == Unique (reborrowing)
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Borrow classes differ");
- }
- // ------------------
- // Coercions!
- // ------------------
- if( node_ptr_ptr != nullptr )
- {
- auto& node_ptr = *node_ptr_ptr;
- const auto& left_inner_res = this->get_type(*l_e.inner);
- const auto& right_inner_res = this->get_type(*r_e.inner);
-
- // Allow cases where `right`: ::core::marker::Unsize<`left`>
- bool succ = this->find_trait_impls(this->m_crate.get_lang_item_path(sp, "unsize"), right_inner_res, [&](const auto& args) {
- DEBUG("- Found unsizing with args " << args);
- return args.m_types[0] == left_inner_res;
- });
- if( succ ) {
- auto span = node_ptr->span();
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Unsize( mv$(span), mv$(node_ptr), l_t.clone() ));
- node_ptr->m_res_type = l_t.clone();
-
- this->mark_change();
- return ;
- }
- // - If left is a trait object, right can unsize
- // - If left is a slice, right can unsize/deref
- if( left_inner_res.m_data.is_Slice() && !right_inner_res.m_data.is_Slice() )
- {
- const auto& left_slice = left_inner_res.m_data.as_Slice();
- TU_IFLET(::HIR::TypeRef::Data, right_inner_res.m_data, Array, right_array,
- this->apply_equality(sp, *left_slice.inner, cb_left, *right_array.inner, cb_right, nullptr);
- auto span = node_ptr->span();
- node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Unsize( mv$(span), mv$(node_ptr), l_t.clone() ));
- node_ptr->m_res_type = l_t.clone();
-
- this->mark_change();
- return ;
- )
- else TU_IFLET(::HIR::TypeRef::Data, right_inner_res.m_data, Generic, right_arg,
- TODO(sp, "Search for Unsize bound on generic");
- )
- else
- {
- // Apply deref coercions
- }
- }
- // - If right has a deref chain to left, build it
- }
- this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
- ),
- (Pointer,
- if( l_e.type != r_e.type ) {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Pointer mutability differs");
- }
- this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
- ),
- (Function,
- if( l_e.is_unsafe != r_e.is_unsafe
- || l_e.m_abi != r_e.m_abi
- || l_e.m_arg_types.size() != r_e.m_arg_types.size()
- )
- {
- ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
- }
- // NOTE: No inferrence in fn types? Not sure, lazy way is to allow it.
- this->apply_equality(sp, *l_e.m_rettype, cb_left, *r_e.m_rettype, cb_right, nullptr);
- for(unsigned int i = 0; i < l_e.m_arg_types.size(); i ++ ) {
- this->apply_equality(sp, l_e.m_arg_types[i], cb_left, r_e.m_arg_types[i], cb_right, nullptr);
- }
- ),
- (Closure,
- TODO(sp, "apply_equality - Closure");
- )
- )
- }
- }
- }
-
- bool check_trait_bound(const Span& sp, const ::HIR::TypeRef& type, const ::HIR::GenericPath& trait, ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> placeholder) const
- {
- if( this->find_trait_impls_bound(sp, trait.m_path, placeholder(type), [&](const auto& args){
- DEBUG("TODO: Check args for " << trait.m_path << args << " against " << trait);
- return true;
- })
- )
- {
- // Satisfied by generic
- return true;
- }
- else if( this->m_crate.find_trait_impls(trait.m_path, type, placeholder, [&](const auto& impl) {
- DEBUG("- Bound " << type << " : " << trait << " satisfied by impl" << impl.m_params.fmt_args());
- // TODO: Recursively check
- return true;
- })
- )
- {
- // Match!
- return true;
- }
- else {
- DEBUG("- Bound " << type << " ("<<placeholder(type)<<") : " << trait << " failed");
- return false;
- }
- }
-
- ///
- ///
- ///
- ::HIR::TypeRef expand_associated_types(const Span& sp, ::HIR::TypeRef input) const
- {
- TRACE_FUNCTION_F(input);
- TU_MATCH(::HIR::TypeRef::Data, (input.m_data), (e),
- (Infer,
- auto& ty = this->get_type(input);
- return ty.clone();
- ),
- (Diverge,
- ),
- (Primitive,
- ),
- (Path,
- // - Only try resolving if the binding isn't known
- if( !e.binding.is_Unbound() )
- return input;
-
- TU_MATCH(::HIR::Path::Data, (e.path.m_data), (e2),
- (Generic,
- for(auto& arg : e2.m_params.m_types)
- arg = expand_associated_types(sp, mv$(arg));
- ),
- (UfcsInherent,
- TODO(sp, "Path - UfcsInherent - " << e.path);
- ),
- (UfcsKnown,
- DEBUG("Locating associated type for " << e.path);
- // TODO: Use the marker `e.binding` to tell if it's worth trying
-
- *e2.type = expand_associated_types(sp, mv$(*e2.type));
-
- // Search for a matching trait impl
- const ::HIR::TraitImpl* impl_ptr = nullptr;
- ::std::vector< const ::HIR::TypeRef*> impl_args;
-
- auto cb_get_infer = [&](const auto& ty)->const auto& {
- if( ty.m_data.is_Infer() )
- return this->get_type(ty);
- else
- return ty;
- };
-
- // 1. Bounds
- bool rv;
- rv = this->iterate_bounds([&](const auto& b) {
- TU_IFLET(::HIR::GenericBound, b, TypeEquality, be,
- DEBUG("Equality - " << be.type << " = " << be.other_type);
- if( input == be.type ) {
- input = be.other_type.clone();
- return true;
- }
- )
- return false;
- });
- if( rv ) {
- return input;
- }
-
- // Use bounds on other associated types too (if `e2.type` was resolved to a fixed associated type)
- TU_IFLET(::HIR::TypeRef::Data, e2.type->m_data, Path, e3,
- TU_IFLET(::HIR::Path::Data, e3.path.m_data, UfcsKnown, pe,
- // TODO: Search for equality bounds on this associated type (e3) that match the entire type (e2)
- // - Does simplification of complex associated types
- const auto& trait_ptr = this->m_crate.get_trait_by_path(sp, pe.trait.m_path);
- const auto& assoc_ty = trait_ptr.m_types.at(pe.item);
- DEBUG("TODO: Search bounds " << assoc_ty.m_params.fmt_bounds());
- // Resolve where Self=e2.type, for the associated type check.
- auto cb_placeholders_type = [&](const auto& ty)->const auto&{
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
- if( e.binding == 0xFFFF )
- return *e2.type;
- else
- TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name);
- )
- else {
- return ty;
- }
- };
- // Resolve where Self=pe.type (i.e. for the trait this inner UFCS is on)
- auto cb_placeholders_trait = [&](const auto& ty)->const auto&{
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
- if( e.binding == 0xFFFF )
- return *pe.type;
- else {
- // TODO: Look in pe.trait.m_params
- TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name);
- }
- )
- else {
- return ty;
- }
- };
- for(const auto& bound : assoc_ty.m_params.m_bounds)
- {
- TU_IFLET(::HIR::GenericBound, bound, TypeEquality, be,
- // IF: bound's type matches the input, replace with bounded equality
- // `<Self::IntoIter as Iterator>::Item = Self::Item`
- if( be.type.compare_with_paceholders(sp, input, cb_placeholders_type ) ) {
- DEBUG("Match of " << be.type << " with " << input);
- DEBUG("- Replace `input` with " << be.other_type << ", Self=" << *pe.type);
- if( monomorphise_type_needed(be.other_type) ) {
- return monomorphise_type_with(sp, be.other_type, cb_placeholders_trait);
- }
- else {
- return be.other_type.clone();
- }
- }
- )
- }
- DEBUG("e2 = " << *e2.type << ", input = " << input);
- )
- )
-
- // 2. Crate-level impls
- rv = this->m_crate.find_trait_impls(e2.trait.m_path, *e2.type, cb_get_infer,
- [&](const auto& impl) {
- DEBUG("Found impl" << impl.m_params.fmt_args() << " " << e2.trait.m_path << impl.m_trait_args << " for " << impl.m_type);
- // - Populate the impl's type arguments
- impl_args.clear();
- impl_args.resize( impl.m_params.m_types.size() );
- // - Match with `Self`
- auto cb_res = [&](unsigned int slot, const ::HIR::TypeRef& ty) {
- DEBUG("- Set " << slot << " = " << ty);
- if( slot >= impl_args.size() ) {
- BUG(sp, "Impl parameter out of range - " << slot);
- }
- auto& slot_r = impl_args.at(slot);
- if( slot_r != nullptr ) {
- DEBUG("TODO: Match " << slot_r << " == " << ty << " when encountered twice");
- }
- else {
- slot_r = &ty;
- }
- };
- impl.m_type.match_generics(sp, *e2.type, cb_get_infer, cb_res);
- for( unsigned int i = 0; i < impl.m_trait_args.m_types.size(); i ++ )
- {
- impl.m_trait_args.m_types[i].match_generics(sp, e2.trait.m_params.m_types.at(i), cb_get_infer, cb_res);
- }
- auto expand_placeholder = [&](const auto& ty)->const auto& {
- if( ty.m_data.is_Infer() )
- return this->get_type(ty);
- else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
- if( e.binding == 0xFFFF ) {
- //TODO(sp, "Look up 'Self' in expand_associated_types::expand_placeholder (" << *e2.type << ")");
- return *e2.type;
- }
- else {
- assert(e.binding < impl_args.size());
- assert( impl_args[e.binding] );
- return *impl_args[e.binding];
- }
- )
- else
- return ty;
- };
- for( const auto& bound : impl.m_params.m_bounds )
- {
- TU_MATCH_DEF(::HIR::GenericBound, (bound), (be),
- (
- ),
- (TraitBound,
- if( !this->check_trait_bound(sp, be.type, be.trait.m_path, expand_placeholder) )
- {
- return false;
- }
- )
- )
- }
- // TODO: Bounds check? (here or elsewhere?)
- // - Need to check bounds before picking this impl, because the bound could be preventing false matches
- if( impl.m_trait_args.m_types.size() > 0 )
- {
- TODO(sp, "Check trait type parameters in expand_associated_types");
- }
- impl_ptr = &impl;
- return true;
- });
- if( rv )
- {
- // An impl was found:
- assert(impl_ptr);
-
- // - Monomorphise the output type
- auto new_type = monomorphise_type_with(sp, impl_ptr->m_types.at( e2.item ), [&](const auto& ty)->const auto& {
- const auto& ge = ty.m_data.as_Generic();
- assert(ge.binding < impl_args.size());
- return *impl_args[ge.binding];
- });
- DEBUG("Converted UfcsKnown - " << e.path << " = " << new_type << " using " << e2.item << " = " << impl_ptr->m_types.at( e2.item ));
- return new_type;
- }
-
- // TODO: If there are no ivars in this path, set its binding to Opaque
-
- DEBUG("Couldn't resolve associated type for " << input);
- ),
- (UfcsUnknown,
- BUG(sp, "Encountered UfcsUnknown");
- )
- )
- ),
- (Generic,
- ),
- (TraitObject,
- // Recurse?
- ),
- (Array,
- *e.inner = expand_associated_types(sp, mv$(*e.inner));
- ),
- (Slice,
- *e.inner = expand_associated_types(sp, mv$(*e.inner));
- ),
- (Tuple,
- for(auto& sub : e) {
- sub = expand_associated_types(sp, mv$(sub));
- }
- ),
- (Borrow,
- *e.inner = expand_associated_types(sp, mv$(*e.inner));
- ),
- (Pointer,
- *e.inner = expand_associated_types(sp, mv$(*e.inner));
- ),
- (Function,
- // Recurse?
- ),
- (Closure,
- // Recurse?
- )
- )
- return input;
- }
-
- bool iterate_bounds( ::std::function<bool(const ::HIR::GenericBound&)> cb) const
- {
- const ::HIR::GenericParams* v[2] = { m_item_params, m_impl_params };
- for(auto p : v)
+ break;
+ case ::HIR::InferClass::Integer:
+ switch(ct)
{
- if( !p ) continue ;
- for(const auto& b : p->m_bounds)
- if(cb(b)) return true;
- }
- return false;
- }
-
- /// Searches for a trait impl that matches the provided trait name and type
- bool find_trait_impls(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback)
- {
- Span sp = Span();
- TRACE_FUNCTION_F("trait = " << trait << ", type = " << type);
- // 1. Search generic params
- if( find_trait_impls_bound(sp, trait, type, callback) )
- return true;
- // 2. Search crate-level impls
- return find_trait_impls_crate(trait, type, callback);
- }
- bool find_named_trait_in_trait(const Span& sp, const ::HIR::SimplePath& des, const ::HIR::Trait& trait_ptr, const ::HIR::PathParams& pp, ::std::function<bool(const ::HIR::PathParams&)> callback) const
- {
- assert( pp.m_types.size() == trait_ptr.m_params.m_types.size() );
- for( const auto& pt : trait_ptr.m_parent_traits )
- {
- auto pt_pp = monomorphise_path_params_with(Span(), pt.m_params.clone(), [&](const auto& gt)->const auto& {
- const auto& ge = gt.m_data.as_Generic();
- if( ge.binding == 0xFFFF ) {
- return gt;
- }
- else {
- if( ge.binding >= pp.m_types.size() )
- BUG(sp, "find_named_trait_in_trait - Generic #" << ge.binding << " " << ge.name << " out of range");
- return pp.m_types[ge.binding];
- }
- }, false);
-
- if( pt.m_path == des ) {
- //TODO(Span(), "Fix arguments for a parent trait and call callback - " << pt << " with paramset " << trait_ptr.m_params.fmt_args() << " = " << pt_pp);
- callback( pt_pp );
- return true;
- }
- }
- return false;
- }
- bool find_trait_impls_bound(const Span& sp, const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const
- {
- return this->iterate_bounds([&](const auto& b) {
- TU_IFLET(::HIR::GenericBound, b, TraitBound, e,
- if( e.type != type )
- return false;
- if( e.trait.m_path.m_path == trait ) {
- if( callback(e.trait.m_path.m_params) ) {
- return true;
- }
- }
- if( this->find_named_trait_in_trait(sp, trait, *e.trait.m_trait_ptr, e.trait.m_path.m_params, callback) ) {
- return true;
- }
- )
- return false;
- });
- }
- bool find_trait_impls_crate(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const
- {
- return this->m_crate.find_trait_impls(trait, type, [&](const auto& ty)->const auto&{
- if( ty.m_data.is_Infer() )
- return this->get_type(ty);
- else
- return ty;
- },
- [&](const auto& impl) {
- DEBUG("[find_trait_impls_crate] Found impl" << impl.m_params.fmt_args() << " " << trait << impl.m_trait_args << " for " << impl.m_type);
- return callback(impl.m_trait_args);
- }
- );
- }
-
- bool trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::GenericPath& out_path) const
- {
- auto it = trait_ptr.m_values.find(name);
- if( it != trait_ptr.m_values.end() ) {
- if( it->second.is_Function() ) {
- out_path = trait_path.clone();
- return true;
- }
- }
-
- // TODO: Prevent infinite recursion
- for(const auto& st : trait_ptr.m_parent_traits)
- {
- auto& st_ptr = this->m_crate.get_trait_by_path(sp, st.m_path);
- if( trait_contains_method(sp, st, st_ptr, name, out_path) ) {
- out_path.m_params = monomorphise_path_params_with(sp, mv$(out_path.m_params), [&](const auto& gt)->const auto& {
- const auto& ge = gt.m_data.as_Generic();
- assert(ge.binding < 256);
- assert(ge.binding < trait_path.m_params.m_types.size());
- return trait_path.m_params.m_types[ge.binding];
- }, false);
- return true;
- }
- }
- return false;
- }
-
- /// Locate the named method by applying auto-dereferencing.
- /// \return Number of times deref was applied (or ~0 if _ was hit)
- unsigned int autoderef_find_method(const Span& sp, const ::HIR::TypeRef& top_ty, const ::std::string& method_name, /* Out -> */::HIR::Path& fcn_path) const
- {
- unsigned int deref_count = 0;
- const auto* current_ty = &top_ty;
- do {
- const auto& ty = this->get_type(*current_ty);
- if( ty.m_data.is_Infer() ) {
- return ~0u;
- }
-
- // 1. Search generic bounds for a match
- const ::HIR::GenericParams* v[2] = { m_item_params, m_impl_params };
- for(auto p : v)
- {
- if( !p ) continue ;
- for(const auto& b : p->m_bounds)
- {
- TU_IFLET(::HIR::GenericBound, b, TraitBound, e,
- DEBUG("Bound " << e.type << " : " << e.trait.m_path);
- // TODO: Match using _ replacement
- if( e.type != ty )
- continue ;
-
- // - Bound's type matches, check if the bounded trait has the method we're searching for
- // > TODO: Search supertraits too
- DEBUG("- Matches " << ty);
- ::HIR::GenericPath final_trait_path;
- assert(e.trait.m_trait_ptr);
- if( !this->trait_contains_method(sp, e.trait.m_path, *e.trait.m_trait_ptr, method_name, final_trait_path) )
- continue ;
- DEBUG("- Found trait " << final_trait_path);
-
- // Found the method, return the UFCS path for it
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return deref_count;
- )
- }
- }
-
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
- // No match, keep trying.
- )
- else if( ty.m_data.is_Path() && ty.m_data.as_Path().path.m_data.is_UfcsKnown() )
- {
- const auto& e = ty.m_data.as_Path().path.m_data.as_UfcsKnown();
- // UFCS known - Assuming that it's reached the maximum resolvable level (i.e. a type within is generic), search for trait bounds on the type
- const auto& trait = this->m_crate.get_trait_by_path(sp, e.trait.m_path);
- const auto& assoc_ty = trait.m_types.at( e.item );
- // NOTE: The bounds here have 'Self' = the type
- for(const auto& bound : assoc_ty.m_params.m_bounds )
- {
- TU_IFLET(::HIR::GenericBound, bound, TraitBound, be,
- assert(be.trait.m_trait_ptr);
- ::HIR::GenericPath final_trait_path;
- if( !this->trait_contains_method(sp, be.trait.m_path, *be.trait.m_trait_ptr, method_name, final_trait_path) )
- continue ;
- DEBUG("- Found trait " << final_trait_path);
-
- // Found the method, return the UFCS path for it
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- mv$(final_trait_path),
- method_name,
- {}
- }) );
- return deref_count;
- )
- }
- }
- else {
- // 2. Search for inherent methods
- for(const auto& impl : m_crate.m_type_impls)
- {
- if( impl.matches_type(ty) ) {
- DEBUG("Mactching impl " << impl.m_type);
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsInherent({
- box$(ty.clone()),
- method_name,
- {}
- }) );
- return deref_count;
- }
- }
- // 3. Search for trait methods (using currently in-scope traits)
- for(const auto& trait_ref : ::reverse(m_traits))
- {
- // TODO: Search supertraits too
- auto it = trait_ref.second->m_values.find(method_name);
- if( it == trait_ref.second->m_values.end() )
- continue ;
- if( !it->second.is_Function() )
- continue ;
- DEBUG("Search for impl of " << *trait_ref.first);
- if( find_trait_impls_crate(*trait_ref.first, ty, [](const auto&) { return true; }) ) {
- DEBUG("Found trait impl " << *trait_ref.first << " for " << ty);
- fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
- box$( ty.clone() ),
- trait_ref.first->clone(),
- method_name,
- {}
- }) );
- return deref_count;
- }
- }
- }
-
- // 3. Dereference and try again
- deref_count += 1;
- TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Borrow, e,
- current_ty = &*e.inner;
- )
- else {
- // TODO: Search for a Deref impl
- current_ty = nullptr;
- }
- } while( current_ty );
- // Dereference failed! This is a hard error (hitting _ is checked above and returns ~0)
- this->dump();
- TODO(sp, "Error when no method could be found, but type is known - (: " << top_ty << ")." << method_name);
- }
-
- public:
- ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> callback_resolve_infer() {
- return [&](const auto& ty)->const auto& {
- if( ty.m_data.is_Infer() )
- return this->get_type(ty);
- else
- return ty;
- };
- }
-
- unsigned int new_ivar()
- {
- m_ivars.push_back( IVar() );
- m_ivars.back().type->m_data.as_Infer().index = m_ivars.size() - 1;
- return m_ivars.size() - 1;
- }
- ::HIR::TypeRef new_ivar_tr() {
- ::HIR::TypeRef rv;
- rv.m_data.as_Infer().index = this->new_ivar();
- return rv;
- }
-
- ::HIR::TypeRef& get_type(::HIR::TypeRef& type)
- {
- TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, e,
- assert(e.index != ~0u);
- return *get_pointed_ivar(e.index).type;
- )
- else {
- return type;
- }
- }
- const ::HIR::TypeRef& get_type(const ::HIR::TypeRef& type) const
- {
- TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, e,
- assert(e.index != ~0u);
- return *get_pointed_ivar(e.index).type;
- )
- else {
- return type;
- }
- }
-
- void check_type_class_primitive(const Span& sp, const ::HIR::TypeRef& type, ::HIR::InferClass ic, ::HIR::CoreType ct)
- {
- switch(ic)
- {
- case ::HIR::InferClass::None:
- break;
- case ::HIR::InferClass::Float:
- switch(ct)
- {
- case ::HIR::CoreType::F32:
- case ::HIR::CoreType::F64:
- break;
- default:
- ERROR(sp, E0000, "Type unificiation of integer literal with non-integer - " << type);
- }
- break;
- case ::HIR::InferClass::Integer:
- switch(ct)
- {
- case ::HIR::CoreType::I8: case ::HIR::CoreType::U8:
- case ::HIR::CoreType::I16: case ::HIR::CoreType::U16:
- case ::HIR::CoreType::I32: case ::HIR::CoreType::U32:
- case ::HIR::CoreType::I64: case ::HIR::CoreType::U64:
- case ::HIR::CoreType::Isize: case ::HIR::CoreType::Usize:
- break;
- default:
- ERROR(sp, E0000, "Type unificiation of integer literal with non-integer - " << type);
- }
+ case ::HIR::CoreType::I8: case ::HIR::CoreType::U8:
+ case ::HIR::CoreType::I16: case ::HIR::CoreType::U16:
+ case ::HIR::CoreType::I32: case ::HIR::CoreType::U32:
+ case ::HIR::CoreType::I64: case ::HIR::CoreType::U64:
+ case ::HIR::CoreType::Isize: case ::HIR::CoreType::Usize:
break;
+ default:
+ ERROR(sp, E0000, "Type unificiation of integer literal with non-integer - " << type);
}
+ break;
}
-
- void set_ivar_to(unsigned int slot, ::HIR::TypeRef type)
- {
- auto sp = Span();
- auto& root_ivar = this->get_pointed_ivar(slot);
- DEBUG("set_ivar_to(" << slot << " { " << *root_ivar.type << " }, " << type << ")");
-
- // If the left type was '_', alias the right to it
- TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, l_e,
- assert( l_e.index != slot );
- DEBUG("Set IVar " << slot << " = @" << l_e.index);
-
- if( l_e.ty_class != ::HIR::InferClass::None ) {
- TU_MATCH_DEF(::HIR::TypeRef::Data, (root_ivar.type->m_data), (e),
- (
- ERROR(sp, E0000, "Type unificiation of literal with invalid type - " << *root_ivar.type);
- ),
- (Primitive,
- check_type_class_primitive(sp, type, l_e.ty_class, e);
- ),
- (Infer,
- // TODO: Check for right having a ty_class
- if( e.ty_class != ::HIR::InferClass::None && e.ty_class != l_e.ty_class ) {
- ERROR(sp, E0000, "Unifying types with mismatching literal classes");
- }
- )
- )
- }
-
- root_ivar.alias = l_e.index;
- root_ivar.type.reset();
- )
- else {
- // Otherwise, store left in right's slot
- DEBUG("Set IVar " << slot << " = " << type);
- root_ivar.type = box$( mv$(type) );
- }
-
- this->mark_change();
- }
-
- void ivar_unify(unsigned int left_slot, unsigned int right_slot)
- {
- auto sp = Span();
- if( left_slot != right_slot )
- {
- auto& left_ivar = this->get_pointed_ivar(left_slot);
-
- // TODO: Assert that setting this won't cause a loop.
- auto& root_ivar = this->get_pointed_ivar(right_slot);
-
- TU_IFLET(::HIR::TypeRef::Data, root_ivar.type->m_data, Infer, re,
- if(re.ty_class != ::HIR::InferClass::None) {
- TU_MATCH_DEF(::HIR::TypeRef::Data, (left_ivar.type->m_data), (le),
- (
- ERROR(sp, E0000, "Type unificiation of literal with invalid type - " << *left_ivar.type);
- ),
- (Infer,
- if( le.ty_class != ::HIR::InferClass::None && le.ty_class != re.ty_class )
- {
- ERROR(sp, E0000, "Unifying types with mismatching literal classes");
- }
- le.ty_class = re.ty_class;
- ),
- (Primitive,
- check_type_class_primitive(sp, *left_ivar.type, re.ty_class, le);
- )
- )
- }
- )
- else {
- BUG(sp, "Unifying over a concrete type - " << *root_ivar.type);
- }
-
- root_ivar.alias = left_slot;
- root_ivar.type.reset();
-
- this->mark_change();
- }
- }
-
- private:
- IVar& get_pointed_ivar(unsigned int slot) const
- {
- auto index = slot;
- unsigned int count = 0;
- assert(index < m_ivars.size());
- while( m_ivars.at(index).is_alias() ) {
- index = m_ivars.at(index).alias;
-
- if( count >= m_ivars.size() ) {
- this->dump();
- BUG(Span(), "Loop detected in ivar list when starting at " << slot << ", current is " << index);
- }
- count ++;
- }
- return const_cast<IVar&>(m_ivars.at(index));
- }
- };
+ }
// Enumerate inferrence variables (most of them) in the expression tree
//
@@ -3141,7 +1542,7 @@ namespace {
};
};
-void Typecheck_Code(TypecheckContext context, const ::HIR::TypeRef& result_type, ::HIR::ExprPtr& expr)
+void Typecheck_Code(typeck::TypecheckContext context, const ::HIR::TypeRef& result_type, ::HIR::ExprPtr& expr)
{
TRACE_FUNCTION;
@@ -3152,7 +1553,7 @@ void Typecheck_Code(TypecheckContext context, const ::HIR::TypeRef& result_type,
// 1. Enumerate inferrence variables and assign indexes to them
{
- ExprVisitor_Enum visitor { context, result_type };
+ typeck::ExprVisitor_Enum visitor { context, result_type };
visitor.visit_node_ptr(root_ptr);
}
// - Apply equality between the node result and the expected type
@@ -3162,7 +1563,7 @@ void Typecheck_Code(TypecheckContext context, const ::HIR::TypeRef& result_type,
context.dump();
// 2. Iterate through nodes applying rules until nothing changes
{
- ExprVisitor_Run visitor { context };
+ typeck::ExprVisitor_Run visitor { context };
unsigned int count = 0;
do {
count += 1;
@@ -3177,7 +1578,7 @@ void Typecheck_Code(TypecheckContext context, const ::HIR::TypeRef& result_type,
context.dump();
{
DEBUG("==== VALIDATE ====");
- ExprVisitor_Apply visitor { context };
+ typeck::ExprVisitor_Apply visitor { context };
expr->visit( visitor );
}
}
@@ -3185,6 +1586,8 @@ void Typecheck_Code(TypecheckContext context, const ::HIR::TypeRef& result_type,
namespace {
+ using typeck::TypecheckContext;
+
class OuterVisitor:
public ::HIR::Visitor
{
diff --git a/src/hir_typeck/expr.hpp b/src/hir_typeck/expr.hpp
new file mode 100644
index 00000000..d1a8ed90
--- /dev/null
+++ b/src/hir_typeck/expr.hpp
@@ -0,0 +1,187 @@
+/*
+ */
+#pragma once
+#include <hir/expr.hpp>
+#include <hir/hir.hpp>
+#include <hir/visitor.hpp>
+
+namespace typeck {
+
+typedef ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> t_cb_generic;
+
+extern bool monomorphise_type_needed(const ::HIR::TypeRef& tpl);
+extern bool monomorphise_pathparams_needed(const ::HIR::PathParams& tpl);
+extern bool monomorphise_path_needed(const ::HIR::Path& tpl);
+
+extern ::HIR::PathParams monomorphise_path_params_with(const Span& sp, const ::HIR::PathParams& tpl, t_cb_generic callback, bool allow_infer);
+extern ::HIR::TypeRef monomorphise_type_with(const Span& sp, const ::HIR::TypeRef& tpl, t_cb_generic callback, bool allow_infer=true);
+extern ::HIR::TypeRef monomorphise_type(const Span& sp, const ::HIR::GenericParams& params_def, const ::HIR::PathParams& params, const ::HIR::TypeRef& tpl);
+
+extern void check_type_class_primitive(const Span& sp, const ::HIR::TypeRef& type, ::HIR::InferClass ic, ::HIR::CoreType ct);
+
+class TypecheckContext
+{
+ struct IVar
+ {
+ unsigned int alias; // If not ~0, this points to another ivar
+ ::std::unique_ptr< ::HIR::TypeRef> type; // Type (only nullptr if alias!=0)
+
+ IVar():
+ alias(~0u),
+ type(new ::HIR::TypeRef())
+ {}
+ bool is_alias() const { return alias != ~0u; }
+ };
+ struct Variable
+ {
+ ::std::string name;
+ ::HIR::TypeRef type;
+
+ Variable()
+ {}
+ Variable(const ::std::string& name, ::HIR::TypeRef type):
+ name( name ),
+ type( mv$(type) )
+ {}
+ Variable(Variable&&) = default;
+
+ Variable& operator=(Variable&&) = default;
+ };
+public:
+ const ::HIR::Crate& m_crate;
+ ::std::vector< ::std::pair< const ::HIR::SimplePath*, const ::HIR::Trait* > > m_traits;
+private:
+ ::std::vector< Variable> m_locals;
+ ::std::vector< IVar> m_ivars;
+ bool m_has_changed;
+
+ const ::HIR::GenericParams* m_impl_params;
+ const ::HIR::GenericParams* m_item_params;
+
+public:
+ TypecheckContext(const ::HIR::Crate& crate, const ::HIR::GenericParams* impl_params, const ::HIR::GenericParams* item_params):
+ m_crate(crate),
+ m_has_changed(false),
+ m_impl_params( impl_params ),
+ m_item_params( item_params )
+ {
+ }
+
+ void dump() const;
+
+ bool take_changed() {
+ bool rv = m_has_changed;
+ m_has_changed = false;
+ return rv;
+ }
+ void mark_change() {
+ DEBUG("- CHANGE");
+ m_has_changed = true;
+ }
+
+ /// Adds a local variable binding (type is mutable so it can be inferred if required)
+ void add_local(unsigned int index, const ::std::string& name, ::HIR::TypeRef type);
+
+ /// Get the type associated with a variable
+ const ::HIR::TypeRef& get_var_type(const Span& sp, unsigned int index);
+
+ /// Add (and bind) all '_' types in `type`
+ void add_ivars(::HIR::TypeRef& type);
+ void add_ivars_params(::HIR::PathParams& params);
+
+ void add_pattern_binding(const ::HIR::PatternBinding& pb, ::HIR::TypeRef type);
+
+ void add_binding(const Span& sp, ::HIR::Pattern& pat, ::HIR::TypeRef& type);
+
+ /// Run inferrence using a pattern
+ void apply_pattern(const ::HIR::Pattern& pat, ::HIR::TypeRef& type);
+
+ /// (wrapper)
+ void apply_equality(const Span& sp, const ::HIR::TypeRef& left, const ::HIR::TypeRef& right, ::HIR::ExprNodeP* node_ptr_ptr = nullptr) {
+ apply_equality(sp, left, [](const auto& x)->const auto&{return x;}, right, [](const auto& x)->const auto&{return x;}, node_ptr_ptr);
+ }
+
+ const ::HIR::TypeRef& expand_associated_types_to(const Span& sp, const ::HIR::TypeRef& t, ::HIR::TypeRef& tmp_t) const;
+ /// Equates the two types, checking that they are equal (and binding ivars)
+ /// \note !! The ordering DOES matter, as the righthand side will get unsizing/deref coercions applied if possible (using node_ptr_ptr)
+ /// \param sp Span for reporting errors
+ /// \param left Lefthand type (destination for coercions)
+ /// \param right Righthand type (source for coercions)
+ /// \param node_ptr Pointer to ExprNodeP, updated with new nodes for coercions
+ void apply_equality(const Span& sp, const ::HIR::TypeRef& left, t_cb_generic cb_left, const ::HIR::TypeRef& right, t_cb_generic cb_right, ::HIR::ExprNodeP* node_ptr_ptr);
+
+ bool check_trait_bound(const Span& sp, const ::HIR::TypeRef& type, const ::HIR::GenericPath& trait, ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> placeholder) const;
+
+ /// Expand any located associated types in the input, operating in-place and returning the result
+ ::HIR::TypeRef expand_associated_types(const Span& sp, ::HIR::TypeRef input) const;
+
+ /// Iterate over in-scope bounds (function then top)
+ bool iterate_bounds( ::std::function<bool(const ::HIR::GenericBound&)> cb) const;
+
+ /// Searches for a trait impl that matches the provided trait name and type
+ bool find_trait_impls(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback);
+ /// Locate a named trait in the provied trait (either itself or as a parent trait)
+ bool find_named_trait_in_trait(const Span& sp, const ::HIR::SimplePath& des, const ::HIR::Trait& trait_ptr, const ::HIR::PathParams& pp, ::std::function<bool(const ::HIR::PathParams&)> callback) const;
+ /// Search for a trait implementation in current bounds
+ bool find_trait_impls_bound(const Span& sp, const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const;
+ /// Search for a trait implementation in the crate
+ bool find_trait_impls_crate(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const;
+
+ /// Locates a named method in a trait, and returns the path of the trait that contains it (with fixed parameters)
+ bool trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::GenericPath& out_path) const;
+
+ /// Locate the named method by applying auto-dereferencing.
+ /// \return Number of times deref was applied (or ~0 if _ was hit)
+ unsigned int autoderef_find_method(const Span& sp, const ::HIR::TypeRef& top_ty, const ::std::string& method_name, /* Out -> */::HIR::Path& fcn_path) const;
+
+public:
+ ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> callback_resolve_infer() {
+ return [&](const auto& ty)->const auto& {
+ if( ty.m_data.is_Infer() )
+ return this->get_type(ty);
+ else
+ return ty;
+ };
+ }
+
+ unsigned int new_ivar()
+ {
+ m_ivars.push_back( IVar() );
+ m_ivars.back().type->m_data.as_Infer().index = m_ivars.size() - 1;
+ return m_ivars.size() - 1;
+ }
+ ::HIR::TypeRef new_ivar_tr() {
+ ::HIR::TypeRef rv;
+ rv.m_data.as_Infer().index = this->new_ivar();
+ return rv;
+ }
+
+ ::HIR::TypeRef& get_type(::HIR::TypeRef& type)
+ {
+ TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, e,
+ assert(e.index != ~0u);
+ return *get_pointed_ivar(e.index).type;
+ )
+ else {
+ return type;
+ }
+ }
+ const ::HIR::TypeRef& get_type(const ::HIR::TypeRef& type) const
+ {
+ TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, e,
+ assert(e.index != ~0u);
+ return *get_pointed_ivar(e.index).type;
+ )
+ else {
+ return type;
+ }
+ }
+
+private:
+ void set_ivar_to(unsigned int slot, ::HIR::TypeRef type);
+ void ivar_unify(unsigned int left_slot, unsigned int right_slot);
+ IVar& get_pointed_ivar(unsigned int slot) const;
+};
+
+} // namespace typeck
+
diff --git a/src/hir_typeck/expr_context.cpp b/src/hir_typeck/expr_context.cpp
new file mode 100644
index 00000000..29bb465f
--- /dev/null
+++ b/src/hir_typeck/expr_context.cpp
@@ -0,0 +1,1522 @@
+/*
+ */
+#include "expr.hpp"
+#include <hir/hir.hpp>
+#include <algorithm> // std::find_if
+
+void typeck::TypecheckContext::dump() const
+{
+ DEBUG("TypecheckContext - " << m_ivars.size() << " ivars, " << m_locals.size() << " locals");
+ unsigned int i = 0;
+ for(const auto& v : m_ivars) {
+ if(v.is_alias()) {
+ DEBUG("#" << i << " = " << v.alias);
+ }
+ else {
+ DEBUG("#" << i << " = " << *v.type);
+ }
+ i ++ ;
+ }
+ i = 0;
+ for(const auto& v : m_locals) {
+ DEBUG("VAR " << i << " '"<<v.name<<"' = " << v.type);
+ i ++;
+ }
+}
+
+void typeck::TypecheckContext::add_local(unsigned int index, const ::std::string& name, ::HIR::TypeRef type)
+{
+ if( m_locals.size() <= index )
+ m_locals.resize(index+1);
+ m_locals[index] = Variable(name, mv$(type));
+}
+
+const ::HIR::TypeRef& typeck::TypecheckContext::get_var_type(const Span& sp, unsigned int index)
+{
+ if( index >= m_locals.size() ) {
+ this->dump();
+ BUG(sp, "Local index out of range " << index << " >= " << m_locals.size());
+ }
+ return m_locals.at(index).type;
+}
+
+///
+/// Add inferrence variables to the provided type (if they're not already set)
+///
+void typeck::TypecheckContext::add_ivars(::HIR::TypeRef& type)
+{
+ TU_MATCH(::HIR::TypeRef::Data, (type.m_data), (e),
+ (Infer,
+ if( e.index == ~0u ) {
+ e.index = this->new_ivar();
+ this->m_ivars[e.index].type->m_data.as_Infer().ty_class = e.ty_class;
+ }
+ ),
+ (Diverge,
+ ),
+ (Primitive,
+ ),
+ (Path,
+ // Iterate all arguments
+ TU_MATCH(::HIR::Path::Data, (e.path.m_data), (e2),
+ (Generic,
+ this->add_ivars_params(e2.m_params);
+ ),
+ (UfcsKnown,
+ this->add_ivars(*e2.type);
+ this->add_ivars_params(e2.trait.m_params);
+ this->add_ivars_params(e2.params);
+ ),
+ (UfcsUnknown,
+ this->add_ivars(*e2.type);
+ this->add_ivars_params(e2.params);
+ ),
+ (UfcsInherent,
+ this->add_ivars(*e2.type);
+ this->add_ivars_params(e2.params);
+ )
+ )
+ ),
+ (Generic,
+ ),
+ (TraitObject,
+ // Iterate all paths
+ ),
+ (Array,
+ add_ivars(*e.inner);
+ ),
+ (Slice,
+ add_ivars(*e.inner);
+ ),
+ (Tuple,
+ for(auto& ty : e)
+ add_ivars(ty);
+ ),
+ (Borrow,
+ add_ivars(*e.inner);
+ ),
+ (Pointer,
+ add_ivars(*e.inner);
+ ),
+ (Function,
+ // No ivars allowed
+ // TODO: Check?
+ ),
+ (Closure,
+ // Shouldn't be possible
+ )
+ )
+}
+void typeck::TypecheckContext::add_ivars_params(::HIR::PathParams& params)
+{
+ for(auto& arg : params.m_types)
+ add_ivars(arg);
+}
+
+void typeck::TypecheckContext::add_pattern_binding(const ::HIR::PatternBinding& pb, ::HIR::TypeRef type)
+{
+ assert( pb.is_valid() );
+ switch( pb.m_type )
+ {
+ case ::HIR::PatternBinding::Type::Move:
+ this->add_local( pb.m_slot, pb.m_name, mv$(type) );
+ break;
+ case ::HIR::PatternBinding::Type::Ref:
+ this->add_local( pb.m_slot, pb.m_name, ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, mv$(type)) );
+ break;
+ case ::HIR::PatternBinding::Type::MutRef:
+ this->add_local( pb.m_slot, pb.m_name, ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Unique, mv$(type)) );
+ break;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+void typeck::TypecheckContext::add_binding(const Span& sp, ::HIR::Pattern& pat, ::HIR::TypeRef& type)
+{
+ TRACE_FUNCTION_F("pat = " << pat << ", type = " << type);
+
+ if( pat.m_binding.is_valid() ) {
+ this->add_pattern_binding(pat.m_binding, type.clone());
+ // TODO: Can there be bindings within a bound pattern?
+ //return ;
+ }
+
+ //
+ TU_MATCH(::HIR::Pattern::Data, (pat.m_data), (e),
+ (Any,
+ // Just leave it, the pattern says nothing
+ ),
+ (Value,
+ //TODO(sp, "Value pattern");
+ ),
+ (Range,
+ //TODO(sp, "Range pattern");
+ ),
+ (Box,
+ TODO(sp, "Box pattern");
+ ),
+ (Ref,
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Borrow({ e.type, box$(this->new_ivar_tr()) });
+ }
+ // Type must be a &-ptr
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Borrow,
+ if( te.type != e.type ) {
+ // TODO: Type mismatch
+ }
+ this->add_binding(sp, *e.sub, *te.inner );
+ )
+ )
+ ),
+ (Tuple,
+ if( type.m_data.is_Infer() ) {
+ ::std::vector< ::HIR::TypeRef> sub_types;
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ sub_types.push_back( this->new_ivar_tr() );
+ type.m_data = ::HIR::TypeRef::Data::make_Tuple( mv$(sub_types) );
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Tuple,
+ if( te.size() != e.sub_patterns.size() ) {
+ // TODO: Type mismatch
+ }
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ this->add_binding(sp, e.sub_patterns[i], te[i] );
+ )
+ )
+ ),
+ (Slice,
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Slice( {box$(this->new_ivar_tr())} );
+ this->mark_change();
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw""; ),
+ (Slice,
+ for(auto& sub : e.sub_patterns)
+ this->add_binding(sp, sub, *te.inner );
+ )
+ )
+ ),
+ (SplitSlice,
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Slice( {box$(this->new_ivar_tr())} );
+ this->mark_change();
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Slice,
+ for(auto& sub : e.leading)
+ this->add_binding( sp, sub, *te.inner );
+ for(auto& sub : e.trailing)
+ this->add_binding( sp, sub, *te.inner );
+ if( e.extra_bind.is_valid() ) {
+ this->add_local( e.extra_bind.m_slot, e.extra_bind.m_name, type.clone() );
+ }
+ )
+ )
+ ),
+
+ // - Enums/Structs
+ (StructTuple,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
+ }
+ assert(e.binding);
+ const auto& str = *e.binding;
+ // - assert check from earlier pass
+ assert( str.m_data.is_Tuple() );
+ const auto& sd = str.m_data.as_Tuple();
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
+ ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
+ }
+ // NOTE: Must be Generic for the above to have passed
+ auto& gp = te.path.m_data.as_Generic();
+
+ if( e.sub_patterns.size() != sd.size() ) {
+ ERROR(sp, E0000, "Tuple struct pattern with an incorrect number of fields");
+ }
+ for( unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ {
+ const auto& field_type = sd[i].ent;
+ if( monomorphise_type_needed(field_type) ) {
+ auto var_ty = monomorphise_type(sp, str.m_params, gp.m_params, field_type);
+ this->add_binding(sp, e.sub_patterns[i], var_ty);
+ }
+ else {
+ // SAFE: Can't have _ as monomorphise_type_needed checks for that
+ this->add_binding(sp, e.sub_patterns[i], const_cast< ::HIR::TypeRef&>(field_type));
+ }
+ }
+ )
+ )
+ ),
+ (StructTupleWildcard,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
+ }
+ assert(e.binding);
+ const auto& str = *e.binding;
+ // - assert check from earlier pass
+ assert( str.m_data.is_Tuple() );
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
+ ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
+ }
+ )
+ )
+ ),
+ (Struct,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
+ }
+ assert(e.binding);
+ const auto& str = *e.binding;
+ // - assert check from earlier pass
+ assert( str.m_data.is_Named() );
+ const auto& sd = str.m_data.as_Named();
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Struct() || te.binding.as_Struct() != &str ) {
+ ERROR(sp, E0000, "Type mismatch in struct pattern - " << type << " is not " << e.path);
+ }
+ // NOTE: Must be Generic for the above to have passed
+ auto& gp = te.path.m_data.as_Generic();
+ for( auto& field_pat : e.sub_patterns )
+ {
+ unsigned int f_idx = ::std::find_if( sd.begin(), sd.end(), [&](const auto& x){ return x.first == field_pat.first; } ) - sd.begin();
+ if( f_idx == sd.size() ) {
+ ERROR(sp, E0000, "Struct " << e.path << " doesn't have a field " << field_pat.first);
+ }
+ const ::HIR::TypeRef& field_type = sd[f_idx].second.ent;
+ if( monomorphise_type_needed(field_type) ) {
+ auto field_type_mono = monomorphise_type(sp, str.m_params, gp.m_params, field_type);
+ this->add_binding(sp, field_pat.second, field_type_mono);
+ }
+ else {
+ // SAFE: Can't have _ as monomorphise_type_needed checks for that
+ this->add_binding(sp, field_pat.second, const_cast< ::HIR::TypeRef&>(field_type));
+ }
+ }
+ )
+ )
+ ),
+ (EnumTuple,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ auto path = e.path.clone();
+ path.m_path.m_components.pop_back();
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
+ }
+ assert(e.binding_ptr);
+ const auto& enm = *e.binding_ptr;
+ const auto& var = enm.m_variants[e.binding_idx].second;
+ assert(var.is_Tuple());
+ const auto& tup_var = var.as_Tuple();
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
+ ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
+ }
+ // NOTE: Must be Generic for the above to have passed
+ auto& gp = te.path.m_data.as_Generic();
+ if( e.sub_patterns.size() != tup_var.size() ) {
+ ERROR(sp, E0000, "Enum pattern with an incorrect number of fields - " << e.path << " - expected " << tup_var.size() << ", got " << e.sub_patterns.size());
+ }
+ for( unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ {
+ if( monomorphise_type_needed(tup_var[i]) ) {
+ auto var_ty = monomorphise_type(sp, enm.m_params, gp.m_params, tup_var[i]);
+ this->add_binding(sp, e.sub_patterns[i], var_ty);
+ }
+ else {
+ // SAFE: Can't have a _ (monomorphise_type_needed checks for that)
+ this->add_binding(sp, e.sub_patterns[i], const_cast< ::HIR::TypeRef&>(tup_var[i]));
+ }
+ }
+ )
+ )
+ ),
+ (EnumTupleWildcard,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ auto path = e.path.clone();
+ path.m_path.m_components.pop_back();
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
+ }
+ assert(e.binding_ptr);
+ const auto& enm = *e.binding_ptr;
+ const auto& var = enm.m_variants[e.binding_idx].second;
+ assert(var.is_Tuple());
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
+ ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
+ }
+ )
+ )
+ ),
+ (EnumStruct,
+ this->add_ivars_params( e.path.m_params );
+ if( type.m_data.is_Infer() ) {
+ auto path = e.path.clone();
+ path.m_path.m_components.pop_back();
+ type.m_data = ::HIR::TypeRef::Data::make_Path( {mv$(path), ::HIR::TypeRef::TypePathBinding(e.binding_ptr)} );
+ }
+ assert(e.binding_ptr);
+ const auto& enm = *e.binding_ptr;
+ const auto& var = enm.m_variants[e.binding_idx].second;
+ assert(var.is_Struct());
+ const auto& tup_var = var.as_Struct();
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (type.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw ""; ),
+ (Path,
+ if( ! te.binding.is_Enum() || te.binding.as_Enum() != &enm ) {
+ ERROR(sp, E0000, "Type mismatch in enum pattern - " << type << " is not " << e.path);
+ }
+ // NOTE: Must be Generic for the above to have passed
+ auto& gp = te.path.m_data.as_Generic();
+
+ for( auto& field_pat : e.sub_patterns )
+ {
+ unsigned int f_idx = ::std::find_if( tup_var.begin(), tup_var.end(), [&](const auto& x){ return x.first == field_pat.first; } ) - tup_var.begin();
+ if( f_idx == tup_var.size() ) {
+ ERROR(sp, E0000, "Enum variant " << e.path << " doesn't have a field " << field_pat.first);
+ }
+ const ::HIR::TypeRef& field_type = tup_var[f_idx].second;
+ if( monomorphise_type_needed(field_type) ) {
+ auto field_type_mono = monomorphise_type(sp, enm.m_params, gp.m_params, field_type);
+ this->add_binding(sp, field_pat.second, field_type_mono);
+ }
+ else {
+ // SAFE: Can't have _ as monomorphise_type_needed checks for that
+ this->add_binding(sp, field_pat.second, const_cast< ::HIR::TypeRef&>(field_type));
+ }
+ }
+ )
+ )
+ )
+ )
+}
+
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+void typeck::TypecheckContext::apply_pattern(const ::HIR::Pattern& pat, ::HIR::TypeRef& type)
+{
+ static Span _sp;
+ const Span& sp = _sp;
+ // TODO: Should this do an equality on the binding?
+
+ auto& ty = this->get_type(type);
+
+ TU_MATCH(::HIR::Pattern::Data, (pat.m_data), (e),
+ (Any,
+ // Just leave it, the pattern says nothing about the type
+ ),
+ (Value,
+ TODO(sp, "Value pattern");
+ ),
+ (Range,
+ TODO(sp, "Range pattern");
+ ),
+ // - Pointer destructuring
+ (Box,
+ // Type must be box-able
+ TODO(sp, "Box patterns");
+ ),
+ (Ref,
+ if( ty.m_data.is_Infer() ) {
+ BUG(sp, "Infer type hit that should already have been fixed");
+ }
+ // Type must be a &-ptr
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Borrow,
+ if( te.type != e.type ) {
+ // TODO: Type mismatch
+ }
+ this->apply_pattern( *e.sub, *te.inner );
+ )
+ )
+ ),
+ (Tuple,
+ if( ty.m_data.is_Infer() ) {
+ BUG(sp, "Infer type hit that should already have been fixed");
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Tuple,
+ if( te.size() != e.sub_patterns.size() ) {
+ // TODO: Type mismatch
+ }
+ for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ )
+ this->apply_pattern( e.sub_patterns[i], te[i] );
+ )
+ )
+ ),
+ // --- Slices
+ (Slice,
+ if( ty.m_data.is_Infer() ) {
+ BUG(sp, "Infer type hit that should already have been fixed");
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Slice,
+ for(const auto& sp : e.sub_patterns )
+ this->apply_pattern( sp, *te.inner );
+ )
+ )
+ ),
+ (SplitSlice,
+ if( ty.m_data.is_Infer() ) {
+ BUG(sp, "Infer type hit that should already have been fixed");
+ }
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Slice,
+ for(const auto& sp : e.leading)
+ this->apply_pattern( sp, *te.inner );
+ for(const auto& sp : e.trailing)
+ this->apply_pattern( sp, *te.inner );
+ // TODO: extra_bind? (see comment at start of function)
+ )
+ )
+ ),
+
+ // - Enums/Structs
+ (StructTuple,
+ if( ty.m_data.is_Infer() ) {
+ ty.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
+ this->mark_change();
+ }
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Path,
+ // TODO: Does anything need to happen here? This can only introduce equalities?
+ )
+ )
+ ),
+ (StructTupleWildcard,
+ ),
+ (Struct,
+ if( ty.m_data.is_Infer() ) {
+ //TODO: Does this lead to issues with generic parameters?
+ ty.m_data = ::HIR::TypeRef::Data::make_Path( {e.path.clone(), ::HIR::TypeRef::TypePathBinding(e.binding)} );
+ this->mark_change();
+ }
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Path,
+ // TODO: Does anything need to happen here? This can only introduce equalities?
+ )
+ )
+ ),
+ (EnumTuple,
+ if( ty.m_data.is_Infer() ) {
+ TODO(sp, "EnumTuple - infer");
+ this->mark_change();
+ }
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Path,
+ )
+ )
+ ),
+ (EnumTupleWildcard,
+ ),
+ (EnumStruct,
+ if( ty.m_data.is_Infer() ) {
+ TODO(sp, "EnumStruct - infer");
+ this->mark_change();
+ }
+
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (ty.m_data), (te),
+ (
+ // TODO: Type mismatch
+ ),
+ (Infer, throw "";),
+ (Path,
+ )
+ )
+ )
+ )
+}
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+const ::HIR::TypeRef& typeck::TypecheckContext::expand_associated_types_to(const Span& sp, const ::HIR::TypeRef& t, ::HIR::TypeRef& tmp_t) const
+{
+ TU_IFLET(::HIR::TypeRef::Data, t.m_data, Path, e,
+ if( e.path.m_data.is_Generic() )
+ return t;
+ else {
+ tmp_t = this->expand_associated_types(sp, t.clone());
+ DEBUG("Expanded " << t << " into " << tmp_t);
+ return tmp_t;
+ }
+ )
+ else {
+ return t;
+ }
+}
+
+void typeck::TypecheckContext::apply_equality(const Span& sp, const ::HIR::TypeRef& left, t_cb_generic cb_left, const ::HIR::TypeRef& right, t_cb_generic cb_right, ::HIR::ExprNodeP* node_ptr_ptr)
+{
+ TRACE_FUNCTION_F(left << ", " << right);
+ assert( ! left.m_data.is_Infer() || left.m_data.as_Infer().index != ~0u );
+ assert( !right.m_data.is_Infer() || right.m_data.as_Infer().index != ~0u );
+ // - Convert left/right types into resolved versions (either root ivar, or generic replacement)
+ const auto& l_t1 = left.m_data.is_Generic() ? cb_left (left ) : this->get_type(left );
+ const auto& r_t1 = right.m_data.is_Generic() ? cb_right(right) : this->get_type(right);
+ if( l_t1 == r_t1 ) {
+ return ;
+ }
+
+ ::HIR::TypeRef left_tmp;
+ const auto& l_t = this->expand_associated_types_to(sp, l_t1, left_tmp);
+ ::HIR::TypeRef right_tmp;
+ const auto& r_t = this->expand_associated_types_to(sp, r_t1, right_tmp);
+
+ DEBUG("- l_t = " << l_t << ", r_t = " << r_t);
+ TU_IFLET(::HIR::TypeRef::Data, r_t.m_data, Infer, r_e,
+ TU_IFLET(::HIR::TypeRef::Data, l_t.m_data, Infer, l_e,
+ // If both are infer, unify the two ivars (alias right to point to left)
+ this->ivar_unify(l_e.index, r_e.index);
+ )
+ else {
+ // Righthand side is infer, alias it to the left
+ // TODO: that `true` should be `false` if the callback isn't unity (for bug checking)
+ this->set_ivar_to(r_e.index, monomorphise_type_with(sp, left, cb_left, true));
+ }
+ )
+ else {
+ TU_IFLET(::HIR::TypeRef::Data, l_t.m_data, Infer, l_e,
+ // Lefthand side is infer, alias it to the right
+ // TODO: that `true` should be `false` if the callback isn't unity (for bug checking)
+ this->set_ivar_to(l_e.index, monomorphise_type_with(sp, right, cb_right, true));
+ )
+ else {
+ // If generic replacement happened, clear the callback
+ if( left.m_data.is_Generic() ) {
+ cb_left = [](const auto& x)->const auto&{return x;};
+ }
+ if( right.m_data.is_Generic() ) {
+ cb_right = [](const auto& x)->const auto&{return x;};
+ }
+
+ // Neither are infer - both should be of the same form
+ // - If either side is `!`, return early (diverging type, matches anything)
+ if( l_t.m_data.is_Diverge() || r_t.m_data.is_Diverge() ) {
+ // TODO: Should diverge check be done elsewhere? what happens if a ! ends up in an ivar?
+ return ;
+ }
+
+ // Helper function for Path and TraitObject
+ auto equality_typeparams = [&](const ::HIR::PathParams& l, const ::HIR::PathParams& r) {
+ if( l.m_types.size() != r.m_types.size() ) {
+ ERROR(sp, E0000, "Type mismatch in type params `" << l << "` and `" << r << "`");
+ }
+ for(unsigned int i = 0; i < l.m_types.size(); i ++)
+ {
+ this->apply_equality(sp, l.m_types[i], cb_left, r.m_types[i], cb_right, nullptr);
+ }
+ };
+
+ if( l_t.m_data.is_Pointer() && r_t.m_data.is_Borrow() ) {
+ const auto& l_e = l_t.m_data.as_Pointer();
+ const auto& r_e = r_t.m_data.as_Borrow();
+ if( l_e.type != r_e.type ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " (pointer type mismatch)");
+ }
+ // 1. Equate inner types
+ this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
+
+ // 2. If that succeeds, add a coerce
+ if( node_ptr_ptr != nullptr )
+ {
+ auto& node_ptr = *node_ptr_ptr;
+ auto span = node_ptr->span();
+ // - Override the existing result type (to prevent infinite recursion)
+ node_ptr->m_res_type = r_t.clone();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Cast( mv$(span), mv$(node_ptr), l_t.clone() ));
+ //node_ptr->m_res_type = l_t.clone(); // < Set by _Cast
+
+ DEBUG("- Borrow->Pointer cast added - " << l_t << " <- " << r_t);
+ this->mark_change();
+ return ;
+ }
+ else
+ {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " (can't coerce)");
+ }
+ }
+
+ // - If tags don't match, error
+ if( l_t.m_data.tag() != r_t.m_data.tag() ) {
+ // Type error
+ this->dump();
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ TU_MATCH(::HIR::TypeRef::Data, (l_t.m_data, r_t.m_data), (l_e, r_e),
+ (Infer,
+ throw "";
+ ),
+ (Diverge,
+ TODO(sp, "Handle !");
+ ),
+ (Primitive,
+ if( l_e != r_e ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ ),
+ (Path,
+ if( l_e.path.m_data.tag() != r_e.path.m_data.tag() ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ TU_MATCH(::HIR::Path::Data, (l_e.path.m_data, r_e.path.m_data), (lpe, rpe),
+ (Generic,
+ if( lpe.m_path != rpe.m_path ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ equality_typeparams(lpe.m_params, rpe.m_params);
+ ),
+ (UfcsInherent,
+ equality_typeparams(lpe.params, rpe.params);
+ if( lpe.item != rpe.item )
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
+ ),
+ (UfcsKnown,
+ equality_typeparams(lpe.trait.m_params, rpe.trait.m_params);
+ equality_typeparams(lpe.params, rpe.params);
+ if( lpe.item != rpe.item )
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
+ ),
+ (UfcsUnknown,
+ // TODO: If the type is fully known, locate a suitable trait item
+ equality_typeparams(lpe.params, rpe.params);
+ if( lpe.item != rpe.item )
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ this->apply_equality(sp, *lpe.type, cb_left, *rpe.type, cb_right, nullptr);
+ )
+ )
+ ),
+ (Generic,
+ if( l_e.binding != r_e.binding ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ ),
+ (TraitObject,
+ if( l_e.m_traits.size() != r_e.m_traits.size() ) {
+ // TODO: Possibly allow inferrence reducing the set?
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - trait counts differ");
+ }
+ // NOTE: Lifetime is ignored
+ // TODO: Is this list sorted in any way? (if it's not sorted, this could fail when source does Send+Any instead of Any+Send)
+ for(unsigned int i = 0; i < l_e.m_traits.size(); i ++ )
+ {
+ auto& l_p = l_e.m_traits[i];
+ auto& r_p = r_e.m_traits[i];
+ if( l_p.m_path != r_p.m_path ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ equality_typeparams(l_p.m_params, r_p.m_params);
+ }
+ ),
+ (Array,
+ this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
+ if( l_e.size_val != r_e.size_val ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - sizes differ");
+ }
+ ),
+ (Slice,
+ this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
+ ),
+ (Tuple,
+ if( l_e.size() != r_e.size() ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Tuples are of different length");
+ }
+ for(unsigned int i = 0; i < l_e.size(); i ++)
+ {
+ this->apply_equality(sp, l_e[i], cb_left, r_e[i], cb_right, nullptr);
+ }
+ ),
+ (Borrow,
+ if( l_e.type != r_e.type ) {
+ // TODO: This could be allowed if left == Shared && right == Unique (reborrowing)
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Borrow classes differ");
+ }
+ // ------------------
+ // Coercions!
+ // ------------------
+ if( node_ptr_ptr != nullptr )
+ {
+ auto& node_ptr = *node_ptr_ptr;
+ const auto& left_inner_res = this->get_type(*l_e.inner);
+ const auto& right_inner_res = this->get_type(*r_e.inner);
+
+ // Allow cases where `right`: ::core::marker::Unsize<`left`>
+ bool succ = this->find_trait_impls(this->m_crate.get_lang_item_path(sp, "unsize"), right_inner_res, [&](const auto& args) {
+ DEBUG("- Found unsizing with args " << args);
+ return args.m_types[0] == left_inner_res;
+ });
+ if( succ ) {
+ auto span = node_ptr->span();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Unsize( mv$(span), mv$(node_ptr), l_t.clone() ));
+ node_ptr->m_res_type = l_t.clone();
+
+ this->mark_change();
+ return ;
+ }
+ // - If left is a trait object, right can unsize
+ // - If left is a slice, right can unsize/deref
+ if( left_inner_res.m_data.is_Slice() && !right_inner_res.m_data.is_Slice() )
+ {
+ const auto& left_slice = left_inner_res.m_data.as_Slice();
+ TU_IFLET(::HIR::TypeRef::Data, right_inner_res.m_data, Array, right_array,
+ this->apply_equality(sp, *left_slice.inner, cb_left, *right_array.inner, cb_right, nullptr);
+ auto span = node_ptr->span();
+ node_ptr = ::HIR::ExprNodeP(new ::HIR::ExprNode_Unsize( mv$(span), mv$(node_ptr), l_t.clone() ));
+ node_ptr->m_res_type = l_t.clone();
+
+ this->mark_change();
+ return ;
+ )
+ else TU_IFLET(::HIR::TypeRef::Data, right_inner_res.m_data, Generic, right_arg,
+ TODO(sp, "Search for Unsize bound on generic");
+ )
+ else
+ {
+ // Apply deref coercions
+ }
+ }
+ // - If right has a deref chain to left, build it
+ }
+ this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
+ ),
+ (Pointer,
+ if( l_e.type != r_e.type ) {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t << " - Pointer mutability differs");
+ }
+ this->apply_equality(sp, *l_e.inner, cb_left, *r_e.inner, cb_right, nullptr);
+ ),
+ (Function,
+ if( l_e.is_unsafe != r_e.is_unsafe
+ || l_e.m_abi != r_e.m_abi
+ || l_e.m_arg_types.size() != r_e.m_arg_types.size()
+ )
+ {
+ ERROR(sp, E0000, "Type mismatch between " << l_t << " and " << r_t);
+ }
+ // NOTE: No inferrence in fn types? Not sure, lazy way is to allow it.
+ this->apply_equality(sp, *l_e.m_rettype, cb_left, *r_e.m_rettype, cb_right, nullptr);
+ for(unsigned int i = 0; i < l_e.m_arg_types.size(); i ++ ) {
+ this->apply_equality(sp, l_e.m_arg_types[i], cb_left, r_e.m_arg_types[i], cb_right, nullptr);
+ }
+ ),
+ (Closure,
+ TODO(sp, "apply_equality - Closure");
+ )
+ )
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+bool typeck::TypecheckContext::check_trait_bound(const Span& sp, const ::HIR::TypeRef& type, const ::HIR::GenericPath& trait, ::std::function<const ::HIR::TypeRef&(const ::HIR::TypeRef&)> placeholder) const
+{
+ if( this->find_trait_impls_bound(sp, trait.m_path, placeholder(type), [&](const auto& args){
+ DEBUG("TODO: Check args for " << trait.m_path << args << " against " << trait);
+ return true;
+ })
+ )
+ {
+ // Satisfied by generic
+ return true;
+ }
+ else if( this->m_crate.find_trait_impls(trait.m_path, type, placeholder, [&](const auto& impl) {
+ DEBUG("- Bound " << type << " : " << trait << " satisfied by impl" << impl.m_params.fmt_args());
+ // TODO: Recursively check
+ return true;
+ })
+ )
+ {
+ // Match!
+ return true;
+ }
+ else {
+ DEBUG("- Bound " << type << " ("<<placeholder(type)<<") : " << trait << " failed");
+ return false;
+ }
+}
+
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+bool typeck::TypecheckContext::iterate_bounds( ::std::function<bool(const ::HIR::GenericBound&)> cb) const
+{
+ const ::HIR::GenericParams* v[2] = { m_item_params, m_impl_params };
+ for(auto p : v)
+ {
+ if( !p ) continue ;
+ for(const auto& b : p->m_bounds)
+ if(cb(b)) return true;
+ }
+ return false;
+}
+bool typeck::TypecheckContext::find_trait_impls(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback)
+{
+ Span sp = Span();
+ TRACE_FUNCTION_F("trait = " << trait << ", type = " << type);
+ // 1. Search generic params
+ if( find_trait_impls_bound(sp, trait, type, callback) )
+ return true;
+ // 2. Search crate-level impls
+ return find_trait_impls_crate(trait, type, callback);
+}
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+::HIR::TypeRef typeck::TypecheckContext::expand_associated_types(const Span& sp, ::HIR::TypeRef input) const
+{
+ TRACE_FUNCTION_F(input);
+ TU_MATCH(::HIR::TypeRef::Data, (input.m_data), (e),
+ (Infer,
+ auto& ty = this->get_type(input);
+ return ty.clone();
+ ),
+ (Diverge,
+ ),
+ (Primitive,
+ ),
+ (Path,
+ // - Only try resolving if the binding isn't known
+ if( !e.binding.is_Unbound() )
+ return input;
+
+ TU_MATCH(::HIR::Path::Data, (e.path.m_data), (e2),
+ (Generic,
+ for(auto& arg : e2.m_params.m_types)
+ arg = expand_associated_types(sp, mv$(arg));
+ ),
+ (UfcsInherent,
+ TODO(sp, "Path - UfcsInherent - " << e.path);
+ ),
+ (UfcsKnown,
+ DEBUG("Locating associated type for " << e.path);
+ // TODO: Use the marker `e.binding` to tell if it's worth trying
+
+ *e2.type = expand_associated_types(sp, mv$(*e2.type));
+
+ // Search for a matching trait impl
+ const ::HIR::TraitImpl* impl_ptr = nullptr;
+ ::std::vector< const ::HIR::TypeRef*> impl_args;
+
+ auto cb_get_infer = [&](const auto& ty)->const auto& {
+ if( ty.m_data.is_Infer() )
+ return this->get_type(ty);
+ else
+ return ty;
+ };
+
+ // 1. Bounds
+ bool rv;
+ rv = this->iterate_bounds([&](const auto& b) {
+ TU_IFLET(::HIR::GenericBound, b, TypeEquality, be,
+ DEBUG("Equality - " << be.type << " = " << be.other_type);
+ if( input == be.type ) {
+ input = be.other_type.clone();
+ return true;
+ }
+ )
+ return false;
+ });
+ if( rv ) {
+ return input;
+ }
+
+ // Use bounds on other associated types too (if `e2.type` was resolved to a fixed associated type)
+ TU_IFLET(::HIR::TypeRef::Data, e2.type->m_data, Path, e3,
+ TU_IFLET(::HIR::Path::Data, e3.path.m_data, UfcsKnown, pe,
+ // TODO: Search for equality bounds on this associated type (e3) that match the entire type (e2)
+ // - Does simplification of complex associated types
+ const auto& trait_ptr = this->m_crate.get_trait_by_path(sp, pe.trait.m_path);
+ const auto& assoc_ty = trait_ptr.m_types.at(pe.item);
+ DEBUG("TODO: Search bounds " << assoc_ty.m_params.fmt_bounds());
+ // Resolve where Self=e2.type, for the associated type check.
+ auto cb_placeholders_type = [&](const auto& ty)->const auto&{
+ TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
+ if( e.binding == 0xFFFF )
+ return *e2.type;
+ else
+ TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name);
+ )
+ else {
+ return ty;
+ }
+ };
+ // Resolve where Self=pe.type (i.e. for the trait this inner UFCS is on)
+ auto cb_placeholders_trait = [&](const auto& ty)->const auto&{
+ TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
+ if( e.binding == 0xFFFF )
+ return *pe.type;
+ else {
+ // TODO: Look in pe.trait.m_params
+ TODO(sp, "Handle type params when expanding associated bound (#" << e.binding << " " << e.name);
+ }
+ )
+ else {
+ return ty;
+ }
+ };
+ for(const auto& bound : assoc_ty.m_params.m_bounds)
+ {
+ TU_IFLET(::HIR::GenericBound, bound, TypeEquality, be,
+ // IF: bound's type matches the input, replace with bounded equality
+ // `<Self::IntoIter as Iterator>::Item = Self::Item`
+ if( be.type.compare_with_paceholders(sp, input, cb_placeholders_type ) ) {
+ DEBUG("Match of " << be.type << " with " << input);
+ DEBUG("- Replace `input` with " << be.other_type << ", Self=" << *pe.type);
+ if( monomorphise_type_needed(be.other_type) ) {
+ return monomorphise_type_with(sp, be.other_type, cb_placeholders_trait);
+ }
+ else {
+ return be.other_type.clone();
+ }
+ }
+ )
+ }
+ DEBUG("e2 = " << *e2.type << ", input = " << input);
+ )
+ )
+
+ // 2. Crate-level impls
+ rv = this->m_crate.find_trait_impls(e2.trait.m_path, *e2.type, cb_get_infer,
+ [&](const auto& impl) {
+ DEBUG("Found impl" << impl.m_params.fmt_args() << " " << e2.trait.m_path << impl.m_trait_args << " for " << impl.m_type);
+ // - Populate the impl's type arguments
+ impl_args.clear();
+ impl_args.resize( impl.m_params.m_types.size() );
+ // - Match with `Self`
+ auto cb_res = [&](unsigned int slot, const ::HIR::TypeRef& ty) {
+ DEBUG("- Set " << slot << " = " << ty);
+ if( slot >= impl_args.size() ) {
+ BUG(sp, "Impl parameter out of range - " << slot);
+ }
+ auto& slot_r = impl_args.at(slot);
+ if( slot_r != nullptr ) {
+ DEBUG("TODO: Match " << slot_r << " == " << ty << " when encountered twice");
+ }
+ else {
+ slot_r = &ty;
+ }
+ };
+ impl.m_type.match_generics(sp, *e2.type, cb_get_infer, cb_res);
+ for( unsigned int i = 0; i < impl.m_trait_args.m_types.size(); i ++ )
+ {
+ impl.m_trait_args.m_types[i].match_generics(sp, e2.trait.m_params.m_types.at(i), cb_get_infer, cb_res);
+ }
+ auto expand_placeholder = [&](const auto& ty)->const auto& {
+ if( ty.m_data.is_Infer() )
+ return this->get_type(ty);
+ else TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
+ if( e.binding == 0xFFFF ) {
+ //TODO(sp, "Look up 'Self' in expand_associated_types::expand_placeholder (" << *e2.type << ")");
+ return *e2.type;
+ }
+ else {
+ assert(e.binding < impl_args.size());
+ assert( impl_args[e.binding] );
+ return *impl_args[e.binding];
+ }
+ )
+ else
+ return ty;
+ };
+ for( const auto& bound : impl.m_params.m_bounds )
+ {
+ TU_MATCH_DEF(::HIR::GenericBound, (bound), (be),
+ (
+ ),
+ (TraitBound,
+ if( !this->check_trait_bound(sp, be.type, be.trait.m_path, expand_placeholder) )
+ {
+ return false;
+ }
+ )
+ )
+ }
+ // TODO: Bounds check? (here or elsewhere?)
+ // - Need to check bounds before picking this impl, because the bound could be preventing false matches
+ if( impl.m_trait_args.m_types.size() > 0 )
+ {
+ TODO(sp, "Check trait type parameters in expand_associated_types");
+ }
+ impl_ptr = &impl;
+ return true;
+ });
+ if( rv )
+ {
+ // An impl was found:
+ assert(impl_ptr);
+
+ // - Monomorphise the output type
+ auto new_type = monomorphise_type_with(sp, impl_ptr->m_types.at( e2.item ), [&](const auto& ty)->const auto& {
+ const auto& ge = ty.m_data.as_Generic();
+ assert(ge.binding < impl_args.size());
+ return *impl_args[ge.binding];
+ });
+ DEBUG("Converted UfcsKnown - " << e.path << " = " << new_type << " using " << e2.item << " = " << impl_ptr->m_types.at( e2.item ));
+ return new_type;
+ }
+
+ // TODO: If there are no ivars in this path, set its binding to Opaque
+
+ DEBUG("Couldn't resolve associated type for " << input);
+ ),
+ (UfcsUnknown,
+ BUG(sp, "Encountered UfcsUnknown");
+ )
+ )
+ ),
+ (Generic,
+ ),
+ (TraitObject,
+ // Recurse?
+ ),
+ (Array,
+ *e.inner = expand_associated_types(sp, mv$(*e.inner));
+ ),
+ (Slice,
+ *e.inner = expand_associated_types(sp, mv$(*e.inner));
+ ),
+ (Tuple,
+ for(auto& sub : e) {
+ sub = expand_associated_types(sp, mv$(sub));
+ }
+ ),
+ (Borrow,
+ *e.inner = expand_associated_types(sp, mv$(*e.inner));
+ ),
+ (Pointer,
+ *e.inner = expand_associated_types(sp, mv$(*e.inner));
+ ),
+ (Function,
+ // Recurse?
+ ),
+ (Closure,
+ // Recurse?
+ )
+ )
+ return input;
+}
+
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+bool typeck::TypecheckContext::find_named_trait_in_trait(const Span& sp,
+ const ::HIR::SimplePath& des, const ::HIR::Trait& trait_ptr,
+ const ::HIR::PathParams& pp,
+ ::std::function<bool(const ::HIR::PathParams&)> callback
+ ) const
+{
+ assert( pp.m_types.size() == trait_ptr.m_params.m_types.size() );
+ for( const auto& pt : trait_ptr.m_parent_traits )
+ {
+ auto pt_pp = monomorphise_path_params_with(Span(), pt.m_params.clone(), [&](const auto& gt)->const auto& {
+ const auto& ge = gt.m_data.as_Generic();
+ if( ge.binding == 0xFFFF ) {
+ return gt;
+ }
+ else {
+ if( ge.binding >= pp.m_types.size() )
+ BUG(sp, "find_named_trait_in_trait - Generic #" << ge.binding << " " << ge.name << " out of range");
+ return pp.m_types[ge.binding];
+ }
+ }, false);
+
+ if( pt.m_path == des ) {
+ //TODO(Span(), "Fix arguments for a parent trait and call callback - " << pt << " with paramset " << trait_ptr.m_params.fmt_args() << " = " << pt_pp);
+ callback( pt_pp );
+ return true;
+ }
+ }
+ return false;
+}
+bool typeck::TypecheckContext::find_trait_impls_bound(const Span& sp, const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const
+{
+ return this->iterate_bounds([&](const auto& b) {
+ TU_IFLET(::HIR::GenericBound, b, TraitBound, e,
+ if( e.type != type )
+ return false;
+ if( e.trait.m_path.m_path == trait ) {
+ if( callback(e.trait.m_path.m_params) ) {
+ return true;
+ }
+ }
+ if( this->find_named_trait_in_trait(sp, trait, *e.trait.m_trait_ptr, e.trait.m_path.m_params, callback) ) {
+ return true;
+ }
+ )
+ return false;
+ });
+}
+bool typeck::TypecheckContext::find_trait_impls_crate(const ::HIR::SimplePath& trait, const ::HIR::TypeRef& type, ::std::function<bool(const ::HIR::PathParams&)> callback) const
+{
+ return this->m_crate.find_trait_impls(trait, type, [&](const auto& ty)->const auto&{
+ if( ty.m_data.is_Infer() )
+ return this->get_type(ty);
+ else
+ return ty;
+ },
+ [&](const auto& impl) {
+ DEBUG("[find_trait_impls_crate] Found impl" << impl.m_params.fmt_args() << " " << trait << impl.m_trait_args << " for " << impl.m_type);
+ return callback(impl.m_trait_args);
+ }
+ );
+}
+
+bool typeck::TypecheckContext::trait_contains_method(const Span& sp, const ::HIR::GenericPath& trait_path, const ::HIR::Trait& trait_ptr, const ::std::string& name, ::HIR::GenericPath& out_path) const
+{
+ auto it = trait_ptr.m_values.find(name);
+ if( it != trait_ptr.m_values.end() ) {
+ if( it->second.is_Function() ) {
+ out_path = trait_path.clone();
+ return true;
+ }
+ }
+
+ // TODO: Prevent infinite recursion
+ for(const auto& st : trait_ptr.m_parent_traits)
+ {
+ auto& st_ptr = this->m_crate.get_trait_by_path(sp, st.m_path);
+ if( trait_contains_method(sp, st, st_ptr, name, out_path) ) {
+ out_path.m_params = monomorphise_path_params_with(sp, mv$(out_path.m_params), [&](const auto& gt)->const auto& {
+ const auto& ge = gt.m_data.as_Generic();
+ assert(ge.binding < 256);
+ assert(ge.binding < trait_path.m_params.m_types.size());
+ return trait_path.m_params.m_types[ge.binding];
+ }, false);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+unsigned int typeck::TypecheckContext::autoderef_find_method(const Span& sp, const ::HIR::TypeRef& top_ty, const ::std::string& method_name, /* Out -> */::HIR::Path& fcn_path) const
+{
+ unsigned int deref_count = 0;
+ const auto* current_ty = &top_ty;
+ do {
+ const auto& ty = this->get_type(*current_ty);
+ if( ty.m_data.is_Infer() ) {
+ return ~0u;
+ }
+
+ // 1. Search generic bounds for a match
+ const ::HIR::GenericParams* v[2] = { m_item_params, m_impl_params };
+ for(auto p : v)
+ {
+ if( !p ) continue ;
+ for(const auto& b : p->m_bounds)
+ {
+ TU_IFLET(::HIR::GenericBound, b, TraitBound, e,
+ DEBUG("Bound " << e.type << " : " << e.trait.m_path);
+ // TODO: Match using _ replacement
+ if( e.type != ty )
+ continue ;
+
+ // - Bound's type matches, check if the bounded trait has the method we're searching for
+ // > TODO: Search supertraits too
+ DEBUG("- Matches " << ty);
+ ::HIR::GenericPath final_trait_path;
+ assert(e.trait.m_trait_ptr);
+ if( !this->trait_contains_method(sp, e.trait.m_path, *e.trait.m_trait_ptr, method_name, final_trait_path) )
+ continue ;
+ DEBUG("- Found trait " << final_trait_path);
+
+ // Found the method, return the UFCS path for it
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( ty.clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return deref_count;
+ )
+ }
+ }
+
+ TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Generic, e,
+ // No match, keep trying.
+ )
+ else if( ty.m_data.is_Path() && ty.m_data.as_Path().path.m_data.is_UfcsKnown() )
+ {
+ const auto& e = ty.m_data.as_Path().path.m_data.as_UfcsKnown();
+ // UFCS known - Assuming that it's reached the maximum resolvable level (i.e. a type within is generic), search for trait bounds on the type
+ const auto& trait = this->m_crate.get_trait_by_path(sp, e.trait.m_path);
+ const auto& assoc_ty = trait.m_types.at( e.item );
+ // NOTE: The bounds here have 'Self' = the type
+ for(const auto& bound : assoc_ty.m_params.m_bounds )
+ {
+ TU_IFLET(::HIR::GenericBound, bound, TraitBound, be,
+ assert(be.trait.m_trait_ptr);
+ ::HIR::GenericPath final_trait_path;
+ if( !this->trait_contains_method(sp, be.trait.m_path, *be.trait.m_trait_ptr, method_name, final_trait_path) )
+ continue ;
+ DEBUG("- Found trait " << final_trait_path);
+
+ // Found the method, return the UFCS path for it
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( ty.clone() ),
+ mv$(final_trait_path),
+ method_name,
+ {}
+ }) );
+ return deref_count;
+ )
+ }
+ }
+ else {
+ // 2. Search for inherent methods
+ for(const auto& impl : m_crate.m_type_impls)
+ {
+ if( impl.matches_type(ty) ) {
+ DEBUG("Mactching impl " << impl.m_type);
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsInherent({
+ box$(ty.clone()),
+ method_name,
+ {}
+ }) );
+ return deref_count;
+ }
+ }
+ // 3. Search for trait methods (using currently in-scope traits)
+ for(const auto& trait_ref : ::reverse(m_traits))
+ {
+ // TODO: Search supertraits too
+ auto it = trait_ref.second->m_values.find(method_name);
+ if( it == trait_ref.second->m_values.end() )
+ continue ;
+ if( !it->second.is_Function() )
+ continue ;
+ DEBUG("Search for impl of " << *trait_ref.first);
+ if( find_trait_impls_crate(*trait_ref.first, ty, [](const auto&) { return true; }) ) {
+ DEBUG("Found trait impl " << *trait_ref.first << " for " << ty);
+ fcn_path = ::HIR::Path( ::HIR::Path::Data::make_UfcsKnown({
+ box$( ty.clone() ),
+ trait_ref.first->clone(),
+ method_name,
+ {}
+ }) );
+ return deref_count;
+ }
+ }
+ }
+
+ // 3. Dereference and try again
+ deref_count += 1;
+ TU_IFLET(::HIR::TypeRef::Data, ty.m_data, Borrow, e,
+ current_ty = &*e.inner;
+ )
+ else {
+ // TODO: Search for a Deref impl
+ current_ty = nullptr;
+ }
+ } while( current_ty );
+ // Dereference failed! This is a hard error (hitting _ is checked above and returns ~0)
+ this->dump();
+ TODO(sp, "Error when no method could be found, but type is known - (: " << top_ty << ")." << method_name);
+}
+
+
+
+
+// -------------------------------------------------------------------------------------------------------------------
+//
+// -------------------------------------------------------------------------------------------------------------------
+void typeck::TypecheckContext::set_ivar_to(unsigned int slot, ::HIR::TypeRef type)
+{
+ auto sp = Span();
+ auto& root_ivar = this->get_pointed_ivar(slot);
+ DEBUG("set_ivar_to(" << slot << " { " << *root_ivar.type << " }, " << type << ")");
+
+ // If the left type was '_', alias the right to it
+ TU_IFLET(::HIR::TypeRef::Data, type.m_data, Infer, l_e,
+ assert( l_e.index != slot );
+ DEBUG("Set IVar " << slot << " = @" << l_e.index);
+
+ if( l_e.ty_class != ::HIR::InferClass::None ) {
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (root_ivar.type->m_data), (e),
+ (
+ ERROR(sp, E0000, "Type unificiation of literal with invalid type - " << *root_ivar.type);
+ ),
+ (Primitive,
+ check_type_class_primitive(sp, type, l_e.ty_class, e);
+ ),
+ (Infer,
+ // TODO: Check for right having a ty_class
+ if( e.ty_class != ::HIR::InferClass::None && e.ty_class != l_e.ty_class ) {
+ ERROR(sp, E0000, "Unifying types with mismatching literal classes");
+ }
+ )
+ )
+ }
+
+ root_ivar.alias = l_e.index;
+ root_ivar.type.reset();
+ )
+ else {
+ // Otherwise, store left in right's slot
+ DEBUG("Set IVar " << slot << " = " << type);
+ root_ivar.type = box$( mv$(type) );
+ }
+
+ this->mark_change();
+}
+
+void typeck::TypecheckContext::ivar_unify(unsigned int left_slot, unsigned int right_slot)
+{
+ auto sp = Span();
+ if( left_slot != right_slot )
+ {
+ auto& left_ivar = this->get_pointed_ivar(left_slot);
+
+ // TODO: Assert that setting this won't cause a loop.
+ auto& root_ivar = this->get_pointed_ivar(right_slot);
+
+ TU_IFLET(::HIR::TypeRef::Data, root_ivar.type->m_data, Infer, re,
+ if(re.ty_class != ::HIR::InferClass::None) {
+ TU_MATCH_DEF(::HIR::TypeRef::Data, (left_ivar.type->m_data), (le),
+ (
+ ERROR(sp, E0000, "Type unificiation of literal with invalid type - " << *left_ivar.type);
+ ),
+ (Infer,
+ if( le.ty_class != ::HIR::InferClass::None && le.ty_class != re.ty_class )
+ {
+ ERROR(sp, E0000, "Unifying types with mismatching literal classes");
+ }
+ le.ty_class = re.ty_class;
+ ),
+ (Primitive,
+ check_type_class_primitive(sp, *left_ivar.type, re.ty_class, le);
+ )
+ )
+ }
+ )
+ else {
+ BUG(sp, "Unifying over a concrete type - " << *root_ivar.type);
+ }
+
+ root_ivar.alias = left_slot;
+ root_ivar.type.reset();
+
+ this->mark_change();
+ }
+}
+typeck::TypecheckContext::IVar& typeck::TypecheckContext::get_pointed_ivar(unsigned int slot) const
+{
+ auto index = slot;
+ unsigned int count = 0;
+ assert(index < m_ivars.size());
+ while( m_ivars.at(index).is_alias() ) {
+ index = m_ivars.at(index).alias;
+
+ if( count >= m_ivars.size() ) {
+ this->dump();
+ BUG(Span(), "Loop detected in ivar list when starting at " << slot << ", current is " << index);
+ }
+ count ++;
+ }
+ return const_cast<IVar&>(m_ivars.at(index));
+}