Skip to content

Commit 3ff102a

Browse files
authored
Change 3 arg Assert to raise an error (#5904)
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: all uses of the three argument version we found all seem to be written by someone expecting them to raise an error! So the new behaviour now is this: gap> Assert(0, false, "MESSAGE"); Error, Assertion failure: MESSAGE not in any function at *stdin*:1 you may 'return;' brk>
1 parent 90743e6 commit 3ff102a

File tree

11 files changed

+66
-64
lines changed

11 files changed

+66
-64
lines changed

doc/ref/debug.xml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,9 @@ if AssertionLevel() >= lev and not <cond> then
344344
fi;
345345
]]></Log>
346346
<P/>
347-
With the <A>message</A> argument form of the <Ref Func="Assert"/> statement,
348-
if the global assertion level is at least <A>lev</A>, condition <A>cond</A>
349-
is tested and if it does not return <K>true</K> then <A>message</A> is
350-
evaluated and printed.
347+
If the <A>message</A> argument form of the <Ref Func="Assert"/> statement
348+
is provided, and if an error is raised, then this message is printed as part of
349+
the error.
351350
<P/>
352351
Assertions are used at various places in the library.
353352
Thus turning assertions on can slow code execution significantly.

src/compiler.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4994,9 +4994,7 @@ static void CompAssert3(Stat stat)
49944994
cnd = CompBoolExpr(READ_STAT(stat, 1));
49954995
Emit( "if ( ! %c ) {\n", cnd );
49964996
msg = CompExpr(READ_STAT(stat, 2));
4997-
Emit( "if ( %c != (Obj)(UInt)0 )", msg );
4998-
Emit( "{\n if ( IS_STRING_REP ( %c ) )\n", msg);
4999-
Emit( " PrintString1( %c);\n else\n PrintObj(%c);\n}\n", msg, msg );
4997+
Emit( "AssertionFailureWithMessage(%c);\n", msg );
50004998
Emit( "}\n" );
50014999
Emit( "}\n" );
50025000

src/error.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,23 @@ void AssertionFailure(void)
596596
ErrorReturnVoid("Assertion failure", 0, 0, "you may 'return;'");
597597
}
598598

599+
void AssertionFailureWithMessage(Obj message)
600+
{
601+
if (message == 0) {
602+
// this case is triggered by code like this: Assert(0, false, Error("boo"));
603+
// at least if the user enters `return;` into the break loop opened by this.
604+
AssertionFailure();
605+
}
606+
else if (IS_STRING_REP(message)) {
607+
ErrorReturnVoid("Assertion failure: %g", (Int)message, 0, "you may 'return;'");
608+
}
609+
else {
610+
PrintObj(message);
611+
Pr("\n", 0, 0);
612+
AssertionFailure();
613+
}
614+
}
615+
599616

600617
/****************************************************************************
601618
**

src/error.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,12 @@ Obj CALL_WITH_CATCH(Obj func, Obj args);
383383
/****************************************************************************
384384
**
385385
*F AssertionFailure() . . . . . . . . . . . trigger a GAP assertion failure
386+
*F AssertionFailureWithMessage(<obj>)
386387
**
387-
** This helper function is used by GAP's 'Assert' statement.
388+
** These helper functions are used by GAP's 'Assert' statement.
388389
*/
389390
void AssertionFailure(void);
391+
void AssertionFailureWithMessage(Obj message);
390392

391393

