Skip to content

Commit

Permalink
fa() stop checking for invalid arg; symbis() don't zap for public ass…
Browse files Browse the repository at this point in the history
…ignment; jtxdefn save a test
  • Loading branch information
HenryHRich committed Jan 7, 2025
1 parent a274583 commit f4f4efd
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 25 deletions.
8 changes: 4 additions & 4 deletions jsrc/cx.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
// 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
// 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
// sure that all assignments-in-place have had the usercount fully restored
#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; \
#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; \
SETTRACK \
if(likely(!(attnval+(NPGpysfmtdl&128+16))))z=parsea(queue,m); \
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;} \
Expand Down Expand Up @@ -125,7 +125,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k;
// an incumbent value, we remove it. We also zap the value we install, just as in any normal assignment
L *asym=&SYMORIGIN[cv->indexsym]; // pointer symbol-table entry, index then item
ASSERT(!(asym->flag&LREADONLY),EVRO) // it had better not be readonly now
fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
if(unlikely(QCWORD(asym->fval)!=0))fa(QCWORD(asym->fval)); // if there is an incumbent value, discard it
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
ACINITUNPUSH(xx); asym->fval=SETNAMED(MAKEFVAL(xx,ATYPETOVALTYPE(INT))); // raise usecount, as local name; install as value of xyz_index
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
Expand All @@ -138,7 +138,7 @@ static B jtforinit(J jt,CDATA*cv,A t){A x;C*s,*v;I k;
// We store the block in 2 places: cv and symp.val. We ra() once for each place
// If there is an incumbent value, discard it
asym=&SYMORIGIN[cv->itemsym]; A val=QCWORD(asym->fval); // stored reference address; incumbent value there
fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error
if(unlikely(val!=0))fa(val); asym->fval=0; // free the incumbent if any, clear val in symbol in case of error
// Calculate the item size and save it
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
// 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
Expand Down Expand Up @@ -169,7 +169,7 @@ static CDATA* jtunstackcv(J jt,CDATA*cv,I assignvirt){
}
}
}
fa(cv->t); // decr the for/select value, protected at beginning. NOP if it is 0
if(likely(cv->t!=0))fa(cv->t); // decr the for/select value, protected at beginning. NOP if it is 0
cv=cv->bchn; // go back to previous stack level
R cv;
}
Expand Down
2 changes: 1 addition & 1 deletion jsrc/dstop.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ F1(jtdbstops){
RZ(w=vs(w));
if(AN(w)){RZ(ras(w));}else w=0; // protect w if it is nonempty; if empty, convert to null
WRITELOCK(JT(jt,dblock)) A stops=JT(jt,dbstops); JT(jt,dbstops)=w; WRITEUNLOCK(JT(jt,dblock)) // swap addresses under lock
fa(stops); // undo the ra() done when value was stored - null is ok
if(stops!=0)fa(stops); // undo the ra() done when value was stored
R mtm;
}

Expand Down
2 changes: 1 addition & 1 deletion jsrc/j.h
Original file line number Diff line number Diff line change
Expand Up @@ -1024,12 +1024,12 @@ struct jtimespec jmtfclk(void); //'fast clock'; maybe less inaccurate; intended
#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
// 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.
#define READLOCK(lock) {S prev; if(unlikely(((prev=__atomic_fetch_add(&lock,1,__ATOMIC_ACQ_REL))&(S)-WLOCKBIT)!=0))readlock(&lock,prev);}
#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits
#if WLOCKBIT==0x8000
#define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_or(&lock,(S)WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);}
#else
#define WRITELOCK(lock) {S prev; if(unlikely((prev=__atomic_fetch_add(&lock,WLOCKBIT,__ATOMIC_ACQ_REL))!=0))writelock(&lock,prev);}
#endif
#define READUNLOCK(lock) __atomic_fetch_sub(&lock,1,__ATOMIC_ACQ_REL); // decrement the read bits
#define WRITEUNLOCK(lock) __atomic_fetch_and(&lock,WLOCKBIT-1, __ATOMIC_ACQ_REL); // clear all the write bits
#else
#define CCOMMON(x,pref,err) (x)
Expand Down
6 changes: 3 additions & 3 deletions jsrc/ja.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,11 @@
// 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
#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
// FAOWED becomes ..0f0; perm becomes .0p00 where perhaps p00 has +-1 added; 0f0>p00 means 'FAOWED & not PERMANENT'
#define fajt(jt,x) {if(likely((x)!=0))faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})}
#define fajt(jt,x) {/*if(likely((x)!=0))obsolete*/faaction(jt,(x),{if(MEMAUDIT&2)audittstack(jt);})}

