source: src/Virtual/ExpandCasts.cc @ 6a0b043

Last change on this file since 6a0b043 was c6b4432, checked in by Andrew Beach <ajbeach@…>, 12 months ago

Remove BaseSyntaxNode? and clean-up.

  • Property mode set to 100644
File size: 8.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// 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/Copy.hpp"
23#include "AST/Decl.hpp"
24#include "AST/Expr.hpp"
25#include "AST/Pass.hpp"
26#include "Common/ScopedMap.h"      // for ScopedMap
27#include "Common/SemanticError.h"  // for SemanticError
28#include "SymTab/Mangler.h"        // for mangleType
29
30namespace Virtual {
31
32namespace {
33
34bool is_prefix( const std::string & prefix, const std::string& entire ) {
35        size_t const p_size = prefix.size();
36        return (p_size < entire.size() && prefix == entire.substr(0, p_size));
37}
38
39bool is_type_id_object( const ast::ObjectDecl * decl ) {
40        return is_prefix( "__cfatid_", decl->name );
41}
42
43        // Indented until the new ast code gets added.
44
45        /// Maps virtual table types the instance for that type.
46
47/// Better error locations for generated casts.
48// TODO: Does the improved distribution of code locations make this unneeded?
49CodeLocation castLocation( const ast::VirtualCastExpr * castExpr ) {
50        if ( castExpr->location.isSet() ) {
51                return castExpr->location;
52        } else if ( castExpr->arg->location.isSet() ) {
53                return castExpr->arg->location;
54        } else {
55                return CodeLocation();
56        }
57}
58
59[[noreturn]] void castError( ast::VirtualCastExpr const * castExpr, std::string const & message ) {
60        SemanticError( castLocation( castExpr ), message );
61}
62
63class TypeIdTable final {
64        ScopedMap<std::string, ast::ObjectDecl const *> instances;
65public:
66        void enterScope() { instances.beginScope(); }
67        void leaveScope() { instances.endScope(); }
68
69        // Attempt to insert an instance into the map. If there is a conflict,
70        // returns the previous declaration for error messages.
71        ast::ObjectDecl const * insert( ast::ObjectDecl const * typeIdDecl ) {
72                std::string mangledName = Mangle::mangleType( typeIdDecl->type );
73                ast::ObjectDecl const *& value = instances[ mangledName ];
74                if ( value ) {
75                        if ( typeIdDecl->storage.is_extern ) {
76                                return nullptr;
77                        } else if ( !value->storage.is_extern ) {
78                                return value;
79                        }
80                }
81                value = typeIdDecl;
82                return nullptr;
83        }
84
85        ast::ObjectDecl const * lookup( ast::Type const * typeIdType ) {
86                std::string mangledName = Mangle::mangleType( typeIdType );
87                auto const it = instances.find( mangledName );
88                return ( instances.end() == it ) ? nullptr : it->second;
89        }
90};
91
92struct ExpandCastsCore final {
93        void previsit( ast::FunctionDecl const * decl );
94        void previsit( ast::StructDecl const * decl );
95        void previsit( ast::ObjectDecl const * decl );
96        ast::Expr const * postvisit( ast::VirtualCastExpr const * expr );
97
98        ast::CastExpr const * cast_to_type_id(
99                ast::Expr const * expr, unsigned int level_of_indirection );
100
101        ast::FunctionDecl const * vcast_decl = nullptr;
102        ast::StructDecl const * info_decl = nullptr;
103
104        TypeIdTable symtab;
105};
106
107void ExpandCastsCore::previsit( ast::FunctionDecl const * decl ) {
108        if ( !vcast_decl && "__cfavir_virtual_cast" == decl->name ) {
109                vcast_decl = decl;
110        }
111}
112
113void ExpandCastsCore::previsit( ast::StructDecl const * decl ) {
114        if ( !info_decl && decl->body && "__cfavir_type_info" == decl->name ) {
115                info_decl = decl;
116        }
117}
118
119void ExpandCastsCore::previsit( ast::ObjectDecl const * decl ) {
120        if ( is_type_id_object( decl ) ) {
121                // Multiple definitions should be fine because of linkonce.
122                symtab.insert( decl );
123        }
124}
125
126/// Get the base type from a pointer or reference.
127ast::Type const * getBaseType( ast::ptr<ast::Type> const & type ) {
128        if ( auto target = type.as<ast::PointerType>() ) {
129                return target->base.get();
130        } else if ( auto target = type.as<ast::ReferenceType>() ) {
131                return target->base.get();
132        } else {
133                return nullptr;
134        }
135}
136
137/// Copy newType, but give the copy the params of the oldType.
138ast::StructInstType * polyCopy(
139                ast::StructInstType const * oldType,
140                ast::StructInstType const * newType ) {
141        assert( oldType->params.size() == newType->params.size() );
142        ast::StructInstType * retType = ast::deepCopy( newType );
143        if ( ! oldType->params.empty() ) {
144                retType->params.clear();
145                for ( auto oldParams : oldType->params ) {
146                        retType->params.push_back( ast::deepCopy( oldParams ) );
147                }
148        }
149        return retType;
150}
151
152/// Follow the "head" field of the structure to get the type that is pointed
153/// to by that field.
154ast::StructInstType const * followHeadPointerType(
155                CodeLocation const & errorLocation,
156                ast::StructInstType const * oldType,
157                std::string const & fieldName ) {
158        ast::StructDecl const * oldDecl = oldType->base;
159        assert( oldDecl );
160
161        // Helper function for throwing semantic errors.
162        auto throwError = [&fieldName, &errorLocation, &oldDecl](
163                        std::string const & message ) {
164                std::string const & context = "While following head pointer of " +
165                        oldDecl->name + " named '" + fieldName + "': ";
166                SemanticError( errorLocation, context + message );
167        };
168
169        if ( oldDecl->members.empty() ) {
170                throwError( "Type has no fields." );
171        }
172        ast::ptr<ast::Decl> const & memberDecl = oldDecl->members.front();
173        assert( memberDecl );
174        ast::ObjectDecl const * fieldDecl = memberDecl.as<ast::ObjectDecl>();
175        assert( fieldDecl );
176        if ( fieldName != fieldDecl->name ) {
177                throwError( "Head field did not have expected name." );
178        }
179
180        ast::ptr<ast::Type> const & fieldType = fieldDecl->type;
181        if ( nullptr == fieldType ) {
182                throwError( "Could not get head field." );
183        }
184        auto ptrType = fieldType.as<ast::PointerType>();
185        if ( nullptr == ptrType ) {
186                throwError( "First field is not a pointer type." );
187        }
188        assert( ptrType->base );
189        auto newType = ptrType->base.as<ast::StructInstType>();
190        if ( nullptr == newType ) {
191                throwError( "First field does not point to a structure type." );
192        }
193
194        return polyCopy( oldType, newType );
195}
196
197/// Get the type-id type from a virtual type.
198ast::StructInstType const * getTypeIdType(
199                CodeLocation const & errorLocation,
200                ast::Type const * type ) {
201        auto typeInst = dynamic_cast<ast::StructInstType const *>( type );
202        if ( nullptr == typeInst ) {
203                return nullptr;
204        }
205        ast::ptr<ast::StructInstType> tableInst =
206                followHeadPointerType( errorLocation, typeInst, "virtual_table" );
207        if ( nullptr == tableInst ) {
208                return nullptr;
209        }
210        ast::StructInstType const * typeIdInst =
211                followHeadPointerType( errorLocation, tableInst, "__cfavir_typeid" );
212        return typeIdInst;
213}
214
215ast::Expr const * ExpandCastsCore::postvisit(
216                ast::VirtualCastExpr const * expr ) {
217        assertf( expr->result, "Virtual cast target not found before expansion." );
218
219        assert( vcast_decl );
220        assert( info_decl );
221
222        ast::Type const * base_type = getBaseType( expr->result );
223        if ( nullptr == base_type ) {
224                castError( expr, "Virtual cast target must be a pointer or reference type." );
225        }
226        ast::StructInstType const * type_id_type =
227                        getTypeIdType( castLocation( expr ), base_type );
228        if ( nullptr == type_id_type ) {
229                castError( expr, "Ill formed virtual cast target type." );
230        }
231        ast::ObjectDecl const * type_id = symtab.lookup( type_id_type );
232        if ( nullptr == type_id ) {
233                // I'm trying to give a different error for polymorpic types as
234                // different things can go wrong there.
235                if ( type_id_type->params.empty() ) {
236                        castError( expr, "Virtual cast does not target a virtual type." );
237                } else {
238                        castError( expr, "Virtual cast does not target a type with a "
239                                "type id (possible missing virtual table)." );
240                }
241        }
242
243        return new ast::CastExpr( expr->location,
244                new ast::ApplicationExpr( expr->location,
245                        ast::VariableExpr::functionPointer( expr->location, vcast_decl ),
246                        {
247                                cast_to_type_id(
248                                        new ast::AddressExpr( expr->location,
249                                                new ast::VariableExpr( expr->location, type_id ) ),
250                                        1 ),
251                                cast_to_type_id( expr->arg, 2 ),
252                        }
253                ),
254                ast::deepCopy( expr->result )
255        );
256}
257
258ast::CastExpr const * ExpandCastsCore::cast_to_type_id(
259                ast::Expr const * expr, unsigned int level_of_indirection ) {
260        assert( info_decl );
261        ast::Type * type = new ast::StructInstType( info_decl, ast::CV::Const );
262        for ( unsigned int i = 0 ; i < level_of_indirection ; ++i ) {
263                type = new ast::PointerType( type );
264        }
265        return new ast::CastExpr( expr->location, expr, type );
266}
267
268} // namespace
269
270void expandCasts( ast::TranslationUnit & translationUnit ) {
271        ast::Pass<ExpandCastsCore>::run( translationUnit );
272}
273
274} // namespace Virtual
Note: See TracBrowser for help on using the repository browser.