/*
REQUIRED_ARGS: -preview=dip1000
TEST_OUTPUT:
---
fail_compilation/retscope.d(22): Error: scope variable `p` may not be returned
fail_compilation/retscope.d(32): Error: returning `b ? nested1(& i) : nested2(& j)` escapes a reference to local variable `j`
fail_compilation/retscope.d(45): Error: scope variable `p` assigned to non-scope `q`
fail_compilation/retscope.d(47): Error: address of variable `i` assigned to `q` with longer lifetime
fail_compilation/retscope.d(48): Error: scope variable `a` assigned to non-scope `b`
fail_compilation/retscope.d(49): Error: address of struct temporary returned by `(*fp2)()` assigned to longer lived variable `q`
---
*/




int* foo1(return scope int* p) @safe { return p; } // ok

int* foo2()(scope int* p) @safe { return p; }  // ok, 'return' is inferred
alias foo2a = foo2!();

int* foo3(scope int* p) @safe { return p; }   // error

int* foo4(bool b) @safe
{
    int i;
    int j;

    int* nested1(scope int* p) { return null; }
    int* nested2(return scope int* p) { return p; }

    return b ? nested1(&i) : nested2(&j);
}

/************************************************/

struct S2 { int a,b,c,d; }

@safe S2 function() fp2;

void test2(scope int* p, int[] a ...) @safe
{
    static int* q;
    static int[] b;
    q = p;
    int i;
    q = &i;
    b = a;
    q = &fp2().d;
}

/**************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(75): Error: function `retscope.HTTP.Impl.onReceive` is `@nogc` yet allocates closures with the GC
fail_compilation/retscope.d(77):        retscope.HTTP.Impl.onReceive.__lambda1 closes over variable this at fail_compilation/retscope.d(75)
---
*/


struct Curl
{
    int delegate() dg;
}

struct HTTP
{
    struct Impl
    {
        Curl curl;
        int x;

        @nogc void onReceive()
        {
            auto dg = ( ) { return x; };
            curl.dg = dg;
        }
    }
}

/***********************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(96): Error: reference to local variable `sa` assigned to non-scope parameter `a` calling retscope.bar8
---
*/
// https://issues.dlang.org/show_bug.cgi?id=8838

int[] foo8() @safe
{
    int[5] sa;
    return bar8(sa);
}

int[] bar8(int[] a) @safe
{
    return a;
}


/*************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(123): Error: returning `foo9(cast(char[])tmp)` escapes a reference to local variable `tmp`
---
*/

char[] foo9(return char[] a) @safe pure nothrow @nogc
{
    return a;
}

char[] bar9() @safe
{
    char[20] tmp;
    foo9(tmp);          // ok
    return foo9(tmp);   // error
}

/*************************************************/

/*
//
//
//fail_compilation/retscope.d(143): To enforce `@safe`, the compiler allocates a closure unless `opApply()` uses `scope`
//
*/

struct S10
{
    static int opApply(int delegate(S10*) dg);
}

S10* test10()
{
    foreach (S10* m; S10)
        return m;
    return null;
}

/************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(158): Error: scope variable `this` may not be returned
---
*/

class C11
{
    @safe C11 foo() scope { return this; }
}


/****************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(177): Error: address of variable `i` assigned to `p` with longer lifetime
---
*/



void foo11() @safe
{
    int[] p;
    int[3] i;
    p = i[];
}

/************************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(197): Error: scope variable `e` may not be returned
---
*/

struct Escaper
{
    void* DG;
}

void* escapeDg1(scope void* d) @safe
{
    Escaper e;
    e.DG = d;
    return e.DG;
}

/*************************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(212): Error: scope variable `p` assigned to non-scope `e.e`
---
*/
struct Escaper3 { void* e; }

void* escape3 (scope void* p) @safe {
    Escaper3 e;
    scope dg = () { return e.e; };
    e.e = p;
    return dg();
}

/**************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(229): Error: scope variable `ptr` may not be returned
---
*/

alias dg_t = void* delegate () return scope @safe;

void* funretscope(scope dg_t ptr) @safe
{
    return ptr();
}

/*****************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
fail_compilation/retscope.d(248): Error: cannot implicitly convert expression `__lambda2` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
fail_compilation/retscope.d(249): Error: cannot implicitly convert expression `__lambda4` of type `void* delegate() pure nothrow @nogc @safe` to `void* delegate() @safe`
---
*/

void escape4() @safe
{
    alias FunDG = void* delegate () @safe;
    int x = 42;
    scope FunDG f = () return { return &x; };
    scope FunDG g = ()        { return &x; };
}

