chapel-lang/chapel — pull request #168 by hildeth (2014-08-19 22:54:50+00:00)
Title: Call user-defined default constructors (by name).

* Call user default constructors

**DESCRIPTION

This patch modifies the normalization and resolution so that user-defined
default constructors are called when a variable or field is declared without any
explicit initializer.

To minimize changes to the compiler, I leveraged the existing mechanism that
replaces the lack of explicit initialization with a PRIM_INIT primitive. During
resolution, this condition is recognized, and the default constructor for the
corresponding base type is called by name.

To get this working for all base types, I had to resolve typedefs during
resolution. To support this for extern types, I modified the structure of the
tree slightly, so that initialization code blocks for extern variables
(including typedefs) move with their declaration. This juxtaposition allows the
definition of an extern typedef to be located and used during that typedef
resolution.

These blocks are specially marked as BLOCK_TYPE_ONLY so they may be
removed after resolution. (Code generation does not expect them, and they are
no longer useful after resolution.) To support the tagging of blocks that
contain types but are not scopeless, I converted BlockTag into a bit-mapped enum
and used the bit-mapping to distinguish scoping from scopeless type blocks.

Complicating matters somewhat was the parallel introduction of the
user-definable _defaultOf() function. This feature also leverages the PRIM_INIT
primitive. The result of the initial merge was the user default constructor
calls worked, but calls to _defaultOf() had been bridged out.

The compromise solution is to replace PRIM_INIT with a call to the default
constructor where that call resolves to a constructor that is user-defined, and
to call _defaultOf() in all other places.

**DETAILS

*** Changes related to calling user-defined default constructors

+++ b/compiler/passes/scopeResolve.cpp
Added the new function addRecordDefaultConstruction(), which directly inserts a
constructor call (with no arguments) where a variable of record type is declared
without an initializer.

+++ b/compiler/resolution/functionResolution.cpp
Added the new function resolvePrimInit() to handle the PRIM_INIT case in
preFold(). This rejects "runtime types" externs and unhandled noinit cases.
Then, if the type has a default Value, we call _defaultOf() for the underlying
type. Otherwise, if the type has a defaultInitializer (which I think means it
is a class or record type), then we create a call to its default constructor and
resolved that call. If the resolved function has FLAG_COMPILER_GENERATED or
FLAG_WAS_COMPILER_GENERATED, we forget about that and replace the PRIM_INIT
primitive with a call to _defaultOf(). Otherwise, the successfully-resolved
user default constructor call replaces the PRIM_INIT.
In requiresImplicitDestroy(), I added a case for _defaultOf, since without that
some test cases using distributions were failing due to double-deletion errors.

+++ b/modules/standard/IO.chpl
Commented out the compilerError in deprecated file.file() because default-initialization
of a var of type "file" somewhere resolves to this constructor version (and triggers the
compilerError).

*** Changes related to extern typedef resolution

+++ b/compiler/include/stmt.h
Converted the BlockTag enum to a bit-mapped form.
Added BLOCK_TYPE_ONLY to specify a block that initializes a typedef
(independently of whether it defines a scope).

+++ b/compiler/AST/AstDumpToNode.cpp
Rewrote the blockTag switch as a series of "if"s (because the tag bits are not
necessarily mutually exclusive).

+++ b/compiler/AST/type.cpp
Adjusted flags for proper handling of some internal types:
- dtStringC is not an extern type but its implementation is (currently) supplied
by runtime code, so I removed FLAG_EXTERN and added FLAG_NO_CODEGEN.
- dtOpaque is not an extern type, so I removed FLAG_EXTERN.
- dtTaskID is like dtStringC: It's a fundamental type but not codegenned. So I
removed FLAG_EXTERN and added FLAG_NO_CODEGEN.

+++ b/compiler/passes/normalize.cpp
Modified moveGlobalDeclarationsToModuleScope() to move non-temporary variable
declarations out to module scope (as before) and also mark the body of extern
typedef blocks out to module scope, so that they immediately follow the related
declaration.

+++ b/compiler/passes/scopeResolve.cpp
Add FLAG_GENERIC to a type argument whose type is unknown.

+++ b/compiler/resolution/functionResolution.cpp
Added the new function resolveTypedefedArgTypes() to resolve argument types down
to their fundamental type equivalents. This was required to keep some c_string
related tests working after removing FLAG_EXTERN from that type. This provides
a more general solution, since internal and external typedefs now behave the
same in this respect.
Added resolveExterVarSymbols() to perform type resolution on those extern
typedef blocks (as a separate pass before resolution proper begins).
Now use the new BLOCK_TYPE_ONLY block tag to ensure that we do not remove other
type blocks occurring at the module level.
Added a few workarounds for PRIM_INITs that remain unresolved in
replaceInitPrims().

+++ b/compiler/resolution/wrappers.cpp
When a function being wrapped displays FLAG_COMPILER_GENERATED, its wrapper is
flagged with FLAG_WAS_COMPILER_GENERATED.

