pawn-lang / compiler

Pawn compiler for SA-MP with bug fixes and new features - runs on Windows, Linux, macOS
Other
306 stars 72 forks source link

operator overloads calling other operator overloads cause assertions to fail #259

Open YashasSamaga opened 6 years ago

YashasSamaga commented 6 years ago
Tag:operator-(Tag:a, Tag:b) { return Tag:10; }
Tag:operator+(Tag:a, Tag:b) {  return a - b; }
main () {
    new Tag:x, Tag:y;
    x += y;
}

The assertions which fail are inside max_stacksize_recurse: assert(sym->refer[i]->ident==iFUNCTN); (line 4637, sc1.c) assert(sym->ident==iFUNCTN); (line 4630, sc1.c)

The list of referrers sym->reffers appears to be filled with garbage. http://tinyimg.io/i/J91Ue4e.png

The + operator seems to be fine. The garbage is present for - operator only.

I am not sure why the compiler does not crash in release build.

Y-Less commented 6 years ago

I've had the release compiler crash a few times while playing with destructors and other operators. Every time changing literally nothing and instantly recompiling fixed it. I could not reproduce the crash reliably ever.

YashasSamaga commented 6 years ago

Do you still have the code with you?

It makes sense that the compiler crashes sometimes and sometimes not as accessing garbage locations (which is what I think is causing this) is undefined behavior.

Y-Less commented 6 years ago

I might do. But as I said, because it was not consistent I can't easilly confirm that the code I still have is triggering it unless I get lucky.

Y-Less commented 6 years ago

OK, I just spammed F5 on this code and it eventually crashed:

// This is a comment
// uncomment the line below if you want to write a filterscript
//#define FILTERSCRIPT

#include <a_samp>
#include <amx_assembly\amx_memory>

new bool:DESTRUCT_Tag = true;

#define __COMPILER_ALL_DESTRUCTORS 0

#if __COMPILER_ALL_DESTRUCTORS
    #define EXPLICIT_RETURN_REQUIRED
#else
    #define EXPLICIT_RETURN_REQUIRED , ExplicitReturnRequired:explicit_return_required = ExplicitReturnRequired:0

    stock operator~(ExplicitReturnRequired:dummy[], size)
    {
        #pragma unused dummy, size
    }
#endif

Tag:operator=(Tag:a);

//#pragma option -a

//Func2(b[3][4])
//{
//}

Tag:Func0()
{
    new Tag:a = Tag:0;
    return a;
}

Tag:Func1(Tag:b EXPLICIT_RETURN_REQUIRED)
{
//  static Tag:a[2][3][4];
//  Func2(a[0]);
    if (DESTRUCT_Tag)
        return b;
//  printf("%d", _:b);
    return b;
}

Tag:Func2(Tag:b EXPLICIT_RETURN_REQUIRED)
{
//  printf("%d", _:b);
    return;
}

#define DESTRUCTOR_REQUIRED(%0)%1) %0%1 EXPLICIT_RETURN_REQUIRED

operator~(Tag:a[], size)
{
    if (DESTRUCT_Tag)
        printf("bye: %d %d %d", size, ref(_:a), _:a[0]);
    DESTRUCT_Tag = true;
}

Tag:operator=(Tag:a)
{
    printf("assign %d", _:a);
    DESTRUCT_Tag = false;
    return a;
}

main()
{
    printf("0");
    new Tag:a;
    new Tag:b[10];
    ++a;
    printf("1");
    Func1(Func0());
    Func2(Func1(a));
    printf("2");
    Func2(Func1(Tag:0));
    printf("3");
    --a;
    return;
}

I'm not too inclined to reduce it further, since it is hard to confirm on any given change.

Y-Less commented 6 years ago

Despite that, I did it anyway. This is the most minimal repro I could manage, if there is anything smaller, everything I tested didn't crash within 40 compiles, while the mean was about 1 in 10 compiles crashing:

ref(...)
{
    #emit load.s.pri 12
    #emit retn
}

Tag:operator=(Tag:a);

Tag:Func0()
{
    new Tag:a = Tag:0;
    return a;
}

operator~(Tag:a[], size)
{
    ref(_:a);
}

Tag:operator=(Tag:a)
{
    return a;
}

main()
{
    Func0();
}

(Not even any includes needed).

Y-Less commented 6 years ago

Got it down even further:

Func(a[])
{
}

Tag:operator=(Tag:a);

operator~(Tag:a[], size)
{
    Func(_:a);
}

Tag:operator=(Tag:a)
{
    return a;
}

main()
{
    new Tag:a = Tag:0;
}

I just use pawno and spam ESC/F5 repeatedly. It now takes about 20 compiles to crash, but does. The most I got at one stage (maybe I should have posted that stage explicitly) was nearly 3/4 compiles crashing.

Y-Less commented 6 years ago

Maybe I should have documented how I did that, as so few people seem to know what a minimal repro is..

Y-Less commented 6 years ago

I'm sure I got it to crash once without the operator forward, but couldn't reproduce that in about 100 compiles.

Y-Less commented 6 years ago

It seems to crash most reliably after changing the code and recompiling.

stale[bot] commented 6 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.