source: src/Tuples/TupleAssignment.cpp @ ea10f64

Last change on this file since ea10f64 was 405dbb3, checked in by Andrew Beach <ajbeach@…>, 5 months ago

Noticing that a function could have an early exit to save a level of indentation lead to a larger refactor. Slight code improvements besides the formatting change.

  • Property mode set to 100644
File size: 13.6 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// TupleAssignment.cpp --
8//
9// Author           : Rodolfo G. Esteves
10// Created On       : Mon May 18 07:44:20 2015
11// Last Modified By : Andrew Beach
12// Last Modified On : Wed Mar 16 14:06:00 2022
13// Update Count     : 10
14//
15
16#include "Tuples.hpp"
17
18#include <algorithm>                       // for transform
19#include <cassert>                         // for assert
20#include <iterator>                        // for back_insert_iterator, back...
21#include <list>                            // for _List_const_iterator, _Lis...
22#include <memory>                          // for unique_ptr, allocator_trai...
23#include <string>                          // for string
24#include <vector>
25
26#include "AST/Decl.hpp"
27#include "AST/Init.hpp"
28#include "AST/Pass.hpp"
29#include "AST/Stmt.hpp"
30#include "AST/TypeEnvironment.hpp"
31#include "CodeGen/OperatorTable.hpp"
32#include "Common/UniqueName.hpp"           // for UniqueName
33#include "Common/Utility.hpp"              // for splice, zipWith
34#include "Explode.hpp"                     // for explode
35#include "InitTweak/GenInit.hpp"           // for genCtorInit
36#include "InitTweak/InitTweak.hpp"         // for getPointerBase, isAssignment
37#include "ResolvExpr/CandidateFinder.hpp"  // for CandidateFinder
38#include "ResolvExpr/Cost.hpp"             // for Cost
39#include "ResolvExpr/Resolver.hpp"         // for resolveCtorInit
40#include "ResolvExpr/Typeops.hpp"          // for combos
41
42#if 0
43#define PRINT(x) x
44#else
45#define PRINT(x)
46#endif
47
48namespace Tuples {
49
50namespace {
51
52/// Checks if `expr` is of tuple type.
53bool isTuple( const ast::Expr * expr ) {
54        if ( !expr ) return false;
55        assert( expr->result );
56        return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
57}
58
59/// Checks if `expr` is of tuple type or a cast to one.
60bool refToTuple( const ast::Expr * expr ) {
61        assert( expr->result );
62        // Check for function returning tuple of reference types.
63        if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
64                return refToTuple( castExpr->arg );
65        } else {
66                return isTuple( expr );
67        }
68}
69
70/// Dispatcher for tuple (multiple and mass) assignment operations.
71class TupleAssignSpotter final {
72        /// Actually finds tuple assignment operations, by subclass.
73        struct Matcher {
74                ResolvExpr::CandidateList lhs, rhs;
75                TupleAssignSpotter & spotter;
76                CodeLocation location;
77                ResolvExpr::Cost baseCost;
78                std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
79                ast::TypeEnvironment env;
80                ast::OpenVarSet open;
81                ast::AssertionSet need;
82
83                void combineState( const ResolvExpr::Candidate & cand ) {
84                        env.simpleCombine( cand.env );
85                        ast::mergeOpenVars( open, cand.open );
86                        need.insert( cand.need.begin(), cand.need.end() );
87                }
88
89                Matcher(
90                        TupleAssignSpotter & s, const CodeLocation & loc,
91                        const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
92                : lhs( l ), rhs( r ), spotter( s ), location( loc ),
93                  baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
94                  env(), open(), need() {
95                        for ( auto & cand : lhs ) combineState( *cand );
96                        for ( auto & cand : rhs ) combineState( *cand );
97                }
98                virtual ~Matcher() = default;
99
100                virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
101
102                /// Removes environments from subexpressions within statement expressions, which could
103                /// throw off later passes like those in Box which rely on PolyMutator, and adds the
104                /// bindings to the env.
105                struct EnvRemover {
106                        /// Environment to hoist ExprStmt environments to.
107                        ast::TypeEnvironment & tenv;
108
109                        EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
110
111                        const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
112                                if ( stmt->expr->env ) {
113                                        tenv.add( *stmt->expr->env );
114                                        ast::ExprStmt * mut = mutate( stmt );
115                                        mut->expr.get_and_mutate()->env = nullptr;
116                                        return mut;
117                                }
118                                return stmt;
119                        }
120                };
121
122                ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
123                        assert( expr->result && !expr->result->isVoid() );
124
125                        ast::ObjectDecl * ret = new ast::ObjectDecl(
126                                location, namer.newName(), expr->result, new ast::SingleInit( location, expr ),
127                                ast::Storage::Classes{}, ast::Linkage::Cforall );
128
129                        // If expression type is a reference, just need an initializer, otherwise construct.
130                        if ( ! expr->result.as< ast::ReferenceType >() ) {
131                                // Resolve ctor/dtor for the new object.
132                                ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
133                                                InitTweak::genCtorInit( location, ret ), spotter.crntFinder.context );
134                                // Remove environments from subexpressions of stmtExpr.
135                                ast::Pass< EnvRemover > rm( env );
136                                ret->init = ctorInit->accept( rm );
137                        }
138
139                        PRINT( std::cerr << "new object: " << ret << std::endl; )
140                        return ret;
141                }
142
143                ast::UntypedExpr * createFunc(
144                        const std::string & fname, const ast::ObjectDecl * left,
145                        const ast::ObjectDecl * right
146                ) {
147                        assert( left );
148                        std::vector< ast::ptr< ast::Expr > > args;
149                        args.emplace_back( new ast::VariableExpr( location, left ) );
150                        if ( right ) { args.emplace_back( new ast::VariableExpr( location, right ) ); }
151
152                        if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
153                                args.front() = new ast::AddressExpr( location, args.front() );
154                                if ( right ) { args.back() = new ast::AddressExpr( location, args.back() ); }
155                                return new ast::UntypedExpr(
156                                        location, new ast::NameExpr( location, "?=?" ), std::move( args ) );
157                        } else {
158                                return new ast::UntypedExpr(
159                                        location, new ast::NameExpr( location, fname ), std::move( args ) );
160                        }
161                }
162        };
163
164        /// Finds mass-assignment operations.
165        struct MassAssignMatcher final : public Matcher {
166                MassAssignMatcher(
167                        TupleAssignSpotter & s, const CodeLocation & loc,
168                        const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
169                : Matcher( s, loc, l, r ) {}
170
171                std::vector< ast::ptr< ast::Expr > > match() override {
172                        static UniqueName lhsNamer( "__massassign_L" );
173                        static UniqueName rhsNamer( "__massassign_R" );
174                        // Empty tuple case falls into this matcher.
175                        assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
176
177                        ast::ptr< ast::ObjectDecl > rtmp =
178                                1 == rhs.size() ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
179
180                        std::vector< ast::ptr< ast::Expr > > out;
181                        for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
182                                // Create a temporary object for each value in
183                                // the LHS and create a call involving the RHS.
184                                ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
185                                out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
186                                tmpDecls.emplace_back( std::move( ltmp ) );
187                        }
188                        if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
189
190                        return out;
191                }
192        };
193
194        /// Finds multiple-assignment operations.
195        struct MultipleAssignMatcher final : public Matcher {
196                MultipleAssignMatcher(
197                        TupleAssignSpotter & s, const CodeLocation & loc,
198                        const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
199                : Matcher( s, loc, l, r ) {}
200
201                std::vector< ast::ptr< ast::Expr > > match() override {
202                        static UniqueName lhsNamer( "__multassign_L" );
203                        static UniqueName rhsNamer( "__multassign_R" );
204
205                        if ( lhs.size() != rhs.size() ) return {};
206
207                        // Produce a new temporary object for each value in
208                        // the LHS and RHS and pairwise create the calls.
209                        std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
210
211                        std::vector< ast::ptr< ast::Expr > > out;
212                        for ( unsigned i = 0; i < lhs.size(); ++i ) {
213                                ResolvExpr::CandidateRef & lhsCand = lhs[i];
214                                ResolvExpr::CandidateRef & rhsCand = rhs[i];
215
216                                // Convert RHS to LHS type minus one reference --
217                                // important for case where LHS is && and RHS is lvalue.
218                                auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
219                                rhsCand->expr = new ast::CastExpr( rhsCand->expr, lhsType->base );
220                                ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
221                                ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
222                                out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
223                                ltmp.emplace_back( std::move( lobj ) );
224                                rtmp.emplace_back( std::move( robj ) );
225
226                                // Resolve the cast expression so that rhsCand return type is bound
227                                // by the cast type as needed, and transfer the resulting environment.
228                                ResolvExpr::CandidateFinder finder( spotter.crntFinder.context, env );
229                                finder.find( rhsCand->expr, ResolvExpr::ResolveMode::withAdjustment() );
230                                assert( 1 == finder.candidates.size() );
231                                env = std::move( finder.candidates.front()->env );
232                        }
233
234                        splice( tmpDecls, ltmp );
235                        splice( tmpDecls, rtmp );
236
237                        return out;
238                }
239        };
240
241        ResolvExpr::CandidateFinder & crntFinder;
242        std::string fname;
243
244public:
245        TupleAssignSpotter( ResolvExpr::CandidateFinder & f )
246        : crntFinder( f ), fname() {}
247
248        /// Find left- and right-hand-sides for mass or multiple assignment.
249        void spot(
250                const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
251        );
252        /// Wrapper around matcher.match.
253        void match( Matcher & matcher );
254};
255
256void TupleAssignSpotter::spot(
257        const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
258) {
259        // Skip non-"assignment" functions.
260        auto op = expr->func.as< ast::NameExpr >();
261        if ( nullptr == op || !CodeGen::isCtorDtorAssign( op->name ) ) return;
262
263        // Handled by CandidateFinder if applicable (both odd cases).
264        if ( args.empty() || ( 1 == args.size() && CodeGen::isAssignment( op->name ) ) ) {
265                return;
266        }
267
268        fname = op->name;
269
270        // Look over all possible left-hand-side.
271        for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
272                // Skip non-tuple LHS.
273                if ( !refToTuple( lhsCand->expr ) ) continue;
274
275                // Explode is aware of casts - ensure every LHS
276                // is sent into explode with a reference cast.
277                if ( !lhsCand->expr.as< ast::CastExpr >() ) {
278                        lhsCand->expr = new ast::CastExpr(
279                                lhsCand->expr, new ast::ReferenceType( lhsCand->expr->result ) );
280                }
281
282                // Explode the LHS so that each field of a tuple-valued expr is assigned.
283                ResolvExpr::CandidateList lhs;
284                explode( *lhsCand, crntFinder.context.symtab, back_inserter(lhs), true );
285                for ( ResolvExpr::CandidateRef & cand : lhs ) {
286                        // Each LHS value must be a reference - some come in
287                        // with a cast, if not just cast to reference here.
288                        if ( !cand->expr->result.as< ast::ReferenceType >() ) {
289                                cand->expr = new ast::CastExpr(
290                                        cand->expr, new ast::ReferenceType( cand->expr->result ) );
291                        }
292                }
293
294                if ( 1 == args.size() ) {
295                        // Mass default-initialization/destruction.
296                        ResolvExpr::CandidateList rhs{};
297                        MassAssignMatcher matcher( *this, expr->location, lhs, rhs );
298                        match( matcher );
299                } else if ( 2 == args.size() ) {
300                        for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
301                                ResolvExpr::CandidateList rhs;
302                                if ( isTuple( rhsCand->expr ) ) {
303                                        // Multiple assignment:
304                                        explode( *rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
305                                        MultipleAssignMatcher matcher( *this, expr->location, lhs, rhs );
306                                        match( matcher );
307                                } else {
308                                        // Mass assignment:
309                                        rhs.emplace_back( rhsCand );
310                                        MassAssignMatcher matcher( *this, expr->location, lhs, rhs );
311                                        match( matcher );
312                                }
313                        }
314                } else {
315                        // Expand all possible RHS possibilities.
316                        std::vector< ResolvExpr::CandidateList > rhsCands;
317                        combos(
318                                std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
319                        for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
320                                // Multiple assignment:
321                                ResolvExpr::CandidateList rhs;
322                                explode( rhsCand, crntFinder.context.symtab, back_inserter( rhs ), true );
323                                MultipleAssignMatcher matcher( *this, expr->location, lhs, rhs );
324                                match( matcher );
325                        }
326                }
327        }
328}
329
330void TupleAssignSpotter::match( TupleAssignSpotter::Matcher & matcher ) {
331        std::vector< ast::ptr< ast::Expr > > newAssigns = matcher.match();
332
333        if ( !( matcher.lhs.empty() && matcher.rhs.empty() ) ) {
334                // If both LHS and RHS are empty than this is the empty tuple
335                // case, wherein it's okay for newAssigns to be empty. Otherwise,
336                // return early so that no new candidates are generated.
337                if ( newAssigns.empty() ) return;
338        }
339
340        ResolvExpr::CandidateList crnt;
341        // Now resolve new assignments.
342        for ( const ast::Expr * expr : newAssigns ) {
343                PRINT(
344                        std::cerr << "== resolving tuple assign ==" << std::endl;
345                        std::cerr << expr << std::endl;
346                )
347
348                ResolvExpr::CandidateFinder finder( crntFinder.context, matcher.env );
349                finder.allowVoid = true;
350
351                try {
352                        finder.find( expr, ResolvExpr::ResolveMode::withAdjustment() );
353                } catch (...) {
354                        // No match is not failure, just that this tuple assignment is invalid.
355                        return;
356                }
357
358                ResolvExpr::CandidateList & cands = finder.candidates;
359                assert( 1 == cands.size() );
360                assert( cands.front()->expr );
361                crnt.emplace_back( std::move( cands.front() ) );
362        }
363
364        // extract expressions from the assignment candidates to produce a list of assignments
365        // that together form a sigle candidate
366        std::vector< ast::ptr< ast::Expr > > solved;
367        for ( ResolvExpr::CandidateRef & cand : crnt ) {
368                solved.emplace_back( cand->expr );
369                matcher.combineState( *cand );
370        }
371
372        crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
373                new ast::TupleAssignExpr(
374                        matcher.location, std::move( solved ), std::move( matcher.tmpDecls ) ),
375                std::move( matcher.env ), std::move( matcher.open ), std::move( matcher.need ),
376                ResolvExpr::sumCost( crnt ) + matcher.baseCost ) );
377}
378
379} // anonymous namespace
380
381void handleTupleAssignment(
382        ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
383        std::vector< ResolvExpr::CandidateFinder > & args
384) {
385        TupleAssignSpotter spotter( finder );
386        spotter.spot( assign, args );
387}
388
389} // namespace Tuples
390
391// Local Variables: //
392// tab-width: 4 //
393// mode: c++ //
394// compile-command: "make install" //
395// End: //
Note: See TracBrowser for help on using the repository browser.