source: src/Virtual/ExpandCasts.cc

Last change on this file was b1f2007, checked in by Peter A. Buhr <pabuhr@…>, 4 months ago

first attempt at simplifying SemanticError? and its usage

  • Property mode set to 100644
File size: 8.4 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 : Peter A. Buhr
12// Last Modified On : Mon Nov 27 09:28:20 2023
13// Update Count     : 10
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]( std::string const & message ) {
163                SemanticError( errorLocation, "While following head pointer of %s named \"%s\": %s",
164                                           oldDecl->name.c_str(), fieldName.c_str(), message.c_str() );
165        };
166
167        if ( oldDecl->members.empty() ) {
168                throwError( "Type has no fields." );
169        }
170        ast::ptr<ast::Decl> const & memberDecl = oldDecl->members.front();
171        assert( memberDecl );
172        ast::ObjectDecl const * fieldDecl = memberDecl.as<ast::ObjectDecl>();
173        assert( fieldDecl );
174        if ( fieldName != fieldDecl->name ) {
175                throwError( "Head field did not have expected name." );
176        }
177
178        ast::ptr<ast::Type> const & fieldType = fieldDecl->type;
179        if ( nullptr == fieldType ) {
180                throwError( "Could not get head field." );
181        }
182        auto ptrType = fieldType.as<ast::PointerType>();
183        if ( nullptr == ptrType ) {
184                throwError( "First field is not a pointer type." );
185        }
186        assert( ptrType->base );
187        auto newType = ptrType->base.as<ast::StructInstType>();
188        if ( nullptr == newType ) {
189                throwError( "First field does not point to a structure type." );
190        }
191
192        return polyCopy( oldType, newType );
193}
194
195/// Get the type-id type from a virtual type.
196ast::StructInstType const * getTypeIdType(
197                CodeLocation const & errorLocation,
198                ast::Type const * type ) {
199        auto typeInst = dynamic_cast<ast::StructInstType const *>( type );
200        if ( nullptr == typeInst ) {
201                return nullptr;
202        }
203        ast::ptr<ast::StructInstType> tableInst =
204                followHeadPointerType( errorLocation, typeInst, "virtual_table" );
205        if ( nullptr == tableInst ) {
206                return nullptr;
207        }
208        ast::StructInstType const * typeIdInst =
209                followHeadPointerType( errorLocation, tableInst, "__cfavir_typeid" );
210        return typeIdInst;
211}
212
213ast::Expr const * ExpandCastsCore::postvisit(
214                ast::VirtualCastExpr const * expr ) {
215        assertf( expr->result, "Virtual cast target not found before expansion." );
216
217        assert( vcast_decl );
218        assert( info_decl );
219
220        ast::Type const * base_type = getBaseType( expr->result );
221        if ( nullptr == base_type ) {
222                castError( expr, "Virtual cast target must be a pointer or reference type." );
223        }
224        ast::StructInstType const * type_id_type =
225                        getTypeIdType( castLocation( expr ), base_type );
226        if ( nullptr == type_id_type ) {
227                castError( expr, "Ill formed virtual cast target type." );
228        }
229        ast::ObjectDecl const * type_id = symtab.lookup( type_id_type );
230        if ( nullptr == type_id ) {
231                // I'm trying to give a different error for polymorpic types as
232                // different things can go wrong there.
233                if ( type_id_type->params.empty() ) {
234                        castError( expr, "Virtual cast does not target a virtual type." );
235                } else {
236                        castError( expr, "Virtual cast does not target a type with a "
237                                "type id (possible missing virtual table)." );
238                }
239        }
240
241        return new ast::CastExpr( expr->location,
242                new ast::ApplicationExpr( expr->location,
243                        ast::VariableExpr::functionPointer( expr->location, vcast_decl ),
244                        {
245                                cast_to_type_id(
246                                        new ast::AddressExpr( expr->location,
247                                                new ast::VariableExpr( expr->location, type_id ) ),
248                                        1 ),
249                                cast_to_type_id( expr->arg, 2 ),
250                        }
251                ),
252                ast::deepCopy( expr->result )
253        );
254}
255
256ast::CastExpr const * ExpandCastsCore::cast_to_type_id(
257                ast::Expr const * expr, unsigned int level_of_indirection ) {
258        assert( info_decl );
259        ast::Type * type = new ast::StructInstType( info_decl, ast::CV::Const );
260        for ( unsigned int i = 0 ; i < level_of_indirection ; ++i ) {
261                type = new ast::PointerType( type );
262        }
263        return new ast::CastExpr( expr->location, expr, type );
264}
265
266} // namespace
267
268void expandCasts( ast::TranslationUnit & translationUnit ) {
269        ast::Pass<ExpandCastsCore>::run( translationUnit );
270}
271
272} // namespace Virtual
Note: See TracBrowser for help on using the repository browser.