Ignore:
Timestamp:
Oct 29, 2019, 4:01:24 PM (6 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
773db65, 9421f3d8
Parents:
7951100 (diff), 8364209 (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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Tuples/TupleAssignment.cc

    r7951100 rb067d9b  
    2222#include <vector>
    2323
     24#include "AST/Decl.hpp"
     25#include "AST/Init.hpp"
     26#include "AST/Pass.hpp"
     27#include "AST/Stmt.hpp"
     28#include "AST/TypeEnvironment.hpp"
    2429#include "CodeGen/OperatorTable.h"
    2530#include "Common/PassVisitor.h"
    2631#include "Common/UniqueName.h"             // for UniqueName
    27 #include "Common/utility.h"                // for zipWith
     32#include "Common/utility.h"                // for splice, zipWith
    2833#include "Explode.h"                       // for explode
    2934#include "InitTweak/GenInit.h"             // for genCtorInit
     
    5156
    5257namespace Tuples {
    53         class TupleAssignSpotter {
     58        class TupleAssignSpotter_old {
    5459          public:
    5560                // dispatcher for Tuple (multiple and mass) assignment operations
    56                 TupleAssignSpotter( ResolvExpr::AlternativeFinder & );
     61                TupleAssignSpotter_old( ResolvExpr::AlternativeFinder & );
    5762                void spot( UntypedExpr * expr, std::vector<ResolvExpr::AlternativeFinder> &args );
    5863
     
    6267                struct Matcher {
    6368                  public:
    64                         Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const
    65                                 ResolvExpr::AltList& rhs );
     69                        Matcher( TupleAssignSpotter_old &spotter, const ResolvExpr::AltList& lhs,
     70                                const ResolvExpr::AltList& rhs );
    6671                        virtual ~Matcher() {}
     72
    6773                        virtual void match( std::list< Expression * > &out ) = 0;
    6874                        ObjectDecl * newObject( UniqueName & namer, Expression * expr );
     75
     76                        void combineState( const ResolvExpr::Alternative& alt ) {
     77                                compositeEnv.simpleCombine( alt.env );
     78                                ResolvExpr::mergeOpenVars( openVars, alt.openVars );
     79                                cloneAll( alt.need, need );
     80                        }
     81
     82                        void combineState( const ResolvExpr::AltList& alts ) {
     83                                for ( const ResolvExpr::Alternative& alt : alts ) { combineState( alt ); }
     84                        }
     85
    6986                        ResolvExpr::AltList lhs, rhs;
    70                         TupleAssignSpotter &spotter;
     87                        TupleAssignSpotter_old &spotter;
    7188                        ResolvExpr::Cost baseCost;
    7289                        std::list< ObjectDecl * > tmpDecls;
    7390                        ResolvExpr::TypeEnvironment compositeEnv;
     91                        ResolvExpr::OpenVarSet openVars;
     92                        ResolvExpr::AssertionSet need;
    7493                };
    7594
    7695                struct MassAssignMatcher : public Matcher {
    7796                  public:
    78                         MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
     97                        MassAssignMatcher( TupleAssignSpotter_old &spotter, const ResolvExpr::AltList& lhs,
    7998                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    8099                        virtual void match( std::list< Expression * > &out );
     
    83102                struct MultipleAssignMatcher : public Matcher {
    84103                  public:
    85                         MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
     104                        MultipleAssignMatcher( TupleAssignSpotter_old &spotter, const ResolvExpr::AltList& lhs,
    86105                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    87106                        virtual void match( std::list< Expression * > &out );
     
    122141        void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr,
    123142                                std::vector<ResolvExpr::AlternativeFinder> &args ) {
    124                 TupleAssignSpotter spotter( currentFinder );
     143                TupleAssignSpotter_old spotter( currentFinder );
    125144                spotter.spot( expr, args );
    126145        }
    127146
    128         TupleAssignSpotter::TupleAssignSpotter( ResolvExpr::AlternativeFinder &f )
     147        TupleAssignSpotter_old::TupleAssignSpotter_old( ResolvExpr::AlternativeFinder &f )
    129148                : currentFinder(f) {}
    130149
    131         void TupleAssignSpotter::spot( UntypedExpr * expr,
     150        void TupleAssignSpotter_old::spot( UntypedExpr * expr,
    132151                        std::vector<ResolvExpr::AlternativeFinder> &args ) {
    133152                if (  NameExpr *op = dynamic_cast< NameExpr * >(expr->get_function()) ) {
     
    210229        }
    211230
    212         void TupleAssignSpotter::match() {
     231        void TupleAssignSpotter_old::match() {
    213232                assert ( matcher != 0 );
    214233
     
    245264                }
    246265
    247                 // extract expressions from the assignment alternatives to produce a list of assignments that
    248                 // together form a single alternative
     266                // extract expressions from the assignment alternatives to produce a list of assignments
     267                // that together form a single alternative
    249268                std::list< Expression *> solved_assigns;
    250269                for ( ResolvExpr::Alternative & alt : current ) {
    251270                        solved_assigns.push_back( alt.expr->clone() );
    252                 }
    253                 // combine assignment environments into combined expression environment
    254                 simpleCombineEnvironments( current.begin(), current.end(), matcher->compositeEnv );
     271                        matcher->combineState( alt );
     272                }
     273
    255274                // xxx -- was push_front
    256                 currentFinder.get_alternatives().push_back( ResolvExpr::Alternative(
    257                         new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv,
    258                         ResolvExpr::sumCost( current ) + matcher->baseCost ) );
    259         }
    260 
    261         TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter,
     275                currentFinder.get_alternatives().push_back( ResolvExpr::Alternative{
     276                        new TupleAssignExpr{ solved_assigns, matcher->tmpDecls }, matcher->compositeEnv,
     277                        matcher->openVars,
     278                        ResolvExpr::AssertionList( matcher->need.begin(), matcher->need.end() ),
     279                        ResolvExpr::sumCost( current ) + matcher->baseCost } );
     280        }
     281
     282        TupleAssignSpotter_old::Matcher::Matcher( TupleAssignSpotter_old &spotter,
    262283                const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs )
    263284        : lhs(lhs), rhs(rhs), spotter(spotter),
    264285          baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ) {
    265                 simpleCombineEnvironments( lhs.begin(), lhs.end(), compositeEnv );
    266                 simpleCombineEnvironments( rhs.begin(), rhs.end(), compositeEnv );
     286                combineState( lhs );
     287                combineState( rhs );
    267288        }
    268289
     
    297318        };
    298319
    299         ObjectDecl * TupleAssignSpotter::Matcher::newObject( UniqueName & namer, Expression * expr ) {
     320        ObjectDecl * TupleAssignSpotter_old::Matcher::newObject( UniqueName & namer, Expression * expr ) {
    300321                assert( expr->result && ! expr->get_result()->isVoid() );
    301322                ObjectDecl * ret = new ObjectDecl( namer.newName(), Type::StorageClasses(), LinkageSpec::Cforall, nullptr, expr->result->clone(), new SingleInit( expr->clone() ) );
     
    313334        }
    314335
    315         void TupleAssignSpotter::MassAssignMatcher::match( std::list< Expression * > &out ) {
     336        void TupleAssignSpotter_old::MassAssignMatcher::match( std::list< Expression * > &out ) {
    316337                static UniqueName lhsNamer( "__massassign_L" );
    317338                static UniqueName rhsNamer( "__massassign_R" );
     
    331352        }
    332353
    333         void TupleAssignSpotter::MultipleAssignMatcher::match( std::list< Expression * > &out ) {
     354        void TupleAssignSpotter_old::MultipleAssignMatcher::match( std::list< Expression * > &out ) {
    334355                static UniqueName lhsNamer( "__multassign_L" );
    335356                static UniqueName rhsNamer( "__multassign_R" );
     
    361382                }
    362383        }
     384
     385namespace {
     386        /// true if `expr` is of tuple type
     387        bool isTuple( const ast::Expr * expr ) {
     388                if ( ! expr ) return false;
     389                assert( expr->result );
     390                return dynamic_cast< const ast::TupleType * >( expr->result->stripReferences() );
     391        }
     392
     393        /// true if `expr` is of tuple type or a reference to one
     394        bool refToTuple( const ast::Expr * expr ) {
     395                assert( expr->result );
     396                // check for function returning tuple of reference types
     397                if ( auto castExpr = dynamic_cast< const ast::CastExpr * >( expr ) ) {
     398                        return refToTuple( castExpr->arg );
     399                } else {
     400                        return isTuple( expr );
     401                }
     402        }
     403
     404        /// Dispatcher for tuple (multiple and mass) assignment operations
     405        class TupleAssignSpotter_new final {
     406                /// Actually finds tuple assignment operations, by subclass
     407                struct Matcher {
     408                        ResolvExpr::CandidateList lhs, rhs;
     409                        TupleAssignSpotter_new & spotter;
     410                        CodeLocation location;
     411                        ResolvExpr::Cost baseCost;
     412                        std::vector< ast::ptr< ast::ObjectDecl > > tmpDecls;
     413                        ast::TypeEnvironment env;
     414                        ast::OpenVarSet open;
     415                        ast::AssertionSet need;
     416
     417                        void combineState( const ResolvExpr::Candidate & cand ) {
     418                                env.simpleCombine( cand.env );
     419                                ast::mergeOpenVars( open, cand.open );
     420                                need.insert( cand.need.begin(), cand.need.end() );
     421                        }
     422
     423                        Matcher(
     424                                TupleAssignSpotter_new & s, const CodeLocation & loc,
     425                                const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
     426                        : lhs( l ), rhs( r ), spotter( s ), location( loc ),
     427                          baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ), tmpDecls(),
     428                          env(), open(), need() {
     429                                for ( auto & cand : lhs ) combineState( *cand );
     430                                for ( auto & cand : rhs ) combineState( *cand );
     431                        }
     432                        virtual ~Matcher() = default;
     433
     434                        virtual std::vector< ast::ptr< ast::Expr > > match() = 0;
     435
     436                        /// removes environments from subexpressions within statement expressions, which could
     437                        /// throw off later passes like those in Box which rely on PolyMutator, and adds the
     438                        /// bindings to the env
     439                        struct EnvRemover {
     440                                /// environment to hoist ExprStmt environments to
     441                                ast::TypeEnvironment & tenv;
     442
     443                                EnvRemover( ast::TypeEnvironment & e ) : tenv( e ) {}
     444
     445                                const ast::ExprStmt * previsit( const ast::ExprStmt * stmt ) {
     446                                        if ( stmt->expr->env ) {
     447                                                tenv.add( *stmt->expr->env );
     448                                                ast::ExprStmt * mut = mutate( stmt );
     449                                                mut->expr.get_and_mutate()->env = nullptr;
     450                                                return mut;
     451                                        }
     452                                        return stmt;
     453                                }
     454                        };
     455
     456                        ast::ObjectDecl * newObject( UniqueName & namer, const ast::Expr * expr ) {
     457                                assert( expr->result && ! expr->result->isVoid() );
     458
     459                                ast::ObjectDecl * ret = new ast::ObjectDecl{
     460                                        location, namer.newName(), expr->result, new ast::SingleInit{ location, expr },
     461                                        ast::Storage::Classes{}, ast::Linkage::Cforall };
     462
     463                                // if expression type is a reference, just need an initializer, otherwise construct
     464                                if ( ! expr->result.as< ast::ReferenceType >() ) {
     465                                        // resolve ctor/dtor for the new object
     466                                        ast::ptr< ast::Init > ctorInit = ResolvExpr::resolveCtorInit(
     467                                                        InitTweak::genCtorInit( location, ret ), spotter.crntFinder.symtab );
     468                                        // remove environments from subexpressions of stmtExpr
     469                                        ast::Pass< EnvRemover > rm{ env };
     470                                        ret->init = ctorInit->accept( rm );
     471                                }
     472
     473                                PRINT( std::cerr << "new object: " << ret << std::endl; )
     474                                return ret;
     475                        }
     476
     477                        ast::UntypedExpr * createFunc(
     478                                const std::string & fname, const ast::ObjectDecl * left,
     479                                const ast::ObjectDecl * right
     480                        ) {
     481                                assert( left );
     482                                std::vector< ast::ptr< ast::Expr > > args;
     483                                args.emplace_back( new ast::VariableExpr{ location, left } );
     484                                if ( right ) { args.emplace_back( new ast::VariableExpr{ location, right } ); }
     485
     486                                if ( left->type->referenceDepth() > 1 && CodeGen::isConstructor( fname ) ) {
     487                                        args.front() = new ast::AddressExpr{ location, args.front() };
     488                                        if ( right ) { args.back() = new ast::AddressExpr{ location, args.back() }; }
     489                                        return new ast::UntypedExpr{
     490                                                location, new ast::NameExpr{ location, "?=?" }, std::move(args) };
     491                                } else {
     492                                        return new ast::UntypedExpr{
     493                                                location, new ast::NameExpr{ location, fname }, std::move(args) };
     494                                }
     495                        }
     496                };
     497
     498                /// Finds mass-assignment operations
     499                struct MassAssignMatcher final : public Matcher {
     500                        MassAssignMatcher(
     501                                TupleAssignSpotter_new & s, const CodeLocation & loc,
     502                                const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
     503                        : Matcher( s, loc, l, r ) {}
     504
     505                        std::vector< ast::ptr< ast::Expr > > match() override {
     506                                static UniqueName lhsNamer( "__massassign_L" );
     507                                static UniqueName rhsNamer( "__massassign_R" );
     508                                // empty tuple case falls into this matcher
     509                                assert( lhs.empty() ? rhs.empty() : rhs.size() <= 1 );
     510
     511                                ast::ptr< ast::ObjectDecl > rtmp =
     512                                        rhs.size() == 1 ? newObject( rhsNamer, rhs.front()->expr ) : nullptr;
     513
     514                                std::vector< ast::ptr< ast::Expr > > out;
     515                                for ( ResolvExpr::CandidateRef & lhsCand : lhs ) {
     516                                        // create a temporary object for each value in the LHS and create a call
     517                                        // involving the RHS
     518                                        ast::ptr< ast::ObjectDecl > ltmp = newObject( lhsNamer, lhsCand->expr );
     519                                        out.emplace_back( createFunc( spotter.fname, ltmp, rtmp ) );
     520                                        tmpDecls.emplace_back( std::move( ltmp ) );
     521                                }
     522                                if ( rtmp ) tmpDecls.emplace_back( std::move( rtmp ) );
     523
     524                                return out;
     525                        }
     526                };
     527
     528                /// Finds multiple-assignment operations
     529                struct MultipleAssignMatcher final : public Matcher {
     530                        MultipleAssignMatcher(
     531                                TupleAssignSpotter_new & s, const CodeLocation & loc,
     532                                const ResolvExpr::CandidateList & l, const ResolvExpr::CandidateList & r )
     533                        : Matcher( s, loc, l, r ) {}
     534
     535                        std::vector< ast::ptr< ast::Expr > > match() override {
     536                                static UniqueName lhsNamer( "__multassign_L" );
     537                                static UniqueName rhsNamer( "__multassign_R" );
     538
     539                                if ( lhs.size() != rhs.size() ) return {};
     540
     541                                // produce a new temporary object for each value in the LHS and RHS and pairwise
     542                                // create the calls
     543                                std::vector< ast::ptr< ast::ObjectDecl > > ltmp, rtmp;
     544
     545                                std::vector< ast::ptr< ast::Expr > > out;
     546                                for ( unsigned i = 0; i < lhs.size(); ++i ) {
     547                                        ResolvExpr::CandidateRef & lhsCand = lhs[i];
     548                                        ResolvExpr::CandidateRef & rhsCand = rhs[i];
     549
     550                                        // convert RHS to LHS type minus one reference -- important for case where LHS
     551                                        // is && and RHS is lvalue
     552                                        auto lhsType = lhsCand->expr->result.strict_as< ast::ReferenceType >();
     553                                        rhsCand->expr = new ast::CastExpr{ rhsCand->expr, lhsType->base };
     554                                        ast::ptr< ast::ObjectDecl > lobj = newObject( lhsNamer, lhsCand->expr );
     555                                        ast::ptr< ast::ObjectDecl > robj = newObject( rhsNamer, rhsCand->expr );
     556                                        out.emplace_back( createFunc( spotter.fname, lobj, robj ) );
     557                                        ltmp.emplace_back( std::move( lobj ) );
     558                                        rtmp.emplace_back( std::move( robj ) );
     559
     560                                        // resolve the cast expression so that rhsCand return type is bound by the cast
     561                                        // type as needed, and transfer the resulting environment
     562                                        ResolvExpr::CandidateFinder finder{ spotter.crntFinder.symtab, env };
     563                                        finder.find( rhsCand->expr, ResolvExpr::ResolvMode::withAdjustment() );
     564                                        assert( finder.candidates.size() == 1 );
     565                                        env = std::move( finder.candidates.front()->env );
     566                                }
     567
     568                                splice( tmpDecls, ltmp );
     569                                splice( tmpDecls, rtmp );
     570
     571                                return out;
     572                        }
     573                };
     574
     575                ResolvExpr::CandidateFinder & crntFinder;
     576                std::string fname;
     577                std::unique_ptr< Matcher > matcher;
     578
     579        public:
     580                TupleAssignSpotter_new( ResolvExpr::CandidateFinder & f )
     581                : crntFinder( f ), fname(), matcher() {}
     582
     583                // find left- and right-hand-sides for mass or multiple assignment
     584                void spot(
     585                        const ast::UntypedExpr * expr, std::vector< ResolvExpr::CandidateFinder > & args
     586                ) {
     587                        if ( auto op = expr->func.as< ast::NameExpr >() ) {
     588                                // skip non-assignment functions
     589                                if ( ! CodeGen::isCtorDtorAssign( op->name ) ) return;
     590                                fname = op->name;
     591
     592                                // handled by CandidateFinder if applicable (both odd cases)
     593                                if ( args.empty() || ( args.size() == 1 && CodeGen::isAssignment( fname ) ) ) {
     594                                        return;
     595                                }
     596
     597                                // look over all possible left-hand-side
     598                                for ( ResolvExpr::CandidateRef & lhsCand : args[0] ) {
     599                                        // skip non-tuple LHS
     600                                        if ( ! refToTuple( lhsCand->expr ) ) continue;
     601
     602                                        // explode is aware of casts - ensure every LHS is sent into explode with a
     603                                        // reference cast
     604                                        if ( ! lhsCand->expr.as< ast::CastExpr >() ) {
     605                                                lhsCand->expr = new ast::CastExpr{
     606                                                        lhsCand->expr, new ast::ReferenceType{ lhsCand->expr->result } };
     607                                        }
     608
     609                                        // explode the LHS so that each field of a tuple-valued expr is assigned
     610                                        ResolvExpr::CandidateList lhs;
     611                                        explode( *lhsCand, crntFinder.symtab, back_inserter(lhs), true );
     612                                        for ( ResolvExpr::CandidateRef & cand : lhs ) {
     613                                                // each LHS value must be a reference - some come in with a cast, if not
     614                                                // just cast to reference here
     615                                                if ( ! cand->expr->result.as< ast::ReferenceType >() ) {
     616                                                        cand->expr = new ast::CastExpr{
     617                                                                cand->expr, new ast::ReferenceType{ cand->expr->result } };
     618                                                }
     619                                        }
     620
     621                                        if ( args.size() == 1 ) {
     622                                                // mass default-initialization/destruction
     623                                                ResolvExpr::CandidateList rhs{};
     624                                                matcher.reset( new MassAssignMatcher{ *this, expr->location, lhs, rhs } );
     625                                                match();
     626                                        } else if ( args.size() == 2 ) {
     627                                                for ( const ResolvExpr::CandidateRef & rhsCand : args[1] ) {
     628                                                        ResolvExpr::CandidateList rhs;
     629                                                        if ( isTuple( rhsCand->expr ) ) {
     630                                                                // multiple assignment
     631                                                                explode( *rhsCand, crntFinder.symtab, back_inserter(rhs), true );
     632                                                                matcher.reset(
     633                                                                        new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
     634                                                        } else {
     635                                                                // mass assignment
     636                                                                rhs.emplace_back( rhsCand );
     637                                                                matcher.reset(
     638                                                                        new MassAssignMatcher{ *this, expr->location, lhs, rhs } );
     639                                                        }
     640                                                        match();
     641                                                }
     642                                        } else {
     643                                                // expand all possible RHS possibilities
     644                                                std::vector< ResolvExpr::CandidateList > rhsCands;
     645                                                combos(
     646                                                        std::next( args.begin(), 1 ), args.end(), back_inserter( rhsCands ) );
     647                                                for ( const ResolvExpr::CandidateList & rhsCand : rhsCands ) {
     648                                                        // multiple assignment
     649                                                        ResolvExpr::CandidateList rhs;
     650                                                        explode( rhsCand, crntFinder.symtab, back_inserter(rhs), true );
     651                                                        matcher.reset(
     652                                                                new MultipleAssignMatcher{ *this, expr->location, lhs, rhs } );
     653                                                        match();
     654                                                }
     655                                        }
     656                                }
     657                        }
     658                }
     659
     660                void match() {
     661                        assert( matcher );
     662
     663                        std::vector< ast::ptr< ast::Expr > > newAssigns = matcher->match();
     664
     665                        if ( ! ( matcher->lhs.empty() && matcher->rhs.empty() ) ) {
     666                                // if both LHS and RHS are empty than this is the empty tuple case, wherein it's
     667                                // okay for newAssigns to be empty. Otherwise, return early so that no new
     668                                // candidates are generated
     669                                if ( newAssigns.empty() ) return;
     670                        }
     671
     672                        ResolvExpr::CandidateList crnt;
     673                        // now resolve new assignments
     674                        for ( const ast::Expr * expr : newAssigns ) {
     675                                PRINT(
     676                                        std::cerr << "== resolving tuple assign ==" << std::endl;
     677                                        std::cerr << expr << std::endl;
     678                                )
     679
     680                                ResolvExpr::CandidateFinder finder{ crntFinder.symtab, matcher->env };
     681
     682                                try {
     683                                        finder.find( expr, ResolvExpr::ResolvMode::withAdjustment() );
     684                                } catch (...) {
     685                                        // no match is not failure, just that this tuple assignment is invalid
     686                                        return;
     687                                }
     688
     689                                ResolvExpr::CandidateList & cands = finder.candidates;
     690                                assert( cands.size() == 1 );
     691                                assert( cands.front()->expr );
     692                                crnt.emplace_back( std::move( cands.front() ) );
     693                        }
     694
     695                        // extract expressions from the assignment candidates to produce a list of assignments
     696                        // that together form a sigle candidate
     697                        std::vector< ast::ptr< ast::Expr > > solved;
     698                        for ( ResolvExpr::CandidateRef & cand : crnt ) {
     699                                solved.emplace_back( cand->expr );
     700                                matcher->combineState( *cand );
     701                        }
     702
     703                        crntFinder.candidates.emplace_back( std::make_shared< ResolvExpr::Candidate >(
     704                                new ast::TupleAssignExpr{
     705                                        matcher->location, std::move( solved ), std::move( matcher->tmpDecls ) },
     706                                std::move( matcher->env ), std::move( matcher->open ), std::move( matcher->need ),
     707                                ResolvExpr::sumCost( crnt ) + matcher->baseCost ) );
     708                }
     709        };
     710} // anonymous namespace
     711
     712void handleTupleAssignment(
     713        ResolvExpr::CandidateFinder & finder, const ast::UntypedExpr * assign,
     714        std::vector< ResolvExpr::CandidateFinder > & args
     715) {
     716        TupleAssignSpotter_new spotter{ finder };
     717        spotter.spot( assign, args );
     718}
     719
    363720} // namespace Tuples
    364721
Note: See TracChangeset for help on using the changeset viewer.