Skip to content

Commit

Permalink
Change 3 arg Assert to raise an error
Browse files Browse the repository at this point in the history
Surprisingly, the three argument version of GAP's `Assert` statement
did not raise an error, unlike the more commonly used two argument
version. That is:

    gap> Assert(0, false);
    Error, Assertion failure
    not in any function at *stdin*:1
    you may 'return;'
    brk>
    gap> Assert(0, false, "MESSAGE");
    MESSAGE

This is a really surprising an unexpected behavior. After some
discussion we agreed that both should raise an error. While this
is technically a breaking change, we expect the impact of this on
the GAP package ecosystem to be minimal, and if at all positive:
the few uses of the three argument version we found all seem to be
written by someone expecting them to raise an error!
  • Loading branch information
fingolfin committed Jan 13, 2025
1 parent 90743e6 commit 4d28cea
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 56 deletions.
7 changes: 3 additions & 4 deletions doc/ref/debug.xml
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,9 @@ if AssertionLevel() >= lev and not <cond> then
fi;
]]></Log>
<P/>
With the <A>message</A> argument form of the <Ref Func="Assert"/> statement,
if the global assertion level is at least <A>lev</A>, condition <A>cond</A>
is tested and if it does not return <K>true</K> then <A>message</A> is
evaluated and printed.
If the <A>message</A> argument form of the <Ref Func="Assert"/> statement
is provided, and if an error is raised, then this message is printed as part of
the error.
<P/>
Assertions are used at various places in the library.
Thus turning assertions on can slow code execution significantly.
Expand Down
4 changes: 1 addition & 3 deletions src/compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -4994,9 +4994,7 @@ static void CompAssert3(Stat stat)
cnd = CompBoolExpr(READ_STAT(stat, 1));
Emit( "if ( ! %c ) {\n", cnd );
msg = CompExpr(READ_STAT(stat, 2));
Emit( "if ( %c != (Obj)(UInt)0 )", msg );
Emit( "{\n if ( IS_STRING_REP ( %c ) )\n", msg);
Emit( " PrintString1( %c);\n else\n PrintObj(%c);\n}\n", msg, msg );
Emit( "AssertionFailureWithMessage(%c);\n", msg );

Check warning on line 4997 in src/compiler.c

View check run for this annotation

Codecov / codecov/patch

src/compiler.c#L4997

Added line #L4997 was not covered by tests
Emit( "}\n" );
Emit( "}\n" );

Expand Down
16 changes: 16 additions & 0 deletions src/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,22 @@ void AssertionFailure(void)
ErrorReturnVoid("Assertion failure", 0, 0, "you may 'return;'");
}

void AssertionFailureWithMessage(Obj message)

Check warning on line 599 in src/error.c

View check run for this annotation

Codecov / codecov/patch

src/error.c#L599

Added line #L599 was not covered by tests
{
if (message == 0) {
// this case is triggered by code like this: Assert(0, false, Error("boo"));
// at least if the user enters `return;` into the break loop opened by this.
AssertionFailure();

Check warning on line 604 in src/error.c

View check run for this annotation

Codecov / codecov/patch

src/error.c#L604

Added line #L604 was not covered by tests
}
else if (IS_STRING_REP(message)) {
ErrorReturnVoid("Assertion failure: %g", (Int)message, 0, "you may 'return;'");

Check warning on line 607 in src/error.c

View check run for this annotation

Codecov / codecov/patch

src/error.c#L606-L607

Added lines #L606 - L607 were not covered by tests
}
else {
PrintObj(message);
AssertionFailure();

Check warning on line 611 in src/error.c

View check run for this annotation

Codecov / codecov/patch

src/error.c#L610-L611

Added lines #L610 - L611 were not covered by tests
}
}


/****************************************************************************
**
Expand Down
4 changes: 3 additions & 1 deletion src/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,12 @@ Obj CALL_WITH_CATCH(Obj func, Obj args);
/****************************************************************************
**
*F AssertionFailure() . . . . . . . . . . . trigger a GAP assertion failure
*F AssertionFailureWithMessage(<obj>)
**
** This helper function is used by GAP's 'Assert' statement.
** These helper functions are used by GAP's 'Assert' statement.
*/
void AssertionFailure(void);
void AssertionFailureWithMessage(Obj message);


