﻿__group__	ticket	summary	component	version	milestone	type	owner	status	created	_changetime	_description	_reporter
Active Tickets	140	conditional expression bad code generation	cfa-cc	1.0		defect		new	2019-07-19T09:40:34-04:00	2025-02-27T16:42:44-05:00	"{{{
int main() {
    char c[1] = { 3 };
    int i = 4;
    int r = 3 < 4 ? c[0] : i;
    printf( ""%d\n"", r );
}
$ a.out
-460718077
}}}
The problem code is:
{{{
    char _X1cA0c_2[((unsigned long int )1)] = { ((char )3) };
    signed int _X1ii_2 = 4;
    signed int _X1ri_2 = (*(((3<4)!=((signed int )0)) ? ((signed int *)(&_X1cA0c_2[((signed long int )0)])) : (&_X1ii_2)));
}}}
and the cast is wrong for _X1cA0c_2. It should be (char *). I think the problem is unifying the type for the two expressions."	pabuhr
Active Tickets	238	Function new incorrectly creates temporary objects	cfa-cc	1.0		defect		new	2021-02-18T14:38:28-05:00	2021-02-18T14:38:28-05:00	"This code leads to a call to a copy constructor that is incorrect.
{{{
#include <stdlib.hfa>
#include <fstream.hfa>

struct cluster {};
void ?{}(cluster & this) { sout | ""Cluster Ctor""; }
void ?{}(cluster & this, cluster ) { sout | ""Cluster CopyCtor""; }

struct inner {
	cluster * cl;
};
void ?{}(inner & this, cluster & ) { sout | ""Inner Ctor""; }

struct outer {
	inner * in;
};
void ?{}(outer & this, cluster & cl ) {
	this.in = new(cl);
}
void ^?{}(outer & this) { delete(this.in); }

int main() {
	cluster cl;
	outer o = { cl };
}
}}}

The output is: 
{{{
Cluster Ctor
Cluster CopyCtor
Inner Ctor
}}}
but the CopyCtor should not be called."	Thierry Delisle
Active Tickets	10	Cannot declare anonymous union around generic type	cfa-cc	1.0		defect	mlbrooks	assigned	2017-05-25T11:30:33-04:00	2020-04-07T15:23:13-04:00	"Minimal example:
{{{
forall(otype T)
struct result {
    #ifdef THE_ERROR
    union {
    #endif

    T value;

    #ifdef THE_ERROR
    };
    #endif
};
}}}

produces : 
{{{
error: ‘_sizeof_Y1T’ undeclared here (not in a function)
}}}


Desired for:

{{{
forall(otype T, otype E)
struct result {
	bool has_value;
	union {
		T value;
		E error;
	};
};
}}}
"	Thierry Delisle
Active Tickets	50	Assertion failure with tuple constructor-expressions	cfa-cc	1.0		defect	Rob Schluntz	assigned	2017-10-05T11:33:26-04:00	2020-02-12T14:41:12-05:00	"{{{
int main() {
  [int, int] x;
  x { [5, 5] };  // assert fail
}
}}}
The relevant assertion is an unhandled case in FixCtorExprs::postmutate( ConstructorExpr *) for TupleAssignExpr."	Rob Schluntz
Active Tickets	66	Function and variable name collision for if	cfa-cc	1.0		defect		new	2017-12-01T11:14:12-05:00	2022-06-22T14:41:40-04:00	"This code does not compile because the function can be evaluated for truthiness :
{{{
void next() {}

int main() {
	int * next = 0p;
	if( next ) {
		return 1;
	}
	return 0;
}
}}}"	Thierry Delisle
Active Tickets	95	Incorrect consideration of global and local variables in resolving waitfor mutex object.	cfa-cc	1.0		defect		assigned	2018-06-30T19:14:23-04:00	2022-06-22T15:00:07-04:00	"The following code :
{{{
#include <monitor.hfa>
monitor M { condition e; } m;
void rtn( M & mutex m );
void bar( M & mutex m ) {
 	waitfor( rtn : m ); // not ambiguous, select parameter
}
}}}


Produces the following error:

{{{
cfa test.c
CFA Version 1.0.0 (debug)
test.c:92:1 error: Ambiguous function in call to waitfor
}}}

If mangling is removed for {{{mutex}}} then the following code encounters widening problems:
{{{
//----------
struct monitor_desc {};

struct monitor_guard_t {};
void ?{}( monitor_guard_t & this, monitor_desc ** m, int count, void (*func)() );

struct monitor_dtor_guard_t {};
void ?{}( monitor_dtor_guard_t & this, monitor_desc ** m, void (*func)() );

//----------
forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );

//----------
monitor test {};

int main() {
	test * t = 0;
	delete(t);
}
}}}
Note the {{{monitor_guard_t}}} and {{{monitor_dtor_guard_t}}} structs are needed because they are expected by the code handling the mutex and monitor keywords."	Thierry Delisle
Active Tickets	104	Tuple assertion generate bad C code	cfa-cc	1.0		defect		new	2018-08-14T15:27:56-04:00	2022-06-22T15:01:58-04:00	"CFA-CPP produces bad C code from this cfa code:
{{{
[ float, float ] modf_( float x );

forall(T | { [T, T] modf_(T); })
void modf(T);

int main() {
    modf(7.0f);
}
}}}"	Thierry Delisle
Active Tickets	120	dtype problem	cfa-cc	1.0		defect		new	2019-02-28T11:22:06-05:00	2019-02-28T11:22:06-05:00	"For dtype, this:
{{{
forall( dtype T ) T * nullptr( void ) { return (T *)0; }
int * pi = nullptr();
int & ri = *nullptr();
}}}
generates:
{{{
CFA Version 1.0.0 (debug)
test1.cfa:39:1 error: No reasonable alternatives for expression Applying untyped:
  Name: *?
...to:
  Applying untyped:
    Name: nullptr
  ...to:
}}}
For otype, it generates:
{{{
cfa test1.cfa
CFA Version 1.0.0 (debug)
test1.cfa:39:33: error: '_tmp_cp_ret2' undeclared here (not in a function)
 int & ri = *nullptr();
                                 ^           
test1.cfa:39:179: error: '_adapterFv_Pii__MP' undeclared here (not in a function)
 int & ri = *nullptr();
                                                                                                                                                                                   ^                 
test1.cfa:39:199: error: '_adapterFi_Pii_P_MP' undeclared here (not in a function)
 int & ri = *nullptr();
}}}
Staying with otype and moving the declarations into a local scope:
{{{
forall( otype T ) T * nullptr( void ) { return (T *)0; }
int fred() {
    int * pi = nullptr();
    int & ri = *nullptr();
}
}}}
does compile.

It fails as above with dtype and declarations in a local scope."	pabuhr
Active Tickets	147	Can't initialize const member of struct	cfa-cc	1.0		defect		new	2019-11-13T16:43:17-05:00	2025-11-12T17:51:21-05:00	"[A, basic]
{{{
#ifdef HIDE_ERR
    #define MAYBE_QUAL(...)
#else
    #define MAYBE_QUAL(...) __VA_ARGS__
#endif

struct thing {
    MAYBE_QUAL( const ) int foo; 
};
void ?{}( thing & this ) { 
    (this.foo){ 5 }; 
}
int main() {
    thing t;
    printf(""%d\n"", t.foo);
}
}}}

A, expected, regardless of defines:  compiles, runs, and prints 5

A, actual, no define:  `error: lvalue required as unary ‘&’ operand`

A, actual, -DHIDE_ERR:  (as expected)

When the bug occurs, CFA-cc passes bad C code to GCC; this code tries to take address of address.  This behaviour occurs in spite of #166 having been fixed, though perhaps the technique of #166 can be applied in a different place to fix this issue.

Excerpt of `-CFA`, no define
{{{
((void)(((void)(_tmp_ctor_expr0=(*((unsigned long int **)(&(&(*_X4thisS6thingy_1)._X3fooKm_1)))
                                )
               )
        )
       ,(((void)((*_tmp_ctor_expr0)=((unsigned long int )5)) /* ?{} */)
        ,_tmp_ctor_expr0
        )
       )
);
}}}

Excerpt -f `-CFA`, -DHIDE_ERR
{{{
((void)(((void)(_tmp_ctor_expr0=(                           &(*_X4thisS6thingy_1)._X3foom_1
                                )
               )
        )
       ,(((void)((*_tmp_ctor_expr0)=((unsigned long int )5)) /* ?{} */)
        ,_tmp_ctor_expr0
        )
       )
);
}}}

By contrast, initializing a const field via the autogenerated member-wise constructor works:

[B, success corner]
{{{
struct thing {
    const int foo; 
};
int main() {
    thing t{ 1234 };
    printf(""%d\n"", t.foo);
}
}}}

B, actual and expected:   compiles, runs, and prints 1234

We found a workaround that uses both field assignment (in place of field constructor call) and a const-cast:

[C, workaround]
{{{
struct thing {
    const int foo; 
};
void ?{}( thing & this ) { 
    * ( int * ) & ( this.foo ) = 5;
}
int main() {
    thing t;
    printf(""%d\n"", t.foo);
}
}}}

C, actual and expected:   compiles, runs, and prints 5

Further variations of the basic pattern have been tried, with mixed success:
[D, mix-n-match]
{{{
#ifndef VAR_noqual
    // default: include qualifiers
    #define MAYBE_QUAL(...) __VA_ARGS__
#else
    // variation: suppress qualifiers
    #define MAYBE_QUAL(...)
#endif

#ifndef VAR_assign
    // default: user's ctor calls field's ctor
    #define INITIALIZE( THISFIELD, VAL ) ( THISFIELD ) { VAL }
#else
    // variation: user's ctor calls field's assignment operator
    #define INITIALIZE( THISFIELD, VAL ) ( THISFIELD ) = ( VAL )
#endif

#if ! defined VAR_concast1  &&  ! defined VAR_concast2
    // default: user's field access is otherwise passthrough
    #define MAYBE_CONCAST( TY, THISFIELD ) THISFIELD
#elif defined VAR_concast1  &&  ! defined VAR_concast2
    // variation, sub 1: user's field access is via a CFA const cast
    #define MAYBE_CONCAST( TY, THISFIELD ) ( TY & )( THISFIELD )
#elif ! defined VAR_concast1  &&  defined VAR_concast2
    // variation, sub 1: user's field access is via a C const cast
    #define MAYBE_CONCAST( TY, THISFIELD ) * ( TY * )( & ( THISFIELD ) )
#else
    #error Bad 'concast' combination
#endif

#ifndef VAR_derefref
    // default: passthrough
    #define MAYBE_DEREFREF( THISFIELD ) THISFIELD
#else
    // variation: take the address, then dereference it
    // (has helped some cases of emitting ""lvalue required"" code)
    #define MAYBE_DEREFREF( THISFIELD ) *&(THISFIELD)
#endif

#ifndef VAR_ctorparm
    // default: user's ctor is parameterless; ctor's body hardcodes the value
    #define MAYBE_CTORPARM( ... )
    #define SWITCH_CTORPARM( CASE_NORMAL, CASE_VAR ) CASE_NORMAL
#else
    // variation: user's ctor takes a `foo_val` param; program main hardcodes the value
    #define MAYBE_CTORPARM( ... ) __VA_ARGS__
    #define SWITCH_CTORPARM( CASE_NORMAL, CASE_VAR ) CASE_VAR
#endif

#ifndef VAR_with
    // default: ctor body accesses the field via this-dot
    #define SWITCH_WITH( CASE_NORMAL, CASE_VAR ) CASE_NORMAL
#else
    // variation: ctor body accesses the field via `with`
    #define SWITCH_WITH( CASE_NORMAL, CASE_VAR ) CASE_VAR
#endif

struct thing {
    MAYBE_QUAL( const ) int foo; 
};
void ?{}( thing & this  MAYBE_CTORPARM(, int foo_val ) ) SWITCH_WITH( , with( this ) ) { 
    INITIALIZE( MAYBE_DEREFREF( MAYBE_CONCAST( int, SWITCH_WITH( this., ) foo ) ), SWITCH_CTORPARM( 42, foo_val ) );
}
int main() {
    thing t{ MAYBE_CTORPARM( 999 ) };
    printf(""%d\n"", t.foo);
}
}}}

D, all variation combinations, ideal: success, printing 5 or 999, as appropriate

D, expectation tolerance: as can be accepted case-by-case, rejection of some ""ideal-valid"" variation combinations may occur due to cfacc having to resort to conservative detection of ""this appears to be a field initialization.""

D, across variation combinations, actual: incomplete list follows

Some of the earlier tests are variation combinations from this general test, specifically:
- A is D with A's HIDE_ERROR being the same as D's VAR_noqual, and with the other VAR_* not defined [actual, VAR_noqual, pass; actual, none defined, fail]
- C is D with VAR_assign and VAR_concast2 defined, and the other VAR_* not defined [actual: pass]

However, in spite of the element of similarity, B is not D with VAR_ctorparm defined and the other VAR_* not defined.  B (pass) uses an autogenerated constructor, while D with VAR_ctorparm defined (fail) uses a user-written constructor that tries to emulate B.

Every VAR_* option is believed to be at least a benign independent contribution, i.e. not inherently invalid.  This is directly observable in all cases VAR_xxx, except VAR_concast2, defining both VAR_noqual (pass on its own) and VAR_xxx leaves a working program (VAR_noqual + VAR_xxx: pass).  For VAR_concast2, its validity along with VAR_assign (as in test C) shows that it can be okay.

Even so, VAR_concast2 all alone is failing, with the same symptom as in A.  No hypothesis generalizing this phenomenon is yet conceived.

VAR_assign alone: different error, from CFA resolver (before chance to generate bad code): ambiguous `?{}` call, between regular and volatile; not remotely understood; have been unable to fix, with any (non)volatile combinations (these not shown)

VAR_assign + VAR_concast1: fixed, as for VAR_concast2 already discussed

VAR_derefref: different error still, also at resolver: ctor call is an invalid application

Except for VAR_noqual, each other VAR_*, on its own has some error.  Except as just identified (VAR_assign and VAR_derefref), the symptom is ""fails to fix the original symptom.""

The switches VAR_ctorparm and VAR_with have never been seen to have an effect, i.e. these factors are independent.

Note that the `-E` and `-CFA -XCFA,-p` outputs from program D are quite readable.  Looking at a `-E` can be easier than mentally expanding macros.
"	mlbrooks
Active Tickets	159	Static expression evaluation	cfa-cc	1.0		defect		new	2020-02-10T15:02:31-05:00	2020-02-10T15:02:31-05:00	"Some static expression evaulation is necessary for constructor designators. Other static expressions can be left for gcc. Currently, CFA does not ignore evaulating static expressions it doesn't need and can't evaulate the ones it does need, e.g.
{{{
enum {
    A = 1 / sizeof(int) // sizeof is not evaulated by CFA and returns 0
};
int mask[ A ]; // force static evaluation of expression for A
}}}
causes a division by zero exception in cfa-cc."	pabuhr
Active Tickets	162	Member access of reference broken in goto statement	cfa-cc	1.0		defect	Thierry Delisle	assigned	2020-03-02T14:30:33-05:00	2020-03-02T15:51:45-05:00	"This code seems to resolve correctly but generates incorrect code.
{{{
struct outer {
	void * v;
};

void foo(outer & b) {
	goto *b.v;
}
}}}

Dumping the ast before and after the resolver seems to indicate this is a resolver bug."	Thierry Delisle
Active Tickets	163	Incorrect cast in polymorphique returns of builtins	cfa-cc	1.0		defect	Thierry Delisle	assigned	2020-03-02T15:55:07-05:00	2020-03-02T15:55:07-05:00	"Given the function 
{{{
forall(dtype T)
static inline T & identity(T & i) {
	return i;
}
}}}
The following code compiles if identity is a regular function but doesn't if it is a CFA builtin.
{{{
struct A { int a; };
void foo( A & a ) {
	printf(""%d %d\n"", a.a, identity(a).a);
}
}}}
"	Thierry Delisle
Active Tickets	168	Plan-9 inheritance is not considered for satisfying a trait's assertion	cfa-cc	1.0		defect		new	2020-04-14T22:28:16-04:00	2020-04-14T22:28:16-04:00	"This code should be valid:
{{{

struct club {
    int membercount;
};

struct finalclub {
    inline club;
    float endowment;
};

void showmembers( club & c ) {
}

forall( dtype T | { void showmembers( T & ); } )
void show_fancy_members(T & x) {
    showmembers(x);
}

int main() {
    finalclub phoenix;
    showmembers( phoenix );

    #ifdef THE_ERROR
    show_fancy_members( phoenix );
    #endif
}
}}}

But cfa-cc gives the error:
{{{
No alternatives with satisfiable assertions for Applying untyped:
  Name: show_fancy_members
...to:
  Name: phoenix
      Unsatisfiable alternative:
        ...
        Environment:( _111_0_T ) -> instance of struct finalclub with body 1

      Could not satisfy assertion:
showmembers: pointer to function
        ... with parameters
          reference to instance of type _111_0_T (not function type)
        ... returning nothing
}}}

The error is saying it needs, but cannot find, a function matching
{{{
    void showmembers( [type of phoenix] & );
}}}
while the previous line has the successful call
{{{
    showmembers( phoenix );
}}}"	mlbrooks
Active Tickets	174	Cannot Check Exception Trait	cfa-cc	1.0		defect		new	2020-05-13T16:05:13-04:00	2020-05-13T16:05:13-04:00	"Currently the exception handing mechanism uses an ad-hoc version of virtual types to implement its exception object. The type system does not know about these. The result of this is no assertion covers what the virtual type requires and the is_exception trait is empty.

Which means every type is an exception as far as the resolver is concerned. This is a problem that can't be solved right now. It requires the virtual system or equivalent to be implemented. But once it is this should be fixed, currently it crashes with a messy stack-trace."	ajbeach
Active Tickets	176	Assignment fails for union structure field	cfa-cc	1.0		defect		new	2020-05-17T09:17:44-04:00	2025-01-23T14:16:13-05:00	"{{{
forall( dtype T )
union Link {
	struct {
		T * top;
		uintptr_t count;
	};
	__int128 atom; // both fields
};

int fred() {
	Link(int) l;
	int i = l.count, * ip = l.top;
	__int128 I = l.atom;
	i = l.count;
	ip = l.top;
	I = l.atom;
}
}}}
{{{
CFA Version 1.0.0 (debug)
test1.cfa:15:1 error: No reasonable alternatives for expression Applying untyped:
  Name: ?=?
...to:
  Name: ip
  Untyped Member Expression, with field: 
    Name: top  ... from aggregate:
    Name: l
}}}
The assignment fails because top is of type T. Assignment to non-polymorphic types works."	pabuhr
Active Tickets	186	Cannot add extra assertion on a constructor	cfa-cc	1.0		defect		new	2020-06-16T16:05:39-04:00	2020-06-16T16:05:39-04:00	"Conflicts or confusion happen when trying to declare a custom constructor, which intends to replace an autogen constructor, if the custom constructor is adding assertions beyond those of the struct declaration (which is the set of assertions that applies to visible autogen constructors).  The replacement happens only partially; two examples are detailed below.

A simple example has neither constructor being callable.

{{{
struct S {};

forall( | {void sayHi();} )
void ?{}( S & this ) {
    sayHi();
}

void sayHi() {
    printf(""hi\n"");
}

int main() {
    S s;
}
}}}

Expected, simple: compile success with output
{{{
hi
}}}

Actual, simple: compiler error
{{{
Unique best alternative includes deleted identifier in Generated Cast of:
  Application of
    Deleted Expression
      Variable Expression: ?{}: static inline function
      ... with parameters
        _dst: reference to instance of struct S with body 1
      ... returning nothing

      ... deleted by: ?{}: forall
        __anonymous0: data type
        ... with assertions
          sayHi: pointer to function
            accepting unspecified arguments
          ... returning nothing
}}}

Note that the error says the custom constructor is responsible for deleting the autogen constructor (which is correct), yet the call site is preferring the autogen constructor (which is incorrect).

A consequence of this first example is, when there is no generic type parameter on the structure, all constructors must use the same set of assertions.  This represents a limitation of the resource-management/traits system, in that a specialized resource acquisition strategy cannot be applied for a scenario indicated by the presence of a certain API.

A more elaborate example has the autogen constructor being called in spite of the same-signature custom constructor, which is confusing and hard to troubleshoot.

{{{
forall( dtype T ) {
    struct S {
        T * t;
    };

    forall( | {void sayHi();} )
    void ?{}( S(T) & this, T * t ) {
        sayHi();
        this.t = 0p;
    }
}

void sayHi() {
    printf(""hi\n"");
}

int main() {
    int x = 7;
    S(int) s = { & x };
    printf(""%s\n"", (s.t == 0p) ? ""null"" : ""not null"");
}
}}}

Expected, elaborate: compiler success with output
{{{
hi
null
}}}

Actual, elaborate: compiler success with output
{{{
not null
}}}

The actual-elaborate output is suggesting that the autogen 1-param constructor is being called.  Manual inspection of -CFA output corroborates.

In this revision of the elaborate case, the behaviour is reversed.  This does provide a workaround (for cases when the struct has generic parameters).  But note the extreme similarity of the two elaborate code listings: all our instincts about forall distribution are screaming that the two should be the same.

{{{
forall( dtype T ) {
    struct S {
        T * t;
    };
}

forall( dtype T | {void sayHi();} ) {
    void ?{}( S(T) & this, T * t ) {
        sayHi();
        this.t = 0p;
    }
}

void sayHi() {
    printf(""hi\n"");
}

int main() {
    int x = 7;
    S(int) s = { & x };
    printf(""%s\n"", (s.t == 0p) ? ""null"" : ""not null"");
}
}}}

Actual, elaborate revised: as expected elaborate
"	mlbrooks
Active Tickets	193	Monomorphization Failures outside of Functions	cfa-cc	1.0		defect		new	2020-07-02T15:13:19-04:00	2020-07-06T14:51:52-04:00	"There are certainly multiple bugs here but currently it is unclear exactly how many there are or where the dividing lines are. Currently all of the bugs can be produced with the following set-up:

{{{
forall(dtype T) 
void poly0(T &) {}

forall(dtype T | sized(T))
void poly1(T &) {}

forall(otype T)
void poly2(T &) {}

struct wrapper {
    void (*mono)(int &);
};
}}}

The first group complain about incompatable pointer assignment inside `__global_init__`. It appears to be coming from C code. Note for the first case it is just a `int *` to `void *` difference but in the other cases the number of parameters is different, likely the hidden polymorphic parameters.
{{{
void (*mono0)(int &) = poly0;
void (*mono1)(int &) = poly1;
void (*mono2)(int &) = poly2;
}}}

The error messages also change if you attempt to do this for a field of a structure instead of a stand alone function pointer.

The following case produces an invalid thunk, the sizeof and alignof symbols are not defined inside of it but are used.
{{{
struct wrapper mono_wrapper1 = { poly1 };
}}}

Here there is a resolution failure as it cannot resolve a constructor. The constructor call appears to be on some generated code. I have not yet pinned down what that generated code is though.
{{{
struct wrapper mono_wrapper2 = { poly2 };
}}}

The last one compiles but I got runtime errors every time I tried to call it, an illegal operation at a very strange location.
{{{
struct wrapper mono_wrapper0 = { poly0 };
}}}

Switching from `=` to `@=` currently fixes the issue. So it seems that trying to insert a constructor call might be the source of the problem. Either because that makes the system forget the function must be monomorphized or it is monomorphized improperly in the new context."	ajbeach
Active Tickets	194	Ambiguous reference vs pointer return [from ticket #70]	cfa-cc	1.0		defect		new	2020-07-07T15:18:25-04:00	2020-11-05T20:24:47-05:00	"{{{
forall( dtype T | sized(T) ) T * foo( void ) {
	printf( ""foo1\n"" );
	return (T *)0;
}
forall( dtype T | sized(T) ) T & foo( void ) {
	printf( ""foo2\n"" );
	return (T &)*(T *)0;
}
int main( void ) {
    int * i = foo();
}
}}}

This should not be ambiguous, one side is an exact match.
This must be either unambiguous or an error to define both

The reported ambiguity is between:

foo1 with T as int  (return is T*, which is int*, assigned into int* variable)

foo2 with T as int* (return is T&, which is int*&, assigned into int* variable)

The reported cost is ( 0, 0, 0, 0, 1, 0, 0 ).  It is surprising that the second case, which we wish to reject, has a zero reference-cost element (last position).  It seems that assignment from X& to X should count one change of reference level.
"	Thierry Delisle
Active Tickets	195	Unnecessary dereference leads to segfault [from ticket #70]	cfa-cc	1.0		defect		new	2020-07-07T15:24:06-04:00	2020-11-05T20:36:34-05:00	"{{{
forall( dtype T | sized(T) ) T * foo( void ) {
	printf( ""foo1\n"" );
	return (T *)0;
}
forall( dtype T | sized(T) ) T & foo( void ) {
	printf( ""foo2\n"" );
	return (T &)*(T *)0;
}
int main( void ) {
    int * i;
    int & j;

    i = foo();  // correctly calls foo1
    &j = foo();  // incorrectly calls foo1 instead of foo2
}
}}}

This leads to a segfault from a null ptr that should never be dereferenced"	Thierry Delisle
Active Tickets	197	Generated constructor calls copy ctor twice on tuple elements	cfa-cc	1.0		defect		new	2020-07-13T18:17:18-04:00	2020-07-13T21:00:56-04:00	"Compiler generated copy constructor for struct with fields of tuple type does a redundant copy.

The tuple elements are first copied to generated temporary variables, then to the actual destination.

Code that creates the problem:


{{{
struct A {};
void ?{}(A& a, A src) { sout | 'A'; }
struct B { [A, A] elem; };

int main() {
  B b;
  B b1 = b;
}
}}}

Correct behavior is the program prints twice. Currently on B b1 = b; it prints four times.

compile this snippet with -n -P resolver, should find the following (note the ""maybe constructed""):