#define fa(x) fajt(jt,(x)) // when the block will usually NOT be deleted
#define falikely(x) fa(x) // when the block will usually be deleted (not used yet)
#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)
#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)
#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
// when x is known to be valid and usecount has gone to 0
#define fanano0(x) faaction(jt,(x),;)
Expand Down Expand Up @@ -923,7 +923,7 @@ extern void jfree4gmp(void*,size_t);
#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
#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
#define raposacv(x) {I c=AC(x); if(likely(!ACISPERM(c))){__atomic_fetch_add(&AC(x),1,__ATOMIC_ACQ_REL);}} // ACV is guaranteed recursive
#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
#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
#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
#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
// 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
Expand Down
1 change: 1 addition & 0 deletions jsrc/je.h
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,7 @@ extern void jtcopyTT(J, void *, void *, I, I,I);
#endif
extern A cw57rep(J,A);
extern I infererrtok(J);
extern I johnson(I);
extern A jtac1(J,AF);
extern A jtac2(J,AF);
extern B jtadd2(J,F,F,C*);
Expand Down
2 changes: 1 addition & 1 deletion jsrc/p.c
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ rejectfrag:;
// 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.
// If final assignment was local this can't happen, and we do the fa
jt->parserstackframe = oframe; // pop the parser frame-stack before tpushna, which may fail
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
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
}else{ // If there was an error during execution or name-stacking, exit with failure. Error has already been signaled. Remove zombiesym. Repurpose pt0ecam
failparsestack: // here we encountered an error during stacking. The error was processed using an old stack, so its spacing is wrong.
// 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
Expand Down
21 changes: 13 additions & 8 deletions jsrc/s.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ A jtprobequiet(J jt,A a){A g;

// assign symbol: assign name a in symbol table g to the value w (but g is ignored if a is a locative)
// Result is 0 if error, otherwise low 2 bits are x1 = final assignment, 1x = local assignment, others garbage
// flags set in jt: bit 0=this is a final assignment;
// flags set in jt: bit 0=this is a final assignment; bit 1 always 0
I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
ARGCHK2(a,w);
I anmf=NAV(a)->flag; // fetch flags for the name
Expand Down Expand Up @@ -739,20 +739,22 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
if((AR(g)&ARLOCALTABLE)!=0){ // if assignment to a local table (which might not be jt->locsyms)
I4 symx=NAV(a)->symx; // fetch the symbol slot assigned to this name (0 if none)
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
g=0; // indicate we have no lock to clear
g=(A)((I)jtinplace|-JTASGNWASLOCAL); // indicate local assignment (we have no lock to clear), remember final assignment
valtype|=QCNAMED|(LOCALRA?QCRAREQD:REPSGN(wt)&QCRAREQD); // enter QCSYMVAL semantics; ra needed if sparse
}else{ // global table
SYMRESERVE(1) // before we go into lock, make sure we have a symbol to assign to
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
valtype|=QCNAMED|QCRAREQD; // must flag local/global type in symbol
e=probeis(a, g); // get the symbol address to use, old or new. This returns holding a lock on the locale
// if we are writing to a non-local table, update the table's Bloom filter.
g=(A)((I)g+((I)jtinplace&JTFINALASGN)); // flags in g: copy final-assignment flag, keep glocal-table flag 0 indicating free needed
BLOOMSET(bloombase,chainno); // g is under lock. This modifies the shared memory every time - might be better to write only when chain is empty
// A couple of debugging flags are set during assignment. We don't bother for local names
if(unlikely(JT(jt,stch)!=0))e->flag|=LCH; // update 'changed' flag if enabled - needed only for globals
e->sn=jt->currslistx; // Save the script in which this name was defined - meaningful only for globals
}
// ****** 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 *******
// ****** 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
// of g are exit flags: bit0=final assignment, bit 1=local assignment. If local assignment, g=-2 (not final) or -1 (final) *******

A x=e->fval; // if x is 0, this name has not been assigned yet; if nonzero, x points to the incumbent value
// 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)
Expand All @@ -775,9 +777,11 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
// Virtual values were realized earlier, and usecounts guaranteed recursive
// If the value is abandoned inplaceable, we can just zap it and set its usecount to 1
// SPARSE nouns must never be inplaceable, because their components are not
if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value
// obsolete if((SGNIF(jtinplace,JTFINALASGNX)&AC(w))<0){ // if final assignment of abandoned value, in local table
if(((I)g+SGNTO0(AC(w)))==0){ // if final assignment of abandoned value, in local table (g=-1 and inplace bit set)
// 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
// also, if the name is global it may vanish, along with the value, at any time, so we
// 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.
// 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.
AFLAGORLOCAL(w,AFKNOWNNAMED); // indicate the value is in a name. We do this to allow virtual extension.
// very often a final assignment will assign the abandoned result of the last computation. These values accumulate on the tstack and have to
// 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
Expand All @@ -802,15 +806,16 @@ I jtsymbis(J jt,A a,A w,A g){F2PREFIP;
}
}else x=0; // repurpose x to be the value needing fa
// x here is the value that needs to be freed
if(g!=0)WRITEUNLOCK(g->lock)else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock);
// obsolete else jtinplace=(J)((I)jtinplace|JTASGNWASLOCAL); // if global, release lock; else indic local in return
// ************* we have released the write lock
// 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,
// in case the new value was being protected by the old (ex: n =. >n).
// It is the responsibility of parse to keep the usecount of a named value raised until it has come out of execution
SYMVALFA2(x); // if the old value needs to be traversed in detail, do it now outside of lock (subroutine call)
R (I)jtinplace; // good return, with bit 0 set if final assignment, bit 1 if local
R (I)g; // good return, with bit 0 set if final assignment, bit 1 if local
exitlock: // error exit
if(g!=0)WRITEUNLOCK(g->lock)
if(!((I)g&JTASGNWASLOCAL))WRITEUNLOCK(QCWORD(g)->lock)
R 0;
} /* a: name; w: value; g: symbol table */