/****************************************************************************
Expand Down
10 changes: 1 addition & 9 deletions src/intrprtr.c
Original file line number Diff line number Diff line change
Expand Up @@ -4082,7 +4082,6 @@ void IntrAssertAfterCondition(IntrState * intr)
return;
}


condition = PopObj(intr);

if (condition == True)
Expand All @@ -4106,7 +4105,6 @@ void IntrAssertEnd2Args(IntrState * intr)
return;
}


if (intr->ignoring == 0)
AssertionFailure();
else
Expand All @@ -4132,15 +4130,9 @@ void IntrAssertEnd3Args(IntrState * intr)
return;
}


if (intr->ignoring == 0) {
message = PopVoidObj(intr);
if (message != (Obj)0) {
if (IS_STRING_REP(message))
PrintString1(message);
else
PrintObj(message);
}
AssertionFailureWithMessage(message);

Check warning on line 4135 in src/intrprtr.c

View check run for this annotation

Codecov / codecov/patch

src/intrprtr.c#L4135

Added line #L4135 was not covered by tests
}
else
intr->ignoring -= 2;
Expand Down
9 changes: 2 additions & 7 deletions src/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -925,13 +925,8 @@ static ExecStatus ExecAssert3Args(Stat stat)
RequireTrueOrFalse("Assert", cond);
if (cond == False) {
message = EVAL_EXPR(READ_STAT(stat, 2));
if ( message != (Obj) 0 ) {
SET_BRK_CALL_TO( stat );
if (IS_STRING_REP( message ))
PrintString1( message );
else
PrintObj(message);
}
SET_BRK_CALL_TO( stat );
AssertionFailureWithMessage(message);

Check warning on line 929 in src/stats.c

View check run for this annotation

Codecov / codecov/patch

src/stats.c#L928-L929

Added lines #L928 - L929 were not covered by tests
}
}
return STATUS_END;
Expand Down
35 changes: 5 additions & 30 deletions tst/test-compile/assert.g.dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,7 @@ static Obj HdlrFunc2 (
t_1 = (Obj)(UInt)(t_2 != False);
if ( ! t_1 ) {
t_2 = MakeString( "fail-A" );
if ( t_2 != (Obj)(UInt)0 ){
if ( IS_STRING_REP ( t_2 ) )
PrintString1( t_2);
else
PrintObj(t_2);
}
AssertionFailureWithMessage(t_2);
}
}

Expand All @@ -77,12 +72,7 @@ static Obj HdlrFunc2 (
t_1 = (Obj)(UInt)(t_2 != False);
if ( ! t_1 ) {
t_2 = MakeString( "fail-B" );
if ( t_2 != (Obj)(UInt)0 ){
if ( IS_STRING_REP ( t_2 ) )
PrintString1( t_2);
else
PrintObj(t_2);
}
AssertionFailureWithMessage(t_2);
}
}

Expand Down Expand Up @@ -128,12 +118,7 @@ static Obj HdlrFunc2 (
t_1 = (Obj)(UInt)(t_2 != False);
if ( ! t_1 ) {
t_2 = MakeString( "fail-C" );
if ( t_2 != (Obj)(UInt)0 ){
if ( IS_STRING_REP ( t_2 ) )
PrintString1( t_2);
else
PrintObj(t_2);
}
AssertionFailureWithMessage(t_2);
}
}

Expand All @@ -152,12 +137,7 @@ static Obj HdlrFunc2 (
t_1 = (Obj)(UInt)(t_2 != False);
if ( ! t_1 ) {
t_2 = MakeString( "fail-D" );
if ( t_2 != (Obj)(UInt)0 ){
if ( IS_STRING_REP ( t_2 ) )
PrintString1( t_2);
else
PrintObj(t_2);
}
AssertionFailureWithMessage(t_2);
}
}

Expand All @@ -176,12 +156,7 @@ static Obj HdlrFunc2 (
t_1 = (Obj)(UInt)(t_2 != False);
if ( ! t_1 ) {
t_2 = MakeString( "pass!\n" );
if ( t_2 != (Obj)(UInt)0 ){
if ( IS_STRING_REP ( t_2 ) )
PrintString1( t_2);
else
PrintObj(t_2);
}
AssertionFailureWithMessage(t_2);
}
}

Expand Down
9 changes: 7 additions & 2 deletions tst/test-compile/assert.g.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
0
2
pass!
end of function
Error, Assertion failure: pass!
in
Assert( 2, false, "pass!\n" ); at assert.g:13 called from
<function "runtest">( <arguments> )
called from read-eval loop at *stdin*:1
you may 'return;'


0 comments on commit 4d28cea

Please sign in to comment.