source: src/Validate/LinkReferenceToTypes.cpp @ b7c53a9d

Last change on this file since b7c53a9d was b7c53a9d, checked in by Andrew Beach <ajbeach@…>, 10 months ago

Added a new invariant check and the fixes required to make it pass. Not the new check is by no means exaustive (it doesn't even check every readonly pointer) but it should catch the most common/problematic cases.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2018 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// LinkReferenceToTypes.cpp -- Connect instance types to declarations.
8//
9// Author           : Andrew Beach
10// Created On       : Thr Apr 21 11:41:00 2022
11// Last Modified By : Andrew Beach
12// Last Modified On : Fri Jul 14  9:19:00 2023
13// Update Count     : 3
14//
15
16#include "Validate/LinkReferenceToTypes.hpp"
17
18#include "AST/Pass.hpp"
19#include "AST/TranslationUnit.hpp"
20#include "Validate/ForallPointerDecay.hpp"
21#include "Validate/NoIdSymbolTable.hpp"
22
23namespace Validate {
24
25namespace {
26
27struct LinkTypesCore : public WithNoIdSymbolTable,
28                public ast::WithCodeLocation,
29                public ast::WithDeclsToAdd<>,
30                public ast::WithGuards,
31                public ast::WithShortCircuiting,
32                public ast::WithVisitorRef<LinkTypesCore> {
33        ast::TypeInstType const * postvisit( ast::TypeInstType const * type );
34        ast::EnumInstType const * postvisit( ast::EnumInstType const * type );
35        ast::StructInstType const * postvisit( ast::StructInstType const * type );
36        ast::UnionInstType const * postvisit( ast::UnionInstType const * type );
37        ast::TraitInstType const * postvisit( ast::TraitInstType const * type );
38        void previsit( ast::QualifiedType const * type );
39        void postvisit( ast::QualifiedType const * type );
40
41        ast::EnumDecl const * postvisit( ast::EnumDecl const * decl );
42        ast::StructDecl const * previsit( ast::StructDecl const * decl );
43        void postvisit( ast::StructDecl const * decl );
44        ast::UnionDecl const * previsit( ast::UnionDecl const * decl );
45        void postvisit( ast::UnionDecl const * decl );
46        ast::TraitDecl const * postvisit( ast::TraitDecl const * decl );
47
48private:
49        using ForwardStructsType =
50                std::map< std::string, std::list< ast::StructInstType * > >;
51        using ForwardUnionsType =
52                std::map< std::string, std::list< ast::UnionInstType * > >;
53        using ForwardEnumsType =
54                std::map< std::string, std::list< ast::EnumInstType * > >;
55
56        ForwardStructsType forwardStructs;
57        ForwardUnionsType forwardUnions;
58        ForwardEnumsType forwardEnums;
59
60        /// true if currently in a generic type body,
61        /// so that type parameter instances can be renamed appropriately
62        bool inGeneric = false;
63
64        template<typename AggrDecl>
65        AggrDecl const * renameGenericParams( AggrDecl const * decl );
66
67        // This cluster is used to add declarations (before) but outside of
68        // any "namespaces" which would qualify the names.
69        bool inNamespace = false;
70        std::list<ast::ptr<ast::Decl>> declsToAddOutside;
71        /// The "leaveNamespace" is handled by guard.
72        void enterNamespace();
73        /// Puts the decl on the back of declsToAddBefore once traversal is
74        /// outside of any namespaces.
75        void addDeclBeforeOutside( ast::Decl const * );
76};
77
78void LinkTypesCore::enterNamespace() {
79        if ( inNamespace ) return;
80        inNamespace = true;
81        GuardAction( [this](){
82                inNamespace = false;
83                declsToAddBefore.splice( declsToAddBefore.begin(), declsToAddOutside );
84        } );
85}
86
87void LinkTypesCore::addDeclBeforeOutside( ast::Decl const * decl ) {
88        if ( inNamespace ) {
89                declsToAddOutside.emplace_back( decl );
90        } else {
91                declsToAddBefore.emplace_back( decl );
92        }
93}
94
95ast::TypeInstType const * LinkTypesCore::postvisit( ast::TypeInstType const * type ) {
96        auto mut = ast::mutate( type );
97        if ( inGeneric && mut->base ) {
98                mut->name = mut->base->name;
99        }
100        if ( auto namedTypeDecl = symtab.lookupType( mut->name ) ) {
101                if ( auto typeDecl = dynamic_cast<ast::TypeDecl const *>( namedTypeDecl ) ) {
102                        mut->kind = typeDecl->kind;
103                }
104        }
105        return mut;
106}
107
108ast::EnumInstType const * LinkTypesCore::postvisit( ast::EnumInstType const * type ) {
109        ast::EnumDecl const * decl = symtab.lookupEnum( type->name );
110        // It's not a semantic error if the enum is not found, just an implicit forward declaration.
111        // The unset code location is used to detect imaginary declarations.
112        // (They may never be used for enumerations.)
113        if ( !decl || decl->location.isUnset() ) {
114                assert( location );
115                decl = new ast::EnumDecl( *location, type->name );
116                symtab.addEnum( decl );
117                addDeclBeforeOutside( decl );
118        }
119
120        ast::EnumInstType * mut = ast::mutate( type );
121
122        // Just linking in the node.
123        mut->base = decl;
124
125        if ( !decl->body ) {
126                forwardEnums[ mut->name ].push_back( mut );
127        }
128        return mut;
129}
130
131ast::StructInstType const * LinkTypesCore::postvisit( ast::StructInstType const * type ) {
132        ast::StructDecl const * decl = symtab.lookupStruct( type->name );
133        // It's not a semantic error if the struct is not found, just an implicit forward declaration.
134        // The unset code location is used to detect imaginary declarations.
135        if ( !decl || decl->location.isUnset() ) {
136                assert( location );
137                decl = new ast::StructDecl( *location, type->name );
138                symtab.addStruct( decl );
139                addDeclBeforeOutside( decl );
140        }
141
142        ast::StructInstType * mut = ast::mutate( type );
143
144        // Just linking in the node.
145        mut->base = decl;
146
147        if ( !decl->body ) {
148                forwardStructs[ mut->name ].push_back( mut );
149        }
150        return mut;
151}
152
153ast::UnionInstType const * LinkTypesCore::postvisit( ast::UnionInstType const * type ) {
154        ast::UnionDecl const * decl = symtab.lookupUnion( type->name );
155        // It's not a semantic error if the union is not found, just an implicit forward declaration.
156        // The unset code location is used to detect imaginary declarations.
157        if ( !decl || decl->location.isUnset() ) {
158                assert( location );
159                decl = new ast::UnionDecl( *location, type->name );
160                symtab.addUnion( decl );
161                addDeclBeforeOutside( decl );
162        }
163
164        ast::UnionInstType * mut = ast::mutate( type );
165
166        // Just linking in the node.
167        mut->base = decl;
168
169        if ( !decl->body ) {
170                forwardUnions[ mut->name ].push_back( mut );
171        }
172        return mut;
173}
174
175ast::TraitInstType const * LinkTypesCore::postvisit( ast::TraitInstType const * type ) {
176        assert( location );
177
178        ast::TraitDecl const * decl = symtab.lookupTrait( type->name );
179        if ( !decl ) {
180                SemanticError( *location, "use of undeclared trait " + type->name );
181        } else if ( decl->params.size() != type->params.size() ) {
182                SemanticError( *location, "incorrect number of trait parameters: " );
183        }
184        auto mut = ast::mutate( type );
185
186        // Just linking in the node.
187        mut->base = decl;
188
189        // Need to carry over the 'sized' status of each decl in the instance.
190        for ( auto p : group_iterate( decl->params, type->params ) ) {
191                ast::TypeExpr const * expr = std::get<1>(p).as<ast::TypeExpr>();
192                if ( !expr ) {
193                        SemanticError( std::get<1>(p).get(), "Expression parameters for trait instances are currently unsupported: " );
194                }
195                if ( auto inst = expr->type.as<ast::TypeInstType>() ) {
196                        ast::ptr<ast::TypeDecl> const & formalDecl = std::get<0>(p);
197                        if ( !formalDecl->sized ) {
198                                continue;
199                        }
200                        // To do this modification we need to reach through a readonly
201                        // pointer. The Pass doesn't quite work in that way, so we just
202                        // ensure it mutates in-place so it should work out.
203                        ast::TypeDecl const * base = inst->base.get();
204                        assert( base->unique() );
205                        ast::TypeDecl * mutBase = ast::mutate( base );
206                        mutBase->sized = true;
207                }
208        }
209        return mut;
210}
211
212void LinkTypesCore::previsit( ast::QualifiedType const * ) {
213        visit_children = false;
214}
215
216void LinkTypesCore::postvisit( ast::QualifiedType const * type ) {
217        // Linking only makes sense for the 'oldest ancestor' of the qualified type.
218        type->parent->accept( *visitor );
219}
220
221ast::EnumDecl const * LinkTypesCore::postvisit( ast::EnumDecl const * decl ) {
222        // After visiting enum members for self-referencing members,
223        // we replace the enum base. Right now it only works for StructDecl.
224        if ( decl->base ) {
225                if ( auto base = decl->base.as<ast::TypeInstType>() ) {
226                        if ( auto structDecl = symtab.lookupStruct( base->name ) ) {
227                                auto mut = ast::mutate( decl );
228                                mut->base = new ast::StructInstType( structDecl );
229                                decl = mut;
230                        }
231                        // visit the base
232                } else if ( auto ptr = decl->base.as<ast::PointerType>() ) {
233                        if ( auto base = ptr->base.as<ast::TypeInstType>() ) {
234                                if ( auto structDecl = symtab.lookupStruct( base->name ) ) {
235                                        auto mut = ast::mutate( decl );
236                                        mut->base = new ast::PointerType(
237                                                new ast::StructInstType( structDecl ) );
238                                        decl = mut;
239                                }
240                        }
241                }
242        }
243
244        // This section is common with struct/union, except for the return value.
245        if ( !decl->body ) {
246                return decl;
247        }
248
249        ForwardEnumsType::iterator fwds = forwardEnums.find( decl->name );
250        if ( fwds != forwardEnums.end() ) {
251                for ( auto inst : fwds->second ) {
252                        inst->base = decl;
253                }
254                forwardEnums.erase( fwds );
255        }
256
257        return decl;
258}
259
260template<typename AggrDecl>
261AggrDecl const * LinkTypesCore::renameGenericParams( AggrDecl const * decl ) {
262        GuardValue( inGeneric ) = !decl->params.empty();
263        if ( !inGeneric ) {
264                return decl;
265        }
266        auto mut = ast::mutate( decl );
267        for ( ast::ptr<ast::TypeDecl> & typeDecl : mut->params ) {
268                typeDecl.get_and_mutate()->name = "__" + typeDecl->name + "_generic_";
269        }
270        return mut;
271}
272
273ast::StructDecl const * LinkTypesCore::previsit( ast::StructDecl const * decl ) {
274        enterNamespace();
275        return renameGenericParams( decl );
276}
277
278void LinkTypesCore::postvisit( ast::StructDecl const * decl ) {
279        if ( !decl->body ) {
280                return;
281        }
282
283        ForwardStructsType::iterator fwds = forwardStructs.find( decl->name );
284        if ( fwds != forwardStructs.end() ) {
285                for ( auto inst : fwds->second ) {
286                        inst->base = decl;
287                }
288                forwardStructs.erase( fwds );
289        }
290}
291
292ast::UnionDecl const * LinkTypesCore::previsit( ast::UnionDecl const * decl ) {
293        enterNamespace();
294        return renameGenericParams( decl );
295}
296
297void LinkTypesCore::postvisit( ast::UnionDecl const * decl ) {
298        if ( !decl->body ) {
299                return;
300        }
301
302        ForwardUnionsType::iterator fwds = forwardUnions.find( decl->name );
303        if ( fwds != forwardUnions.end() ) {
304                for ( auto inst : fwds->second ) {
305                        inst->base = decl;
306                }
307                forwardUnions.erase( fwds );
308        }
309}
310
311ast::TraitDecl const * LinkTypesCore::postvisit( ast::TraitDecl const * decl ) {
312        // There is some overlap with code from decayForallPointers,
313        // perhaps reorganization or shared helper functions are called for.
314        // Move assertions from type parameters into the body of the trait.
315        auto mut = ast::mutate( decl );
316        for ( ast::ptr<ast::TypeDecl> const & td : decl->params ) {
317                auto expanded = expandAssertions( td->assertions );
318                for ( auto declWithType : expanded ) {
319                        mut->members.emplace_back( declWithType.release() );
320                }
321        }
322        return mut;
323}
324
325} // namespace
326
327void linkReferenceToTypes( ast::TranslationUnit & translationUnit ) {
328        ast::Pass<LinkTypesCore>::run( translationUnit );
329}
330
331} // namespace Validate
332
333// Local Variables: //
334// tab-width: 4 //
335// mode: c++ //
336// compile-command: "make install" //
337// End: //
Note: See TracBrowser for help on using the repository browser.