Skip to content

Commit a321f36

Browse files
committed
Implemented main function command line arguments (finally)!
1 parent b0ee548 commit a321f36

29 files changed

+422
-178
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
## tests checking of correct main() signature
2+
3+
## wrong arguments:
4+
5+
#experr 1: main( errArgC : Int, args : &[]&[]UByte ) { }
6+
7+
#experr 1: main( args : &[]&[]UByte, errArg : &[]&[]UByte ) { }
8+
9+
#experr 1: main( args : &[]&[]Byte ) { }
10+
11+
#experr 1: main( args : &[]&[]UInt ) { }
12+
13+
#experr 1: main( args : &[]&[]Bool ) { }
14+
15+
#experr 1: main( args : &[]&UByte ) { }
16+
17+
#experr 1: main( args : &[]UByte ) { }
18+
19+
#experr 1: main( args : &&UByte ) { }
20+
21+
#experr 1: main( args : &UByte ) { }
22+
23+
#experr 1: main( args : UByte ) { }
24+
25+
26+
## wrong return type:
27+
28+
#experr 1: main( args : &[]&[]UByte ) -> Bool { return FALSE; }
29+
30+
#experr 1: main( args : &[]&[]UByte ) -> Float { return 0.0; }
31+
32+
#experr 1: main( args : &[]&[]UByte ) -> &Int { return new Int(); }
33+
34+
#experr 1: main( args : &[]&[]UByte ) -> [1]Int { return [ 0I ]; }
35+
36+
37+
## correct signature:
38+
39+
main( args : &[]&[]UByte ) -> Int {
40+
assert args.L == 0;
41+
return 0;
42+
}
43+
44+
45+
/* Note: these are also correct signatures but implicitly tested in the other tests:
46+
main( args : &[]&[]UByte )
47+
main() -> Int
48+
main()
49+
*/

proto/autotest/lang/test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def run_cmd( cmdline, expected_ret_code=0 ):
4444
"helloworld.tx",
4545

4646
"errtest.tx",
47+
"mainsignaturetest.tx",
4748

4849
"inttest.tx",
4950
"floattest.tx",

proto/src/ast/ast_entitydecls.cpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,28 +80,7 @@ void TxFieldDeclNode::declaration_pass() {
8080
// Note: If declared virtual, the virtual declaration flag is still set on this declaration
8181
}
8282

83-
std::string declName = this->fieldDef->fieldName->str();
84-
if ( declName == "self" ) {
85-
// handle constructor declaration
86-
if ( storage != TXS_INSTANCEMETHOD )
87-
CERROR( this, "Illegal declaration name for non-constructor member: " << declName );
88-
declName = CONSTR_IDENT;
89-
flags = flags | TXD_CONSTRUCTOR;
90-
}
91-
else if ( declName == CONSTR_IDENT ) { // built-in
92-
ASSERT( flags & TXD_BUILTIN, "Built-in flag not set: " << flags << " at " << this << " in " << this->context().scope() );
93-
if ( flags & TXD_INITIALIZER ) {
94-
ASSERT( storage == TXS_STATIC,
95-
"Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() );
96-
}
97-
else {
98-
ASSERT( flags & TXD_CONSTRUCTOR, "Constructor flag not set: " << flags << " at " << this << " in " << this->context().scope() );
99-
ASSERT( storage == TXS_INSTANCEMETHOD,
100-
"Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() );
101-
}
102-
}
103-
104-
this->fieldDef->declare_field( declName, lexContext.scope(), flags, storage );
83+
this->fieldDef->declare_field( lexContext.scope(), flags, storage );
10584
// Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration.
10685
}
10786

proto/src/ast/ast_entitydecls_codegen.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,16 @@ void TxTypeDeclNode::code_gen( LlvmGenerationContext& context ) const {
1010
LOG_DEBUG( this->LOGGER(), "Skipping codegen for AST of type with ExpErr context: " << this->typeCreatingNode->qtype() );
1111
return;
1212
}
13+
14+
// all "vtable types" except the ones that are type-generic-dependent are generated:
1315
auto type = this->typeCreatingNode->qtype();
14-
if ( type->is_type_generic_dependent() ) {
15-
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of generic-dependent type: "
16+
if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) {
17+
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: "
1618
<< this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() );
17-
// Note that this skips codegen for the entire AST of all generic-dependent types,
18-
// which means none of their members are generated, including any statically declared inner/local types.
19-
// FUTURE: Evaluate capability for generic types to have global static members (e.g. inner types independent of the outer type parameters).
20-
// if ( !( type->is_builtin() || !type->has_runtime_type_id()
21-
// || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) )
22-
// LOG_NOTE( this->LOGGER(), "Skipping codegen for AST of generic-dependent type that has vtable type id: " << type );
2319
return;
2420
}
25-
if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) {
26-
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: "
21+
else if ( type->suppress_code_gen() ) {
22+
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of suppressed-code-gen type: "
2723
<< this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() );
2824
return;
2925
}

