Skip to content

Conversation

@emilienlemaire
Copy link

No description provided.

@GitMensch
Copy link
Collaborator

Why do we need a new flag instead of -fec?
Note: if not implemented yet we should add the cobol2027 ec-all-fatal to enable only those checks (that needs to iterate over the existing table instead of the current "enable the single/group entry".

Note: no changelog needed for new tests

@emilienlemaire
Copy link
Author

The new flag is introduced because the current behavior is to continue normally when fatal exceptions are raised and are not checked, which is not the behavior described by the standard.

To avoid breaking programs running on GC3 after the PR is merged I added the flag, so now user can choose how these exceptions should behave, i.e do nothing as the default behavior or fail with the flag set.

@GitMensch
Copy link
Collaborator

GitMensch commented Oct 7, 2025 via email

@emilienlemaire
Copy link
Author

I edited the tests that shouldn't work with -fec=EC-SIZE to add -fno-ec=EC-SIZE so they pass

@emilienlemaire emilienlemaire changed the title Handle size exceptions as per the standard with -fstandard-exception Fail on EC-SIZE exceptions as per the standard Oct 8, 2025
@GitMensch
Copy link
Collaborator

While I'll need to check the details (@ddeclerck can possibly help with the all failing CI tests):

  • tests/Changelog can be reverted
  • EC-SIZE-EXPONENTIATION should be included
  • for the testcases I'd suggest (but not enforce) using the shortened form fno-ec=SIZE (possibly lower-case)
  • run_misc should include a test for all checked EC-SIZE - that can go all into a single testcase with multiple programs + AT_CHECKs
  • NEWS should have a user-view entry for this change

@emilienlemaire
Copy link
Author

I added the tests for all the exceptions that are set in libcob

@emilienlemaire emilienlemaire force-pushed the fail-on-exception branch 2 times, most recently from dc54b34 to ae7da33 Compare October 9, 2025 12:40
@emilienlemaire
Copy link
Author

I also added a configuration option to enable the failure of the exceptions. I figured many could want to run with --debug, yet the COBOL dialect they used should not fail on such exceptions, and this avoid adding -fno-ec each time it is the case

Copy link
Collaborator

@GitMensch GitMensch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we getting near to the solution :-)

@GitMensch
Copy link
Collaborator

I also added a configuration option to enable the failure of the exceptions. I figured many could want to run with --debug, yet the COBOL dialect they used should not fail on such exceptions, and this avoid adding -fno-ec each time it is the case

--debug is something similar (actually more) that you have with other compiler switches on other compilers where you need to enable checks as well. It is fine to keep as-is and if someone ports he can have a one-time conversion of his old options to the new ones (or let a consultant do that or even better, let someone write a wrapper that takes the old command line options (and possibly environment variables) and calls cobc with the appropriate options).

@emilienlemaire
Copy link
Author

Hello @GitMensch,
I am trying to implement what I think you asked for in this comment, but as I mentioned it is not that easy, as we don't have knowledge of what exceptions have been disabled at runtime.

I made this commit (which will be edited), and the runtime knowledge is the last part I have to add, so I was wondering how I should add the information on the exceptions that enabled / disabled ?

@emilienlemaire emilienlemaire force-pushed the fail-on-exception branch 2 times, most recently from f6f1356 to 7a7b37e Compare November 10, 2025 14:49
@GitMensch
Copy link
Collaborator

👋 Emilien and thanks again for working on this.

Concerning your quuestion - the information about the checked exceptions is codegen'd into the module - it can even be different "per line" using the TURN directives.
The runtime just always sets the specific exception code and the generated code checks per "what is active":

  • if there are tests<, then before the actual statement the same value that is tested for later on (normally checked by its group) is reset
  • then the code in the runtime is executed which would set those values
  • then the exception values actually active on this line will have a generated test for

that approach should be visible if you now generate with ON SIZE ERROR - it "just" needs to be added for the error case without that clause

Your last commit seems to do that quite cleanly, no?

How does the generated code with for example a check for divzero look like?

Did you do anything to the conditions (IF MY-VAR / MY-OTHER-VAR may create a divzero) as well or is that open?

What are the parts this PR needs a direction / review on?

Note: instead of a cob_call_size_error (void) it seems better top have something more general like cob_fatal_exception (int exception_code).

@emilienlemaire
Copy link
Author

Hello Simon!

Thank you for the help!

I updated my commits, and hopefully we are getting closer to get it merged 😄

Concerning the IF MY-VAR / MY-OTHER-VAR I have not yet added any code that can handle that, I'll look into it.

I think I made the changes you were looking for, yet this pose a problem: when any EC-SIZE exception is enabled then the check is valid for all EC-SIZE errors, this was something that was avoided with my initial switch generation. This can also be avoided by generating a check for enabled error at compile time, but I don't think it would be a good idea to generate this inside parser.y, so this would mean moving the code generation to codegen.c and do something closer to what I did before (excluding the configs and flags I added, which we can forget about).