{{{
?{}: autogenerated cfa static inline function
... with parameters
  _dst: reference to instance of struct B with body 1
  _src: instance of struct B with body 1
... returning nothing
... with body
  CompoundStmt
    Implicit Ctor Dtor Statement
    ... with Ctor/Dtor: Expression Statement:
        Tuple Assignment Expression, with stmt expr:
          Statement Expression: 
            CompoundStmt
              Declaration of __multassign_L0: reference to instance of struct A with body 1 with initializer (not constructed)
                Simple Initializer: Generated Cast of:
                  Tuple Index Expression, with tuple:
                    Member Expression, with field:
                      elem: tuple of types
                        instance of struct A with body 1
                        instance of struct A with body 1

                    ... from aggregate:
                      Generated Cast of:
                        Variable Expression: _dst: reference to instance of struct B with body 1
                      ... to:
                        instance of struct B with body 1                    with index: 0

                ... to:
                  reference to instance of struct A with body 1

              Declaration of __multassign_L1: reference to instance of struct A with body 1 with initializer (not constructed)
                Simple Initializer: Generated Cast of:
                  Tuple Index Expression, with tuple:
                    Member Expression, with field:
                      elem: tuple of types
                        instance of struct A with body 1
                        instance of struct A with body 1

                    ... from aggregate:
                      Generated Cast of:
                        Variable Expression: _dst: reference to instance of struct B with body 1
                      ... to:
                        instance of struct B with body 1                    with index: 1

                ... to:
                  reference to instance of struct A with body 1

              Declaration of __multassign_R0: instance of struct A with body 1 with initializer (maybe constructed)
                Constructor initializer: 
                ... initially constructed with Implicit Ctor Dtor Statement
                  ... with Ctor/Dtor: Expression Statement:
                      Application of
                        Variable Expression: ?{}: static inline function
                        ... with parameters
                          _dst: reference to instance of struct A with body 1
                          _src: instance of struct A with body 1
                        ... returning nothing

                      ... to arguments
                        Generated Cast of:
                          Variable Expression: __multassign_R0: instance of struct A with body 1
                        ... to:
                          reference to instance of struct A with body 1
                        Tuple Index Expression, with tuple:
                          Member Expression, with field:
                            elem: tuple of types
                              instance of struct A with body 1
                              instance of struct A with body 1

                          ... from aggregate:
                            Variable Expression: _src: instance of struct B with body 1                          with index: 0


                ... destructed with Implicit Ctor Dtor Statement
                  ... with Ctor/Dtor: Expression Statement:
                      Application of
                        Variable Expression: ^?{}: static inline function
                        ... with parameters
                          _dst: reference to instance of struct A with body 1
                        ... returning nothing

                      ... to arguments
                        Generated Cast of:
                          Variable Expression: __multassign_R0: instance of struct A with body 1
                        ... to:
                          reference to instance of struct A with body 1



              Declaration of __multassign_R1: instance of struct A with body 1 with initializer (maybe constructed)
                Constructor initializer: 
                ... initially constructed with Implicit Ctor Dtor Statement
                  ... with Ctor/Dtor: Expression Statement:
                      Application of
                        Variable Expression: ?{}: static inline function
                        ... with parameters
                          _dst: reference to instance of struct A with body 1
                          _src: instance of struct A with body 1
                        ... returning nothing

                      ... to arguments
                        Generated Cast of:
                          Variable Expression: __multassign_R1: instance of struct A with body 1
                        ... to:
                          reference to instance of struct A with body 1
                        Tuple Index Expression, with tuple:
                          Member Expression, with field:
                            elem: tuple of types
                              instance of struct A with body 1
                              instance of struct A with body 1

                          ... from aggregate:
                            Variable Expression: _src: instance of struct B with body 1                          with index: 1


                ... destructed with Implicit Ctor Dtor Statement
                  ... with Ctor/Dtor: Expression Statement:
                      Application of
                        Variable Expression: ^?{}: static inline function
                        ... with parameters
                          _dst: reference to instance of struct A with body 1
                        ... returning nothing

                      ... to arguments
                        Generated Cast of:
                          Variable Expression: __multassign_R1: instance of struct A with body 1
                        ... to:
                          reference to instance of struct A with body 1



              Expression Statement:
                Tuple:
                  Application of
                    Variable Expression: ?{}: static inline function
                    ... with parameters
                      _dst: reference to instance of struct A with body 1
                      _src: instance of struct A with body 1
                    ... returning nothing

                  ... to arguments
                    Variable Expression: __multassign_L0: reference to instance of struct A with body 1
                    Variable Expression: __multassign_R0: instance of struct A with body 1

                  Application of
                    Variable Expression: ?{}: static inline function
                    ... with parameters
                      _dst: reference to instance of struct A with body 1
                      _src: instance of struct A with body 1
                    ... returning nothing

                  ... to arguments
                    Variable Expression: __multassign_L1: reference to instance of struct A with body 1
                    Variable Expression: __multassign_R1: instance of struct A with body 1
}}}

"	f37yu
Active Tickets	200	Polymorphic Function Pointers not handled by Constructor	cfa-cc	1.0		defect		new	2020-07-16T15:55:12-04:00	2020-07-16T15:55:12-04:00	"There seems to be an issue with function pointers in constructors if the argument or return type is a polymorphic type with an otype type variable (error disappears with dtype). A pointer to the polymorphic type seems to work just fine.

Code:
{{{
forall(otype T) struct tag {};
forall(otype U)
struct object {
    void (*function1)(tag(U));
    tag(U) (*function2)();
};
}}}
Error:
{{{
bug.cfa: In function '_X12_constructorQ1_0_0_4__X16_operator_assignFBD0_BD0BD0__X12_constructorFv_BD0__X12_constructorFv_BD0BD0__X11_destructorFv_BD0__Fv_S6object_BD0_Fv_S3tag_BD0___autogen___1':
bug.cfa:24:259: warning: assignment to 'void (*)(long unsigned int,  long unsigned int,  void *)' from incompatible pointer type 'void (*)(void *)' [-Wincompatible-pointer-types]
   24 |  void (*function1)(tag(U));
      |

bug.cfa: In function '_X12_constructorQ1_0_0_4__X16_operator_assignFBD0_BD0BD0__X12_constructorFv_BD0__X12_constructorFv_BD0BD0__X11_destructorFv_BD0__Fv_S6object_BD0_Fv_S3tag_BD0__FS3tag_BD0____autogen___1':
bug.cfa:24:259: warning: assignment to 'void (*)(long unsigned int,  long unsigned int,  void *)' from incompatible pointer type 'void (*)(void *)' [-Wincompatible-pointer-types]
   24 |  void (*function1)(tag(U));
      |
}}}

Also I was not able to get this error message to appear anywhere but the constructor. I tried several types and although it is not an exhaustive search it didn't turn up anything."	ajbeach
Active Tickets	201	GCC-7,8,9 Bug with -O2,3	prelude	1.0		defect		new	2020-07-17T16:41:11-04:00	2020-07-17T16:41:11-04:00	"Compiling the program below will give the following error with GCC versions 7, 8, and 9 if optimization level is O2 or O3:

during IPA pass: inline
prelude.cfa: In function ‘_X4mainFi___1’:
prelude.cfa:9:14: internal compiler error: in initialize_inlined_parameters, at tree-inline.c:3444
Please submit a full bug report,
with preprocessed source if appropriate.
See <file:///usr/share/doc/gcc-9/README.Bugs> for instructions.


forall( dtype T | sized(T) ) {
	struct S1 { T * oaddr; bool copy; };
}
forall( dtype T | sized(T) ) {
	T * $xalloc_internal(S1(T) in )		{ return 0p; }
	T * ?() (S1(T) in, char fill)		{ return $xalloc_internal( (S1(T)) {}); }
	S1(T) ?`fill(S1(T) in)				{ return (S1(T)){}; }
	S1(T) ?() (S1(T) in, size_t align)	{ return (S1(T)){}; }
	S1(T) ?`align(S1(T) in)				{ return (S1(T)){}; }
	S1(T) xalloc(size_t dim = 1)		{ return (S1(T)){}; }
}
int main() {
	int * ip = xalloc()`align(32)`fill('f');
	return 0;
}"	m3zulfiq
Active Tickets	202	Type manipulation in long chains of post-fix functions	cfa-cc	1.0		defect		new	2020-07-17T17:47:42-04:00	2020-07-17T17:47:42-04:00	"In the code below, ndp1 initialization goes to the wrong polymorphic version of function xalloc. Although, nip1 and ndp0 initializations work fine.
The only difference between ndp0 and ndp1 is the length of the chain of postfix functions.

forall( dtype T | sized(T) ) {
	struct S1 { T * oaddr; bool copy;};

	T * 	?`post_2(S1(T) in) 		{ return (T*)0p; }
	S1(T) 	?`post_1(S1(T) in) 		{ return (S1(T)){(T*)in.oaddr}; }
	S1(T) 	?`xalloc (T * in) 		{ printf(""TT ""); return (S1(T)){(T*)in}; }

	forall( dtype S | sized(S) ) {
		S1(T) 	?`xalloc (S * in) 		{ printf(""TS ""); return (S1(T)){(T*)in}; }
	}
}

int main() {
	int * ip = malloc(4);
	int * nip0 = ip`xalloc`post_2;				// prints TT
	int * nip1 = ip`xalloc`post_1`post_2;		// prints TT
	double * ndp0 = ip`xalloc`post_2;			// prints TS
	double * ndp1 = ip`xalloc`post_1`post_2;	// prints TT but should TS
	return 0;
}"	m3zulfiq
Active Tickets	203	Polymorphic Structure Initalization Errors	cfa-cc	1.0		defect		new	2020-07-23T16:36:55-04:00	2020-09-08T22:28:48-04:00	"So I am pretty sure there is more than one bug here, but I'm not entirely sure where the lines are. There are a few places nothing is found, a few times extra options are found. There are times initialization style matters and there are times it doesn't.

Just because it might be a problem I made it a bit easier to pick out each one.

{{{
// Can be changed here or on the command line.
#ifndef SHOW_ERROR
#define SHOW_ERROR 0
#endif

forall(dtype A)
struct empty {
    // Nothing.
};

forall(dtype B)
struct counter {
    int count;
};

forall(dtype C)
struct wrap_e {
    empty(C) field;
};

forall(dtype D)
struct wrap_c {
    counter(D) field;
};

forall(dtype E)
struct wrap_d {
    E * field;
};

forall(otype F)
struct wrap_o {
    F field;
};

forall(dtype G)
struct base_vtable {
    base_vtable(G) const * const parent;
};

forall(dtype H)
struct child_vtable {
    base_vtable(H) const * const parent;
};

empty(int) empty_obj = {};
empty(char) empty_obj = {};
counter(int) count_obj = {5};
counter(char) count_obj = {5};

base_vtable(int) base_vtable_instance @= { 0 };
base_vtable(char) base_vtable_instance @= { 0 };

#if SHOW_ERROR == 2
// No alternatives:
wrap_e(char) error_obj @= { empty_obj };

#elif SHOW_ERROR == 3
// GCC: initializer element is not constant
wrap_c(int) error_obj @= { count_obj };

#elif SHOW_ERROR == 4
// GCC: initializer element is not constant
wrap_c(char) error_obj @= { count_obj };

#elif SHOW_ERROR == 7
// Initalizer too deep:
wrap_e(bool) error_obj = { {} };

#elif SHOW_ERROR == 8
// Initalizer too deep:
wrap_c(bool) error_obj = { { 0 } };

#elif SHOW_ERROR == 9
// Assertion failure:
wrap_e(bool) error_obj @= { {} };

#endif

// Related things that do not produce errors.

int some_int = -7;
char some_char = 'c';

wrap_e(int) wrap_obj = { empty_obj };
wrap_c(int) wrap_obj = { count_obj };
wrap_d(int) wrap_obj = { &some_int };
wrap_d(char) wrap_obj @= { &some_char };
wrap_o(int) wrap_obj = { 13 };
wrap_o(char) wrap_obj @= { 'c' };

// related to SHOW_ERROR == 3
void local_case3() {
    // correctly picks preferred among 2 alternatives
    wrap_c(int) error_obj @= { count_obj };
}

// related to SHOW_ERROR == 4
void local_case4() {
    // correctly picks preferred among 2 alternatives
    wrap_c(char) error_obj @= { count_obj };
}

// formerely SHOW_ERROR == 5
// correctly picks preferred among 2 alternatives
child_vtable(int) child_vtable_instance = {
    &base_vtable_instance
};

// formerely SHOW_ERROR == 6
// correctly picks preferred among 2 alternatives
child_vtable(char) child_vtable_instance @= {
    &base_vtable_instance
};


int main(void) {
    wrap_e(int) wrap_obj = { empty_obj };
    wrap_c(int) wrap_obj = { count_obj };
    wrap_d(int) wrap_obj = { &some_int };
    wrap_d(char) wrap_obj @= { &some_char };
    wrap_o(int) wrap_obj = { 13 };
    wrap_o(char) wrap_obj @= { 'c' };

    {
        wrap_e(bool) alpha;
        wrap_e(bool) beta;
        beta = alpha;
        // alpha.field = beta.field; (See #166.)
        wrap_e(bool) delta = { beta };
    }
    {
        wrap_c(bool) alpha;
        wrap_c(bool) beta;
        beta = alpha;
        // alpha.field = beta.field; (See #166.)
        wrap_c(bool) delta = { beta };
    }

    return 0;
}
}}}"	ajbeach
Active Tickets	205	Incorrect Lvalues On Expressions	cfa-cc	1.0		defect		new	2020-07-29T11:24:43-04:00	2020-07-29T11:24:43-04:00	"There are a some expressions we do not choose the correct lvalue for an expression. Unfortunately some of these are used incorrectly in some other places in the code base so multiple fixes would be required.

The places lvalue does not match C are:

''Cast Expressions'': C casts are never lvalues, currently they pass through the lvalueness of their argument. - Other types of casts seem to be working properly.

''Member Expressions'': These should be lvalues only if the left hand side is an lvalue. Currently they are always lvalues. - Untyped member expressions do the check which does suggest this was a hack at some point.

''Comma Expressions'': These should never be lvalues, currently they are lvalues if there right-hand-side is an lvalue.

''Constant Expressions'': Currently never lvalues, however string literals should be lvalues. Although maybe just for `&` which is a no-op in this situation.

''Name Expressions'': Function names and enumeration value names are not lvalues. Function names are kind of special in a way that it sort of works out. Enumeration values though are represented with names and should be rvalues.

Additional Notes: We don't have to match C's understanding of lvalue on input (as long as we handle it properly) but we do have to on our C output. As we only have one mechanism to handle both for now they are going to have to match. Statement Expressions are non-standard and are not consistently lvalues, it depends on the compiler and for safety we assume they never are lvalues. If you need more information lvalue is one of the value categories in C, try looking that up."	ajbeach
Active Tickets	215	Cannot declare object whose size takes multiple steps of dynamic calculation	cfa-cc	1.0		defect		new	2020-09-09T17:38:14-04:00	2020-09-09T17:38:14-04:00	"CFA-CPP generates bad C code when declaring generic-of-generic objects like vec(vec(T)), where both the size of the generic wrappers depend on the size of the element, and where the original size unit is kept abstract (like T, not like float).  That is, multi-step dynamic size calculations do not work.

Two specific symptoms are noted, which are cases 1 and 2 below.

{{{
forall( otype X )
struct wrap_in { X x; };

forall( otype Y )
struct wrap_out { Y y; };

forall( dtype X )
struct ref_in { X * x; };

forall( dtype Y )
struct ref_out { Y * y; };

#if SHOW_ERR == 1
    forall( otype Z )
    void f() {
        wrap_out( wrap_in( Z ) ) obj;  // GCC: '____sizeof_S7wrap_in_Y1Z_' undeclared
    }
#elif SHOW_ERR == 2
    forall( otype Z )
    void f() {
        wrap_in( Z ) obj_0;
        wrap_out( wrap_in( Z ) ) obj;  // GCC: dereferencing 'void *' pointer AND invalid use of void expression
    }
#endif

// related declarations that work

void f2() {
    wrap_out( wrap_in( float ) ) obj;
}

forall( otype Z )
void f3() {
     ref_out(  ref_in( Z ) ) obj;
     ref_out( wrap_in( Z ) ) obj;
    wrap_out(  ref_in( Z ) ) obj;
}
}}}
In all cases, we expect compiler success.  In the SHOW_ERR cases, the actual error is commented.

Note that case 2 adds an extra valid declaration before the line that is broken in case 1, which acts as a workaround for the case-1 error, meaning that only the case-2 error remains.  The case-2 error also occurs in the case-1 program.

The error of case 1 has a similar symptom to #214.  In #214, the required sizeof variable (and calculation to fill it) is emitted by default, but when the bug's problematic declaration is in scope, that variable is no longer emitted.  Here, the sizeof variable for wrap_in is not emitted by default (case 1), but adding a workaround declaration forces it to be emitted (case 2).

The error of case 2 has no known workaround.
"	mlbrooks
Active Tickets	218	Distributed Qualifiers Change Signature	cfa-cc	1.0		defect		new	2020-09-18T11:02:15-04:00	2020-09-18T11:02:15-04:00	"Using distributed forall qualifiers (the others seem to be fine) can change how a function is resolved and how a name is mangled.

So they most obvious example we have currently is this one:

{{{
forall( otype T ) {
    forall( | { T ?+?( T, T ); } ) {
        forall( | { T ?-?( T, T ); } ) {
            T fred( T t );
        }
    }
}

forall( otype T ) {
    forall( | { T ?-?( T, T ); } ) {
        forall( | { T ?+?( T, T ); } ) {
            T fred( T t ) { return t + t - t; }
        }
    }
}

int main() {
    int i = fred( 3 );
}
}}}

Although this should be one function, forward declared and fully defined, the resolver sees it as two almost identical functions and they are ambiguous. So we get a resolution error.

The one that lead me onto this error is actually much quieter add may be a different problem (further investigation is required).
{{{
forall(dtype T)
struct unique_ptr {
    T * data;
};

forall(dtype T) {
    forall( | { void ^?{}(T &); })
    void move(unique_ptr(T) & this, unique_ptr(T) & that);
}

forall(dtype T | { void ^?{}(T &); })
void move(unique_ptr(T) & this, unique_ptr(T) & that) {
    // ...
}
}}}

This is from the `memory.hfa`/`cfa` module in the standard library. Anyways the resolver doesn't seem to notice any difference between these two. Instead it gets all the way through resolution and the difference only shows up during name mangling where the names come out slightly differently.

`_X4moveQ2_0_0_1__X11_destructorFv_BD0__Fv_S10unique_ptr_BD0_S10unique_ptr_BD0___1`

`_X4moveQ1_0_0_1__X11_destructorFv_BD0__Fv_S10unique_ptr_BD0_S10unique_ptr_BD0___1`

If they are in the same file the call seems to pick the one with a body and everything is fine. If the forward declaration is in a header and the full declaration is in a code file then building will fail on linking."	ajbeach
Active Tickets	220	Reference to Array of Generics does not work	cfa-cc	1.0		defect		new	2020-09-22T20:59:35-04:00	2020-09-22T21:00:56-04:00	"The declaration `float (&a)[7]`, which is pronounced, ""a is a reference to a length-7 array of floats,"" is well-defined, useful, and works.

Its generic version, where the element type T replaces float, ought to be equivalently usable, but it is broken.

{{{
#ifndef __cforall
#include <stdio.h>
#endif

void doPrint(float v) {
    printf(""%f\n"", v);
}

void try1( float (&a)[7] ) {
    printf(""%ld %p %p\n"", sizeof(a), &a, &a[0]);
    doPrint(a[0]);
}

#ifdef __cforall
forall( otype T | {void doPrint(T);} )
void try2( T (&a)[7] ) {
    printf(""%ld %p %p\n"", sizeof(a), &a, &a[0]); // prints
    doPrint(a[0]);  // crashes
}
#endif

int main() {
    float items[7];
    items[0] = 3.14;
    printf(""%f is 0x%x\n"", items[0], *(unsigned int *)&items[0]);
    try1(items);
  #ifdef __cforall
    try2(items);
  #endif
}

}}}

CFA Actual: compiles with output
{{{
3.140000 is 0x4048f5c3
try1: 28 0x7fffffffe2b0 0x7fffffffe2b0
doPrint: 3.140000
try2: 8 0x7fffffffe2b0 0x4048f5c3
Cforall Runtime error (UNIX pid:920) Segment fault at memory location 0x4048f5c3.
}}}

CFA Expected: compiles with output
{{{
3.140000 is 0x4048f5c3
try1: 28 0x7fffffffe2b0 0x7fffffffe2b0
doPrint: 3.140000
try2: 28 0x7fffffffe2b0 0x7fffffffe2b0
doPrint: 3.140000
}}}

g++ Actual and Expected: compiles with output
{{{
3.140000 is 0x4048f5c3
try1: 28 0x7fffffffe370 0x7fffffffe370
doPrint: 3.140000
}}}

The CFA try2 output suggests that `typeof(a)` is `float*` instead of `float[7]`.

The fact that g++ treats try1 consistently with CFA-actual supports CFA-try1 behaviour being a model for the expected behaviour, with the inconsistency of CFA-try2 being a bug.

For the printed in-stack address (0x7ff...), it is not relevant what the absolute value is.  The same value occurs throughout one program run.  It is relevant when the program prints that value, vs 0x4048f5c3.
"	mlbrooks
Active Tickets	221	Reference to Value Assertion Passes Value	cfa-cc	1.0		defect		new	2020-10-05T15:36:12-04:00	2020-10-05T15:36:12-04:00	"If you have an assertion that calls for a reference to a value and have an instance of that value (with the correct name and type) that value will resolve to match the reference (as expected) but the code will not generate properly.

The value will be passed in directly which doesn't work because the type has been converted into a pointer for C compatibility. Adding a `&` would fix this but it has not been added.

Minimal Reproduction:
{{{
struct Cell {
    int x;
};

trait has_cell(dtype T) {
    Cell & cell_instance;
    int & int_instance;
};

Cell cell_instance = { -7 };
int int_instance = -7;

forall(dtype T | has_cell(T))
void func(T & _) {}

int main(int argc, char * argv[]) {
    char x;
    func(x);
    return 0;
}
}}}

Error Message:
{{{
CFA Version 1.0.0 (debug)
minimal.cfa: In function '_X4mainFi_iPPc__1':
minimal.cfa:18:83: error: incompatible type for argument 1 of '_X4funcQ1_0_0_2__X13cell_instanceS4Cell_X12int_instancei_Fv_BD0__1'
   18 |  func(x);
      |                                                                                   ^                        
      |                                                                                   |
      |                                                                                   struct Cell
minimal.cfa:6:39: note: expected 'struct Cell *' but argument is of type 'struct Cell'
    6 |  Cell & cell_instance;
      |                                       ^                        
minimal.cfa:18:110: warning: passing argument 2 of '_X4funcQ1_0_0_2__X13cell_instanceS4Cell_X12int_instancei_Fv_BD0__1' makes pointer from integer without a cast [-Wint-conversion]
   18 |  func(x);
      |                                                                                                              ^                  
      |                                                                                                              |
      |                                                                                                              int
minimal.cfa:7:38: note: expected 'int *' but argument is of type 'int'
    7 |  int & int_instance;
      |  ~~~~~~~~~~~~~~~~~~~                 ^    
}}}

Likely source is that the LValue pass is run before the Box pass and some interaction from that. Which terrifies me."	ajbeach
Active Tickets	225	Wrong disambiguation for call overloaded on bare type-variable vs generic	cfa-cc	1.0		defect		new	2020-11-05T20:50:46-05:00	2020-11-05T20:50:46-05:00	"Overload selection fails to pick the most specific option, when choosing, for a parameter, between T and thing(T).

{{{
forall( dtype T )
void friend( T & ) {
    printf(""friending generically\n"");
}

forall(dtype T)
struct thing {
    int x;
};

forall( dtype T )
void friend( thing(T) & ) {
    printf(""friending specifically\n"");
}

int main() {
    float x;           friend( x );
    thing(float) y;    friend( y );
}
}}}

Actual output:
{{{
friending generically
friending generically
}}}

Expected output:
{{{
friending generically
friending specifically
}}}

Selecting in the expected way is required to support the in-progress design for multidimensional array slices.  This slicing is facilitated by a `packed` structure, which represents objects arranged with nontrivial striding.  The design aims to offer subscripting that appears the same, between traditional arrays, and those that involve `packed` elements.  So the design needs to offer one subscript operator on ""array of T"", and a specialized one on ""array of packed of T"".  The above repro scenario is a simplification of this requirement.

If overload selection stays as Actual, then a subscript that should look like
{{{
a[u][v][w][x][y][z]
}}}
has to use a workaround like
{{{
a[u]`unpack[v][w][x]`unpack[y][z]
}}}
where the locations of the required unpack calls are hard for a user to understand, and are meant to be implementation-hidden.

Some white-box observations about the simple repro:

The call `friend(y)` considers the two alternatives following, as expected.  Their costs are at issue.
{{{
friend: void (*)( T )          [ thing(float) / T ]   ( 0, 1, 0, 0, 1, -1, 1 )   ""opt 1""
friend: void (*)( thing( T ) ) [ float / T ]          ( 0, 1, 0, 0, 1,  0, 1 )   ""opt 2""
}}}

The cost tuples are formatted ( _, poly, _, _, var, spec, _ ), which are documented in Cost.h as:
- poly: Count of parameters and return values bound to some poly type
- var: Count of polymorphic type variables
- spec: Polymorphic type specializations (type assertions), negative cost

The difference between actual and expected behaviour has two levels:

First, why isn't option 2 preferred over option 1?
- ""Actual"" perspective: because the poly and var elements are equal.
- ""Expected"" perspective: they should differ on the poly element, because this is the only element that considers a count of parameters, and we are lookaing at one parameter, where calling it a T is strictly more general than calling it a thing(T)

Second, assuming option 2 isn't preferred, why aren't the options reported as ambiguous?
- ""Actual"" perspective: because the spec elements are different.
- ""Expected"" perspective, for this issue: don't really care
- Long-term perspective: there may be a further bug illustrated here; the code that credits -1 for option 1 is doing so on account of the parameter being a reference (which is dubious, given the stated mission to count assertions), yet this code counts the reference-as-specialization benefit differently for T& vs thing(T)&.  Changing the example to pass otypes by value (so T vs thing(T)) gives ambiguous resolution (with spec=-4).

This does not seem to be the same ambiguity as #194, where there, the desired resolution is in the last-place reference-cost element.

Potential overlap with #195 should be assessed further.
"	mlbrooks
Active Tickets	226	Resolver is slow at finding constructors for 3+ dimensional arrays	cfa-cc	1.0		defect		new	2020-11-06T13:31:20-05:00	2020-12-31T17:46:27-05:00	"
This bug describes a simplification of a case that challenges new multidimensional array work, while doing bread-and-butter operations on short lists of dimensions.

