@@ -7,7 +7,7 @@ expand(Meta, Args, E, RequireSize) ->
77 case ? key (E , context ) of
88 match ->
99 {EArgs , Alignment , EE } =
10- expand (Meta , fun elixir_expand :expand /2 , Args , [], E , 0 , RequireSize ),
10+ expand (Meta , fun elixir_expand :expand /2 , Args , [], E , E , 0 , RequireSize ),
1111
1212 case find_match (EArgs ) of
1313 false ->
@@ -17,13 +17,13 @@ expand(Meta, Args, E, RequireSize) ->
1717 end ;
1818 _ ->
1919 {EArgs , Alignment , {_EC , EV }} =
20- expand (Meta , fun elixir_expand :expand_arg /2 , Args , [], {E , E }, 0 , RequireSize ),
20+ expand (Meta , fun elixir_expand :expand_arg /2 , Args , [], {E , E }, E , 0 , RequireSize ),
2121 {{'<<>>' , [{alignment , Alignment } | Meta ], EArgs }, EV }
2222 end .
2323
24- expand (_BitstrMeta , _Fun , [], Acc , E , Alignment , _RequireSize ) ->
24+ expand (_BitstrMeta , _Fun , [], Acc , E , _OriginalE , Alignment , _RequireSize ) ->
2525 {lists :reverse (Acc ), Alignment , E };
26- expand (BitstrMeta , Fun , [{'::' , Meta , [Left , Right ]} | T ], Acc , E , Alignment , RequireSize ) ->
26+ expand (BitstrMeta , Fun , [{'::' , Meta , [Left , Right ]} | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
2727 {ELeft , EL } = expand_expr (Meta , Left , Fun , E ),
2828
2929 % % Variables defined outside the binary can be accounted
@@ -38,7 +38,7 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, Alignment, Re
3838 end ,
3939
4040 EType = expr_type (ELeft ),
41- {ERight , EAlignment , ES } = expand_specs (EType , Meta , Right , EM , RequireSize or MatchSize ),
41+ {ERight , EAlignment , ES } = expand_specs (EType , Meta , Right , EM , OriginalE , RequireSize or MatchSize ),
4242
4343 EE =
4444 case EL of
@@ -65,15 +65,15 @@ expand(BitstrMeta, Fun, [{'::', Meta, [Left, Right]} | T], Acc, E, Alignment, Re
6565 prepend_unless_bitstring_in_match (EType , Meta , ELeft , ERight , Acc , E )
6666 end ,
6767
68- expand (BitstrMeta , Fun , T , EAcc , EE , alignment (Alignment , EAlignment ), RequireSize );
69- expand (BitstrMeta , Fun , [{_ , Meta , _ } = H | T ], Acc , E , Alignment , RequireSize ) ->
68+ expand (BitstrMeta , Fun , T , EAcc , EE , OriginalE , alignment (Alignment , EAlignment ), RequireSize );
69+ expand (BitstrMeta , Fun , [{_ , Meta , _ } = H | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
7070 {Expr , ES } = expand_expr (Meta , H , Fun , E ),
7171 {EAcc , EAlignment } = wrap_expr (Expr , Acc ),
72- expand (BitstrMeta , Fun , T , EAcc , ES , alignment (Alignment , EAlignment ), RequireSize );
73- expand (Meta , Fun , [H | T ], Acc , E , Alignment , RequireSize ) ->
72+ expand (BitstrMeta , Fun , T , EAcc , ES , OriginalE , alignment (Alignment , EAlignment ), RequireSize );
73+ expand (Meta , Fun , [H | T ], Acc , E , OriginalE , Alignment , RequireSize ) ->
7474 {Expr , ES } = expand_expr (Meta , H , Fun , E ),
7575 {EAcc , EAlignment } = wrap_expr (Expr , Acc ),
76- expand (Meta , Fun , T , EAcc , ES , alignment (Alignment , EAlignment ), RequireSize ).
76+ expand (Meta , Fun , T , EAcc , ES , OriginalE , alignment (Alignment , EAlignment ), RequireSize ).
7777
7878prepend_unless_bitstring_in_match (Type , Meta , Left , Right , Acc , E ) ->
7979 Expr = {'::' , Meta , [Left , Right ]},
@@ -157,15 +157,15 @@ env_for_error(E) -> E.
157157
158158% % Expands and normalizes types of a bitstring.
159159
160- expand_specs (ExprType , Meta , Info , E , RequireSize ) ->
160+ expand_specs (ExprType , Meta , Info , E , OriginalE , RequireSize ) ->
161161 Default =
162162 #{size => default ,
163163 unit => default ,
164164 sign => default ,
165165 type => default ,
166166 endianness => default },
167167 {#{size := Size , unit := Unit , type := Type , endianness := Endianness , sign := Sign }, ES } =
168- expand_each_spec (Meta , unpack_specs (Info , []), Default , E ),
168+ expand_each_spec (Meta , unpack_specs (Info , []), Default , E , OriginalE ),
169169 MergedType = type (Meta , ExprType , Type , E ),
170170 validate_size_required (Meta , RequireSize , ExprType , MergedType , Size , ES ),
171171 SizeAndUnit = size_and_unit (Meta , ExprType , Size , Unit , ES ),
@@ -190,30 +190,30 @@ type(_, default, Type, _) ->
190190type (Meta , Other , Value , E ) ->
191191 form_error (Meta , ? key (E , file ), ? MODULE , {bittype_mismatch , Value , Other , type }).
192192
193- expand_each_spec (Meta , [{Expr , _ , Args } = H | T ], Map , E ) when is_atom (Expr ) ->
193+ expand_each_spec (Meta , [{Expr , _ , Args } = H | T ], Map , E , OriginalE ) when is_atom (Expr ) ->
194194 case validate_spec (Expr , Args ) of
195195 {Key , Arg } ->
196196 {Value , EE } = expand_spec_arg (Arg , E ),
197- validate_spec_arg (Meta , Key , Value , EE ),
197+ validate_spec_arg (Meta , Key , Value , EE , OriginalE ),
198198
199199 case maps :get (Key , Map ) of
200200 default -> ok ;
201201 Value -> ok ;
202202 Other -> form_error (Meta , ? key (E , file ), ? MODULE , {bittype_mismatch , Value , Other , Key })
203203 end ,
204204
205- expand_each_spec (Meta , T , maps :put (Key , Value , Map ), EE );
205+ expand_each_spec (Meta , T , maps :put (Key , Value , Map ), EE , OriginalE );
206206 none ->
207207 case 'Elixir.Macro' :expand (H , elixir_env :linify ({? line (Meta ), E })) of
208208 H ->
209209 form_error (Meta , ? key (E , file ), ? MODULE , {undefined_bittype , H });
210210 NewTypes ->
211- expand_each_spec (Meta , unpack_specs (NewTypes , []) ++ T , Map , E )
211+ expand_each_spec (Meta , unpack_specs (NewTypes , []) ++ T , Map , E , OriginalE )
212212 end
213213 end ;
214- expand_each_spec (Meta , [Expr | _ ], _Map , E ) ->
214+ expand_each_spec (Meta , [Expr | _ ], _Map , E , _OriginalE ) ->
215215 form_error (Meta , ? key (E , file ), ? MODULE , {undefined_bittype , Expr });
216- expand_each_spec (_Meta , [], Map , E ) ->
216+ expand_each_spec (_Meta , [], Map , E , _OriginalE ) ->
217217 {Map , E }.
218218
219219unpack_specs ({'-' , _ , [H , T ]}, Acc ) ->
@@ -251,17 +251,33 @@ validate_spec(_, _) -> none.
251251expand_spec_arg (Expr , E ) when is_atom (Expr ); is_integer (Expr ) -> {Expr , E };
252252expand_spec_arg (Expr , E ) -> elixir_expand :expand (Expr , E ).
253253
254- validate_spec_arg (Meta , size , Value , E ) ->
254+ validate_spec_arg (Meta , size , Value , E , OriginalE ) ->
255255 case Value of
256- {Var , _ , Context } when is_atom (Var ) and is_atom (Context ) -> ok ;
257- _ when is_integer (Value ) -> ok ;
258- _ -> form_error (Meta , ? key (E , file ), ? MODULE , {bad_size_argument , Value })
256+ {Var , _ , Context } when is_atom (Var ) and is_atom (Context ) ->
257+ case is_valid_spec_arg_var ({Var , Context }, E , OriginalE ) of
258+ true -> ok ;
259+ false -> form_error (Meta , ? key (E , file ), ? MODULE , {undefined_var_in_spec , Value })
260+ end ;
261+ _ when is_integer (Value ) ->
262+ ok ;
263+ _ ->
264+ form_error (Meta , ? key (E , file ), ? MODULE , {bad_size_argument , Value })
259265 end ;
260- validate_spec_arg (Meta , unit , Value , E ) when not is_integer (Value ) ->
266+ validate_spec_arg (Meta , unit , Value , E , _OriginalE ) when not is_integer (Value ) ->
261267 form_error (Meta , ? key (E , file ), ? MODULE , {bad_unit_argument , Value });
262- validate_spec_arg (_Meta , _Key , _Value , _E ) ->
268+ validate_spec_arg (_Meta , _Key , _Value , _E , _OriginalE ) ->
263269 ok .
264270
271+ is_valid_spec_arg_var (Var , E , #{context := match } = OriginalE ) ->
272+ case ? key (OriginalE , prematch_vars ) of
273+ #{Var := _ } ->
274+ true ;
275+ _ ->
276+ maps :is_key (Var , ? key (E , current_vars )) andalso
277+ not maps :is_key (Var , ? key (OriginalE , current_vars ))
278+ end ;
279+ is_valid_spec_arg_var (_Var , _E , _OriginalE ) -> true .
280+
265281validate_size_required (Meta , true , default , Type , default , E ) when Type == binary ; Type == bitstring ->
266282 form_error (Meta , ? key (E , file ), ? MODULE , unsized_binary );
267283validate_size_required (_ , _ , _ , _ , _ , _ ) ->
@@ -375,4 +391,10 @@ format_error({nested_match, Expr}) ->
375391 Message =
376392 " cannot pattern match inside a bitstring "
377393 " that is already in match, got: ~ts " ,
378- io_lib :format (Message , ['Elixir.Macro' :to_string (Expr )]).
394+ io_lib :format (Message , ['Elixir.Macro' :to_string (Expr )]);
395+ format_error ({undefined_var_in_spec , Var }) ->
396+ Message =
397+ " undefined variable \" ~ts \" in bitstring segment. If the size of the binary is a "
398+ " variable, the variable must be defined prior to its use in the binary/bitstring match "
399+ " itself, or outside the pattern match" ,
400+ io_lib :format (Message , ['Elixir.Macro' :to_string (Var )]).
0 commit comments