Changeset 221c542e


Ignore:
Timestamp:
Jan 24, 2024, 8:19:18 AM (3 months ago)
Author:
Peter A. Buhr <pabuhr@…>
Branches:
master
Children:
64c4b4d
Parents:
bad9c8f (diff), 71b5aad5 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

Files:
11 edited

Legend:

Unmodified
Added
Removed
  • doc/proposals/enum.tex

    rbad9c8f r221c542e  
    245245
    246246
    247 \subsection{Aggressive Inline}
    248 To avoid allocating memory for enumeration data structures, \CFA inline the result of enumeration attribute pseudo-function whenever it is possible.
    249 \begin{lstlisting}[label=lst:enumeration_inline]
    250 enum(int) OddNumber { A=1, B=3 };
    251 sout | "A: " | OddNumber.A | "B: " | OddNumber.B | "A+B: " | OddNumber.A + OddNumber.B
    252 \end{lstlisting}
    253 Instead of calling pseudo-function @value@ on expression $OddNumber.A$ and $OddNumber.B$, because the result is known statistically, \CFA will inline the constant expression 1 and 3, respectively. Because no runtime lookup for enumeration value is necessary, \CFA will not generate data structure for enumeration OddNumber.
    254 
    255 \subsection{Weak Reference}
    256 \begin{lstlisting}[label=lst:week_ref]
    257 enum(int) OddNumber { A=1, B=3 };
    258 enum OddNumber i = ...;
    259 ...
    260 sout | OddNumber;
    261 \end{lstlisting}
    262 In this example, \CFA cannot determine the static value of the enum variable i, and Runtime lookup is necessary. The OddNumber can be referenced in multiple compilations, and allocating the arrays in all compilation units is not desirable. \CFA addresses this by declaring the value array as a weak reference. All compilation units reference OddNumber have weak references to the same enumeration data structure. No extra memory is allocated if more compilation units reference OddNumber, and the OddNumber is initialized once.
    263 
     247\
    264248\section{Unification}
    265249
     
    638622\section{Implementation}
    639623
    640 \subsection{Compiler Representation (Reworking)}
     624\subsection{Static Attribute Expression}
     625\begin{lstlisting}[label=lst:static_attr]
     626enum( char * ) Colour {
     627    Red = "red", Blue = "blue", Green = "green" 
     628};
     629\end{lstlisting}
     630An enumerator expression returns its enumerator value as a constant expression with no runtime cost. For example, @Colour.Red@ is equivalent to the constant expression "red", and \CFA finishes the expression evaluation before generating the corresponding C code. Applying a pseudo-function to a constant enumerator expression results in a constant expression as well. @value( Colour.Red )@, @position( Colour. Red )@, and @label( Colour.Red )@ are equivalent to constant expression with char * value "red", int value 0, and char * value "Red", respectively.
     631
     632\subsection{Runtime Attribute Expression and Weak Referenced Data}
     633\begin{lstlisting}[label=lst:dynamic_attr]
     634Colour c;
     635...
     636value( c ); // or c
     637\end{lstlisting}
     638An enumeration variable c is equivalent to an integer variable with the value of @position( c )@ In Example~\ref{lst:dynamic_attr}, the value of enumeration variable c is unknown at compile time. In this case, the pseudo-function calls are reduced to expression that returns the enumerator values at runtime.
     639
     640\CFA stores the variables and labels in const arrays to provide runtime lookup for enumeration information.
     641
     642\begin{lstlisting}[label=lst:attr_array]
     643const char * Colour_labels [3] = { "Red", "Blue", "Green" };
     644const char * Colour_values [3] = { "red", "blue", "green" };
     645\end{lstlisting}
     646The \CFA compiles transforms the attribute expressions into array access.
     647\begin{lstlisting}[label=lst:attr_array_access]
     648position( c ) // c; an integer
     649value( c ); // Colour_values[c]
     650label( c ); // Colour_labels[c]
     651\end{lstlisting}
     652
     653To avoid unnecessary memory usage, the labels and values array are only generated as needed, and only generate once across all compilation units. By default, \CFA defers the declaration of the label and value arrays until an call to attribute function with a dynamic value. If an attribute function is never called on a dynamic value of an enumerator, the array will never be allocated. Once the arrays are created, all compilation units share a weak reference to the allocation array.
     654
     655\subsection{Enum Prelude}
     656
     657\begin{lstlisting}[label=lst:enum_func_dec]
     658forall( T ) {
     659    unsigned position( unsigned );
     660    T value( unsigned );
     661    char * label( unsigned );
     662}
     663\end{lstlisting}
     664\CFA loads the declaration of enumeration function from the enum.hfa.
     665
     666\subsection{Internal Representation}
     667
    641668The definition of an enumeration is represented by an internal type called @EnumDecl@. At the minimum, it stores all the information needed to construct the companion object. Therefore, an @EnumDecl@ can be represented as the following:
    642669\begin{lstlisting}[label=lst:EnumDecl]
     
    667694
    668695
    669 \subsection{(Rework) Companion Object and Companion Function}
    670 
    671 \begin{lstlisting}[caption={Enum Type Functions}, label=lst:cforall_enum_functions]
    672 forall( T )
    673 struct Companion {
    674         const T * const values;
    675         const char * label;
    676         int length;
    677 };
    678 \end{lstlisting}
    679 \CFA generates companion objects, an instance of structure that encloses @necessary@ data to represent an enumeration. The size of the companion is unknown at the compilation time, and it "grows" in size to compensate for the @usage@.
    680 
    681 The companion object is singleton across the compilation (investigation). 
    682 
    683 \CFA generates the definition of companion functions.
    684 Because \CFA implicitly stores an enumeration instance as its position, the companion function @position@ does nothing but return the position it is passed.
    685 Companions function @value@ and @label@ return the array item at the given position of @values@ and @labels@, respectively.
    686 \begin{lstlisting}[label=lst:companion_definition]
    687 int position( Companion o, int pos ) { return pos; }
    688 T value( Companion o, int pos ) { return o.values[ pos ]; }
    689 char * label( Companion o, int pos ) { return o.labels[ pos ]; }
    690 \end{lstlisting}
    691 Notably, the @Companion@ structure definition, and all companion objects, are visible to users.
    692 A user can retrieve values and labels defined in an enumeration by accessing the values and labels directly, or indirectly by calling @Companion@ functions @values@ and @labels@
    693 \begin{lstlisting}[label=lst:companion_definition_values_labels]
    694 Colour.values; // read the Companion's values
    695 values( Colour ); // same as Colour.values
    696 \end{lstlisting}
     696% \subsection{(Rework) Companion Object and Companion Function}
     697
     698% \begin{lstlisting}[caption={Enum Type Functions}, label=lst:cforall_enum_functions]
     699% forall( T )
     700% struct Companion {
     701%       const T * const values;
     702%         const char * label;
     703%       int length;
     704% };
     705% \end{lstlisting}
     706% \CFA generates companion objects, an instance of structure that encloses @necessary@ data to represent an enumeration. The size of the companion is unknown at the compilation time, and it "grows" in size to compensate for the @usage@.
     707
     708% The companion object is singleton across the compilation (investigation). 
     709
     710% \CFA generates the definition of companion functions.
     711% Because \CFA implicitly stores an enumeration instance as its position, the companion function @position@ does nothing but return the position it is passed.
     712% Companions function @value@ and @label@ return the array item at the given position of @values@ and @labels@, respectively.
     713% \begin{lstlisting}[label=lst:companion_definition]
     714% int position( Companion o, int pos ) { return pos; }
     715% T value( Companion o, int pos ) { return o.values[ pos ]; }
     716% char * label( Companion o, int pos ) { return o.labels[ pos ]; }
     717% \end{lstlisting}
     718% Notably, the @Companion@ structure definition, and all companion objects, are visible to users.
     719% A user can retrieve values and labels defined in an enumeration by accessing the values and labels directly, or indirectly by calling @Companion@ functions @values@ and @labels@
     720% \begin{lstlisting}[label=lst:companion_definition_values_labels]
     721% Colour.values; // read the Companion's values
     722% values( Colour ); // same as Colour.values
     723% \end{lstlisting}
    697724
    698725\subsection{Companion Traits (experimental)}
  • src/AST/Decl.hpp

    rbad9c8f r221c542e  
    312312        ptr<Type> base;
    313313        enum class EnumHiding { Visible, Hide } hide;
    314 
    315314        EnumDecl( const CodeLocation& loc, const std::string& name, bool isTyped = false,
    316315                std::vector<ptr<Attribute>>&& attrs = {}, Linkage::Spec linkage = Linkage::Cforall,
  • src/CodeGen/CodeGenerator.cpp

    rbad9c8f r221c542e  
    11351135                if ( clause->when_cond ) {
    11361136                        output << "when(";
    1137                         stmt->timeout_cond->accept( *visitor );
     1137                        clause->when_cond->accept( *visitor );
    11381138                        output << ") ";
    11391139                }
  • src/InitTweak/FixInit.cpp

    rbad9c8f r221c542e  
    4949        if ( auto inst = dynamic_cast<const ast::StructInstType *>( t ) ) {
    5050                return inst->base->params;
    51         }
    52         if ( auto inst = dynamic_cast<const ast::UnionInstType *>( t ) ) {
     51        } else if ( auto inst = dynamic_cast<const ast::UnionInstType *>( t ) ) {
    5352                return inst->base->params;
    5453        }
     
    245244const ast::DeclWithType * getDtorFunc( const ast::ObjectDecl * objDecl, const ast::Stmt * input, std::list< ast::ptr<ast::Stmt> > & stmtsToAdd ) {
    246245        const CodeLocation loc = input->location;
    247         // unwrap implicit statement wrapper
    248         // Statement * dtor = input;
    249246        assert( input );
    250         // std::list< const ast::Expr * > matches;
    251247        auto matches = collectCtorDtorCalls( input );
    252248
    253         if ( dynamic_cast< const ast::ExprStmt * >( input ) ) {
    254                 // only one destructor call in the expression
    255                 if ( matches.size() == 1 ) {
    256                         auto func = getFunction( matches.front() );
    257                         assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
    258 
    259                         // cleanup argument must be a function, not an object (including function pointer)
    260                         if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
    261                                 if ( dtorFunc->type->forall.empty() ) {
    262                                         // simple case where the destructor is a monomorphic function call - can simply
    263                                         // use that function as the cleanup function.
    264                                         return func;
    265                                 }
     249        // The simple case requires a direct call and only one destructor call.
     250        if ( dynamic_cast< const ast::ExprStmt * >( input ) && matches.size() == 1 ) {
     251                auto func = getFunction( matches.front() );
     252                assertf( func, "getFunction failed to find function in %s", toString( matches.front() ).c_str() );
     253
     254                // cleanup argument must be a function, not an object (including function pointer)
     255                if ( auto dtorFunc = dynamic_cast< const ast::FunctionDecl * > ( func ) ) {
     256                        if ( dtorFunc->type->forall.empty() ) {
     257                                // simple case where the destructor is a monomorphic function call - can simply
     258                                // use that function as the cleanup function.
     259                                return func;
    266260                        }
    267261                }
     
    301295        for ( auto i = translationUnit.decls.begin(); i != translationUnit.decls.end(); ++i ) {
    302296                try {
    303                         // maybeAccept( *i, fixer ); translationUnit should never contain null
    304297                        *i = (*i)->accept(fixer);
    305298                        translationUnit.decls.splice( i, fixer.core.staticDtorDecls );
     
    314307
    315308const ast::StmtExpr * StmtExprResult::previsit( const ast::StmtExpr * stmtExpr ) {
    316         // we might loose the result expression here so add a pointer to trace back
    317309        assert( stmtExpr->result );
    318         const ast::Type * result = stmtExpr->result;
    319         if ( ! result->isVoid() ) {
    320                 auto mutExpr = mutate(stmtExpr);
    321                 const ast::CompoundStmt * body = mutExpr->stmts;
    322                 assert( ! body->kids.empty() );
    323                 mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
    324                 return mutExpr;
    325         }
    326         return stmtExpr;
     310        if ( stmtExpr->result->isVoid() ) return stmtExpr;
     311
     312        auto mutExpr = mutate( stmtExpr );
     313        const ast::CompoundStmt * body = mutExpr->stmts;
     314        assert( !body->kids.empty() );
     315        mutExpr->resultExpr = body->kids.back().strict_as<ast::ExprStmt>();
     316        return mutExpr;
    327317}
    328318
     
    330320        // wrap each top-level ExprStmt in a block so that destructors for argument and return temporaries are destroyed
    331321        // in the correct places
    332         ast::CompoundStmt * ret = new ast::CompoundStmt( stmt->location, { stmt } );
    333         return ret;
     322        return new ast::CompoundStmt( stmt->location, { stmt } );
    334323}
    335324
     
    556545        arg = cpCtor;
    557546        return destructRet( tmp, arg );
    558 
    559         // impCpCtorExpr->dtors.push_front( makeCtorDtor( "^?{}", tmp ) );
    560547}
    561548
     
    608595        }
    609596        return nullptr;
    610         // impCpCtorExpr->get_dtors().push_front( makeCtorDtor( "^?{}", ret ) );
    611597}
    612598
     
    625611        for ( auto & arg : appExpr->args ) {
    626612                const ast::Type * formal = nullptr;
    627                 if ( iter != params.end() ) { // does not copy construct C-style variadic arguments
    628                         // DeclarationWithType * param = *iter++;
     613                // Do not copy construct C-style variadic arguments.
     614                if ( iter != params.end() ) {
    629615                        formal = *iter++;
    630616                }
     
    659645        // deletion of wrapper should be handled by pass template now
    660646
    661         // impCpCtorExpr->callExpr = nullptr;
    662647        assert (appExpr->env == nullptr);
    663648        appExpr->env = impCpCtorExpr->env;
    664         // std::swap( impCpCtorExpr->env, appExpr->env );
    665         // assert( impCpCtorExpr->env == nullptr );
    666         // delete impCpCtorExpr;
    667649
    668650        if ( returnDecl ) {
     
    676658                }
    677659                // move env from appExpr to retExpr
    678                 // std::swap( assign->env, appExpr->env );
    679660                assign->env = appExpr->env;
    680661                // actual env is handled by common routine that replaces WithTypeSubstitution
     
    716697
    717698        assert( stmtExpr->result );
    718         // const ast::Type * result = stmtExpr->result;
    719         if ( ! stmtExpr->result->isVoid() ) {
    720                 static UniqueName retNamer("_tmp_stmtexpr_ret");
    721 
    722                 // result = result->clone();
    723                 auto result = env->apply( stmtExpr->result.get() ).node;
    724                 if ( ! InitTweak::isConstructable( result ) ) {
    725                         // delete result;
    726                         return stmtExpr;
    727                 }
    728                 auto mutResult = result.get_and_mutate();
    729                 mutResult->set_const(false);
    730 
    731                 // create variable that will hold the result of the stmt expr
    732                 auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
    733                 stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
    734 
    735                 assertf(
    736                         stmtExpr->resultExpr,
    737                         "Statement-Expression should have a resulting expression at %s:%d",
    738                         stmtExpr->location.filename.c_str(),
    739                         stmtExpr->location.first_line
    740                 );
    741 
    742                 const ast::ExprStmt * last = stmtExpr->resultExpr;
    743                 // xxx - if this is non-unique, need to copy while making resultExpr ref
    744                 assertf(last->unique(), "attempt to modify weakly shared statement");
    745                 auto mutLast = mutate(last);
    746                 // above assertion means in-place mutation is OK
    747                 try {
    748                         mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
    749                 } catch(...) {
    750                         std::cerr << "*CFA internal error: ";
    751                         std::cerr << "can't resolve implicit constructor";
    752                         std::cerr << " at " << stmtExpr->location.filename;
    753                         std::cerr << ":" << stmtExpr->location.first_line << std::endl;
    754 
    755                         abort();
    756                 }
    757 
    758                 // add destructors after current statement
    759                 stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
    760 
    761                 // must have a non-empty body, otherwise it wouldn't have a result
    762                 assert( ! stmts.empty() );
    763 
    764                 // if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
    765                 stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
    766         } // if
     699        if ( stmtExpr->result->isVoid() ) {
     700                assert( stmtExpr->returnDecls.empty() );
     701                assert( stmtExpr->dtors.empty() );
     702
     703                return stmtExpr;
     704        }
     705
     706        static UniqueName retNamer("_tmp_stmtexpr_ret");
     707
     708        auto result = env->apply( stmtExpr->result.get() ).node;
     709        if ( ! InitTweak::isConstructable( result ) ) {
     710                return stmtExpr;
     711        }
     712        auto mutResult = result.get_and_mutate();
     713        mutResult->set_const(false);
     714
     715        // create variable that will hold the result of the stmt expr
     716        auto ret = new ast::ObjectDecl(loc, retNamer.newName(), mutResult, nullptr );
     717        stmtsToAddBefore.push_back( new ast::DeclStmt(loc, ret ) );
     718
     719        assertf(
     720                stmtExpr->resultExpr,
     721                "Statement-Expression should have a resulting expression at %s:%d",
     722                stmtExpr->location.filename.c_str(),
     723                stmtExpr->location.first_line
     724        );
     725
     726        const ast::ExprStmt * last = stmtExpr->resultExpr;
     727        // xxx - if this is non-unique, need to copy while making resultExpr ref
     728        assertf(last->unique(), "attempt to modify weakly shared statement");
     729        auto mutLast = mutate(last);
     730        // above assertion means in-place mutation is OK
     731        try {
     732                mutLast->expr = makeCtorDtor( "?{}", ret, mutLast->expr );
     733        } catch(...) {
     734                std::cerr << "*CFA internal error: ";
     735                std::cerr << "can't resolve implicit constructor";
     736                std::cerr << " at " << stmtExpr->location.filename;
     737                std::cerr << ":" << stmtExpr->location.first_line << std::endl;
     738
     739                abort();
     740        }
     741
     742        // add destructors after current statement
     743        stmtsToAddAfter.push_back( new ast::ExprStmt(loc, makeCtorDtor( "^?{}", ret ) ) );
     744
     745        // must have a non-empty body, otherwise it wouldn't have a result
     746        assert( ! stmts.empty() );
     747
     748        // if there is a return decl, add a use as the last statement; will not have return decl on non-constructable returns
     749        stmts.push_back( new ast::ExprStmt(loc, new ast::VariableExpr(loc, ret ) ) );
    767750
    768751        assert( stmtExpr->returnDecls.empty() );
     
    798781        auto mutExpr = mutate(unqExpr);
    799782        if ( ! unqMap.count( unqExpr->id ) ) {
    800                 // resolve expr and find its
    801 
    802783                auto impCpCtorExpr = mutExpr->expr.as<ast::ImplicitCopyCtorExpr>();
    803                 // PassVisitor<ResolveCopyCtors> fixer;
    804 
    805784                mutExpr->expr = mutExpr->expr->accept( *visitor );
    806785                // it should never be necessary to wrap a void-returning expression in a UniqueExpr - if this assumption changes, this needs to be rethought
     
    820799        } else {
    821800                // take data from other UniqueExpr to ensure consistency
    822                 // delete unqExpr->get_expr();
    823801                mutExpr->expr = unqMap[mutExpr->id]->expr;
    824                 // delete unqExpr->result;
    825802                mutExpr->result = mutExpr->expr->result;
    826803        }
     
    832809
    833810        // since this removes the init field from objDecl, it must occur after children are mutated (i.e. postvisit)
    834         if ( ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>() ) {
    835                 auto objDecl = mutate(_objDecl);
    836 
    837                 // could this be non-unique?
    838                 if (objDecl != _objDecl) {
    839                         std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
    840                 }
    841                 // a decision should have been made by the resolver, so ctor and init are not both non-NULL
    842                 assert( ! ctorInit->ctor || ! ctorInit->init );
    843                 if ( const ast::Stmt * ctor = ctorInit->ctor ) {
    844                         if ( objDecl->storage.is_static ) {
    845                                 addDataSectionAttribute(objDecl);
    846                                 // originally wanted to take advantage of gcc nested functions, but
    847                                 // we get memory errors with this approach. To remedy this, the static
    848                                 // variable is hoisted when the destructor needs to be called.
    849                                 //
    850                                 // generate:
    851                                 // static T __objName_static_varN;
    852                                 // void __objName_dtor_atexitN() {
    853                                 //   __dtor__...;
    854                                 // }
    855                                 // int f(...) {
    856                                 //   ...
    857                                 //   static bool __objName_uninitialized = true;
    858                                 //   if (__objName_uninitialized) {
    859                                 //     __ctor(__objName);
    860                                 //     __objName_uninitialized = false;
    861                                 //     atexit(__objName_dtor_atexitN);
    862                                 //   }
    863                                 //   ...
    864                                 // }
    865 
    866                                 static UniqueName dtorCallerNamer( "_dtor_atexit" );
    867 
    868                                 // static bool __objName_uninitialized = true
    869                                 auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
    870                                 auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
    871                                 auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
    872                                 isUninitializedVar->fixUniqueId();
    873 
    874                                 // __objName_uninitialized = false;
    875                                 auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
    876                                 setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
    877                                 setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
    878 
    879                                 // generate body of if
    880                                 auto initStmts = new ast::CompoundStmt(loc);
    881                                 auto & body = initStmts->kids;
    882                                 body.push_back( ctor );
    883                                 body.push_back( new ast::ExprStmt(loc, setTrue ) );
    884 
    885                                 // put it all together
    886                                 auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
    887                                 stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
    888                                 stmtsToAddAfter.push_back( ifStmt );
    889 
    890                                 const ast::Stmt * dtor = ctorInit->dtor;
    891 
    892                                 // these should be automatically managed once reassigned
    893                                 // objDecl->set_init( nullptr );
    894                                 // ctorInit->set_ctor( nullptr );
    895                                 // ctorInit->set_dtor( nullptr );
    896                                 if ( dtor ) {
    897                                         // if the object has a non-trivial destructor, have to
    898                                         // hoist it and the object into the global space and
    899                                         // call the destructor function with atexit.
    900 
    901                                         // Statement * dtorStmt = dtor->clone();
    902 
    903                                         // void __objName_dtor_atexitN(...) {...}
    904                                         ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
    905                                         dtorCaller->fixUniqueId();
    906                                         // dtorCaller->stmts->push_back( dtor );
    907 
    908                                         // atexit(dtor_atexit);
    909                                         auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
    910                                         callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
    911 
    912                                         body.push_back( new ast::ExprStmt(loc, callAtexit ) );
    913 
    914                                         // hoist variable and dtor caller decls to list of decls that will be added into global scope
    915                                         staticDtorDecls.push_back( objDecl );
    916                                         staticDtorDecls.push_back( dtorCaller );
    917 
    918                                         // need to rename object uniquely since it now appears
    919                                         // at global scope and there could be multiple function-scoped
    920                                         // static variables with the same name in different functions.
    921                                         // Note: it isn't sufficient to modify only the mangleName, because
    922                                         // then subsequent SymbolTable passes can choke on seeing the object's name
    923                                         // if another object has the same name and type. An unfortunate side-effect
    924                                         // of renaming the object is that subsequent NameExprs may fail to resolve,
    925                                         // but there shouldn't be any remaining past this point.
    926                                         static UniqueName staticNamer( "_static_var" );
    927                                         objDecl->name = objDecl->name + staticNamer.newName();
    928                                         objDecl->mangleName = Mangle::mangle( objDecl );
    929                                         objDecl->init = nullptr;
    930 
    931                                         // xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
    932                                         // create a new object which is never used
    933                                         static UniqueName dummyNamer( "_dummy" );
    934                                         auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
    935                                         // delete ctorInit;
    936                                         return dummy;
    937                                 } else {
    938                                         objDecl->init = nullptr;
    939                                         return objDecl;
    940                                 }
     811        ast::ptr<ast::ConstructorInit> ctorInit = _objDecl->init.as<ast::ConstructorInit>();
     812
     813        if ( nullptr == ctorInit ) return _objDecl;
     814
     815        auto objDecl = mutate(_objDecl);
     816
     817        // could this be non-unique?
     818        if (objDecl != _objDecl) {
     819                std::cerr << "FixInit: non-unique object decl " << objDecl->location << objDecl->name << std::endl;
     820        }
     821        // a decision should have been made by the resolver, so ctor and init are not both non-NULL
     822        assert( ! ctorInit->ctor || ! ctorInit->init );
     823        if ( const ast::Stmt * ctor = ctorInit->ctor ) {
     824                if ( objDecl->storage.is_static ) {
     825                        addDataSectionAttribute(objDecl);
     826                        // originally wanted to take advantage of gcc nested functions, but
     827                        // we get memory errors with this approach. To remedy this, the static
     828                        // variable is hoisted when the destructor needs to be called.
     829                        //
     830                        // generate:
     831                        // static T __objName_static_varN;
     832                        // void __objName_dtor_atexitN() {
     833                        //   __dtor__...;
     834                        // }
     835                        // int f(...) {
     836                        //   ...
     837                        //   static bool __objName_uninitialized = true;
     838                        //   if (__objName_uninitialized) {
     839                        //     __ctor(__objName);
     840                        //     __objName_uninitialized = false;
     841                        //     atexit(__objName_dtor_atexitN);
     842                        //   }
     843                        //   ...
     844                        // }
     845
     846                        static UniqueName dtorCallerNamer( "_dtor_atexit" );
     847
     848                        // static bool __objName_uninitialized = true
     849                        auto boolType = new ast::BasicType( ast::BasicType::Kind::Bool );
     850                        auto boolInitExpr = new ast::SingleInit(loc, ast::ConstantExpr::from_int(loc, 1 ) );
     851                        auto isUninitializedVar = new ast::ObjectDecl(loc, objDecl->mangleName + "_uninitialized", boolType, boolInitExpr, ast::Storage::Static, ast::Linkage::Cforall);
     852                        isUninitializedVar->fixUniqueId();
     853
     854                        // __objName_uninitialized = false;
     855                        auto setTrue = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "?=?" ) );
     856                        setTrue->args.push_back( new ast::VariableExpr(loc, isUninitializedVar ) );
     857                        setTrue->args.push_back( ast::ConstantExpr::from_int(loc, 0 ) );
     858
     859                        // generate body of if
     860                        auto initStmts = new ast::CompoundStmt(loc);
     861                        auto & body = initStmts->kids;
     862                        body.push_back( ctor );
     863                        body.push_back( new ast::ExprStmt(loc, setTrue ) );
     864
     865                        // put it all together
     866                        auto ifStmt = new ast::IfStmt(loc, new ast::VariableExpr(loc, isUninitializedVar ), initStmts, 0 );
     867                        stmtsToAddAfter.push_back( new ast::DeclStmt(loc, isUninitializedVar ) );
     868                        stmtsToAddAfter.push_back( ifStmt );
     869
     870                        const ast::Stmt * dtor = ctorInit->dtor;
     871                        if ( dtor ) {
     872                                // if the object has a non-trivial destructor, have to
     873                                // hoist it and the object into the global space and
     874                                // call the destructor function with atexit.
     875
     876                                // void __objName_dtor_atexitN(...) {...}
     877                                ast::FunctionDecl * dtorCaller = new ast::FunctionDecl(loc, objDecl->mangleName + dtorCallerNamer.newName(), {}, {}, {}, {}, new ast::CompoundStmt(loc, {dtor}), ast::Storage::Static, ast::Linkage::C );
     878                                dtorCaller->fixUniqueId();
     879
     880                                // atexit(dtor_atexit);
     881                                auto callAtexit = new ast::UntypedExpr(loc, new ast::NameExpr(loc, "atexit" ) );
     882                                callAtexit->args.push_back( new ast::VariableExpr(loc, dtorCaller ) );
     883
     884                                body.push_back( new ast::ExprStmt(loc, callAtexit ) );
     885
     886                                // hoist variable and dtor caller decls to list of decls that will be added into global scope
     887                                staticDtorDecls.push_back( objDecl );
     888                                staticDtorDecls.push_back( dtorCaller );
     889
     890                                // need to rename object uniquely since it now appears
     891                                // at global scope and there could be multiple function-scoped
     892                                // static variables with the same name in different functions.
     893                                // Note: it isn't sufficient to modify only the mangleName, because
     894                                // then subsequent SymbolTable passes can choke on seeing the object's name
     895                                // if another object has the same name and type. An unfortunate side-effect
     896                                // of renaming the object is that subsequent NameExprs may fail to resolve,
     897                                // but there shouldn't be any remaining past this point.
     898                                static UniqueName staticNamer( "_static_var" );
     899                                objDecl->name = objDecl->name + staticNamer.newName();
     900                                objDecl->mangleName = Mangle::mangle( objDecl );
     901                                objDecl->init = nullptr;
     902
     903                                // xxx - temporary hack: need to return a declaration, but want to hoist the current object out of this scope
     904                                // create a new object which is never used
     905                                static UniqueName dummyNamer( "_dummy" );
     906                                auto dummy = new ast::ObjectDecl(loc, dummyNamer.newName(), new ast::PointerType(new ast::VoidType()), nullptr, ast::Storage::Static, ast::Linkage::Cforall, 0, { new ast::Attribute("unused") } );
     907                                return dummy;
    941908                        } else {
    942                                 auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
    943                                 auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
    944                                 const ast::ApplicationExpr * ctorCall = nullptr;
    945                                 if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
    946                                         // clean up intrinsic copy constructor calls by making them into SingleInits
    947                                         const ast::Expr * ctorArg = ctorCall->args.back();
    948                                         // ctorCall should be gone afterwards
    949                                         auto mutArg = mutate(ctorArg);
    950                                         mutArg->env = ctorCall->env;
    951                                         // std::swap( ctorArg->env, ctorCall->env );
    952                                         objDecl->init = new ast::SingleInit(loc, mutArg );
    953 
    954                                         // ctorCall->args.pop_back();
    955                                 } else {
    956                                         stmtsToAddAfter.push_back( ctor );
    957                                         objDecl->init = nullptr;
    958                                         // ctorInit->ctor = nullptr;
    959                                 }
    960 
    961                                 const ast::Stmt * dtor = ctorInit->dtor;
    962                                 if ( dtor ) {
    963                                         auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
    964                                         const ast::Stmt * dtorStmt = implicit->callStmt;
    965 
    966                                         // don't need to call intrinsic dtor, because it does nothing, but
    967                                         // non-intrinsic dtors must be called
    968                                         if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
    969                                                 // set dtor location to the object's location for error messages
    970                                                 auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
    971                                                 objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
    972                                                 // ctorInit->dtor = nullptr;
    973                                         } // if
    974                                 }
    975                         } // if
    976                 } else if ( const ast::Init * init = ctorInit->init ) {
    977                         objDecl->init = init;
    978                         // ctorInit->init = nullptr;
     909                                objDecl->init = nullptr;
     910                                return objDecl;
     911                        }
    979912                } else {
    980                         // no constructor and no initializer, which is okay
    981                         objDecl->init = nullptr;
     913                        auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * > ( ctor );
     914                        auto ctorStmt = implicit->callStmt.as<ast::ExprStmt>();
     915                        const ast::ApplicationExpr * ctorCall = nullptr;
     916                        if ( ctorStmt && (ctorCall = isIntrinsicCallExpr( ctorStmt->expr )) && ctorCall->args.size() == 2 ) {
     917                                // clean up intrinsic copy constructor calls by making them into SingleInits
     918                                const ast::Expr * ctorArg = ctorCall->args.back();
     919                                // ctorCall should be gone afterwards
     920                                auto mutArg = mutate(ctorArg);
     921                                mutArg->env = ctorCall->env;
     922                                objDecl->init = new ast::SingleInit(loc, mutArg );
     923                        } else {
     924                                stmtsToAddAfter.push_back( ctor );
     925                                objDecl->init = nullptr;
     926                        }
     927
     928                        const ast::Stmt * dtor = ctorInit->dtor;
     929                        if ( dtor ) {
     930                                auto implicit = strict_dynamic_cast< const ast::ImplicitCtorDtorStmt * >( dtor );
     931                                const ast::Stmt * dtorStmt = implicit->callStmt;
     932
     933                                // don't need to call intrinsic dtor, because it does nothing, but
     934                                // non-intrinsic dtors must be called
     935                                if ( ! isIntrinsicSingleArgCallStmt( dtorStmt ) ) {
     936                                        // set dtor location to the object's location for error messages
     937                                        auto dtorFunc = getDtorFunc( objDecl, dtorStmt, stmtsToAddBefore );
     938                                        objDecl->attributes.push_back( new ast::Attribute( "cleanup", { new ast::VariableExpr(loc, dtorFunc ) } ) );
     939                                } // if
     940                        }
    982941                } // if
    983                 // delete ctorInit;
    984                 return objDecl;
     942        } else if ( const ast::Init * init = ctorInit->init ) {
     943                objDecl->init = init;
     944        } else {
     945                // no constructor and no initializer, which is okay
     946                objDecl->init = nullptr;
    985947        } // if
    986         return _objDecl;
     948        return objDecl;
    987949}
    988950
     
    1006968
    1007969void LabelFinder::previsit( const ast::CompoundStmt * stmt ) {
    1008         previsit( (const ast::Stmt *) stmt );
     970        previsit( (const ast::Stmt *)stmt );
    1009971        Parent::previsit( stmt );
    1010972}
     
    1022984        // its children manually.
    1023985        if (funcDecl->type) funcDecl->type->accept(finder);
    1024         // maybeAccept( funcDecl->type, finder );
    1025         if (funcDecl->stmts) funcDecl->stmts->accept(finder) ;
     986        if (funcDecl->stmts) funcDecl->stmts->accept(finder);
    1026987
    1027988        // all labels for this function have been collected, insert destructors as appropriate via implicit recursion.
     
    10781039}
    10791040
     1041/// Should we check for warnings? (The function is user-defined constrctor or destructor.)
    10801042bool checkWarnings( const ast::FunctionDecl * funcDecl ) {
    1081         // only check for warnings if the current function is a user-defined constructor or destructor
    10821043        if ( ! funcDecl ) return false;
    10831044        if ( ! funcDecl->stmts ) return false;
     
    11051066
    11061067        isCtor = CodeGen::isConstructor( function->name );
    1107         if ( checkWarnings( function ) ) {
    1108                 // const ast::FunctionType * type = function->type;
    1109                 // assert( ! type->params.empty() );
    1110                 thisParam = function->params.front().strict_as<ast::ObjectDecl>();
    1111                 auto thisType = getPointerBase( thisParam->get_type() );
    1112                 auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
    1113                 if ( structType ) {
    1114                         structDecl = structType->base;
    1115                         for ( auto & member : structDecl->members ) {
    1116                                 if ( auto field = member.as<ast::ObjectDecl>() ) {
    1117                                         // record all of the struct type's members that need to be constructed or
    1118                                         // destructed by the end of the function
    1119                                         unhandled.insert( field );
    1120                                 }
     1068
     1069        // Remaining code is only for warnings.
     1070        if ( ! checkWarnings( function ) ) return;
     1071        thisParam = function->params.front().strict_as<ast::ObjectDecl>();
     1072        auto thisType = getPointerBase( thisParam->get_type() );
     1073        auto structType = dynamic_cast< const ast::StructInstType * >( thisType );
     1074        if ( structType ) {
     1075                structDecl = structType->base;
     1076                for ( auto & member : structDecl->members ) {
     1077                        if ( auto field = member.as<ast::ObjectDecl>() ) {
     1078                                // record all of the struct type's members that need to be constructed or
     1079                                // destructed by the end of the function
     1080                                unhandled.insert( field );
    11211081                        }
    11221082                }
     
    11641124
    11651125                        // insert and resolve default/copy constructor call for each field that's unhandled
    1166                         // std::list< const ast::Stmt * > stmt;
    11671126                        ast::Expr * arg2 = nullptr;
    11681127                        if ( function->name == "?{}" && isCopyFunction( function ) ) {
    11691128                                // if copy ctor, need to pass second-param-of-this-function.field
    1170                                 // std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
    11711129                                assert( function->params.size() == 2 );
    11721130                                arg2 = new ast::MemberExpr(funcDecl->location, field, new ast::VariableExpr(funcDecl->location, function->params.back() ) );
     
    11791137
    11801138                        if ( callStmt ) {
    1181                                 // auto & callStmt = stmt.front();
    1182 
    11831139                                try {
    11841140                                        callStmt = callStmt->accept( *visitor );
     
    11861142                                                mutStmts->push_front( callStmt );
    11871143                                        } else { // TODO: don't generate destructor function/object for intrinsic calls
    1188                                                 // destructor statements should be added at the end
    1189                                                 // function->get_statements()->push_back( callStmt );
    11901144
    11911145                                                // Optimization: do not need to call intrinsic destructors on members
     
    12221176                throw errors;
    12231177        }
    1224         // return funcDecl;
    12251178        return function;
    12261179}
     
    12561209
    12571210        std::string fname = getFunctionName( appExpr );
    1258         if ( fname == function->name ) {
    1259                 // call to same kind of function
    1260                 const ast::Expr * firstParam = appExpr->args.front();
    1261 
    1262                 if ( isThisExpression( firstParam, thisParam ) ) {
    1263                         // if calling another constructor on thisParam, assume that function handles
    1264                         // all members - if it doesn't a warning will appear in that function.
    1265                         unhandled.clear();
    1266                 } else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
    1267                         // if first parameter is a member expression on the this parameter,
    1268                         // then remove the member from unhandled set.
    1269                         if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
    1270                                 unhandled.erase( memberExpr->member );
    1271                         }
     1211        if ( fname != function->name ) return;
     1212
     1213        // call to same kind of function
     1214        const ast::Expr * firstParam = appExpr->args.front();
     1215        if ( isThisExpression( firstParam, thisParam ) ) {
     1216                // if calling another constructor on thisParam, assume that function handles
     1217                // all members - if it doesn't a warning will appear in that function.
     1218                unhandled.clear();
     1219        } else if ( auto memberExpr = isThisMemberExpr( firstParam, thisParam ) ) {
     1220                // if first parameter is a member expression on the this parameter,
     1221                // then remove the member from unhandled set.
     1222                if ( isThisExpression( memberExpr->aggregate, thisParam ) ) {
     1223                        unhandled.erase( memberExpr->member );
    12721224                }
    12731225        }
     
    13151267        ast::ptr<ast::ApplicationExpr> callExpr = ctorExpr->callExpr.strict_as<ast::ApplicationExpr>();
    13161268        ast::ptr<ast::TypeSubstitution> env = ctorExpr->env;
    1317         // ctorExpr->set_callExpr( nullptr );
    1318         // ctorExpr->set_env( nullptr );
    13191269
    13201270        // xxx - ideally we would reuse the temporary generated from the copy constructor passes from within firstArg if it exists and not generate a temporary if it's unnecessary.
  • src/Parser/ExpressionNode.cc

    rbad9c8f r221c542e  
    673673                ast::LogicalFlag flag ) {
    674674        return new ast::LogicalExpr( location,
    675                 notZeroExpr( maybeMoveBuild( expr_node1 ) ),
    676                 notZeroExpr( maybeMoveBuild( expr_node2 ) ),
     675                maybeMoveBuild( expr_node1 ),
     676                maybeMoveBuild( expr_node2 ),
    677677                flag
    678678        );
     
    713713                ExpressionNode * expr_node3 ) {
    714714        return new ast::ConditionalExpr( location,
    715                 notZeroExpr( maybeMoveBuild( expr_node1 ) ),
     715                maybeMoveBuild( expr_node1 ),
    716716                maybeMoveBuild( expr_node2 ),
    717717                maybeMoveBuild( expr_node3 )
  • src/Parser/parserutility.cc

    rbad9c8f r221c542e  
    2727//    if ( (int)(x != 0) ) ...
    2828
    29 ast::Expr * notZeroExpr( ast::Expr * orig ) {
     29ast::Expr * notZeroExpr( const ast::Expr * orig ) {
    3030        return ( !orig ) ? nullptr : new ast::CastExpr( orig->location,
    3131                ast::UntypedExpr::createCall( orig->location,
  • src/Parser/parserutility.h

    rbad9c8f r221c542e  
    2121}
    2222
    23 ast::Expr * notZeroExpr( ast::Expr *orig );
     23ast::Expr * notZeroExpr( const ast::Expr *orig );
    2424
    2525template< typename T >
  • src/ResolvExpr/CandidateFinder.cpp

    rbad9c8f r221c542e  
    4646#include "AST/Type.hpp"
    4747#include "Common/utility.h"       // for move, copy
     48#include "Parser/parserutility.h" // for notZeroExpr
    4849#include "SymTab/Mangler.h"
    4950#include "Tuples/Tuples.h"        // for handleTupleAssignment
     
    15021503        void Finder::postvisit( const ast::LogicalExpr * logicalExpr ) {
    15031504                CandidateFinder finder1( context, tenv );
    1504                 finder1.find( logicalExpr->arg1, ResolveMode::withAdjustment() );
     1505                ast::ptr<ast::Expr> arg1 = notZeroExpr( logicalExpr->arg1 );
     1506                finder1.find( arg1, ResolveMode::withAdjustment() );
    15051507                if ( finder1.candidates.empty() ) return;
    15061508
    15071509                CandidateFinder finder2( context, tenv );
    1508                 finder2.find( logicalExpr->arg2, ResolveMode::withAdjustment() );
     1510                ast::ptr<ast::Expr> arg2 = notZeroExpr( logicalExpr->arg2 );
     1511                finder2.find( arg2, ResolveMode::withAdjustment() );
    15091512                if ( finder2.candidates.empty() ) return;
    15101513
     
    15321535                // candidates for condition
    15331536                CandidateFinder finder1( context, tenv );
    1534                 finder1.find( conditionalExpr->arg1, ResolveMode::withAdjustment() );
     1537                ast::ptr<ast::Expr> arg1 = notZeroExpr( conditionalExpr->arg1 );
     1538                finder1.find( arg1, ResolveMode::withAdjustment() );
    15351539                if ( finder1.candidates.empty() ) return;
    15361540
  • src/Validate/Autogen.cpp

    rbad9c8f r221c542e  
    803803}
    804804
    805 struct PseudoFuncGenerateRoutine final :
    806                 public ast::WithDeclsToAdd<>,
    807                 public ast::WithShortCircuiting {
    808         void previsit( const ast::EnumDecl * enumDecl );
    809 };
    810 
    811 void PseudoFuncGenerateRoutine::previsit( const ast::EnumDecl * enumDecl ) {
    812         const CodeLocation& location = enumDecl->location;
    813         if ( enumDecl->members.size() == 0 || !enumDecl->base || enumDecl->name == "" ) return;
    814 
    815         std::vector<ast::ptr<ast::Init>> inits;
    816         std::vector<ast::ptr<ast::Init>> labels;
    817         for ( const ast::Decl * mem: enumDecl->members ) {
    818                 auto memAsObjectDecl = dynamic_cast< const ast::ObjectDecl * >( mem );
    819                 inits.emplace_back( memAsObjectDecl->init );
    820                 labels.emplace_back( new ast::SingleInit(
    821                         location, ast::ConstantExpr::from_string(location, mem->name) ) );
    822         }
    823         auto init = new ast::ListInit( location, std::move( inits ) );
    824         auto label_strings = new ast::ListInit( location, std::move(labels) );
    825         auto values = new ast::ObjectDecl(
    826                 location,
    827                 "__enum_values_"+enumDecl->name,
    828                 new ast::ArrayType(
    829                         // new ast::PointerType( new ast::BasicType{ ast::BasicType::Char} ),
    830                         enumDecl->base,
    831                         ast::ConstantExpr::from_int( location, enumDecl->members.size() ),
    832                         ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim
    833                 )
    834                 ,init
    835                 ,
    836                 ast::Storage::Static,
    837                 ast::Linkage::AutoGen
    838         );
    839         auto label_arr = new ast::ObjectDecl(
    840                 location,
    841                 "__enum_labels_"+enumDecl->name,
    842                 new ast::ArrayType(
    843                         new ast::PointerType( new ast::BasicType{ ast::BasicType::Char} ),
    844                         ast::ConstantExpr::from_int( location, enumDecl->members.size() ),
    845                         ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim
    846                 ),
    847                 label_strings,
    848                 ast::Storage::Static,
    849                 ast::Linkage::AutoGen
    850         );
    851 
    852         declsToAddAfter.push_back( values );
    853         declsToAddAfter.push_back( label_arr );
    854 }
    855 
    856805} // namespace
    857806
    858807void autogenerateRoutines( ast::TranslationUnit & translationUnit ) {
    859808        ast::Pass<AutogenerateRoutines>::run( translationUnit );
    860         // ast::Pass<PseudoFuncGenerateRoutine>::run( translationUnit );
    861809}
    862810
  • src/Validate/ReplacePseudoFunc.cpp

    rbad9c8f r221c542e  
     1#include "ReplacePseudoFunc.hpp"
     2
     3#include <set>
     4
    15#include "AST/Decl.hpp"
     6#include "AST/Inspect.hpp"
    27#include "AST/Pass.hpp"
    38#include "AST/Stmt.hpp"
    4 #include "AST/Inspect.hpp"
    59#include "Common/utility.h"
    6 #include "ReplacePseudoFunc.hpp"
    7 
     10#include "ResolvExpr/Resolver.h"
     11#include "SymTab/Mangler.h"  // for Mangler
    812namespace Validate {
    913
    1014namespace {
    1115
    12 struct ReplacePseudoFuncCore {
    13     ast::Expr const * postvisit( ast::ApplicationExpr const * decl );
     16std::set<std::string> queryLabels;
     17std::set<std::string> queryValues;
     18
     19struct FindGenEnumArray final : public ast::WithShortCircuiting {
     20    void previsit(const ast::ApplicationExpr* enumDecl);
    1421};
     22
     23void FindGenEnumArray::previsit(const ast::ApplicationExpr* expr) {
     24    auto fname = ast::getFunctionName(expr);
     25    if (fname == "labelE" || fname == "valueE") {
     26        if (expr->args.size() != 1) {
     27            SemanticError(expr, "Position Expression only take one parameter");
     28        }
     29        const ast::VariableExpr* arg =
     30            expr->args.front().as<const ast::VariableExpr>();
     31        if (!arg) {
     32            SemanticError(expr, "Unimplement Pseudo Function Cases");
     33        }
     34        const ast::ObjectDecl* argAsVar = arg->var.as<const ast::ObjectDecl>();
     35        const std::string referredName = argAsVar->name;
     36        const ast::EnumInstType* argType =
     37            argAsVar->type.as<const ast::EnumInstType>();
     38        if (!argType) {
     39            SemanticError(
     40                argAsVar,
     41                "Position can only be used on an enumeration instance");
     42        }
     43        ast::ptr<ast::EnumDecl> base = argType->base;
     44        assert(base);
     45        if (fname == "labelE") queryLabels.insert(base->name);
     46        if (fname == "valueE") queryValues.insert(base->name);
     47    }
    1548}
    1649
    17 ast::Expr const * ReplacePseudoFuncCore::postvisit( ast::ApplicationExpr const * expr) {
    18     auto fname = ast::getFunctionName( expr );
    19     if ( fname == "posE" ) {
    20         // std::cerr << "Found App in ReplacePseudoFunc" << std::endl;
    21         if ( expr->args.size() != 1 ) {
    22             SemanticError( expr, "Position Expression only take one parameter" );
     50struct PseudoFuncGenerateRoutine final : public ast::WithDeclsToAdd<>,
     51                                         public ast::WithSymbolTable,
     52                                         public ast::WithShortCircuiting {
     53    void previsit(const ast::EnumDecl* enumDecl);
     54};
     55
     56void PseudoFuncGenerateRoutine::previsit(const ast::EnumDecl* enumDecl) {
     57    visit_children = false;
     58    const CodeLocation& location = enumDecl->location;
     59    if (enumDecl->members.size() == 0 || !enumDecl->base) return;
     60
     61    std::vector<ast::ptr<ast::Init>> inits;
     62    std::vector<ast::ptr<ast::Init>> labels;
     63    for (const ast::Decl* mem : enumDecl->members) {
     64        auto memAsObjectDecl = dynamic_cast<const ast::ObjectDecl*>(mem);
     65        inits.emplace_back(memAsObjectDecl->init);
     66        labels.emplace_back(new ast::SingleInit(
     67            location, ast::ConstantExpr::from_string(location, mem->name)));
     68    }
     69    if (queryValues.count(enumDecl->name)) {
     70        auto init = new ast::ListInit(location, std::move(inits));
     71        auto values = new ast::ObjectDecl(
     72            location, "values_" + enumDecl->name,
     73            new ast::ArrayType(
     74                enumDecl->base,
     75                ast::ConstantExpr::from_int(location, enumDecl->members.size()),
     76                ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim),
     77            init, ast::Storage::Static, ast::Linkage::AutoGen);
     78        symtab.addId(values);
     79        values->mangleName = Mangle::mangle(values);
     80        declsToAddAfter.push_back(values);
     81    }
     82    if (queryLabels.count(enumDecl->name)) {
     83        auto label_strings = new ast::ListInit(location, std::move(labels));
     84        auto label_arr = new ast::ObjectDecl(
     85            location, "labels_" + enumDecl->name,
     86            new ast::ArrayType(
     87                new ast::PointerType(new ast::BasicType{ast::BasicType::Char}),
     88                ast::ConstantExpr::from_int(location, enumDecl->members.size()),
     89                ast::LengthFlag::FixedLen, ast::DimensionFlag::DynamicDim),
     90            label_strings, ast::Storage::Static, ast::Linkage::AutoGen);
     91        symtab.addId(label_arr);
     92        label_arr->mangleName = Mangle::mangle(label_arr);
     93        declsToAddAfter.push_back(label_arr);
     94    }
     95}
     96
     97struct ReplacePseudoFuncCore : public ast::WithShortCircuiting,
     98                               public ast::WithSymbolTable,
     99                               public ast::WithConstTranslationUnit {
     100    ast::Expr const* postvisit(ast::ApplicationExpr const* decl);
     101};
     102
     103ast::Expr const* ReplacePseudoFuncCore::postvisit(
     104    ast::ApplicationExpr const* expr) {
     105    auto fname = ast::getFunctionName(expr);
     106    auto location = expr->location;
     107    if (fname == "posE" || fname == "valueE" || fname == "labelE") {
     108        if (expr->args.size() != 1) {
     109            SemanticError(expr,
     110                          "Pseudo Enum Expression only take one parameter");
    23111        }
    24         const ast::VariableExpr * arg = expr->args.front().as<const ast::VariableExpr>();
    25         if ( !arg ) {
    26             SemanticError( expr, "Unimplement Pseudo Function Cases" );
     112        ast::ptr<ast::VariableExpr> arg =
     113            expr->args.front().as<const ast::VariableExpr>();
     114        if (!arg) {
     115            SemanticError(expr, "Unimplement Pseudo Function Cases");
    27116        }
    28         const ast::ObjectDecl * argAsVar = arg->var.as<const ast::ObjectDecl>();
     117        const ast::ObjectDecl* argAsVar = arg->var.as<const ast::ObjectDecl>();
    29118        const std::string referredName = argAsVar->name;
    30         const ast::EnumInstType * argType = argAsVar->type.as<const ast::EnumInstType>();
    31         if ( !argType ) {
    32             SemanticError( argAsVar, "Position can only be used on an enumeration instance" );
     119        const ast::EnumInstType* argType =
     120            argAsVar->type.as<const ast::EnumInstType>();
     121        if (!argType) {
     122            SemanticError(argAsVar,
     123                          "Pseudo Enum Expression can only be used on an "
     124                          "enumeration instance");
    33125        }
    34         const ast::EnumDecl * base = argType->base;
    35         for ( size_t i = 0; i < base->members.size(); i++ ) {
    36             if ( base->members[i]->name == referredName ) {
    37                 return ast::ConstantExpr::from_int( expr->location, i );
     126        const ast::EnumDecl* base = argType->base;
     127        for (size_t i = 0; i < base->members.size(); i++) {
     128            if (base->members[i]->name == referredName) {
     129                if (fname == "posE")
     130                    return ast::ConstantExpr::from_int(expr->location, i);
     131                else if (fname == "labelE")
     132                    return ast::ConstantExpr::from_string(expr->location,
     133                                                          referredName);
     134                else
     135                    return new ast::TypeExpr(expr->location, argType);
     136            }
     137        }
     138
     139        ResolvExpr::ResolveContext context{symtab, transUnit().global};
     140
     141        if (fname == "labelE") {
     142            ast::Expr* toResolve =
     143                new ast::NameExpr(expr->location, "labels_" + base->name);
     144            auto result = ResolvExpr::findVoidExpression(toResolve, context);
     145            if (result.get()) {
     146                auto arrAsVar = result.strict_as<ast::VariableExpr>();
     147                auto untyped = new ast::UntypedExpr(
     148                    location, new ast::NameExpr(location, "?[?]"),
     149                    {new ast::VariableExpr(*arrAsVar),
     150                     ast::ConstantExpr::from_int(
     151                         location,
     152                         0)});  /// TODO: dummy value.
     153                                /// To make it works need to change the unifier
     154
     155                auto typedResult =
     156                    ResolvExpr::findVoidExpression(untyped, context);
     157                if (result.get()) {
     158                    ast::ptr<ast::ApplicationExpr> ret =
     159                        typedResult.strict_as<ast::ApplicationExpr>();
     160                    return new ast::ApplicationExpr(*ret);
     161                }
     162            }
     163        }
     164       
     165        if (fname == "valueE") {
     166            ast::Expr* toResolve =
     167                new ast::NameExpr(expr->location, "values_" + base->name);
     168            auto result = ResolvExpr::findVoidExpression(toResolve, context);
     169            if (result.get()) {
     170                auto arrAsVar = result.strict_as<ast::VariableExpr>();
     171                auto untyped = new ast::UntypedExpr(
     172                    location, new ast::NameExpr(location, "?[?]"),
     173                    {new ast::VariableExpr(*arrAsVar),
     174                     ast::ConstantExpr::from_int(
     175                         location,
     176                         0)});  /// TODO: dummy value.
     177                                /// To make it works need to change the unifier
     178
     179                auto typedResult =
     180                    ResolvExpr::findVoidExpression(untyped, context);
     181                if (result.get()) {
     182                    ast::ptr<ast::ApplicationExpr> ret =
     183                        typedResult.strict_as<ast::ApplicationExpr>();
     184                    return new ast::ApplicationExpr(*ret);
     185                }
    38186            }
    39187        }
     
    42190}
    43191
     192}  // namespace
    44193
    45 
    46 void replacePseudoFunc( ast::TranslationUnit & translationUnit ) {
    47     ast::Pass<ReplacePseudoFuncCore>::run( translationUnit );
     194void replacePseudoFunc(ast::TranslationUnit& translationUnit) {
     195    ast::Pass<FindGenEnumArray>::run(translationUnit);
     196    ast::Pass<PseudoFuncGenerateRoutine>::run(translationUnit);
     197    ast::Pass<ReplacePseudoFuncCore>::run(translationUnit);
    48198}
    49 
    50 }
     199}  // namespace Validate
  • src/main.cc

    rbad9c8f r221c542e  
    8282#include "Validate/ReturnCheck.hpp"         // for checkReturnStatements
    8383#include "Validate/VerifyCtorDtorAssign.hpp" // for verifyCtorDtorAssign
    84 #include "Validate/ReplacePseudoFunc.hpp"
     84#include "Validate/ReplacePseudoFunc.hpp"   // for replacePseudoFunc
    8585#include "Virtual/ExpandCasts.h"            // for expandCasts
    8686#include "Virtual/VirtualDtor.hpp"          // for implementVirtDtors
Note: See TracChangeset for help on using the changeset viewer.