/**************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(266): Error: cannot take address of `scope` local `p` in `@safe` function `escape5`
---
*/

void escape5() @safe
{
    int* q;
    scope int* p;
    scope int** pp = &q; // ok
    pp = &p; // error
}

/***********************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(286): Error: returning `foo6(& b)` escapes a reference to local variable `b`
---
*/

@safe int* foo6()(int* arg)
{
    return arg;
}

int* escape6() @safe
{
    int b;
    return foo6(&b);
}

/***************************************************/

struct S7
{
    int[10] a;
    int[3] abc(int i) @safe
    {
        return a[0 .. 3]; // should not error
    }
}

/***************************************************/

int[3] escape8(scope int[] p) @safe { return p[0 .. 3]; } // should not error
char*[3] escape9(scope char*[] p) @safe { return p[0 .. 3]; }

/***************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(319): Error: reference to local variable `i` assigned to non-scope `f`
---
*/

int* escape10() @safe
{
    int i;
    int* f;
    scope int** x = &f;
    f = &i;

    return bar10(x);
}

int* bar10( scope int** ptr ) @safe
{
    return *ptr;
}

/******************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(342): Error: cannot take address of `scope` local `aa` in `@safe` function `escape11`
---
*/

int* escape11() @safe
{
    int i;
    int*[3] aa = [ &i, null, null ];
    return bar11(&aa[0]);
}

int* bar11(scope int** x) @safe
{
    return foo11(*x);
}

int* foo11(int* x) @safe { return x; }

/******************************************/

void escape15() @safe
{
    int arg;
    const(void)*[1] argsAddresses;
    argsAddresses[0] = // MUST be an array assignment
        (ref arg)@trusted{ return cast(const void*) &arg; }(arg);
}

/******************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1003): Error: returning `f.foo()` escapes a reference to local variable `f`
---
*/

#line 1000
int* escape12() @safe
{
    Foo12 f;
    return f.foo;
}

struct Foo12
{
    int* foo() return @safe;
}

/******************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1103): Error: scope variable `f` may not be returned
---
*/

#line 1100
int* escape13() @safe
{
    scope Foo13 f;
    return f.foo;
}

class Foo13
{
    int* foo() return @safe;
}

/******************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1205): Error: scope variable `f14` assigned to non-scope parameter `this` calling retscope.Foo14.foo
---
*/

#line 1200
int* escape14() @safe
{
    int i;
    Foo14 f14;
    f14.v = &i;
    return f14.foo;
}

struct Foo14
{
    int* v;
    int* foo () @safe { return this.v; }
}

/******************************************/
/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1311): Error: scope variable `u2` assigned to `ek` with longer lifetime
---
*/

#line 1300
@safe struct U13 {
  int* k;
  int* get() return scope { return k; }
  static int* sget(return scope ref U13 u) { return u.k; }
}

@safe void foo13() {
  int* ek;

  int i;
  auto u2 = U13(&i);
  ek = U13.sget(u2); // Error: scope variable u2 assigned to ek with longer lifetime

  auto u1 = U13(new int);
  ek = u1.get();   // ok
  ek = U13.sget(u1); // ok
}

/************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1405): Error: reference to local variable `buf` assigned to non-scope parameter `__anonymous_param` calling retscope.myprintf
---
*/

#line 1400
@trusted extern(C) int myprintf(const(char)*, ...);

@safe void foo14()
{
    char[4] buf = [ 'h', 'i', '\n', 0 ];
    myprintf(&buf[0]);
}

/************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1509): Error: reference to stack allocated value returned by `(*fp15)()` assigned to non-scope parameter `__anonymous_param`
---
*/

#line 1500

@safe void bar15(int*);

struct S15 { int a,b,c,d; }

@safe S15 function() fp15;

void test15() @safe
{
    bar15(&fp15().d);
}


/*************************************************/

void foo16() @nogc nothrow
{
    alias dg_t = string delegate(string) @nogc nothrow;

    dg_t dg = (string s) => s;
}

/*************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1701): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(scope int* p)`
fail_compilation/retscope.d(1702): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(return int* p)`
fail_compilation/retscope.d(1703): Error: cannot implicitly convert expression `& func` of type `int* function(int* p)` to `int* function(return scope int* p)`
fail_compilation/retscope.d(1711): Error: cannot implicitly convert expression `& funcr` of type `int* function(return int* p)` to `int* function(scope int* p)`
fail_compilation/retscope.d(1716): Error: cannot implicitly convert expression `& funcrs` of type `int* function(return scope int* p)` to `int* function(scope int* p)`
---
*/

int* func(int* p);
int* funcs(scope int* p);
int* funcr(return int* p);
int* funcrs(return scope int* p);

