source: src/Virtual/ExpandCasts.cc@ 8d182b1

Last change on this file since 8d182b1 was c6b4432, checked in by Andrew Beach <ajbeach@…>, 23 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.