source: src/Validate/Autogen.cpp @ d96f7c4

Last change on this file since d96f7c4 was 35cc6d4, checked in by Michael Brooks <mlbrooks@…>, 6 weeks ago

Mitigate several unused-declaration warnings in generated code.

See tests/nowarn/unused for the specific cases.

  • Property mode set to 100644
File size: 27.5 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Autogen.cpp -- Generate automatic routines for data types.
8//
9// Author           : Andrew Beach
10// Created On       : Thu Dec  2 13:44:00 2021
11// Last Modified By : Andrew Beach
12// Last Modified On : Tue Sep 20 16:00:00 2022
13// Update Count     : 2
14//
15
16#include "Autogen.hpp"
17
18#include <algorithm>                   // for count_if
19#include <cassert>                     // for strict_dynamic_cast, assert, a...
20#include <iterator>                    // for back_insert_iterator, back_ins...
21#include <list>                        // for list, _List_iterator, list<>::...
22#include <set>                         // for set, _Rb_tree_const_iterator
23#include <utility>                     // for pair
24#include <vector>                      // for vector
25
26#include "AST/Attribute.hpp"
27#include "AST/Copy.hpp"
28#include "AST/Create.hpp"
29#include "AST/Decl.hpp"
30#include "AST/DeclReplacer.hpp"
31#include "AST/Expr.hpp"
32#include "AST/Inspect.hpp"
33#include "AST/Pass.hpp"
34#include "AST/Stmt.hpp"
35#include "AST/SymbolTable.hpp"
36#include "CodeGen/OperatorTable.hpp"   // for isCtorDtor, isCtorDtorAssign
37#include "Common/ScopedMap.hpp"        // for ScopedMap<>::const_iterator, S...
38#include "Common/Utility.hpp"          // for cloneAll, operator+
39#include "GenPoly/ScopedSet.hpp"       // for ScopedSet, ScopedSet<>::iterator
40#include "InitTweak/GenInit.hpp"       // for fixReturnStatements
41#include "InitTweak/InitTweak.hpp"     // for isAssignment, isCopyConstructor
42#include "SymTab/GenImplicitCall.hpp"  // for genImplicitCall
43#include "SymTab/Mangler.hpp"          // for Mangler
44#include "CompilationState.hpp"
45
46namespace Validate {
47
48namespace {
49
50// --------------------------------------------------------------------------
51struct AutogenerateRoutines final :
52                public ast::WithDeclsToAdd,
53                public ast::WithShortCircuiting {
54        void previsit( const ast::EnumDecl * enumDecl );
55        void previsit( const ast::StructDecl * structDecl );
56        void previsit( const ast::UnionDecl * structDecl );
57        void previsit( const ast::TypeDecl * typeDecl );
58        void previsit( const ast::TraitDecl * traitDecl );
59        void previsit( const ast::FunctionDecl * functionDecl );
60        void postvisit( const ast::FunctionDecl * functionDecl );
61
62private:
63        // Current level of nested functions.
64        unsigned int functionNesting = 0;
65};
66
67// --------------------------------------------------------------------------
68/// Class used to generate functions for a particular declaration.
69/// Note it isn't really stored, it is just a class for organization and to
70/// help pass around some of the common arguments.
71class FuncGenerator {
72public:
73        std::list<ast::ptr<ast::Decl>> definitions;
74
75        FuncGenerator( const ast::Type * type, unsigned int functionNesting ) :
76                type( type ), functionNesting( functionNesting )
77        {}
78
79        /// Generate functions (and forward decls.) and append them to the list.
80        void generateAndAppendFunctions( std::list<ast::ptr<ast::Decl>> & );
81
82        virtual bool shouldAutogen() const = 0;
83protected:
84        const ast::Type * type;
85        unsigned int functionNesting;
86        ast::Linkage::Spec proto_linkage = ast::Linkage::AutoGen;
87
88        // Internal helpers:
89        virtual void genStandardFuncs();
90        void produceDecl( const ast::FunctionDecl * decl );
91
92        const CodeLocation& getLocation() const { return getDecl()->location; }
93        ast::FunctionDecl * genProto( std::string&& name,
94                std::vector<ast::ptr<ast::DeclWithType>>&& params,
95                std::vector<ast::ptr<ast::DeclWithType>>&& returns ) const;
96
97        ast::ObjectDecl * dstParam() const;
98        ast::ObjectDecl * srcParam() const;
99        ast::FunctionDecl * genCtorProto() const;
100        ast::FunctionDecl * genCopyProto() const;
101        ast::FunctionDecl * genDtorProto() const;
102        ast::FunctionDecl * genAssignProto() const;
103        ast::FunctionDecl * genFieldCtorProto( unsigned int fields ) const;
104
105        // Internal Hooks:
106        virtual void genFuncBody( ast::FunctionDecl * decl ) = 0;
107        virtual void genFieldCtors() = 0;
108        virtual bool isConcurrentType() const { return false; }
109        virtual const ast::Decl * getDecl() const = 0;
110};
111
112class StructFuncGenerator final : public FuncGenerator {
113        const ast::StructDecl * decl;
114public:
115        StructFuncGenerator( const ast::StructDecl * decl,
116                        const ast::StructInstType * type,
117                        unsigned int functionNesting ) :
118                FuncGenerator( type, functionNesting ), decl( decl )
119        {}
120
121        // Built-ins do not use autogeneration.
122        bool shouldAutogen() const final { return !decl->linkage.is_builtin && !structHasFlexibleArray(decl); }
123private:
124        void genFuncBody( ast::FunctionDecl * decl ) final;
125        void genFieldCtors() final;
126        bool isConcurrentType() const final {
127                return decl->is_thread() || decl->is_monitor();
128        }
129        virtual const ast::Decl * getDecl() const final { return decl; }
130
131        /// Generates a single struct member operation.
132        /// (constructor call, destructor call, assignment call)
133        const ast::Stmt * makeMemberOp(
134                const CodeLocation& location,
135                const ast::ObjectDecl * dstParam, const ast::Expr * src,
136                const ast::ObjectDecl * field, ast::FunctionDecl * func,
137                SymTab::LoopDirection direction );
138
139        /// Generates the body of a struct function by iterating the struct members
140        /// (via parameters). Generates default constructor, copy constructor,
141        /// copy assignment, and destructor bodies. No field constructor bodies.
142        template<typename Iterator>
143        void makeFunctionBody( Iterator member, Iterator end,
144                        ast::FunctionDecl * func, SymTab::LoopDirection direction );
145
146        /// Generate the body of a constructor which takes parameters that match
147        /// fields. (With arguments for one to all of the fields.)
148        template<typename Iterator>
149        void makeFieldCtorBody( Iterator member, Iterator end,
150                        ast::FunctionDecl * func );
151};
152
153class UnionFuncGenerator final : public FuncGenerator {
154        const ast::UnionDecl * decl;
155public:
156        UnionFuncGenerator( const ast::UnionDecl * decl,
157                        const ast::UnionInstType * type,
158                        unsigned int functionNesting ) :
159                FuncGenerator( type, functionNesting ), decl( decl )
160        {}
161
162        // Built-ins do not use autogeneration.
163        bool shouldAutogen() const final { return !decl->linkage.is_builtin; }
164private:
165        void genFuncBody( ast::FunctionDecl * decl ) final;
166        void genFieldCtors() final;
167        const ast::Decl * getDecl() const final { return decl; }
168
169        /// Generate a single union assignment expression (using memcpy).
170        ast::ExprStmt * makeAssignOp( const CodeLocation& location,
171                const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam );
172};
173
174class EnumFuncGenerator final : public FuncGenerator {
175        const ast::EnumDecl * decl;
176public:
177        EnumFuncGenerator( const ast::EnumDecl * decl,
178                        const ast::EnumInstType * type,
179                        unsigned int functionNesting ) :
180                FuncGenerator( type, functionNesting ), decl( decl )
181        {
182                // TODO: These functions are somewhere between instrinsic and autogen,
183                // could possibly use a new linkage type. For now we just make the
184                // basic ones intrinsic to code-gen them as C assignments.
185                // const auto & real_type = decl->base;
186                // const auto & basic = real_type.as<ast::BasicType>();
187
188                // if(!real_type || (basic && basic->isInteger())) proto_linkage = ast::Linkage::Intrinsic;
189
190                // Turns other enumeration type into Intrinstic as well to temporarily fix the recursive
191                // construction bug
192                proto_linkage = ast::Linkage::Intrinsic;
193        }
194
195        bool shouldAutogen() const final { return true; }
196private:
197        void genFuncBody( ast::FunctionDecl * decl ) final;
198        void genFieldCtors() final;
199        const ast::Decl * getDecl() const final { return decl; }
200protected:
201        void genStandardFuncs() override;
202};
203
204class TypeFuncGenerator final : public FuncGenerator {
205        const ast::TypeDecl * decl;
206public:
207        TypeFuncGenerator( const ast::TypeDecl * decl,
208                        ast::TypeInstType * type,
209                        unsigned int functionNesting ) :
210                FuncGenerator( type, functionNesting ), decl( decl )
211        {}
212
213        bool shouldAutogen() const final { return true; }
214        void genFieldCtors() final;
215private:
216        void genFuncBody( ast::FunctionDecl * decl ) final;
217        const ast::Decl * getDecl() const final { return decl; }
218};
219
220// --------------------------------------------------------------------------
221const std::vector<ast::ptr<ast::TypeDecl>>& getGenericParams( const ast::Type * t ) {
222        if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
223                return inst->base->params;
224        } else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
225                return inst->base->params;
226        }
227        static std::vector<ast::ptr<ast::TypeDecl>> const empty;
228        return empty;
229}
230
231/// Changes the freshly-constructed (non-const) decl so that it has the unused attribute.
232ast::ObjectDecl * addUnusedAttribute( ast::ObjectDecl * decl ) {
233        decl->attributes.push_back( new ast::Attribute( "unused" ) );
234        return decl;
235}
236
237// --------------------------------------------------------------------------
238void AutogenerateRoutines::previsit( const ast::EnumDecl * enumDecl ) {
239        if ( !enumDecl->body ) return;
240
241        ast::EnumInstType enumInst( enumDecl->name );
242        enumInst.base = enumDecl;
243
244        EnumFuncGenerator gen( enumDecl, &enumInst, functionNesting );
245        gen.generateAndAppendFunctions( declsToAddAfter );
246}
247
248void AutogenerateRoutines::previsit( const ast::StructDecl * structDecl ) {
249        visit_children = false;
250        if ( !structDecl->body ) return;
251
252        ast::StructInstType structInst( structDecl->name );
253        structInst.base = structDecl;
254        for ( const ast::TypeDecl * typeDecl : structDecl->params ) {
255                structInst.params.push_back( new ast::TypeExpr(
256                        typeDecl->location,
257                        new ast::TypeInstType( typeDecl )
258                ) );
259        }
260        StructFuncGenerator gen( structDecl, &structInst, functionNesting );
261        gen.generateAndAppendFunctions( declsToAddAfter );
262}
263
264void AutogenerateRoutines::previsit( const ast::UnionDecl * unionDecl ) {
265        visit_children = false;
266        if ( !unionDecl->body ) return;
267
268        ast::UnionInstType unionInst( unionDecl->name );
269        unionInst.base = unionDecl;
270        for ( const ast::TypeDecl * typeDecl : unionDecl->params ) {
271                unionInst.params.push_back( new ast::TypeExpr(
272                        unionDecl->location,
273                        new ast::TypeInstType( typeDecl )
274                ) );
275        }
276        UnionFuncGenerator gen( unionDecl, &unionInst, functionNesting );
277        gen.generateAndAppendFunctions( declsToAddAfter );
278}
279
280/// Generate ctor/dtors/assign for typedecls, e.g., otype T = int *;
281void AutogenerateRoutines::previsit( const ast::TypeDecl * typeDecl ) {
282        if ( !typeDecl->base ) return;
283
284        ast::TypeInstType refType( typeDecl->name, typeDecl );
285        TypeFuncGenerator gen( typeDecl, &refType, functionNesting );
286        gen.generateAndAppendFunctions( declsToAddAfter );
287}
288
289void AutogenerateRoutines::previsit( const ast::TraitDecl * ) {
290        // Ensure that we don't add assignment ops for types defined as part of the trait
291        visit_children = false;
292}
293
294void AutogenerateRoutines::previsit( const ast::FunctionDecl * ) {
295        // Track whether we're currently in a function.
296        // Can ignore function type idiosyncrasies, because function type can never
297        // declare a new type.
298        functionNesting += 1;
299}
300
301void AutogenerateRoutines::postvisit( const ast::FunctionDecl * ) {
302        functionNesting -= 1;
303}
304
305void FuncGenerator::generateAndAppendFunctions(
306                std::list<ast::ptr<ast::Decl>> & decls ) {
307        if ( !shouldAutogen() ) return;
308
309        // Generate the functions (they go into forwards and definitions).
310        genStandardFuncs();
311        genFieldCtors();
312
313        // Now export the lists contents.
314//      decls.splice( decls.end(), forwards );    // mlb wip: delete me
315        decls.splice( decls.end(), definitions );
316}
317
318void FuncGenerator::produceDecl( const ast::FunctionDecl * decl ) {
319        assert( nullptr != decl->stmts );
320        assert( decl->type_params.size() == getGenericParams( type ).size() );
321
322        definitions.push_back( decl );
323}
324
325void replaceAll( std::vector<ast::ptr<ast::DeclWithType>> & dwts,
326                const ast::DeclReplacer::TypeMap & map ) {
327        for ( auto & dwt : dwts ) {
328                dwt = strict_dynamic_cast<const ast::DeclWithType *>(
329                                ast::DeclReplacer::replace( dwt, map ) );
330        }
331}
332
333/// Generates a basic prototype function declaration.
334ast::FunctionDecl * FuncGenerator::genProto( std::string&& name,
335                std::vector<ast::ptr<ast::DeclWithType>>&& params,
336                std::vector<ast::ptr<ast::DeclWithType>>&& returns ) const {
337
338        // Handle generic prameters and assertions, if any.
339        auto const & old_type_params = getGenericParams( type );
340        ast::DeclReplacer::TypeMap oldToNew;
341        std::vector<ast::ptr<ast::TypeDecl>> type_params;
342        std::vector<ast::ptr<ast::DeclWithType>> assertions;
343        for ( auto & old_param : old_type_params ) {
344                ast::TypeDecl * decl = ast::deepCopy( old_param );
345                decl->init = nullptr;
346                splice( assertions, decl->assertions );
347                oldToNew.emplace( old_param, decl );
348                type_params.push_back( decl );
349        }
350        replaceAll( params, oldToNew );
351        replaceAll( returns, oldToNew );
352        replaceAll( assertions, oldToNew );
353
354        ast::FunctionDecl * decl = new ast::FunctionDecl(
355                // Auto-generated routines use the type declaration's location.
356                getLocation(),
357                std::move( name ),
358                std::move( type_params ),
359                std::move( assertions ),
360                std::move( params ),
361                std::move( returns ),
362                // Only a prototype, no body.
363                nullptr,
364                // Use static storage if we are at the top level.
365                (0 < functionNesting) ? ast::Storage::Classes() : ast::Storage::Static,
366                proto_linkage,
367                std::vector<ast::ptr<ast::Attribute>>(),
368                // Auto-generated routines are inline to avoid conflicts.
369                ast::Function::Specs( ast::Function::Inline ) );
370        decl->fixUniqueId();
371        return decl;
372}
373
374ast::ObjectDecl * FuncGenerator::dstParam() const {
375        return addUnusedAttribute(
376                new ast::ObjectDecl( getLocation(), "_dst",
377                        new ast::ReferenceType( ast::deepCopy( type ) ) ) );
378}
379
380ast::ObjectDecl * FuncGenerator::srcParam() const {
381        return addUnusedAttribute(
382                new ast::ObjectDecl( getLocation(), "_src",
383                        ast::deepCopy( type ) ) );
384}
385
386/// Use the current type T to create `void ?{}(T & _dst)`.
387ast::FunctionDecl * FuncGenerator::genCtorProto() const {
388        return genProto( "?{}", { dstParam() }, {} );
389}
390
391/// Use the current type T to create `void ?{}(T & _dst, T _src)`.
392ast::FunctionDecl * FuncGenerator::genCopyProto() const {
393        return genProto( "?{}", { dstParam(), srcParam() }, {} );
394}
395
396/// Use the current type T to create `void ^?{}(T & _dst)`.
397ast::FunctionDecl * FuncGenerator::genDtorProto() const {
398        // The destructor must be mutex on a concurrent type.
399        auto dst = dstParam();
400        if ( isConcurrentType() ) {
401                add_qualifiers( dst->type, ast::CV::Qualifiers( ast::CV::Mutex ) );
402        }
403        return genProto( "^?{}", { dst }, {} );
404}
405
406/// Use the current type T to create `T ?=?(T & _dst, T _src)`.
407ast::FunctionDecl * FuncGenerator::genAssignProto() const {
408        // Only the name is different, so just reuse the generation function.
409        auto retval = srcParam();
410        retval->name = "_ret";
411        return genProto( "?=?", { dstParam(), srcParam() }, { retval } );
412}
413
414// This one can return null if the last field is an unnamed bitfield.
415ast::FunctionDecl * FuncGenerator::genFieldCtorProto(
416                unsigned int fields ) const {
417        assert( 0 < fields );
418        auto aggr = strict_dynamic_cast<const ast::AggregateDecl *>( getDecl() );
419
420        std::vector<ast::ptr<ast::DeclWithType>> params = { dstParam() };
421        for ( unsigned int index = 0 ; index < fields ; ++index ) {
422                auto member = aggr->members[index].strict_as<ast::DeclWithType>();
423                if ( ast::isUnnamedBitfield(
424                                dynamic_cast<const ast::ObjectDecl *>( member ) ) ) {
425                        if ( index == fields - 1 ) {
426                                return nullptr;
427                        }
428                        continue;
429                }
430
431                auto * paramType = ast::deepCopy( member->get_type() );
432                erase_if( paramType->attributes, []( ast::Attribute const * attr ){
433                        return !attr->isValidOnFuncParam();
434                } );
435                ast::ObjectDecl * param = new ast::ObjectDecl(
436                        getLocation(), member->name, paramType );
437                for ( auto & attr : member->attributes ) {
438                        if ( attr->isValidOnFuncParam() ) {
439                                param->attributes.push_back( attr );
440                        }
441                }
442                params.emplace_back( param );
443        }
444        return genProto( "?{}", std::move( params ), {} );
445}
446
447void appendReturnThis( ast::FunctionDecl * decl ) {
448        assert( 1 <= decl->params.size() );
449        assert( 1 == decl->returns.size() );
450        assert( decl->stmts );
451
452        const CodeLocation& location = (decl->stmts->kids.empty())
453                ? decl->stmts->location : decl->stmts->kids.back()->location;
454        const ast::DeclWithType * thisParam = decl->params.front();
455        decl->stmts.get_and_mutate()->push_back(
456                new ast::ReturnStmt( location,
457                        new ast::VariableExpr( location, thisParam )
458                )
459        );
460}
461
462void FuncGenerator::genStandardFuncs() {
463        // The order here determines the order that these functions are generated.
464        // Assignment should come last since it uses copy constructor in return.
465        ast::FunctionDecl *(FuncGenerator::*standardProtos[4])() const = {
466                        &FuncGenerator::genCtorProto, &FuncGenerator::genCopyProto,
467                        &FuncGenerator::genDtorProto, &FuncGenerator::genAssignProto };
468        for ( auto & generator : standardProtos ) {
469                ast::FunctionDecl * decl = (this->*generator)();
470                genFuncBody( decl );
471                if ( CodeGen::isAssignment( decl->name ) ) {
472                        appendReturnThis( decl );
473                }
474                produceDecl( decl );
475        }
476}
477
478void StructFuncGenerator::genFieldCtors() {
479        // The field constructors are only generated if the default constructor
480        // and copy constructor are both generated, since the need both.
481        unsigned numCtors = std::count_if( definitions.begin(), definitions.end(),
482                [](const ast::Decl * decl){ return CodeGen::isConstructor( decl->name ); }
483        );
484        if ( 2 != numCtors ) return;
485
486        for ( unsigned int num = 1 ; num <= decl->members.size() ; ++num ) {
487                ast::FunctionDecl * ctor = genFieldCtorProto( num );
488                if ( nullptr == ctor ) {
489                        continue;
490                }
491                makeFieldCtorBody( decl->members.begin(), decl->members.end(), ctor );
492                produceDecl( ctor );
493        }
494}
495
496void StructFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
497        // Generate appropriate calls to member constructors and assignment.
498        // Destructor needs to do everything in reverse,
499        // so pass "forward" based on whether the function is a destructor
500        if ( CodeGen::isDestructor( functionDecl->name ) ) {
501                makeFunctionBody( decl->members.rbegin(), decl->members.rend(),
502                        functionDecl, SymTab::LoopBackward );
503        } else {
504                makeFunctionBody( decl->members.begin(), decl->members.end(),
505                        functionDecl, SymTab::LoopForward );
506        }
507}
508
509const ast::Stmt * StructFuncGenerator::makeMemberOp(
510                const CodeLocation& location, const ast::ObjectDecl * dstParam,
511                const ast::Expr * src, const ast::ObjectDecl * field,
512                ast::FunctionDecl * func, SymTab::LoopDirection direction ) {
513        InitTweak::InitExpander srcParam( src );
514        // Assign to destination.
515        ast::MemberExpr * dstSelect = new ast::MemberExpr(
516                location,
517                field,
518                new ast::CastExpr(
519                        location,
520                        new ast::VariableExpr( location, dstParam ),
521                        dstParam->type.strict_as<ast::ReferenceType>()->base
522                )
523        );
524        const ast::Stmt * stmt = genImplicitCall(
525                srcParam, dstSelect, location, func->name,
526                field, direction
527        );
528        // This could return the above directly, except the generated code is
529        // built using the structure's members and that means all the scoped
530        // names (the forall parameters) are incorrect. This corrects them.
531        if ( stmt && !decl->params.empty() ) {
532                ast::DeclReplacer::TypeMap oldToNew;
533                for ( auto pair : group_iterate( decl->params, func->type_params ) ) {
534                        oldToNew.emplace( std::get<0>(pair), std::get<1>(pair) );
535                }
536                auto node = ast::DeclReplacer::replace( stmt, oldToNew );
537                stmt = strict_dynamic_cast<const ast::Stmt *>( node );
538        }
539        return stmt;
540}
541
542template<typename Iterator>
543void StructFuncGenerator::makeFunctionBody( Iterator current, Iterator end,
544                ast::FunctionDecl * func, SymTab::LoopDirection direction ) {
545        // Trying to get the best code location. Should probably use a helper or
546        // just figure out what that would be given where this is called.
547        assert( nullptr == func->stmts );
548        const CodeLocation& location = func->location;
549
550        ast::CompoundStmt * stmts = new ast::CompoundStmt( location );
551
552        for ( ; current != end ; ++current ) {
553                const ast::ptr<ast::Decl> & member = *current;
554                auto field = member.as<ast::ObjectDecl>();
555                if ( nullptr == field ) {
556                        continue;
557                }
558
559                // Don't assign to constant members (but do construct/destruct them).
560                if ( CodeGen::isAssignment( func->name ) ) {
561                        // For array types we need to strip off the array layers.
562                        const ast::Type * type = field->get_type();
563                        while ( auto at = dynamic_cast<const ast::ArrayType *>( type ) ) {
564                                type = at->base;
565                        }
566                        if ( type->is_const() ) {
567                                continue;
568                        }
569                }
570
571                assert( !func->params.empty() );
572                const ast::ObjectDecl * dstParam =
573                        func->params.front().strict_as<ast::ObjectDecl>();
574                const ast::ObjectDecl * srcParam = nullptr;
575                if ( 2 == func->params.size() ) {
576                        srcParam = func->params.back().strict_as<ast::ObjectDecl>();
577                }
578
579                ast::MemberExpr * srcSelect = (srcParam) ? new ast::MemberExpr(
580                        location, field, new ast::VariableExpr( location, srcParam )
581                ) : nullptr;
582                const ast::Stmt * stmt =
583                        makeMemberOp( location, dstParam, srcSelect, field, func, direction );
584
585                if ( nullptr != stmt ) {
586                        stmts->kids.emplace_back( stmt );
587                }
588        }
589
590        func->stmts = stmts;
591}
592
593template<typename Iterator>
594void StructFuncGenerator::makeFieldCtorBody( Iterator current, Iterator end,
595                ast::FunctionDecl * func ) {
596        const CodeLocation& location = func->location;
597        auto & params = func->params;
598        // Need at least the constructed parameter and one field parameter.
599        assert( 2 <= params.size() );
600
601        ast::CompoundStmt * stmts = new ast::CompoundStmt( location );
602
603        auto dstParam = params.front().strict_as<ast::ObjectDecl>();
604        // Skip over the 'this' parameter.
605        for ( auto param = params.begin() + 1 ; current != end ; ++current ) {
606                const ast::ptr<ast::Decl> & member = *current;
607                const ast::Stmt * stmt = nullptr;
608                auto field = member.as<ast::ObjectDecl>();
609                // Not sure why it could be null.
610                // Don't make a function for a parameter that is an unnamed bitfield.
611                if ( nullptr == field || ast::isUnnamedBitfield( field ) ) {
612                        continue;
613                // Matching Parameter: Initialize the field by copy.
614                } else if ( params.end() != param ) {
615                        const ast::Expr *srcSelect = new ast::VariableExpr(
616                                func->location, param->get() );
617                        stmt = makeMemberOp( location, dstParam, srcSelect, field, func, SymTab::LoopForward );
618                        ++param;
619                // No Matching Parameter: Initialize the field by default constructor.
620                } else {
621                        stmt = makeMemberOp( location, dstParam, nullptr, field, func, SymTab::LoopForward );
622                }
623
624                if ( nullptr != stmt ) {
625                        stmts->kids.emplace_back( stmt );
626                }
627        }
628        func->stmts = stmts;
629}
630
631void UnionFuncGenerator::genFieldCtors() {
632        // Field constructors are only generated if default and copy constructor
633        // are generated, since they need access to both
634        unsigned numCtors = std::count_if( definitions.begin(), definitions.end(),
635                []( const ast::Decl * d ){ return CodeGen::isConstructor( d->name ); }
636        );
637        if ( 2 != numCtors ) {
638                return;
639        }
640
641        // Create a constructor which takes the first member type as a
642        // parameter. For example for `union A { int x; char y; };` generate
643        // a function with signature `void ?{}(A *, int)`. This mimics C's
644        // behaviour which initializes the first member of the union.
645
646        // Still, there must be some members.
647        if ( !decl->members.empty() ) {
648                ast::FunctionDecl * ctor = genFieldCtorProto( 1 );
649                if ( nullptr == ctor ) {
650                        return;
651                }
652                auto params = ctor->params;
653                auto dstParam = params.front().strict_as<ast::ObjectDecl>();
654                auto srcParam = params.back().strict_as<ast::ObjectDecl>();
655                ctor->stmts = new ast::CompoundStmt( getLocation(),
656                        { makeAssignOp( getLocation(), dstParam, srcParam ) }
657                );
658                produceDecl( ctor );
659        }
660}
661
662void UnionFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
663        const CodeLocation& location = functionDecl->location;
664        auto & params = functionDecl->params;
665        if ( InitTweak::isCopyConstructor( functionDecl )
666                        || InitTweak::isAssignment( functionDecl ) ) {
667                assert( 2 == params.size() );
668                auto dstParam = params.front().strict_as<ast::ObjectDecl>();
669                auto srcParam = params.back().strict_as<ast::ObjectDecl>();
670                functionDecl->stmts = new ast::CompoundStmt( location,
671                        { makeAssignOp( location, dstParam, srcParam ) }
672                );
673        } else {
674                assert( 1 == params.size() );
675                // Default constructor and destructor is empty.
676                functionDecl->stmts = new ast::CompoundStmt( location );
677        }
678}
679
680ast::ExprStmt * UnionFuncGenerator::makeAssignOp( const CodeLocation& location,
681                const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam ) {
682        return new ast::ExprStmt( location, new ast::UntypedExpr(
683                location,
684                new ast::NameExpr( location, "__builtin_memcpy" ),
685                {
686                        new ast::AddressExpr( location,
687                                new ast::VariableExpr( location, dstParam ) ),
688                        new ast::AddressExpr( location,
689                                new ast::VariableExpr( location, srcParam ) ),
690                        new ast::SizeofExpr( location, srcParam->type ),
691                } ) );
692}
693
694void EnumFuncGenerator::genStandardFuncs() {
695        // do everything FuncGenerator does except not make ForwardDecls
696        ast::FunctionDecl *(FuncGenerator::*standardProtos[4])() const = {
697                &EnumFuncGenerator::genCtorProto, &EnumFuncGenerator::genCopyProto,
698                &EnumFuncGenerator::genDtorProto, &EnumFuncGenerator::genAssignProto };
699
700        for ( auto & generator : standardProtos ) {
701                ast::FunctionDecl * decl = (this->*generator)();
702                genFuncBody( decl );
703                if ( CodeGen::isAssignment( decl->name ) ) {
704                        appendReturnThis( decl );
705                }
706                produceDecl( decl );
707        }
708}
709
710void EnumFuncGenerator::genFieldCtors() {
711        // Enumerations to not have field constructors.
712}
713
714void EnumFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
715        const CodeLocation& location = functionDecl->location;
716        auto & params = functionDecl->params;
717        if ( InitTweak::isCopyConstructor( functionDecl )
718                        || InitTweak::isAssignment( functionDecl ) ) {
719                assert( 2 == params.size() );
720                auto dstParam = params.front().strict_as<ast::ObjectDecl>();
721                auto srcParam = params.back().strict_as<ast::ObjectDecl>();
722
723                /* This looks like a recursive call, but code-gen will turn it into
724                 * a C-style assignment.
725                 *
726                 * This is still before function pointer type conversion,
727                 * so this will have to do it manually.
728                 *
729                 * It will also reference the parent function declaration, creating
730                 * a cycle for references. This also means that the ref-counts are
731                 * now non-zero and the declaration will be deleted if it ever
732                 * returns to zero.
733                 */
734                auto callExpr = new ast::ApplicationExpr( location,
735                        ast::VariableExpr::functionPointer( location, functionDecl ),
736                        {
737                                new ast::VariableExpr( location, dstParam ),
738                                new ast::VariableExpr( location, srcParam )
739                        }
740                );
741
742                functionDecl->stmts = new ast::CompoundStmt( location,
743                        { new ast::ExprStmt( location, callExpr ) }
744                );
745        } else {
746                assert( 1 == params.size() );
747                // Default constructor and destructor is empty.
748                functionDecl->stmts = new ast::CompoundStmt( location );
749        }
750}
751
752void TypeFuncGenerator::genFieldCtors() {
753        // Opaque types do not have field constructors.
754}
755
756void TypeFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
757        const CodeLocation& location = functionDecl->location;
758        auto & params = functionDecl->type->params;
759        assertf( 1 == params.size() || 2 == params.size(),
760                "Incorrect number of parameters in autogenerated typedecl function: %zd",
761                params.size() );
762        auto dstParam = params.front().strict_as<ast::ObjectDecl>();
763        auto srcParam = (2 == params.size())
764                ? params.back().strict_as<ast::ObjectDecl>() : nullptr;
765        // Generate appropriate calls to member constructor and assignment.
766        ast::UntypedExpr * expr = new ast::UntypedExpr( location,
767                new ast::NameExpr( location, functionDecl->name )
768        );
769        expr->args.push_back( new ast::CastExpr( location,
770                new ast::VariableExpr( location, dstParam ),
771                new ast::ReferenceType( decl->base )
772        ) );
773        if ( srcParam ) {
774                expr->args.push_back( new ast::CastExpr( location,
775                        new ast::VariableExpr( location, srcParam ),
776                        decl->base
777                ) );
778        }
779        functionDecl->stmts = new ast::CompoundStmt( location,
780                { new ast::ExprStmt( location, expr ) }
781        );
782}
783
784} // namespace
785
786void autogenerateRoutines( ast::TranslationUnit & translationUnit ) {
787        ast::Pass<AutogenerateRoutines>::run( translationUnit );
788}
789
790} // Validate
791
792// Local Variables: //
793// tab-width: 4 //
794// mode: c++ //
795// compile-command: "make install" //
796// End: //
Note: See TracBrowser for help on using the repository browser.