Skip to content

Commit f4f4efd

Browse files
committed
fa() stop checking for invalid arg; symbis() don't zap for public assignment; jtxdefn save a test
1 parent a274583 commit f4f4efd

File tree

11 files changed

+41
-25
lines changed

11 files changed

+41
-25
lines changed

jsrc/cx.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
// popstmt is used to call tpop if the tpop stack is not empty; asgstmt is issued after the parse, where a variable can be invalidated to prevent it from being saved over calls
6666
// we call only when the stack has values on it. We do our best during assignment to retrace the stack rather than zap it. If the stack has any values, we must call to make
6767
// sure that all assignments-in-place have had the usercount fully restored
68-
#define parseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); if((UI)jt->tnextpushp!=(UI)old){popstmt} S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \
68+
#define parseline(z,popstmt,asgstmt) {tcesx=CWTCESX2(cwsent,ic); popstmt S attnval=__atomic_load_n((S*)JT(jt,adbreakr),__ATOMIC_ACQUIRE); A *queue=&cwsent[(tcesx>>32)&TCESXSXMSK]; I m=(tcesx-(tcesx>>32))&TCESXSXMSK; \
6969
SETTRACK \
7070
if(likely(!(attnval+(NPGpysfmtdl&128+16))))z=parsea(queue,m); \
7171
else {if(jt->sitop&&jt->sitop->dclnk&&jt->sitop->dclnk->dctype==DCCALL)jt->sitop->dclnk->dcix=~ic; z=parsex(queue,m,CWSOURCE(cwsent,CNSTOREDCW,ic),(NPGpysfmtdl&128+16)?jt->sitop->dclnk:0); if(!(jt->uflags.trace&TRACEDB))NPGpysfmtdl&=~2;} \
@@ -125,7 +125,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k;
125125
// an incumbent value, we remove it. We also zap the value we install, just as in any normal assignment
126126
L *asym=&SYMORIGIN[cv->indexsym]; // pointer symbol-table entry, index then item
127127
ASSERT(!(asym->flag&LREADONLY),EVRO) // it had better not be readonly now
128-
fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
128+
if(unlikely(QCWORD(asym->fval)!=0))fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
129129
A xx; GAT0(xx,INT,1,0); IAV0(xx)[0]=-1; AFLAGINIT(xx,AFRO) // -1 is the iteration number if there are no iterations; mark value RO to prevent xxx_index =: xxx_index + 1 from changing inplace
130130
ACINITUNPUSH(xx); asym->fval=SETNAMED(MAKEFVAL(xx,ATYPETOVALTYPE(INT))); // raise usecount, as local name; install as value of xyz_index
131131
rifv(t); // it would be work to handle virtual t, because you can't just ra() a virtual, as virtuals are freed only from the tpop stack. So we wimp out & realize. note we can free from a boxed array now
@@ -138,7 +138,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k;
138138
// We store the block in 2 places: cv and symp.val. We ra() once for each place
139139
// If there is an incumbent value, discard it
140140
asym=&SYMORIGIN[cv->itemsym]; A val=QCWORD(asym->fval); // stored reference address; incumbent value there
141-
fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error
141+
if(unlikely(val!=0))fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error
142142
// Calculate the item size and save it
143143
I isz; I r=AR(t)-((UI)AR(t)>0); PROD(isz,r,AS(t)+1); I tt=AT(t); cv->itemsiz=isz<<bplg(tt); // rank of item; number of bytes in an item
144144
// Allocate a virtual block. Zap it, fill it in, make noninplaceable. Point it to the item before the data, since we preincrement in the loop
@@ -169,7 +169,7 @@ static CDATA* jtunstackcv(J jt,CDATA*cv,I assignvirt){
169169
}
170170
}
171171
}
172-
fa(cv->t); // decr the for/select value, protected at beginning. NOP if it is 0
172+
if(likely(cv->t!=0))fa(cv->t); // decr the for/select value, protected at beginning. NOP if it is 0
173173
cv=cv->bchn; // go back to previous stack level
174174
R cv;
175175
}

jsrc/dstop.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ F1(jtdbstops){
7878
RZ(w=vs(w));
7979
if(AN(w)){RZ(ras(w));}else w=0; // protect w if it is nonempty; if empty, convert to null
8080
WRITELOCK(JT(jt,dblock)) A stops=JT(jt,dbstops); JT(jt,dbstops)=w; WRITEUNLOCK(JT(jt,dblock)) // swap addresses under lock
81-
fa(stops); // undo the ra() done when value was stored - null is ok
81+
if(stops!=0)fa(stops); // undo the ra() done when value was stored
8282
R mtm;
8383
}
8484

