[m-rev.] diff: fix compilation of the lext library in extras

Julien Fischer jfischer at opturion.com
Mon Dec 29 22:38:13 AEDT 2014


Fix compilation of the lex library in extras.

The recent change to the compiler that enabled stricter checking of non-ground
final insts "broke" the lex library in extras in three separate places.  The
underlying problem is the same in all of them: subtype insts are not preserved
through calls to various procedures in the list module.  This diff rewrites
two of those places to avoid the need for a call to a list procedure that
will cause the subtype information to be lost.  In the third case, we add a
runtime check and an unsafe cast.

extras/lex/lex.convert_NFA_to_DFA.m:
 	Rewrite map_state_set_transitions_to_numbers/2 in order to avoid a call
 	to list.map/2.

 	Use set.map/2 in a spot where it would be appropriate.

extras/lex/lex.regexp.m:
 	Rewrite trans_closure/5 in order to avoid a call to list.map/2.  Also,
 	fold directly over a set rather than converting it into a list in a
 	couple of spots.

 	Add a runtime check to ensure that add_atom_transitions/2 only
 	returns atom_transitions and add a call to an unsafe cast
 	predicate to restore the correct subtype inst.

 	Add a comment explaining all of this.

Julien.

diff --git a/extras/lex/lex.convert_NFA_to_DFA.m b/extras/lex/lex.convert_NFA_to_DFA.m
index 9b4a9da..add1d3f 100644
--- a/extras/lex/lex.convert_NFA_to_DFA.m
+++ b/extras/lex/lex.convert_NFA_to_DFA.m
@@ -86,11 +86,7 @@ convert_NFA_to_DFA(NFA) = DFA :-
          %
      DFAStateNos      = number_state_sets(DFAStateSets),
      map.lookup(DFAStateNos, DFAStartStateSet, DFAStartState),
-    DFAStopStates0   = list.map(
-                            map.lookup(DFAStateNos),
-                            set.to_sorted_list(DFAStopStateSets)
-                       ),
-    DFAStopStates    = set.list_to_set(DFAStopStates0),
+    DFAStopStates = set.map(map.lookup(DFAStateNos), DFAStopStateSets),
      DFATransitions   = map_state_set_transitions_to_numbers(
                              DFAStateNos,
                              DFAStateSetTransitions
@@ -251,20 +247,16 @@ number_state_sets(Ss) = StateNos :-

  %-----------------------------------------------------------------------------%

-:- func map_state_set_transitions_to_numbers(state_set_no_map,
-            state_set_transitions
-        ) = transitions.
-:- mode map_state_set_transitions_to_numbers(in, in) =
-            out(atom_transitions).
-
-map_state_set_transitions_to_numbers(Map, STs) =
-    list.map(
-        ( func(trans(SX, C, SY)) = trans(X, C, Y) :-
-            X = map.lookup(Map, SX),
-            Y = map.lookup(Map, SY)
-        ),
-        STs
-    ).
+:- func map_state_set_transitions_to_numbers(state_set_no_map::in,
+    state_set_transitions::in) = (transitions::out(atom_transitions)).
+
+map_state_set_transitions_to_numbers(_Map, []) = [].
+map_state_set_transitions_to_numbers(Map, [ST | STs]) = [T | Ts] :-
+    Ts = map_state_set_transitions_to_numbers(Map, STs),
+    ST = trans(SX, C, SY),
+    X = map.lookup(Map, SX),
+    Y = map.lookup(Map, SY),
+    T = trans(X, C ,Y).

  %-----------------------------------------------------------------------------%
  :- end_module lex.convert_NFA_to_DFA.
diff --git a/extras/lex/lex.regexp.m b/extras/lex/lex.regexp.m
index 49c1ce8..61e9c24 100644
--- a/extras/lex/lex.regexp.m
+++ b/extras/lex/lex.regexp.m
@@ -114,6 +114,7 @@ remove_null_transitions(NFA0) = NFA :-
                  ^ smc_state_transitions := NullFreeTs )
                  ^ smc_stop_states       := StopStates).

+
  %-----------------------------------------------------------------------------%

  :- pred split_transitions(transitions, transitions, transitions).
@@ -134,30 +135,21 @@ split_transitions([trans(X, C, Y) | Ts], NTs, [trans(X, C, Y) | CTs]) :-
  :- pred trans_closure(transitions, null_map, null_map, null_map, null_map).
  :- mode trans_closure(in(null_transitions), in, out, in, out) is det.

-trans_closure(Ts, Ins0, Ins, Outs0, Outs) :-
-    list.foldl2(add_edge, Ts, Ins0, Ins, Outs0, Outs).
-
-%-----------------------------------------------------------------------------%
-
-:- pred add_edge(transition, null_map, null_map, null_map, null_map).
-:- mode add_edge(in(null_transition), in, out, in, out) is det.
-
-add_edge(null(X, Y), Ins0, Ins, Outs0, Outs) :-
-    XInAndX  = set.insert(null_map_lookup(X, Ins0), X),
-    YOutAndY = set.insert(null_map_lookup(Y, Outs0), Y),
-    Xs = set.to_sorted_list(XInAndX),
-    Ys = set.to_sorted_list(YOutAndY),
-    Outs = list.foldl(add_to_null_mapping(YOutAndY), Xs, Outs0),
-    Ins  = list.foldl(add_to_null_mapping(XInAndX),  Ys, Ins0).
+trans_closure([], !Ins, !Outs).
+trans_closure([T | Ts], !Ins, !Outs) :-
+    T = null(X, Y),
+    XInAndX  = set.insert(null_map_lookup(X, !.Ins), X),
+    YOutAndY = set.insert(null_map_lookup(Y, !.Outs), Y),
+    !:Outs = set.fold(add_to_null_mapping(YOutAndY), XInAndX, !.Outs),
+    !:Ins = set.fold(add_to_null_mapping(XInAndX),  YOutAndY, !.Ins),
+    trans_closure(Ts, !Ins, !Outs).

  %-----------------------------------------------------------------------------%

  :- func null_map_lookup(state_no, null_map) = set(state_no).

  null_map_lookup(X, Map) =