{{{
#ifndef N
#define N 3
#endif

struct nil {};

forall( otype L | sized(L),
        otype R | sized(R) )
struct cons { L l; R r; };

#ifdef NOCTOR
#define XINIT @={}
#else
#define XINIT
#endif

int main() {
  #if   N==0
                                                                          nil           x XINIT;
  #elif N==1
                                                            cons( size_t, nil )         x XINIT;
  #elif N==2
                                              cons( size_t, cons( size_t, nil ) )       x XINIT;
  #elif N==3
                                cons( size_t, cons( size_t, cons( size_t, nil ) ) )     x XINIT;
  #elif N==4
                  cons( size_t, cons( size_t, cons( size_t, cons( size_t, nil ) ) ) )   x XINIT;
  #elif N==5
    cons( size_t, cons( size_t, cons( size_t, cons( size_t, cons( size_t, nil ) ) ) ) ) x XINIT;
  #else
    #error unsupported N
  #endif
}
}}}

Actual performance (seconds)
{{{
N   ctor   no-ctor
0     3.0  3.1
1     3.2  3.0
2     4.0  3.2
3    12    3.1
4   128    3.1
5   (err)  3.1
}}}

Target performance suggestion: under 10 sec for up to N=6
"	mlbrooks
Active Tickets	227	Interpretation of assertions with struct declaration	cfa-cc	1.0		defect		new	2020-11-16T19:56:47-05:00	2025-02-13T11:35:01-05:00	"Currently, assertion parameters placed on a struct declaration is interpreted as assertion on special operators (ctor/dtor/assign), which means they might be elided or forcefully bypassed.

Some test results:


{{{
forall(T | { void foo(T); })
struct S {
  T t;
};

S(int) s1; // error: cannot satisfy assertion for ?{}
S(int) s2 = {0}; // error: cannot satisfy assertion for ?{}
S(int) s3 @= {0}; // ok ...?

forall(T | { void foo(T); })
struct U {
  T * t;
};

U(int) u1; // ok ...?
}}}


A problem comes up, under the ""business as usual"" interpretation, that the assertions apply to the autogen functions.  The problem is that it is possible to declare custom constructors or destructors with fewer assertions than the autogen ones; by the specialization cost element, the custom ones are less specialized than the autogen ones, so the autogen ones are still preferred.

{{{
forall(T)
trait is_happy {
    void sayHi(T);
};

forall(T           | is_happy(T) )
struct thing { T item; };

forall(T
#ifdef REMEMBER_ASSN
                          | is_happy(T)
#endif
                                        )
void ^?{}( thing(T) & ) {
    printf(""custom dtor called\n"");
}

void sayHi(float) {}

int main() {
    thing(float) x;
}
}}}

Actual, plain compile:  Compile succeeds; run produces no output, showing the custom destructor was not called.

Expected, plain compile:  Error, you are obviously trying to declare a destructor, but because your declaration is missing an assertion, it will never be chosen.

Actual and expected -DREMEMBER_ASSN:  Compile succeeds, custom destructor is called.  This is the state that the expected error will help the programmer reach.
"	f37yu
Active Tickets	229	Incorrect implementation of casting to monomorphic	cfa-cc	1.0		defect		new	2020-12-10T14:12:39-05:00	2020-12-10T14:18:17-05:00	"This program attempts to assign a polymorphic function to a compatible monomorphic function pointer. It is allowed by cfa-cpp but produces incorrect C code:


{{{
forall (dtype T | {int asst (T *);}) void foo (T * x) {}

int asst (void *) { return 1; }

void f() {
    void (*bar) (void*);
    bar = foo;
}
}}}

cfa-cpp output:

{{{
void _X3fooQ1_0_0_1__X4asstFi_PBD0__Fv_PBD0__1(__attribute__ ((unused)) signed int (*_X4asstFi_PY1T__1)(void *__param_0), void *_X1xPY1T_1){
}
signed int _X4asstFi_Pv__1(__attribute__ ((unused)) void *__anonymous_object0){
    __attribute__ ((unused)) signed int _X12_retval_assti_1;
    {
        ((void)(_X12_retval_assti_1=1) /* ?{} */);
    }

    return _X12_retval_assti_1;
}
void _X1fFv___1(){
    void (*_X3barFv_Pv__2)(void *__param_0);
    {
        ((void)(_X3barFv_Pv__2=_X3fooQ1_0_0_1__X4asstFi_PBD0__Fv_PBD0__1));
    }

}
}}}

The incompatible type problem is caught by gcc afterwards.

{{{
test.c: In function '_X1fFv___1':
test.c:7:31: warning: assignment to 'void (*)(void *)' from incompatible pointer type 'void (*)(int (*)(void *), void *)' [-Wincompatible-pointer-types]
    7 |     bar = foo;
}}}


"	f37yu
Active Tickets	232	Crash: address-of should not have intrinsic reference argument	cfa-cc	1.0		defect		new	2021-01-14T14:18:59-05:00	2021-01-20T10:42:24-05:00	"The code crashes the compiler due to an assertion:

CFA assertion error* ""false"" from program ""cfa-cpp"" in ""Expression* GenPoly::{anonymous}::AddrRef::postmutate(AddressExpr*)"" at line 302 in file ""../../main/src/GenPoly/Lvalue.cc"": AddrRef : address-of should not have intrinsic reference argument: Application of [...]

{{{
extern ""C"" {
	#include <sys/select.h>
}

int main() {
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(0, &fds);
}
}}}"	Thierry Delisle
Active Tickets	236	SIG_IGN does not compile	cfa-cc	1.0		defect		new	2021-01-18T15:54:16-05:00	2021-01-18T15:54:16-05:00	"This code fails to compile:
{{{
#include <signal.h>

int main() {
	SIG_IGN;
}
}}}

It tries to cast 1 to a function pointer and cforall refuses:
{{{
test.c:4:1 error: Invalid application of existing declaration(s) in expression Explicit Cast of:
  Constant Expression (1: one_t)
  ... with resolved type:
    one_t
... to:
  pointer to function
  ... with parameters
    signed int
  ... returning nothing

... with resolved type:
  pointer to function
  ... with parameters
    signed int
  ... returning nothing

}}}

This is a backwards compatibility problem."	Thierry Delisle
Active Tickets	239	Including kernel but not threads crashes vtable generation	cfa-cc	1.0		defect		new	2021-02-18T16:39:46-05:00	2021-02-18T16:39:46-05:00	"This code crashes the compiler:
{{{
#include <kernel.hfa>

thread test {};
void ?{}(test & this) {}
void ^?{}(test & mutex this) {}
void main(test & this) {}

int main() {
	test t;
}
}}}