jsrc/j.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1024,12 +1024,12 @@ struct jtimespec jmtfclk(void); //'fast clock'; maybe less inaccurate; intended
10241024
#define CCOMMON(x,pref,err) ({A res=(x); pref if(unlikely((AT(res)&PYX)!=0)){if(unlikely((res=jtpyxval(jt,res))==0))err;} res; }) // extract & resolve contents; execute err if error in resolution x may have side effects
10251025
// since most uses of contents check the type first, it would be good to have the type loaded at any finish. But the compiler doesn't do that.
10261026
#define READLOCK(lock) {S prev; if(unlikely(((prev=__atomic_fetch_add(&lock,1,__ATOMIC_ACQ_REL))&(S)-WLOCKBIT)!=0))readlock(&lock,prev);}
1027+
#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits
10271028
#if WLOCKBIT==0x8000
10281029
#define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_or(&lock,(S)WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);}
10291030
#else
10301031
#define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_add(&lock,WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);}
10311032
#endif
1032-
#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits
10331033
#define WRITEUNLOCK(lock) __atomic_fetch_and(&lock,WLOCKBIT-1, __ATOMIC_ACQ_REL); // clear all the write bits
10341034
#else
10351035
#define CCOMMON(x,pref,err) (x)

jsrc/ja.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -358,11 +358,11 @@
358358
// faifowed does the free only if the block is marked FAOWED in stkf. These may have been stacked by local names, if the stack was later protected
359359
#define faifowed(x,Zc,tt,stkf) {if(unlikely(tt<0) || ((((Zc>>(ACPERMANENTX-(STKFAOWEDX+1)))&(4*STKFAOWED-1))<((I)stkf&STKFAOWED)) && unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2)))jtfamf(jt,x,tt);} // call if sparse or ending; never touch a PERM
360360
// FAOWED becomes ..0f0; perm becomes .0p00 where perhaps p00 has +-1 added; 0f0>p00 means 'FAOWED & not PERMANENT'
361-
#define fajt(jt,x) {if(likely((x)!=0))faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})}
361+
#define fajt(jt,x) {/*if(likely((x)!=0))obsolete*/faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})}
362362

363363
#define fa(x) fajt(jt,(x)) // when the block will usually NOT be deleted
364364
#define falikely(x) fa(x) // when the block will usually be deleted (not used yet)
365-
#define fatype(x,tt) {if((x)==0)SEGFAULT; if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // scaf when type is known (i. e. NAME)
365+
#define fatype(x,tt) {if(likely(AC(x)<=ACUC1)){jtfamf(jt,x,tt);}else{if(likely(!ACISPERM(AC(x)))){if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,tt);}}} // when type is known (i. e. NAME)
366366
#define fanamedacv(x) {I Zc=AC(x); if(likely(!ACISPERM(Zc)))if(unlikely(__atomic_fetch_sub(&AC(x),1,__ATOMIC_ACQ_REL)<2))jtfamf(jt,x,AT(x));} // block is known to be ACV, recursive, AC>0, and almost always AC>1
367367
// when x is known to be valid and usecount has gone to 0
368368
#define fanano0(x) faaction(jt,(x),;)
@@ -923,7 +923,7 @@ extern void jfree4gmp(void*,size_t);
923923
#define rapos(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0
924924
#define raposlocal(x,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(ISSPARSE(AT(x))))sv=jtra((x),SPARSE,sv);}} // better a misbranch than an atomic instruction if c<0
925925
#define raposacv(x) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL);}} // ACV is guaranteed recursive
926-
#define raposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){ACADD(x,1); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
926+
#define raposgblqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
927927
#define raposlocalqcgsv(x,qct,sv) {I c=AC(x); if(likely(!ACISPERM(c))){if(c==1)AC(x)=ACUC2;else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); if(unlikely(qct==VALTYPESPARSE))sv=jtra((x),SPARSE,sv);}} // must be recursive usecount but may be sparse
928928
#define rarecur(x) {I c=AC(x); if(likely(!ACISPERM(c))){if(c<0)AC(x)=(I)((UI)c+(ACINPLACE+ACUC1));else __atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL); I tt=AT(x); if(unlikely(ISSPARSE(tt)))x=jtra((x),(tt),x);}} // ra but recursibles know to be recursive
929929
// NOTE that every() produces blocks with usecount 0x8..2 (if a recursive block has pristine contents whose usecount is 2); if we ZAP that it must go to 2

jsrc/je.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ extern void jtcopyTT(J, void *, void *, I, I,I);
681681
#endif
682682
extern A cw57rep(J,A);
683683
extern I infererrtok(J);
684+
extern I johnson(I);
684685
extern A jtac1(J,AF);
685686
extern A jtac2(J,AF);
686687
extern B jtadd2(J,F,F,C*);