-    ( if map.search(Map, X, Ys) then Ys
-                                 else set.init
-    ).
+    ( if map.search(Map, X, Ys) then Ys else set.init ).

  %-----------------------------------------------------------------------------%

@@ -168,12 +160,26 @@ add_to_null_mapping(Xs, Y, Map) =

  %-----------------------------------------------------------------------------%

+    % XXX add_atom_transitions (and its callees) originally used the inst-
+    % subtyping given in the commented out mode declarations.  Limitations in
+    % the compiler meant that this code compiled when it was originally written
+    % but with more recent versions of the compiler it causes a compilation
+    % error due to the aforementioned limitations having been lifted.
+    %
+    % As a workaround we perform a runtime check in maybe_copy_transition/4
+    % below and then use an unsafe cast (defined via a foreign_proc) to restore
+    % the subtype inst.  Doing so means that other code in this library that
+    % uses the same inst-subtyping continues to work without modification.
+    %
+    % If / when the standard library has versions of list.condense, list.map etc
+    % that preserve subtype insts then the original modes can be restored (and
+    % the workarounds deleted).
+    %
  :- func add_atom_transitions(null_map, transitions) = transitions.
-:- mode add_atom_transitions(in, in(atom_transitions)) =
-            out(atom_transitions) is det.
+:- mode add_atom_transitions(in, in(atom_transitions)) = out(atom_transitions).

-add_atom_transitions(Outs, CTs) =
-    list.sort_and_remove_dups(
+add_atom_transitions(Outs, CTs) = NullFreeTs :-
+    NullFreeTs0 = list.sort_and_remove_dups(
          list.condense(
              [ CTs
              | list.map(
@@ -182,14 +188,46 @@ add_atom_transitions(Outs, CTs) =
                )
              ]
          )
-    ).
+    ),
+    unsafe_cast_to_atom_transitions(NullFreeTs0, NullFreeTs).
+
+:- pred unsafe_cast_to_atom_transitions(transitions::in,
+    transitions::out(atom_transitions)) is det.
+
+:- pragma foreign_proc("C",
+    unsafe_cast_to_atom_transitions(X::in, Y::out(atom_transitions)),
+    [promise_pure, will_not_call_mercury, thread_safe, will_not_modify_trail],
+"
+    Y = X;
+").
+
+:- pragma foreign_proc("Java",
+    unsafe_cast_to_atom_transitions(X::in, Y::out(atom_transitions)),
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    Y = X;
+").
+
+:- pragma foreign_proc("C#",
+    unsafe_cast_to_atom_transitions(X::in, Y::out(atom_transitions)),
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    Y = X;
+").
+
+:- pragma foreign_proc("Erlang",
+    unsafe_cast_to_atom_transitions(X::in, Y::out(atom_transitions)),
+    [promise_pure, will_not_call_mercury, thread_safe],
+"
+    Y = X
+").

  %-----------------------------------------------------------------------------%

  :- func add_atom_transitions_0(transitions, pair(state_no, set(state_no))) =
              transitions.
-:- mode add_atom_transitions_0(in(atom_transitions), in) =
-            out(atom_transitions) is det.
+%:- mode add_atom_transitions_0(in(atom_transitions), in) =
+%            out(atom_transitions) is det.

  add_atom_transitions_0(CTs, X - Ys) =
      list.condense(
@@ -199,8 +237,8 @@ add_atom_transitions_0(CTs, X - Ys) =
  %-----------------------------------------------------------------------------%

  :- func add_atom_transitions_1(transitions, state_no, state_no) = transitions.
-:- mode add_atom_transitions_1(in(atom_transitions), in, in) =
-            out(atom_transitions) is det.
+%:- mode add_atom_transitions_1(in(atom_transitions), in, in) =
+%            out(atom_transitions) is det.

  add_atom_transitions_1(CTs0, X, Y) = CTs :-
      list.filter_map(maybe_copy_transition(X, Y), CTs0, CTs).
@@ -208,9 +246,12 @@ add_atom_transitions_1(CTs0, X, Y) = CTs :-
  %-----------------------------------------------------------------------------%

  :- pred maybe_copy_transition(state_no, state_no, transition, transition).
-:- mode maybe_copy_transition(in,in,in(atom_transition),out(atom_transition))
-            is semidet.
+%:- mode maybe_copy_transition(in,in,in(atom_transition),out(atom_transition))
+%            is semidet.
+:- mode maybe_copy_transition(in, in, in, out) is semidet.

+maybe_copy_transition(_, _, null(_, _) , _) :-
+    unexpected($file, $pred, "null transition").
  maybe_copy_transition(X, Y, trans(Y, C, Z), trans(X, C, Z)).

  %-----------------------------------------------------------------------------%



More information about the reviews mailing list