source: src/Virtual/ExpandCasts.cpp@ b9b8643

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

Updated the rest of the names in src/ (except for the generated files).

  • 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.cpp --
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.hpp"
17
18#include <cassert> // for assert, assertf
19#include <iterator> // for back_inserter, inserter
20#include <string> // for string, allocator, operator==, o...
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.hpp" // for ScopedMap
27#include "Common/SemanticError.hpp" // for SemanticError
28#include "SymTab/Mangler.hpp" // 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.