Skip to content

Commit

Permalink
Implemented main function command line arguments (finally)!
Browse files Browse the repository at this point in the history
  • Loading branch information
christerswahn committed Oct 25, 2017
1 parent b0ee548 commit a321f36
Show file tree
Hide file tree
Showing 29 changed files with 422 additions and 178 deletions.
49 changes: 49 additions & 0 deletions proto/autotest/lang/mainsignaturetest.tx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## tests checking of correct main() signature

## wrong arguments:

#experr 1: main( errArgC : Int, args : &[]&[]UByte ) { }

#experr 1: main( args : &[]&[]UByte, errArg : &[]&[]UByte ) { }

#experr 1: main( args : &[]&[]Byte ) { }

#experr 1: main( args : &[]&[]UInt ) { }

#experr 1: main( args : &[]&[]Bool ) { }

#experr 1: main( args : &[]&UByte ) { }

#experr 1: main( args : &[]UByte ) { }

#experr 1: main( args : &&UByte ) { }

#experr 1: main( args : &UByte ) { }

#experr 1: main( args : UByte ) { }


## wrong return type:

#experr 1: main( args : &[]&[]UByte ) -> Bool { return FALSE; }

#experr 1: main( args : &[]&[]UByte ) -> Float { return 0.0; }

#experr 1: main( args : &[]&[]UByte ) -> &Int { return new Int(); }

#experr 1: main( args : &[]&[]UByte ) -> [1]Int { return [ 0I ]; }


## correct signature:

main( args : &[]&[]UByte ) -> Int {
assert args.L == 0;
return 0;
}


/* Note: these are also correct signatures but implicitly tested in the other tests:
main( args : &[]&[]UByte )
main() -> Int
main()
*/
1 change: 1 addition & 0 deletions proto/autotest/lang/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def run_cmd( cmdline, expected_ret_code=0 ):
"helloworld.tx",

"errtest.tx",
"mainsignaturetest.tx",

"inttest.tx",
"floattest.tx",
Expand Down
23 changes: 1 addition & 22 deletions proto/src/ast/ast_entitydecls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,28 +80,7 @@ void TxFieldDeclNode::declaration_pass() {
// Note: If declared virtual, the virtual declaration flag is still set on this declaration
}

std::string declName = this->fieldDef->fieldName->str();
if ( declName == "self" ) {
// handle constructor declaration
if ( storage != TXS_INSTANCEMETHOD )
CERROR( this, "Illegal declaration name for non-constructor member: " << declName );
declName = CONSTR_IDENT;
flags = flags | TXD_CONSTRUCTOR;
}
else if ( declName == CONSTR_IDENT ) { // built-in
ASSERT( flags & TXD_BUILTIN, "Built-in flag not set: " << flags << " at " << this << " in " << this->context().scope() );
if ( flags & TXD_INITIALIZER ) {
ASSERT( storage == TXS_STATIC,
"Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() );
}
else {
ASSERT( flags & TXD_CONSTRUCTOR, "Constructor flag not set: " << flags << " at " << this << " in " << this->context().scope() );
ASSERT( storage == TXS_INSTANCEMETHOD,
"Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() );
}
}

this->fieldDef->declare_field( declName, lexContext.scope(), flags, storage );
this->fieldDef->declare_field( lexContext.scope(), flags, storage );
// Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration.
}

Expand Down
16 changes: 6 additions & 10 deletions proto/src/ast/ast_entitydecls_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,16 @@ void TxTypeDeclNode::code_gen( LlvmGenerationContext& context ) const {
LOG_DEBUG( this->LOGGER(), "Skipping codegen for AST of type with ExpErr context: " << this->typeCreatingNode->qtype() );
return;
}

// all "vtable types" except the ones that are type-generic-dependent are generated:
auto type = this->typeCreatingNode->qtype();
if ( type->is_type_generic_dependent() ) {
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of generic-dependent type: "
if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) {
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: "
<< this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() );
// Note that this skips codegen for the entire AST of all generic-dependent types,
// which means none of their members are generated, including any statically declared inner/local types.
// FUTURE: Evaluate capability for generic types to have global static members (e.g. inner types independent of the outer type parameters).
// if ( !( type->is_builtin() || !type->has_runtime_type_id()
// || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) )
// LOG_NOTE( this->LOGGER(), "Skipping codegen for AST of generic-dependent type that has vtable type id: " << type );
return;
}
if ( !type->has_runtime_type_id() || type->get_runtime_type_id() >= this->registry().vtable_types_count() ) {
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of non-vtable type: "
else if ( type->suppress_code_gen() ) {
LOG_DEBUG( context.LOGGER(), "Skipping codegen for AST of suppressed-code-gen type: "
<< this->typeCreatingNode << " : " << this->typeCreatingNode->qtype() );
return;
}
Expand Down
87 changes: 87 additions & 0 deletions proto/src/ast/ast_fielddef_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "ast_declpass.hpp"
#include "type/ast_types.hpp"

#include "symbol/package.hpp"


const TxField* TxFieldDefiningNode::resolve_field() {
ASSERT( this->is_context_set(), "Declaration pass has not been run (lexctx not set) before resolving " << this );
Expand Down Expand Up @@ -141,3 +143,88 @@ void TxFieldDefiningNode::verification_pass() const {
// TODO: check that constructor function type has void return value
}
}



void TxLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
this->declaration = scope->declare_field( this->fieldName->str(), this, declFlags, storage, TxIdentifier() );
}