It's something to do with vtables:
{{{
CFA Version 1.0.0 (debug)
cfa-cpp: ../../main/src/Concurrency/Keywords.cc:412: DeclarationWithType* Concurrency::ConcurrentSueKeyword::postmutate(FunctionDecl*): Assertion `vtable_decl' failed.
Stack back trace for: /u/tdelisle/workspace/cforall/build/driver/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x36f36 [0x7ffff7a0af36]
(1) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : Concurrency::ConcurrentSueKeyword::postmutate(FunctionDecl*)+0x228 [0x555555eb2138]
(2) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<Concurrency::ThreadKeyword>::mutate(FunctionDecl*)+0x150 [0x555555f3fa70]
(3) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : Concurrency::ThreadKeyword::implement(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&)+0x244 [0x555555edea34]
(4) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : Concurrency::applyKeywords(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&)+0x51 [0x555555eb73f1]
(5) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : SymTab::validate(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&, bool)+0xc25 [0x55555695a545]
(6) /u/tdelisle/workspace/cforall/build/driver/cfa-cpp : main(/*unknown*/)+0x792 [0x555555c33ab2]
CC1 Translator error: stage 2, child failed 6
}}}"	Thierry Delisle
Active Tickets	259	Compiler crash on large recursive assertion requests	cfa-cc	1.0		defect		new	2022-04-09T21:39:08-04:00	2022-04-09T21:39:08-04:00	"This bug report is the closest we have to a reproduction of intermittent failures seen in the array-md-sbscr-cases test.

That test, as written 2022-April-09, fails intermittently on 32-bit overnight builds, cannot be made to fail manually (even on 32-bit), and succeeds on 64-bit.

The case in here fails deterministically when run manually on 32-bit and succeeds on 64-bit.  It fails with the same symptom as in the failed overnight build logs.

The case in here is edited substantially, to remove unnecessary features of array.hfa and all but a minimal repro based on the array-md-sbscr-cases test.

{{{
forall( __CFA_tysys_id_only_X & ) struct tag {};


forall( [N], S &, Timmed &, Tbase & ) {
    struct thing {};

    // get1, first alternative (int overload)
    Timmed & get1( thing(N, S, Timmed, Tbase) & a, int i );
}


forall( TA &, TB &, TC &, IxAB_0, IxBC | { TB & get1( TA &, IxAB_0 ); TC & get1( TB &, IxBC ); } )
TC & get2( TA & this, IxAB_0 ab, IxBC bc );

forall( TA &, TB &, TC &, IxAB_0, IxAB_1, IxBC | { TB & get2( TA &, IxAB_0, IxAB_1 ); TC & get1( TB &, IxBC ); } )
TC & get3( TA & this, IxAB_0 ab0, IxAB_1 ab1, IxBC bc );

forall( TA &, TB &, TC &, IxAB_0, IxAB_1, IxAB_2, IxBC | { TB & get3( TA &, IxAB_0, IxAB_1, IxAB_2 ); TC & get1( TB &, IxBC ); } )
TC & get4( TA & this, IxAB_0 ab0, IxAB_1 ab1, IxAB_2 ab2, IxBC bc );


// Base
forall( [Nq], Sq &, Tbase & )
tag(thing(Nq, Sq, Tbase, Tbase)) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(Tbase) );

// Rec
forall( [Nq], Sq &, [N], S &, recq &, recr &, Tbase & | { tag(recr) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(recq) ); } )
tag(thing(N, S, recr, Tbase)) enq_( tag(Tbase), tag(Nq), tag(Sq), tag(thing(N, S, recq, Tbase)) );

struct all_t {} all;

// get1, second alternative (""all"" overload)
forall( [N], S &, Te &, result &, Tbase & | { tag(result) enq_( tag(Tbase), tag(N), tag(S), tag(Te) ); } )
result & get1( thing(N, S, Te, Tbase) & this, all_t );


void test() {

    thing(5, char, thing(6, char, thing(7, char, thing(8, char, float, float), float), float), float) wxyz;

    float ans1 = get1( get4( wxyz, all, 3, 4,   5 ), 2 );  // works everywhere
    float ans2 = get1( get4( wxyz, 2,   3, all, 5 ), 4 );  // crashes on 32bit, works on 64bit

    float ans3 = get1( get1( get1( get1( get1( wxyz, 2 ), 3), all), 5 ), 4 );  // works everywhere (is the intended expansion of get4 from ans2)
}
}}}

cfa -c thistest.cfa

32-bit, as-is, actual: compiler error, with message beginning
{{{
CFA Version 1.0.0 (debug)
*CFA assertion error* ""inferParam != appExpr->inferParams.end()"" from program ""cfa-cpp"" in ""void GenPoly::{anonymous}::Pass1::addInferredParams(ApplicationExpr*, FunctionType*, std::__cxx11::list<Expression*>::iterator&, const TyVarMap&)"" at line 801 in file ""../../cfa-cc/src/GenPoly/Box.cc"": addInferredParams missing inferred parameter: enq_: extern pointer to function
}}}

32-bit, as-is, expected;
32-bit, edited to remove the ""crashes"" line, actual and expected;
64-bit, as-is, actual and expected:
compilation finishes and produces a *.o file
"	mlbrooks
Active Tickets	260	comparing signal return value crashes	cfa-cc	1.0		defect		new	2022-06-03T16:49:23-04:00	2022-06-03T16:49:23-04:00	"The following code crashes the compiler:
{{{
#include <signal.h>
typedef void (*sighandler_t)(int);
void dummy(int) {}

int main() {
        sighandler_t ret = signal(SIGTERM, dummy);
        if(ret != SIG_ERR) return 1;
}
}}}

The output error is :
{{{
CFA Version 1.0.0 (debug)
*CFA assertion error* ""aftype"" from program ""cfa-cpp"" in ""bool GenPoly::needsTupleSpecialization(Type *, Type *)"" at line 133 in file ""../../main/src/GenPoly/Specialize.cc"": formal type is a function type, but actual type is not: signed int
Stack back trace for: /home/tdelisle/workspace/cforall/build/driver/cfa-cpp
(0) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : GenPoly::needsTupleSpecialization(Type*, Type*)+0x1b7 [0x11bbdc7]
(1) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : GenPoly::Specialize::doSpecialization(Type*, Expression*, std::map<unsigned int, ParamEntry, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, ParamEntry> > >*)+0x3f [0x11bbe5f]
(2) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<GenPoly::Specialize>::mutate(CastExpr*)+0xc6 [0x11c54a6]
(3) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : void maybeMutate_impl<std::__cxx11::list<Expression*, std::allocator<Expression*> >, GenPoly::Specialize>(std::__cxx11::list<Expression*, std::allocator<Expression*> >&, PassVisitor<GenPoly::Specialize>&)+0xcb [0x11ce7db]
(4) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<GenPoly::Specialize>::mutate(ApplicationExpr*)+0x9a [0x11c4f2a]
(5) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : void maybeMutate_impl<std::__cxx11::list<Expression*, std::allocator<Expression*> >, GenPoly::Specialize>(std::__cxx11::list<Expression*, std::allocator<Expression*> >&, PassVisitor<GenPoly::Specialize>&)+0xcb [0x11ce7db]
(6) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<GenPoly::Specialize>::mutate(ApplicationExpr*)+0x9a [0x11c4f2a]
(7) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<GenPoly::Specialize>::mutate(IfStmt*)+0x49 [0x11c42c9]
(8) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : void PassVisitor<GenPoly::Specialize>::handleStatementList<PassVisitor<GenPoly::Specialize>::mutateStatementList(std::__cxx11::list<Statement*, std::allocator<Statement*> >&)::{lambda(Statement*&)#1}>(std::__cxx11::list<Statement*, std::allocator<Statement*> >&, PassVisitor<GenPoly::Specialize>::mutateStatementList(std::__cxx11::list<Statement*, std::allocator<Statement*> >&)::{lambda(Statement*&)#1})+0x275 [0x11ce2c5]
(9) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : non-virtual thunk to PassVisitor<GenPoly::Specialize>::mutate(CompoundStmt*)+0x36 [0x11c8a56]
(10) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : PassVisitor<GenPoly::Specialize>::mutate(FunctionDecl*)+0xa7 [0x11c3667]
(11) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : void mutateAll<GenPoly::Specialize>(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&, PassVisitor<GenPoly::Specialize>&)+0xed [0x11bd29d]
(12) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : GenPoly::convertSpecializations(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&)+0xa0 [0x11bcc40]
(13) /home/tdelisle/workspace/cforall/build/driver/cfa-cpp : main(/*unknown*/)+0x2541 [0x9852a1]

}}}"	Thierry Delisle
Active Tickets	263	Missing Code Generation on Control Structure Initializers	cfa-cc	1.0		defect		new	2022-07-13T11:33:50-04:00	2022-07-13T11:33:50-04:00	"Cforall adds initializers to some control structures that C did not have (if and while). These are ignored always during code generation. While this is fair while generating C code, as everything has been moved out of them, but if we do a dump ""ascodegen"" then we can still have code in there and it does not appear there.

This lead me astray for a while when I was doing some debugging. If it happens there probably be one of those assertions that makes sure we are not generating C code."	ajbeach
Active Tickets	265	Compilation failure passing zero_t through assertions	cfa-cc	1.0		defect		new	2022-10-20T13:39:21-04:00	2022-10-20T13:39:21-04:00	"{{{
forall( T | { void helper(T); } )
void wrapper( T x ) {
    helper(x);
}

void helper( zero_t ) { printf(""zero_t overload\n""); }
void helper( one_t )  { printf(""one_t overload\n""); }
void helper( int x )  { printf(""int overload, value %d\n"", x); }

int main() {
                                #ifndef SUPPRESS_AT_ISSUE_COMPILE_ERROR
    wrapper( 0 );
                                #endif

    wrapper( 1 );
    wrapper( (int) 0 );
    wrapper( (int) 1 );
}

/*
$cfa x.cfa; ./a.out
ACTUAL: Unintelligible error message from GCC blaming the `wrapper(0)` call
EXPECTED: compiles and runs with output:
    zero_t overload
    one_t overload
    int overload, value 0
    int overload, value 1

$cfa x.cfa -DSUPPRESS_AT_ISSUE_COMPILE_ERROR; ./a.out
ACTUAL, EXPECTED: compiles and runs with output:
    one_t overload
    int overload, value 0
    int overload, value 1

Diagnostic:
$cfa x.cfa -CFA -XCFA,-p | grep -F '(void)(0=0)'
ACTUAL: Found
EXPECTED: uh... No.

*/
}}}

There is an impact on a programmer using new arrays, when trying to index into a higher-dimensional array using a hardcoded 0 as an index.  The repro isn't runnable on the currently-committed source.  I'll put the repro here, once the change required to reach the issue is committed.
"	mlbrooks
Active Tickets	267	Expression candidate elimination with unsatisfiable assertion	cfa-cc	1.0		defect		new	2022-11-16T00:08:23-05:00	2022-11-16T00:08:23-05:00	"Reproduction:

{{{
int * g (int ) ;
long * g (long );

void h (long *);

forall (T * | {void h (T *); })
void f (T *);


int main() {
f(g(1));
}
}}}

Expected: calls g(long)
Actual: Attempts to resolve as g(int) and prints unsatisfiable assertion error. g(long) option is ignored."	f37yu
Active Tickets	268	Downcasting against type parameter failed	cfa-cc	1.0		defect		new	2022-12-13T16:03:46-05:00	2022-12-13T16:56:54-05:00	"Minimal reproduction:

{{{
forall (T) struct wrap {
    T elem;
};

forall (T) void foo (wrap(T) x, T y) {
}



int main() {
    wrap(int) w{10};
    foo (w, 2.0); // error?
}
}}}


"	f37yu
Active Tickets	272	Compiler crash when referencing generic member of generic struct	cfa-cc	1.0		defect		new	2023-02-03T15:13:43-05:00	2023-02-03T15:13:43-05:00	"Possible overlap with #166, but the specific failure cases and symptom here are different.  Some of the test-case driving presented here might help drive a similar breadth of cases for working #166.

Headline case:

{{{
forall( E & ) struct inner { E * item; };
forall( E & ) struct outer { inner(E) wrapped; };

forall(E &)
void f( outer(E) & out, E * elem ) { 
    out.wrapped.item = elem;
}
}}}

ACUTAL: Assertion failure in `Type::genericSubstitution()` during new-to-old AST conversion.

EXPECTED: Compiles successfully. If run with the harness below, prints `-1.000000 3.140000 1.414000`.

The breadth of cases that follows illustrates when the issue does (not) happen.  I don't expect you to read the preprocessor acrobatics, but I hope you can understand the -E output of each concrete case.  The headline case above is a small formatting edit of the -E output of the no-defines case, whose explicit coordinates are `-DRUNCASE=1 -DTYPECASE_INNER_GENER -DTYPECASE_OUTER_GENER -DTYPECASE_WRAPPER_TRAD`.

demo.cfa:
{{{
#if 1 < ((defined TYPECASE_INNER_GENER) + (defined TYPECASE_INNER_CONCR))
#error multiple TYPECASE_INNER_X
#endif

#if 1 < ((defined TYPECASE_OUTER_GENER) + (defined TYPECASE_OUTER_CONCR))
#error multiple TYPECASE_OUTER_X
#endif

#if 1 < ((defined TYPECASE_WRAPPER_TRAD) + (defined TYPECASE_WRAPPER_PLN9))
#error multiple TYPECASE_WRAPPER_X
#endif


#if (!defined TYPECASE_INNER_CONCR) && (!defined TYPECASE_INNER_GENER)
#define TYPECASE_INNER_GENER
#endif

#if (!defined TYPECASE_OUTER_CONCR) && (!defined TYPECASE_OUTER_GENER)
#define TYPECASE_OUTER_GENER
#endif

#if (!defined TYPECASE_WRAPPER_PLN9) && (!defined TYPECASE_WRAPPER_TRAD)
#define TYPECASE_WRAPPER_TRAD
#endif



                        #define DEFTY_INNER_FRAG(T) struct inner { T * item; };
#if defined TYPECASE_INNER_GENER
                        #define DEFTY_INNER  forall( E & ) DEFTY_INNER_FRAG(E)
                        #define TY_INNER(T)  inner(T)
#else
                        #define DEFTY_INNER  DEFTY_INNER_FRAG(float)
                        #define TY_INNER(T)  inner
#endif

#if defined TYPECASE_WRAPPER_TRAD
                        #define DEFFLD(ty)   ty wrapped;
                        #define FLD(obj)     obj.wrapped
#else
                        #define DEFFLD(ty)   inline ty;
                        #define FLD(obj)     obj
#endif

                        #define DEFTY_OUTER_FRAG(T) struct outer { DEFFLD(TY_INNER(T)) };
#if defined TYPECASE_OUTER_GENER
                        #define DEFTY_OUTER  forall( E & ) DEFTY_OUTER_FRAG(E)
                        #define TY_OUTER(T)  outer(T)
                        #define DEFFUNC(...) forall(E &) { __VA_ARGS__ }
#else
                        #define DEFTY_OUTER  DEFTY_OUTER_FRAG(float)
                        #define TY_OUTER(T)  outer
                        #define DEFFUNC(...) __VA_ARGS__
#endif
                        #define GENTY_OUTER  TY_OUTER(E)

#if (defined TYPECASE_INNER_GENER) && (defined TYPECASE_OUTER_GENER)
                        #define ELEM E
#else
                        #define ELEM float
#endif



DEFTY_INNER
DEFTY_OUTER


                                 #if   RUNCASE==4 || RUNCASE==5 || RUNCASE==6 || RUNCASE==7
DEFFUNC (
    void rval_helper( ELEM * ) {
        // this helper is a nop
        // SUT action to be done as follow-up step
    }
    void lval_helper( ELEM * & tgt, ELEM * val ) {
        // this helper does the SUT action
        tgt = val;
    }
    void mockStep( ELEM * & tgt, ELEM * val ) {
    }
)
// follow-up step for no-op variations
// applies workaround B (assumes it succeeds)
// (cases that use the mock action only illustrate wheteher the compiler claims success)
#define MOCK_SUT_ACTION ((TY_INNER(ELEM)&)(FLD(out))).item = elem; 

                                 #endif

DEFFUNC (
    void f( GENTY_OUTER & out, ELEM * elem ) {
                                 #if   RUNCASE==1  // headline, maximally broken
        FLD(out).item = elem;
                                 #elif RUNCASE==2  // workaround A, successful sometimes
        (*&(FLD(out))).item = elem;
                                 #elif RUNCASE==3  // workaround B, successful always
        ((TY_INNER(ELEM)&)(FLD(out))).item = elem;
                                 #elif RUNCASE==4  // variation as top-level expr, not broken
        FLD(out).item;
        MOCK_SUT_ACTION
                                 #elif RUNCASE==5  // variation as simple r-value, not broken
        rval_helper( FLD(out).item );
        MOCK_SUT_ACTION
                                 #elif RUNCASE==6  // variation as simple l-value, blocked by #166
        lval_helper( FLD(out).item, elem );
                                 #elif RUNCASE==7  // variation as hacked l-value, maximally broken
        // here, *& is a #166-workaround
        // looks like workaround A: coincidence?
        lval_helper( *& FLD(out).item, elem );
                                 #else
                                 #error bad RUNCASE
                                 #endif
    }
)

// Gives a demonstration of ""compiler does right thing,"" as more than ""compiler claims success.""
// Except for runcases 4,5.
// While the innermost datum being a pointer adds complexity at the harness level, it removes a
// confounding factor from the core repro: the failure happens with `forall(E&)`.  Switching the
// core datum to a non-pointer would make these declarations `forall(E)`, which would add boxing
// to the polymorphic calls.
int main() {

    TY_OUTER(float) obj;

    // `obj` content test point: because ""proper"" access via fields is SUT,
    // circumvent that for the test harness
    float * & objContTP = * (float **) & obj;

    // state 1, via TP: obj points to oldbox
    float oldbox = -1.0;
    objContTP = &oldbox;
    printf(""%f "", *objContTP);

    // state 2, via SUT: obj points to newbox
    float newbox = 3.14;
    f(obj, &newbox);
    printf(""%f "", *objContTP);

    // mutation in state 2: show state 2 has the pointing expected
    newbox = 1.414;
    printf(""%f\n"", *objContTP);
}
}}}

run.sh:
{{{
dotest() {
#   (set -x; $cfa demo.cfa $RUNCASE $TYPECASE_INNER $TYPECASE_OUTER $TYPECASE_WRAPPER -E           )
    (set -x; $cfa demo.cfa $RUNCASE $TYPECASE_INNER $TYPECASE_OUTER $TYPECASE_WRAPPER    && ./a.out)
}

# Failures that are success-adjacent, and successes that are failure-adjacent, are driven here.

for RUNCASE in '-DRUNCASE=1'
do
for TYPECASE_INNER in '-DTYPECASE_INNER_GENER' '-DTYPECASE_INNER_CONCR'
do
for TYPECASE_OUTER in '-DTYPECASE_OUTER_GENER' '-DTYPECASE_OUTER_CONCR'
do
for TYPECASE_WRAPPER in '-DTYPECASE_WRAPPER_TRAD' '-DTYPECASE_WRAPPER_PLN9'
do
    dotest
done
done
done
done

for RUNCASE in '-DRUNCASE=2' '-DRUNCASE=3'
do
for TYPECASE_INNER in '-DTYPECASE_INNER_GENER'
do
for TYPECASE_OUTER in '-DTYPECASE_OUTER_GENER'
do
for TYPECASE_WRAPPER in '-DTYPECASE_WRAPPER_TRAD' '-DTYPECASE_WRAPPER_PLN9'
do
    dotest
done
done
done
done

for RUNCASE in '-DRUNCASE=4' '-DRUNCASE=5' '-DRUNCASE=6' '-DRUNCASE=7'
do
for TYPECASE_INNER in '-DTYPECASE_INNER_GENER'
do
for TYPECASE_OUTER in '-DTYPECASE_OUTER_GENER'
do
for TYPECASE_WRAPPER in '-DTYPECASE_WRAPPER_TRAD'
do
    dotest
done
done
done
done


# All combinations are driven here
# ""All combinations pass"" is a fine condition for statisfaction.

# for RUNCASE in '-DRUNCASE=1' '-DRUNCASE=2' '-DRUNCASE=3' '-DRUNCASE=4' '-DRUNCASE=5' '-DRUNCASE=6' '-DRUNCASE=7'
# do
# for TYPECASE_INNER in '-DTYPECASE_INNER_GENER' '-DTYPECASE_INNER_CONCR'
# do
# for TYPECASE_OUTER in '-DTYPECASE_OUTER_GENER' '-DTYPECASE_OUTER_CONCR'
# do
# for TYPECASE_WRAPPER in '-DTYPECASE_WRAPPER_TRAD' '-DTYPECASE_WRAPPER_PLN9'
# do
#     dotest
# done
# done
# done
# done
}}}

Invoking `./run.sh` gives

ACUTAL:  mix of crashes, as in headline-actual, and successful runs, as in headline-expected, according to the case summary following, except with RUNCASE=6 being an occurrence of #166

EXPECTED:  always a successful run, as in headline-expected, except with RUNCASE=6 being either a successful run or an occurrence of #166

Summary of the cases and actual outcomes (to be read along with the -E dump):
- RUNCASE=1 is the vanilla version.  Running it across varied generic/concrete options for TYPECASE_INNER and TYPECASE_OUTER shows that it only happens when both levels are generic.  Running it across varied traditional/plan-9 options for TYPECASE_WRAPPER shows that it is unaffected by using a plan-9 wrapping of the inner within the outer.
- RUNCASE=2 (add `*&`) and RUNCASE=3 (add a cast to the desired reference type) are possible workarounds.  Adding `*&` works for a traditional wrapper but not for a plan-9 wrapper.  Adding a cast always works.
- The variations for RUNCASE in 4..7 show that no magic coming from the assignment operator is at issue, rather, that seeking a _reference_ to the contained element seems to be the problem (as in 6 and 7 being broken), while mere mention of the contained element works (as in 4 and 5 succeeding).
"	mlbrooks
Active Tickets	274	Size of struct and alignment/offset of polymorphic fields inside a struct is incorrect on 32 bit	cfa-cc	1.0		defect		new	2023-05-15T09:20:02-04:00	2023-05-15T09:20:02-04:00	"If a polymorphic field of a struct is not the first field the alignment of fields and the size of the struct on 32 bit may be wrong in certain cases. The generated alignment of field accesses seems to differ if the struct is passed by value or reference. Additionally, there may be some relation to fields being 4-byte aligned in some cases on 32-bit.

While this is not fixed, it can be avoided in any cases where you only have one polymorphic field in a struct by making the polymorphic field the first field in the struct.

Repro with some prints to highlight issues:

{{{#!div style=""font-size: 80%""
Code highlighting:
  {{{#!c
forall( T ) {
struct chan_write {
    int a;
    T elem;
};

static inline void ?{}( chan_write(T) & cw, T elem ) {
    printf(""ctor\tsize of cw: %lu, size of a: %zu, size of elem: %lu\n"", sizeof(cw), sizeof(cw.a), sizeof(cw.elem));
    cw.elem = elem;
    printf(""ctor\telem: %lld, elem ptr: %p, cw ptr: %p\n"", *((long long int *)&cw.elem), &cw.elem, &cw);
}

static inline void print( chan_write(T) & cw ) {
    printf(""PRINT\tsizeof cw: %lu, sizeof a: %zu, sizeof elem: %lu\n"", sizeof(cw), sizeof(cw.a), sizeof(cw.elem));
    printf(""PRINT\telem: %lld, elem ptr: %p, cw ptr: %p\n"", *((long long int *)&(cw.elem)), &cw.elem, &cw);
}

static inline void print_noref( chan_write(T) cw ) {
    printf(""NoRef\tsizeof cw: %lu, size of a: %zu, sizeof elem: %lu\n"", sizeof(cw), sizeof(cw.a), sizeof(cw.elem));
    printf(""NoRef\telem: %lld, elem ptr: %p, cw ptr: %p\n"", *((long long int *)&(cw.elem)), &cw.elem, &cw);
}
}

int main() {
    long long int INDEX = 1;
    printf(""i: %lld, sizeof i: %zu\n"", INDEX, sizeof(INDEX));
    chan_write(long long int) apple{ INDEX };
    printf(""main\tsizeof cw: %zu, sizeof a: %zu, sizeof elem: %zu\n"", sizeof(apple), sizeof(apple.a), sizeof(apple.elem));
    printf(""main\telem: %lld, elem ptr: %p, cw ptr: %p\n"", apple.elem, &apple.elem, &apple);
    print( apple );
    print_noref( apple );
    return 0;
}
  }}}
}}}"	caparson
Active Tickets	277	Constructors for nested types suffer from definition before use issues	cfa-cc	1.0		defect		new	2023-06-20T13:43:56-04:00	2023-06-20T13:43:56-04:00	"The following program does not call the appropriate constructor for A when constructing B so the print does not occur.

{{{
#!div style=""font-size: 80%""
  {{{#!C
struct A {};
struct B { A a; };
void ?{}( A & this ) { printf(""A ctor\n""); }

int main() {
    B b;
}
  }}}
}}}"	caparson
Active Tickets	278	Performance issue on ARM64 due to __aarch64_sync_cache_range calls	cfa-cc	1.0		defect		new	2023-06-20T13:59:07-04:00	2023-06-20T13:59:07-04:00	"On the ARM64, calls to aarch64_sync_cache_range occur when a pointer to a nested function is stored. This is likely due to the compiler worrying about self-modifying code.

The following minimal repro will generate a call to aarch64_sync_cache_range:
{{{
#!div style=""font-size: 80%""
  {{{#!C
  int main() {
        inline void foo() {}
        void (*f)(void) = foo;
  }
  }}}
}}}

Calls to aarch64_sync_cache_range are very expensive since they involve a memory barrier instruction ISB.
 ISB - whenever instruction fetches need to explicitly take place after a certain point in the program, for example after memory map updates or after writing code to be executed. (In practice, this means ""throw away any prefetched instructions at this point"".)

Currently we store pointers to nested functions in 2 cases: polymorphic adapters and destructors of polymorphic fields.

We believe that the adapter issue can be fixed by hoisting adapters to global scope since they do not need a closure.

The dtor issue may be able to be fixed by changing the calling convention of destructors to not store a function pointer:

{{{
#!div style=""font-size: 80%""
  {{{#!C
__attribute__ ((cleanup(__destroy_Destructor))) struct __Destructor __memberDtor0 = { ((void *)_X1tS1A_Y1T__1), ((void (*)(void *__param_0))__cleanup_dtor20) }; // old way
    __attribute__ ((cleanup(__cleanup_dtor20))) void * nd = ((void *)_X1tS1A_Y1T__1); // fix
  }}}
}}}

Attached is a program that generates output with both the issues. Also attached is a trimmed down version of the output C code with the fixes implemented inline."	caparson
Active Tickets	280	Compound Literal Hoisted Out of Function/typeof	cfa-cc	1.0		defect		new	2023-07-21T17:09:23-04:00	2025-05-02T16:05:06-04:00	"A simple example of the problem:
{{{
forall( T & )
struct proxy {};

forall( T & )
void action( typeof( (proxy(T)){} ) & p ) {
    (void)p;
}
// Becomes (trimmed and cleaned):
proxy(T) _compLit0 = {};
forall(T &)
void action(typeof(_compLit0) &p){
    ((void)p);
}
}}}

Because T is a data type and the proxy constructor has no assertions, this actually compiles, it seems adding anything else to it will cause errors. However, it is non-sense and we should not be generating it.

To avoid cases like this, there should not be hoisting out of typeof, sizeof or other non-evaluated contexts. Since they are not evaluated anyways we don't actually need the memory.

If there are other cases where the storage would have to be hoisted out of a function and are evaluated, they may have to forbidden if the constructed type is polymorphic. Or handled specially depending on the case."	ajbeach
Active Tickets	282	Double Member Expressions Cannot Be Used as Lvalues	cfa-cc	1.0		defect		new	2023-07-26T17:03:57-04:00	2025-03-25T11:17:03-04:00	"Getting the address of a field of a field does not work:

{{{
forall( U & )
struct LinkData {
    U * top;
};

forall( S & )
struct Link {
    LinkData(S) data;
};

void g1(int*) {}
void g2(int**) {}
void g3(int*&) {}
void f(Link(int) * link)  {
    // This works:
    g1( link->data.top);
    // Assertion failure in convert.
    g2(&link->data.top);
    // Assertion failure in convert.
    g2(&(((*link).data).top));
    // This works:
    g2(&(*&(link->data)).top);
    // error: lvalue required as unary '&' operand
    g3( link->data.top);
}
}}}

I did some investigation of this error but was unable to find a solution. The error seems to occur in ""Convert L-Value"" particularly in reference type elimination when some references, and the implicit dereferences they imply, are removed.

Both CastExpr and MemberExpr don't currently track what there lvalue status would be in C, that may be related. I also have a note that FixDtypeStatic may be related but I have since forgotten the rational for that.

Because the error message is triggered by convert, as translation continues and eventually the conversion may never need to happen, the initial error messages will probably change."	ajbeach
Active Tickets	283	Shadowing Cause C Incompatability	cfa-cc	1.0		defect		new	2023-09-11T15:20:05-04:00	2023-09-11T15:20:05-04:00	"In the following program compiles in both C and Cforall but shows different behaviour in them:
{{{
#include <stdio.h>

int load = 2;

int main(void) {
    double load = 3.0;
    printf(""load (as int) = %d\n"", (int)load);
}
}}}
The printed number is 3 in C and 2 in Cforall.

This seems to be a result of the differing shadowing rules in C and Cforall."	ajbeach
Active Tickets	290	Two-Argument Conditional Revaluates Condition	cfa-cc	1.0		defect		new	2024-08-20T13:57:51-04:00	2025-02-27T16:42:37-05:00	"The two-argument conditional currently is implemented as a short hand and evaluates its condition twice instead of once. For example the following piece of code prints ""yes"" twice instead of once:
{{{
bool yes() {
    printf(""yes\n"");
    return true;
}

int main(int argc, char * argv[]) {
    bool result = yes() ?: false;
    printf(""result: %s\n"", result ? ""true"" : ""false"");
}
}}}

This is because, under the hood we copy the entire expression instead of using the result of one expression twice. To write it out in code:
{{{
condExpr ?: elseExpr
// Is treated as:
condExpr ? condExpr : elseExpr
// Should be treated as:
({ TYPE NAME = condExpr; NAME ? NAME : elseExpr; })
}}}

(Where TYPE and NAME are found internally, if they explicitly exist at all.) This will probably require some resolver trickery as the expression must be resolved so it can be used as both the condition-expression and then-expression.

It is also theoretically possible to construct a case where one of the condition and then-expression are resolved differently than the expression that should be used in both locations. Or even both. I haven't constructed such a case and expect it is extremely rare in practice."	ajbeach
Active Tickets	291	Cannot Lift Reference Initialization	cfa-cc	1.0		defect		new	2024-10-29T14:28:00-04:00	2024-10-29T15:46:56-04:00	"The initial symptom of this error is a problem in the box pass. But that is because of a floating node, which you can catch with the invariant check, revealing that it comes from Fix Init.

Here is code to reproduce the error:
{{{
int var = 4;

int & getVar() {
    return var;
}

int & ref = getVar();
}}}

There are various ways this can cause errors, but the root cause seems to be fairly consistent. That is reference initializers cannot be hoisted into the `__global_init__` function. This decision is made inside the resolver even though the actual lifting happens later.

The decision is communicated from the resolver to the hoisting pass (Fix Global Init, part of Fix Init) by wrapping the initializer in a ConstructorInit. There are two sets of fields on the ConstructorInit, ctor/dtor for a managed type and init for an unmanaged type.

At least that appears to be the intent, a lone Init couldn't be hoisted to an assignment as is, but also that entire option isn't used here so that is just a guess at what the intent is. The result is that only constructors are hoisted.

On a very basic level, all non-constant expressions need to be hoisted, because C will not evaluate them at the global scope. All functions are not constant, hence all constructors are not constant and do need to be hoisted. However, some non-constructor initializers also need to be lifted.

Original issue was discovered with the following example. This has the issue with a non-constant expressions but also some polymorphic code that is not specialized outside of a function and a temporary value that is lost with no block to insert it into.
{{{
#include <stdlib.hfa>

struct S {
    int x;
};

S & s = (*malloc()){ 2 };
}}}"	ajbeach
Active Tickets	299	Unusable overload allowed, between array and pointer	cfa-cc	1.0		defect		new	2025-01-02T15:29:10-05:00	2025-01-02T18:54:25-05:00	"{{{
    #ifndef SUPPRESS_COMP_ERR
    void f1() {
        int a[100];
        int *a;                     // dup
        a[0];                       // ambiguous
    }

    void f2() {
        int *a;
        int a[100];                 // dup
        a[0];                       // ambiguous
    }
    #endif

    int a[100];

    void f3() {
        int x = 42;
        int *a = & x;
        printf(""%d\n"", a[0]);       // ambiguous
    }

    void f4( int *a ) {
        printf(""%d\n"", a[0]);       // ambiguous
    }

    void f5( int a[100] ) {
        printf(""%d\n"", a[0]);       // ambiguous
    }

    int main() {
        int x = 42;
        f3();
        f4( & x );
        f5( & x );
        return 0;
    }
}}}

    CFA actual (with and without -DSUPPRESS_COMP_ERR): compiler rejection ""Cannot choose between 2 alternatives for a"" at all five lines commented ""ambiguous""

    GCC actual, CFA exptected (without -DSUPPRESS_COMP_ERR): compiler rejection ""conflicting types"" or ""duplicate definition"" at two lines commented ""dup""

    GCC actual, CFA exptected (with -DSUPPRESS_COMP_ERR): runs and prints ""42"" three times
"	mlbrooks
Active Tickets	301	Incorrect & When Building Reference To Pointer	cfa-cc	1.0		defect		new	2025-01-22T17:07:58-05:00	2025-01-22T17:07:58-05:00	"If you write the following code:
{{{
void callee(int * const & value) {
    (void)value;
}

void caller() {
    int data[2] = {0, 0};
    callee(&data[0]);
}
}}}

Then you get the following error:
{{{
repro-lvalue.cfa: In function ‘_X6callerFv___1’:
repro-lvalue.cfa:10:58: error: lvalue required as unary ‘&’ operand
   10 |  callee(&data[0]);
      |
}}}

Investigating it, it does happen during the lowering from Cforall to C. As the references are replaced with pointers, the conversion from the innermost reference to a pointer conflicts with the `&` that is already there. The result is to address-of operators in a row, which actually works out type wise but the address-of operator isn't an lvalue and we cannot take its address. This is mixed in with (and missed by) the pass that handles lifting some expressions to temporaries so we can take their address.

This might be because of some bad/unexpected typing information or it may be adding address-of near an existing address-of was never considered.

This is a blocking issue for #133 and may be related to #282."	ajbeach
Active Tickets	303	Static Functions Used Inside Inline Functions (socket2.h)	cfa-cc	1.0		defect		new	2025-02-13T14:32:33-05:00	2025-02-18T12:57:01-05:00	"To reproduce:
{{{
#include <concurrency/actor.hfa>
}}}

This will lead to warnings from GCC that a static function function is being used in an inline function. In particular the inline function is `recvfrom` and the {{{__SOCKADDR_ARG}}} constructor[1] and destructor[2].

I have not done the full investigation, so solving these two instances may actually be unrelated with similar symptoms. However, they are both instances of Cforall operations not being properly ""lowered"" to C. The constructor call should be converted to a C initializor (and there are cases where that happens). We should be able to omit the destructor call because it should be a no-op, but I don't know if we have any tools for that.

[1] `_X12_constructorFv_U26__anonymous___SOCKADDR_ARGU26__anonymous___SOCKADDR_ARG_autogen___1`
[2] `_X11_destructorFv_U26__anonymous___SOCKADDR_ARG_autogen___1`"	ajbeach
Active Tickets	305	Rvalue-to-reference promotion circumvents RAII	cfa-cc	1.0		defect		new	2025-03-05T13:42:23-05:00	2025-03-05T13:42:23-05:00	"For background, present-state rvalue-to-reference promotion allows this case:

{{{
int foo() {
    return 42;
}
int & x = foo();
// ... call whatever else
printf(""%d\n"", x); // 42
}}}

Ongoing work to rule out certain cases may consider ruling out this case too.

Perhaps surprisingly, present-state handling does not consider this arrangment to be an instant memory error.  The returned value's lifetime is not limited to the call expression; the value lives in a hoised temporary with lifetime roughly block scope.

As it is, objects like this returned 42, should they have RAII, miss an RAII callback step.  A demo follows.

First, we need an object that logs its RAII calls (included for reproduction, can skip on reading):

{{{
struct Obj { 
    int idCopied;
    int idNotCopied;
};

int nextIdCopied = 1;
int nextIdNotCopied = 1;

ptrdiff_t getRelativeAddr( void * addr ) {
    size_t addrVal = (size_t) addr;
    static size_t refAddr = 0;
    if (refAddr == 0 ) refAddr = addrVal;
    return refAddr - addrVal;
}

void ?{}( Obj & this ) {
    this.idCopied = nextIdCopied;
    this.idNotCopied = nextIdNotCopied;
    nextIdCopied += 1;
    nextIdNotCopied += 1;
    printf(""ctor dflt @%+6zd, $%d, #%d\n"", getRelativeAddr(&this), this.idCopied, this.idNotCopied);
}

void ?{}( Obj & this, Obj src ) {
    this.idCopied = src.idCopied;
    this.idNotCopied = nextIdNotCopied;
    nextIdNotCopied += 1;
    printf(""ctor copy @%+6zd, $%d, #%d:=#%d\n"", getRelativeAddr(&this), this.idCopied, this.idNotCopied, src.idNotCopied);
}

void ^?{}( Obj & this ) {
    printf(""dtor      @%+6zd, $%d, #%d\n"", getRelativeAddr(&this), this.idCopied, this.idNotCopied);
}

void observe( Obj & this ) {
    printf(""observe   @%+6zd, $%d, #%d\n"", getRelativeAddr(&this), this.idCopied, this.idNotCopied);
}

Obj bar() {
    return (Obj){};
}
}}}

Now the real test harness:
{{{
printf(""==== Case 1, baseline\n"");
{
    Obj o1 = bar();
    observe(o1);
}
printf(""==== Case 2, SUT\n"");
{
    Obj & o2 = bar();
    observe(o2);
}
}}}

Output key:
@_ is an address offset from an arbitrary point
$_ is a value carried in an object and kept by all its copies
#_ is like a version number, different for each ctor-defined copy

Actual: Runs with output
{{{
==== Case 1, baseline
ctor dflt @    +0, $1, #1
ctor copy @    +8, $1, #2:=#1
dtor      @    +0, $1, #1
ctor copy @  -136, $1, #3:=#2
dtor      @  -144, $1, #2
observe   @  -136, $1, #3
dtor      @  -136, $1, #3
==== Case 2, SUT
ctor dflt @    +0, $2, #4
ctor copy @    +8, $2, #5:=#4
dtor      @    +0, $2, #4
observe   @  -144, $2, #5
dtor      @  -144, $2, #5
}}}

Expected: Runs with case-1 output as-is, case-2 shows same variable/RAII pattern as case-1;  OR  compile error that returned values cannot be saved in reference variables

Further background:

All copy ctors have the anomaly that the src is just bits at a meaningless address (not even shown in output),
for which no lifecycle-maintence occurs.  Work is in progress to fix it (change copy ctor signature to use const reference for `src` argument).

Baseline has the known anomaly that `#2` shows up at a different address (`dtor @-144`) than where it was created (`ctor @+8`).  It's not desired, but a fix is not coming soon (requires custom ABI or using the Box Pass in more cases).  ""Expected"" outcome statement assumes present-state on this matter.  The good being illustrated is that `observe`, a user's function, sees an address (`#3 @-136`) that got a lifecycle maintenance call (`copy ctor #3 @-136`).
The anomaly is contained to phenomena seen by the RAII routines, meaning it's just a limitation on what kinds of RAII can be used along with returning by value.

Illustration of the issue with rvalue-to-reference promotion:

SUT has an anomaly that `observe`, a user's function, sees an address (`#5 @-144`) never before ""made right"" by lifecycle maintenance.  So, an object that contains references into itself, which normally can be returned by value (provided that its RAII accommodates the baseline anomaly), presents garbage if the returned value is stored in a reference.
"	mlbrooks
Active Tickets	307	Rvalue-reference promotion missed temporaries	cfa-cc	1.0		defect		new	2025-03-12T17:19:12-04:00	2025-03-12T17:19:12-04:00	"Another issue when building references and double-references.  Possibly related to #301 and #282.  Peter found while trying to reproduce #305.  Team discussed at meeting 2025-03-12.

{{{
#include <fstream.hfa>

struct S {};
void ?{}( S & s ) { sout | 'C'; }
void ^?{}( S & s ) { sout | 'D'; }

void foo( S && rrs ) {}

int main() {
	const int & xxx = 42; // A, ok
        int && rri = 3;       // B, warning
        S s;
        S & rs = s;
        S & rs2 = (S){};
        S && rrs = rs;
        S && rrs2 = (S){};    // C, error
        foo( s );             // D, error
}
}}}

Actual:
{{{
CFA Version 1.0.0 (debug)
[Line B] warning: rvalue to reference conversion of rvalue: Constant Expression (3: signed int)
... with resolved type:
  signed int.
[Line C] error: Attempt to take address of non-lvalue expression pointer to instance of struct S with body
[Line D] error: Attempt to take address of non-lvalue expression pointer to instance of struct S with body
}}}

Expected outcome is undecided; we are reconsidering how much implicit rvalue-to-reference promotion is appropriate.  Assuming a maximum amount of promotion being appropriate, the example should compile, perhaps with warnings.

Note Line A [https://en.cppreference.com/w/cpp/language/implicit_conversion#Temporary_materialization is valid] in modern C++.  Issue #305 is about a variation of Line A, where the type has RAII.

On the call, we speculated that Line B, if allowed, translates correctly like
{{{
int rri_obj = 3;
int * rri_temp = & rri_obj;
int ** rri = & rri_temp;
}}}
while Line C is actually translating like
{{{
struct S ** rrs2 = &&(S){};
}}}
though Line C, if allowed, should translate like:
{{{
struct S * rrs2_temp = &(S){};
struct S ** rrs2 = &rrs2_temp;
}}}
"	mlbrooks
Active Tickets	309	Resolver selects arbitrary candidate from ambiguity when return types are reused	cfa-cc	1.0		defect		new	2025-04-11T00:26:33-04:00	2025-04-11T00:26:33-04:00	"{{{
struct Cat{};
struct Dog{};

struct X{};
struct Y{};
struct Z{};

Cat foo(X) { printf(""a\n""); }
Cat foo(Y) { printf(""b\n""); }
Dog foo(Z) { printf(""c\n""); }

X bar(double) { printf(""1-""); }
Y bar(double) { printf(""2-""); }
Z bar(int)    { printf(""3-""); }

void fred() {
    foo(bar(3.14)); // HERE
}

int main() {
    fred();
    return 0;
}
}}}

Actual: Compiles, runs, prints, ""3-c.""

Expected: Ambiguous at HERE: Cannot decide between 1-a and 2-b.

Comment out options 1 or a to see 2b get picked.  Shows 2b < 3c.
Comment out options 2 or b to see 1a get picked.  Shows 1a < 3c.
Comment out options 3 or c to see ambiguity.  Shows 1a = 2b.

Therefore, expected handling for the all-in scenario is ambiguity among 1a and 2b.  But it actually picks 3c.

So, here, the all-in answer is picking a non-cheapest alternative (3c) when 1a and 2a are available and cheaper.

Change return type of foo-a from Cat to int.  Now, actual = expected = ambiguity among 1a and 2b.

A larger, more realistic, scenario, with a different dubious selection:

{{{
struct string{};
typedef signed long long int strmul_factor_t;

string times(string, strmul_factor_t) { printf(""times1( s=s*n )\n""); }
string times(char, strmul_factor_t)   { printf(""times2( s=c*n )\n""); }
string times(strmul_factor_t, string) { printf(""times3( s=n*s )\n""); }
string times(strmul_factor_t, char)   { printf(""times4( s=n*c )\n""); }
int    times(int, int)                { printf(""times5( i=i*i )\n""); }

string plus(string, string) { printf(""plusA( s=s+s ), ""); }
string plus(string, char)   { printf(""plusB( s=s+c ), ""); }
string plus(char, string)   { printf(""plusC( s=c+s ), ""); }
string plus(char, char)     { printf(""plusD( s=c+c ), ""); }
char plus(char, char)       { printf(""plusE( c=c+c ), ""); }

void fred() {
    times(plus('a', 'b'), 3); // HERE
}

int main() {
    fred();
    return 0;
}
}}}

Actual: Compiles, runs, prints, ""plusE( c=c+c ), times5( i=i*i ).""

Expected: Ambiguity at HERE: cannot choose between D1, E2, and E5.

Comment out times5: actual = expected = ambiguity among
-  plusE( c=c+c ), times2( s=c*n )
-  plusD( s=c+c ), times1( s=s*n )
Error message showing both with cost ( 0, 0, 0, 4, 0, 0, 0, 0 )

Comment out times2: actual = expected = get ambiguity among
-  plusD( s=c+c ), times1( s=s*n )
-  plusE( c=c+c ), times5( i=i*i )
Error message showing both with cost ( 0, 0, 0, 4, 0, 0, 0, 0 )

So, alternatives E2, D1 and E5 all have the same cost.
And this time, the all-in answer is picking one among equals.
"	mlbrooks
Active Tickets	313	Test ticket for email behavior	cfa-cc	1.0		defect		new	2025-10-09T11:40:38-04:00	2025-10-09T11:40:38-04:00	Description content here	j42taylo
Active Tickets	28	Look at automatically generating ?==?, ?!=? for all structs	cfa-cc	1.0		enhancement		new	2017-07-26T14:37:49-04:00	2020-06-24T09:28:15-04:00	"  - Meaning would be recursive member-wise comparison.
  - Would it require adding these two operators to `otype` constraints, or
    otherwise they could not be automatically generated for generic types.
  - Need to consider if there are any complete types for which equality is 
    not a meaningful concept, and if it would be harmful to generate these 
    for those types.
"	pabuhr
Active Tickets	30	Generating builtin C routines is awkward	prelude	1.0		enhancement		new	2017-07-26T15:04:27-04:00	2017-07-26T15:04:46-04:00	Mechanism to generate builtin C routines is awkward and incomplete, also concern about GPL licence	pabuhr
Active Tickets	101	Multi operators on tuples	cfa-cc	1.0		enhancement		new	2018-07-11T16:44:43-04:00	2018-07-11T16:44:43-04:00	"It would be nice to have the these operations:
{{{
t.[x,y] = 11;
t.[x,y] += 11;
t.[x,y] + 11;
t.[x,y]++;
}}}"	Thierry Delisle
Active Tickets	133	Better signature for copy constructors.	cfa-cc	1.0		enhancement		new	2019-06-18T16:49:04-04:00	2025-05-01T14:26:46-04:00	"Copy constructors now have a weird C copy as a parameter. This is due to historical reasons and should use const reference instead.

I.e. this:
{{{
void ?{}(char &, char);
char ?=?(char &, char);
}}}
Instead of
{{{
void ?{}(char &, char const &);
char const & ?=?(char &, char const &); 
}}}"	Thierry Delisle
Active Tickets	134	Unification is not the right logic for assertion resolution	cfa-cc	1.0		enhancement		new	2019-06-18T16:57:07-04:00	2019-06-18T16:57:07-04:00	"Currently assertion resolution uses unification. This means that to check if a function can satisfy an assertion we use the same rules as to obtain the type of ternary if expression.

The leads to problems where an expression that would resolve normally doesn't necessarily resolve assertions.
For example:
{{{
forall( dtype T | sized(T) | { void ^?{}( T & ); } ) void delete( T * ptr );

monitor a {};
void ^?{}( T mutex & this );

int main() {
    a * p = ...;
    delete(a); // destructor does not unify because of mutex
}
}}}

To fix this we need to use check assertion satisfaction using resolution rather than unification."	Thierry Delisle
Active Tickets	136	PtrsAssignable Rework	cfa-cc	1.0		enhancement		new	2019-06-24T15:16:23-04:00	2019-06-26T06:49:23-04:00	"The visitor used to implement part of the helper function is almost entirely boiler plate. It never recurses (not even manually) and most of the functions don't do anything.

The entire thing should be easy to replace with a pair of if statements which check for a successful dynamic_cast. The bodies of the if statements being the bodies of the few postvisit functions that actually do anything."	ajbeach
Active Tickets	143	Extended Designator Syntax for Arrays	cfa-cc	1.0		enhancement		new	2019-08-08T10:09:07-04:00	2019-08-08T10:09:07-04:00	"I propose 3 new forms of designator in array initializer lists. The syntax reflects the syntax used in a case statement.

{{{
[0~9] = <expr>
}}}
Range initialization, initializes every index in the range.

{{{
['a', 'c'] = <expr>
}}}
List initialization, initializes every index in the list. Also every element of the list can be a single index or a range as above.

{{{
default = <expr>
}}}
Default initialization, initializes every index not initialized already. Maybe required to come last. This is the biggest break from the existing designator syntax but it is perhaps more well known from switch statements already.

Each form initializes several values, the expression should be evaluated only once and the result copied into place. That works for primitive types and types with trivial constructors. Interactions with constructors are left undefined for now.

One thing this does not cover is any new designator syntax for structures. There are three reasons for this. The first is constructors work better on structures so there is less need for it. Also this helps less since structures have mixed types. That means the list form is the most useful and the best syntax I have for fields is {{{.x, y = <expr>}}} which could use some work."	ajbeach
Active Tickets	206	Replace Lvalues With References	cfa-cc	1.0		enhancement		new	2020-07-29T12:09:31-04:00	2020-07-29T12:09:31-04:00	"We have worked through the theory of replacing all lvalues with references. References can already be used in every place an lvalue can be so to remove lvalues from the language would be to replace the existing uses of lvalue.

For instance consider a variable of type `int`. In C (and current CFA) a use of this variable would be of type `int` and an lvalue. With this change a use would be of `int &` and not an lvalue because that question is irrelevant.

This does not add any functionality but it would simplify the language description.

For implementation the compiler will still have to know about lvalues; at least as long as we use C as output. The current system (once corrected, see #205) could be used, lvalue could be treated as a flag on reference type or the passes that work with C code could just handle it themselves but that seems dangerous."	ajbeach
Active Tickets	248	Lazily generate prelude functions on demande.	prelude	1.0		enhancement		new	2021-05-12T14:47:31-04:00	2023-07-21T16:08:19-04:00	"Prelude functions are currenctly generated as regular C, except when used through assertions. In that case, the assertions require a function pointer so we have a function definition for each prelude function in libcfa.

This is a performance problem since it stops inlining of polymorphic function using basic types.

A solution to this would be to generate the functions for each source files as {{{static inline}}}.

However, doing this naively doesn't work because the definition of prelude functions is self-referential. The solution is to only insert the bodies of the needed prelude function on demande before code generation."	Thierry Delisle
Active Tickets	249	"extern ""Builtins"""	prelude	1.0		enhancement		new	2021-05-12T15:05:14-04:00	2021-05-12T15:05:14-04:00	"gcc and clang support thousands of builtin functions which cforall must know about. Both gcc and clang lists these builtins using a custom internal format which is difficult to copy.

Some of these builtins, especially the platform specific ones, are mostly wrapped in {{{extern inline}}} functions and listed in headers.

A ""simple"" approach to supporting these would be to add a special tag to these functions so that they are added to the symbol table, but the definition is not result and simply code generated as-is.

Packaging these as {{{extern ""builtins"" {/*...*/}}}} would allowing including the wrapper headers and automatically get support for a large section of the blueprint functionnality."	Thierry Delisle
Active Tickets	295	Prelude Rework (New Header for Regular Declarations)	cfa-cc	1.0		enhancement		new	2024-12-17T14:29:39-05:00	2024-12-17T15:09:27-05:00	"As we put more code into the prelude some issues have come up. That is no section of the prelude (even builtins.cfa) is not regular Cforall code. All of them have various special linkage-specifiers set, at the very least Builtin, which changes the calling conventions of a function and changes auto-generation rules.

Creating a new part of the prelude for these declarations would mean that Cforall declarations can be moved to or from this header from the library without any (or at least less) rewriting.

To do this:
-   Add a new prelude file (call it cfa-builtins, cfa-extras, autoinclude or something else) and move regular declarations - that are just Cforall definitions we think you should not have to include to use - over to it.
-   Update automake to update these files and include them in distribution (this is the current issue).
-   Split the Builtin linkage flag into two, one that marks is part of the prelude (the entire prelude, not the single file) and one that says it just use the lower level calling convention.
-   Parse the new file with the Cforall flags set, with the ""is in prelude"" flag set but not the ""use lower calling conventions"" flag."	ajbeach
Active Tickets	310	Find Cause of IO Header Compliation Slowdown	cfa-cc	1.0		enhancement		new	2025-04-28T10:47:37-04:00	2025-04-28T10:47:37-04:00	"Follow up to ae0c1c3bd041ed739c4bdaa8dd6b3cd33d35f692

This change manually creates virtual tables for some of the IO traits. This means call sites only have to resolve one assertion which gives them all the functions they could need. This should speed up the resolution of assertions by a lot, but those wins seem to be eaten up by a further increase in how long it takes to resolve the IO headers. And it already was a significant part of the cost of small files that used IO.

The thing is, we are not sure why this is the case. There was a theory it was all the with clauses, but those are mostly in the cfa files, not the headers. Most other theories hit similar issues in that most of the changes aren't in the headers. This problem has to be pinpointed and then fixed.

If the improvements to the call-site resolution are as good they seem (and they seem good, but they are hard to isolate) and we get this down, we would be solving the source of a lot of the practical compilation speed issues. That would help a lot until the root cause can be addressed."	ajbeach
Active Tickets	314	Disallow implicit conversion from arithmetic type to char	cfa-cc	1.0		enhancement		new	2026-01-12T14:48:05-05:00	2026-01-12T14:48:05-05:00	"char overloads are not intended to be used by arithmetic types and usually have distinct behavior (e.g. sout prints a character instead of numeric value) so we should disallow overloads to be resolved to the char variant.

Conversion from char to arithmetic types should still be allowed.
"	f37yu
Active Tickets	132	Remove reference cost from resolution	cfa-cc	1.0		task		new	2019-06-18T16:43:19-04:00	2019-06-18T16:47:12-04:00	"{{{
char foo(int);
long foo(int&);

int x;
foo(x);
}}}
These two functions are not ambiguous on resolution and should be.