Expand Down
2 changes: 1 addition & 1 deletion jsrc/vcat.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ A jtapip(J jt, A a, A w){F2PREFIP;A h;
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)
// 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
// 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)
if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0) {
if(EXTENDINPLACENJA(a,w) && ((at&(DIRECT|BOX))|(AT(w)&SPARSE))>0){
// collect some values into a flags register
#define FGLGK 0x7
#define FGVIRTREQDX 3 // if virtual extension required
Expand Down
2 changes: 1 addition & 1 deletion jsrc/vrand.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ F1(jtrngseeds){I k,r;
if(r){
// w is not an atom. the RNG had better be Mersenne Twister. Initialize using w, and save the w list
ASSERT(1==r&&MTI==jt->rngdata->rng,EVRANK);
RZ(ras(w)); fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers
RZ(ras(w)); if(jt->rngdata->rngseed!=0)fa(jt->rngdata->rngseed); jt->rngdata->rngseed=w; // note ra before fa, in case same buffers
mt_init_by_array(AV(w),AN(w));
}else switch(jt->rngdata->rng){
// atomic w. We can use that for any generator. Choose the current one.
Expand Down
12 changes: 11 additions & 1 deletion test/g320ip.ijs
Original file line number Diff line number Diff line change
Expand Up @@ -602,9 +602,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@, ''b'''
f =: 3 : 0
9!:53 (1)
a =: 10000#'c'
assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@(({.''b'') ,~ ]) a'
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > err =: 7!:2 'a =: {.@(({.''b'') ,~ ]) a'
a =: 10000#'c'
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@, ''b'''
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@, ''b'''
1
)
Expand All @@ -625,9 +628,12 @@ IGNOREIFFVI 3000 < 7!:2 'a =: a ]@:, ''b'''
f =: 3 : 0
9!:53 (1)
a =: 10000#'c'
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > 7!:2 'a =: {.@:(({.''b'') ,~ ]) a'
a =: 10000#'c'
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > 7!:2 'a =: a ]@:, ''b'''
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 < 7!:2 'a =: a unsafename@:, ''b'''
1
)
Expand All @@ -644,8 +650,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a' NB. realize any vir
f =: 3 : 0
9!:53 (1)
a =: i. 1000
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ ]) a'
a =: i. 1000
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&([: >@{.@< 5 ,~ unsafename) a'
1
)
Expand All @@ -661,8 +669,10 @@ IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a'
f =: 3 : 0
9!:53 (1)
a =: i. 1000
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 > 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ ]) a'
a =: i. 1000
for. i. 4 do. 0 end. NB. ensure tpop
assert. IGNOREIFFVI 3000 < 7!:2 'a =: ($0) {.&:([: >@{.@< 5 ,~ unsafename) a'
1
)
Expand Down
8 changes: 4 additions & 4 deletions test/gtdot5.ijs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ EMPTY

t1=: 3 : 0
ALL=: 0$0
pyx=. (p1 t.'')"0 i. 300
pyx=. (p1 t.'')"0 i. 100
1:&>pyx
echo #~.ALL
echo #/.~ ALL
Expand All @@ -50,7 +50,7 @@ EMPTY

t2=: 3 : 0
ALL=: 0 0$0
pyx=. (p2 t.'')"0 i. 300
pyx=. (p2 t.'')"0 i. 100
1:&>pyx
echo #~.ALL
echo #/.~ ALL
Expand All @@ -74,13 +74,13 @@ end.
)

t3=: 3 : 0
pyx=. (p3 t.'')"0 i. 300
pyx=. (p3 t.'')"0 i. 100
echo ;pyx
EMPTY
)

t4=: 3 : 0
pyx=. (p4 t.'')"0 i. 300
pyx=. (p4 t.'')"0 i. 100
echo ;pyx
EMPTY
)
Expand Down

0 comments on commit f4f4efd

Please sign in to comment.