source: src/Validate/ReplaceTypedef.cpp @ 81e768d

Last change on this file since 81e768d was 81e768d, checked in by Michael Brooks <mlbrooks@…>, 3 weeks 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.