void foo17()
{
#line 1700
    typeof(func)   *fp1 = &func;
    typeof(funcs)  *fp2 = &func; // error
    typeof(funcr)  *fp3 = &func; // error
    typeof(funcrs) *fp4 = &func; // error

    typeof(func)   *fq1 = &funcs;
    typeof(funcs)  *fq2 = &funcs;
    typeof(funcr)  *fq3 = &funcs;
    typeof(funcrs) *fq4 = &funcs;

    typeof(func)   *fr1 = &funcr;
    typeof(funcs)  *fr2 = &funcr; // error
    typeof(funcr)  *fr3 = &funcr;
    typeof(funcrs) *fr4 = &funcr;

    typeof(func)   *fs1 = &funcrs;
    typeof(funcs)  *fs2 = &funcrs; // error
    typeof(funcr)  *fs3 = &funcrs;
    typeof(funcrs) *fs4 = &funcrs;
}

/*************************************************/

/*
TEST_OUTPUT:
---
fail_compilation/retscope.d(1801): Error: cannot implicitly convert expression `&c.func` of type `int* delegate()` to `int* delegate() scope`
fail_compilation/retscope.d(1802): Error: cannot implicitly convert expression `&c.func` of type `int* delegate()` to `int* delegate() return scope`
fail_compilation/retscope.d(1803): Error: cannot implicitly convert expression `&c.func` of type `int* delegate()` to `int* delegate() return scope`
fail_compilation/retscope.d(1811): Error: cannot implicitly convert expression `&c.funcr` of type `int* delegate() return scope` to `int* delegate() scope`
fail_compilation/retscope.d(1816): Error: cannot implicitly convert expression `&c.funcrs` of type `int* delegate() return scope` to `int* delegate() scope`
---
*/

class C18
{
    int* func();
    int* funcs() scope;
    int* funcr() return;
    int* funcrs() return scope;
}

void foo18()
{
    C18 c;

#line 1800
    typeof(&c.func)   fp1 = &c.func;
    typeof(&c.funcs)  fp2 = &c.func; // error
    typeof(&c.funcr)  fp3 = &c.func; // error
    typeof(&c.funcrs) fp4 = &c.func; // error

    typeof(&c.func)   fq1 = &c.funcs;
    typeof(&c.funcs)  fq2 = &c.funcs;
    typeof(&c.funcr)  fq3 = &c.funcs;
    typeof(&c.funcrs) fq4 = &c.funcs;

    typeof(&c.func)   fr1 = &c.funcr;
    typeof(&c.funcs)  fr2 = &c.funcr; // error
    typeof(&c.funcr)  fr3 = &c.funcr;
    typeof(&c.funcrs) fr4 = &c.funcr;

    typeof(&c.func)   fs1 = &c.funcrs;
    typeof(&c.funcs)  fs2 = &c.funcrs; // error
    typeof(&c.funcr)  fs3 = &c.funcrs;
    typeof(&c.funcrs) fs4 = &c.funcrs;
}

/*********************************************/

@safe void foo19(C)(ref C[] str)  // infer 'scope' for 'str'
{
    str = str;
    str = str[1 .. str.length];
}

@safe void test19()
{
    char[10] s;
    char[] t = s[];
    foo19(t);
}

/********************************************/


bool foo20(const string a) @safe pure nothrow @nogc
{
    return !a.length;
}

struct Result(R)
{
    R source;

    bool empty() // infer 'scope' for 'this'
    { return foo20(source); }
}

@safe void test20()
{
    scope n = Result!string("abc");
    n.empty();
}

/************************************************/

// https://issues.dlang.org/show_bug.cgi?id=17117

ref int foo21(return ref int s)
{
        return s;
}

int fail21()
{
        int s;
        return foo21(s); // Error: escaping reference to local variable s
}

int test21()
{
        int s;
        s = foo21(s);
        return s;
}

/**********************************************/

@safe void foo22()(ref char[] s)
{
    char[] a = s;
}

@safe void test22(scope char[] s)
{
    foo22(s);
}

/*********************************************
TEST_OUTPUT:
---
fail_compilation/retscope.d(1907): Error: scope variable `x` assigned to `this` with longer lifetime
fail_compilation/retscope.d(1913): Error: scope variable `x` may not be returned
---
*/
#line 1900
struct Constant
{
    int* member;

    int* foo(scope Repeat!(int*) grid) @safe
    {
        foreach(ref x; grid)
            member = x;

        foreach(ref x; grid)
            x = member;

        foreach(ref x; grid)
            return x;

        return null;
    }

    alias Repeat(T...) = T;
}