+++ b/modules/internal/ChapelBase.chpl
Since dtOpaque no longer has FLAG_EXTERN, I had to supply an explicit
implementation for its assignment operator.

+++ b/modules/internal/ChapelLocale.chpl
Added a use of LocaleModel to make obvious the dependency of ChapelLocale on the
locale model.
Removed the redundant chpl_nodeID declaration. The locale model supplies this
typedef.

+++ b/modules/internal/ChapelStandard.chpl
Removed the use of ChapelNumLocales -- assuming that the use in ChapelLocale covers this.
Moved the mention of LocaleModel before ChapelLocale, to resolve a type dependency loop
through chpl_here_alloc. (Can't create a RootLocale instance until we can resolve
chpl_nodeID, which depends on LocaleModel.)

*** Changes related to integrating _defaultOf().

+++ b/compiler/include/flags_list.h
Expanded the meaning of FLAG_GENERIC to include arguments and added a comment to
this effect.
Added FLAG_WAS_COMPILER_GENERATED, so we can expose whether the compiler
generated a given function in wrappers that are generated to wrap it as well.

+++ b/compiler/passes/normalize.cpp
Changed the definition of an initializer expression so it could be inserted as
an extern type block if it happens to be the definition of an extern typedef.

*** Miscellany

+++ b/compiler/passes/buildDefaultFunctions.cpp
Removed a redundant addition of FLAG_COMPILER_GENERATED to chpl_gen_main.

*** Interesting test behavior changes

+++ b/test/distributions/deitz/test_distribution_syntax2.bad
+++ b/test/distributions/deitz/test_distribution_syntax2.future
Added this future because the insertion of default constructor calls reveals that the
implementation of privatized classes fail when more than one uninitialized variable of a
privatizable (array or domain) type is declared and separately assigned within the same
scope. Since the privatized class index is initilized to zero, this should be a special
"invalid" index. Initialization of privatized types should also obtain a new class index
and update that index in the implementation.

+++ b/test/types/records/const-checking/constructors.chpl
+++ b/test/types/records/const-checking/constructors.good
The expect and actual output changed, because now we actually call user-defined default
constructors.

+++ b/test/types/records/hilde/callUserDefaultConstructor.future
Removed the future because this test now works!!!

+++ b/test/types/records/hilde/newUserDefCtorInFn.good
+++ b/test/types/records/hilde/newUserDefCtorInFn.bad
+++ b/test/types/records/hilde/newUserDefCtorInFn.future
This test now works correctly. The constructor and destructor are each called twice.

+++ b/test/types/records/hilde/newUserDefaultCtor.good
+++ b/test/types/records/hilde/newUserDefaultCtor.bad
+++ b/test/types/records/hilde/newUserDefaultCtor.future
This test now works correctly. The constructor and destructor are each called twice.

+++ b/test/types/records/sungeun/chapelTypes/record_range.future
This test now fails because default constructors are resolved by name. So an attempt to
create a range in a program that defined "record range" will fail.

+++ b/test/types/records/sungeun/classInRecord.good
The record constructor is now called uniformly, whether or not the "= new R()" syntax is
used. As a result, the contained class variable is always initialized. (This is highly
desirable for reference-counted types.)

+++ b/test/types/records/sungeun/constructor2.bad
+++ b/test/types/records/sungeun/constructor2.future
Added a future to capture the fact that
var r : myR = new myR();
is implemented as a default-initialization followed by an assignment, where it should be
implemented as a copy-initialization.

+++ b/test/users/ferguson/recordConstructor.future
This flagship test now works as expected!!!

+++ b/test/users/vass/km/array-of-records-crash-1.future
+++ b/test/users/vass/km/array-of-records-crash-1.explicit-type.future
These tests futurized as examples of the "initialization is not assignment" problem.
Resolution of these depends on implementing constructor calls as method calls, so updates
to records during initialization can be done in-place. It is excessively awkward to try
to initialize sync variables using only assignment.

*** Boring test behavior changes

+++ b/test/classes/hilde/explainInstantiation.good
Output changed because the first instantiation of range() is now implicit, due to the
insertion of default constructor calls.

+++ b/test/execflags/ferguson/help2.good
+++ b/test/execflags/shannon/configs/help/basehelp.txt
+++ b/test/execflags/shannon/help.good
The position of numLocales in the config vars list changed due to elimination of the
explicit mention of ChapelNumLocales.chpl in ChapelStandard.chpl.

+++ b/test/modules/bradc/printModStuff/foo.good
The position where DSIUtil is mentioned in the module list changed.

+++ b/test/modules/sungeun/init/printModuleInitOrder.good
The output changed due to changed use statements in ChapelStandard, ChapelLocale and
LocaleModel files.

+++ b/test/types/records/sungeun/constructor1.good
The output changed, because the number of default constructor calls increased (as
expected).