-
Notifications
You must be signed in to change notification settings - Fork 5
Multi-decl overhaul (including inline struct fixes) #657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 44 commits
eeab8d1
b7a119a
dcf9da0
4bf6ff9
8184556
c2ea286
17de215
9dc4968
fb819ef
20b23b8
9af2fe9
500d870
52db941
a4c4ad1
843cad0
755f651
faf5006
d3a48d6
8839ad1
6bbf9ca
51e777a
5f69ae8
c91ba7b
ff1ae35
ccdc611
4de64f7
6032aaf
6fb03df
d926bd6
56d7303
3666d95
cd6cccb
e8ad64c
8d1735a
ae9e314
740390e
e6a0d4d
e89ece7
d75748f
77145eb
d3220b2
86b1acc
74685c6
2073f33
13c0d77
0d578bc
e594faa
c9888f2
220234c
0c2b38c
d3de950
f7ae158
f18cc23
d437bad
4c85580
5ed5ad3
eea9b94
36e262a
ab2d1a5
0bbfe7f
a83daa6
bc9f3bc
2a58731
31b82a6
aff9f80
f7ff59f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
//=--MultiDecls.h-------------------------------------------------*- C++-*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// Code to deal with "multi-decls": constructs in which one or more identifiers | ||
// are declared in a comma-separated list based on a single type "on the left". | ||
// A simple example: | ||
// | ||
// struct my_struct x, *p; | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_3C_MULTIDECLS_H | ||
#define LLVM_CLANG_3C_MULTIDECLS_H | ||
|
||
#include "clang/3C/PersistentSourceLoc.h" | ||
#include "clang/AST/Decl.h" | ||
#include "clang/AST/Type.h" | ||
#include "clang/AST/RecursiveASTVisitor.h" | ||
#include "llvm/ADT/Optional.h" | ||
|
||
using namespace clang; | ||
|
||
// Some more information about multi-decls in the context of 3C: | ||
// | ||
// The "members" of a given multi-decl may be ordinary variables (VarDecls), | ||
// struct/union fields (FieldDecls), or typedefs (TypedefDecls), but all members | ||
// of a given multi-decl are of the same kind. | ||
// | ||
// If the "left type" of a multi-decl is a TagDecl, it may have an inline | ||
// definition; if it does, then the TagDecl may be unnamed. Examples: | ||
// | ||
// struct my_struct { int *y; } x, *p; | ||
// struct { int *y; } x, *p; | ||
// | ||
// Multi-decls (especially those with inline TagDecls) have historically been | ||
// tricky for 3C to rewrite. If the type of one member becomes a _Ptr (or | ||
// similar), then the left type of the members is no longer the same, so the | ||
// multi-decl must be broken up, for example: | ||
// | ||
// struct my_struct x; | ||
// _Ptr<struct my_struct> p; | ||
// | ||
// To keep the logic simpler, if 3C needs to change the type of at least one | ||
// member of a multi-decl, it breaks up all members of the multi-decl into | ||
// separate declarations. To preserve SourceLocations as much as possible and | ||
// avoid interfering with rewrites to any other constructs in the multi-decl | ||
// (e.g., within existing initializer expressions), this breakup is performed by | ||
// replacing the commas with semicolons in place and inserting additional | ||
// occurrences of the left type and any common qualifiers as needed. | ||
// | ||
// If there is an inline TagDecl, it is separated too and moved out of any | ||
// containing RecordDecl to avoid a compiler warning, and if the TagDecl is | ||
// unnamed, it is given an automatically generated name so that it can be | ||
// referenced by the new, separate declarations of the multi-decl members. | ||
// Example: | ||
// | ||
// static struct { int *y; } x, *p: | ||
// | ||
// -> | ||
// | ||
// struct x_struct_1 { _Ptr<int> y; }; | ||
// static struct x_struct_1 x; | ||
// static _Ptr<struct x_struct_1> p; | ||
// | ||
// Exception: In a typedef multi-decl, if the _first_ member refers to the | ||
// TagDecl itself (not a pointer to it, etc.), then 3C uses that name for the | ||
// TagDecl rather than generating a new one. This produces nicer output for the | ||
// idiom: | ||
// | ||
// typedef struct { int *y; } FOO, *PFOO; | ||
// | ||
// -> | ||
// | ||
// typedef struct { _Ptr<int> y; } FOO; | ||
// typedef _Ptr<FOO> PFOO; | ||
// | ||
// The multi-decl code is used even for "multi-decls" of VarDecls, FieldDecls, | ||
// or TypedefDecls that have only a single member to avoid having to maintain a | ||
// separate code path for them. But a multi-decl always has at least one member; | ||
// a pure TagDecl such as `struct my_struct { int *y; };` is _not_ considered a | ||
// multi-decl. ParmVarDecls are handled differently. In fact, ParmVarDecls with | ||
// inline TagDecls are known to be handled poorly, but that's a rare and poor | ||
// practice and it's not easy to handle them better. | ||
|
||
// Currently, we automatically generate a name for every unnamed TagDecl defined | ||
// in a multi-decl and use the name in ConstraintVariables, but we only insert | ||
// the name into the definition if the multi-decl gets rewritten for some other | ||
// reason. This solves the common case of allowing the types of all the | ||
// multi-decl members to refer to the TagDecl, but it doesn't address cases in | ||
// which 3C might need to insert a reference to the unnamed TagDecl elsewhere | ||
// even if the multi-decl isn't being rewritten. In these cases, 3C typically | ||
// uses the generated name even though it is not defined, causing a compile | ||
// error that the user has to correct manually. The problematic cases include: | ||
// | ||
// - Type argument insertion. TypeVariableEntry has a check for | ||
// `isTypeAnonymous`, but it has at least one bug (it misses double pointers). | ||
// | ||
// - Cast insertion, potentially. I was unable to find an example, but that | ||
// doesn't mean it will never happen, especially with future changes to the | ||
// code. | ||
// | ||
// - Typedef itype insertion. | ||
// | ||
// One approach to try to rule out all of these bugs at once would be to | ||
// preemptively rewrite all multi-decls containing unnamed TagDecls, but those | ||
// changes might be undesirable or could even cause errors in the presence of | ||
// macros, etc. Or we could try to add the necessary code so that insertion of a | ||
// reference to an unnamed TagDecl would trigger insertion of the name into the | ||
// definition. For now, we don't deal with the problem. | ||
|
||
// Implementation note: The Clang AST does not represent multi-decls explicitly | ||
// (except in functions, where they are represented by DeclStmts). In other | ||
// contexts, we detect them based on the property that the beginning | ||
// SourceLocation of all the members is the same. And as long as we are making | ||
// this assumption, we use it in functions too rather than having a separate | ||
// code path that looks for DeclStmts. | ||
|
||
// NamedDecl is the nearest common superclass of all Decl subtypes that can be | ||
// multi-decl members. There is no enforcement that a MultiDeclMemberDecl is | ||
// actually one of the allowed subtypes, so use of the MultiDeclMemberDecl | ||
// typedef serves as documentation only. (If we wanted to enforce it, we'd need | ||
// a wrapper object of some kind, which currently seems to be more trouble than | ||
// it's worth.) | ||
typedef NamedDecl MultiDeclMemberDecl; | ||
|
||
// Returns D if it can be a multi-decl member, otherwise null. | ||
MultiDeclMemberDecl *getAsMultiDeclMember(Decl *D); | ||
|
||
// Helpers to cope with the different APIs to do corresponding things with a | ||
// TypedefDecl or DeclaratorDecl. | ||
QualType getTypeOfMultiDeclMember(MultiDeclMemberDecl *MMD); | ||
TypeSourceInfo *getTypeSourceInfoOfMultiDeclMember(MultiDeclMemberDecl *MMD); | ||
|
||
struct MultiDeclInfo { | ||
// The TagDecl that is defined inline in the multi-decl and needs to be split | ||
// from it during rewriting, if any, otherwise null. In a case like | ||
// `typedef struct { ... } T`, there is an inline tag definition but we don't | ||
// need to split it out, so this will be null. | ||
TagDecl *TagDefToSplit = nullptr; | ||
|
||
// True if the base type was an unnamed TagDecl defined inline for which we | ||
// are using a new name. Note that TagDefToSplit can be nonnull and | ||
// BaseTypeRenamed can be false if the inline TagDecl was named, and the | ||
// reverse can occur in the `typedef struct { ... } T` case. | ||
bool BaseTypeRenamed = false; | ||
|
||
// The members of the multi-decl in their original order. | ||
std::vector<MultiDeclMemberDecl *> Members; | ||
|
||
// Set by DeclRewriter::rewriteMultiDecl after it rewrites the entire | ||
// multi-decl to ensure that it doesn't try to do so more than once if | ||
// multiple members needed changes. | ||
// REVIEW: A design argument could be made that this flag doesn't belong here | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
// and the rewriter should instead keep a visited set or something like that. | ||
bool AlreadyRewritten = false; | ||
}; | ||
|
||
// All multi-decls, keyed by the common beginning source location of their | ||
// members. Note that the beginning source location of TagDefToSplit may be | ||
// later if there is a keyword such as `static` or `typedef` in between. | ||
typedef std::map<SourceLocation, MultiDeclInfo> TUMultiDeclsInfo; | ||
|
||
class MultiDeclsInfo { | ||
mattmccutchen-cci marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private: | ||
// Set of TagDecl names already used at least once in the program, so we can | ||
// avoid colliding with them. | ||
std::set<std::string> UsedTagNames; | ||
|
||
// Map from PSL of an originally unnamed TagDecl to the string that should be | ||
// used to refer to its type, so we can ensure that names are assigned | ||
// consistently when 3C naively rewrites the same header file multiple times | ||
// as part of different translation units (see | ||
// https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-804283984). | ||
// Note that unlike UsedTagNames, this includes the tag kind keyword (such as | ||
// `struct`), except when we use an existing typedef (which doesn't require a | ||
// tag keyword). | ||
std::map<PersistentSourceLoc, std::string> AssignedTagTypeStrs; | ||
|
||
std::map<ASTContext *, TUMultiDeclsInfo> TUInfos; | ||
|
||
// Recursive helpers. | ||
void findUsedTagNames(DeclContext *DC); | ||
void findMultiDecls(DeclContext *DC, ASTContext &Context); | ||
|
||
public: | ||
void findUsedTagNames(ASTContext &Context); | ||
void findMultiDecls(ASTContext &Context); | ||
llvm::Optional<std::string> getTypeStrOverride(const Type *Ty, ASTContext &C); | ||
MultiDeclInfo *findContainingMultiDecl(MultiDeclMemberDecl *MMD); | ||
bool wasBaseTypeRenamed(Decl *D); | ||
}; | ||
|
||
#endif // LLVM_CLANG_3C_MULTIDECLS_H |
Uh oh!
There was an error while loading. Please reload this page.