chapel icon indicating copy to clipboard operation
chapel copied to clipboard

[Bug]:Ternary Operator in return statement causes (subsequent) stack corruption

Open damianmoz opened this issue 9 months ago • 23 comments

The following routine, without the ternary operator used in the return statement, works.

inline proc positive(z : string)
{   
    param EMPTY = '';
    
/*
 *  THIS WORKS 
 */ 
    if z == '~' then
        return  ' ';
    else if z == '+' then
        return z;
    else
        return EMPTY;
/*      
 *  THIS IS BROKEN
    return if z == '~' then ' ' else (if z == '+' then z else EMPTY);
 */     
}           

If the BROKEN return statement is enabled (and the earlier code disabled), it must do naughty things to the stack because subsequent assignment to a freshly allocated array of strings gets corrupted. Or am I using strings the wrong way?

Sample file (suitably trimmed of excess functionality) appears here:

inline proc positive(z : string)
{   
    param EMPTY = '';
    
/*
 *  THIS WORKS 
 */ 
    if z == '~' then
        return  ' ';
    else if z == '+' then
        return z;
    else
        return EMPTY;
/*      
 *  THIS IS BROKEN
    return if z == '~' then ' ' else (if z == '+' then z else EMPTY);
 */     
}           
        
proc itoa(a : int, p = '', s = '', g = 0, b = 10)
{       
    // capture the sign of the argument and if negative, use a minus sign
    // if positive, decide on whether to use plus sign, blank, or nothing
    const sign = if a < 0 then '-' else positive(p);
    // compute |a| safely if a == 2^-31
    var n = if a == min(int) then 1:uint + max(int):uint else abs(a):uint;
    // cast base 'b' to an unsigned nteger
    const _b = b:uint;
    // decode |a| into digits
    var m = 0;
    var t : [0..#20] uint(8);

    do
    {
        const j = n / _b;

        t[m] = (n - _b * j):uint(8); n = j; m += 1;
    }
    while n > 0;

    // writeln("digits ", t[0..#m]);

    // reverse the order
    {
        param NUL = '';
        const digit = "0123456789abcdef";
        var rt : [0..m] string;

        rt[0] = sign;
        // BETWEEN HERE and the JOIN below, parts of rt[0] get clobbered
        // writeln("rt[0] prior to assembly ", rt[0..#1]);
        // sometime during this loop, rt[0] is sometimes overwritten
        for i in 1..m
        {
            rt[i] = digit[t[m - i]];
        }
        // writeln("rt[0] after an assembly ", rt[0..#1]);
        // NORMALLY THIS is ''.join(rt)
        return '|'.join(rt);
    }
}

const _2p32 = (1 << 32):real(64);

// writeln(_2p32 * _2p32);
writeln(itoa(1234567, "+"));
writeln(itoa(-1234567, "+"));
writeln(itoa(max(int), "+"), " <--- MAXINT");
writeln(itoa(min(int), "+"));
writeln(itoa(max(int), "+", "_", 6), " <--- +MAXINT - not quite!!");
writeln(itoa(-max(int), "+", "_", 6), " <--- -MAXINT - not quite!!");
// writeln(itoa(0));
// writeln(itoa(0, "+"));
// writeln(itoa(1234567, "+", ",", 3));
// writeln(itoa(0x1234567f, s = "_", g = 2, b = 16));
// writeln(itoa(min(int), s = "_", g = 2, b = 16));
exit(0);

Tagging @mppf and @vasslitvinov. Thanks.

The above code is supposed to encode an integer in base 10 or base 16. The functionality removed for purposes here is related to handling grouping separators, e.g. a comma every three digits or an underscore every two digits in a hexadecimal number.

damianmoz avatar May 14 '24 02:05 damianmoz