source: src/AST/SymbolTable.hpp @ b9c06b98

Last change on this file since b9c06b98 was b9fe89b, checked in by Michael Brooks <mlbrooks@…>, 17 months ago

Make the symbol table's error-checking times explicit.

Previously, error checking happened on all WithSymbolTable? uses. Error checking means having a symbol-table add operation potentially cause a user-visible error report. Now, this only happens on the resolver pass's symbol table, while other passes' run in an "assert no errors can happen" mode.

An "ignore errors for now" mode is implemented too, which will be used in upcoming commits, for pre-resolver passes that use the symbol table.

  • Property mode set to 100644
File size: 10.9 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// SymbolTable.hpp --
8//
9// Author           : Aaron B. Moss
10// Created On       : Wed May 29 11:00:00 2019
11// Last Modified By : Aaron B. Moss
12// Last Modified On : Wed May 29 11:00:00 2019
13// Update Count     : 1
14//
15
16#pragma once
17
18#include <memory>                  // for shared_ptr, enable_shared_from_this
19#include <vector>
20
21#include "Fwd.hpp"
22#include "Node.hpp"                // for ptr, readonly
23#include "Common/CodeLocation.h"
24#include "Common/PersistentMap.h"
25
26namespace ResolvExpr {
27        class Cost;
28}
29
30namespace ast {
31
32/// Builds and stores the symbol table, mapping identifiers to declarations.
33class SymbolTable final : public std::enable_shared_from_this<ast::SymbolTable> {
34public:
35        /// special functions stored in dedicated tables, with different lookup keys
36        enum SpecialFunctionKind {CTOR, DTOR, ASSIGN, NUMBER_OF_KINDS};
37        static SpecialFunctionKind getSpecialFunctionKind(const std::string & name);
38
39        /// Stored information about a declaration
40        struct IdData {
41                readonly<DeclWithType> id = nullptr;  ///< Identifier of declaration
42                readonly<Expr> baseExpr = nullptr;    ///< Implied containing aggregate (from WithExpr)
43                readonly<Decl> deleter = nullptr;     ///< Node deleting this declaration (if non-null)
44                unsigned long scope = 0;              ///< Scope of identifier
45
46                IdData() = default;
47                IdData( const DeclWithType * i, const Expr * base, const Decl * del, unsigned long s )
48                : id( i ), baseExpr( base ), deleter( del ), scope( s ) {}
49
50                /// Modify an existing node with a new deleter
51                IdData( const IdData & o, const Decl * del )
52                : id( o.id ), baseExpr( o.baseExpr ), deleter( del ), scope( o.scope ) {}
53
54                /// Constructs an expression referring to this identifier.
55                /// Increments `cost` by cost of reference conversion
56                Expr * combine( const CodeLocation & loc, ResolvExpr::Cost & cost ) const;
57        };
58
59private:
60        /// wraps a reference to D with a scope
61        template<typename D>
62        struct scoped {
63                readonly<D> decl;     ///< wrapped declaration
64                unsigned long scope;  ///< scope of this declaration
65
66                scoped(const D * d, unsigned long s) : decl(d), scope(s) {}
67        };
68
69        using MangleTable = PersistentMap< std::string, IdData >;
70        using IdTable = PersistentMap< std::string, MangleTable::Ptr >;
71        using TypeTable = PersistentMap< std::string, scoped<NamedTypeDecl> >;
72        using StructTable = PersistentMap< std::string, scoped<StructDecl> >;
73        using EnumTable = PersistentMap< std::string, scoped<EnumDecl> >;
74        using UnionTable = PersistentMap< std::string, scoped<UnionDecl> >;
75        using TraitTable = PersistentMap< std::string, scoped<TraitDecl> >;
76
77        IdTable::Ptr idTable;          ///< identifier namespace
78        TypeTable::Ptr typeTable;      ///< type namespace
79        StructTable::Ptr structTable;  ///< struct namespace
80        EnumTable::Ptr enumTable;      ///< enum namespace
81        UnionTable::Ptr unionTable;    ///< union namespace
82        TraitTable::Ptr traitTable;    ///< trait namespace
83        IdTable::Ptr specialFunctionTable[NUMBER_OF_KINDS];
84
85        // using SpecialFuncTable = PersistentMap< std::string, IdTable::Ptr >; // fname (ctor/dtor/assign) - otypekey
86        // SpecialFuncTable::Ptr specialFuncTable;
87
88        using Ptr = std::shared_ptr<const SymbolTable>;
89
90        Ptr prevScope;                 ///< Indexer for parent scope
91        unsigned long scope;           ///< Scope index of this indexer
92        unsigned long repScope;        ///< Scope index of currently represented scope
93
94public:
95
96        /// Mode to control when (during which pass) user-caused name-declaration errors get reported.
97        /// The default setting `AssertClean` supports, "I expect all user-caused errors to have been
98        /// reported by now," or, "I wouldn't know what to do with an error; are there even any here?"
99        enum ErrorDetection {
100                AssertClean,               ///< invalid user decls => assert fails during addFoo (default)
101                ValidateOnAdd,             ///< invalid user decls => calls SemanticError during addFoo
102                IgnoreErrors               ///< acts as if unspecified decls were removed, forcing validity
103        };
104
105        explicit SymbolTable(
106                ErrorDetection             ///< mode for the lifetime of the symbol table (whole pass)
107        );
108        SymbolTable() : SymbolTable(AssertClean) {}
109        ~SymbolTable();
110
111        ErrorDetection getErrorMode() const {
112                return errorMode;
113        }
114
115        // when using an indexer manually (e.g., within a mutator traversal), it is necessary to
116        // tell the indexer explicitly when scopes begin and end
117        void enterScope();
118        void leaveScope();
119
120        /// Gets all declarations with the given ID
121        std::vector<IdData> lookupId( const std::string &id ) const;
122        /// Gets special functions associated with a type; if no key is given, returns everything
123        std::vector<IdData> specialLookupId( SpecialFunctionKind kind, const std::string & otypeKey = "" ) const;
124        /// Gets the top-most type declaration with the given ID
125        const NamedTypeDecl * lookupType( const std::string &id ) const;
126        /// Gets the top-most struct declaration with the given ID
127        const StructDecl * lookupStruct( const std::string &id ) const;
128        /// Gets the top-most enum declaration with the given ID
129        const EnumDecl * lookupEnum( const std::string &id ) const;
130        /// Gets the top-most union declaration with the given ID
131        const UnionDecl * lookupUnion( const std::string &id ) const;
132        /// Gets the top-most trait declaration with the given ID
133        const TraitDecl * lookupTrait( const std::string &id ) const;
134
135        /// Gets the type declaration with the given ID at global scope
136        const NamedTypeDecl * globalLookupType( const std::string &id ) const;
137        /// Gets the struct declaration with the given ID at global scope
138        const StructDecl * globalLookupStruct( const std::string &id ) const;
139        /// Gets the union declaration with the given ID at global scope
140        const UnionDecl * globalLookupUnion( const std::string &id ) const;
141        /// Gets the enum declaration with the given ID at global scope
142        const EnumDecl * globalLookupEnum( const std::string &id ) const;
143
144        /// Adds an identifier declaration to the symbol table
145        void addId( const DeclWithType * decl, const Expr * baseExpr = nullptr );
146        /// Adds a deleted identifier declaration to the symbol table
147        void addDeletedId( const DeclWithType * decl, const Decl * deleter );
148
149        /// Adds a type to the symbol table
150        void addType( const NamedTypeDecl * decl );
151        /// Adds a struct declaration to the symbol table by name
152        void addStruct( const std::string & id );
153        /// Adds a struct declaration to the symbol table
154        void addStruct( const StructDecl * decl );
155        /// Adds an enum declaration to the symbol table
156        void addEnum( const EnumDecl * decl );
157        /// Adds a union declaration to the symbol table by name
158        void addUnion( const std::string & id );
159        /// Adds a union declaration to the symbol table
160        void addUnion( const UnionDecl * decl );
161        /// Adds a trait declaration to the symbol table
162        void addTrait( const TraitDecl * decl );
163
164        /// adds all of the IDs from WithStmt exprs
165        void addWith( const std::vector< ptr<Expr> > & withExprs, const Decl * withStmt );
166
167        /// convenience function for adding a list of Ids to the indexer
168        void addIds( const std::vector< ptr<DeclWithType> > & decls );
169
170        /// convenience function for adding a list of forall parameters to the indexer
171        void addTypes( const std::vector< ptr<TypeDecl> > & tds );
172
173        /// convenience function for adding all of the declarations in a function type to the indexer
174        void addFunction( const FunctionDecl * );
175
176private:
177        void OnFindError( CodeLocation location, std::string error ) const;
178
179        template< typename T >
180        void OnFindError( const T * obj, const std::string & error ) const {
181                OnFindError( obj->location, toString( error, obj ) );
182        }
183
184        template< typename T >
185        void OnFindError( CodeLocation location, const T * obj, const std::string & error ) const {
186                OnFindError( location, toString( error, obj ) );
187        }
188
189        /// Ensures that a proper backtracking scope exists before a mutation
190        void lazyInitScope();
191
192        /// Gets the symbol table at a given scope
193        const SymbolTable * atScope( unsigned long i ) const;
194
195        /// Removes matching autogenerated constructors and destructors so that they will not be
196        /// selected. If returns false, passed decl should not be added.
197        bool removeSpecialOverrides( IdData & decl, MangleTable::Ptr & mangleTable );
198
199        /// Error detection mode given at construction (pass-specific).
200        /// Logically const, except that the symbol table's push-pop is achieved by autogenerated
201        /// assignment onto self.  The feield is left motuable to keep this code-gen simple.
202        /// Conceptual constness is preserved by all SymbolTable in a stack sharing the same mode.
203        ErrorDetection errorMode;
204
205        /// Options for handling identifier conflicts.
206        /// Varies according to AST location during traversal: captures semantics of the construct
207        /// being visited as "would shadow" vs "must not collide."
208        /// At a given AST location, is the same for every pass.
209        struct OnConflict {
210                enum {
211                        Error,  ///< Follow the current pass's ErrorDetection mode (may throw a semantic error)
212                        Delete  ///< Delete the earlier version with the delete statement
213                } mode;
214                const Decl * deleter;  ///< Statement that deletes this expression
215
216        private:
217                OnConflict() : mode(Error), deleter(nullptr) {}
218                OnConflict( const Decl * d ) : mode(Delete), deleter(d) {}
219        public:
220                OnConflict( const OnConflict& ) = default;
221
222                static OnConflict error() { return {}; }
223                static OnConflict deleteWith( const Decl * d ) { return { d }; }
224        };
225
226        /// true if the existing identifier conflicts with the added identifier
227        bool addedIdConflicts(
228                const IdData & existing, const DeclWithType * added, OnConflict handleConflicts,
229                const Decl * deleter );
230
231        /// true if redeclaration conflict between two types
232        bool addedTypeConflicts( const NamedTypeDecl * existing, const NamedTypeDecl * added ) const;
233
234        /// true if redeclaration conflict between two aggregate declarations
235        bool addedDeclConflicts( const AggregateDecl * existing, const AggregateDecl * added ) const;
236
237        /// common code for addId, addDeletedId, etc.
238        void addIdCommon(
239                const DeclWithType * decl, OnConflict handleConflicts,
240                const Expr * baseExpr = nullptr, const Decl * deleter = nullptr );
241
242        /// common code for addId when special decls are placed into separate tables
243        void addIdToTable(
244                const DeclWithType * decl, const std::string & lookupKey,
245                IdTable::Ptr & idTable, OnConflict handleConflicts,
246                const Expr * baseExpr = nullptr, const Decl * deleter = nullptr);
247
248        /// adds all of the members of the Aggregate (addWith helper)
249        void addMembers( const AggregateDecl * aggr, const Expr * expr, OnConflict handleConflicts );
250
251        /// returns true if there exists a declaration with C linkage and the given name with the same mangled name
252        bool hasCompatibleCDecl( const std::string &id, const std::string &mangleName ) const;
253        /// returns true if there exists a declaration with C linkage and the given name with a different mangled name
254        bool hasIncompatibleCDecl( const std::string &id, const std::string &mangleName ) const;
255};
256
257}
258
259
260// Local Variables: //
261// tab-width: 4 //
262// mode: c++ //
263// compile-command: "make install" //
264// End: //
Note: See TracBrowser for help on using the repository browser.