source: src/AST/TypeEnvironment.cpp @ a935892

ADTarm-ehast-experimentalenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprpthread-emulationqualifiedEnum
Last change on this file since a935892 was d76c588, checked in by Aaron Moss <a3moss@…>, 5 years ago

Stubs for new resolver, implementation of new indexer, type environment

  • Property mode set to 100644
File size: 13.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// TypeEnvironment.cpp --
8//
9// Author           : Aaron B. Moss
10// Created On       : Wed May 29 11:00:00 2019
11// Last Modified By : Aaron B. Moss
12// Last Modified On : Wed May 29 11:00:00 2019
13// Update Count     : 1
14//
15
16#include "TypeEnvironment.hpp"
17
18#include <algorithm>  // for copy
19#include <cassert>
20#include <iterator>   // for ostream_iterator
21#include <iostream>
22#include <string>
23#include <utility>    // for move
24#include <vector>
25
26#include "Decl.hpp"
27#include "Node.hpp"
28#include "Pass.hpp"
29#include "Print.hpp"
30#include "Type.hpp"
31#include "Common/Indenter.h"
32#include "ResolvExpr/typeops.h"    // for occurs
33#include "ResolvExpr/WidenMode.h"
34#include "ResolvExpr/Unify.h"      // for unifyInexact
35#include "Tuples/Tuples.h"         // for isTtype
36
37using ResolvExpr::WidenMode;
38
39namespace ast {
40
41void print( std::ostream & out, const AssertionSet & assns, Indenter indent ) {
42        for ( const auto & i : assns ) {
43                print( out, i.first, indent );
44                out << ( i.second.isUsed ? " (used)" : " (not used)" );
45        }
46}
47
48void print( std::ostream & out, const OpenVarSet & openVars, Indenter indent ) {
49        out << indent;
50        bool first = true;
51        for ( const auto & i : openVars ) {
52                if ( first ) { first = false; } else { out << ' '; }
53                out << i.first << "(" << i.second << ")";
54        }
55}
56
57void print( std::ostream & out, const EqvClass & clz, Indenter indent ) {
58        out << "( ";
59        std::copy( clz.vars.begin(), clz.vars.end(), std::ostream_iterator< std::string >( out, " " ) );
60        out << ")";
61       
62        if ( clz.bound ) {
63                out << " -> ";
64                print( out, clz.bound, indent+1 );
65        }
66
67        if ( ! clz.allowWidening ) {
68                out << " (no widening)";
69        }
70
71        out << std::endl;
72}
73
74const EqvClass * TypeEnvironment::lookup( const std::string & var ) const {
75        for ( ClassList::const_iterator i = env.begin(); i != env.end(); ++i ) {
76                if ( i->vars.find( var ) != i->vars.end() ) return &*i;
77        }
78        return nullptr;
79}
80
81namespace {
82        /// Removes any class from env that intersects eqvClass
83        void filterOverlappingClasses( std::list<EqvClass> & env, const EqvClass & eqvClass ) {
84                auto i = env.begin();
85                while ( i != env.end() ) {
86                        auto next = i; ++next;  // save next node in case of erasure
87
88                        for ( const auto & v : eqvClass.vars ) {
89                                if ( i->vars.count( v ) ) {
90                                        env.erase( i );  // remove overlapping class
91                                        break;           // done with this class
92                                }
93                        }
94                       
95                        i = next;  // go to next node even if this removed
96                }
97        }
98}
99
100void TypeEnvironment::add( const ParameterizedType::ForallList & tyDecls ) {
101        for ( const TypeDecl * tyDecl : tyDecls ) {
102                env.emplace_back( tyDecl );
103        }
104}
105
106void TypeEnvironment::add( const TypeSubstitution & sub ) {
107        for ( const auto p : sub ) {
108                add( EqvClass{ p.first, p.second } );
109        }
110}
111
112void TypeEnvironment::writeToSubstitution( TypeSubstitution & sub ) const {
113        for ( const auto & clz : env ) {
114                std::string clzRep;
115                for ( const auto & var : clz.vars ) {
116                        if ( clz.bound ) {
117                                sub.add( var, clz.bound );
118                        } else if ( clzRep.empty() ) {
119                                clzRep = var;
120                        } else {
121                                sub.add( var, new TypeInstType{ clzRep, clz.data.kind } );
122                        }
123                }
124        }
125        sub.normalize();
126}
127
128void TypeEnvironment::simpleCombine( const TypeEnvironment & o ) {
129        env.insert( env.end(), o.env.begin(), o.env.end() );
130}
131
132namespace {
133        /// Implements occurs check by traversing type
134        struct Occurs : public ast::WithVisitorRef<Occurs> {
135                bool result;
136                std::set< std::string > vars;
137                const TypeEnvironment & tenv;
138
139                Occurs( const std::string & var, const TypeEnvironment & env )
140                : result( false ), vars(), tenv( env ) {
141                        if ( const EqvClass * clz = tenv.lookup( var ) ) {
142                                vars = clz->vars;
143                        } else {
144                                vars.emplace( var );
145                        }
146                }
147
148                void previsit( const TypeInstType * typeInst ) {
149                        if ( vars.count( typeInst->name ) ) {
150                                result = true;
151                        } else if ( const EqvClass * clz = tenv.lookup( typeInst->name ) ) {
152                                if ( clz->bound ) {
153                                        clz->bound->accept( *visitor );
154                                }
155                        }
156                }
157        };
158
159        /// true if `var` occurs in `ty` under `env`
160        bool occurs( const Type * ty, const std::string & var, const TypeEnvironment & env ) {
161                Pass<Occurs> occur{ var, env };
162                maybe_accept( ty, occur );
163                return occur.pass.result;
164        }
165}
166
167bool TypeEnvironment::combine( 
168                const TypeEnvironment & o, OpenVarSet & openVars, const SymbolTable & symtab ) {
169        // short-circuit easy cases
170        if ( o.empty() ) return true;
171        if ( empty() ) {
172                simpleCombine( o );
173                return true;
174        }
175
176        // merge classes
177        for ( const EqvClass & c : o.env ) {
178                // index of typeclass in local environment bound to c
179                auto rt = env.end();
180
181                // look for first existing bound variable
182                auto vt = c.vars.begin();
183                for ( ; vt != c.vars.end(); ++vt ) {
184                        rt = internal_lookup( *vt );
185                        if ( rt != env.end() ) break;
186                }
187
188                if ( rt != env.end() ) {  // c needs to be merged into *rt
189                        EqvClass & r = *rt;
190                        // merge bindings
191                        if ( ! mergeBound( r, c, openVars, symtab ) ) return false;
192                        // merge previous unbound variables into this class, checking occurs if needed
193                        if ( r.bound ) for ( const auto & u : c.vars ) {
194                                if ( occurs( r.bound, u, *this ) ) return false;
195                                r.vars.emplace( u );
196                        } else { r.vars.insert( c.vars.begin(), vt ); }
197                        // merge subsequent variables into this class (skipping *vt, already there)
198                        while ( ++vt != c.vars.end() ) {
199                                auto st = internal_lookup( *vt );
200                                if ( st == env.end() ) {
201                                        // unbound, safe to add if occurs
202                                        if ( r.bound && occurs( r.bound, *vt, *this ) ) return false;
203                                        r.vars.emplace( *vt );
204                                } else if ( st != rt ) {
205                                        // bound, but not to the same class
206                                        if ( ! mergeClasses( rt, st, openVars, symtab ) ) return false;
207                                }       // ignore bound into the same class
208                        }
209                } else {  // no variables in c bound; just copy up
210                        env.emplace_back( c );
211                }
212        }
213
214        // merged all classes
215        return true;
216}
217
218void TypeEnvironment::extractOpenVars( OpenVarSet & openVars ) const {
219        for ( const auto & clz : env ) {
220                for ( const auto & var : clz.vars ) {
221                        openVars[ var ] = clz.data;
222                }
223        }
224}
225
226void TypeEnvironment::addActual( const TypeEnvironment & actualEnv, OpenVarSet & openVars ) {
227        for ( const auto & clz : actualEnv ) {
228                EqvClass c = clz;
229                c.allowWidening = false;
230                for ( const auto & var : c.vars ) {
231                        openVars[ var ] = c.data;
232                }
233                env.emplace_back( std::move(c) );
234        }
235}
236
237namespace {
238        /// true if a type is a function type
239        bool isFtype( const Type * type ) {
240                if ( dynamic_cast< const FunctionType * >( type ) ) {
241                        return true;
242                } else if ( auto typeInst = dynamic_cast< const TypeInstType * >( type ) ) {
243                        return typeInst->kind == TypeVar::Ftype;
244                } else return false;
245        }
246
247        /// true if the given type can be bound to the given type variable
248        bool tyVarCompatible( const TypeDecl::Data & data, const Type * type ) {
249                switch ( data.kind ) {
250                  case TypeVar::Dtype:
251                        // to bind to an object type variable, the type must not be a function type.
252                        // if the type variable is specified to be a complete type then the incoming
253                        // type must also be complete
254                        // xxx - should this also check that type is not a tuple type and that it's not a ttype?
255                        return ! isFtype( type ) && ( ! data.isComplete || type->isComplete() );
256                  case TypeVar::Ftype:
257                        return isFtype( type );
258                  case TypeVar::Ttype:
259                        // ttype unifies with any tuple type
260                        return dynamic_cast< const TupleType * >( type ) || Tuples::isTtype( type );
261                  default:
262                        assertf(false, "Unhandled tyvar kind: %d", data.kind);
263                        return false;
264                }
265        }
266}
267
268bool TypeEnvironment::bindVar( 
269                const TypeInstType * typeInst, const Type * bindTo, const TypeDecl::Data & data, 
270                AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
271                WidenMode widenMode, const SymbolTable & symtab ) {
272        // remove references from bound type, so that type variables can only bind to value types
273        bindTo = bindTo->stripReferences();
274        auto tyvar = openVars.find( typeInst->name );
275        assert( tyvar != openVars.end() );
276        if ( ! tyVarCompatible( tyvar->second, bindTo ) ) return false;
277        if ( occurs( bindTo, typeInst->name, *this ) ) return false;
278
279        auto it = internal_lookup( typeInst->name );
280        if ( it != env.end() ) {
281                if ( it->bound ) {
282                        // attempt to unify equivalence class type with type to bind to.
283                        // equivalence class type has stripped qualifiers which must be restored
284                        const Type * common = nullptr;
285                        ptr<Type> newType = it->bound;
286                        newType.get_and_mutate()->qualifiers = typeInst->qualifiers;
287                        if ( unifyInexact( 
288                                        newType, bindTo, *this, need, have, openVars, 
289                                        widenMode & WidenMode{ it->allowWidening, true }, symtab, common ) ) {
290                                if ( common ) {
291                                        it->bound = common;
292                                        clear_qualifiers( it->bound );
293                                }
294                        } else return false;
295                } else {
296                        it->bound = bindTo;
297                        clear_qualifiers( it->bound );
298                        it->allowWidening = widenMode.widenFirst && widenMode.widenSecond;
299                }
300        } else {
301                env.emplace_back( 
302                        typeInst->name, bindTo, widenMode.widenFirst && widenMode.widenSecond, data );
303        }
304        return true;
305}
306
307bool TypeEnvironment::bindVarToVar( 
308                const TypeInstType * var1, const TypeInstType * var2, TypeDecl::Data && data, 
309                AssertionSet & need, AssertionSet & have, const OpenVarSet & openVars, 
310                WidenMode widenMode, const SymbolTable & symtab ) {
311        auto c1 = internal_lookup( var1->name );
312        auto c2 = internal_lookup( var2->name );
313       
314        // exit early if variables already bound together
315        if ( c1 != env.end() && c1 == c2 ) {
316                c1->allowWidening &= widenMode;
317                return true;
318        }
319
320        bool widen1 = false, widen2 = false;
321        const Type * type1 = nullptr, * type2 = nullptr;
322
323        // check for existing bindings, perform occurs check
324        if ( c1 != env.end() ) {
325                if ( c1->bound ) {
326                        if ( occurs( c1->bound, var2->name, *this ) ) return false;
327                        type1 = c1->bound;
328                }
329                widen1 = widenMode.widenFirst && c1->allowWidening;
330        }
331        if ( c2 != env.end() ) {
332                if ( c2->bound ) {
333                        if ( occurs( c2->bound, var1->name, *this ) ) return false;
334                        type2 = c2->bound;
335                }
336                widen2 = widenMode.widenSecond && c2->allowWidening;
337        }
338
339        if ( type1 && type2 ) {
340                // both classes bound, merge if bound types can be unified
341                ptr<Type> newType1{ type1 }, newType2{ type2 };
342                WidenMode newWidenMode{ widen1, widen2 };
343                const Type * common = nullptr;
344
345                if ( unifyInexact(
346                                newType1, newType2, *this, need, have, openVars, newWidenMode, symtab, common ) ) {
347                        c1->vars.insert( c2->vars.begin(), c2->vars.end() );
348                        c1->allowWidening = widen1 && widen2;
349                        if ( common ) {
350                                c1->bound = common;
351                                clear_qualifiers( c1->bound );
352                        }
353                        c1->data.isComplete |= data.isComplete;
354                        env.erase( c2 );
355                } else return false;
356        } else if ( c1 != env.end() && c2 != env.end() ) {
357                // both classes exist, at least one unbound, merge unconditionally
358                if ( type1 ) {
359                        c1->vars.insert( c2->vars.begin(), c2->vars.end() );
360                        c1->allowWidening = widen1;
361                        c1->data.isComplete |= data.isComplete;
362                        env.erase( c2 );
363                } else {
364                        c2->vars.insert( c1->vars.begin(), c1->vars.end() );
365                        c2->allowWidening = widen2;
366                        c2->data.isComplete |= data.isComplete;
367                        env.erase( c1 );
368                }
369        } else if ( c1 != env.end() ) {
370                // var2 unbound, add to env[c1]
371                c1->vars.emplace( var2->name );
372                c1->allowWidening = widen1;
373                c1->data.isComplete |= data.isComplete;
374        } else if ( c2 != env.end() ) {
375                // var1 unbound, add to env[c2]
376                c2->vars.emplace( var1->name );
377                c2->allowWidening = widen2;
378                c2->data.isComplete |= data.isComplete;
379        } else {
380                // neither var bound, create new class
381                env.emplace_back( var1->name, var2->name, widen1 && widen2, data );
382        }
383
384        return true;
385}
386
387void TypeEnvironment::forbidWidening() {
388        for ( EqvClass& c : env ) c.allowWidening = false;
389}
390
391void TypeEnvironment::add( EqvClass && eqvClass ) {
392        filterOverlappingClasses( env, eqvClass );
393        env.emplace_back( std::move(eqvClass) );
394}
395
396bool TypeEnvironment::mergeBound( 
397                EqvClass & to, const EqvClass & from, OpenVarSet & openVars, const SymbolTable & symtab ) {
398        if ( from.bound ) {
399                if ( to.bound ) {
400                        // attempt to unify bound types
401                        ptr<Type> toType{ to.bound }, fromType{ from.bound };
402                        WidenMode widenMode{ to.allowWidening, from.allowWidening };
403                        const Type * common = nullptr;
404                        AssertionSet need, have;
405
406                        if ( unifyInexact( 
407                                        toType, fromType, *this, need, have, openVars, widenMode, symtab, common ) ) {
408                                // unifies, set common type if necessary
409                                if ( common ) {
410                                        to.bound = common;
411                                        clear_qualifiers( to.bound );
412                                }
413                        } else return false; // cannot unify
414                } else {
415                        to.bound = from.bound;
416                }
417        }
418
419        // unify widening if matches
420        to.allowWidening &= from.allowWidening;
421        return true;
422}
423
424bool TypeEnvironment::mergeClasses( 
425                ClassList::iterator to, ClassList::iterator from, OpenVarSet & openVars, 
426                const SymbolTable & symtab ) {
427        EqvClass & r = *to, & s = *from;
428
429        // ensure bounds match
430        if ( ! mergeBound( r, s, openVars, symtab ) ) return false;
431
432        // check safely bindable
433        if ( r.bound ) {
434                for ( const auto & v : s.vars ) if ( occurs( r.bound, v, *this ) ) return false;
435        }
436
437        // merge classes
438        r.vars.insert( s.vars.begin(), s.vars.end() );
439        r.allowWidening &= s.allowWidening;
440        env.erase( from );
441
442        return true;
443}
444
445TypeEnvironment::ClassList::iterator TypeEnvironment::internal_lookup( const std::string & var ) {
446        for ( ClassList::iterator i = env.begin(); i != env.end(); ++i ) {
447                if ( i->vars.count( var ) ) return i;
448        }
449        return env.end();
450}
451
452void print( std::ostream & out, const TypeEnvironment & env, Indenter indent ) {
453        for ( const auto & clz : env ) {
454                print( out, clz, indent );
455        }
456}
457
458std::ostream & operator<<( std::ostream & out, const TypeEnvironment & env ) {
459        print( out, env );
460        return out;
461}
462
463}
464
465// Local Variables: //
466// tab-width: 4 //
467// mode: c++ //
468// compile-command: "make install" //
469// End: //
Note: See TracBrowser for help on using the repository browser.