proto/src/ast/ast_fielddef_node.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "ast_declpass.hpp"
55
#include "type/ast_types.hpp"
66

7+
#include "symbol/package.hpp"
8+
79

810
const TxField* TxFieldDefiningNode::resolve_field() {
911
ASSERT( this->is_context_set(), "Declaration pass has not been run (lexctx not set) before resolving " << this );
@@ -141,3 +143,88 @@ void TxFieldDefiningNode::verification_pass() const {
141143
// TODO: check that constructor function type has void return value
142144
}
143145
}
146+
147+
148+
149+
void TxLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
150+
this->declaration = scope->declare_field( this->fieldName->str(), this, declFlags, storage, TxIdentifier() );
151+
}
152+
153+
154+
void TxNonLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
155+
std::string declName = this->fieldName->str();
156+
if ( declName == "self" ) {
157+
// handle constructor declaration
158+
if ( storage != TXS_INSTANCEMETHOD )
159+
CERROR( this, "Illegal declaration name for non-constructor member: " << declName );
160+
declName = CONSTR_IDENT;
161+
declFlags = declFlags | TXD_CONSTRUCTOR;
162+
}
163+
else if ( declName == CONSTR_IDENT ) { // built-in
164+
ASSERT( declFlags & TXD_BUILTIN, "Built-in flag not set: " << declFlags << " at " << this << " in " << this->context().scope() );
165+
if ( declFlags & TXD_INITIALIZER ) {
166+
ASSERT( storage == TXS_STATIC,
167+
"Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() );
168+
}
169+
else {
170+
ASSERT( declFlags & TXD_CONSTRUCTOR, "Constructor flag not set: " << declFlags << " at " << this << " in " << this->context().scope() );
171+
ASSERT( storage == TXS_INSTANCEMETHOD,
172+
"Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() );
173+
}
174+
}
175+
176+
// Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration.
177+
this->declaration = scope->declare_field( declName, this, declFlags, storage, TxIdentifier() );
178+
}
179+
180+
bool TxNonLocalFieldDefNode::is_main_signature_valid( const TxActualType* funcType ) const {
181+
auto retType = funcType->return_type();
182+
bool retOk = bool( retType->get_type_class() == TXTC_VOID
183+
|| retType->is_a( *this->context().package()->registry().get_builtin_type( TXBT_INTEGER ) ) );
184+
if ( !retOk )
185+
CERROR( this, "main() method has non-integer return type (must be void or integer): " << retType );
186+
187+
auto & argTypes = funcType->argument_types();
188+
if ( argTypes.size() == 0 )
189+
return retOk;
190+
else if ( argTypes.size() == 1 ) {
191+
const TxActualType* argsType = argTypes.at( 0 );
192+
if ( argsType->get_type_class() == TXTC_REFERENCE ) {
193+
auto targetType = argsType->target_type();
194+
if ( targetType->get_type_class() == TXTC_ARRAY ) {
195+
auto elemType = targetType->element_type();
196+
if ( elemType->get_type_class() == TXTC_REFERENCE ) {
197+
auto elemTargetType = elemType->target_type();
198+
if ( elemTargetType->get_type_class() == TXTC_ARRAY ) {
199+
auto elemTargetElemType = elemTargetType->element_type();
200+
if ( elemTargetElemType->is_builtin( TXBT_UBYTE ) )
201+
return retOk;
202+
}
203+
}
204+
}
205+
}
206+
CERROR( this, "main() method has invalid argument [required signature is main() or main( &[]&[]UByte )] : " << argsType );
207+
}
208+
else
209+
CERROR( this, "main() method has too many arguments [required signature is main() or main( &[]&[]UByte )]" );
210+
return false;
211+
}
212+
213+
void TxNonLocalFieldDefNode::resolution_pass() {
214+
TxFieldDefiningNode::resolution_pass();
215+
216+
// handle main() function declarations:
217+
if ( this->fieldName->str() == "main" ) {
218+
auto funcField = this->field();
219+
if ( funcField->qtype()->get_type_class() == TXTC_FUNCTION ) {
220+
// verify main program function candidate
221+
if ( !( funcField->get_storage() == TXS_GLOBAL || funcField->get_storage() == TXS_STATIC ) )
222+
CERROR( this, "main() method must have global or static storage: " << funcField->get_storage() );
223+
if ( is_main_signature_valid( funcField->qtype().type() ) ) {
224+
// register main program function candidate
225+
this->context().package()->registerMainFunc( this->declaration );
226+
}
227+
}
228+
// non-function symbols declared with the name 'main' are allowed
229+
}
230+
}

proto/src/ast/ast_fielddef_node.hpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,11 @@ class TxFieldDefiningNode : public TxEntityResolvingNode {
7979

8080

8181
/** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
82-
inline void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
83-
this->declare_field( this->fieldName->str(), scope, declFlags, storage );
84-
}
85-
86-
/** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
87-
inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
88-
this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() );
89-
}
82+
virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) = 0;
83+
// /** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
84+
// inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
85+
// this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() );
86+
// }
9087

9188
virtual TxExpressionNode* get_init_expression() const {
9289
return this->initExpression;
@@ -141,6 +138,8 @@ class TxLocalFieldDefNode : public TxFieldDefiningNode {
141138
return new TxLocalFieldDefNode( this->ploc, this->fieldName->str(), typeExpr, initExpr, this->modifiable, this->_explicit );
142139
}
143140

141+
virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override;
142+
144143
virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override;
145144

146145
void code_gen_field( LlvmGenerationContext& context, GenScope* scope ) const;
@@ -149,11 +148,16 @@ class TxLocalFieldDefNode : public TxFieldDefiningNode {
149148
class TxNonLocalFieldDefNode : public TxFieldDefiningNode {
150149
void inner_code_gen_field( LlvmGenerationContext& context, bool genBody ) const;
151150

151+
bool is_main_signature_valid( const TxActualType* funcType ) const;
152+
152153
TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName,
153154
TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression, bool modifiable )
154155
: TxFieldDefiningNode( ploc, fieldName, typeExpression, initExpression, modifiable, false ) {
155156
}
156157

158+
protected:
159+
virtual void resolution_pass() override;
160+
157161
public:
158162
TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName,
159163
TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression )
@@ -171,6 +175,8 @@ class TxNonLocalFieldDefNode : public TxFieldDefiningNode {
171175
return new TxNonLocalFieldDefNode( this->ploc, this->fieldName->str(), typeExpr, initExpr, this->modifiable );
172176
}
173177

178+
virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override;
179+
174180
/** Generates this field, potentially only as a declaration without initializer. Invoked from code referencing this field. */
175181
virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override;
176182

proto/src/ast/ast_node.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ unsigned TxNode::nextNodeId = 0;
1313

1414
std::string TxNode::str() const {
1515
auto ident = this->get_descriptor();
16-
const size_t bsize = 128;
16+
const size_t bsize = 512;
1717
char buf[bsize];
18-
std::string filename = ploc.begin.filename ? get_file_name( *ploc.begin.filename ) : "";
19-
snprintf( buf, bsize, "%s %-11s %4u %-24s %s", filename.c_str(), this->parse_loc_string().c_str(),
18+
snprintf( buf, bsize, "%-13s %4u %-24s %s", this->parse_loc_string().c_str(),
2019
this->get_node_id(), typeid(*this).name(), ident.c_str() );
2120
if ( this->lexContext.reinterpretationDefiner )
2221
return std::string( buf ) + " <: " + this->lexContext.reinterpretationDefiner->str();
@@ -25,14 +24,16 @@ std::string TxNode::str() const {
2524
}
2625

2726
std::string TxNode::parse_loc_string() const {
28-
const size_t bsize = 32;
27+
const size_t bsize = 256;
2928
char buf[bsize];
29+
std::string filename = ploc.begin.filename ? get_file_name( *ploc.begin.filename ) : "";
3030
if ( ploc.begin.line == ploc.end.line ) {
3131
int lcol = ( ploc.end.column > ploc.begin.column ) ? ploc.end.column : ploc.end.column;
32-
snprintf( buf, bsize, "%3d.%2d-%d", ploc.begin.line, ploc.begin.column, lcol );
32+
snprintf( buf, bsize, "%s:%3d.%2d-%d", filename.c_str(), ploc.begin.line, ploc.begin.column, lcol );
3333
}
3434
else
35-
snprintf( buf, bsize, "%3d.%2d-%d.%d", ploc.begin.line, ploc.begin.column, ploc.end.line, ploc.end.column );
35+
snprintf( buf, bsize, "%s:%3d.%2d-%d.%d", filename.c_str(), ploc.begin.line, ploc.begin.column,
36+
ploc.end.line, ploc.end.column );
3637
return std::string( buf );
3738
}
3839

proto/src/ast/expr/ast_exprs.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ TxQualType TxFunctionCallNode::define_type( TxPassInfo passInfo ) {
132132
}
133133
}
134134

135-
// auto funcType = static_cast<const TxFunctionType*>( actualCalleeType );
136135
if ( this->calleeType->modifiable_closure() && !constructorType ) {
137136
ASSERT( this->callee->get_data_graph_origin_expr(), "Callee with modifiable closere didn't have origin expression: " << this->callee );
138137
if ( !this->callee->get_data_graph_origin_expr()->check_chain_mutable() ) {

proto/src/ast/expr/ast_field.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ const TxFieldDeclaration* resolve_field( const TxExpressionNode* origin, TxEntit
7272

7373
// first screen the fields that are of function type and take the correct number of arguments:
7474
if ( field->qtype()->get_type_class() == TXTC_FUNCTION ) {
75-
const TxFunctionType* fieldType = static_cast<const TxFunctionType*>( field->qtype().type() );
75+
auto fieldType = field->qtype().type();
7676
auto candArgTypes = fieldType->argument_types();
7777
const TxActualType* arrayArgElemType = fieldType->vararg_elem_type();
7878
const TxActualType* fixedArrayArgType = nullptr;

proto/src/ast/expr/ast_lambda_codegen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Function* TxLambdaExprNode::code_gen_function_decl( LlvmGenerationContext& conte
4848
ASSERT( funcT, "Couldn't get LLVM type for function type " << this->funcHeaderNode->qtype() );
4949

5050
Function* function = cast<Function>( context.llvmModule().getOrInsertFunction( funcName, funcT ) );
51-
function->setLinkage(GlobalValue::InternalLinkage); // (Note, could earlier cause LLVM to rename function)
51+
function->setLinkage( GlobalValue::InternalLinkage );
5252
// Note: function is of LLVM function pointer type (since it is an LLVM global value)
5353
return function;
5454
}

proto/src/ast/expr/ast_ref.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ llvm::Constant* gen_get_ref_typeid ( LlvmGenerationContext& context, llvm::Const
2020
llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, llvm::Type* refT, llvm::Value* ptrV, llvm::Value* tidV );
2121
llvm::Constant* gen_ref( LlvmGenerationContext& context, llvm::Type* refT, llvm::Constant* ptrC, llvm::Constant* tidC );
2222

23+
/** Generates a Ref value where the type id equals the specified type's *statically known* type id.
24+
* Caller must ensure this cannot be different from the proper runtime id.
25+
*/
26+
llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, const TxActualType* refType, llvm::Value* ptrV );
27+
2328
/** Converts a reference value from one type to another. If targetTypeId is specified, it will replace the original type id. */
2429
llvm::Value* gen_ref_conversion( LlvmGenerationContext& context, GenScope* scope, llvm::Value* origRefV,
2530
llvm::Type* targetRefT, uint32_t targetTypeId = UINT32_MAX );

proto/src/ast/expr/ast_ref_codegen.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, Type* refT, Val
5353
return refV;
5454
}
5555

56+
Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, const TxActualType* refType, Value* ptrV ) {
57+
ASSERT( refType->get_type_class() == TXTC_REFERENCE, "Not a reference type: " << refType );
58+
auto refT = context.get_llvm_type( refType );
59+
auto tidV = refType->target_type()->gen_typeid( context );
60+
return gen_ref( context, scope, refT, ptrV, tidV );
61+
}
62+
5663

5764
Constant* gen_get_ref_pointer( LlvmGenerationContext& context, Constant* refC ) {
5865
return refC->getAggregateElement( 0U );

proto/src/ast/type/ast_typecreating_node.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class TxAliasTypeNode : public TxTypeCreatingNode {
7979
}
8080

8181

82-
virtual void code_gen_type( LlvmGenerationContext& context ) const override;
82+
virtual void code_gen_type( LlvmGenerationContext& context ) const override { }
8383

8484
virtual void visit_descendants( const AstVisitor& visitor, const AstCursor& thisCursor, const std::string& role, void* context ) override {
8585
this->baseTypeNode->visit_ast( visitor, thisCursor, "basetype", context );

proto/src/ast/type/ast_types_codegen.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ void TxEmptyDerivedTypeNode::code_gen_type( LlvmGenerationContext& context ) con
4747
this->baseTypeNode->code_gen_type( context );
4848
}
4949

50-
void TxAliasTypeNode::code_gen_type( LlvmGenerationContext& context ) const {
51-
this->baseTypeNode->code_gen_type( context );
52-
}
53-
5450
void TxDerivedTypeNode::code_gen_builtin_type( LlvmGenerationContext& context ) const {
5551
this->inner_code_gen_type( context );
5652
}

proto/src/driver.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -482,15 +482,10 @@ int TxDriver::llvm_compile( const std::string& outputFileName ) {
482482

483483
bool mainGenerated = false;
484484
if ( auto funcDecl = this->package->getMainFunc() ) {
485-
auto funcField = funcDecl->get_definer()->resolve_field();
486-
if ( funcField->qtype()->get_type_class() == TXTC_FUNCTION ) {
487-
auto retType = static_cast<const TxFunctionType*>(funcField->qtype().type())->return_type();
488-
if ( retType->get_type_class() != TXTC_VOID
489-
&& !retType->is_a( *this->package->registry().get_builtin_type( TXBT_INTEGER ) ) )
490-
this->_LOG.error( "main() method had invalid return type: %s", retType->str().c_str() );
491-
else if ( ( mainGenerated = this->genContext->generate_main( funcDecl->get_unique_full_name(), funcField->qtype().type() ) ) )
492-
this->_LOG.debug( "Created program entry for user method %s", funcDecl->get_unique_full_name().c_str() );
493-
}
485+
auto funcField = funcDecl->get_definer()->field();
486+
this->genContext->generate_main( funcDecl->get_unique_full_name(), funcField->qtype().type() );
487+
mainGenerated = true;
488+
LOG_DEBUG( &_LOG, "Generated program entry for user main method " << funcDecl );
494489
}
495490
_LOG.info( "+ LLVM code generated (not yet written)" );
496491

0 commit comments

Comments
 (0)