void TxNonLocalFieldDefNode::declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
std::string declName = this->fieldName->str();
if ( declName == "self" ) {
// handle constructor declaration
if ( storage != TXS_INSTANCEMETHOD )
CERROR( this, "Illegal declaration name for non-constructor member: " << declName );
declName = CONSTR_IDENT;
declFlags = declFlags | TXD_CONSTRUCTOR;
}
else if ( declName == CONSTR_IDENT ) { // built-in
ASSERT( declFlags & TXD_BUILTIN, "Built-in flag not set: " << declFlags << " at " << this << " in " << this->context().scope() );
if ( declFlags & TXD_INITIALIZER ) {
ASSERT( storage == TXS_STATIC,
"Initializer not a static field: " << storage << " at " << this << " in " << this->context().scope() );
}
else {
ASSERT( declFlags & TXD_CONSTRUCTOR, "Constructor flag not set: " << declFlags << " at " << this << " in " << this->context().scope() );
ASSERT( storage == TXS_INSTANCEMETHOD,
"Constructor not an instance method: " << storage << " at " << this << " in " << this->context().scope() );
}
}

// Note: Field is processed in the 'outer' scope and not in the 'inner' scope of its declaration.
this->declaration = scope->declare_field( declName, this, declFlags, storage, TxIdentifier() );
}

bool TxNonLocalFieldDefNode::is_main_signature_valid( const TxActualType* funcType ) const {
auto retType = funcType->return_type();
bool retOk = bool( retType->get_type_class() == TXTC_VOID
|| retType->is_a( *this->context().package()->registry().get_builtin_type( TXBT_INTEGER ) ) );
if ( !retOk )
CERROR( this, "main() method has non-integer return type (must be void or integer): " << retType );

auto & argTypes = funcType->argument_types();
if ( argTypes.size() == 0 )
return retOk;
else if ( argTypes.size() == 1 ) {
const TxActualType* argsType = argTypes.at( 0 );
if ( argsType->get_type_class() == TXTC_REFERENCE ) {
auto targetType = argsType->target_type();
if ( targetType->get_type_class() == TXTC_ARRAY ) {
auto elemType = targetType->element_type();
if ( elemType->get_type_class() == TXTC_REFERENCE ) {
auto elemTargetType = elemType->target_type();
if ( elemTargetType->get_type_class() == TXTC_ARRAY ) {
auto elemTargetElemType = elemTargetType->element_type();
if ( elemTargetElemType->is_builtin( TXBT_UBYTE ) )
return retOk;
}
}
}
}
CERROR( this, "main() method has invalid argument [required signature is main() or main( &[]&[]UByte )] : " << argsType );
}
else
CERROR( this, "main() method has too many arguments [required signature is main() or main( &[]&[]UByte )]" );
return false;
}

