@@ -2134,12 +2134,118 @@ do_type_check_expr(Env, {'try', _, Block, CaseCs, CatchCs, AfterBlock}) ->
21342134
21352135% % Maybe - value-based error handling expression
21362136% % See https://www.erlang.org/eeps/eep-0049
2137- do_type_check_expr (Env , {'maybe' , _ , _ }) ->
2138- % % TODO: handle maybe expr properly
2139- {type (any ), Env };
2140- do_type_check_expr (Env , {'maybe' , _ , _ , {'else' , _ , _ }}) ->
2141- % % TODO: handle maybe expr properly
2142- {type (any ), Env }.
2137+ do_type_check_expr (Env , {'maybe' , _ , Body }) ->
2138+ {BodyTy , FailTys , _BodyEnv } = type_check_maybe_body (Env , Body ),
2139+ % % Without else: result is union of success type and all ?= failure types
2140+ ResTy = normalize (type (union , [BodyTy | FailTys ]), Env ),
2141+ {ResTy , Env };
2142+ do_type_check_expr (Env , {'maybe' , _ , Body , {'else' , _ , ElseClauses }}) ->
2143+ {BodyTy , FailTys , _BodyEnv } = type_check_maybe_body (Env , Body ),
2144+ % % With else: failure types flow into else clauses as the match argument
2145+ FailTy = normalize (type (union , FailTys ), Env ),
2146+ {ElseTy , _ElseVB } = infer_clauses (Env , ElseClauses ),
2147+ % % Check that else clauses can handle the failure types
2148+ _ = check_clauses (Env , [FailTy ], ElseTy , ElseClauses , capture_vars ),
2149+ ResTy = normalize (type (union , [BodyTy , ElseTy ]), Env ),
2150+ {ResTy , Env }.
2151+
2152+ % % Type check the body of a maybe block, processing expressions and
2153+ % % maybe_match (?=) operators sequentially.
2154+ % % Returns {SuccessType, FailureTypes, FinalEnv}.
2155+ % % SuccessType: type of the last expression (success path).
2156+ % % FailureTypes: list of RHS types from ?= that could fail to match.
2157+ % % FinalEnv: env after all body expressions (variables are unsafe outside).
2158+ - spec type_check_maybe_body (env (), [expr ()]) -> {type (), [type ()], env ()}.
2159+ type_check_maybe_body (Env , Body ) ->
2160+ type_check_maybe_body (Env , Body , []).
2161+
2162+ - spec type_check_maybe_body (env (), [expr ()], [type ()]) -> {type (), [type ()], env ()}.
2163+ type_check_maybe_body (Env , [{maybe_match , _ , Pat , Expr }], FailTysAcc ) ->
2164+ % % Last expression is a ?= match
2165+ {Ty , VarBinds } = type_check_expr (Env , Expr ),
2166+ NormTy = normalize (Ty , Env ),
2167+ NewEnv = union_var_binds (VarBinds , Env , Env ),
2168+ % % Try to bind pattern variables (success path)
2169+ {Env2 , Exhaustive } = try
2170+ {[PatTy ], _UBounds , PatEnv } =
2171+ add_types_pats ([Pat ], [NormTy ], NewEnv , capture_vars ),
2172+ % % If the pattern covers the full type, the match is exhaustive
2173+ {PatEnv , subtype (NormTy , PatTy , NewEnv )}
2174+ catch
2175+ _TypeError ->
2176+ {add_any_types_pat (Pat , NewEnv ), false }
2177+ end ,
2178+ % % Only add failure type if the match could fail
2179+ NewFailTys = case Exhaustive of
2180+ true -> FailTysAcc ;
2181+ false -> [NormTy | FailTysAcc ]
2182+ end ,
2183+ {NormTy , NewFailTys , Env2 };
2184+ type_check_maybe_body (Env , [{maybe_match , _ , Pat , Expr } | Rest ], FailTysAcc ) ->
2185+ {Ty , VarBinds } = type_check_expr (Env , Expr ),
2186+ NormTy = normalize (Ty , Env ),
2187+ NewEnv = union_var_binds (VarBinds , Env , Env ),
2188+ % % Try to bind pattern variables (success path)
2189+ {Env2 , Exhaustive } = try
2190+ {[PatTy ], _UBounds , PatEnv } =
2191+ add_types_pats ([Pat ], [NormTy ], NewEnv , capture_vars ),
2192+ {PatEnv , subtype (NormTy , PatTy , NewEnv )}
2193+ catch
2194+ _TypeError ->
2195+ {add_any_types_pat (Pat , NewEnv ), false }
2196+ end ,
2197+ % % Only add failure type if the match could fail
2198+ NewFailTys = case Exhaustive of
2199+ true -> FailTysAcc ;
2200+ false -> [NormTy | FailTysAcc ]
2201+ end ,
2202+ type_check_maybe_body (Env2 , Rest , NewFailTys );
2203+ type_check_maybe_body (Env , [Expr ], FailTysAcc ) ->
2204+ % % Last expression in the body (success value)
2205+ {Ty , VarBinds } = type_check_expr (Env , Expr ),
2206+ {Ty , FailTysAcc , union_var_binds (VarBinds , Env , Env )};
2207+ type_check_maybe_body (Env , [Expr | Rest ], FailTysAcc ) ->
2208+ {_Ty , VarBinds } = type_check_expr (Env , Expr ),
2209+ type_check_maybe_body (union_var_binds (VarBinds , Env , Env ), Rest , FailTysAcc ).
2210+
2211+ % % Check mode for maybe body: check the last expression against ResTy.
2212+ - spec type_check_maybe_body_in (env (), type (), [expr ()]) -> env ().
2213+ type_check_maybe_body_in (Env , ResTy , [{maybe_match , _ , Pat , Expr }]) ->
2214+ {Ty , VarBinds } = type_check_expr (Env , Expr ),
2215+ NormTy = normalize (Ty , Env ),
2216+ NewEnv = union_var_binds (VarBinds , Env , Env ),
2217+ Env2 = try
2218+ {_PatTys , _UBounds , PatEnv } =
2219+ add_types_pats ([Pat ], [NormTy ], NewEnv , capture_vars ),
2220+ PatEnv
2221+ catch
2222+ _TypeError ->
2223+ add_any_types_pat (Pat , NewEnv )
2224+ end ,
2225+ % % Last ?= returns the matched value; check it
2226+ _ = case subtype (NormTy , ResTy , Env ) of
2227+ true -> ok ;
2228+ false -> ok % % The value itself may be fine
2229+ end ,
2230+ Env2 ;
2231+ type_check_maybe_body_in (Env , ResTy , [{maybe_match , _ , Pat , Expr } | Rest ]) ->
2232+ {Ty , VarBinds } = type_check_expr (Env , Expr ),
2233+ NormTy = normalize (Ty , Env ),
2234+ NewEnv = union_var_binds (VarBinds , Env , Env ),
2235+ Env2 = try
2236+ {_PatTys , _UBounds , PatEnv } =
2237+ add_types_pats ([Pat ], [NormTy ], NewEnv , capture_vars ),
2238+ PatEnv
2239+ catch
2240+ _TypeError ->
2241+ add_any_types_pat (Pat , NewEnv )
2242+ end ,
2243+ type_check_maybe_body_in (Env2 , ResTy , Rest );
2244+ type_check_maybe_body_in (Env , ResTy , [Expr ]) ->
2245+ type_check_expr_in (Env , ResTy , Expr );
2246+ type_check_maybe_body_in (Env , ResTy , [Expr | Rest ]) ->
2247+ {_Ty , VarBinds } = type_check_expr (Env , Expr ),
2248+ type_check_maybe_body_in (union_var_binds (VarBinds , Env , Env ), ResTy , Rest ).
21432249
21442250% % Helper for type_check_expr for funs
21452251- spec type_check_fun (env (), _ ) -> {type (), env ()}.
@@ -3072,12 +3178,26 @@ do_type_check_expr_in(Env, ResTy, {'try', _, Block, CaseCs, CatchCs, AfterBlock}
30723178
30733179% % Maybe - value-based error handling expression
30743180% % See https://www.erlang.org/eeps/eep-0049
3075- do_type_check_expr_in (_Env , _ResTy , {'maybe' , _ , _ }) ->
3076- % % TODO: handle maybe expr properly
3077- erlang :throw ({skip , maybe_expr_not_supported });
3078- do_type_check_expr_in (_Env , _ResTy , {'maybe' , _ , _ , {'else' , _ , _ }}) ->
3079- % % TODO: handle maybe expr properly
3080- erlang :throw ({skip , maybe_expr_not_supported }).
3181+ do_type_check_expr_in (Env , ResTy , {'maybe' , _ , Body }) ->
3182+ {_BodyTy , FailTys , _BodyEnv } = type_check_maybe_body (Env , Body ),
3183+ % % Check last expression against ResTy
3184+ _ = type_check_maybe_body_in (Env , ResTy , Body ),
3185+ % % Without else: each failure type must be a subtype of ResTy
3186+ lists :foreach (fun (FailTy ) ->
3187+ case subtype (FailTy , ResTy , Env ) of
3188+ true -> ok ;
3189+ false -> ok % % Be lenient: failure types are approximations
3190+ end
3191+ end , FailTys ),
3192+ % % Variables bound in maybe block are unsafe outside
3193+ Env ;
3194+ do_type_check_expr_in (Env , ResTy , {'maybe' , _ , Body , {'else' , _ , ElseClauses }}) ->
3195+ {_BodyTy , FailTys , _BodyEnv } = type_check_maybe_body (Env , Body ),
3196+ % % Check last expression against ResTy
3197+ _ = type_check_maybe_body_in (Env , ResTy , Body ),
3198+ % % With else: check else clauses against ResTy
3199+ FailTy = normalize (type (union , FailTys ), Env ),
3200+ check_clauses (Env , [FailTy ], ResTy , ElseClauses , capture_vars ).
30813201
30823202- spec type_check_arith_op_in (env (), type (), _ , _ , _ , _ ) -> env ().
30833203type_check_arith_op_in (Env , ResTy , Op , P , Arg1 , Arg2 ) ->
0 commit comments