392394
/****************************************************************************

src/intrprtr.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4082,7 +4082,6 @@ void IntrAssertAfterCondition(IntrState * intr)
40824082
return;
40834083
}
40844084

4085-
40864085
condition = PopObj(intr);
40874086

40884087
if (condition == True)
@@ -4106,7 +4105,6 @@ void IntrAssertEnd2Args(IntrState * intr)
41064105
return;
41074106
}
41084107

4109-
41104108
if (intr->ignoring == 0)
41114109
AssertionFailure();
41124110
else
@@ -4132,15 +4130,9 @@ void IntrAssertEnd3Args(IntrState * intr)
41324130
return;
41334131
}
41344132

4135-
41364133
if (intr->ignoring == 0) {
41374134
message = PopVoidObj(intr);
4138-
if (message != (Obj)0) {
4139-
if (IS_STRING_REP(message))
4140-
PrintString1(message);
4141-
else
4142-
PrintObj(message);
4143-
}
4135+
AssertionFailureWithMessage(message);
41444136
}
41454137
else
41464138
intr->ignoring -= 2;

src/stats.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -925,13 +925,8 @@ static ExecStatus ExecAssert3Args(Stat stat)
925925
RequireTrueOrFalse("Assert", cond);
926926
if (cond == False) {
927927
message = EVAL_EXPR(READ_STAT(stat, 2));
928-
if ( message != (Obj) 0 ) {
929-
SET_BRK_CALL_TO( stat );
930-
if (IS_STRING_REP( message ))
931-
PrintString1( message );
932-
else
933-
PrintObj(message);
934-
}
928+
SET_BRK_CALL_TO( stat );
929+
AssertionFailureWithMessage(message);
935930
}
936931
}
937932
return STATUS_END;

tst/test-compile/assert.g

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ runtest := function()
1010
Assert(3, false);
1111
Assert(2, true, "fail-D");
1212
Assert(2, true);
13+
14+
# ensure we don't abort after an error
15+
BreakOnError := false;
16+
1317
Assert(2, false, "pass!\n");
14-
# We can't test this next line, as it produces
15-
# <compiled or corrupted statement> when compiled
16-
# Assert(2, false);
18+
Assert(2, false);
1719
Print("end of function\n");
1820
end;

tst/test-compile/assert.g.dynamic.c

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* C file produced by GAC */
22
#include "compiled.h"
3-
#define FILE_CRC "47091879"
3+
#define FILE_CRC "-125967076"
44

55
/* global variables used in handlers */
66
static GVar G_Print;
@@ -10,6 +10,7 @@ static Obj GF_SetAssertionLevel;
1010
static GVar G_AssertionLevel;
1111
static Obj GF_AssertionLevel;
1212
static GVar G_runtest;
13+
static GVar G_BreakOnError;
1314

1415
/* record names used in handlers */
1516

@@ -53,12 +54,7 @@ static Obj HdlrFunc2 (
5354
t_1 = (Obj)(UInt)(t_2 != False);
5455
if ( ! t_1 ) {
5556
t_2 = MakeString( "fail-A" );
56-
if ( t_2 != (Obj)(UInt)0 ){
57-
if ( IS_STRING_REP ( t_2 ) )
58-
PrintString1( t_2);
59-
else
60-
PrintObj(t_2);
61-
}
57+
AssertionFailureWithMessage(t_2);
6258
}
6359
}
6460

@@ -77,12 +73,7 @@ static Obj HdlrFunc2 (
7773
t_1 = (Obj)(UInt)(t_2 != False);
7874
if ( ! t_1 ) {
7975
t_2 = MakeString( "fail-B" );
80-
if ( t_2 != (Obj)(UInt)0 ){
81-
if ( IS_STRING_REP ( t_2 ) )
82-
PrintString1( t_2);
83-
else
84-
PrintObj(t_2);
85-
}
76+
AssertionFailureWithMessage(t_2);
8677
}
8778
}
8879

@@ -128,12 +119,7 @@ static Obj HdlrFunc2 (
128119
t_1 = (Obj)(UInt)(t_2 != False);
129120
if ( ! t_1 ) {
130121
t_2 = MakeString( "fail-C" );
131-
if ( t_2 != (Obj)(UInt)0 ){
132-
if ( IS_STRING_REP ( t_2 ) )
133-
PrintString1( t_2);
134-
else
135-
PrintObj(t_2);
136-
}
122+
AssertionFailureWithMessage(t_2);
137123
}
138124
}
139125

@@ -152,12 +138,7 @@ static Obj HdlrFunc2 (
152138
t_1 = (Obj)(UInt)(t_2 != False);
153139
if ( ! t_1 ) {
154140
t_2 = MakeString( "fail-D" );
155-
if ( t_2 != (Obj)(UInt)0 ){
156-
if ( IS_STRING_REP ( t_2 ) )
157-
PrintString1( t_2);
158-
else
159-
PrintObj(t_2);
160-
}
141+
AssertionFailureWithMessage(t_2);
161142
}
162143
}
163144