This is because the cost function counts references.
The reference cost should be removed from the cost tuple."	Thierry Delisle
Active Tickets	254	Constructors and destructors for TLS	cfa-cc	1.0		task		new	2021-09-29T13:58:57-04:00	2021-09-29T13:58:57-04:00	"The current implementation of constructors and destructors does not work with {{{thread_local}}}.

We need to use something like {{{__cxa_thread_atexit_impl}}} (Google it) for the destructors.
I don't know how to do the constructors.

This may also fall in the category of: we just need to fork clang."	Thierry Delisle
Active Tickets	5	Undeclared variable with MRV/tuple function composition	cfa-cc	1.0		defect		assigned	2017-05-12T17:02:19-04:00	2020-02-07T17:04:27-05:00	"When calling a function taking multiple tuple arguments using a function argument returning multiple values, codegen puts a temporary in the wrong place. E.g.,
{{{
[int, int] bar() { return [3, 4]; }
[void] foo([int] x, [int] y) {}

int main() {
  foo(bar());
}
}}}
The _unq_finished variable is inserted into the statement expression generated as part of the tuple constructor for x, but needs to be accessible in y's constructor as well. "	Rob Schluntz
Active Tickets	7	CFA Assertion Error: CodeGenerator visits TypeExpr in trait/genaric type	cfa-cc	1.0		defect		new	2017-05-16T11:27:31-04:00	2018-09-27T14:46:27-04:00	"So I was trying to create a new stack with an implicate error handler and it still is not working. I am getting a failure in code generation. It appears to be trying to generate the code for the trait I created.

A trimmed down version of the code, which still generates the same error, is included:
{{{
#include <stdlib.hfa>
extern ""C"" {
#include <stdbool.h>
}

// (Bug 1 unresolved as of this test.)
forall(otype T)
struct stack_node;

forall(otype T)
struct stack_node {
    stack_node(T) * next;
    T item;
};

forall(otype T)
struct stack {
    stack_node(T) * head;
};

trait stack_errors(otype T) {
    T emptyStackHandler (stack(T) * this);
};

forall(otype T | stack_errors(T))
T pop (stack(T) * this) {
    return (T){};
}

int emptyStackHandler (stack(int) * this) {
    return 0;
}

int main (int argc, char * argv[]) {
    stack(int) stackOfInts;
    pop(&stackOfInts);
    return 0;
}
}}}
I did a trace with gdb and it seems to be happening at line 22, which is the body of the trait stack_errors. Here is also a copy of the error message (minus stack trace).
{{{
*CFA assertion error* from program ""cfa-cpp"" in ""virtual void CodeGen::CodeGenerator::visit(TypeExpr*)"" at line 733 in file ""CodeGen/CodeGenerator.cc"": TypeExpr should not reach code generation.
}}}"	ajbeach
Active Tickets	11	Compiler uses all Resources when Copying from Generic Function Result	cfa-cc	1.0		defect		new	2017-05-25T17:38:48-04:00	2017-05-26T15:14:26-04:00	"If I try to compile the following code the cfa compiler uses 100% CPU and 100% MEM and crashes the computer.
{{{
maybe(char) letter = maybe_value('a');
}}}
However the following code compiles with no noticeable slowdown:
{{{
maybe(char) letter;
letter = maybe_value('a');
maybe(char) rune = letter;
}}}

----

I also tried annotating the argument, (char)'a', and that got it to work. I'm not sure why as under the current rules that should be a no-op. But it does suggest a resolver issue.

----

I also tried annotating the call of maybe_value with maybe(char) and got the following error:
{{{
*CFA assertion error* from program ""cfa-cpp"" in ""virtual void CodeGen::CodeGenerator::visit(TypeExpr*)"" at line 733 in file ""CodeGen/CodeGenerator.cc"": TypeExpr should not reach code generation.
Stack back trace for: /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp
(0) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::CodeGenerator::visit(TypeExpr*)+0xd2 [0x4630d2]
(1) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::GenType::handleGeneric[abi:cxx11](ReferenceToType*)+0x206 [0x468d56]
(2) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::GenType::visit(StructInstType*)+0x32 [0x46b6f2]
(3) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::genType(Type*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, bool, bool)+0x1fc [0x4691ac]
(4) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::CodeGenerator::visit(ObjectDecl*)+0xa7 [0x467247]
(5) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::CodeGenerator::visit(DeclStmt*)+0x18 [0x4636d8]
(6) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::CodeGenerator::visit(CompoundStmt*)+0x16c [0x46665c]
(7) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::CodeGenerator::visit(FunctionDecl*)+0xef [0x4670af]
(8) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : CodeGen::generate(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >, std::ostream&, bool, bool, bool, bool)+0x80 [0x462be0]
(9) /home/ajbeach/cfa-cc/lib/cfa/cfa-cpp : main(/*unknown*/)+0x89f [0x45c37f]
CFA Translator error: cfa-cpp failed with signal 6
}}}

----

Some related maybe function definitions (from the new containers/maybe library header)
{{{
forall(otype T)
void ?{}(maybe(T) * this, maybe(T) other);

forall(otype T)
maybe(T) ?=?(maybe(T) * this, maybe(T) other);

forall(otype T)
maybe(T) maybe_value(T value);
}}}"	ajbeach
Active Tickets	20	zero_t doesn't work well with default arguments	cfa-cc	1.0		defect		new	2017-07-18T11:56:53-04:00	2017-08-25T11:30:45-04:00	"The following code does not compile :

{{{
struct foo {
	int i;
};

void ?{}( foo & this, zero_t zero ) {
	this.i = zero;
}

extern void bar( foo this = 0 );

int main() {
	bar();
}
}}}
"	Thierry Delisle
Active Tickets	34	Incorrect Support for Two Argument ?: Operator	cfa-cc	1.0		defect		new	2017-08-14T11:34:40-04:00	2017-08-14T11:41:20-04:00	"In parser.yy there is the following:
{{{
conditional_expression:
    logical_OR_expression
    | logical_OR_expression '?' comma_expression ':' conditional_expression
        { $$ = new ExpressionNode( build_cond( $1, $3, $5 ) ); }
        // FIX ME: this hack computes $1 twice
    | logical_OR_expression '?' /* empty */ ':' conditional_expression // GCC, $
        { $$ = new ExpressionNode( build_cond( $1, $1, $4 ) ); }
    ;
}}}
The two argument conditional operator should evaluate the first argument, the condition, once."	ajbeach
Active Tickets	57	UniqueExprs with same ID are resolved independently	cfa-cc	1.0		defect	Rob Schluntz	assigned	2017-11-16T16:39:52-05:00	2022-06-22T14:39:11-04:00	"{{{
struct inner {
    int size;
};

struct outer {
    inner;
    int func;
};

int main() {
    outer o;
    o.[size, func];
}
}}}

Here, the aggregate portion of the tuple member expression is wrapped in a `UniqueExpr` so that the expression is only evaluated once, and rewritten as [UniqueExpr(o).size, UniqueExpr(o).func].

Currently, these `UniqueExpr`s are resolved independently, so in this case the first becomes `UniqueExpr(o.anon).size` and the second becomes `UniqueExpr(o).func`. This approach needs to be modified so that all `UniqueExpr`s  with the same ID resolve to the same expression, and that resolution needs to take into account the context for all instances of the expression."	Rob Schluntz
Active Tickets	82	Wrong Overload Chosen	cfa-cc	1.0		defect	a3moss	assigned	2018-02-28T13:42:41-05:00	2022-06-22T14:51:38-04:00	"The following example currently chooses the signed ?-?, but should choose unsigned ?-?. I believe it’s getting there because it delays the conversion cost for arguments until getting to bsearch, so by this point signed ?-? and unsigned ?-? are still both candidates, and unsigned ?-? fails because E is bound to unsigned and signed */unsigned * do not unify. 
{{{
forall( E ) void bsearch( E key, const E * vals );
signed int ?-?( signed int, signed int );
unsigned int ?-?( unsigned int, unsigned int );

void f() {
  int iarr[10];
  bsearch( (unsigned int)10 - (unsigned int)0, iarr );
}
}}}

If you take the polymorphism away, the unsigned ?-? is chosen.
{{{
void bsearch( signed int key, const signed int * vals );  // chosen
void bsearch( unsigned key, const unsigned int * vals );
signed int ?-?( signed int, signed int );
unsigned int ?-?( unsigned int, unsigned int );           // chosen

void f() {
  int iarr[10];
  bsearch( (unsigned int)10 - (unsigned int)0, iarr );
} 
}}}"	Rob Schluntz
Active Tickets	85	character types	cfa-cc	1.0		defect		new	2018-04-27T14:19:46-04:00	2018-04-27T14:19:46-04:00	"Character types char_16, char32, and wchar_t are typedefs for short, int, and long int (or some such). Hence, it is impossible to over load on these essentially basic types.

Cforall should make these 3 types into basic types to allow overloading, similar to making char, signed char, and unsigned char into separate types."	pabuhr
Active Tickets	87	Inconsistent Assertion Ordering	cfa-cc	1.0		defect		new	2018-05-16T13:29:12-04:00	2018-05-16T13:29:12-04:00	"Currently we use this comparator to sort assertions in `AssertionSet`s.
{{{
struct AssertCompare {
  bool operator()( DeclarationWithType * d1, DeclarationWithType * d2 ) const {
    int cmp = d1->get_name().compare( d2->get_name() );
    return cmp < 0 ||
      ( cmp == 0 && d1->get_type() < d2->get_type() );
  }
};

typedef std::map< DeclarationWithType*, AssertionSetValue, AssertCompare > AssertionSet;
}}}

This leads to potentially large changes in compilation time from seemingly minor changes in the input file, since assertions are partially sorted by type pointer, wherein changes to the input can change the memory layout of the program. The ordering of assertions in `AssertionSet`s is important in inferRecursive, where the order determines how many candidates are checked. 

One idea is to attempt to order assertions so that the assertions with the fewest number of candidates come first. Here is an example implementation:

{{{
size_t Indexer::estimateId( const std::string &id ) const {
  size_t count = 0;
  std::unordered_set< std::string > foundMangleNames;
  Indexer::Impl *searchTables = tables;
  while ( searchTables ) {
    IdTable::const_iterator decls = searchTables->idTable.find( id );
    if ( decls != searchTables->idTable.end() ) {
      const MangleTable &mangleTable = decls->second;
      for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) {
      // mark the mangled name as found, skipping this insertion if a declaration for that name has already been found
      if ( foundMangleNames.insert( decl->first ).second == false ) continue;
      ++count;
      }
    }
    // get declarations from base indexers
    searchTables = searchTables->base.tables;
  }
  return count;
}

struct AssertCompare2 {
  AssertCompare compare;
  const SymTab::Indexer & decls;
  AssertCompare2( const SymTab::Indexer & decls ) : decls( decls ) {}
  bool operator()( DeclarationWithType * d1, DeclarationWithType * d2 ) {
    auto n1 = decls.estimateId( d1->name );
    auto n2 = decls.estimateId( d2->name );
    if ( n1 == n2 ) {
      return compare( d1, d2 );
    } else {
      return n1 < n2;
    }
  }
};
typedef std::map< DeclarationWithType*, AssertionSetValue, AssertCompare2 > AssertionSet2;
}}}

Then replace calls to `inferRecursive` with
{{{
AssertCompare2 comp( decls );
AssertionSet2 ordered( newNeed.begin(), newNeed.end(), comp );
inferRecursive( ordered.begin(), ordered.end(), newAlt, openVars, decls, newerNeed, /*needParents,*/ level+1, indexer, out );
}}}

This exact setup leads to the results below:
> Here's a few data points from full runs of the test suite:
> 
> origin/master
> real  22m23.644s
> user  179m19.648s
> sys 1m55.240s
> 
> origin/master + reordering
> real  41m27.435s
> user  374m52.536s
> sys 2m2.412s
> 
> origin/master without reordering, but with calls to calculate number of candidates per assertion (to try and measure the overhead of that operation, and the potential for caching)
> real  30m59.777s
> user  267m18.048s
> sys 2m22.136s
> 
> origin/master without reordering, without candidate calculations, but with the extra copies required to make new ordered maps where necessary (to measure the overhead of the copies)
> real  23m27.677s
> user  183m15.980s
> sys 1m56.080s
> 
> This seems to indicate to me that
> 1) this ordering is still worse than the somewhat 'random' ordering that we currently have, for at least some inputs
> 2) if we did take such an approach, there is a noticeable overhead introduced by repeatedly counting the number of candidates at each comparison, so tracking the number of overloads for a name in the Indexer might alleviate some of this
> 3) There is relatively little overhead to the extra copies needed to take this approach
> 
> So I'm not ready to give up on the idea of a consistent ordering, but this specific ordering probably isn't the right approach. I'm not sure offhand how to make it better, so it's probably best to leave it out for now but keep it in mind as a known defect. "	Rob Schluntz
Active Tickets	88	Arrays of references	cfa-cc	1.0		defect		new	2018-05-16T13:46:12-04:00	2020-07-07T15:34:30-04:00	"Currently the subscript operation does not work for arrays of references:
{{{
int main() {
  int & x[10];
  x[3];
}

$ cfa test.c
error: No reasonable alternatives for expression Applying untyped: 
  Name: ?[?]
...to: 
  Name: x
  constant expression (3 3: signed int)
}}}

This is because subscript is defined as
{{{
forall( dtype T | sized(T) ) T & ?[?]( T *, ptrdiff_t );
}}}
And type parameters do not currently bind to reference types. To make this work correctly, we want to allow bare type variables to strip references, but not otherwise. 