jsrc/p.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,7 @@ rejectfrag:;
10601060
// by another thread while we are using the value. The case is very rare but we test for it, so we have to make it work.
10611061
// If final assignment was local this can't happen, and we do the fa
10621062
jt->parserstackframe = oframe; // pop the parser frame-stack before tpushna, which may fail
1063-
if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} else tpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush
1063+
if(unlikely(ISSTKFAOWED(z))){if(pt0ecam&JTASGNWASLOCAL){faowed(QCWORD(z),AC(QCWORD(z)),AT(QCWORD(z)))} else tpushna(QCWORD(z));} // if the result needs a free, do it, possibly deferred via tpush
10641064
}else{ // If there was an error during execution or name-stacking, exit with failure. Error has already been signaled. Remove zombiesym. Repurpose pt0ecam
10651065
failparsestack: // here we encountered an error during stacking. The error was processed using an old stack, so its spacing is wrong.
10661066
// we set the error word# for the failing word and then resignal the error to get the spacing right and call eformat to annotate it

jsrc/s.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ A jtprobequiet(J jt,A a){A g;
691691

692692
// assign symbol: assign name a in symbol table g to the value w (but g is ignored if a is a locative)
693693
// Result is 0 if error, otherwise low 2 bits are x1 = final assignment, 1x = local assignment, others garbage
694-
// flags set in jt: bit 0=this is a final assignment;
694+
// flags set in jt: bit 0=this is a final assignment; bit 1 always 0
695695
I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
696696
ARGCHK2(a,w);
697697
I anmf=NAV(a)->flag; // fetch flags for the name
@@ -739,20 +739,22 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
739739
if((AR(g)&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms)
740740
I4 symx=NAV(a)->symx; // fetch the symbol slot assigned to this name (0 if none)
741741
e=likely((SGNIF(AR(g),ARLCLONEDX)|(symx-1))>=0)?SYMORIGIN+(I)symx:probeislocal(a,g); // local symbol given and we are using the original table: use the symbol. Otherwise, look up and reserve 1 symbol
742-
g=0; // indicate we have no lock to clear
742+
g=(A)((I)jtinplace|-JTASGNWASLOCAL); // indicate local assignment (we have no lock to clear), remember final assignment
743743
valtype|=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse
744744
}else{ // global table
745745
SYMRESERVE(1) // before we go into lock, make sure we have a symbol to assign to
746746
C *bloombase=BLOOMBASE(g); I chainno=SYMHASH(NAV(a)->hash,AN(g)-SYMLINFOSIZE); // get addr of Bloom filter and the location we are storing to
747747
valtype|=QCNAMED|QCRAREQD; // must flag local/global type in symbol
748748
e=probeis(a, g); // get the symbol address to use, old or new. This returns holding a lock on the locale
749749
// if we are writing to a non-local table, update the table's Bloom filter.
750+
g=(A)((I)g+((I)jtinplace&JTFINALASGN)); // flags in g: copy final-assignment flag, keep glocal-table flag 0 indicating free needed
750751
BLOOMSET(bloombase,chainno); // g is under lock. This modifies the shared memory every time - might be better to write only when chain is empty
751752
// A couple of debugging flags are set during assignment. We don't bother for local names
752753
if(unlikely(JT(jt,stch)!=0))e->flag|=LCH; // update 'changed' flag if enabled - needed only for globals
753754
e->sn=jt->currslistx; // Save the script in which this name was defined - meaningful only for globals
754755
}
755-
// ****** if g is a global table, we have a write lock on the locale, which we must release in any error paths. g=0 otherwise *******
756+
// ****** if g is a global table, bit2=0 and we have a write lock on the locale, which we must release in any error paths. The low 2 bits
757+
// of g are exit flags: bit0=final assignment, bit 1=local assignment. If local assignment, g=-2 (not final) or -1 (final) *******
756758

757759
A x=e->fval; // if x is 0, this name has not been assigned yet; if nonzero, x points to the incumbent value
758760
// If we are assigning the same data block that's already there, don't bother with changing use counts or anything else (assignment-in-place)
@@ -775,9 +777,11 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
775777
// Virtual values were realized earlier, and usecounts guaranteed recursive
776778
// If the value is abandoned inplaceable, we can just zap it and set its usecount to 1
777779
// SPARSE nouns must never be inplaceable, because their components are not
778-
if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value
780+
// obsolete if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value, in local table
781+
if(((I)g+SGNTO0(AC(w)))==0){ // if final assignment of abandoned value, in local table (g=-1 and inplace bit set)
779782
// We can zap abandoned nouns. But only if they are final assignment: something like nm:_ [ nm=. 4+4 would free the active block if we zapped, when the FAOWED was applied
780-
// also, if the name is global it may vanish, along with the value, at any time, so we
783+
// the idea is that the assignment will protect the value; this is true for local assignment but not for global, because another thread may free the name immediately.
784+
// If we could be sure that the result of the sentence would never be inspected we could return a value that might be freed, but without such a guarantee we can zap only local assignment.
781785
AFLAGORLOCAL(w,AFKNOWNNAMED); // indicate the value is in a name. We do this to allow virtual extension.
782786
// very often a final assignment will assign the abandoned result of the last computation. These values accumulate on the tstack and have to
783787
// be popped off every now & then in jtxdefn, since there is nothing else to pop them. We detect this important case, in which the zaploc of the
@@ -802,15 +806,16 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
802806
}
803807
}else x=0; // repurpose x to be the value needing fa
804808
// x here is the value that needs to be freed
805-
if(g!=0)WRITEUNLOCK(g->lock)else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
809+
if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock);
810+
// obsolete else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
806811
// ************* we have released the write lock
807812
// If this is a reassignment, we need to decrement the use count in the old value, since that value is no longer used. Do so after the new value is raised,
808813
// in case the new value was being protected by the old (ex: n =. >n).
809814
// It is the responsibility of parse to keep the usecount of a named value raised until it has come out of execution
810815
SYMVALFA2(x); // if the old value needs to be traversed in detail, do it now outside of lock (subroutine call)
811-
R (I)jtinplace; // good return, with bit 0 set if final assignment, bit 1 if local
816+
R (I)g; // good return, with bit 0 set if final assignment, bit 1 if local
812817
exitlock: // error exit
813-
if(g!=0)WRITEUNLOCK(g->lock)
818+
if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock)
814819
R 0;
815820
} /* a: name; w: value; g: symbol table */
816821

