@@ -3232,18 +3232,57 @@ type_check_arith_op_in(Env, Kind, ResTy, Op, P, Arg1, Arg2) ->
32323232 try
32333233 arith_op_do_check (ArgTy21 , ArgTy22 , Arg1 , Arg2 , Env )
32343234 catch
3235- throw :_ -> throw (E )
3235+ throw :_ ->
3236+ arith_op_infer_fallback (Env , Op , P , Arg1 , Arg2 , ResTy1 , E )
32363237 end
32373238 end ;
32383239 {ArgTy1 , ArgTy2 } ->
3239- VarBinds1 = type_check_expr_in (Env , ArgTy1 , Arg1 ),
3240- VarBinds2 = type_check_expr_in (Env , ArgTy2 , Arg2 ),
3241- union_var_binds (VarBinds1 , VarBinds2 , Env );
3240+ try
3241+ VarBinds1 = type_check_expr_in (Env , ArgTy1 , Arg1 ),
3242+ VarBinds2 = type_check_expr_in (Env , ArgTy2 , Arg2 ),
3243+ union_var_binds (VarBinds1 , VarBinds2 , Env )
3244+ catch
3245+ throw :E when element (1 , E ) == type_error ->
3246+ arith_op_infer_fallback (Env , Op , P , Arg1 , Arg2 , ResTy1 , E )
3247+ end ;
32423248 false ->
3243- throw (type_error (op_type_too_precise , Op , P , ResTy1 ))
3249+ arith_op_infer_fallback (Env , Op , P , Arg1 , Arg2 , ResTy1 ,
3250+ type_error (op_type_too_precise , Op , P , ResTy1 ))
32443251 end
32453252 end .
32463253
3254+ % % Fall back to inference mode for arithmetic operations. Infers the result
3255+ % % type and checks if it's compatible with the expected type. This handles
3256+ % % cases where arith_op_arg_types is too conservative (e.g. 0 - 0 :: non_neg_integer()).
3257+ - spec arith_op_infer_fallback (env (), atom (), _ , expr (), expr (), type (), _ ) -> env ().
3258+ arith_op_infer_fallback (Env , Op , P , Arg1 , Arg2 , ResTy1 , OrigError ) ->
3259+ try
3260+ {InferredTy , VB } = type_check_arith_op (Env , Op , P , Arg1 , Arg2 ),
3261+ % % For constant expressions, try to evaluate to get a precise result type
3262+ InferredTy2 = try_const_eval_arith (Op , Arg1 , Arg2 , InferredTy ),
3263+ case subtype (InferredTy2 , ResTy1 , Env ) of
3264+ true -> VB ;
3265+ false -> throw (OrigError )
3266+ end
3267+ catch
3268+ throw :E when element (1 , E ) == type_error ->
3269+ throw (OrigError )
3270+ end .
3271+
3272+ % % Try to evaluate a constant arithmetic expression for a more precise type.
3273+ % % If both arguments are integer literals, compute the result and return a
3274+ % % singleton integer type. Otherwise, return the inferred type as-is.
3275+ - spec try_const_eval_arith (atom (), expr (), expr (), type ()) -> type ().
3276+ try_const_eval_arith (Op , {integer , _ , V1 }, {integer , _ , V2 }, _InferredTy ) ->
3277+ try
3278+ Result = erlang :Op (V1 , V2 ),
3279+ {integer , erl_anno :new (0 ), Result }
3280+ catch
3281+ _ :_ -> _InferredTy
3282+ end ;
3283+ try_const_eval_arith (_Op , _Arg1 , _Arg2 , InferredTy ) ->
3284+ InferredTy .
3285+
32473286- spec arith_op_do_check (type (), type (), expr (), expr (), env ()) -> env ().
32483287arith_op_do_check (ArgTy1 , ArgTy2 , Arg1 , Arg2 , Env ) ->
32493288 VarBinds1 = type_check_expr_in (Env , ArgTy1 , Arg1 ),
0 commit comments