<div dir="ltr"><div dir="ltr"><br></div><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Fri, 17 Jul 2026 at 10:32, Zoltan Somogyi <<a href="mailto:zoltan.somogyi@runbox.com">zoltan.somogyi@runbox.com</a>> wrote:</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
The diff is huge and boring. </blockquote><div><br></div><div>Yes.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The only parts I am seeking feedback on<br>
are the parts marked by "XXX DIAG_SPEC" and a comment. The parts<br>
marked by "XXX DIAG_SPEC" without a new comment are reminders<br>
to myself about minor changes to do after this diff is committed.</blockquote><div><br></div><div>> Use {err,warn,info}_spec as subtypes of diag_spec ...<br><br>...<br><br>> compiler/superhomogeneous_lambda.m:<br>>     Return one (or more) of {err,warn,info}_specs instead of<br>>     diag_specs. Perform the same replacements in data structures.<br>>     (In most modules, diag_specs are replaced by *only* err_specs.)<br>> <br>>     In modules where the diag_specs have all been replaced<br>>     by the same subtype, keep using Specs as (part of) the names<br>>     of the variables containing them. In modules that use<br>>     more than one of the new subtypes, use ErrSpecs, WarnSpecs<br>>     and/or InfoSpecs.<br>> <br>>     Consistenly put err_specs before warn_specs, and both<br>>     before info_specs.<br><br>s/Consistenly/Consistently/<br><br>...<br><br><br>> diff --git a/compiler/add_pragma_util.m b/compiler/add_pragma_util.m<br>> index 2d1a1f336..c61b80635 100644<br>> --- a/compiler/add_pragma_util.m<br>> +++ b/compiler/add_pragma_util.m<br><br>...<br><br><br>> @@ -295,10 +292,13 @@ look_up_pragma_pf_sym_arity(ModuleInfo, IsFullyQualified, FailHandling,<br>>                  Specs = []<br>>              ;<br>>                  InformIgnored = yes,<br>> -                Severity =<br>> -                    severity_informational(inform_ignored_pragma_errors),<br>> -                Spec = report_ambiguous_pred_or_func(Severity, PragmaName,<br>> -                    Context, PredOrFunc, SymName, UserArity),<br>> +                % XXX DIAG_SPEC<br>> +                % This Spec used to have this severity:<br>> +                % Severity =<br>> +                %   severity_informational(inform_ignored_pragma_errors),<br>> +                % Should it stay that way?<br>> +                Spec = report_ambiguous_pred_or_func(PragmaName, Context,<br>> +                    PredOrFunc, SymName, UserArity),<br>>                  Specs = [Spec]<br><br><br>Strictly speaking, unrecognised pragmas should be ignored. Practically, since<br>there is only one Mercury implementation, it's probably better to treat them<br>as errors.<br><br>...<br><br>> diff --git a/compiler/check_module_interface.m b/compiler/check_module_interface.m<br>> index 500110a36..b765b69e2 100644<br>> --- a/compiler/check_module_interface.m<br>> +++ b/compiler/check_module_interface.m<br>> @@ -30,7 +30,7 @@<br>>      % report a warning.<br>>      %<br>>  :- pred check_module_interface_for_no_exports(globals::in,<br>> -    parse_tree_module_src::in, list(diag_spec)::out) is det.<br>> +    parse_tree_module_src::in, list(warn_spec)::out) is det.<br>>  <br>>  %---------------------------------------------------------------------------%<br>>  %---------------------------------------------------------------------------%<br>> @@ -63,7 +63,7 @@ check_module_interface_for_no_exports(Globals, ParseTreeModuleSrc, Specs) :-<br>>              _IntFIMs, _ImpFIMs, _IntSelfFIMLangs, _ImpSelfFIMLangs,<br>>  <br>>              TypeCtorCheckedMap, InstCtorCheckedMap, ModeCtorCheckedMap,<br>> -            TypeSpecs, InstModeSpecs,<br>> +            TypeErrSpecs, TypeWarnSpecs, InstModeErrSpecs, InstModeWarnSpecs,<br>>  <br>>              IntTypeClasses, IntInstances, IntPredDecls, IntModeDecls,<br>>              IntDeclPragmas, IntDeclMarkers, IntPromises, _IntBadClauses,<br>> @@ -133,8 +133,15 @@ check_module_interface_for_no_exports(Globals, ParseTreeModuleSrc, Specs) :-<br>>              %   a "no exports" warning or not. This means that the warning<br>>              %   is not really needed *now*; it can be generated later,<br>>              %   once the complained-about invalid definitions are fixed.<br>> -            TypeSpecs = [],<br>> -            InstModeSpecs = [],<br>> +            TypeErrSpecs = [],<br>> +            InstModeErrSpecs = [],<br>> +<br>> +            % XXX DIAG_SPECS<br>> +            % These two conditions preserve existing behavior, from a time<br>> +            % where the parse tree stored co-mingled diag_specs. but<br>> +            % I (zs) don't think it should be preserved.<br><br><br>I don't think it's worth preserving the old behaviour here.<br><br>...<br><br>> diff --git a/compiler/grab_modules.m b/compiler/grab_modules.m<br>> index b65725ec7..442c9c946 100644<br>> --- a/compiler/grab_modules.m<br>> +++ b/compiler/grab_modules.m<br><br>...<br><br><br>> @@ -725,10 +724,11 @@ grab_trans_opt_files(ProgressStream, Globals, TransOptModuleNames,<br>>  <br>>      read_trans_opt_files(ProgressStream, Globals, TransOptModuleNames,<br>>          cord.init, ParseTreeTransOptsCord,<br>> -        [], NonBlockingSpecs, [], BlockingSpecs, !IO),<br>> +        [], BlockErrSpecs, [], WarnSpecs, !IO),<br>>      list.foldl(aug_compilation_unit_add_trans_opt,<br>>          cord.list(ParseTreeTransOptsCord), !AugCompUnit),<br>> -    module_baggage_add_nonfatal_specs(NonBlockingSpecs, !Baggage),<br>> +    % XXX DIAG_SPECS We used to call module_baggage_add_nonfatal_specs.<br>> +    module_baggage_add_warnings(WarnSpecs, !Baggage),<br><br>Looking at the nonfatal errors we have rme_unexpected_term_in_int_or_opt,<br>which seems relevant to reading .trans_opt files.<br><br>>      % XXX why ignore any existing errors?<br>>      !Baggage ^ mb_errors := init_read_module_errors,<br>>  <br><br>...<br><br><br>> diff --git a/compiler/make_hlds_passes.m b/compiler/make_hlds_passes.m<br>> index bee01e019..67bc39850 100644<br>> --- a/compiler/make_hlds_passes.m<br>> +++ b/compiler/make_hlds_passes.m<br><br>...<br><br>> @@ -698,7 +707,9 @@ maybe_warn_include_and_non_include(Globals, ParseTreeModuleSrc, Specs) :-<br>>              words("Moving those entities to other modules can reduce"),<br>>              words("the number of modules needing to be recompiled"),<br>>              words("after a change."), nl],<br>> -        Spec = spec($pred, severity_error, phase_pt2h, Context, Pieces),<br>> +        % XXX DIAG_SPECS The severity used to be error.<br>> +        Severity = severity_warning(warn_include_and_non_include),<br>> +        Spec = spec($pred, Severity, phase_pt2h, Context, Pieces),<br>>          Specs = [Spec]<br>>      else<br>>          Specs = []<br><br>The old behaviour was surely a mistake. The message says that this is a<br>warning, as does the name of this predicate.<br><br><br>> diff --git a/compiler/maybe_error.m b/compiler/maybe_error.m<br>> index 540e72d35..f62eb3f76 100644<br>> --- a/compiler/maybe_error.m<br>> +++ b/compiler/maybe_error.m<br><br>...<br><br><br>> @@ -106,36 +111,79 @@<br>>  <br>>  %---------------------%<br>>  <br>> -:- func get_any_errors1(maybe1(T1)) = list(diag_spec).<br>> -:- func get_any_errors2(maybe2(T1, T2)) = list(diag_spec).<br>> -:- func get_any_errors3(maybe3(T1, T2, T3)) = list(diag_spec).<br>> -:- func get_any_errors4(maybe4(T1, T2, T3, T4)) = list(diag_spec).<br>> -:- func get_any_errors5(maybe5(T1, T2, T3, T4, T5)) = list(diag_spec).<br>> -:- func get_any_errors6(maybe6(T1, T2, T3, T4, T5, T6)) = list(diag_spec).<br>> +    % This type, which is used mostly by code that parses goals,<br>> +    % allows us to record both<br>> +    % - the presence of at least one error, and<br>> +    % - the presence of zero or more warnings.<br>> +    % The reason why we use tuples is that any readable function symbol<br>> +    % we could use would make the lines that construct errors too long.<br>> +:- type err_warn_error == {one_or_more(err_spec), list(warn_spec)}.<br>>  <br>> -:- func get_any_errors1el(maybe1el(T1)) = list(diag_spec).<br>> -:- func get_any_errors2el(maybe2el(T1, T2)) = list(diag_spec).<br>> -:- func get_any_errors3el(maybe3el(T1, T2, T3)) = list(diag_spec).<br>> -:- func get_any_errors4el(maybe4el(T1, T2, T3, T4)) = list(diag_spec).<br>> -:- func get_any_errors5el(maybe5el(T1, T2, T3, T4, T5)) = list(diag_spec).<br>> -:- func get_any_errors6el(maybe6el(T1, T2, T3, T4, T5, T6)) = list(diag_spec).<br>> +% XXX DIAG_SPECS<br>> +% The numbers in these names mirror the number of arguments of the okN<br>> +% function symbol, but they do not match the type constructor's arity,<br>> +% due to the warn_specs being implicit. Which is more important?<br><br>I would say the latter.<br><br>> +:- type parse_result2(T) ==<br>> +    maybe2(T, list(warn_spec), err_warn_error).<br>> +:- type parse_result3(T1, T2) ==<br>> +    maybe3(T1, T2, list(warn_spec), err_warn_error).<br>> +:- type parse_result4(T1, T2, T3) ==<br>> +    maybe4(T1, T2, T3, list(warn_spec), err_warn_error).<br>>  <br><br>...<br><br><br>> diff --git a/compiler/mercury_compile_args.m b/compiler/mercury_compile_args.m<br>> index 8e53f54f1..a3c0bd2f6 100644<br>> --- a/compiler/mercury_compile_args.m<br>> +++ b/compiler/mercury_compile_args.m<br><br><br>...<br><br>> @@ -286,34 +288,37 @@ process_options_std(ProgressStream, DefaultOptionTable, CmdLineArgs,<br>>          DefaultOptionTable, ArgsOptionTable, cord.init, _UserData, !IO),<br>>      (<br>>          MaybeError = yes(OptionError),<br>> -        Specs = report_option_error(OptionError),<br>> -        Result = opr_failure(Specs)<br>> +        OoMErrSpecs = report_option_error(OptionError),<br>> +        Result = opr_failure(OoMErrSpecs, [])<br>>      ;<br>>          MaybeError = no,<br>>          read_options_files_named_in_options_file_option(ProgressStream,<br>>              ArgsOptionTable, EnvOptFileVariables0,<br>> -            OptFileNonUndefSpecs, OptFileUndefSpecs, !IO),<br>> +            OptFileErrSpecs, OptFileUndefWarnSpecs0, !IO),<br>>          getopt.lookup_bool_option(ArgsOptionTable,<br>>              warn_undefined_options_variables, WarnUndef),<br>>          (<br>>              WarnUndef = no,<br>> -            OptFileSpecs = OptFileNonUndefSpecs<br>> +            OptFileUndefWarnSpecs = []<br>>          ;<br>>              WarnUndef = yes,<br>> -            OptFileSpecs = OptFileNonUndefSpecs ++ OptFileUndefSpecs<br>> +            OptFileUndefWarnSpecs = OptFileUndefWarnSpecs0<br>>          ),<br>>          io.environment.get_environment_var_map(EnvVarMap, !IO),<br>> +        % XXX DIAG_SPEC we used to include OptFileUndefWarnSpecs in this<br><br>Not sure about that one; are they written out somewhere else?<br><br>...<br><br><br>> diff --git a/compiler/parse_dcg_goal.m b/compiler/parse_dcg_goal.m<br>> index 70e3d7ed4..87e769306 100644<br>> --- a/compiler/parse_dcg_goal.m<br>> +++ b/compiler/parse_dcg_goal.m<br><br>...<br><br><br>> @@ -505,7 +509,7 @@ parse_dcg_goal_some_all(GoalKind, ArgTerms, Context, ContextPieces,<br>>      % to warrant a small amount of code target language code duplication.<br>>      %<br>>  :- pred parse_dcg_goal_conj(dcg_goal_kind, list(term),<br>> -    prog_context, cord(format_piece), maybe2(goal, list(warning_spec)),<br>> +    prog_context, cord(format_piece), parse_result2(goal),<br>>      prog_varset, prog_varset, counter, counter, prog_var, prog_var).<br>>  :- mode parse_dcg_goal_conj(in(dcg_goal_kind_conj), in, in, in, out,<br>>      in, out, in, out, in, out) is det.<br>> @@ -518,10 +522,13 @@ parse_dcg_goal_conj(GoalKind, ArgTerms, Context, ContextPieces,<br>>      string_dcg_goal_kind(Functor, GoalKind),<br>>      ( if ArgTerms = [TermA, TermB] then<br>>          parse_dcg_goal_conjunction(Functor, TermA, TermB, Context,<br>> -            ContextPieces, cord.init, ConjunctsCord, [], Warnings, [], Errors,<br>> -            !VarSet, !Counter, !DCGVar),<br>> +            ContextPieces, cord.init, ConjunctsCord,<br>> +            [], ErrSpecs, [], WarnSpecs, !VarSet, !Counter, !DCGVar),<br>>          (<br>> -            Errors = [],<br>> +            ErrSpecs = [],<br>> +            % XXX DIAG_SPEC In the past, we used to get here only if<br>> +            % parse_dcg_goal_conjunction returned no diag_specs of<br>> +            % *any* severity.<br>>              Conjuncts = cord.list(ConjunctsCord),<br><br>The old behaviour seems wrong. parse_goal is called by parse_dcg_goal_conjuction<br>and that can produce warnings.<br><br>...<br><br>  <br>>  :- pred parse_dcg_goal_semicolon(list(term)::in,<br>>      prog_context::in, cord(format_piece)::in,<br>> -    maybe2(goal, list(warning_spec))::out,<br>> +    parse_result2(goal)::out,<br>>      prog_varset::in, prog_varset::out, counter::in, counter::out,<br>>      prog_var::in, prog_var::out) is det.<br>>  :- pragma inline(pred(parse_dcg_goal_semicolon/10)).<br>> @@ -611,10 +618,13 @@ parse_dcg_goal_semicolon(ArgTerms, Context, ContextPieces,<br>>              InitDCGVar = !.DCGVar,<br>>              parse_dcg_goal_disjunction(InitDCGVar, ContextPieces,<br>>                  SubGoalTermA, SubGoalTermB, no, MaybeFirstDiffDCGVar,<br>> -                cord.init, DisjunctsDCGVarsCord, [], Warnings, [], ErrorSpecs,<br>> -                !VarSet, !Counter),<br>> +                cord.init, DisjunctsDCGVarsCord,<br>> +                [], ErrSpecs, [], WarnSpecs, !VarSet, !Counter),<br>>              (<br>> -                ErrorSpecs = [],<br>> +                ErrSpecs = [],<br>> +                % XXX DIAG_SPEC In the past, we used to get here only if<br>> +                % parse_dcg_goal_disjunction returned no diag_specs of<br>> +                % *any* severity.<br><br>Ditto for this one.<br><br>...<br><br><br>> diff --git a/compiler/parse_goal.m b/compiler/parse_goal.m<br>> index c40c607a7..5f84f9ddf 100644<br>> --- a/compiler/parse_goal.m<br>> +++ b/compiler/parse_goal.m<br><br>...<br><br><br>> @@ -768,10 +772,13 @@ parse_goal_conj(GoalKind, ArgTerms, Context, ContextPieces,<br>>      string_goal_kind(Functor, GoalKind),<br>>      ( if ArgTerms = [SubGoalTermA, SubGoalTermB] then<br>>          parse_goal_conjunction(Functor, SubGoalTermA, SubGoalTermB,<br>> -            ContextPieces, cord.init, ConjunctsCord, [], WarningSpecs,<br>> -            [], ErrorSpecs, !VarSet),<br>> +            ContextPieces, cord.init, ConjunctsCord,<br>> +            [], ErrSpecs, [], WarnSpecs, !VarSet),<br>>          (<br>> -            ErrorSpecs = [],<br>> +            ErrSpecs = [],<br>> +            % XXX DIAG_SPEC In the past, we used to get here only if<br>> +            % parse_goal_conjunction returned no diag_specs of<br>> +            % *any* severity.<br>>              Conjuncts = cord.list(ConjunctsCord),<br><br>As in the above DCG cases, the old behaviour seems wrong.<br><br>...<br><br><br>> @@ -852,28 +859,36 @@ parse_goal_semicolon(ArgTerms, Context, ContextPieces, MaybeGoal, !VarSet) :-<br>>              parse_goal(ElseGoalTerm, ContextPieces, MaybeElseGoal, !VarSet),<br>>              ( if<br>>                  MaybeCondGoal =<br>> -                    ok4(Vars, StateVars, CondGoal, CondWarningSpecs),<br>> -                MaybeThenGoal = ok2(ThenGoal, ThenWarningSpecs),<br>> -                MaybeElseGoal = ok2(ElseGoal, ElseWarningSpecs)<br>> +                    ok4(Vars, StateVars, CondGoal, CondWarnSpecs),<br>> +                MaybeThenGoal = ok2(ThenGoal, ThenWarnSpecs),<br>> +                MaybeElseGoal = ok2(ElseGoal, ElseWarnSpecs)<br>>              then<br>>                  Goal = if_then_else_expr(Context, Vars, StateVars,<br>>                      CondGoal, ThenGoal, ElseGoal),<br>> -                WarningSpecs = CondWarningSpecs ++<br>> -                    ThenWarningSpecs ++ ElseWarningSpecs,<br>> -                MaybeGoal = ok2(Goal, WarningSpecs)<br>> +                WarnSpecs = CondWarnSpecs ++<br>> +                    ThenWarnSpecs ++ ElseWarnSpecs,<br>> +                MaybeGoal = ok2(Goal, WarnSpecs)<br>>              else<br>> -                Specs = get_any_errors_warnings4(MaybeCondGoal) ++<br>> -                    get_any_errors_warnings2(MaybeThenGoal) ++<br>> -                    get_any_errors_warnings2(MaybeElseGoal),<br>> -                det_list_to_one_or_more(Specs, OoMSpecs),<br>> -                MaybeGoal = error2(OoMSpecs)<br>> +                get_all_errors_warnings4(MaybeCondGoal,<br>> +                    CondErrSpecs, CondWarnSpecs),<br>> +                get_all_errors_warnings2(MaybeThenGoal,<br>> +                    ThenErrSpecs, ThenWarnSpecs),<br>> +                get_all_errors_warnings2(MaybeElseGoal,<br>> +                    ElseErrSpecs, ElseWarnSpecs),<br>> +                ErrSpecs = CondErrSpecs ++ ThenErrSpecs ++ ElseErrSpecs,<br>> +                WarnSpecs = CondWarnSpecs ++ ThenWarnSpecs ++ ElseWarnSpecs,<br>> +                det_list_to_one_or_more(ErrSpecs, OoMErrSpecs),<br>> +                MaybeGoal = error2({OoMErrSpecs, WarnSpecs})<br>>              )<br>>          else<br>>              parse_goal_disjunction(SubGoalTermA, SubGoalTermB, ContextPieces,<br>> -                cord.init, DisjunctsCord, [], WarningSpecs, [], ErrorSpecs,<br>> +                cord.init, DisjunctsCord, [], ErrSpecs, [], WarnSpecs,<br>>                  !VarSet),<br>>              (<br>> -                ErrorSpecs = [],<br>> +                ErrSpecs = [],<br>> +                % XXX DIAG_SPEC In the past, we used to get here only if<br>> +                % parse_goal_disjunction returned no diag_specs of<br>> +                % *any* severity.<br>>                  Disjuncts = cord.list(DisjunctsCord),<br><br><br>Ditto here.<br><br><br>...<br><br>> diff --git a/compiler/parse_item.m b/compiler/parse_item.m<br>> index 18e2ca3da..dc74b1d23 100644<br>> --- a/compiler/parse_item.m<br>> +++ b/compiler/parse_item.m<br><br>...<br><br><br>> @@ -1583,13 +1594,15 @@ read_term_to_iom_result(ModuleName, FileName, ReadTermResult, ReadIOMResult,<br>>          ReadTermResult = term(VarSet, Term),<br>>          counter.allocate(SeqNum, !SeqNumCounter),<br>>          parse_item_or_marker(ModuleName, VarSet, Term, item_seq_num(SeqNum),<br>> -            MaybeItemOrMarker),<br>> +            MaybeItemOrMarker, [], WarnSpecs),<br>>          (<br>>              MaybeItemOrMarker = ok1(ItemOrMarker),<br>> +            % XXX DIAG_SPEC This ignores WarnSpecs.<br>>              ReadIOMResult = read_iom_ok(VarSet, Term, ItemOrMarker)<br>>          ;<br>> -            MaybeItemOrMarker = error1(Specs),<br>> -            ReadIOMResult = read_iom_parse_item_errors(VarSet, Term, Specs)<br>> +            MaybeItemOrMarker = error1(OoMErrSpecs),<br>> +            ReadIOMResult = read_iom_parse_item_errors(VarSet, Term,<br>> +                OoMErrSpecs, WarnSpecs)<br>>          )<br>>      ).<br>>  <br><br>...<br><br>> diff --git a/compiler/read_modules.m b/compiler/read_modules.m<br>> index 24482f774..dc2c19124 100644<br>> --- a/compiler/read_modules.m<br>> +++ b/compiler/read_modules.m<br>> @@ -1487,7 +1487,10 @@ record_and_report_missing_timestamp(Globals, FileName, IOError,<br>>              words("Smart recompilation will not work."), nl],<br>>          Severity = severity_warning(warn_smart_recompilation),<br>>          Spec = no_ctxt_spec($pred, Severity, phase_read_files, Pieces),<br>> -        add_nonfatal_error(rme_cannot_find_modify_time, Spec, !Errors)<br>> +        % XXX DIAG_SPEC This used to be:<br>> +        %   add_nonfatal_error(rme_cannot_find_modify_time, Spec, !Errors)<br>> +        % but this is a warning, not an error.<br>> +        add_warning(Spec, !Errors)<br>>      ;<br>>          Warn = no<br>>      ).<br><br>I don't know about that one. I would leave it marked with an XXX and whoever<br>undertakes the work need to complete smart recompilation can deal with it.<br></div><div><br></div><div>Julien.</div><div><br></div></div></div>