void TxNonLocalFieldDefNode::resolution_pass() {
TxFieldDefiningNode::resolution_pass();

// handle main() function declarations:
if ( this->fieldName->str() == "main" ) {
auto funcField = this->field();
if ( funcField->qtype()->get_type_class() == TXTC_FUNCTION ) {
// verify main program function candidate
if ( !( funcField->get_storage() == TXS_GLOBAL || funcField->get_storage() == TXS_STATIC ) )
CERROR( this, "main() method must have global or static storage: " << funcField->get_storage() );
if ( is_main_signature_valid( funcField->qtype().type() ) ) {
// register main program function candidate
this->context().package()->registerMainFunc( this->declaration );
}
}
// non-function symbols declared with the name 'main' are allowed
}
}
22 changes: 14 additions & 8 deletions proto/src/ast/ast_fielddef_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,11 @@ class TxFieldDefiningNode : public TxEntityResolvingNode {


/** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
inline void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
this->declare_field( this->fieldName->str(), scope, declFlags, storage );
}

/** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() );
}
virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) = 0;
// /** Performs the declaration of the field defined by this node. To be run before declaration pass is run on this node. */
// inline void declare_field( const std::string& name, TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) {
// this->declaration = scope->declare_field( name, this, declFlags, storage, TxIdentifier() );
// }

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

virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override;

virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override;

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

bool is_main_signature_valid( const TxActualType* funcType ) const;

TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName,
TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression, bool modifiable )
: TxFieldDefiningNode( ploc, fieldName, typeExpression, initExpression, modifiable, false ) {
}

protected:
virtual void resolution_pass() override;

public:
TxNonLocalFieldDefNode( const TxLocation& ploc, const std::string& fieldName,
TxTypeExpressionNode* typeExpression, TxExpressionNode* initExpression )
Expand All @@ -171,6 +175,8 @@ class TxNonLocalFieldDefNode : public TxFieldDefiningNode {
return new TxNonLocalFieldDefNode( this->ploc, this->fieldName->str(), typeExpr, initExpr, this->modifiable );
}

virtual void declare_field( TxScopeSymbol* scope, TxDeclarationFlags declFlags, TxFieldStorage storage ) override;

/** Generates this field, potentially only as a declaration without initializer. Invoked from code referencing this field. */
virtual llvm::Value* code_gen_field_decl( LlvmGenerationContext& context ) const override;

Expand Down
13 changes: 7 additions & 6 deletions proto/src/ast/ast_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ unsigned TxNode::nextNodeId = 0;

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

std::string TxNode::parse_loc_string() const {
const size_t bsize = 32;
const size_t bsize = 256;
char buf[bsize];
std::string filename = ploc.begin.filename ? get_file_name( *ploc.begin.filename ) : "";
if ( ploc.begin.line == ploc.end.line ) {
int lcol = ( ploc.end.column > ploc.begin.column ) ? ploc.end.column : ploc.end.column;
snprintf( buf, bsize, "%3d.%2d-%d", ploc.begin.line, ploc.begin.column, lcol );
snprintf( buf, bsize, "%s:%3d.%2d-%d", filename.c_str(), ploc.begin.line, ploc.begin.column, lcol );
}
else
snprintf( buf, bsize, "%3d.%2d-%d.%d", ploc.begin.line, ploc.begin.column, ploc.end.line, ploc.end.column );
snprintf( buf, bsize, "%s:%3d.%2d-%d.%d", filename.c_str(), ploc.begin.line, ploc.begin.column,
ploc.end.line, ploc.end.column );
return std::string( buf );
}

Expand Down
1 change: 0 additions & 1 deletion proto/src/ast/expr/ast_exprs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ TxQualType TxFunctionCallNode::define_type( TxPassInfo passInfo ) {
}
}