@@ -170,18 +151,26 @@ static Obj HdlrFunc2 (
170151
}
171152
}
172153

154+
/* BreakOnError := false; */
155+
t_1 = False;
156+
AssGVar( G_BreakOnError, t_1 );
157+
173158
/* Assert( 2, false, "pass!\n" ); */
174159
if ( STATE(CurrentAssertionLevel) >= 2 ) {
175160
t_2 = False;
176161
t_1 = (Obj)(UInt)(t_2 != False);
177162
if ( ! t_1 ) {
178163
t_2 = MakeString( "pass!\n" );
179-
if ( t_2 != (Obj)(UInt)0 ){
180-
if ( IS_STRING_REP ( t_2 ) )
181-
PrintString1( t_2);
182-
else
183-
PrintObj(t_2);
184-
}
164+
AssertionFailureWithMessage(t_2);
165+
}
166+
}
167+
168+
/* Assert( 2, false ); */
169+
if ( STATE(CurrentAssertionLevel) >= 2 ) {
170+
t_2 = False;
171+
t_1 = (Obj)(UInt)(t_2 != False);
172+
if ( ! t_1 ) {
173+
AssertionFailure();
185174
}
186175
}
187176

@@ -223,15 +212,17 @@ static Obj HdlrFunc1 (
223212
Assert( 3, false );
224213
Assert( 2, true, "fail-D" );
225214
Assert( 2, true );
215+
BreakOnError := false;
226216
Assert( 2, false, "pass!\n" );
217+
Assert( 2, false );
227218
Print( "end of function\n" );
228219
return;
229220
end; */
230221
t_1 = NewFunction( NameFunc[2], 0, 0, HdlrFunc2 );
231222
SET_ENVI_FUNC( t_1, STATE(CurrLVars) );
232223
t_2 = NewFunctionBody();
233224
SET_STARTLINE_BODY(t_2, 1);
234-
SET_ENDLINE_BODY(t_2, 18);
225+
SET_ENDLINE_BODY(t_2, 20);
235226
SET_FILENAME_BODY(t_2, FileName);
236227
SET_BODY_FUNC(t_1, t_2);
237228
AssGVar( G_runtest, t_1 );
@@ -250,6 +241,7 @@ static Int PostRestore ( StructInitInfo * module )
250241
G_SetAssertionLevel = GVarName( "SetAssertionLevel" );
251242
G_AssertionLevel = GVarName( "AssertionLevel" );
252243
G_runtest = GVarName( "runtest" );
244+
G_BreakOnError = GVarName( "BreakOnError" );
253245

254246
/* record names used in handlers */
255247

@@ -309,7 +301,7 @@ static Int InitLibrary ( StructInitInfo * module )
309301
static StructInitInfo module = {
310302
.type = MODULE_DYNAMIC,
311303
.name = "assert.g",
312-
.crc = 47091879,
304+
.crc = -125967076,
313305
.initKernel = InitKernel,
314306
.initLibrary = InitLibrary,
315307
.postRestore = PostRestore,

tst/test-compile/assert.g.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
0
22
2
3-
pass!
4-
end of function
3+
Error, Assertion failure: pass!
4+

tst/testinstall/kernel/intrprtr.tst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ gap> Assert(0, 0, "message\n");
2222
Error, Assert: <cond> must be 'true' or 'false' (not the integer 0)
2323
gap> Assert(0, true, "message\n");
2424
gap> Assert(0, false, "message\n");
25-
message
25+
Error, Assertion failure: message
26+
2627
gap> Assert(0, false, 1); Print("\n"); # message can also be any object
2728
1
29+
Error, Assertion failure
30+
2831
gap> Assert(100, 0, "message\n");
2932
gap> Assert(100, true, "message\n");
3033
gap> Assert(100, false, "message\n");

0 commit comments

Comments
 (0)