Changing this line in `bindVar` in Unify.cc from
{{{
other = other->stripReferences();
}}}
to
{{{
if ( widenMode.widenSecond ) other = other->stripReferences();
}}}
strips references for bare type parameters and not otherwise, as desired. For one reason or another this causes a 2x slowdown. "	Rob Schluntz
Active Tickets	91	Assignment to bool, not a boolean context	cfa-cc	1.0		defect		new	2018-05-30T14:16:57-04:00	2018-06-08T14:18:09-04:00	"Assignment to bool and condition evaluation behave differently :
{{{
#include <stdbool.h>

struct fred {};
bool ?!=?(const fred & this, zero_t) { return true; }

void foo() {
	fred f;
	if(f) {}
	bool test = f;
}
}}}"	Thierry Delisle
Active Tickets	93	New builtin types _FloatN and _FloatNx	cfa-cc	1.0		defect		new	2018-06-06T17:58:48-04:00	2018-06-06T17:58:48-04:00	Handle new builtin types _FloatN and _FloatNx.	pabuhr
Active Tickets	108	tuple overloading problem	cfa-cc	1.0		defect		new	2018-11-05T14:18:12-05:00	2018-11-05T14:18:12-05:00	"{{{
void fred( int i1, int i2 );
int fred( int i1, int i2 );
[int, int] fred( int i1, int i2 ) {}
int main() {
    int i;
    [i, i] = fred( 4, 5 );
}
}}}
{{{
cfa-cpp: Tuples/TupleAssignment.cc:300: ObjectDecl* Tuples::TupleAssignSpotter::Matcher::newObject(UniqueName&, Expression*): Assertion `expr->result && ! expr->get_result()->isVoid()' failed.
Stack back trace for: /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x2dc82 [0x7f2e2b823c82]
(1) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : Tuples::TupleAssignSpotter::Matcher::newObject(UniqueName&, Expression*)+0x261 [0xec2311]
(2) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : Tuples::TupleAssignSpotter::MassAssignMatcher::match(std::__cxx11::list<Expression*, std::allocator<Expression*> >&)+0x204 [0xec2544]
(3) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : Tuples::TupleAssignSpotter::match()+0x57 [0xec18c7]
(4) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : Tuples::TupleAssignSpotter::spot(UntypedExpr*, std::vector<ResolvExpr::AlternativeFinder, std::allocator<ResolvExpr::AlternativeFinder> >&)+0x308 [0xec2e28]
(5) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : Tuples::handleTupleAssignment(ResolvExpr::AlternativeFinder&, UntypedExpr*, std::vector<ResolvExpr::AlternativeFinder, std::allocator<ResolvExpr::AlternativeFinder> >&)+0x42 [0xec3242]
(6) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::AlternativeFinder::Finder::postvisit(UntypedExpr*)+0xfd [0xb9cb1d]
(7) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : PassVisitor<ResolvExpr::AlternativeFinder::Finder>::visit(UntypedExpr*)+0xfa [0xbbafda]
(8) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::AlternativeFinder::find(Expression*, bool, bool, bool)+0xb1 [0xb9a561]
(9) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::AlternativeFinder::Finder::postvisit(CastExpr*)+0xd3 [0xb9da03]
(10) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : PassVisitor<ResolvExpr::AlternativeFinder::Finder>::visit(CastExpr*)+0xb5 [0xbbb105]
(11) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::AlternativeFinder::find(Expression*, bool, bool, bool)+0xb1 [0xb9a561]
(12) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp() [0xc68ac5]
(13) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::resolveInVoidContext(Expression*, SymTab::Indexer const&, ResolvExpr::TypeEnvironment&)+0xe0 [0xc69040]
(14) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::findVoidExpression(Expression*&, SymTab::Indexer const&)+0x4a [0xc692da]
(15) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : PassVisitor<ResolvExpr::Resolver>::visit(ExprStmt*)+0x53 [0xca4173]
(16) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : void PassVisitor<ResolvExpr::Resolver>::handleStatementList<PassVisitor<ResolvExpr::Resolver>::visitStatementList(std::__cxx11::list<Statement*, std::allocator<Statement*> >&)::{lambda(Statement*)#1}>(std::__cxx11::list<Statement*, std::allocator<Statement*> >&, PassVisitor<ResolvExpr::Resolver>::visitStatementList(std::__cxx11::list<Statement*, std::allocator<Statement*> >&)::{lambda(Statement*)#1})+0x18b [0xca774b]
(17) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : PassVisitor<ResolvExpr::Resolver>::visit(CompoundStmt*)+0x69 [0xca7b09]
(18) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : PassVisitor<ResolvExpr::Resolver>::visit(FunctionDecl*)+0x1a1 [0xca7d61]
(19) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : ResolvExpr::resolve(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >)+0x121 [0xc8c621]
(20) /u0/pabuhr/software/cfa-cc/lib/cfa/cfa-cpp : main(/*unknown*/)+0x687 [0x71f817]
}}}"	pabuhr
Active Tickets	113	void & type	cfa-cc	1.0		defect		new	2018-12-21T13:53:13-05:00	2018-12-21T13:53:13-05:00	"{{{
void & f( void & );
}}}
This declaration compiles and generates this code:
{{{
void _X1fFv___1(void);
}}}"	pabuhr
Active Tickets	130	Flexible array member of struct crashes CFA compiler when element type is generic	cfa-cc	1.0		defect		new	2019-05-29T10:47:25-04:00	2019-05-29T10:47:25-04:00	"{{{
// this code causes the problem
forall (otype T)
struct s {
    int hdr;
    T bdy[];
};

// here is a workaround
forall (otype T)
struct s2 {
    int hdr;
    T bdy[0];
};

// FYI this works
struct s3 {
    int hdr;
    int bdy[];
};
}}}

Symptom on problem code = segmentation fault at:

Stack back trace for: /home/mlbrooks/cfa101/build/driver/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x354b0 [0x7f7dea81d4b0]
(1) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(UntypedExpr*)+0x6ea [0x138de62]
(2) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x1397ee0]
(3) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<UntypedExpr>(UntypedExpr*)+0x2c [0x13c8ea2]
(4) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(UntypedExpr*)+0x166 [0x13a251a]
(5) /home/mlbrooks/cfa101/build/driver/cfa-cpp : UntypedExpr::accept(Visitor&)+0x30 [0x2353dfe]
(6) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(UntypedExpr*)+0x707 [0x138de7f]
(7) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x1397ee0]
(8) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<UntypedExpr>(UntypedExpr*)+0x2c [0x13c8ea2]
(9) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(UntypedExpr*)+0x166 [0x13a251a]
(10) /home/mlbrooks/cfa101/build/driver/cfa-cpp : UntypedExpr::accept(Visitor&)+0x30 [0x2353dfe]
(11) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(CastExpr*)+0x167 [0x138e457]
(12) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x1397f84]
(13) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<CastExpr>(CastExpr*)+0x2c [0x13c8f62]
(14) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(CastExpr*)+0xe6 [0x13a279a]
(15) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CastExpr::accept(Visitor&)+0x30 [0x2353f72]
(16) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(ExprStmt*)+0xa2 [0x139054c]
(17) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x1397878]
(18) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<ExprStmt>(ExprStmt*)+0x2c [0x13c7c84]
(19) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(ExprStmt*)+0xc6 [0x13a05a2]
(20) /home/mlbrooks/cfa101/build/driver/cfa-cpp : ExprStmt::accept(Visitor&)+0x2e [0x235ec3e]
(21) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(CompoundStmt*)+0x130 [0x13903c6]
(22) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x1397826]
(23) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<CompoundStmt>(CompoundStmt*)+0x2c [0x13c7c24]
(24) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(CompoundStmt*)+0x1b5 [0x13a0435]
(25) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CompoundStmt::accept(Visitor&)+0x2e [0x23615e6]
(26) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::CodeGenerator::postvisit(FunctionDecl*)+0x181 [0x138b731]
(27) /home/mlbrooks/cfa101/build/driver/cfa-cpp() [0x13974c7]
(28) /home/mlbrooks/cfa101/build/driver/cfa-cpp : void PassVisitor<CodeGen::CodeGenerator>::call_postvisit<FunctionDecl>(FunctionDecl*)+0x2c [0x13c6b22]
(29) /home/mlbrooks/cfa101/build/driver/cfa-cpp : PassVisitor<CodeGen::CodeGenerator>::visit(FunctionDecl*)+0x440 [0x139f1fc]
(30) /home/mlbrooks/cfa101/build/driver/cfa-cpp : FunctionDecl::accept(Visitor&)+0x2e [0x2365f42]
(31) /home/mlbrooks/cfa101/build/driver/cfa-cpp : CodeGen::generate(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >, std::ostream&, bool, bool, bool, bool, bool)+0x1f0 [0x13e3cc4]
(32) /home/mlbrooks/cfa101/build/driver/cfa-cpp : main(/*unknown*/)+0xe90 [0x11f015e]
CFA Translator error: cfa-cpp failed with signal 6"	mlbrooks
Active Tickets	138	"Reference parameter in constructor with ""new"" initialization"	cfa-cc	1.0		defect		new	2019-06-26T08:51:09-04:00	2019-06-26T08:51:09-04:00	"This works if I change the cluster parameter from reference to pointer.
{{{
#include <stdlib.hfa>

struct cluster {
   unsigned int nthreads;
};

struct Worker {};

void ?{}( Worker & worker, cluster & wc ) {} // change to pointer

int main() {
   cluster * clust;
   Worker * fred = new( *clust ); // remove dereference
}

@plg2[17]% a.out
Cforall Runtime error (UNIX pid:11897) Addressing invalid memory at location (nil)
Possible cause is reading outside the address space or writing to a protected area within the address space with an invalid pointer or subscript.
Error occurred while executing thread Main Thread (0x7fa1f0add080).
Stack back trace for: /u0/pabuhr/software/cfa-cc/lib/cfa/x64-debug/libcfa.so.1
(0) a.out : _X4mainFi___1 + 0xa0  [0x400b5a]
Abort (core dumped)
}}}
"	pabuhr
Active Tickets	141	conditional expression warning	cfa-cc	1.0		defect		new	2019-07-19T09:44:47-04:00	2019-07-20T07:57:36-04:00	"{{{
int main() {
    char c[1] = { 3 };
    int r = 3 < 4 ? c[0] : 4;
}
cfa test1.cfa
CFA Version 1.0.0 (debug)
test1.cfa:3:1 warning: rvalue to reference conversion of rvalue: constant expression (4 4: signed int)
}}}
This warning seems wrong."	pabuhr
Active Tickets	145	Subscript in tuple fails	cfa-cc	1.0		defect		new	2019-10-14T08:04:47-04:00	2019-10-14T08:04:47-04:00	"{{{
int main() {
    int i, a[10];
    [i, a[3]] = 3;
}
warning: rvalue to reference conversion of rvalue: Address of:
  Application of
    Variable Expression: ?[?]: forall
      DT: sized object type
      function
    ... with parameters
      intrinsic pointer to instance of type DT (not function type)
      intrinsic signed long int
    ... returning
      _retval__operator_index: reference to instance of type DT (not function type)
      ... with attributes:
        Attribute with name: unused


  ... to arguments
    Variable Expression: a: array of signed int with dimension of Generated Cast of:
      constant expression (10 10: signed int)
    ... to:
      unsigned long int
    ... with environment:
      Types:
      Non-types:

    Generated Cast of:
      constant expression (3 3: signed int)
    ... to:
      signed long int

cfa-cpp: GenPoly/Lvalue.cc:391: Expression* GenPoly::{anonymous}::ReferenceConversions::postmutate(CastExpr*): Assertion `diff == 1' failed.
Stack back trace for: /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x2dc82 [0x7fbff5a6fc82]
(1) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdce154]
(2) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdce4d7]
(3) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xd55cfb]
(4) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdaaf02]
(5) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xd55f3b]
(6) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb0ea9]
(7) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb1300]
(8) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb1b3c]
(9) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xd5649b]
(10) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb0ea9]
(11) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb1300]
(12) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb0ea9]
(13) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb1300]
(14) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp() [0xdb1469]
(15) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp : GenPoly::convertLvalue(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&)+0x457 [0xda7757]
(16) /u0/pabuhr/software/mary/cfa-cc/lib/cfa/cfa-cpp : main(/*unknown*/)+0xac1 [0x932701]
CC1 Translator error: stage 2, child failed 6
}}}
"	pabuhr
Active Tickets	146	Tuple member access	cfa-cc	1.0		defect		new	2019-10-14T13:50:01-04:00	2019-10-14T13:50:01-04:00	"{{{
int main() {
    double d;  float f;
    [ int, float, double ] f() {};
    [ double, float ] x = f().[ 2, 1 ]; // works
    x = f().[ 2, 1 ]; // works
    [ d, f ] = f().[ 2, 1 ]; // fails
}
CFA Version 1.0.0 (debug)
error: No reasonable alternatives for expression Generated Cast of:
  Tuple Index Expression, with tuple:
    Unique Expression with id:2
      Application of
        Variable Expression: f: function
        ... returning
          _retval_f: tuple of types
            signed int
            float
            double
 with initializer (not constructed)
            Compound initializer: 


          ... with attributes:
            Attribute with name: unused


    with index: 1

... to:
  pointer to function
  ... returning
    _retval_f: tuple of types
      signed int
      float
      double
 with initializer (not constructed)
      Compound initializer: 


    ... with attributes:
      Attribute with name: unused
}}}"	pabuhr
Active Tickets	148	"Incorrect ""Initializer is too deep"" error"	cfa-cc	1.0		defect		new	2019-11-15T14:45:43-05:00	2019-11-15T14:45:43-05:00	"The code :
{{{
struct Global {
	int * a;
	struct {
		int a;
	} c;
	int d;
};

Global g1 = { 0p, {6}, 6 };
}}}

Should not lead to the error :
{{{
error: Managed object's initializer is too deep g1: instance of struct Global with body 1 with initializer (maybe constructed)
  Compound initializer: 
    Simple Initializer: Applying untyped:
      Name: intptr
    ...to:
      Explicit Cast of:
        constant expression (0 0: signed int)
      ... to:
        unsigned long int

    Compound initializer: 
      Simple Initializer: constant expression (6 6: signed int)

    Simple Initializer: constant expression (6 6: signed int)
}}}"	Thierry Delisle
Active Tickets	153	Compiler error when referencing a polymorphic type from within a polymorphic method which uses a trait whose methods operate on that same polymorphic type	cfa-cc	1.0		defect		new	2019-11-29T14:16:34-05:00	2019-11-29T14:16:34-05:00	"Example code:
an anonymous trait is created containing a method operating on a polymorphic type `A`, and a polymorphic method `bar` relying on this trait attempts to use `A` by instantiating it. A compiler error is generated

{{{
forall (otype T) {
    struct A {};
}

forall (otype T | {void foo(A(T));}) {
    void bar() {
        A(T) T;
    }
}

int main() {}
}}}

----

main.cfa: In function '_X3barQ1_0_0_5__X16_operator_assignFBD0_BD0BD0__X12_constructorFv_BD0__X12_constructorFv_BD0BD0__X11_destructorFv_BD0__X3fooFv_S1A_BD0___Fv___1':
main.cfa:10:46: error: '_sizeof_S1A_Y1T_' undeclared (first use in this function)
     void bar() {
                                              ^               
main.cfa:10:46: note: each undeclared identifier is reported only once for each function it appears in

----

The same compiler error is generated when the trait is not anonymous

----

Removing the requirement on `A` in the trait resolves the issue:
{{{
forall (otype T) {
    struct A {};
}

forall (otype T | {void foo(T);}) {
    void bar() {
        A(T) T;
    }
}

int main() {}
}}}


using the `-CFA` flag during compilation shows that the working version 

contains the following snippet:
{{{
unsigned long int _sizeof_S1A_Y1T_ = 1;
unsigned long int _alignof_S1A_Y1T_ = 1;
__attribute__ ((aligned(8))) char _buf22[_sizeof_S1A_Y1T_];
}}}

whereas in the broken version, the same region of code is missing the first two lines, thus resulting in an `undeclared identifier` error
{{{
__attribute__ ((aligned(8))) char _buf22[_sizeof_S1A_Y1T_];
}}}"	dkobets
Active Tickets	155	Missing error message	cfa-cc	1.0		defect		new	2020-01-10T16:29:02-05:00	2020-01-10T16:29:02-05:00	"{{{
void fred() {
    return 0;
}
}}}
does not print an error messages because a void cast is generated
{{{
return ((void)0);
}}}
This void cast should not appear in this context."	pabuhr
Active Tickets	156	"Warning for ""returning address of local variable"" does not catch polymorphic functions"	cfa-cc	1.0		defect		new	2020-01-16T14:56:38-05:00	2020-01-16T14:56:38-05:00	"Given the following code
{{{
int & bad_copy(int & a) {
	int b = a;
	return b;
}
}}}

Cforall correctly warns that this is unsafe.


However, the warning is not printed in the following equivalent case.
{{{
forall(otype T)
T & bad_copy2(T & a) {
	T b = a;
	return b;
}
}}}"	Thierry Delisle
Active Tickets	157	Initializing functions	cfa-cc	1.0		defect		new	2020-02-02T08:06:17-05:00	2020-02-02T08:06:17-05:00	"The initializer is parsed but never used and no error given.
{{{
struct S { int i; };
int f() = 3;
void ?{}( S & ) = delete;
void ^?{}( S & ) = delete + 3;
}}}
Compiles but the initializers are ignored rather than giving an error that the function cannot be initialized unless it is a function pointer."	pabuhr
Active Tickets	177	typeof not being handled correctly by resolver	cfa-cc	1.0		defect		new	2020-05-23T17:00:43-04:00	2020-05-23T17:00:43-04:00	"The following program compiles in C but fails to compile in Cforall. The resolver is not handling typeof correctly with respect to 0.
{{{
int main() {
    int i;
    int *ip = &i;
    typeof(*ip) x = 0;
}
}}}
{{{
cfa test2.cfa
CFA Version 1.0.0 (debug)
error: No reasonable alternatives for expression Untyped Init Expression
  constant expression (0 0: zero_t)  InitAlternative: reference to signed int
}}}
If the 0 is changed to 2 for the assignment, the error changes to
{{{
cfa test2.cfa
CFA Version 1.0.0 (debug)
test2.cfa:4:1 warning: rvalue to reference conversion of rvalue: constant expression (2 2: signed int)
}}}"	pabuhr
Active Tickets	178	mutex in thread main	libcfa	1.0		defect		new	2020-05-25T12:37:00-04:00	2020-05-25T12:37:00-04:00	"The main of a (mutex) thread should require the keyword mutex on its parameter.

