source: src/Virtual/ExpandCasts.cc

Last change on this file was c36814a, checked in by Andrew Beach <ajbeach@…>, 2 weeks ago

Adding 'final' and removing a redundent namespace in the post resolve new ast code.

  • Property mode set to 100644
File size: 15.9 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// ExpandCasts.cc --
8//
9// Author           : Andrew Beach
10// Created On       : Mon Jul 24 13:59:00 2017
11// Last Modified By : Andrew Beach
12// Last Modified On : Thu Aug 11 12:06:00 2022
13// Update Count     : 5
14//
15
16#include "ExpandCasts.h"
17
18#include <cassert>                 // for assert, assertf
19#include <iterator>                // for back_inserter, inserter
20#include <string>                  // for string, allocator, operator==, ope...
21
22#include "AST/Decl.hpp"
23#include "AST/Expr.hpp"
24#include "AST/Pass.hpp"
25#include "Common/PassVisitor.h"    // for PassVisitor
26#include "Common/ScopedMap.h"      // for ScopedMap
27#include "Common/SemanticError.h"  // for SemanticError
28#include "SymTab/Mangler.h"        // for mangleType
29#include "SynTree/Declaration.h"   // for ObjectDecl, StructDecl, FunctionDecl
30#include "SynTree/Expression.h"    // for VirtualCastExpr, CastExpr, Address...
31#include "SynTree/Mutator.h"       // for mutateAll
32#include "SynTree/Type.h"          // for Type, PointerType, StructInstType
33#include "SynTree/Visitor.h"       // for acceptAll
34
35namespace Virtual {
36
37namespace {
38
39bool is_prefix( const std::string & prefix, const std::string& entire ) {
40        size_t const p_size = prefix.size();
41        return (p_size < entire.size() && prefix == entire.substr(0, p_size));
42}
43
44bool is_type_id_object( const ObjectDecl * objectDecl ) {
45        const std::string & objectName = objectDecl->name;
46        return is_prefix( "__cfatid_", objectName );
47}
48
49bool is_type_id_object( const ast::ObjectDecl * decl ) {
50        return is_prefix( "__cfatid_", decl->name );
51}
52
53        // Indented until the new ast code gets added.
54
55        /// Maps virtual table types the instance for that type.
56        class VirtualTableMap final {
57                ScopedMap<std::string, ObjectDecl *> vtable_instances;
58        public:
59                void enterScope() {
60                        vtable_instances.beginScope();
61                }
62                void leaveScope() {
63                        vtable_instances.endScope();
64                }
65
66                ObjectDecl * insert( ObjectDecl * vtableDecl ) {
67                        std::string const & mangledName = SymTab::Mangler::mangleType( vtableDecl->type );
68                        ObjectDecl *& value = vtable_instances[ mangledName ];
69                        if ( value ) {
70                                if ( vtableDecl->storageClasses.is_extern ) {
71                                        return nullptr;
72                                } else if ( ! value->storageClasses.is_extern ) {
73                                        return value;
74                                }
75                        }
76                        value = vtableDecl;
77                        return nullptr;
78                }
79
80                ObjectDecl * lookup( const Type * vtableType ) {
81                        std::string const & mangledName = SymTab::Mangler::mangleType( vtableType );
82                        const auto it = vtable_instances.find( mangledName );
83                        return ( vtable_instances.end() == it ) ? nullptr : it->second;
84                }
85        };
86
87        class VirtualCastCore {
88                CastExpr * cast_to_type_id( Expression * expr, int level_of_indirection ) {
89                        Type * type = new StructInstType(
90                                Type::Qualifiers( Type::Const ), pvt_decl );
91                        for (int i = 0 ; i < level_of_indirection ; ++i) {
92                                type = new PointerType( noQualifiers, type );
93                        }
94                        return new CastExpr( expr, type );
95                }
96
97        public:
98                VirtualCastCore() :
99                        indexer(), vcast_decl( nullptr ), pvt_decl( nullptr )
100                {}
101
102                void premutate( FunctionDecl * functionDecl );
103                void premutate( StructDecl * structDecl );
104                void premutate( ObjectDecl * objectDecl );
105
106                Expression * postmutate( VirtualCastExpr * castExpr );
107
108                VirtualTableMap indexer;
109        private:
110                FunctionDecl *vcast_decl;
111                StructDecl *pvt_decl;
112        };
113
114        void VirtualCastCore::premutate( FunctionDecl * functionDecl ) {
115                if ( (! vcast_decl) &&
116                     functionDecl->get_name() == "__cfavir_virtual_cast" ) {
117                        vcast_decl = functionDecl;
118                }
119        }
120
121        void VirtualCastCore::premutate( StructDecl * structDecl ) {
122                if ( pvt_decl || ! structDecl->has_body() ) {
123                        return;
124                } else if ( structDecl->get_name() == "__cfavir_type_info" ) {
125                        pvt_decl = structDecl;
126                }
127        }
128
129        void VirtualCastCore::premutate( ObjectDecl * objectDecl ) {
130                if ( is_type_id_object( objectDecl ) ) {
131                        // Multiple definitions should be fine because of linkonce.
132                        indexer.insert( objectDecl );
133                }
134        }
135
136        /// Better error locations for generated casts.
137        CodeLocation castLocation( const VirtualCastExpr * castExpr ) {
138                if ( castExpr->location.isSet() ) {
139                        return castExpr->location;
140                } else if ( castExpr->arg->location.isSet() ) {
141                        return castExpr->arg->location;
142                } else if ( castExpr->result->location.isSet() ) {
143                        return castExpr->result->location;
144                } else {
145                        return CodeLocation();
146                }
147        }
148
149        [[noreturn]] void castError( const VirtualCastExpr * castExpr, std::string const & message ) {
150                SemanticError( castLocation( castExpr ), message );
151        }
152
153        /// Get the base type from a pointer or reference.
154        const Type * getBaseType( const Type * type ) {
155                if ( auto target = dynamic_cast<const PointerType *>( type ) ) {
156                        return target->base;
157                } else if ( auto target = dynamic_cast<const ReferenceType *>( type ) ) {
158                        return target->base;
159                } else {
160                        return nullptr;
161                }
162        }
163
164        /* Attempt to follow the "head" field of the structure to get the...
165         * Returns nullptr on error, otherwise owner must free returned node.
166         */
167        StructInstType * followHeadPointerType(
168                        const StructInstType * oldType,
169                        const std::string& fieldName,
170                        const CodeLocation& errorLocation ) {
171
172                // First section of the function is all about trying to fill this variable in.
173                StructInstType * newType = nullptr;
174                {
175                        const StructDecl * oldDecl = oldType->baseStruct;
176                        assert( oldDecl );
177
178                        // Helper function for throwing semantic errors.
179                        auto throwError = [&fieldName, &errorLocation, &oldDecl](const std::string& message) {
180                                const std::string& context = "While following head pointer of " +
181                                        oldDecl->name + " named '" + fieldName + "': ";
182                                SemanticError( errorLocation, context + message );
183                        };
184
185                        if ( oldDecl->members.empty() ) {
186                                throwError( "Type has no fields." );
187                        }
188                        const Declaration * memberDecl = oldDecl->members.front();
189                        assert( memberDecl );
190                        const ObjectDecl * fieldDecl = dynamic_cast<const ObjectDecl *>( memberDecl );
191                        assert( fieldDecl );
192                        if ( fieldName != fieldDecl->name ) {
193                                throwError( "Head field did not have expected name." );
194                        }
195
196                        const Type * fieldType = fieldDecl->type;
197                        if ( nullptr == fieldType ) {
198                                throwError( "Could not get head field." );
199                        }
200                        const PointerType * ptrType = dynamic_cast<const PointerType *>( fieldType );
201                        if ( nullptr == ptrType ) {
202                                throwError( "First field is not a pointer type." );
203                        }
204                        assert( ptrType->base );
205                        newType = dynamic_cast<StructInstType *>( ptrType->base );
206                        if ( nullptr == newType ) {
207                                throwError( "First field does not point to a structure type." );
208                        }
209                }
210
211                // Now we can look into copying it.
212                newType = newType->clone();
213                if ( ! oldType->parameters.empty() ) {
214                        deleteAll( newType->parameters );
215                        newType->parameters.clear();
216                        cloneAll( oldType->parameters, newType->parameters );
217                }
218                return newType;
219        }
220
221        /// Get the type-id type from a virtual type.
222        StructInstType * getTypeIdType( const Type * type, const CodeLocation& errorLocation ) {
223                const StructInstType * typeInst = dynamic_cast<const StructInstType *>( type );
224                if ( nullptr == typeInst ) {
225                        return nullptr;
226                }
227                StructInstType * tableInst =
228                        followHeadPointerType( typeInst, "virtual_table", errorLocation );
229                if ( nullptr == tableInst ) {
230                        return nullptr;
231                }
232                StructInstType * typeIdInst =
233                        followHeadPointerType( tableInst, "__cfavir_typeid", errorLocation );
234                delete tableInst;
235                return typeIdInst;
236        }
237
238        Expression * VirtualCastCore::postmutate( VirtualCastExpr * castExpr ) {
239                assertf( castExpr->result, "Virtual Cast target not found before expansion." );
240
241                assert( vcast_decl );
242                assert( pvt_decl );
243
244                const Type * base_type = getBaseType( castExpr->result );
245                if ( nullptr == base_type ) {
246                        castError( castExpr, "Virtual cast target must be a pointer or reference type." );
247                }
248                const Type * type_id_type = getTypeIdType( base_type, castLocation( castExpr ) );
249                if ( nullptr == type_id_type ) {
250                        castError( castExpr, "Ill formed virtual cast target type." );
251                }
252                ObjectDecl * type_id = indexer.lookup( type_id_type );
253                delete type_id_type;
254                if ( nullptr == type_id ) {
255                        castError( castExpr, "Virtual cast does not target a virtual type." );
256                }
257
258                Expression * result = new CastExpr(
259                        new ApplicationExpr( VariableExpr::functionPointer( vcast_decl ), {
260                                cast_to_type_id( new AddressExpr( new VariableExpr( type_id ) ), 1 ),
261                                cast_to_type_id( castExpr->get_arg(), 2 ),
262                        } ),
263                        castExpr->get_result()->clone()
264                );
265
266                castExpr->set_arg( nullptr );
267                castExpr->set_result( nullptr );
268                delete castExpr;
269                return result;
270        }
271
272/// Better error locations for generated casts.
273// TODO: Does the improved distribution of code locations make this unneeded?
274CodeLocation castLocation( const ast::VirtualCastExpr * castExpr ) {
275        if ( castExpr->location.isSet() ) {
276                return castExpr->location;
277        } else if ( castExpr->arg->location.isSet() ) {
278                return castExpr->arg->location;
279        } else {
280                return CodeLocation();
281        }
282}
283
284[[noreturn]] void castError( ast::VirtualCastExpr const * castExpr, std::string const & message ) {
285        SemanticError( castLocation( castExpr ), message );
286}
287
288class TypeIdTable final {
289        ScopedMap<std::string, ast::ObjectDecl const *> instances;
290public:
291        void enterScope() { instances.beginScope(); }
292        void leaveScope() { instances.endScope(); }
293
294        // Attempt to insert an instance into the map. If there is a conflict,
295        // returns the previous declaration for error messages.
296        ast::ObjectDecl const * insert( ast::ObjectDecl const * typeIdDecl ) {
297                std::string const & mangledName =
298                                Mangle::mangle( typeIdDecl->type, Mangle::typeMode() );
299                ast::ObjectDecl const *& value = instances[ mangledName ];
300                if ( value ) {
301                        if ( typeIdDecl->storage.is_extern ) {
302                                return nullptr;
303                        } else if ( !value->storage.is_extern ) {
304                                return value;
305                        }
306                }
307                value = typeIdDecl;
308                return nullptr;
309        }
310
311        ast::ObjectDecl const * lookup( ast::Type const * typeIdType ) {
312                std::string const & mangledName =
313                                Mangle::mangle( typeIdType, Mangle::typeMode() );
314                auto const it = instances.find( mangledName );
315                return ( instances.end() == it ) ? nullptr : it->second;
316        }
317};
318
319struct ExpandCastsCore final {
320        void previsit( ast::FunctionDecl const * decl );
321        void previsit( ast::StructDecl const * decl );
322        void previsit( ast::ObjectDecl const * decl );
323        ast::Expr const * postvisit( ast::VirtualCastExpr const * expr );
324
325        ast::CastExpr const * cast_to_type_id(
326                ast::Expr const * expr, unsigned int level_of_indirection );
327
328        ast::FunctionDecl const * vcast_decl = nullptr;
329        ast::StructDecl const * info_decl = nullptr;
330
331        TypeIdTable symtab;
332};
333
334void ExpandCastsCore::previsit( ast::FunctionDecl const * decl ) {
335        if ( !vcast_decl && "__cfavir_virtual_cast" == decl->name ) {
336                vcast_decl = decl;
337        }
338}
339
340void ExpandCastsCore::previsit( ast::StructDecl const * decl ) {
341        if ( !info_decl && decl->body && "__cfavir_type_info" == decl->name ) {
342                info_decl = decl;
343        }
344}
345
346void ExpandCastsCore::previsit( ast::ObjectDecl const * decl ) {
347        if ( is_type_id_object( decl ) ) {
348                // Multiple definitions should be fine because of linkonce.
349                symtab.insert( decl );
350        }
351}
352
353/// Get the base type from a pointer or reference.
354ast::Type const * getBaseType( ast::ptr<ast::Type> const & type ) {
355        if ( auto target = type.as<ast::PointerType>() ) {
356                return target->base.get();
357        } else if ( auto target = type.as<ast::ReferenceType>() ) {
358                return target->base.get();
359        } else {
360                return nullptr;
361        }
362}
363
364/// Copy newType, but give the copy the params of the oldType.
365ast::StructInstType * polyCopy(
366                ast::StructInstType const * oldType,
367                ast::StructInstType const * newType ) {
368        assert( oldType->params.size() == newType->params.size() );
369        ast::StructInstType * retType = ast::deepCopy( newType );
370        if ( ! oldType->params.empty() ) {
371                retType->params.clear();
372                for ( auto oldParams : oldType->params ) {
373                        retType->params.push_back( ast::deepCopy( oldParams ) );
374                }
375        }
376        return retType;
377}
378
379/// Follow the "head" field of the structure to get the type that is pointed
380/// to by that field.
381ast::StructInstType const * followHeadPointerType(
382                CodeLocation const & errorLocation,
383                ast::StructInstType const * oldType,
384                std::string const & fieldName ) {
385        ast::StructDecl const * oldDecl = oldType->base;
386        assert( oldDecl );
387
388        // Helper function for throwing semantic errors.
389        auto throwError = [&fieldName, &errorLocation, &oldDecl](
390                        std::string const & message ) {
391                std::string const & context = "While following head pointer of " +
392                        oldDecl->name + " named '" + fieldName + "': ";
393                SemanticError( errorLocation, context + message );
394        };
395
396        if ( oldDecl->members.empty() ) {
397                throwError( "Type has no fields." );
398        }
399        ast::ptr<ast::Decl> const & memberDecl = oldDecl->members.front();
400        assert( memberDecl );
401        ast::ObjectDecl const * fieldDecl = memberDecl.as<ast::ObjectDecl>();
402        assert( fieldDecl );
403        if ( fieldName != fieldDecl->name ) {
404                throwError( "Head field did not have expected name." );
405        }
406
407        ast::ptr<ast::Type> const & fieldType = fieldDecl->type;
408        if ( nullptr == fieldType ) {
409                throwError( "Could not get head field." );
410        }
411        auto ptrType = fieldType.as<ast::PointerType>();
412        if ( nullptr == ptrType ) {
413                throwError( "First field is not a pointer type." );
414        }
415        assert( ptrType->base );
416        auto newType = ptrType->base.as<ast::StructInstType>();
417        if ( nullptr == newType ) {
418                throwError( "First field does not point to a structure type." );
419        }
420
421        return polyCopy( oldType, newType );
422}
423
424/// Get the type-id type from a virtual type.
425ast::StructInstType const * getTypeIdType(
426                CodeLocation const & errorLocation,
427                ast::Type const * type ) {
428        auto typeInst = dynamic_cast<ast::StructInstType const *>( type );
429        if ( nullptr == typeInst ) {
430                return nullptr;
431        }
432        ast::ptr<ast::StructInstType> tableInst =
433                followHeadPointerType( errorLocation, typeInst, "virtual_table" );
434        if ( nullptr == tableInst ) {
435                return nullptr;
436        }
437        ast::StructInstType const * typeIdInst =
438                followHeadPointerType( errorLocation, tableInst, "__cfavir_typeid" );
439        return typeIdInst;
440}
441
442ast::Expr const * ExpandCastsCore::postvisit(
443                ast::VirtualCastExpr const * expr ) {
444        assertf( expr->result, "Virtual cast target not found before expansion." );
445
446        assert( vcast_decl );
447        assert( info_decl );
448
449        ast::Type const * base_type = getBaseType( expr->result );
450        if ( nullptr == base_type ) {
451                castError( expr, "Virtual cast target must be a pointer or reference type." );
452        }
453        ast::StructInstType const * type_id_type =
454                        getTypeIdType( castLocation( expr ), base_type );
455        if ( nullptr == type_id_type ) {
456                castError( expr, "Ill formed virtual cast target type." );
457        }
458        ast::ObjectDecl const * type_id = symtab.lookup( type_id_type );
459        if ( nullptr == type_id ) {
460                // I'm trying to give a different error for polymorpic types as
461                // different things can go wrong there.
462                if ( type_id_type->params.empty() ) {
463                        castError( expr, "Virtual cast does not target a virtual type." );
464                } else {
465                        castError( expr, "Virtual cast does not target a type with a "
466                                "type id (possible missing virtual table)." );
467                }
468        }
469
470        return new ast::CastExpr( expr->location,
471                new ast::ApplicationExpr( expr->location,
472                        ast::VariableExpr::functionPointer( expr->location, vcast_decl ),
473                        {
474                                cast_to_type_id(
475                                        new ast::AddressExpr( expr->location,
476                                                new ast::VariableExpr( expr->location, type_id ) ),
477                                        1 ),
478                                cast_to_type_id( expr->arg, 2 ),
479                        }
480                ),
481                ast::deepCopy( expr->result )
482        );
483}
484
485ast::CastExpr const * ExpandCastsCore::cast_to_type_id(
486                ast::Expr const * expr, unsigned int level_of_indirection ) {
487        assert( info_decl );
488        ast::Type * type = new ast::StructInstType( info_decl, ast::CV::Const );
489        for ( unsigned int i = 0 ; i < level_of_indirection ; ++i ) {
490                type = new ast::PointerType( type );
491        }
492        return new ast::CastExpr( expr->location, expr, type );
493}
494
495} // namespace
496
497void expandCasts( std::list< Declaration * > & translationUnit ) {
498        PassVisitor<VirtualCastCore> translator;
499        mutateAll( translationUnit, translator );
500}
501
502void expandCasts( ast::TranslationUnit & translationUnit ) {
503        ast::Pass<ExpandCastsCore>::run( translationUnit );
504}
505
506} // namespace Virtual
Note: See TracBrowser for help on using the repository browser.