source: src/Validate/ReplaceTypedef.cpp@ fbb5bdd

Last change on this file since fbb5bdd was 81e768d, checked in by Michael Brooks <mlbrooks@…>, 10 months ago

Fix #276; add support for c-array parameters using dependent lengths.

Without this fix, declarations like

void f( int m, int n, float[m][n] );

would either

  • generate bad C code, with unmangled variable names appearing in the function definition, or
  • refuse to resolve a valid-c call of such a function.

tests/array-collections/c-dependent: add direct tests of such cases
tests/tuplearray: activate and expand cases which were blocked on #276
tests/array: activate case fm5y, which was blocked on #276; [noise] adjust source line numbers in .expect
tests/typedefRedef: expand coverage of "error, an array detail is different" cases; [noise] adjust source line numbers in .expect
tests/functions: [noise] adjust .expect to have resolved array sizes (extra casts) in the diffed code dump

The fix is:

  • (ResolvExpr/ResolveTypeof, ResolvExpr/Resolver) Resolve the dimension expressions, where they were missed.
  • (ResolvExpr/Resolver) Prevent dimension expressions that are bound to other parameters from escaping in the function's type, to where they are out of scope. In the f example above, redact the type shown to callers from void (*)(int, int, float[m][n]) to void (*)(int, int, float[][*]).
  • (ResolvExpr/Unify) Relax the matching rules for such a type, when used at a call site, letting the patameters wildcard type match with the concrete type in scope at the caller's side.
  • (Validate/ReplaceTypedef) Apply the former, stricter matching rules to the one place where they are still needed: detecting inconsistent typedefs.
  • Property mode set to 100644