jsrc/vcat.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ A jtapip(J jt, A a, A w){F2PREFIP;A h;
357357
I lgk=bplg(at); I lgatomsini=MAX(LGSZI-lgk,0); // lg of size of atom of a; lg of number of atoms in an I (0 if <1)
358358
// Because the test for inplaceability is rather lengthy, start with a quick check of the atom counts. If adding the atoms in w to those in a
359359
// would push a over a power-of-2 boundary, skip the rest of the testing. We detect this by absence of carry out of the high bit (inside EXTENDINPLACE)
360-
if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0) {
360+
if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0){
361361
// collect some values into a flags register
362362
#define FGLGK 0x7
363363
#define FGVIRTREQDX 3 // if virtual extension required

jsrc/vrand.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,7 @@ F1(jtrngseeds){I k,r;
570570
if(r){
571571
// w is not an atom. the RNG had better be Mersenne Twister. Initialize using w, and save the w list
572572
ASSERT(1==r&&MTI==jt->rngdata->rng,EVRANK);
573-
RZ(ras(w)); fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers
573+
RZ(ras(w)); if(jt->rngdata->rngseed!=0)fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers
574574
mt_init_by_array(AV(w),AN(w));
575575
}else switch(jt->rngdata->rng){
576576
// atomic w. We can use that for any generator. Choose the current one.

test/g320ip.ijs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@, ''b'''
602602
f =: 3 : 0
603603
9!:53 (1)
604604
a =: 10000#'c'
605-
assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@(({.''b'') ,~ ]) a'
605+
for. i. 4 do. 0 end. NB. ensure tpop
606+
assert. IGNOREIFFVI 3000 > err =: 7!:2 'a =: {.@(({.''b'') ,~ ]) a'
606607
a =: 10000#'c'
608+
for. i. 4 do. 0 end. NB. ensure tpop
607609
assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@, ''b'''
610+
for. i. 4 do. 0 end. NB. ensure tpop
608611
assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@, ''b'''
609612
1
610613
)
@@ -625,9 +628,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@:, ''b'''
625628
f =: 3 : 0
626629
9!:53 (1)
627630
a =: 10000#'c'
631+
for. i. 4 do. 0 end. NB. ensure tpop
628632
assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@:(({.''b'') ,~ ]) a'
629633
a =: 10000#'c'
634+
for. i. 4 do. 0 end. NB. ensure tpop
630635
assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@:, ''b'''
636+
for. i. 4 do. 0 end. NB. ensure tpop
631637
assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@:, ''b'''
632638
1
633639
)
@@ -644,8 +650,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a' NB. realize any vir
644650
f =: 3 : 0
645651
9!:53 (1)
646652
a =: i. 1000
653+
for. i. 4 do. 0 end. NB. ensure tpop
647654
assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a'
648655
a =: i. 1000
656+
for. i. 4 do. 0 end. NB. ensure tpop
649657
assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ unsafename) a'
650658
1
651659
)
@@ -661,8 +669,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a'
661669
f =: 3 : 0
662670
9!:53 (1)
663671
a =: i. 1000
672+
for. i. 4 do. 0 end. NB. ensure tpop
664673
assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a'
665674
a =: i. 1000
675+
for. i. 4 do. 0 end. NB. ensure tpop
666676
assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ unsafename) a'
667677
1
668678
)

0 commit comments

Comments
 (0)