@GitMensch
Copy link
Collaborator

Why do the changes here fail one of the NC tests? did the behavior changed between standards (and we miss a possible distinction between COBOL85 and 2002+) or is there an error in the NIST test or does it depend on undefined behaviour ... or there is an error in the PR itself?

Note: In all cases: please try to "extract" the failure into one of the tests in our own testsuite before working on fixing it,
Thanks for your efforts in this important area (and note that this is one of the missing changes we need this year before the 3.3rc and after that we want to sent the final out asap [=if no breaking tests occur within 2- weeks]).

Concerning the IF MY-VAR / MY-OTHER-VAR I have not yet added any code that can handle that, I'll look into it.

Thanks. In effect it should have a "post check" generated that inspects the error code - similar as the generation without the SIZE ERROR clause.

For the EC-SIZE error problem - that's no issue as long as the check either:

  • generates specific check for the enabled EC-SIZEs
  • generates a check that will go into the runtime for any EC-SIZE and passes the EC-SIZE values needed into the function

Just a fair warning: I'm likely not be available for any (or any longer) reviews this week (and also did not checked the current changes either, just the CI results).

@emilienlemaire
Copy link
Author

There was no EXCEPTION in the COBOL85 standard, and I forgot to update the compilation options in my commit, hence the NIST failures (which are remediated by the last commit).

@codecov-commenter
Copy link

codecov-commenter commented Nov 19, 2025

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 75.51020% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.36%. Comparing base (eda8905) to head (1bb1708).
⚠️ Report is 6 commits behind head on gitside-gnucobol-3.x.

Files with missing lines Patch % Lines
cobc/codegen.c 78.57% 0 Missing and 6 partials ⚠️
cobc/parser.y 40.00% 0 Missing and 6 partials ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@                   Coverage Diff                    @@
##           gitside-gnucobol-3.x     #247      +/-   ##
========================================================
+ Coverage                 67.34%   67.36%   +0.01%     
========================================================
  Files                        34       34              
  Lines                     61487    61535      +48     
  Branches                  16022    16045      +23     
========================================================
+ Hits                      41411    41451      +40     
+ Misses                    14146    14142       -4     
- Partials                   5930     5942      +12     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@GitMensch GitMensch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just stumbled over an interesting explicit rule in COBOL2027 (before it possibly wasn't specified, would have to check): if the EC-SIZE happens on "item identification", then the ON SIZE phrase is not used but the fatal exception - if enabled should be done.

That is the part that will be covered by the "expression addition" - we should definitely add it to our testsuite - even with an expected failure:

MOVE 0 TO SOME
ADD TABLE-VAR (5 / SOME) TO ADD TABLE-VAR (5)
ON SIZE ERROR DISPLAY 'Should not be taken'.

The changes look good in general, but apart from some style and other minor issues there's one issue that is logically wrong - see the parser comment about NOT ON SIZE ERROR; I guess that some of the NIST tests don't need the -fno-ec=size flags because of that - but this can be easily checked after adjusting parser.y

@GitMensch GitMensch changed the base branch from gcos4gnucobol-3.x to gitside-gnucobol-3.x November 20, 2025 15:16
Copy link
Collaborator

@GitMensch GitMensch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apart from the things mentioned in the PR this is good for upstream; please commit the missing adjustments and I can make it available

@emilienlemaire
Copy link
Author

The PR should be OK to merge now, I should have edited everything you asked, and also fixed a few side effects those edits lit up, the main one being to reset the check of the exception before a new statement but still allow checking the exception with intrinsic functions (I checked on the standard, and the exception check should be reset between every statement, but the last exception code and stuff should still be available with intrinsics)

Copy link
Collaborator

@GitMensch GitMensch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Currently NIST NC fails, but I guess you'll push a fix soon.

@emilienlemaire
Copy link
Author

Yes I'm working on a fix

@GitMensch
Copy link
Collaborator

GitMensch commented Nov 24, 2025

Something is broken. If we compile NC2032A with -fec=all (or just --debug) it aborts with

libcob: NC203A.CBL:307: error: Fatal exception not handled: EC-SIZE-ZERO-DIVIDE
libcob: NC203A.CBL:307: warning: implicit CLOSE of PRINT-FILE ('REPORT')

 Last statement of "NC203A" was ADD
        PASS OF SECT-NC203A-001 at NC203A.CBL:307
        DIV-TEST-F4-4-0 OF SECT-NC203A-001 at NC203A.CBL:483
        ENTRY NC203A at NC203A.CBL:295
 Started by ./NC203A

The perform in 403 is

048300     DIVIDE  25COUNT INTO 100 GIVING 25ANS REMAINDER 25REM        NC2034.2
048400             ON SIZE ERROR                                        NC2034.2
048500             PERFORM PASS                                         NC2034.2   *>   here
048600             GO TO DIV-WRITE-F4-4-0.                              NC2034.2

the ADD is

 030900 PASS.  MOVE "PASS " TO P-OR-F.  ADD 1 TO PASS-COUNTER.           NC2034.2

so the DIVIDE error which is correctly handled by its ON SIZE ERROR is evaluated witht the next statement ADD.

Before any change in codegen it would good to have a similar example in the testsuite!

The reason is that before the statements that check the exceptions after their execution we need to reset it, which is done in codegen.c by those lines:

		if (!p->file && (p->ex_handler || p->not_ex_handler)) {
			output_line ("cob_glob_ptr->cob_exception_code = 0;");
		}

which are not taken because no handler is set.

I suggest to add a check p->handler_type == SIZE_ERROR_HANDLER (+active ec-size) there and drop cob_reset_exception.

This should lead to much more (all?) NC tests not needing -fno-ec=size.

For the ec handler output I've meant something along

static void
output_ec_size_handler (void)
{
	int ec_checked = 0;
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_ADDRESS)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_ADDRESS);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_EXPONENTIATION)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_EXPONENTIATION);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_IMP)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_IMP);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_OVERFLOW)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_OVERFLOW);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_TRUNCATION)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_TRUNCATION);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_UNDERFLOW)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_UNDERFLOW);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_ZERO_DIVIDE)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_ZERO_DIVIDE);
	}
	if (ec_checked) {
		output_line("if ((cob_glob_ptr->cob_exception_code & 0x%04x) == 0x%04x)",
			CB_EXCEPTION_CODE (COB_EC_SIZE), CB_EXCEPTION_CODE (COB_EC_SIZE));
		output_line("\t" "cob_fatal_exception (cob_glob_ptr->cob_exception_code);");
	}
}

