Opened 42 hours ago

Last modified 40 hours ago

#291 new defect

Cannot Lift Reference Initialization

Reported by: ajbeach Owned by:
Priority: major Component: cfa-cc
Version: 1.0 Keywords:
Cc:

Description

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 };

Change History (2)

comment:1 Changed 41 hours ago by ajbeach

During clean-up of the research I did for this. This is less stable information but might help out whoever comes back to this issue.

The decision about how to handle the initializer happens in the Resolver::previsit for ObjectDecl. Or you could say that is where the result is encoded into the output. For ObjectDecl? initializers (not inside an enumeration) the initializer is either left as is or is wrapped in a ConstructorInit?. That decision comes from Resolver::shouldGenCtorInit, which calls InitTweak::tryConstruct (and some other functions) and that does some other work and then calls InitTweak::isConstructable which is effectively just some low level types that don't have constructors, and one of those is ReferenceType?, no no constructor is ever created for it. (And of these types could in theory have the same problem.)

One of those other functions called by shouldGenCtorInit is InitTweak::isConstExpr, and that is actually a function we need to take into account because if it is false, then the initializer has to be hoisted at some point. So the Resolver needs to detect that, create a resolved assignment (because it needs to be hoisted away from the declaration, so a C init will not work) and pass on the result to the hoisting pass.

The hoisting takes place in InitTweak::fixGlobalInit (a subpass of InitTweak::fix, and unlike most subpasses there is actually in a different file). This pass always short circuits and just goes along the top of the AST. At the top of the ObjectDecl? previsit is a check if the initializer is a ConstructorInit?, if it is there it is lifted.

One of my attempted solutions was check for a non-constant expression after that check and hoist it in that case as well. This does do some rechecking but also, generating the correct code didn't work out. Maybe it is possible, but getting the code in and resolved post resolver is problematic.

comment:2 Changed 40 hours ago by ajbeach

Oh yes, and the code generation problem involves resolving implicit references and cast expressions acting as annotations, so doing it post resolver does confuse the issue further.

Note: See TracTickets for help on using tickets.