File size: 13.6 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2018 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// ReplaceTypedef.cpp -- Fill in all typedefs with the underlying type.
8//
9// Author : Andrew Beach
10// Created On : Tue Jun 29 14:59:00 2022
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Thu Dec 14 16:11:51 2023
13// Update Count : 4
14//
15
16#include "ReplaceTypedef.hpp"
17
18#include "AST/Copy.hpp"
19#include "AST/Pass.hpp"
20#include "Common/ScopedMap.hpp"
21#include "Common/UniqueName.hpp"
22#include "ResolvExpr/Unify.hpp"
23
24namespace Validate {
25
26namespace {
27
28struct ReplaceTypedefCore final :
29 public ast::WithCodeLocation,
30 public ast::WithDeclsToAdd,
31 public ast::WithGuards,
32 public ast::WithShortCircuiting,
33 public ast::WithVisitorRef<ReplaceTypedefCore> {
34
35 void previsit( ast::QualifiedType const * );
36 ast::Type const * postvisit( ast::QualifiedType const * );
37 ast::Type const * postvisit( ast::TypeInstType const * );
38 ast::Decl const * postvisit( ast::TypedefDecl const * );
39 void previsit( ast::TypeDecl const * );
40 void previsit( ast::FunctionDecl const * );
41 void previsit( ast::ObjectDecl const * );
42 ast::DeclWithType const * postvisit( ast::ObjectDecl const * );
43
44 void previsit( ast::CastExpr const * );
45 void previsit( ast::CompoundStmt const * );
46 void postvisit( ast::CompoundStmt const * );
47
48 ast::StructDecl const * previsit( ast::StructDecl const * );
49 ast::UnionDecl const * previsit( ast::UnionDecl const * );
50 void previsit( ast::EnumDecl const * );
51 void previsit( ast::TraitDecl const * );
52
53 template<typename AggrDecl>
54 void addImplicitTypedef( AggrDecl * aggDecl );
55 template<typename AggrDecl>
56 AggrDecl const * handleAggregate( AggrDecl const * aggDecl );
57
58 using TypedefDeclPtr = ast::ptr<ast::TypedefDecl>;
59 using TypedefMap = ScopedMap<std::string, std::pair<TypedefDeclPtr, int>>;
60 using TypeDeclMap = ScopedMap<std::string, ast::TypeDecl const *>;
61
62 TypedefMap typedefNames;
63 TypeDeclMap typedeclNames;
64 int scopeLevel;
65 bool isAtFunctionTop = false;
66};
67
68void ReplaceTypedefCore::previsit( ast::QualifiedType const * ) {
69 visit_children = false;
70}
71
72ast::Type const * ReplaceTypedefCore::postvisit(
73 ast::QualifiedType const * type ) {
74 // Replacing typedefs only makes sense for the 'oldest ancestor'
75 // of the qualified type.
76 return ast::mutate_field( type, &ast::QualifiedType::parent,
77 type->parent->accept( *visitor ) );
78}
79
80ast::Type const * ReplaceTypedefCore::postvisit(
81 ast::TypeInstType const * type ) {
82 // Instances of typedef types will come here. If it is an instance
83 // of a typedef type, link the instance to its actual type.
84 TypedefMap::const_iterator def = typedefNames.find( type->name );
85 if ( def != typedefNames.end() ) {
86 ast::Type * ret = ast::deepCopy( def->second.first->base );
87 ret->qualifiers |= type->qualifiers;
88 // We ignore certain attributes on function parameters if they arrive
89 // by typedef. GCC appears to do the same thing.
90 if ( isAtFunctionTop ) {
91 erase_if( ret->attributes, []( ast::Attribute const * attr ){
92 return !attr->isValidOnFuncParam();
93 } );
94 }
95 for ( const auto & attribute : type->attributes ) {
96 ret->attributes.push_back( attribute );
97 }
98 // Place instance parameters on the typedef'd type.
99 if ( !type->params.empty() ) {
100 auto rtt = dynamic_cast<ast::BaseInstType *>( ret );
101 if ( !rtt ) {
102 assert( location );
103 SemanticError( *location, "Cannot apply type parameters to base type of %s.", type->name.c_str() );
104 }
105 rtt->params.clear();
106 for ( auto it : type->params ) {
107 rtt->params.push_back( ast::deepCopy( it ) );
108 }
109 // Recursively fix typedefs on parameters.
110 ast::mutate_each( rtt, &ast::BaseInstType::params, *visitor );
111 }
112 return ret;
113 } else {
114 TypeDeclMap::const_iterator base = typedeclNames.find( type->name );
115 if ( base == typedeclNames.end() ) {
116 assert( location );
117 SemanticError( *location, "Use of undefined type %s.", type->name.c_str() );
118 }
119 return ast::mutate_field( type, &ast::TypeInstType::base, base->second );
120 }
121}
122struct VarLenChecker : public ast::WithShortCircuiting {
123 bool result = false;
124 void previsit( ast::FunctionType const * ) { visit_children = false; }
125 void previsit( ast::ArrayType const * at ) { result |= at->isVarLen; }
126};
127static bool hasVarLen( const ast::Type * t ) {
128 return ast::Pass<VarLenChecker>::read( t );
129}
130struct ArrayTypeExtractor {
131 std::vector<const ast::ArrayType *> result;
132 void postvisit( const ast::ArrayType * at ) {
133 result.push_back( at );
134 }
135};
136static bool dimensionPresenceMismatched( const ast::Type * t0, const ast::Type * t1) {
137 std::vector<const ast::ArrayType *> at0s = std::move(
138 ast::Pass<ArrayTypeExtractor>::read( t0 ) );
139 std::vector<const ast::ArrayType *> at1s = std::move(
140 ast::Pass<ArrayTypeExtractor>::read( t1 ) );
141 assert( at0s.size() == at1s.size() );
142 for (size_t i = 0; i < at0s.size(); i++) {
143 const ast::ArrayType * at0 = at0s[i];
144 const ast::ArrayType * at1 = at1s[i];
145 assert( ResolvExpr::typesCompatible( at0, at1 ) );
146 if ( (at0->dimension != nullptr) != (at1->dimension != nullptr) ) return true;
147 }
148 return false;
149}
150ast::Decl const * ReplaceTypedefCore::postvisit(
151 ast::TypedefDecl const * decl ) {
152 if ( 1 == typedefNames.count( decl->name ) &&
153 typedefNames[ decl->name ].second == scopeLevel ) {
154 ast::Type const * t0 = decl->base;
155 ast::Type const * t1 = typedefNames[ decl->name ].first->base;
156 // [hasVarLen]
157 // Cannot redefine VLA typedefs. Note: this is slightly incorrect,
158 // because our notion of VLAs at this point in the translator is
159 // imprecise. In particular, this will disallow redefining typedefs
160 // with arrays whose dimension is an enumerator or a cast of a
161 // constant/enumerator. The effort required to fix this corner case
162 // likely outweighs the utility of allowing it.
163 // [dimensionPresenceMismatched]
164 // Core typesCompatible logic interprets absent dimensions as wildcards,
165 // i.e. float[][*] matches float[][42].
166 // For detecting incompatible typedefs, we have to interpret them verbatim,
167 // i.e. float[] is different than float[42].
168 // But typesCompatible does assure that the pair of types is structurally
169 // consistent, outside of the dimension expressions. This assurance guards
170 // the dimension-presence traversal. So this traversal logic can (and does)
171 // assume that ArrayTypes will be encountered in analogous places.
172 if ( !ResolvExpr::typesCompatible( t0, t1 )
173 || hasVarLen( t0 )
174 || hasVarLen( t1 )
175 || dimensionPresenceMismatched( t0, t1 ) ) {
176 SemanticError( decl->location, "Cannot redefine typedef %s", decl->name.c_str() );
177 }
178 } else {
179 typedefNames[ decl->name ] =
180 std::make_pair( TypedefDeclPtr( decl ), scopeLevel );
181 }
182
183 // When a typedef is a forward declaration:
184 // > typedef struct screen SCREEN;
185 // the declaration portion must be retained:
186 // > struct screen;
187 // because the expansion of the typedef is:
188 // > void func( SCREEN * p ) -> void func( struct screen * p );
189 // hence type name "screen" must be defined.
190 // Note: qualifiers on the typedef are not used for the forward declaration.
191
192 ast::Type const * designatorType = decl->base->stripDeclarator();
193 if ( auto structType = dynamic_cast<ast::StructInstType const *>( designatorType ) ) {
194 declsToAddBefore.push_back( new ast::StructDecl(
195 decl->location, structType->name, ast::AggregateDecl::Struct, {},
196 decl->linkage ) );
197 } else if ( auto unionType = dynamic_cast<ast::UnionInstType const *>( designatorType ) ) {
198 declsToAddBefore.push_back( new ast::UnionDecl(
199 decl->location, unionType->name, {}, decl->linkage ) );
200 } else if ( auto enumType = dynamic_cast<ast::EnumInstType const *>( designatorType ) ) {
201 declsToAddBefore.push_back( new ast::EnumDecl(
202 decl->location, enumType->name, false, {}, decl->linkage,
203 ( (enumType->base) ? enumType->base->base : nullptr )
204 ) );
205 }
206 return ast::deepCopy( decl );
207}
208
209void ReplaceTypedefCore::previsit( ast::TypeDecl const * decl ) {
210 typedefNames.erase( decl->name );
211 typedeclNames.insert( decl->name, decl );
212}
213
214void ReplaceTypedefCore::previsit( ast::FunctionDecl const * ) {
215 GuardScope( typedefNames );
216 GuardScope( typedeclNames );
217 GuardValue( isAtFunctionTop ) = true;
218}
219
220void ReplaceTypedefCore::previsit( ast::ObjectDecl const * ) {
221 GuardScope( typedefNames );
222 GuardScope( typedeclNames );
223}
224
225ast::DeclWithType const * ReplaceTypedefCore::postvisit(
226 ast::ObjectDecl const * decl ) {
227 if ( ast::FunctionType const * type = decl->type.as<ast::FunctionType>() ) {
228 using DWTVector = std::vector<ast::ptr<ast::DeclWithType>>;
229 using DeclVector = std::vector<ast::ptr<ast::TypeDecl>>;
230 CodeLocation const & declLocation = decl->location;
231 UniqueName paramNamer( decl->name + "Param" );
232
233 // Replace the current object declaration with a function declaration.
234 ast::FunctionDecl const * newDecl = new ast::FunctionDecl(
235 declLocation,
236 decl->name,
237 map_range<DeclVector>( type->forall, []( const ast::TypeInstType * inst ) {
238 return ast::deepCopy( inst->base );
239 } ),
240 map_range<DWTVector>( type->assertions, []( const ast::VariableExpr * expr ) {
241 return ast::deepCopy( expr->var );
242 } ),
243 map_range<DWTVector>( type->params, [&declLocation, &paramNamer]( const ast::Type * type ) {
244 assert( type );
245 return new ast::ObjectDecl( declLocation, paramNamer.newName(), ast::deepCopy( type ) );
246 } ),
247 map_range<DWTVector>( type->returns, [&declLocation, &paramNamer]( const ast::Type * type ) {
248 assert( type );
249 return new ast::ObjectDecl( declLocation, paramNamer.newName(), ast::deepCopy( type ) );
250 } ),
251 nullptr,
252 decl->storage,
253 decl->linkage,
254 {/* attributes */},
255 decl->funcSpec
256 );
257 return newDecl;
258 }
259 return decl;
260}
261
262void ReplaceTypedefCore::previsit( ast::CastExpr const * ) {
263 GuardScope( typedefNames );
264 GuardScope( typedeclNames );
265}
266
267void ReplaceTypedefCore::previsit( ast::CompoundStmt const * ) {
268 GuardScope( typedefNames );
269 GuardScope( typedeclNames );
270 GuardValue( isAtFunctionTop ) = false;
271 scopeLevel += 1;
272}
273
274void ReplaceTypedefCore::postvisit( ast::CompoundStmt const * ) {
275 scopeLevel -= 1;
276}
277
278ast::StructDecl const * ReplaceTypedefCore::previsit( ast::StructDecl const * decl ) {
279 visit_children = false;
280 addImplicitTypedef( decl );
281 return handleAggregate( decl );
282}
283
284ast::UnionDecl const * ReplaceTypedefCore::previsit( ast::UnionDecl const * decl ) {
285 visit_children = false;
286 addImplicitTypedef( decl );
287 return handleAggregate( decl );
288}
289
290void ReplaceTypedefCore::previsit( ast::EnumDecl const * decl ) {
291 addImplicitTypedef( decl );
292}
293
294void ReplaceTypedefCore::previsit( ast::TraitDecl const * ) {
295 GuardScope( typedefNames );
296 GuardScope( typedeclNames );
297}
298
299template<typename AggrDecl>
300void ReplaceTypedefCore::addImplicitTypedef( AggrDecl * aggrDecl ) {
301 if ( 0 != typedefNames.count( aggrDecl->name ) ) {
302 return;
303 }
304 ast::Type * type = nullptr;
305 if ( auto structDecl = dynamic_cast<const ast::StructDecl *>( aggrDecl ) ) {
306 type = new ast::StructInstType( structDecl->name );
307 } else if ( auto unionDecl = dynamic_cast<const ast::UnionDecl *>( aggrDecl ) ) {
308 type = new ast::UnionInstType( unionDecl->name );
309 } else if ( auto enumDecl = dynamic_cast<const ast::EnumDecl *>( aggrDecl ) ) {
310 type = new ast::EnumInstType( enumDecl->name );
311 }
312 assert( type );
313
314 TypedefDeclPtr typeDecl = new ast::TypedefDecl( aggrDecl->location,
315 aggrDecl->name, ast::Storage::Classes(), type, aggrDecl->linkage );
316 // Add the implicit typedef to the AST.
317 declsToAddAfter.push_back( ast::deepCopy( typeDecl.get() ) );
318 // Shore the name in the map of names.
319 typedefNames[ aggrDecl->name ] =
320 std::make_pair( std::move( typeDecl ), scopeLevel );
321}
322
323template<typename AggrDecl>
324AggrDecl const * ReplaceTypedefCore::handleAggregate( AggrDecl const * decl ) {
325 SemanticErrorException errors;
326
327 ValueGuard<decltype(declsToAddBefore)> oldBeforeDecls( declsToAddBefore );
328 ValueGuard<decltype(declsToAddAfter )> oldAfterDecls( declsToAddAfter );
329 declsToAddBefore.clear();
330 declsToAddAfter.clear();
331
332 GuardScope( typedefNames );
333 GuardScope( typedeclNames );
334 decl = mutate_each( decl, &ast::AggregateDecl::params, *visitor );
335 decl = mutate_each( decl, &ast::AggregateDecl::attributes, *visitor );
336
337 auto mut = ast::mutate( decl );
338
339 std::list<ast::ptr<ast::Decl>> members;
340 // Unroll accept_all for decl->members so that implicit typedefs for
341 // nested types are added to the aggregate body.
342 for ( ast::ptr<ast::Decl> const & member : mut->members ) {
343 assert( declsToAddBefore.empty() );
344 assert( declsToAddAfter.empty() );
345 ast::Decl const * newMember = nullptr;
346 try {
347 newMember = member->accept( *visitor );
348 } catch ( SemanticErrorException & e ) {
349 errors.append( e );
350 }
351 if ( !declsToAddBefore.empty() ) {
352 members.splice( members.end(), declsToAddBefore );
353 }
354 members.push_back( newMember );
355 if ( !declsToAddAfter.empty() ) {
356 members.splice( members.end(), declsToAddAfter );
357 }
358 }
359 assert( declsToAddBefore.empty() );
360 assert( declsToAddAfter.empty() );
361 errors.throwIfNonEmpty();
362
363 mut->members.clear();
364 for ( auto member : members ) {
365 mut->members.push_back( member );
366 }
367
368 return mut;
369}
370
371} // namespace
372
373void replaceTypedef( ast::TranslationUnit & translationUnit ) {
374 ast::Pass<ReplaceTypedefCore> pass;
375 ast::accept_all( translationUnit, pass );
376 if ( pass.core.typedefNames.count( "size_t" ) ) {
377 translationUnit.global.sizeType =
378 ast::deepCopy( pass.core.typedefNames["size_t"].first->base );
379 } else {
380 // Missing the global definition, default to long unsigned int.
381 // Perhaps this should be a warning instead.
382 translationUnit.global.sizeType =
383 new ast::BasicType( ast::BasicKind::LongUnsignedInt );
384 }
385}
386
387} // namespace Validate
388
389// Local Variables: //
390// tab-width: 4 //
391// mode: c++ //
392// compile-command: "make install" //
393// End: //
Note: See TracBrowser for help on using the repository browser.