Clean up constraint solving a bit
This commit is contained in:
parent
86d7b36ba7
commit
6207cd09e2
@ -2609,22 +2609,19 @@ solve_constraints(Env) ->
|
|||||||
end;
|
end;
|
||||||
(_) -> true
|
(_) -> true
|
||||||
end,
|
end,
|
||||||
AmbiguousConstraints = lists:filter(IsAmbiguous, get_constraints()),
|
|
||||||
|
|
||||||
% The two passes on AmbiguousConstraints are needed
|
solve_ambiguous_constraints(Env, lists:filter(IsAmbiguous, get_constraints())).
|
||||||
solve_ambiguous_constraints(Env, AmbiguousConstraints ++ AmbiguousConstraints).
|
|
||||||
|
|
||||||
-spec solve_ambiguous_constraints(env(), [constraint()]) -> ok.
|
-spec solve_ambiguous_constraints(env(), [constraint()]) -> ok.
|
||||||
solve_ambiguous_constraints(Env, Constraints) ->
|
solve_ambiguous_constraints(Env, Constraints) ->
|
||||||
Unknown = solve_known_record_types(Env, Constraints),
|
Unsolved = solve_constraints(Env, Constraints),
|
||||||
if Unknown == [] -> ok;
|
if length(Unsolved) < length(Constraints) ->
|
||||||
length(Unknown) < length(Constraints) ->
|
|
||||||
%% progress! Keep trying.
|
%% progress! Keep trying.
|
||||||
solve_ambiguous_constraints(Env, Unknown);
|
solve_ambiguous_constraints(Env, Unsolved);
|
||||||
true ->
|
true ->
|
||||||
case solve_unknown_record_types(Env, Unknown) of
|
case solve_unknown_record_types(Env, Unsolved) of
|
||||||
true -> %% Progress!
|
true -> %% Progress!
|
||||||
solve_ambiguous_constraints(Env, Unknown);
|
solve_ambiguous_constraints(Env, Unsolved);
|
||||||
_ -> ok %% No progress. Report errors later.
|
_ -> ok %% No progress. Report errors later.
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
@ -2661,17 +2658,13 @@ destroy_and_report_unsolved_constraints(Env) ->
|
|||||||
(_) -> false
|
(_) -> false
|
||||||
end, OtherCs5),
|
end, OtherCs5),
|
||||||
|
|
||||||
Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ],
|
UnsolvedNamedArgCs = solve_constraints(Env, NamedArgCs),
|
||||||
S == unsolved ],
|
[ type_error({unsolved_named_argument_constraint, C}) || C <- UnsolvedNamedArgCs ],
|
||||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
|
||||||
|
|
||||||
Unknown = solve_known_record_types(Env, FieldCs),
|
UnsolvedFieldCs = solve_constraints(Env, FieldCs),
|
||||||
if Unknown == [] -> ok;
|
case solve_unknown_record_types(Env, UnsolvedFieldCs) of
|
||||||
true ->
|
true -> ok;
|
||||||
case solve_unknown_record_types(Env, Unknown) of
|
Errors -> [ type_error(Err) || Err <- Errors ]
|
||||||
true -> ok;
|
|
||||||
Errors -> [ type_error(Err) || Err <- Errors ]
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
check_record_create_constraints(Env, CreateCs),
|
check_record_create_constraints(Env, CreateCs),
|
||||||
@ -2693,20 +2686,21 @@ get_oracle_type(_Fun, _Args, _Ret) -> false.
|
|||||||
|
|
||||||
%% -- Named argument constraints --
|
%% -- Named argument constraints --
|
||||||
|
|
||||||
%% If false, a type error has been emitted, so it's safe to drop the constraint.
|
%% True if solved (unified or type error), false otherwise
|
||||||
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false | unsolved.
|
-spec check_named_argument_constraint(env(), named_argument_constraint()) -> true | false.
|
||||||
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
|
check_named_argument_constraint(_Env, #named_argument_constraint{ args = {uvar, _, _} }) ->
|
||||||
unsolved;
|
false;
|
||||||
check_named_argument_constraint(Env,
|
check_named_argument_constraint(Env,
|
||||||
C = #named_argument_constraint{ args = Args,
|
C = #named_argument_constraint{ args = Args,
|
||||||
name = Id = {id, _, Name},
|
name = Id = {id, _, Name},
|
||||||
type = Type }) ->
|
type = Type }) ->
|
||||||
case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of
|
case [ T || {named_arg_t, _, {id, _, Name1}, T, _} <- Args, Name1 == Name ] of
|
||||||
[] ->
|
[] ->
|
||||||
type_error({bad_named_argument, Args, Id}),
|
type_error({bad_named_argument, Args, Id});
|
||||||
false;
|
[T] ->
|
||||||
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
unify(Env, T, Type, {check_named_arg_constraint, C})
|
||||||
end;
|
end,
|
||||||
|
true;
|
||||||
check_named_argument_constraint(Env,
|
check_named_argument_constraint(Env,
|
||||||
#dependent_type_constraint{ named_args_t = NamedArgsT0,
|
#dependent_type_constraint{ named_args_t = NamedArgsT0,
|
||||||
named_args = NamedArgs,
|
named_args = NamedArgs,
|
||||||
@ -2723,10 +2717,11 @@ check_named_argument_constraint(Env,
|
|||||||
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|
||||||
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
|
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
|
||||||
GenType1 = specialize_dependent_type(ArgEnv, GenType),
|
GenType1 = specialize_dependent_type(ArgEnv, GenType),
|
||||||
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
|
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType});
|
||||||
true;
|
_ ->
|
||||||
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
|
unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType})
|
||||||
end.
|
end,
|
||||||
|
true.
|
||||||
|
|
||||||
specialize_dependent_type(Env, Type) ->
|
specialize_dependent_type(Env, Type) ->
|
||||||
case dereference(Type) of
|
case dereference(Type) of
|
||||||
@ -2744,12 +2739,13 @@ specialize_dependent_type(Env, Type) ->
|
|||||||
|
|
||||||
%% -- Bytes constraints --
|
%% -- Bytes constraints --
|
||||||
|
|
||||||
|
%% Returns true if solved (unified or type error)
|
||||||
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
|
solve_constraint(_Env, #field_constraint{record_t = {uvar, _, _}}) ->
|
||||||
not_solved;
|
false;
|
||||||
solve_constraint(Env, C = #field_constraint{record_t = RecType,
|
solve_constraint(Env, #field_constraint{record_t = RecType,
|
||||||
field = FieldName,
|
field = FieldName,
|
||||||
field_t = FieldType,
|
field_t = FieldType,
|
||||||
context = When}) ->
|
context = When}) ->
|
||||||
RecId = record_type_name(RecType),
|
RecId = record_type_name(RecType),
|
||||||
Attrs = aeso_syntax:get_ann(RecId),
|
Attrs = aeso_syntax:get_ann(RecId),
|
||||||
case lookup_type(Env, RecId) of
|
case lookup_type(Env, RecId) of
|
||||||
@ -2758,26 +2754,24 @@ solve_constraint(Env, C = #field_constraint{record_t = RecType,
|
|||||||
{id, _, FieldString} = FieldName,
|
{id, _, FieldString} = FieldName,
|
||||||
case proplists:get_value(FieldString, FieldTypes) of
|
case proplists:get_value(FieldString, FieldTypes) of
|
||||||
undefined ->
|
undefined ->
|
||||||
type_error({missing_field, FieldName, RecId}),
|
type_error({missing_field, FieldName, RecId});
|
||||||
not_solved;
|
|
||||||
FldType ->
|
FldType ->
|
||||||
create_freshen_tvars(),
|
create_freshen_tvars(),
|
||||||
FreshFldType = freshen(FldType),
|
FreshFldType = freshen(FldType),
|
||||||
FreshRecType = freshen(app_t(Attrs, RecId, Formals)),
|
FreshRecType = freshen(app_t(Attrs, RecId, Formals)),
|
||||||
destroy_freshen_tvars(),
|
destroy_freshen_tvars(),
|
||||||
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
|
unify(Env, FreshFldType, FieldType, {field_constraint, FreshFldType, FieldType, When}),
|
||||||
unify(Env, FreshRecType, RecType, {record_constraint, FreshRecType, RecType, When}),
|
unify(Env, FreshRecType, RecType, {record_constraint, FreshRecType, RecType, When})
|
||||||
C
|
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
type_error({not_a_record_type, instantiate(RecType), When}),
|
type_error({not_a_record_type, instantiate(RecType), When})
|
||||||
not_solved
|
end,
|
||||||
end;
|
true;
|
||||||
solve_constraint(Env, C = #dependent_type_constraint{}) ->
|
solve_constraint(Env, C = #dependent_type_constraint{}) ->
|
||||||
check_named_argument_constraint(Env, C);
|
check_named_argument_constraint(Env, C);
|
||||||
solve_constraint(Env, C = #named_argument_constraint{}) ->
|
solve_constraint(Env, C = #named_argument_constraint{}) ->
|
||||||
check_named_argument_constraint(Env, C);
|
check_named_argument_constraint(Env, C);
|
||||||
solve_constraint(_Env, {is_bytes, _}) -> ok;
|
solve_constraint(_Env, {is_bytes, _}) -> false;
|
||||||
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
|
solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
|
||||||
A = unfold_types_in_type(Env, dereference(A0)),
|
A = unfold_types_in_type(Env, dereference(A0)),
|
||||||
B = unfold_types_in_type(Env, dereference(B0)),
|
B = unfold_types_in_type(Env, dereference(B0)),
|
||||||
@ -2786,9 +2780,9 @@ solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
|
|||||||
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
|
{{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
|
||||||
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
|
{{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
|
||||||
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
|
{_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
|
||||||
_ -> ok
|
_ -> false
|
||||||
end;
|
end;
|
||||||
solve_constraint(_, _) -> ok.
|
solve_constraint(_, _) -> false.
|
||||||
|
|
||||||
check_bytes_constraints(Env, Constraints) ->
|
check_bytes_constraints(Env, Constraints) ->
|
||||||
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
|
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
|
||||||
@ -2897,17 +2891,9 @@ solve_unknown_record_types(Env, Unknown) ->
|
|||||||
false -> Solutions
|
false -> Solutions
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% This will solve all kinds of constraints but will only return the
|
-spec solve_constraints(env(), [constraint()]) -> [constraint()].
|
||||||
%% unsolved field constraints
|
solve_constraints(Env, Constraints) ->
|
||||||
-spec solve_known_record_types(env(), [constraint()]) -> [field_constraint()].
|
[ C1 || C <- Constraints, C1 <- [dereference_deep(C)], not solve_constraint(Env, C1) ].
|
||||||
solve_known_record_types(Env, Constraints) ->
|
|
||||||
DerefConstraints = lists:map(fun(C = #field_constraint{record_t = RecordType}) ->
|
|
||||||
C#field_constraint{record_t = dereference(RecordType)};
|
|
||||||
(C) -> dereference_deep(C)
|
|
||||||
end, Constraints),
|
|
||||||
SolvedConstraints = lists:map(fun(C) -> solve_constraint(Env, dereference_deep(C)) end, DerefConstraints),
|
|
||||||
Unsolved = DerefConstraints--SolvedConstraints,
|
|
||||||
lists:filter(fun(#field_constraint{}) -> true; (_) -> false end, Unsolved).
|
|
||||||
|
|
||||||
record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) ->
|
record_type_name({app_t, _Attrs, RecId, _Args}) when ?is_type_id(RecId) ->
|
||||||
RecId;
|
RecId;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user