// auto funcType = static_cast<const TxFunctionType*>( actualCalleeType );
if ( this->calleeType->modifiable_closure() && !constructorType ) {
ASSERT( this->callee->get_data_graph_origin_expr(), "Callee with modifiable closere didn't have origin expression: " << this->callee );
if ( !this->callee->get_data_graph_origin_expr()->check_chain_mutable() ) {
Expand Down
2 changes: 1 addition & 1 deletion proto/src/ast/expr/ast_field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const TxFieldDeclaration* resolve_field( const TxExpressionNode* origin, TxEntit

// first screen the fields that are of function type and take the correct number of arguments:
if ( field->qtype()->get_type_class() == TXTC_FUNCTION ) {
const TxFunctionType* fieldType = static_cast<const TxFunctionType*>( field->qtype().type() );
auto fieldType = field->qtype().type();
auto candArgTypes = fieldType->argument_types();
const TxActualType* arrayArgElemType = fieldType->vararg_elem_type();
const TxActualType* fixedArrayArgType = nullptr;
Expand Down
2 changes: 1 addition & 1 deletion proto/src/ast/expr/ast_lambda_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Function* TxLambdaExprNode::code_gen_function_decl( LlvmGenerationContext& conte
ASSERT( funcT, "Couldn't get LLVM type for function type " << this->funcHeaderNode->qtype() );

Function* function = cast<Function>( context.llvmModule().getOrInsertFunction( funcName, funcT ) );
function->setLinkage(GlobalValue::InternalLinkage); // (Note, could earlier cause LLVM to rename function)
function->setLinkage( GlobalValue::InternalLinkage );
// Note: function is of LLVM function pointer type (since it is an LLVM global value)
return function;
}
Expand Down
5 changes: 5 additions & 0 deletions proto/src/ast/expr/ast_ref.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ llvm::Constant* gen_get_ref_typeid ( LlvmGenerationContext& context, llvm::Const
llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, llvm::Type* refT, llvm::Value* ptrV, llvm::Value* tidV );
llvm::Constant* gen_ref( LlvmGenerationContext& context, llvm::Type* refT, llvm::Constant* ptrC, llvm::Constant* tidC );

/** Generates a Ref value where the type id equals the specified type's *statically known* type id.
* Caller must ensure this cannot be different from the proper runtime id.
*/
llvm::Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, const TxActualType* refType, llvm::Value* ptrV );

/** Converts a reference value from one type to another. If targetTypeId is specified, it will replace the original type id. */
llvm::Value* gen_ref_conversion( LlvmGenerationContext& context, GenScope* scope, llvm::Value* origRefV,
llvm::Type* targetRefT, uint32_t targetTypeId = UINT32_MAX );
Expand Down
7 changes: 7 additions & 0 deletions proto/src/ast/expr/ast_ref_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ Value* gen_ref( LlvmGenerationContext& context, GenScope* scope, Type* refT, Val
return refV;
}

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


Constant* gen_get_ref_pointer( LlvmGenerationContext& context, Constant* refC ) {
return refC->getAggregateElement( 0U );
Expand Down
2 changes: 1 addition & 1 deletion proto/src/ast/type/ast_typecreating_node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class TxAliasTypeNode : public TxTypeCreatingNode {
}


virtual void code_gen_type( LlvmGenerationContext& context ) const override;
virtual void code_gen_type( LlvmGenerationContext& context ) const override { }

virtual void visit_descendants( const AstVisitor& visitor, const AstCursor& thisCursor, const std::string& role, void* context ) override {
this->baseTypeNode->visit_ast( visitor, thisCursor, "basetype", context );
Expand Down
4 changes: 0 additions & 4 deletions proto/src/ast/type/ast_types_codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ void TxEmptyDerivedTypeNode::code_gen_type( LlvmGenerationContext& context ) con
this->baseTypeNode->code_gen_type( context );
}

void TxAliasTypeNode::code_gen_type( LlvmGenerationContext& context ) const {
this->baseTypeNode->code_gen_type( context );
}

void TxDerivedTypeNode::code_gen_builtin_type( LlvmGenerationContext& context ) const {
this->inner_code_gen_type( context );
}
Expand Down
13 changes: 4 additions & 9 deletions proto/src/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,15 +482,10 @@ int TxDriver::llvm_compile( const std::string& outputFileName ) {

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

Expand Down
Loading

0 comments on commit a321f36

Please sign in to comment.