However, no code should be genereted for this since it's handled in invoke main"	Thierry Delisle
Active Tickets	179	Bad Error Message if Missing sized in struct forall.	cfa-cc	1.0		defect		new	2020-06-01T14:56:11-04:00	2020-06-01T14:56:11-04:00	"The code does not support the following code:
{{{
forall(dtype T | sized(T))
struct wrap {
    T get;
};

forall(dtype U)
struct wrap_ptr {
    wrap(U) * get;
};
}}}
And even if we never support this the error message is terrible. The compiler spits about over a half dozen errors, most of which refer to missing members in the wrap_ptr type. The fix is to add `| sized(U)` to the second forall list, but the error message does not hint at that at all."	ajbeach
Active Tickets	180	Assertion Failure Due to Missing sized	cfa-cc	1.0		defect		new	2020-06-01T15:13:14-04:00	2020-06-01T15:13:14-04:00	"The following code has an error which is not caught and reported but instead triggers an assertion failure:
{{{
forall(dtype T)
struct wrap {
    T object;
};

forall(dtype T | sized(T) | { void ?{}(T &); }) 
void ?{}(wrap(T) & this) { 
    (this.object){};
}
}}}
The assertion error and paired stack trace is:
{{{
cfa-cpp: GenPoly/Box.cc:1193: Expression* GenPoly::{anonymous}::Pass1::postmutate(AddressExpr*): Assertion `addrExpr->arg->result && ! addrExpr->arg->result->isVoid()' failed.
Stack back trace for: /u0/ajbeach/cfa-cc/driver/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x2dc82 [0x7ffff723bc82]
(1) /u0/ajbeach/cfa-cc/driver/cfa-cpp() [0xd8d1f0]
...
(14) /u0/ajbeach/cfa-cc/driver/cfa-cpp : GenPoly::box(std::__cxx11::list<Declaration*, std::allocator<Declaration*> >&)+0xa46 [0xdcebe6]
(15) /u0/ajbeach/cfa-cc/driver/cfa-cpp : main(/*unknown*/)+0xc8c [0x94baac]
CC1 Translator error: stage 2, child failed 6
}}}
A check with a user facing error message should be added to prevent this from happening and to highlight the actual fix (adding `| sized(T)` to the struct forall)."	ajbeach
Active Tickets	183	Polymorphic Variable Name Reuse Causes Crash	cfa-cc	1.0		defect		new	2020-06-15T10:10:59-04:00	2020-06-17T15:36:52-04:00	"It appears that polymorphic variables are being confused. The following code works however if you replace occurrences of the name ""Tx"" with ""T"" the compiler segfaults.

{{{
trait satifies(dtype Tx) {
  void assertion(Tx &);
};

forall(dtype T)
struct Wrapper {};

forall(dtype T | satifies(Wrapper(T)))
T & function(T & value) {
    return value;
}
}}}

It appears somehow T=Wrapper(T) is causing a problem. Even if that actual equation is not used, it might be the mangling from one is applied to the other or something else. It does appear to lead to a segmentation fault during validation due to stack overflow."	ajbeach
Active Tickets	184	Struct in forall cannot have member named same as an assertion	cfa-cc	1.0		defect		new	2020-06-16T13:59:58-04:00	2020-06-16T15:01:57-04:00	"Code:
{{{
forall ( dtype T | { int x; } ) {
    struct S {
      #ifndef NOBUG
        int x;
      #endif
    };
    void ?{}( S(T) & ) {
        printf(""%d\n"", x);
    }
}

int main() {
    int x = 37;
    printf(""%d\n"", x);

    S(float) s;
}
}}}

Expect: Plain compile succeeds and runs with output 
{{{
37
37
}}}

Actual a: Plain compile fails with
{{{
error: duplicate object definition for x: signed int
}}}

Actual b: Compile -DNOBUG behaves as expected for plain.

Note that the type parameter on S is not used.  It's included as a workaround for #185."	mlbrooks
Active Tickets	185	Cannot declare generic struct with zero type parameters	cfa-cc	1.0		defect		new	2020-06-16T15:01:25-04:00	2025-08-07T16:00:13-04:00	"Wishing for:
{{{
forall ( | {void fun();} ) {

    struct thing {};

    void ?{}( thing & this ) {
        fun();
    }
}

void fun() {
    printf(""this is fun\n"");
}

int main() {
   thing x;
}
}}}

That is, the forall is used only to declare an assertion.

Expect: compile succeeds; run prints
{{{
this is fun
}}}

Actual: compiler error
{{{
Too few type arguments in generic type instance of struct thing with body 1
}}}

Workaround is to treat the generic type as if it had one type parameter, i.e.
{{{
forall ( dtype T | {void fun();} ) {

    struct thing ...
}}}

This workaround is
{{{
forall ( | {void fun();} ) {

    struct thing {};

    void ?{}( thing(float) & this ) {
        fun();
    }
}

void fun() {
    printf(""this is fun\n"");
}

int main() {
   thing(float) x;
}
}}}

Workaround Expected: compiler error
{{{
Too many type arguments in generic type instance of struct thing...
}}}

Workaround Actual: compile succeeds; run prints
{{{
this is fun
}}}

The cause (as originally captured in #222) of the issue is that the parser adds the extra parameter.  To see it:

{{{
forall (T & ) {
  forall ( | {void foo (T *); }) struct bar_t { T * fld; };
}
}}}

Actual prefix of compiling `-CFA -XCFA,-p,-Past`:
{{{
struct bar_t with body
... with parameters
  T: data type
  data type
  ...
}}}
Note that bar_t has two parameters (the two lines following `... with parameters`), the first named T, the second anonymous.

Expecting one parameter named T.
"	mlbrooks
Active Tickets	187	Exiting from catch Leaks Exception Memory	cfa-cc	1.0		defect		new	2020-06-18T17:47:38-04:00	2020-06-19T09:14:00-04:00	"Exception is not cleaned up for exit from handler.
{{{
#include <exception.hfa>

TRIVIAL_EXCEPTION(fred);

int main( int argc, char * argv[] ) {
	try {
		throw (fred){};
	} catch( fred * ex ) {
		exit( -1 );
	} // try
}
}}}
{{{
@plg2[101]% a.out
CFA warning (UNIX pid:17447) : program terminating with 32(0x20) bytes of storage allocated but not freed.
Possible cause is unfreed storage allocated by the program or system/library routines called from the program.
}}}"	pabuhr
Active Tickets	208	Zero-pointer literal does not work as an argument for a const parameter	cfa-cc	1.0		defect		new	2020-07-29T22:10:25-04:00	2020-07-29T22:10:25-04:00	"This issue seems to be a case of the #189 fix going too far: a typed pointer use that was working, and that should be allowed, is now failing.

{{{
void f( const float * ) {}

int main() {
    f( 0p );
}
}}}

Actual: No reasonable alternatives for expression ... Application of ... intptr

Expected: compiles successfully

An explicit cast is a workaround:
{{{
void f( const float * ) {}

int main() {
    f( (const float *) 0p );
}
}}}
Actual and Expected: compiles successfully

Indirection through a named constant is a workaround:
{{{
void f( const float * ) {}

int main() {
    const float * arg = 0p;
    f( arg );
}
}}}
Actual and Expected: compiles successfully

Further notes summarize investigation already done.

When the declaration of the intptr function is moved from the builtins to the current file, it starts working.

The 0p literal desugars as:
{{{
void f( const float * ) {}

int main() {
    f( intptr(0) );
}
}}}

Actual: No reasonable alternatives for expression ... Application of ... intptr

Expected: compiles successfully

With the equivalent declaration inline:
{{{
// up to name change, copied from builtins.c
static inline forall( dtype DT )
DT * intptr2( uintptr_t addr ) { return (DT *)addr; }

void f( const float * ) {}

int main() {
    f( intptr2(0) );
}
}}}
Actual and Expected: compiles successfully

When the error happens, the resolver pass is completing successfully, and the No Reasonable Alternatives finding is coming from the FixInit pass

`cfa-cpp -p -P astexpr ...` of any failing case still prints an AST.
"	mlbrooks
Active Tickets	209	Mutable reference to constant is not forbidden	cfa-cc	1.0		defect		new	2020-07-29T22:33:23-04:00	2020-07-29T22:33:23-04:00	"{{{
void fred( int & i) {
    printf(""fred got:  %d\n"", i);
    i = 4;
    printf(""fred left: %d\n"", i);
}

int produce() {
    return 17;
}

int main() {
    const int i = 3;
    printf(""main a:    %d\n"", i);
    fred( i );                     // unsound
    printf(""main b:    %d\n"", i);  // i is changed!

    // we soundly allow a mutable reference to a temporary
    fred( produce() );
}
}}}


Actual: Compile succeeds, run prints:
{{{
main a:    3
fred got:  3
fred left: 4
main b:    4
fred got:  17
fred left: 4
}}}

Expected (minimal fix): Compile error, cannot get mutable reference to const i at ""unsound"" line

Further candidate feature request: Compile succeeds, run prints:
{{{
main a:    3
fred got:  3
fred left: 4
main b:    3
fred got:  17
fred left: 4
}}}

This bug was discovered during discssions about #189 and the 189 fix is not addressing this case.
"	mlbrooks
Active Tickets	213	Cannot assign static-length array to VLA	cfa-cc	1.0		defect		new	2020-08-11T17:53:33-04:00	2020-08-11T17:53:33-04:00	"Intro case:
{{{
void f( int rows, int cols, float x[rows][cols] );

void g() {
    float m[4][5];
    f( 4, 5, m );
}
}}}

GCC, Actual and Authority: compiles successfully
CFA Actual: 
{{{
No reasonable alternatives for expression Applying untyped:
  Name: f
...to:
  constant expression (4 4: signed int)
  constant expression (5 5: signed int)
  Name: m
}}}
CFA Expected: as GCC

The following cases show where the problem does or does not occur.

In summary, with all cases in discussion being assignment/initialization of a pointer or reference (to an array), C allows these assignments to disregard statically known stride information, in favour of ""trust me"" runtime stride information, but CFA is finding the types incompatible.  The requested assignment is no less sound than any use of C-style ""trust me"" runtime stride information.  CFA's rejection of these cases is not differentiating safer C programs from unsafer C programs.

{{{
#ifndef VER
#define VER 1
#endif

#if   VER==1
                            #define RECEIVER_OBJ(x)    float x[rows][cols]
#elif VER==2
                            #define RECEIVER_OBJ(x)    float x[][cols]
#elif VER==3
                            #define RECEIVER_OBJ(x)    float (*x)[cols]
#elif VER==4 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[rows][cols]
#elif VER==5 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[rows][5]
#elif VER==11
                            #define RECEIVER_OBJ(x)    float x[*][*] 
#elif VER==12
                            #define RECEIVER_OBJ(x)    float x[][*]
#elif VER==13
                            #define RECEIVER_OBJ(x)    float (*x)[*]
#elif VER==14 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[*][*]
#elif VER==15 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[*][5]
#elif VER==901
                            #define RECEIVER_OBJ(x)    float x[rows][5] 
#elif VER==991
                            #define RECEIVER_OBJ(x)    float x[*][5] 
#elif VER==902
                            #define RECEIVER_OBJ(x)    float x[][5]
#elif VER==903
                            #define RECEIVER_OBJ(x)    float (*x)[5]
#elif VER==9045 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[4][5] 
#elif VER==9945 && \
      defined(__cforall)
                            #define RECEIVER_OBJ(x)    float (&x)[*][5] 
#else
#error Bad Version
#endif

void f( int rows, int cols, RECEIVER_OBJ() );

void help( int rows, int cols ) {

    float m[4][5];

    f( 4, 5, m );

  #if VER==3 || VER==903 || VER==4 || VER==5 || VER==9045
    RECEIVER_OBJ(x) = m;
    RECEIVER_OBJ(y);
  #endif

  #if VER==3 || VER==903
    y = m;
  #elif VER==4 || VER==5 || VER==9045
    &y = &m;
  #endif
}
}}}

Behaviour:

{{{
gcc -x c x.cfa -c -DVER=1
gcc -x c x.cfa -c -DVER=2
gcc -x c x.cfa -c -DVER=3
gcc -x c x.cfa -c -DVER=11
gcc -x c x.cfa -c -DVER=12
gcc -x c x.cfa -c -DVER=13
gcc -x c x.cfa -c -DVER=901
gcc -x c x.cfa -c -DVER=991
gcc -x c x.cfa -c -DVER=902
gcc -x c x.cfa -c -DVER=903
}}}
Actual and Authority: All succeed

{{{
cfa x.cfa -c -DVER=1
cfa x.cfa -c -DVER=2
cfa x.cfa -c -DVER=3
cfa x.cfa -c -DVER=4
cfa x.cfa -c -DVER=5
cfa x.cfa -c -DVER=11
cfa x.cfa -c -DVER=12
cfa x.cfa -c -DVER=13
cfa x.cfa -c -DVER=14
cfa x.cfa -c -DVER=15
}}
Actual: All fail, each with at least one no reasonable alternatives error similar to intro case CFA actual.
Expected: All succeed, as for GCC

{{{
cfa x.cfa -c -DVER=901
cfa x.cfa -c -DVER=991
cfa x.cfa -c -DVER=902
cfa x.cfa -c -DVER=903
cfa x.cfa -c -DVER=9045
cfa x.cfa -c -DVER=9945
}}}
Actual and Expected: All succeed

White-box investigation has found:

Within AlternativeFinder's makeFunctionAlternatives, the unify call is rejecting the assignment.

Unify_old::postvisit(ArrayType *) is making that judgment, requiring ""array types must both be VLA or both not VLA.""

Yet in this case, C allows a pointer to non-VLA argument to be passed for a pointer to a VLA parameter.

Note the judgment at issue is the same as is troubling ticket #175:
 - GenPoly::typesCompatible ... case for ArrayType
 - Unify_old::postvisit(ArrayType *)
"	mlbrooks
Active Tickets	216	Update Virtual Table Copy (Waiting on Copy Type Fix)	cfa-cc	1.0		defect		new	2020-09-11T17:26:38-04:00	2020-09-11T17:26:38-04:00	"Currently the signature for copying a type is `void ?{}(T &, T);` put there are plans to change it to `void ?{}(T &, T const &)`.

When that happens the copy field of the base exception virtual table should be changed from `void (*copy)(TYPE *, TYPE *);` to `void (*?{})(TYPE &, TYPE const &);` (TYPE is the exception type name.) This will remove the need for the intermediate function to change the type signature.

This is the same trick used to change `void (*free)(TYPE *);` into `void(*^?{})(TYPE &);`."	ajbeach
Active Tickets	231	Cannot print tuple literal containing 0 or 1	cfa-cc	1.0		defect		new	2020-12-22T15:55:41-05:00	2020-12-22T15:55:41-05:00	"{{{
sout | [1, 2]; // error: cannot satisfy assertion
sout | [2, 2]; // OK
}}}

Something might be wrong with implicit cast cost from zero_t and one_t to integral types. int should be preferred?"	f37yu
Active Tickets	233	Colliding Exceptions Not Detected	cfa-cc	1.0		defect		new	2021-01-18T11:12:50-05:00	2021-01-20T12:13:43-05:00	"Considering the following function:
{{{
void too_many(void) {
    try {
        throw exception_zero;
    } finally {
        throw exception_one;
    }
}
}}}
Here what exception does this function throw? Both actually, unless the entire program to aborts before we get to throwing exception_one. The problem is if exception_zero is caught the stack will be unwind and run throw exception_one, if it is caught it will be caught outside the finally clause. The handler of exception_one cannot return control to the finally block so we cannot resume handling exception_zero (even if it's handler still exists) and there is a section of stack that wants to be unwound twice.

I believe this can only happen in code that is run during an unwind operation. Which means finally clauses and destructors. I do not believe there is a general way of handling this so I recommend the C++ approach and abort the program if it happens.

In terms of style I would recommend never throwing in a finally or destructor, but function calls make that impossible to check. So this might have to be a runtime check, an extra annotation used but the personality function that says ""you shouldn't be unwinding/searching across this line"" and aborts if there is an attempt to do so."	ajbeach
Active Tickets	244	Cannot construct a struct with a volatile array member	cfa-cc	1.0		defect		new	2021-04-05T13:03:38-04:00	2021-04-05T13:03:38-04:00	"{{{
                                struct helper { float x; };
                                void ?{}( helper & ) { printf(""ctor\n""); }

                                struct thing {
#ifdef VARIATION_PRINTING_CTOR
                                    helper h;
#endif

#if defined DECL_NO_ARRAY
                                    volatile float stuff;
#elif defined DECL_NO_VOLATILE
                                    float stuff[16];
#else
                                    volatile float stuff[16]; // doesn't work
#endif
                                };

                                int main() {
                                    thing x, y;
#ifdef VARIATION_ASSIGN
                                    x = y;
#endif
                                    printf(""done\n"");
                                }

}}}

The DECL_ defines change details of the problematic declaration, thereby showing similar declarations that work as expected.

The VARIATION_ defines show different symptoms of the problematic declaration, when it's used in different contexts.

Problematic Actuals:
(no defines): GCC warning: _X12_constructorFv_S5thingS5thing_autogen___1 used but never defined; makes an executable that prints ""done""
-DVARIATION_PRINTING_CTOR: linker error: undefined reference to _X12_constructorFv_S5thing_autogen___1, and others
-DVARIATION_ASSIGN: resolver error: Unique best alternative includes deleted identifier in ?{ thing & }

Expectations for problematic cases:  same as for adding -DDECL_NO_ARRAY or -DDECL_NO_VOLATILE

Actuals as Expected:
{{{
-DVARIATION_PRINTING_CTOR, -DDECL_NO_ARRAY:    makes successfully; prints ~""ctor, ctor, done""
-DVARIATION_PRINTING_CTOR, -DDECL_NO_VOLATILE: same
-DDECL_NO_ARRAY:                               makes successfully; prints ""done""
-DDECL_NO_VOLATILE:                            same
-DDECL_NO_ARRAY -DVARIATION_ASSIGNL            same
-DDECL_NO_VOLATILE -DVARIATION_ASSIGN:         same
}}}
"	mlbrooks
Active Tickets	245	Reference-to-reference misses required implicit dereference	cfa-cc	1.0		defect		new	2021-04-05T14:15:13-04:00	2021-04-05T14:15:13-04:00	"A lot of double-reference behaviour does work.  Here is a detail that does not.

When calling a function that takes a single-reference parameter, using a double-reference variable as the argument, the resolver allows the type coercion (&& to &), which ought to imply a dereference, but no such dereference occurs.

The example also provides a workaround.

{{{
#ifdef POLY
  #define POLY_DECL(T) forall( T & )
#else
  #define POLY_DECL(T)
  #define T float
#endif

POLY_DECL(T)
void helper(T & x) {

    printf(""helper sees var at %p\n"", & x);

    // cast is working correctly, meaning consistently with the address just printed
    // cast is redundant on non-POLY version
    float val = (float &) x;
    printf(""helper sees pi  as %f\n"", val);
}

POLY_DECL(T)
void worker(T && x) {

    printf(""worker sees var at %p\n"", & x);
    printf(""worker sees ref at %p\n"", && x);

    printf(""no workaround:\n"");
    helper(x);

    printf(""with workaround:\n"");
    T & xx = x;
    helper(xx);
}

int main() {
    float var = 3.14;
    float & ref = var;

    printf(""main   sees pi  as %f\n"", var);
    printf(""main   sees pi  as %f\n"", ref);
    printf(""main   sees var at %p\n"", & var);
    printf(""main   sees var at %p\n"", & ref);
    printf(""main   sees ref at %p\n"", && ref);
    worker(ref);
}
}}}

The -DPOLY control only influences whether or not GCC issues a warning about the problem.  The problem is the same in both cases.

Actual, plain: GCC warning: passing argument 1 of _X6helperFv_f__1 from incompatible pointer type ... float **.  Result runs with this output:
{{{
main   sees pi  as 3.140000
main   sees pi  as 3.140000
main   sees var at 0x7fffffffe318
main   sees var at 0x7fffffffe318
main   sees ref at 0x7fffffffe320
worker sees var at 0x7fffffffe318
worker sees ref at 0x7fffffffe320
no workaround:
helper sees var at 0x7fffffffe320
helper sees pi  as -nan
with workaround:
helper sees var at 0x7fffffffe318
helper sees pi  as 3.140000
}}}

Actual, -DPOLY: No warning.  Same output as plain.

Expected, both:
{{{
main   sees pi  as 3.140000
main   sees pi  as 3.140000
main   sees var at 0x7fffffffe318
main   sees var at 0x7fffffffe318
main   sees ref at 0x7fffffffe320
worker sees var at 0x7fffffffe318
worker sees ref at 0x7fffffffe320
no workaround:
helper sees var at 0x7fffffffe318
helper sees pi  as 3.140000
with workaround:
helper sees var at 0x7fffffffe318
helper sees pi  as 3.140000
}}}
"	mlbrooks
Active Tickets	251	C Array within tuple crashes the compiler	cfa-cc	1.0		defect		new	2021-06-10T15:11:43-04:00	2021-06-15T13:17:35-04:00	"Basic example:

{{{
[[] int, size_t] getarray() {
        int * a;
        size_t c;
        return [a, c];
}
}}}

Actual: That code crashes the compiler.

{{{
Null pointer (nullptr) dereference.                                                                                                                                                                                  
Stack back trace for: driver/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x354c0 [0x7f1c0895b4c0]
(1) driver/cfa-cpp : ast::print(std::ostream&, ast::Node const*, Indenter)+0x31 [0xb19791]
(2) driver/cfa-cpp : ast::operator<<(std::ostream&, ast::Node const*)+0x18 [0xad73e8]
(3) driver/cfa-cpp : ast::ArrayIterator::ArrayIterator(CodeLocation const&, ast::ArrayType const*)+0x19f [0x154a2ef]
(4) driver/cfa-cpp : ast::createMemberIterator(CodeLocation const&, ast::Type const*)+0x169 [0x15428f9]
(5) driver/cfa-cpp : ast::CurrentObject::enterListInit(CodeLocation const&)+0x4a [0x1542bda]
(6) driver/cfa-cpp : ResolvExpr::Resolver_new::previsit(ast::ListInit const*)+0x2b [0x16a15db]
(7) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ListInit const*)+0x92 [0x1731652]
(8) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ObjectDecl const*)+0x1ae [0x172975e]
(9) driver/cfa-cpp : std::vector<ast::ptr_base<ast::Decl, (ast::Node::ref_type)0> > ast::Pass<ResolvExpr::Resolver_new>::call_accept<std::vector, ast::Decl>(std::vector<ast::ptr_base<ast::Decl, (ast::Node::ref_type)0> > const&)+0x119 [0x172b0e9]
(10) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::TupleType const*)+0x1b1 [0x172b4c1]
(11) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ObjectDecl const*)+0x14c [0x17296fc]
(12) driver/cfa-cpp : std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > ast::Pass<ResolvExpr::Resolver_new>::call_accept<std::vector, ast::DeclWithType>(std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > const&)+0x119 [0x1729a29]
(13) driver/cfa-cpp : void ast::Pass<ResolvExpr::Resolver_new>::maybe_accept<ast::FunctionDecl, ast::FunctionDecl, std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0>, std::allocator<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > > >(ast::FunctionDecl const*&, std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0>, std::allocator<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > > ast::FunctionDecl::*)+0x42 [0x172a1a2]
(14) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::FunctionDecl const*)+0x1d5 [0x172a885]
(15) driver/cfa-cpp : void ast::Pass<ResolvExpr::Resolver_new>::run<>(ast::TranslationUnit&)+0x1dc [0x16e906c]
(16) driver/cfa-cpp : main(/*unknown*/)+0xa82 [0x9c9e82]
Stack back trace for: driver/cfa-cpp
(0) /lib/x86_64-linux-gnu/libc.so.6 : (/*unknown*/)+0x354c0 [0x7f1c0895b4c0]
(1) driver/cfa-cpp : ast::print(std::ostream&, ast::Node const*, Indenter)+0x31 [0xb19791]
(2) driver/cfa-cpp : ast::operator<<(std::ostream&, ast::Node const*)+0x18 [0xad73e8]
(3) driver/cfa-cpp : ast::ArrayIterator::ArrayIterator(CodeLocation const&, ast::ArrayType const*)+0x19f [0x154a2ef]
(4) driver/cfa-cpp : ast::createMemberIterator(CodeLocation const&, ast::Type const*)+0x169 [0x15428f9]
(5) driver/cfa-cpp : ast::CurrentObject::enterListInit(CodeLocation const&)+0x4a [0x1542bda]
(6) driver/cfa-cpp : ResolvExpr::Resolver_new::previsit(ast::ListInit const*)+0x2b [0x16a15db]
(7) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ListInit const*)+0x92 [0x1731652]
(8) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ObjectDecl const*)+0x1ae [0x172975e]
(9) driver/cfa-cpp : std::vector<ast::ptr_base<ast::Decl, (ast::Node::ref_type)0> > ast::Pass<ResolvExpr::Resolver_new>::call_accept<std::vector, ast::Decl>(std::vector<ast::ptr_base<ast::Decl, (ast::Node::ref_type)0> > const&)+0x119 [0x172b0e9]
(10) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::TupleType const*)+0x1b1 [0x172b4c1]
(11) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::ObjectDecl const*)+0x14c [0x17296fc]
(12) driver/cfa-cpp : std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > ast::Pass<ResolvExpr::Resolver_new>::call_accept<std::vector, ast::DeclWithType>(std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > const&)+0x119 [0x1729a29]
(13) driver/cfa-cpp : void ast::Pass<ResolvExpr::Resolver_new>::maybe_accept<ast::FunctionDecl, ast::FunctionDecl, std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0>, std::allocator<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > > >(ast::FunctionDecl const*&, std::vector<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0>, std::allocator<ast::ptr_base<ast::DeclWithType, (ast::Node::ref_type)0> > > ast::FunctionDecl::*)+0x42 [0x172a1a2]
(14) driver/cfa-cpp : ast::Pass<ResolvExpr::Resolver_new>::visit(ast::FunctionDecl const*)+0x1d5 [0x172a885]
(15) driver/cfa-cpp : void ast::Pass<ResolvExpr::Resolver_new>::run<>(ast::TranslationUnit&)+0x1dc [0x16e906c]
(16) driver/cfa-cpp : main(/*unknown*/)+0xa82 [0x9c9e82]
}}}

Expected (all examples):  Same as for type [* int, size_t].

Other examples that produce similar crashes, each on its own:
{{{
 [[] int, size_t] f();
 [void] f( [[] int, size_t] arg );
 void f() {  [[] int, size_t] var;  }
}}}

This behaviour is consistent with the description: Decay from type [[] foo] to type [* foo] is not being attempted within tuples, but is required  to happen."	Thierry Delisle
Active Tickets	252	Designator problem with arrays	cfa-cc	1.0		defect		new	2021-06-17T10:52:53-04:00	2021-06-17T10:52:53-04:00	"{{{
struct A {
	int w, z; // must have 2 fields
};
void fred() {
	A a0, a2[4] = { [2] : a0, [0] : a0 }; // must have 2 variable initializers
}
}}}
This error should not occur.
{{{
cfa test.cfa
CFA Version 1.0.0 (debug)
test.cfa:5:1 error: Invalid application of existing declaration(s) in expression Untyped Init Expression
  Name: a0  InitAlternative: signed int... designated by: 
    Constant Expression (0: unsigned long int)
    ... with resolved type:
      unsigned long int
    Variable Expression: z: signed int
    ... with resolved type:
      signed int
}}}"	pabuhr
Active Tickets	257	Pointer arithmetic silently adds narrowing conversions	cfa-cc	1.0		defect		new	2022-03-14T15:33:33-04:00	2022-03-14T15:33:33-04:00	"{{{
int main() {
    int *a, *b, x;
    b = a + 3.5;
    x = a[3.5];
    x = (3.5)[a];
}
}}}

All three seem to be asking for pointer + double, which is nonsense.

CFA, actual:  Compiles successfully.  The 3.5 is rounded down to the integral 3.  The generated code includes the lines:
{{{
((void)(_X1bPi_2=(_X1aPi_2+((signed long int )3.5))));
((void)(_X1xi_2=_X1aPi_2[((signed long int )3.5)]));
((void)(_X1xi_2=((signed long int )3.5)[_X1aPi_2]));
}}}

GCC, actual:  Compilation errors on all three: ""invalid operands to binary + (have ‘int *’ and ‘double’)"" or ""array subscript is not an integer"""	mlbrooks
Active Tickets	258	Unclear intended type for char literal	cfa-cc	1.0		defect		new	2022-03-21T16:45:18-04:00	2022-03-21T16:45:18-04:00	"Bug or feature?  Here is a case of C disagreeing with CFA.  Are we okay with it?  If so, we need to fix CFA's behaviour to be self-consistent.  If not, we need to reconcile this point of C compatibility with our rules for overload selection.

C's preferred type for 'x' is int.  Today's CFA mostly prefers type char.

This disagreement is a point of backward incompatibility.  Yet, CFA's overloading provides precedent for ""CFA is right.""

The incompatibility:
{{{
#include <stdio.h>

int main( int argc, char ** argv ) {
    printf(""sizeof('x') is %ld\n"", sizeof('x'));

    typeof('x') var = 'x';
    printf(""sizeof(var) is %ld\n"", sizeof(var));

    printf(""sizeof(typeof('x')) is %ld\n"", sizeof(typeof('x')));
}
}}}
CFA, actual:
{{{
sizeof('x') is 4
sizeof(var) is 1
sizeof(typeof('x')) is 1
}}}
GCC, actual:
{{{
sizeof('x') is 4
sizeof(var) is 4
sizeof(typeof('x')) is 4
}}}
Proposed self-consistent, for CFA:
{{{
sizeof('x') is 1
sizeof(var) is 1
sizeof(typeof('x')) is 1
}}}


Precedent for, ""'x' should be a char."" (Code is CFA only)
{{{
void f( int ) {
    printf(""int overload called\n"");
}

void f( char ) {
    printf(""char overload called\n"");
}

int main( int argc, char ** argv ) {
    typeof('q') var;
    printf(""%ld\n"", sizeof(var));
    f( 'q' );
    f( var );
}
}}}
CFA, actual and expected:
{{{
1
char overload called
char overload called
}}}
"	mlbrooks
Active Tickets	262	Extra declaration of deleted autogen routines	cfa-cc	1.0		defect		new	2022-06-22T14:22:57-04:00	2022-06-22T14:22:57-04:00	"Given a struct that can't be assigned, i.e.:
{{{
struct Fi {
	int i;
};
struct Fi ?=?(struct Fi &, struct Fi) = void;

struct Fo {
	struct Fi fi;
};
}}}

In this example, the resolver will attempt to resolve the assignment operator for Fo and fail to resolved it. It will then mark that operator as deleted, which means code gen will skip it.

However, this process does not remove any forward declaration of the autogenerated routine. This results in a warning of the form : ""warning: ‘_X16_operator_assignFS2Fo_S2FoS2Fo_autogen___1’ declared ‘static’ but never defined [-Wunused-function]"""	Thierry Delisle
Active Tickets	264	Can't compile direct call of a function pointer that's a member of a struct returned by reference	cfa-cc	1.0		defect		new	2022-10-20T11:00:29-04:00	2022-10-20T11:00:29-04:00	"{{{
struct Dog {
    void (*bark)(void);
};

Dog & getFido()
                                #ifdef SUPPRESS_RUNTIME_DEMO_CRUFT
                ;
                                #else
                {
    // runtime demo cruft is: Fido is a singleton dog whose bark is to print ""hi""
    
    void sayHi(void) {
        printf(""hi\n"");
    }
    void ?{}( Dog & this ) {
        this.bark = sayHi;
    }
    static Dog fido;

    return fido;
}
                                #endif

int main() {
    // indirect call
    Dog & d = getFido();
    d.bark();
                                #ifndef SUPPRESS_AT_ISSUE_COMPILE_ERROR
    // direct call
    getFido().bark();
                                #endif
}

/*
#1
$cfa x.cfa; ./a.out
ACTUAL: CFA compiler error: Attempt to take address of non-lvalue expression: pointer to instance of struct Dog with body 1
EXPECTED: compiles, runs, prints ""hi"" twice

#2
$cfa x.cfa -DSUPPRESS_RUNTIME_DEMO_CRUFT; ./a.out
ACTUAL: same as #1 actual
EXPECTED: same as #4 expected

#3
$cfa x.cfa -DSUPPRESS_AT_ISSUE_COMPILE_ERROR; ./a.out
ACTUAL, EXPECTED: compiles, runs, prints ""hi"" once

#4
$cfa x.cfa -DSUPPRESS_AT_ISSUE_COMPILE_ERROR -DSUPPRESS_RUNTIME_DEMO_CRUFT; ./a.out
ACTUAL, EXPECTED: compiles, linker error finding getFido
*/
}}}

Originally discovered by Thierry with the following, more applied, repro.

{{{
#include <assert.h>
#include <containers/array.hfa>


struct S {
     void (*foo)(void *);
     void * bar;
};

forall([N])
void works( array( struct S, N ) & arr ) {
     void * val = arr[0].bar;
     void (*func)(void * ) = arr[0].foo;
     assert( func == arr[0].foo );
     func( val );
                                #ifndef SUPPRESS_AT_ISSUE_COMPILE_ERROR
     arr[0].foo( val );
                                #endif
}
}}}"	mlbrooks
Active Tickets	270	No NoCtor	cfa-cc	1.0		defect		new	2023-01-23T16:14:10-05:00	2023-01-23T16:14:10-05:00	"While attempting to create a CFA NoCtor, I hit a show-stopper bug. Something is generating bad C code that fails. Note, the use of sizeof(T) in a generic type, which may not have been tested before.
<<<
forall( T & | sized(T) )
	struct NoCtor {
		char storage[sizeof(T)];
	};
forall( T )
	void ctor( NoCtor(T) & t ) { (*(T *)t.storage){}; }
test1.cfa: In function '_X4ctorQ1_0_0_4__X16_operator_assignFBD0_BD0BD0__X12_constructorFv_BD0__X12_constructorFv_BD0BD0__X11_destructorFv_BD0__Fv_S6NoCtor_BD0___1':
test1.cfa:5:17: error: '_sizeof_Y12__T_generic_' undeclared (first use in this function)
    5 |   char storage[sizeof(T)];
      |                 ^~~~~~~~~~             
test1.cfa:5:17: note: each undeclared identifier is reported only once for each function it appears in
>>>"	pabuhr
Active Tickets	271	Compiler crash on valid typename--identifier pun	cfa-cc	1.0		defect		new	2023-02-03T13:42:37-05:00	2023-02-03T13:42:37-05:00	"This program tries to use `s` as both a typename and an identifier (the parameter to function `f`).

{{{
#ifndef __cforall__
extern ""C"" int printf(const char*, ...);
#endif 

struct s { float item; };

void f( s   s ) { printf( ""%.2f\n"",   s.item); }   // at issue
void g( s ess ) { printf( ""%.2f\n"", ess.item); }   // for comparison, works fine

int main( int argc, char ** argv ) {
    s thing = { 3.14 };
    f(thing);
    g(thing);
}
}}}

ACTUAL:  cfa-cpp segment faults during `Validate::fixQualifiedTypes`

EXPECTED:  compiles successfully; running prints ""3.14"" twice

Expectation is justified by ""works in C++"":

{{{
$ g++ -xc++ demo.cfa 
$ ./a.out
3.14
3.14
}}}
"	mlbrooks
Active Tickets	273	Comma expression wrecks typechecking for address of array element	cfa-cc	1.0		defect		new	2023-03-30T16:26:33-04:00	2023-03-30T16:26:33-04:00	"{{{
#ifndef __cforall
int printf(const char *, ...);
#endif

struct Animal {
    int lunch;
};

int main() {

    struct Animal theTiger;
    theTiger.lunch = 1;

    struct Animal menagerie[42];
    menagerie[0 ].lunch = 2;
    menagerie[17].lunch = 3;

    struct Animal * myPet;
    void show() { printf(""%d "", myPet->lunch); }

    myPet =      & theTiger       ; show();
    myPet = (0,  & theTiger      ); show();

    myPet = (0,    menagerie     ); show();
    myPet = (0,    menagerie+17  ); show();
    myPet =      & menagerie[17]  ; show();
    myPet = (0,  & menagerie[17] ); show(); // here

    printf(""\n"");
}
}}}

`gcc -x c demo.cfa; ./a.out`

`cfa demo.cfa; ./a.out`

GCC, Expected and Actual: Compiles and prints `1 1 2 3 3 3`

CFA, Expected: Same as GCC

CFA, Actual: ""error: incompatible types when assigning to type ‘struct Animal *’ from type ‘struct Animal’"" on the line marked ""here""
"	mlbrooks
Active Tickets	275	Array length accepts implicit conversion from non-int	cfa-cc	1.0		defect		new	2023-05-25T17:02:26-04:00	2023-05-25T17:02:26-04:00	"{{{
int a[3.14];
}}}

GCC actual, CFA expected: reject

CFA actual: accept
"	mlbrooks
Active Tickets	279	Compound Literal Construction Runs with Incorrect Timing	cfa-cc	1.0		defect		new	2023-07-21T16:26:55-04:00	2023-07-21T16:26:55-04:00	"The construction of a compound literal is happens too early.

The following code works in both C and Cforall:
{{{
#include <stdio.h>

struct iwrap {
    int value;
};

int main(int argc, char** argv) {
    int i = 3;
    printf(""before construction: %d\n"", i);
    struct iwrap obj = (++i, (struct iwrap){i});
    printf(""during construction: %d\n"", obj.value);
    printf(""after construction: %d\n"", i);
}
}}}

C outputs:
{{{
before construction: 3
during construction: 4
after construction: 4
}}}

Cforall outputs:
{{{
before construction: 3
during construction: 3
after construction: 4
}}}

I believe the constructor is being run when the storage is being allocated and not when the expression is actually evaluated."	ajbeach
Active Tickets	281	createBitwiseAssignment has an Internal Root	cfa-cc	1.0		defect		new	2023-07-24T16:16:21-04:00	2023-07-24T16:16:21-04:00	"The function InitTweak::createBitwiseAssignment creates an operation, but the operation is the application of a magic function. The magic function is created the first time the function is called and stored in a reference counted pointer. This makes it a pretty well defined root for the AST system. It does however exist outside the translation unit and breaks a bunch of assumptions we can generally count on in the AST.

The usual solution for these cases is to put it in the prelude and then save a copy in the globals of the translation unit. This case has an extra problem in that it is assignment over any data (unsized) type, which although does match the operation, is far too general because it matches everything.

So it does have to say hidden, but it should probably be moved. Or we could encode primitive C operations a different way.

I believe this is actually used as a kind of optimization or escape hatch to force the generated code to use a C assignment."	ajbeach
Active Tickets	284	Cannot have multiple forward declarations of a polymorphic type	cfa-cc	1.0		defect		new	2023-10-06T14:56:39-04:00	2023-10-06T15:04:58-04:00	"{{{
forall (T&) struct S;
forall (T&) struct S;
}}}
Expected: Allowed
Actual: Compilation error on second declaration, ""invalid type qualifier for forall""

{{{
forall (T&) struct S;
forall (U&) struct S;
}}}
Expected: Allowed
Actual: Compilation error on second declaration, ""invalid type qualifier for forall""

{{{
forall (T&) struct S;
forall (U&) struct S { U * x; };
}}}
Actual and Expected: Allowed
"	mlbrooks
Active Tickets	285	Parameter given as typeof( ({-}) ) gets unpredictable compiler error	cfa-cc	1.0		defect		new	2024-02-01T17:30:13-05:00	2024-02-01T17:30:13-05:00	"Is a possible blocker of a suggested approach to fixing #280.

The function declaration
{{{
void f( typeof( ({ foo(); }) ), int );
}}}
suffers from the f-actual error detailed below, while these ones
{{{
   void g( typeof( ({ 42   ; }) ), int );
   void h( typeof( ({ foo(); }) )      );
}}}
are fine.

Note that
- f has two parameters and the ({-}) wrapping a function call
- g has two parameters
- h the ({-}) wrapping a function call

f-actual: `cfa -c test.cfa` gives ""error: invalid type void in function type"".  FYI it occurs after -Past, before -Pvaldecl.

f-expected, g- and h- actual and expected: `cfa -c test.cfa` produces a test.o.
"	mlbrooks
Active Tickets	288	Struct managed member declared with typeof does not support multiple levels of wrapping	cfa-cc	1.0		defect		new	2024-08-14T13:22:37-04:00	2024-08-14T13:22:37-04:00	"A single level of wrapping is demonstrated successfully in the test raii/typeof-member.

Here is an analogous test that fails, where there are two levels of wrapping:

{{{
    struct A {
        int x;
    };

    void  ?{}( A & ) { printf(""custom A ctor called\n""); }
    void ^?{}( A & ) { printf(""custom A dtor called\n""); }

    A foo( void );

    struct mid {
        typeof( foo() ) a;
    };

    struct outer {
        mid x;
    };                 // here
}}}

Actual: unique best alternative includes deleted identifier in ... ?{}, at ""here""

Expect: compile successfully
"	mlbrooks
Active Tickets	293	String Initialization Cases	cfa-cc	1.0		defect		new	2024-12-03T14:07:49-05:00	2024-12-05T10:09:20-05:00	"A string literal (possibly enclosed in `{}`)[1] used to initialize an array is treated as a list initializer with the characters in the string (and sometimes the null terminator) as elements in the list. This needs some special handling to be fully handled, and currently there are no special cases to handle that.

This is a refactoring to unblock another ticket[2]. It appears to work currently works out without any special handling by decomposing both arrays to pointers. Making string literals mean it would try to initialize a pointer to mutable character from a pointer to constant characters, this is not allowed, but copying data to a mutable array from a constant arrray is.

[1] https://en.cppreference.com/w/c/language/array_initialization
[2] https://cforall.uwaterloo.ca/trac/ticket/210"	ajbeach
Active Tickets	296	Forgetting an exception vtable gives runtime crash	cfa-cc	1.0		defect		new	2025-01-02T15:19:11-05:00	2025-01-02T15:19:11-05:00	"{{{

#if defined WITH_USER_FIX
#define UFIX(...) __VA_ARGS__ // user adds this code, after understanding what the problem is
#else
#define UFIX(...)
#endif

exception E {};
UFIX( vtable(E) E_vt; )
int main() {
	try {
		throw (E){ UFIX( & E_vt ) };
	} catch ( E * ) {
	}
	printf( ""done\n"" );
	return 0;
}
}}}

Actual: Runtime error, segment fault at memory location near zero

Expected: Compile warning or error, indicating cannot throw `(E){}`

`-DWITH_USER_FIX`, actual and expected: print ""done""

Note that a user learning CFA exceptions is likely to forget either a manual vtable declaration or to use Exception.hfa.  Such a user is not well equipped to understand that the segment fault means the vtable is missing.

Mike would be satisfied by handling the problem case as:  Error, no default ctor for E.  Others may disagree.
"	mlbrooks
Active Tickets	297	Parameter name does not shadow in-scope type name	cfa-cc	1.0		defect		new	2025-01-02T15:20:59-05:00	2025-01-02T15:20:59-05:00	"{{{
    typedef int T;

    void sut( int T ) {
    	printf(""%d\n"", T);          // syntax error suggesting T is a type
    }
    void fyi() {
        int T = 1;
        printf(""%d\n"", T);          // ok
    }
    int main() {
        sut( 1 );
        fyi();
        return 0;
    }
}}}

    CFA Actual:  compile error, as commented above

    GCC Actual, All Expected:  print ""1"" twice
"	mlbrooks
Active Tickets	300	sizeof/typeof as a variable's usage may leave this variable unused	cfa-cc	1.0		defect		new	2025-01-06T12:23:43-05:00	2025-01-07T14:03:00-05:00	"{{{
void fred( const char & p ) {
    printf( ""%zu\n"", sizeof(p) );
}

forall( T * )
void mary( const T & p ) {
    printf( ""%zu\n"", sizeof(p) );
}

int main() {
    char x = 'a';
    fred(x);
    mary(x);
    return 0;
}
}}}

{{{
$cfa -Wall -Wextra -Werror test.cfa
}}}

Expected: run, print 1 twice
Actual: compile error: unused parameter (mangled name of) p, twice


Note that in -CFA output, the sizeof(p) expressions are rewritten as
- sizeof(const char), within fred
- _sizeof_Y1T, within mary

Also occurs with plain variables, as in tests/sizeof.cfa.

Also occurs with typeof, as in tests/typeof.cfa.
"	mlbrooks
Active Tickets	315	Peculiar trait syntax neither rejected nor supported	cfa-cc	1.0		defect		new	2026-02-02T19:31:43-05:00	2026-02-02T19:31:43-05:00	"Consider this basic, working, program with a trait.
{{{
forall( T & )
trait has_global {
    T * global;
};

forall( T & | has_global(T) )
void f( T & parm ) {
    printf(""f: global @ %p; parm @ %p\n"", global, & parm );
}

int main() {
    float x;
    float * global = & x;
    f(x);
}
}}}

Actual and expected: output like:
{{{
f: global @ 0x7fffffffde88; parm @ 0x7fffffffde88
}}}

Now suppose the programmer instead made this mistake, or curious choice:
{{{
forall( T & | { T * global; } )
trait has_global {};
}}}

Actual:  error: duplicate object definition for global: pointer to instance of type T (not function type)

Expected:  one of:
- error like: a trait's forall cannot introduce bare assertions, put new declarations in the trait's body, or
- same as working version

This mistake came up naturally, when trying to refactor a more complex trait, turning
{{{
forall( T & | IS_RESUMPTION_EXCEPTION(CoroutineCancelled(T)) )
trait is_coroutine {
        void main(T & this);
        coroutine$ * get_coroutine(T & this);
};
forall( T & | is_coroutine(T) | { EHM_DEFAULT_VTABLE(CoroutineCancelled(T)); } )
void prime(T & cor);
}}}
into
{{{
forall( T & | IS_RESUMPTION_EXCEPTION(CoroutineCancelled(T)) )
trait is_coroutine {
        void main(T & this);
        coroutine$ * get_coroutine(T & this);
        EHM_DEFAULT_VTABLE(CoroutineCancelled(T));
};
forall( T & | is_coroutine(T) )
void prime(T & cor);
}}}
but mistakenly giving
{{{
forall( T & | IS_RESUMPTION_EXCEPTION(CoroutineCancelled(T)) | { EHM_DEFAULT_VTABLE(CoroutineCancelled(T)); } )
trait is_coroutine {
        void main(T & this);
        coroutine$ * get_coroutine(T & this);
};
}}}

"	mlbrooks
Active Tickets	33	Updating Exception Built-ins to CFA Code (Requires Virtual)	cfa-cc	1.0		enhancement		new	2017-08-11T11:59:29-04:00	2017-08-11T11:59:29-04:00	"This only applies once explicate/strict virtual has been implemented and it is time to implement the base exception as a virtual trait.

It should then be easier to implement the parts of the exception handling system in Cforall, instead of plan C. In particular the memory management section (currently, the functions allocate exception and delete exception) uses a lot of parts of that trait.

The actual unwinding code will likely have to stay in plain C, due to name mangling and inlined assembly. The other functions can go in either language, as long as name mangling is accounted for properly. Also the C code should be able to get away with an opaque definition of the types, the stack unwinding doesn't need the data in the structure."	ajbeach
Active Tickets	125	Warning for uninitialised fields	cfa-cc	1.0		enhancement		new	2019-04-09T10:25:51-04:00	2019-04-09T10:25:51-04:00	"Given the following code, it would be nice to have a warning that field ''a'' will be uninitialised.

{{{
struct Test {
        int a;
        int b;
};

void ?{}(Test & this) {
        this.b = 0;
}
}}}
"	Thierry Delisle
Active Tickets	181	Add Support for Opaque Polymorphic Types	cfa-cc	1.0		enhancement		new	2020-06-01T16:09:24-04:00	2020-06-01T16:09:24-04:00	"I have been trying to make some smart pointers and I hit a problem with the polymorphic data structures. All are polymorphic on a type T which is the type they point at (their dereference returns a T&).

One of the pointers points directly at a T object and it works as a pointer and T can be opaque. The other one wraps the T type in a new structure. This means it must have a `sized` assertion and in turn the pointer type must also include it, which means it cannot be used with an opaque.

To explain it here are the current definitions:
{{{
forall(dtype T | sized(T))
struct counter_data {
    unsigned int counter;
    T object;
};

forall(dtype T | sized(T) /* ! */)
struct counter_ptr {
    counter_data(T) * data;
};
}}}
The ! assertion is the one that means you can't use an opaque type. This conflicts with the traditional C use of a pointer to an opaque type if you wanted to pass around a memory managed pointer.

The problem is that all uses of a polymorphic type must carry its assertions, even if they aren't used here. There should be a way to drop sized from the assertion list when you just have a pointer to the type. The system doesn't have to know the size or layout as long as you don't dereference the pointer except for the assertion list."	ajbeach
Active Tickets	188	Aliasing C names with Cforall Names	cfa-cc	1.0		enhancement		new	2020-06-19T11:46:53-04:00	2025-01-23T15:53:10-05:00	"There was discussion of adding an aliasing feature that would allow you to create a CFA name that aliases a C name, having no other definition. These would likely have to be visible but might be able to be implemented entirely as part of code generation, possibly even as a special mangled name.

This could be used on variable (two variables of the same time), but its main purpose is to alias functions (which should allow reference<->pointer conversions).

Use cases include aliasing multiple C types with the same name to take advantage of overloading (syntax is just something I came up with in the moment, it could change):
{{{
extern ""C"" {
int operation_int(int, int);
char operation_char(char, char);
}
valuedef int operation(int, int) = operation_int;
valuedef char operation(char, char) = operation_char;
}}}

Turning some operations into operators:
{{{
void append_data(struct Data *, const struct Data *);

valuedef void ?+=?(struct Data &, const struct Data &);
}}}

Turning initialization and clean-up into constructors and destructors (this one might have to be used carefully otherwise the operations could be inserted in the wrong place):
{{{
void init_Data(struct Data *, void *, size_t);
void cleanup_Data(struct Data *);

valuedef ?{}(struct Data &, void *, size_t) = init_Data;
valuedef ^?{}(struct Data &);
}}}

Internally we could also use it for mangled C names:
{{{
struct __cfadbg_state __cfadbg_current_state;

valuedef __cfadbg_state current_debug_state = __cfadbg_current_state;
}}}

It could probably alias CFA names to CFA names as well, although perhaps that use should be discouraged. At least I cannot think of as many use cases for it."	ajbeach
Active Tickets	191	Pass More Information to Resolver	cfa-cc	1.0		enhancement		new	2020-06-24T09:55:00-04:00	2025-01-06T17:00:45-05:00	"I would like to add an extra argument to the resolver's `findSingleExpression` or to add a similar function that has a similar role but makes sure the result time satisfies a given trait or matches a certain pattern.

Language constructs do not always interact with fixed types. But those are the main constraints we can pass into the resolver. So instead language constructs are converted into functions that are more expressive and then resolved. Sometimes that is enough, other times we have to then convert the resolved functions into something else.

Some examples include `throw` (there is a unbound type that must match a trait) and `intptr` (an unknown type that must be some variety of pointer). These and other examples all work, but they can include as much as a library or prelude function, a pre-resolver pass to convert the node to that and a post-resolver pass to convert or erase the function."	ajbeach
Active Tickets	207	Safe const variations are not considered for assertion satisfaction	cfa-cc	1.0		enhancement		new	2020-07-29T22:02:29-04:00	2020-07-29T22:02:29-04:00	"Assertion satisfaction is invariant in the function type, but it should be cotravariant in the function's parameters and covariant in the function's returns:

{{{
A function that consumes a const            reference does not satisfy an assertion for a function that consumes a mutation-allowed reference
A function that produces a mutation-allowed reference does not satisfy an assertion for a function that produces a const            reference
}}}

{{{
#ifndef QPROD
#define QPROD
#endif

#ifndef QCONS
#define QCONS
#endif

forall( | { void        helpConsume( QCONS int * );
            QPROD int * helpProduce(             ); } )
void f() {
    int copy = * helpProduce();
    helpConsume( & copy );
}

void helpConsume(int * x) {
    printf( ""got %d\n"", *x );
}

int *helpProduce() {
    static int theVal = 17;
    return & theVal;
}

int main() {
    f();
}
}}}

expected -DQPROD=const, expected -DCONS=const, actual plain: compile succeeds; program prints ""got 17""

actual -DQPROD=const: No alternatives with satisfiable assertions for Applying ... f ... Could not satisfy assertion: helpProduce

actual -DQCONS=const: No alternatives with satisfiable assertions for Applying ... f ... Could not satisfy assertion: helpConsume

A practical example occurs in the sout ?|? treatment of tuples.  The intuition is if a type T is has `sout | T` then by that it also has `sout | [ ... T ... ]`.

We want and have `sout | const char *`.   It is and should be usable as `sout | char *`.  It is and should be usable to satisfy `sout | [ ... const char * ...]`.  It should be, but by this bug is not, usable to satisfy `sout | [ ... char * ...]`.

{{{
int main() {
    sout | ""one"";
    sout | 2;
  #ifdef WORKAROUND
    sout | [ ( const char * ) ""three"", 4 ];
  #else
    sout | [                  ""three"", 4 ];
  #endif
}
}}}

expected plain, actual -DWORKAROUND: compiles and runs with
{{{
one
2
three, 4
}}}

actual plain: No alternatives with satisfiable assertions for Applying untyped:  Name: ?|?  to:  Name: sout,  Untyped Tuple ..."	mlbrooks
Active Tickets	210	Should string literals be const, like in C++?	cfa-cc	1.0		enhancement		new	2020-07-29T22:51:39-04:00	2024-07-05T17:28:31-04:00	"With array type:
{{{
#ifndef __cforall
#include <stdio.h>
#endif

int main(int argc, char ** argv) {
    char s[] = ""hello"";
    s[3] = 'p';
    s[4] = '!';
    printf(""%s\n"", s);
}
}}}

gcc actual, g++ actual, cfa actual: compile succeeds, run prints ""help!""


With pointer type:
{{{
#ifndef __cforall
#include <stdio.h>
#endif

int main(int argc, char ** argv) {
    char *s = ""hello"";
    s[3] = 'p';
    s[4] = '!';
    printf(""%s\n"", s);
}
}}}

gcc actual, cfa actual: compile succeeds, run gets segmentation fault

g++ actual: compiler warning, ISO C++ forbids converting a string constant to 'char*'
"	mlbrooks
Active Tickets	211	Mutable Initializers are not Detected	cfa-cc	1.0		enhancement		new	2020-07-30T10:53:18-04:00	2020-07-31T08:34:51-04:00	"{{{
int x = 0;
int y @= x; // invalid C initialization

int main(void) {
    return (x + y);
}
}}}
C only allows initialization of a global variable with a constant expression; therefore, the initialization y @= x fails with the gcc error:

`error: initializer element is not constant`

which is correct.

The enhancement is for Cforall to detect this case and print an appropriate error message instead of passing it off to the underlying C compiler."	ajbeach
Active Tickets	219	Forall Typedef	cfa-cc	1.0		enhancement		new	2020-09-18T17:38:17-04:00	2020-09-18T17:38:17-04:00	"This adds (useful) forall clauses to typedefs. An example:
{{{
forall(dtype T)
typedef T & ref;

void ?{}(ref(some_struct) this) {
    // ...
}
}}}
This is not a particularly useful example but should illustrate the feature.

Let's start at the result. `ref` is just a typedefed name like any other except that it is followed by a forall argument list. Now `ref` takes exactly one argument, because it had one forall argument in its definition, but two or more should also be supported.

We get this by putting a forall clause on the typedef. The forall clause should contain one or more polymorphic parameter declarations (likely no assertions). For consistency with other declarations the forall should be allowed before the typedef instead of just before the type (as a type qualifier).

Assertions are meaningless on a forall typedef. Because this is just an alias all assertions are drawn from the type being aliased. Similarly `otype` (and likely `ftype`) is meaningless. `ttype` is not because of its list nature. However there are a couple of choices on how these meaningless items are used.

**Alternatives**:
The other proposed syntax was `forall(dtype T) typedef T & ref(T);` which adds the parameters at the end. This isn't needed but might be clearer.

Meaningless items in the forall clause could be forbidden (so using an `otype` is an error), ignored (but allowed as an informal documentation) or checked (must match the underlying type's requirements).

**Why**:
This isn't a particularly impressive feature but it can be useful (as typedef itself is) and it seems like the most natural thing for this syntax to do."	ajbeach
Active Tickets	243	Add warning for thread casts in constructors	cfa-cc	1.0		enhancement		new	2021-03-24T14:34:48-04:00	2021-03-24T14:34:48-04:00	"When not using the thread keyword, thread casts are unsafe in constructors.

The following code for example:
{{{
struct SomeThread {
	$thread self;
	// ...
};
// ...
void ?{}( SomeThread & this, /*...*/ ) {
	((thread&)this){/*...*/};
}
}}}
leads to undefined behaviour because the constructor of 'self' will be called twice. 
We should add a warning or error for this. An easy fix would be to warn against thread casts in the constructor, but that could lead to many false positives. A better but harder approach would be to detect the case where this breaks, but that requires some amount of tracing through resolved code."	Thierry Delisle
Active Tickets	246	Increase Control of AutoGenerated Routines	cfa-cc	1.0		enhancement		new	2021-04-08T10:29:00-04:00	2021-04-08T10:29:00-04:00	"I have been caught up several times by the auto-generated routines being called when they aren't supposed to. Better matching rules might help but ultimately just being able to mark types as not needing auto-generated routines would help.

This would help with types that have a lot of invariants, user defined or part of the language. I have also some types that generating constructors and destructors for is just a waste of time as they are all C-style initialized and have global life time."	ajbeach
Active Tickets	294	Polymorphic nested aggregates may over-generalize the inner type	cfa-cc	1.0		enhancement		new	2024-12-13T16:45:42-05:00	2024-12-13T16:45:42-05:00	"Compare these two applications of nested aggregates with polymorphism, both for the union-within-struct arrangement.

{{{
// style 1
forall( T, U )
struct variant {
	int tag;
	union {
		T first;
		U second;
	};
};
}}}


{{{
// style 2
forall( T )
struct _Ostream_Manip {
	T val;
	int wd, pc;
	char base;
	union {
		unsigned char all;
		_Ostream_Flags flags;
	};
};
}}}

In style 1, the polymorphic part is within the inner aggregate, while in style 2, it is properly within the outer.  So, the style-2 inner aggregate need not be lowered as a generic: its size and layout do not depend on the size or alignment of T.  With style 1, the opposite is true.

Present state treats style 1 as the general case: sometimes the generic parameters are used by the inner aggregate, so treat an inner aggregate as being as generic as the outer.

This ticket parks the suggestion to have more cases in the above rule, and make the inner aggregate only as generic as it needs to be.  Doing so could reduce unnecessary runtime cost from handling the inner part via RTTI.

A consequence of present state is that style 2 gets an unused variable warning: _sizeof_T is not used in function _layoutof____Ostream_Manip____anonymousN. (Note the ""anonymous"" suffix means it's the layoutof function for the inner aggregate, the anonymous union).  This warning is meaningful (though could be cleaned up for non-CFA gurus) in the case of
{{{
forall( T ) // same for T * or explicit sized(T)
struct X {
   T * x;
};
}}}
where it signifies, ""You don't need to be forall-T or forall-T* here; you can be forall-T&,"" a helpful catch.  Therefore, one is reluctant to silence the warning outright, by stamping attribute-unused on all sizeof input parameters to layoutof functions.
"	mlbrooks
Active Tickets	308	Unused Polymorpic Parameters	cfa-cc	1.0		enhancement		new	2025-03-17T16:49:14-04:00	2025-03-17T16:49:14-04:00	"I think we should consider detecting and reporting unused polymorphic attributes and possibly assertions on functions and type definitions.

It might look like this:
{{{
forall([[maybe_unused]] T)
void example([[maybe_unused]] int test, int) {}

forall(T | [[maybe_unused]] arithmetic( T ) )
struct rational {
    T numerator;
    T denominator;
};
}}}
Now we already have a way to specify an unused parameter, leave it unnamed. This is used in the second parameter to example, but doesn't work for forall clauses because the names are part of the syntax for both parameters and assertions.

We would need to insert an extra pass, but if we get that far we could probably have a big pass that does all the extra warning checks. If there is any use of a polymorphic parameter, it is fine, if there is any function in an assertion that is used, the entire assertion is fine.

From an actual language design perspective though, most languages with similar features don't have similar errors. Even if a lot of those cases turn out to be unnecessary one additional wrinkle we have in this language is there are multiple ways to declare the parameters and it is pretty easy to use one that is more robust than required and that can significantly change the underlying code, so this should generate a warning:

{{{
forall(T)
struct Data {
    T * data;
};
}}}

If a use case does require assertions than the compiler expects, you could write `T & | sized(T)` (possibly with `[[maybe_unused]]` to indicate to the compiler that it is on purpose. Same idea with `is_value` and `is_object`."	ajbeach
Active Tickets	111	Option to disable threading	other	1.0		task		new	2018-12-19T15:15:38-05:00	2018-12-19T15:15:38-05:00	The feature to disable threading in the configure has be lost in the shared library/cross compilation.	Thierry Delisle
Active Tickets	139	Unification with less cloning	cfa-cc	1.0		task		new	2019-06-26T17:01:22-04:00	2019-06-26T17:01:22-04:00	"I have selectively re-introduced deeper clones in the new AST port to handle mutation of forall-parameterized types in a way that keeps the `TypeInstType` instances pointing to the correct `TypeDecl`. This was done as an attempt to get the new AST working without making deeper changes to the compiler architecture, but Thierry's profiling results suggest these deeper clones are actually a performance hot spot.

The core reason for these clones is to support distinct type variables //per function call instance//. Consider the following:
{{{
forall(otype T) T * foo( T );

foo( foo( 42 ) );
}}}
The inner `foo` has `T => int`, the outer foo has `T => int *` -- since this is legal Cforall, both `T` variables need to exist within the same type environment and be able to be disambiguated.

The current solution is to clone the type of `foo` and rename the type variables, which is effective, but has undesirable memory-management implications. An alternate approach which avoids this cloning would be to give each `ApplicationExpr` a unique identifier (the pointer may work, but would introduce memory-management complications), and store type variables in an environment or unification calculation as the pair `(<variable identifier>, <application identifier>)`. This change should be investigated if cloning types continues to be a performance bottleneck."	a3moss
Active Tickets	234	Resolver allows assignment to declared function	cfa-cc	1.0		defect		new	2021-01-18T14:40:32-05:00	2021-01-18T14:40:32-05:00	"{{{
void foo(void) {}
void bar(void) {}

int main() {
    foo = bar;
}
}}}
Actual: Error: `In function ‘_X4mainFi___1’ ... lvalue required as left operand of assignment`
Expected: Error: No reasonable alternatives for ?=?( foo, bar )

The reasonableness of the candidate that presumes to assign to the declared function has been seen causing an ambiguity between assigning to a local variable and assigning to the declared function, when ""obviously"" the local variable should be assigned to.
"	mlbrooks
Active Tickets	276	Cannot give outermost array-param's length from earlier param	cfa-cc	1.0		defect		new	2023-05-25T17:09:57-04:00	2023-05-25T17:09:57-04:00	"{{{
void f( int n, int a[n] );
}}}

GCC actual, CFA expected: accept

CFA actual: reject (bad ""-CFA"" code gen)

For context:

{{{
void f( int n, int a[][n] );
}}}

GCC actual, CFA actual and expected: accept

Which is to say, the ticket's title is technically wrong.  In the buggy case, the user-specified parameter type is a pointer (with specified length, which is a legal C no-op), rather than an array.
"	mlbrooks
Active Tickets	302	Built-in functions may have wrong types on 32-bit	cfa-cc	1.0		defect		new	2025-02-05T16:16:46-05:00	2025-02-05T16:16:46-05:00	"I stumbled across this phenomenon while doing other work.  It may represent a broader issue whose impact is not yet understood.  If further impacts are found, please increase the ticket priority accordingly.

A builtin is something that can be called like a function but that GCC may implement in a way that is not a function call.  GCC does not provide prototypes for them.  CFA provides the resolver with prototypes declared in build/libcfa/(host)/prelude/gcc-builtins.cfa.

One such prototype should be
{{{
void * __builtin_memcpy(void *, const void *, size_t);
}}}

I consider this signature correct because 
- it matches `memcpy`
- gcc is built from a table whose signature-implying symbol for it is BT_FN_PTR_PTR_CONST_PTR_SIZE; reading the name suggests my ""correct"" types (this table is in cfa-cc/libcfa/prelude/builtins.def, excerpting from the row marked BUILT_IN_MEMCPY)

However, the signature we actually send to the resolver is:
{{{
void * __builtin_memcpy(void *, const void *, unsigned long);
}}}
which is incorrect on x86, where size_t != unsigned long.

The only impact seen so far is in seeing superfluous casts when inspecting generated code.  Given a prior fix to the return type of sizeof (#269, in which its type becomes size_t), builtin calls on x86 change as in
{{{
git show d3d54b3f6 -- tests/.expect/extension.x86.txt
}}}
having differences like
{{{
- ((void)__builtin_memcpy(((void *)_X4_dstU1U_1), ((const void *)(&_X1ai_1)), sizeof(signed int )));
+ ((void)__builtin_memcpy(((void *)_X4_dstU1U_1), ((const void *)(&_X1ai_1)), ((unsigned long int )sizeof(signed int ))));
}}}
"	mlbrooks
Active Tickets	311	Meta: Test of trac on new PLG2	cfa-cc	1.0		defect		new	2025-09-15T13:56:21-04:00	2025-10-08T14:21:43-04:00	"This ticket is being used for testing Trac.

Once Trac is known to be all good, we should close this ticket."	mlbrooks
Active Tickets	312	Temp ticket to test email sending	cfa-cc	1.0		defect		assigned	2025-10-07T15:26:47-04:00	2025-10-07T15:28:37-04:00	Ignore	j42taylo
