%% -*- erlang-indent-level: 2 -*-
%%------------------------------------------------------------------------------
-module(cuter_types).
-export([parse_spec/3, retrieve_types/1, retrieve_specs/1, find_spec/2, get_kind/1]).
-export([params_of_t_function_det/1, ret_of_t_function_det/1, atom_of_t_atom_lit/1, integer_of_t_integer_lit/1,
elements_type_of_t_list/1, elements_type_of_t_nonempty_list/1, elements_types_of_t_tuple/1,
elements_types_of_t_union/1, bounds_of_t_range/1, segment_size_of_bitstring/1]).
-export_type([erl_type/0, erl_spec_clause/0, erl_spec/0, stored_specs/0, stored_types/0, stored_spec_value/0, t_range_limit/0]).
-include("cuter_macros.hrl").
-include("cuter_types.hrl").
%% Define tags
-define(type_variable, vart).
-define(type_var, tvar).
-define(max_char, 16#10ffff).
%% Pre-processed types.
-type type_name() :: atom().
-type type_arity() :: byte().
-type type_var() :: {?type_var, atom()}.
-type remote_type() :: {module(), type_name(), type_arity()}.
-type record_name() :: atom().
-type record_field_name() :: atom().
-type record_field_type() :: {record_field_name(), raw_type()}.
-type dep() :: remote_type().
-type deps() :: ordsets:ordset(remote_type()).
-record(t, {
kind,
rep,
deps = ordsets:new() :: deps()
}).
-type erl_type() :: t_any() % any()
| t_nil() % []
| t_atom() % atom()
| t_atom_lit() % Erlang_Atom
| t_integer() % integer(), +infinity, -inifinity
| t_integer_lit() % Erlang_Integer
| t_float() % float()
| t_tuple() % tuple(), {TList}
| t_list() % list(Type)
| t_nonempty_list() % nonempty_list(Type)
| t_union() % Type1 | ... | TypeN
| t_range() % Erlang_Integer..Erlang_Integer
| t_bitstring() % <<_:M>>
| t_function() % function() | Fun | BoundedFun
.
-type raw_type() :: erl_type()
| t_local() % Local Type Usage
| t_remote() % Remote Type Usage
| t_record() % Record Usage
| t_type_var() % Type Variable
.
-type t_any() :: #t{kind :: ?any_tag}.
-type t_nil() :: #t{kind :: ?nil_tag}.
-type t_atom() :: #t{kind :: ?atom_tag}.
-type t_atom_lit() :: #t{kind :: ?atom_lit_tag, rep :: atom()}.
-type t_integer() :: #t{kind :: ?integer_tag}.
-type t_integer_lit() :: #t{kind :: ?integer_lit_tag, rep :: integer()}.
-type t_float() :: #t{kind :: ?float_tag}.
-type t_tuple() :: #t{kind :: ?tuple_tag, rep :: [raw_type()]}.
-type t_list() :: #t{kind :: ?list_tag, rep :: raw_type()}.
-type t_nonempty_list() :: #t{kind :: ?nonempty_list_tag, rep :: raw_type()}.
-type t_union() :: #t{kind :: ?union_tag, rep :: [raw_type()]}.
-type t_range() :: #t{kind :: ?range_tag, rep :: {t_range_limit(), t_range_limit()}}.
-type t_range_limit() :: t_integer_lit() | t_integer_inf().
-type t_integer_inf() :: t_integer_pos_inf() | t_integer_neg_inf().
-type t_integer_pos_inf() :: #t{kind :: ?pos_inf}.
-type t_integer_neg_inf() :: #t{kind :: ?neg_inf}.
-type t_bitstring() :: #t{kind :: ?bitstring_tag, rep :: 1|8}.
-type t_function() :: #t{kind :: ?function_tag} | t_function_det().
-type t_function_det() :: #t{kind :: ?function_tag, rep :: {[raw_type()], raw_type(), [t_constraint()]}, deps :: deps()}.
-type t_constraint() :: {t_type_var(), raw_type()}.
-type t_local() :: #t{kind :: ?local_tag, rep :: {type_name(), [raw_type()]}}.
-type t_remote() :: #t{kind :: ?remote_tag, rep :: {module(), type_name(), [raw_type()]}}.
-type t_record() :: #t{kind :: ?record_tag, rep :: {record_name(), [record_field_type()]}}.
-type t_type_var() :: #t{kind :: ?type_variable, rep :: type_var()}.
%% How pre-processed types are stored.
-type stored_type_key() :: {record, record_name()} | {type, type_name(), type_arity()}.
-type stored_type_value() :: [record_field_type()] | {any(), [type_var()]}. % raw_type()
-type stored_types() :: dict:dict(stored_type_key(), stored_type_value()).
-type stored_spec_key() :: {type_name(), type_arity()}.
-type stored_spec_value() :: [t_function_det()].
-type stored_specs() :: dict:dict(stored_spec_key(), stored_spec_value()).
-type type_var_env() :: dict:dict(type_var(), raw_type()).
-type erl_spec_clause() :: t_function_det().
-type erl_spec() :: [erl_spec_clause()].
%% Pre-process the type & record declarations of a module.
-spec retrieve_types([cuter_cerl:cerl_attr_type()]) -> stored_types().
retrieve_types(TypeAttrs) ->
lists:foldl(fun process_type_attr/2, dict:new(), TypeAttrs).
-spec process_type_attr(cuter_cerl:cerl_recdef() | cuter_cerl:cerl_typedef(), stored_types()) -> stored_types().
%% Declaration of a record.
process_type_attr({{record, Name}, Fields, []}, Processed) ->
Fs = [t_field_from_form(Field) || Field <- Fields],
Record = t_record(Name, Fs),
dict:store({record, Name}, Record, Processed);
%% Declaration of a type.
process_type_attr({Name, Repr, Vars}, Processed) ->
Type = safe_t_from_form(Repr),
Vs = [{?type_var, Var} || {var, _, Var} <- Vars],
dict:store({type, Name, length(Vs)}, {Type, Vs}, Processed).
%% The fields of a declared record.
-spec t_field_from_form(cuter_cerl:cerl_record_field()) -> record_field_type().
t_field_from_form({record_field, _, {atom, _, Name}}) ->
{Name, t_any()};
t_field_from_form({record_field, _, {atom, _, Name}, _Default}) ->
{Name, t_any()};
t_field_from_form({typed_record_field, {record_field, _, {atom, _, Name}}, Type}) ->
{Name, safe_t_from_form(Type)};
t_field_from_form({typed_record_field, {record_field, _, {atom, _, Name}, _Default}, Type}) ->
{Name, safe_t_from_form(Type)}.
%% Provision for unsupported types.
safe_t_from_form(Form) ->
try t_from_form(Form)
catch throw:{unsupported, Info} ->
cuter_pp:form_has_unsupported_type(Info),
t_any()
end.
%% Parse a type.
-spec t_from_form(cuter_cerl:cerl_type()) -> raw_type().
%% Erlang_Atom
t_from_form({atom, _, Atom}) ->
t_atom_lit(Atom);
%% Erlang_Integer
t_from_form({integer, _, Integer}) ->
t_integer_lit(Integer);
%% integer()
t_from_form({type, _, integer, []}) ->
t_integer();
%% nil
t_from_form({type, _, nil, []}) ->
t_nil();
%% any()
t_from_form({type, _, any, []}) ->
t_any();
%% term()
t_from_form({type, _, term, []}) ->
t_any();
%% atom()
t_from_form({type, _, atom, []}) ->
t_atom();
%% module()
t_from_form({type, _, module, []}) ->
t_module();
%% float()
t_from_form({type, _, float, []}) ->
t_float();
%% tuple()
t_from_form({type, _, tuple, any}) ->
t_tuple();
%% {TList}
t_from_form({type, _, tuple, Types}) ->
Ts = [t_from_form(T) || T <- Types],
t_tuple(Ts);
%% list()
t_from_form({type, _, list, []}) ->
t_list();
%% list(Type)
t_from_form({type, _, list, [Type]}) ->
T = t_from_form(Type),
t_list(T);
%% Type1 | ... | TypeN
t_from_form({type, _, union, Types}) ->
Ts = [t_from_form(T) || T <- Types],
t_union(Ts);
%% boolean()
t_from_form({type, _, boolean, []}) ->
t_union([t_atom_lit(true), t_atom_lit(false)]);
%% number()
t_from_form({type, _, number, []}) ->
t_union([t_integer(), t_float()]);
%% Erlang_Integer..Erlang_Integer
t_from_form({type, _, range, [{integer, _, I1}, {integer, _, I2}]}) ->
t_range(t_integer_lit(I1), t_integer_lit(I2));
%% non_neg_integer()
t_from_form({type, _, non_neg_integer, []}) ->
t_range(t_integer_lit(0), t_pos_inf());
%% pos_integer()
t_from_form({type, _, pos_integer, []}) ->
t_range(t_integer_lit(1), t_pos_inf());
%% neg_integer()
t_from_form({type, _, neg_integer, []}) ->
t_range(t_neg_inf(), t_integer_lit(-1));
%% char()
t_from_form({type, _, char, []}) ->
t_char();
%% byte()
t_from_form({type, _, byte, []}) ->
t_byte();
%% mfa()
t_from_form({type, _, mfa, []}) ->
t_tuple([t_module(), t_atom(), t_byte()]);
%% string()
t_from_form({type, _, string, []}) ->
t_list(t_char());
%% nonempty_list()
t_from_form({type, _, nonempty_list, []}) ->
t_nonempty_list();
%% nonempty_list(Type)
t_from_form({type, _, nonempty_list, [Type]}) ->
T = t_from_form(Type),
t_nonempty_list(T);
%% binary()
t_from_form({type, _, binary, []}) ->
t_bitstring(8);
%% bitstring()
t_from_form({type, _, bitstring, []}) ->
t_bitstring(1);
%% function()
t_from_form({type, _, function, []}) ->
t_function();
%% fun((TList) -> Type)
t_from_form({type, _, 'fun', [_Product, _RetType]}=Fun) ->
t_function_from_form(Fun);
%% fun((TList) -> Type) (bounded_fun)
t_from_form({type, _, 'bounded_fun', [_Fun, _Cs]}=BoundedFun) ->
t_bounded_function_from_form(BoundedFun);
%% ann_type
t_from_form({ann_type, _, [_Var, Type]}) ->
t_from_form(Type);
%% paren_type
t_from_form({paren_type, _, [Type]}) ->
t_from_form(Type);
%% remote_type
t_from_form({remote_type, _, [{atom, _, M}, {atom, _, Name}, Types]}) ->
Ts = [t_from_form(T) || T <- Types],
t_remote(M, Name, Ts);
%% Record
t_from_form({type, _, record, [{atom, _, Name} | FieldTypes]}) ->
Fields = [t_bound_field_from_form(F) || F <- FieldTypes],
t_record(Name, Fields);
%% Map
t_from_form({type, _, map, _}=X) ->
throw({unsupported, X});
%% local type
t_from_form({type, _, Name, Types}) ->
Ts = [t_from_form(T) || T <- Types],
t_local(Name, Ts);
%% Type Variable
t_from_form({var, _, Var}) ->
t_var(Var);
%% Unsupported forms
t_from_form(Type) ->
throw({unsupported, Type}).
-spec t_bound_field_from_form(cuter_cerl:cerl_type_record_field()) -> record_field_type().
%% Record Field.
t_bound_field_from_form({type, _, field_type, [{atom, _, Name}, Type]}) ->
{Name, t_from_form(Type)}.
-spec t_function_from_form(cuter_cerl:cerl_func()) -> t_function_det().
t_function_from_form({type, _, 'fun', [{type, _, 'product', Types}, RetType]}) ->
Ret = t_from_form(RetType),
Ts = [t_from_form(T) || T <- Types],
t_function(Ts, Ret).
-spec t_bounded_function_from_form(cuter_cerl:cerl_bounded_func()) -> t_function_det().
t_bounded_function_from_form({type, _, 'bounded_fun', [Fun, Constraints]}) ->
{type, _, 'fun', [{type, _, 'product', Types}, RetType]} = Fun,
Ret = t_from_form(RetType),
Ts = [t_from_form(T) || T <- Types],
Cs = [t_constraint_from_form(C) || C <- Constraints],
t_function(Ts, Ret, Cs).
-spec t_constraint_from_form(cuter_cerl:cerl_constraint()) -> t_constraint().
t_constraint_from_form({type, _, constraint, [{atom, _, is_subtype}, [{var, _, Var}, Type]]}) ->
{t_var(Var), t_from_form(Type)}.
%% Type constructors.
-spec t_any() -> t_any().
t_any() ->
#t{kind = ?any_tag}.
-spec t_atom_lit(atom()) -> t_atom_lit().
t_atom_lit(Atom) ->
#t{kind = ?atom_lit_tag, rep = Atom}.
-spec t_atom() -> t_atom().
t_atom() ->
#t{kind = ?atom_tag}.
-spec t_module() -> t_atom().
t_module() -> t_atom().
-spec t_integer_lit(integer()) -> t_integer_lit().
t_integer_lit(Integer) ->
#t{kind = ?integer_lit_tag, rep = Integer}.
-spec t_integer() -> t_integer().
t_integer() ->
#t{kind = ?integer_tag}.
-spec t_range(t_range_limit(), t_range_limit()) -> t_range().
t_range(Int1, Int2) ->
#t{kind = ?range_tag, rep = {Int1, Int2}}.
-spec t_pos_inf() -> t_integer_pos_inf().
t_pos_inf() ->
#t{kind = ?pos_inf}.
-spec t_neg_inf() -> t_integer_neg_inf().
t_neg_inf() ->
#t{kind = ?neg_inf}.
-spec t_char() -> t_range().
t_char() ->
t_range(t_integer_lit(0), t_integer_lit(?max_char)).
-spec t_nil() -> t_nil().
t_nil() ->
#t{kind = ?nil_tag}.
-spec t_float() -> t_float().
t_float() ->
#t{kind = ?float_tag}.
-spec t_list() -> t_list().
t_list() ->
#t{kind = ?list_tag, rep = t_any()}.
-spec t_list(raw_type()) -> t_list().
t_list(Type) ->
#t{kind = ?list_tag, rep = Type, deps = get_deps(Type)}.
-spec t_nonempty_list() -> t_nonempty_list().
t_nonempty_list() ->
#t{kind = ?nonempty_list_tag, rep = t_any()}.
-spec t_nonempty_list(raw_type()) -> t_nonempty_list().
t_nonempty_list(Type) ->
#t{kind = ?nonempty_list_tag, rep = Type, deps = get_deps(Type)}.
-spec t_tuple() -> t_tuple().
t_tuple() ->
#t{kind = ?tuple_tag, rep = []}.
-spec t_tuple([raw_type()]) -> t_tuple().
t_tuple(Types) ->
#t{kind = ?tuple_tag, rep = Types, deps = unify_deps(Types)}.
-spec t_union([raw_type()]) -> t_union().
t_union(Types) ->
#t{kind = ?union_tag, rep = Types, deps = unify_deps(Types)}.
-spec t_byte() -> t_range().
t_byte() ->
t_range(t_integer_lit(0), t_integer_lit(255)).
-spec t_local(type_name(), [raw_type()]) -> t_local().
t_local(Name, Types) ->
Rep = {Name, Types},
#t{kind = ?local_tag, rep = Rep, deps = unify_deps(Types)}.
-spec t_remote(module(), type_name(), [raw_type()]) -> t_remote().
t_remote(Mod, Name, Types) ->
Rep = {Mod, Name, Types},
Dep = {Mod, Name, length(Types)},
#t{kind = ?remote_tag, rep = Rep, deps = add_dep(Dep, unify_deps(Types))}.
-spec t_var(atom()) -> t_type_var().
t_var(Var) ->
#t{kind = ?type_variable, rep = {?type_var, Var}}.
-spec t_record(record_name(), [record_field_type()]) -> t_record().
t_record(Name, Fields) ->
Rep = {Name, Fields},
Ts = [T || {_, T} <- Fields],
#t{kind = ?record_tag, rep = Rep, deps = unify_deps(Ts)}.
-spec fields_of_t_record(t_record()) -> [record_field_type()].
fields_of_t_record(Record) ->
Rep = Record#t.rep,
element(2, Rep).
-spec t_bitstring(1 | 8) -> t_bitstring().
t_bitstring(N) ->
#t{kind = ?bitstring_tag, rep = N}.
-spec t_function() -> t_function().
t_function() ->
#t{kind = ?function_tag}.
-spec t_function([raw_type()], raw_type()) -> t_function_det().
t_function(Types, Ret) ->
Rep = {Types, Ret, []},
#t{kind = ?function_tag, rep = Rep, deps = unify_deps([Ret|Types])}.
-spec t_function([raw_type()], raw_type(), [t_constraint()]) -> t_function_det().
t_function(Types, Ret, Constraints) ->
Rep = {Types, Ret, Constraints},
Ts = [T || {_V, T} <- Constraints],
#t{kind = ?function_tag, rep = Rep, deps = unify_deps([Ret|Types] ++ Ts)}.
%% Accessors of representations.
-spec params_of_t_function_det(t_function_det()) -> [raw_type()].
params_of_t_function_det(#t{kind = ?function_tag, rep = {Params, _Ret, _Constraints}}) ->
Params.
-spec ret_of_t_function_det(t_function_det()) -> raw_type().
ret_of_t_function_det(#t{kind = ?function_tag, rep = {_Params, Ret, _Constraints}}) ->
Ret.
-spec atom_of_t_atom_lit(t_atom_lit()) -> atom().
atom_of_t_atom_lit(#t{kind = ?atom_lit_tag, rep = Atom}) ->
Atom.
-spec integer_of_t_integer_lit(t_integer_lit()) -> integer().
integer_of_t_integer_lit(#t{kind = ?integer_lit_tag, rep = Integer}) ->
Integer.
-spec elements_type_of_t_list(t_list()) -> raw_type().
elements_type_of_t_list(#t{kind = ?list_tag, rep = Type}) ->
Type.
-spec elements_type_of_t_nonempty_list(t_nonempty_list()) -> raw_type().
elements_type_of_t_nonempty_list(#t{kind = ?nonempty_list_tag, rep = Type}) ->
Type.
-spec elements_types_of_t_tuple(t_tuple()) -> [raw_type()].
elements_types_of_t_tuple(#t{kind = ?tuple_tag, rep = Types}) ->
Types.
-spec elements_types_of_t_union(t_union()) -> [raw_type()].
elements_types_of_t_union(#t{kind = ?union_tag, rep = Types}) ->
Types.
-spec bounds_of_t_range(t_range()) -> {t_range_limit(), t_range_limit()}.
bounds_of_t_range(#t{kind = ?range_tag, rep = Limits}) ->
Limits.
-spec segment_size_of_bitstring(t_bitstring()) -> integer().
segment_size_of_bitstring(#t{kind = ?bitstring_tag, rep = Sz}) ->
Sz.
-spec is_tvar_wild_card(t_type_var()) -> boolean().
is_tvar_wild_card(#t{kind = ?type_variable, rep = {?type_var, Var}}) ->
Var =:= '_'.
%% Helper functions for kinds.
-spec get_kind(raw_type()) -> atom().
get_kind(Type) ->
Type#t.kind.
%% Helper functions for dependencies.
-spec get_deps(raw_type()) -> deps().
get_deps(Type) ->
Type#t.deps.
-spec has_deps(raw_type()) -> boolean().
has_deps(Type) ->
get_deps(Type) =/= ordsets:new().
-spec add_dep(dep(), deps()) -> deps().
add_dep(Dep, Deps) ->
ordsets:add_element(Dep, Deps).
-spec unify_deps([raw_type()]) -> deps().
unify_deps(Types) ->
ordsets:union([T#t.deps || T <- Types]).
%% Deal with specs.
-spec retrieve_specs([cuter_cerl:cerl_attr_spec()]) -> stored_specs().
retrieve_specs(SpecAttrs) ->
lists:foldl(fun process_spec_attr/2, dict:new(), SpecAttrs).
-spec process_spec_attr(cuter_cerl:cerl_attr_spec(), stored_specs()) -> stored_specs().
process_spec_attr({FA, Specs}, Processed) ->
Xs = [t_spec_from_form(Spec) || Spec <- Specs],
dict:store(FA, Xs, Processed).
-spec t_spec_from_form(cuter_cerl:cerl_spec_func()) -> t_function_det().
t_spec_from_form({type, _, 'fun', _}=Fun) ->
t_function_from_form(Fun);
t_spec_from_form({type, _, 'bounded_fun', _}=Fun) ->
t_bounded_function_from_form(Fun).
-spec find_spec(stored_spec_key(), stored_specs()) -> {'ok', stored_spec_value()} | 'error'.
find_spec(FA, Specs) ->
dict:find(FA, Specs).
%% Parse the spec of an MFA.
-type spec_parse_reply() :: {error, has_remote_types | recursive_type}
| {error, unsupported_type, type_name()}
| {ok, erl_spec()}.
-spec parse_spec(stored_spec_key(), stored_spec_value(), stored_types()) -> spec_parse_reply().
parse_spec(FA, Spec, Types) ->
try parse_spec_clauses(FA, Spec, Types, []) of
{error, has_remote_types}=E -> E;
Parsed -> {ok, Parsed}
catch
throw:remote_type -> {error, has_remote_types};
throw:recursive_type -> {error, recursive_type};
throw:{unsupported, Name} -> {error, unsupported_type, Name}
end.
parse_spec_clauses(_FA, [], _Types, Acc) ->
lists:reverse(Acc);
parse_spec_clauses(FA, [Clause|Clauses], Types, Acc) ->
case has_deps(Clause) of
true -> {error, has_remote_types};
false ->
Visited = ordsets:add_element(FA, ordsets:new()),
Simplified = simplify(Clause, Types, dict:new(), Visited),
parse_spec_clauses(FA, Clauses, Types, [Simplified|Acc])
end.
add_constraints_to_env([], Env) ->
Env;
add_constraints_to_env([{Var, Type}|Cs], Env) ->
F = fun(StoredTypes, E, Visited) -> simplify(Type, StoredTypes, E, Visited) end,
Env1 = dict:store(Var#t.rep, F, Env),
add_constraints_to_env(Cs, Env1).
bind_parameters([], [], Env) ->
Env;
bind_parameters([P|Ps], [A|As], Env) ->
F = fun(StoredTypes, E, Visited) -> simplify(A, StoredTypes, E, Visited) end,
Env1 = dict:store(P, F, Env),
bind_parameters(Ps, As, Env1).
-spec simplify(raw_type(), stored_types(), type_var_env(), ordsets:ordset(stored_spec_key())) -> raw_type().
%% fun
simplify(#t{kind = ?function_tag, rep = {Params, Ret, Constraints}}=Raw, StoredTypes, Env, Visited) ->
Env1 = add_constraints_to_env(Constraints, Env),
ParamsSimplified = [simplify(P, StoredTypes, Env1, Visited) || P <- Params],
RetSimplified = simplify(Ret, StoredTypes, Env1, Visited),
Rep = {ParamsSimplified, RetSimplified, []},
Raw#t{rep = Rep};
%% tuple
simplify(#t{kind = ?tuple_tag, rep = Types}=Raw, StoredTypes, Env, Visited) ->
Rep = [simplify(T, StoredTypes, Env, Visited) || T <- Types],
Raw#t{rep = Rep};
%% list / nonempty_list
simplify(#t{kind = Tag, rep = Type}=Raw, StoredTypes, Env, Visited) when Tag =:= ?list_tag; Tag =:= ?nonempty_list_tag ->
Rep = simplify(Type, StoredTypes, Env, Visited),
Raw#t{rep = Rep};
%% union
simplify(#t{kind = ?union_tag, rep = Types}=Raw, StoredTypes, Env, Visited) ->
Rep = [simplify(T, StoredTypes, Env, Visited) || T <- Types],
Raw#t{rep = Rep};
%% local type
simplify(#t{kind = ?local_tag, rep = {Name, Args}}, StoredTypes, Env, Visited) ->
Arity = length(Args),
TA = {Name, Arity},
case ordsets:is_element(TA, Visited) of
true -> throw(recursive_type);
false ->
case dict:find({type, Name, Arity}, StoredTypes) of
error -> throw({unsupported, Name});
{ok, {Type, Params}} ->
Env1 = bind_parameters(Params, Args, Env),
simplify(Type, StoredTypes, Env1, [TA|Visited])
end
end;
%% type variable
simplify(#t{kind = ?type_variable, rep = TVar}=T, StoredTypes, Env, Visited) ->
case is_tvar_wild_card(T) of
true -> t_any();
false ->
V = dict:fetch(TVar, Env),
V(StoredTypes, Env, Visited)
end;
simplify(#t{kind = ?remote_tag}, _StoredTypes, _Env, _Visited) ->
throw(remote_type);
%% record
simplify(#t{kind = ?record_tag, rep = {Name, OverridenFields}}, StoredTypes, Env, Visited) ->
RecordDecl = dict:fetch({record, Name}, StoredTypes),
Fields = fields_of_t_record(RecordDecl),
ActualFields = replace_record_fields(Fields, OverridenFields),
FinalFields = [{N, simplify(T, StoredTypes, Env, Visited)} || {N, T} <- ActualFields],
Simplified = [T || {_, T} <- FinalFields],
t_tuple([t_atom_lit(Name)|Simplified]);
%% all others
simplify(Raw, _StoredTypes, _Env, _Visited) ->
Raw.
-spec replace_record_fields([record_field_type()], [record_field_type()]) -> [record_field_type()].
replace_record_fields(Fields, []) ->
Fields;
replace_record_fields(Fields, [{Name, Type}|Rest]) ->
Replaced = lists:keyreplace(Name, 1, Fields, {Name, Type}),
replace_record_fields(Replaced, Rest).