... but then possibly also have cob_fatal_exception not take an argument (it is only called if there is an error and operates on the last error).

@emilienlemaire
Copy link
Author

For the ec handler output I've meant something along

static void
output_ec_size_handler (void)
{
	int ec_checked = 0;
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_ADDRESS)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_ADDRESS);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_EXPONENTIATION)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_EXPONENTIATION);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_IMP)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_IMP);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_OVERFLOW)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_OVERFLOW);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_TRUNCATION)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_TRUNCATION);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_UNDERFLOW)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_UNDERFLOW);
	}
	if (CB_EXCEPTION_ENABLE (COB_EC_SIZE_ZERO_DIVIDE)) {
		ec_checked |= CB_EXCEPTION_CODE (COB_EC_SIZE_ZERO_DIVIDE);
	}
	if (ec_checked) {
		output_line("if ((cob_glob_ptr->cob_exception_code & 0x%04x) == 0x%04x)",
			CB_EXCEPTION_CODE (COB_EC_SIZE), CB_EXCEPTION_CODE (COB_EC_SIZE));
		output_line("\t" "cob_fatal_exception (cob_glob_ptr->cob_exception_code);");
	}
}

On this, I was doing something close to this at one point, but let's say we have only EC-SIZE-OVERFLOW enabled, and we compute something that sets a EC-SIZE-ZERO-DIVIDE, which is set (among other places), with theses lines:

	if (unlikely (mpz_sgn (d2->value) == 0)) {
		d1->scale = COB_DECIMAL_NAN;
		cob_set_exception (COB_EC_SIZE_ZERO_DIVIDE);
		return;
	}

then the generated condition will be true, and the exception will be wrongly raised, even if it was disabled.

As for the exceptions that were wrongly raised in handler statements, the last commit should fix it 😄

@GitMensch
Copy link
Collaborator

On this, I was doing something close to this at one point, but let's say we have only EC-SIZE-OVERFLOW enabled, and we compute something that sets a EC-SIZE-ZERO-DIVIDE, which is set (among other places), with theses lines:

	if (unlikely (mpz_sgn (d2->value) == 0)) {
		d1->scale = COB_DECIMAL_NAN;
		cob_set_exception (COB_EC_SIZE_ZERO_DIVIDE);
		return;
	}

Of course my code was wrong, should have been something like

	if (ec_checked) {
		output_line("if ((cob_glob_ptr->cob_exception_code & 0x%04x) == 0x%04x) && ((cob_glob_ptr->cob_exception_code & 0x%04x) != 0))",
			CB_EXCEPTION_CODE (COB_EC_SIZE), CB_EXCEPTION_CODE (COB_EC_SIZE)), (ec_checked & 0x00FF);

--> group is set, and one of the |= exception bits are set

@emilienlemaire
Copy link
Author

I implemented the check you suggested, but had to edit the exception codes in exception.def to avoid overlapping bits in the checks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants