Skip to content

Commit 5f70035

Browse files
Insert itypes correctly for constant sized arrays (#702)
Given the declaration of a global variable `int foo[10]` converted with `3c -itypes-for-extern -alltypes` the resulting declaration incorrectly moved the length of the array before identifier. This changes fixes the rewriting to correctly be `int foo[10] : itype(int _Checked[10])`. This also fixes itype insertion for pointers to constant size arrays. Another change is included to pass idempotence checks for the new tests added. This change expands the source range replaced for global variable declarations to include the itype or bounds declarations if present.
1 parent c88031e commit 5f70035

File tree

9 files changed

+212
-107
lines changed

9 files changed

+212
-107
lines changed

clang/include/clang/3C/ConstraintVariables.h

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct MkStringOpts {
5252
};
5353
#define MKSTRING_OPTS(...) PACK_OPTS(MkStringOpts, __VA_ARGS__)
5454

55+
// Name for function return, for debugging
56+
#define RETVAR "$ret"
57+
5558
// Base class for ConstraintVariables. A ConstraintVariable can either be a
5659
// PointerVariableConstraint or a FunctionVariableConstraint. The difference
5760
// is that FunctionVariableConstraints have constraints on the return value
@@ -66,9 +69,23 @@ class ConstraintVariable {
6669
ConstraintVariableKind Kind;
6770

6871
protected:
72+
// A string representation for the type of this variable. Note that for
73+
// complex types (e.g., function pointer, constant sized arrays), you cannot
74+
// concatenate the type string with an identifier and expect to obtain a valid
75+
// variable declaration.
6976
std::string OriginalType;
70-
// Underlying name of the C variable this ConstraintVariable represents.
77+
// Underlying name of the C variable this ConstraintVariable represents. This
78+
// is not always a valid C identifier. It will be empty if no name was given
79+
// (e.g., some parameter declarations). It will be the predefined string
80+
// "$ret" when the ConstraintVariable represents a function return. It may
81+
// take other values if the ConstraintVariable does not represent a C
82+
// variable (e.g., explict casts and compound literals) .
7183
std::string Name;
84+
// The combination of the type and name of the represented C variable. The
85+
// combination is handled by clang library routines, so complex types
86+
// like function pointers and constant size are handled correctly. See
87+
// comments on Name for when name should be a valid identifier.
88+
std::string OriginalTypeWithName;
7289
// Set of constraint variables that have been constrained due to a
7390
// bounds-safe interface (itype). They are remembered as being constrained
7491
// so that later on we do not introduce a spurious constraint
@@ -85,9 +102,15 @@ class ConstraintVariable {
85102
bool IsForDecl;
86103

87104
// Only subclasses should call this
88-
ConstraintVariable(ConstraintVariableKind K, std::string T, std::string N)
89-
: Kind(K), OriginalType(T), Name(N), HasEqArgumentConstraints(false),
90-
ValidBoundsKey(false), IsForDecl(false) {}
105+
ConstraintVariable(ConstraintVariableKind K, std::string T, std::string N,
106+
std::string TN)
107+
: Kind(K), OriginalType(T), Name(N), OriginalTypeWithName(TN),
108+
HasEqArgumentConstraints(false), ValidBoundsKey(false),
109+
IsForDecl(false) {}
110+
111+
ConstraintVariable(ConstraintVariableKind K, QualType QT, std::string N)
112+
: ConstraintVariable(K, qtyToStr(QT), N,
113+
qtyToStr(QT, N == RETVAR ? "" : N)) {}
91114

92115
public:
93116
// Create a "for-rewriting" representation of this ConstraintVariable.
@@ -166,6 +189,7 @@ class ConstraintVariable {
166189
// Get the original type string that can be directly
167190
// used for rewriting.
168191
std::string getRewritableOriginalTy() const;
192+
std::string getOriginalTypeWithName() const;
169193
std::string getName() const { return Name; }
170194

171195
void setValidDecl() { IsForDecl = true; }
@@ -378,7 +402,7 @@ class PointerVariableConstraint : public ConstraintVariable {
378402
// other fields are initialized to default values. This is used to construct
379403
// variables for non-pointer expressions.
380404
PointerVariableConstraint(std::string Name) :
381-
ConstraintVariable(PointerVariable, "", Name), FV(nullptr),
405+
ConstraintVariable(PointerVariable, "", Name, ""), FV(nullptr),
382406
SrcHasItype(false), PartOfFuncPrototype(false), Parent(nullptr),
383407
SourceGenericIndex(-1), InferredGenericIndex(-1),
384408
IsZeroWidthArray(false), IsTypedef(false),
@@ -530,8 +554,6 @@ class PointerVariableConstraint : public ConstraintVariable {
530554
};
531555

532556
typedef PointerVariableConstraint PVConstraint;
533-
// Name for function return, for debugging
534-
#define RETVAR "$ret"
535557

536558
// This class contains a pair of PVConstraints that represent an internal and
537559
// external view of a variable for use as the parameter and return constraints
@@ -622,9 +644,10 @@ class FunctionVariableConstraint : public ConstraintVariable {
622644
const clang::ASTContext &C);
623645
FunctionVariableConstraint(clang::TypedefDecl *D, ProgramInfo &I,
624646
const clang::ASTContext &C);
625-
FunctionVariableConstraint(const clang::Type *Ty, clang::DeclaratorDecl *D,
626-
std::string N, ProgramInfo &I,
627-
const clang::ASTContext &C,
647+
648+
FunctionVariableConstraint(const clang::QualType Ty,
649+
clang::DeclaratorDecl *D, std::string N,
650+
ProgramInfo &I, const clang::ASTContext &C,
628651
TypeSourceInfo *TSI = nullptr);
629652

630653
PVConstraint *getExternalReturn() const {

clang/include/clang/3C/RewriteUtils.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ class DeclReplacement {
2828

2929
std::string getReplacement() const { return Replacement; }
3030

31-
virtual SourceRange getSourceRange(SourceManager &SM) const {
32-
return getDecl()->getSourceRange();
33-
}
31+
virtual SourceRange getSourceRange(SourceManager &SM) const;
3432

3533
// Discriminator for LLVM-style RTTI (dyn_cast<> et al.).
3634
enum DRKind {

clang/include/clang/3C/Utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ void getPrintfStringArgIndices(const clang::CallExpr *CE,
228228
int64_t getStmtIdWorkaround(const clang::Stmt *St,
229229
const clang::ASTContext &Context);
230230

231+
clang::SourceLocation getCheckedCAnnotationsEnd(const clang::Decl *D);
232+
231233
// Shortcut for the getCustomDiagID + Report sequence to report a custom
232234
// diagnostic as we currently do in 3C.
233235
//

clang/lib/3C/ConstraintVariables.cpp

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ std::string ConstraintVariable::getRewritableOriginalTy() const {
4646
return OrigTyString;
4747
}
4848

49+
std::string ConstraintVariable::getOriginalTypeWithName() const {
50+
if (Name == RETVAR)
51+
return getRewritableOriginalTy();
52+
return OriginalTypeWithName;
53+
}
54+
4955
PointerVariableConstraint *PointerVariableConstraint::getWildPVConstraint(
5056
Constraints &CS, const std::string &Rsn, PersistentSourceLoc *PSL) {
5157
auto *WildPVC = new PointerVariableConstraint("wildvar");
@@ -121,9 +127,9 @@ PointerVariableConstraint *PointerVariableConstraint::addAtomPVConstraint(
121127
PointerVariableConstraint::PointerVariableConstraint(
122128
PointerVariableConstraint *Ot)
123129
: ConstraintVariable(ConstraintVariable::PointerVariable, Ot->OriginalType,
124-
Ot->Name), BaseType(Ot->BaseType), Vars(Ot->Vars),
125-
SrcVars(Ot->SrcVars), FV(Ot->FV), QualMap(Ot->QualMap),
126-
ArrSizes(Ot->ArrSizes), ArrSizeStrs(Ot->ArrSizeStrs),
130+
Ot->Name, Ot->OriginalTypeWithName),
131+
BaseType(Ot->BaseType), Vars(Ot->Vars), SrcVars(Ot->SrcVars), FV(Ot->FV),
132+
QualMap(Ot->QualMap), ArrSizes(Ot->ArrSizes), ArrSizeStrs(Ot->ArrSizeStrs),
127133
SrcHasItype(Ot->SrcHasItype), ItypeStr(Ot->ItypeStr),
128134
PartOfFuncPrototype(Ot->PartOfFuncPrototype), Parent(Ot),
129135
BoundsAnnotationStr(Ot->BoundsAnnotationStr),
@@ -206,7 +212,7 @@ PointerVariableConstraint::PointerVariableConstraint(
206212
const ASTContext &C, std::string *InFunc, int ForceGenericIndex,
207213
bool PotentialGeneric,
208214
bool VarAtomForChecked, TypeSourceInfo *TSInfo, const QualType &ITypeT)
209-
: ConstraintVariable(ConstraintVariable::PointerVariable, qtyToStr(QT), N),
215+
: ConstraintVariable(ConstraintVariable::PointerVariable, QT, N),
210216
FV(nullptr), SrcHasItype(false), PartOfFuncPrototype(InFunc != nullptr),
211217
Parent(nullptr) {
212218
QualType QTy = QT;
@@ -512,7 +518,7 @@ PointerVariableConstraint::PointerVariableConstraint(
512518
// tn fname = ...,
513519
// where tn is the typedef'ed type name.
514520
// There is possibly something more elegant to do in the code here.
515-
FV = new FVConstraint(Ty, IsDeclTy ? D : nullptr, IsTypedef ? "" : N, I, C,
521+
FV = new FVConstraint(QTy, IsDeclTy ? D : nullptr, IsTypedef ? "" : N, I, C,
516522
TSInfo);
517523

518524
// Get a string representing the type without pointer and array indirection.
@@ -965,8 +971,11 @@ PointerVariableConstraint::mkString(Constraints &CS,
965971
}
966972

967973
// No space after itype.
968-
if (!EmittedName && !UseName.empty())
969-
Ss << " " << UseName;
974+
if (!EmittedName && !UseName.empty()) {
975+
if (!StringRef(Ss.str()).endswith("*"))
976+
Ss << " ";
977+
Ss << UseName;
978+
}
970979

971980
// Final array dropping.
972981
if (!ConstArrs.empty()) {
@@ -1004,10 +1013,10 @@ const CVarSet &PVConstraint::getArgumentConstraints() const {
10041013

10051014
FunctionVariableConstraint::FunctionVariableConstraint(FVConstraint *Ot)
10061015
: ConstraintVariable(ConstraintVariable::FunctionVariable, Ot->OriginalType,
1007-
Ot->getName()), ReturnVar(Ot->ReturnVar),
1008-
ParamVars(Ot->ParamVars), FileName(Ot->FileName), Hasproto(Ot->Hasproto),
1009-
Hasbody(Ot->Hasbody), IsStatic(Ot->IsStatic), Parent(Ot),
1010-
IsFunctionPtr(Ot->IsFunctionPtr), TypeParams(Ot->TypeParams) {
1016+
Ot->getName(), Ot->OriginalTypeWithName),
1017+
ReturnVar(Ot->ReturnVar), ParamVars(Ot->ParamVars), FileName(Ot->FileName),
1018+
Hasproto(Ot->Hasproto), Hasbody(Ot->Hasbody), IsStatic(Ot->IsStatic),
1019+
Parent(Ot), IsFunctionPtr(Ot->IsFunctionPtr), TypeParams(Ot->TypeParams) {
10111020
this->HasEqArgumentConstraints = Ot->HasEqArgumentConstraints;
10121021
}
10131022

@@ -1019,22 +1028,23 @@ FunctionVariableConstraint::FunctionVariableConstraint(DeclaratorDecl *D,
10191028
ProgramInfo &I,
10201029
const ASTContext &C)
10211030
: FunctionVariableConstraint(
1022-
D->getType().getTypePtr(), D,
1031+
D->getType(), D,
10231032
D->getDeclName().isIdentifier() ? std::string(D->getName()) : "", I,
10241033
C, D->getTypeSourceInfo()) {}
10251034

10261035
FunctionVariableConstraint::FunctionVariableConstraint(TypedefDecl *D,
10271036
ProgramInfo &I,
10281037
const ASTContext &C)
1029-
: FunctionVariableConstraint(D->getUnderlyingType().getTypePtr(), nullptr,
1038+
: FunctionVariableConstraint(D->getUnderlyingType(), nullptr,
10301039
D->getNameAsString(), I, C,
10311040
D->getTypeSourceInfo()) {}
10321041

10331042
FunctionVariableConstraint::FunctionVariableConstraint(
1034-
const Type *Ty, DeclaratorDecl *D, std::string N, ProgramInfo &I,
1043+
const QualType QT, DeclaratorDecl *D, std::string N, ProgramInfo &I,
10351044
const ASTContext &Ctx, TypeSourceInfo *TSInfo)
1036-
: ConstraintVariable(ConstraintVariable::FunctionVariable, tyToStr(Ty), N),
1045+
: ConstraintVariable(ConstraintVariable::FunctionVariable, QT, N),
10371046
Parent(nullptr) {
1047+
const Type *Ty = QT.getTypePtr();
10381048
QualType RT, RTIType;
10391049
Hasproto = false;
10401050
Hasbody = false;
@@ -2004,8 +2014,10 @@ void PointerVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV,
20042014
"Merging error, pointer depth change");
20052015
Vars = NewVAtoms;
20062016
SrcVars = NewSrcAtoms;
2007-
if (Name.empty())
2017+
if (Name.empty()) {
20082018
Name = From->Name;
2019+
OriginalTypeWithName = From->OriginalTypeWithName;
2020+
}
20092021
SrcHasItype = SrcHasItype || From->SrcHasItype;
20102022
if (!From->ItypeStr.empty())
20112023
ItypeStr = From->ItypeStr;
@@ -2265,16 +2277,20 @@ void FVComponentVariable::equateWithItype(ProgramInfo &I,
22652277
? "Internal constraint for generic function declaration, "
22662278
"for which 3C currently does not support re-solving."
22672279
: ReasonUnchangeable;
2268-
bool HasBounds = ExternalConstraint->srcHasBounds();
22692280
bool HasItype = ExternalConstraint->srcHasItype();
22702281
// If the type cannot change at all (ReasonUnchangeable2 is set), then we
2271-
// constrain both the external and internal types to not change. Otherwise, if
2272-
// the variable has bounds, then we don't want the checked (external) portion
2273-
// of the type to change because that could blow away the bounds, but we still
2274-
// allow the internal type to change so that the type can change from an itype
2275-
// to fully checked.
2282+
// constrain both the external and internal types to not change.
22762283
bool MustConstrainInternalType = !ReasonUnchangeable2.empty();
2277-
if (HasItype && (MustConstrainInternalType || HasBounds)) {
2284+
// Otherwise, if a pointer is an array pointer with declared bounds or is a
2285+
// constant size array, then we want to ensure the external type continues to
2286+
// solve to ARR or NTARR; see the comment on
2287+
// ConstraintVariable::equateWithItype re how this is achieved. This avoids
2288+
// losing bounds on array pointers, and converting constant sized arrays into
2289+
// pointers. We still allow the internal type to change so that the type can
2290+
// change from an itype to fully checked.
2291+
bool MustBeArray =
2292+
ExternalConstraint->srcHasBounds() || ExternalConstraint->hasSomeSizedArr();
2293+
if (HasItype && (MustConstrainInternalType || MustBeArray)) {
22782294
ExternalConstraint->equateWithItype(I, ReasonUnchangeable2, PSL);
22792295
if (ExternalConstraint != InternalConstraint)
22802296
linkInternalExternal(I, false);

clang/lib/3C/DeclRewriter.cpp

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -36,62 +36,59 @@ void DeclRewriter::buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl,
3636
std::string &Type, std::string &IType,
3737
ProgramInfo &Info, ArrayBoundsRewriter &ABR) {
3838
const EnvironmentMap &Env = Info.getConstraints().getVariables();
39-
bool IsTypedefVarUnchecked =
39+
// True when the type of this variable is defined by a typedef, and the
40+
// constraint variable representing the typedef solved to an unchecked type.
41+
// In these cases, the typedef should be used in the unchecked part of the
42+
// itype. The typedef is expanded using checked pointer types for the checked
43+
// portion. In ItypesForExtern mode, typedefs are treated as unchecked because
44+
// 3C will not rewrite the typedef to a checked type. Even if it solves to a
45+
// checked type, it is not rewritten, so it remains unchecked in the converted
46+
// code.
47+
bool IsUncheckedTypedef =
4048
Defn->isTypedef() && (_3COpts.ItypesForExtern ||
4149
!Defn->getTypedefVar()->isSolutionChecked(Env));
42-
if (Defn->getFV()) {
43-
// This declaration is for a function pointer. Writing itypes on function
44-
// pointers is a little bit harder since the original type string will not
45-
// work for the unchecked portion of the itype. We need to generate the
46-
// unchecked type from the PVConstraint. The last argument of this call
47-
// tells mkString to generate a string using unchecked types instead of
48-
// checked types.
49-
if (Defn->isTypedef() && !IsTypedefVarUnchecked)
50-
Type = Defn->mkString(Info.getConstraints(),
51-
MKSTRING_OPTS(UnmaskTypedef = true,
52-
ForItypeBase = true));
53-
else
54-
Type = Defn->mkString(Info.getConstraints(),
55-
MKSTRING_OPTS(ForItypeBase = true));
50+
// True if this variable is defined by a typedef, and the constraint variable
51+
// representing the typedef solves to a checked type. Notably not the negation
52+
// of IsUncheckedTypedef because both require the variable type be defined
53+
// by a typedef. The checked typedef is expanded using unchecked types in the
54+
// unchecked portion of the itype. The typedef is used directly in the checked
55+
// portion of the itype.
56+
bool IsCheckedTypedef = Defn->isTypedef() && !IsUncheckedTypedef;
57+
58+
// It should in principle be possible to always generate the unchecked portion
59+
// of the itype by going through mkString. For practical reason, this doesn't
60+
// always work, so we instead use the original type string as generated by
61+
// clang so that we emit valid syntax in more cases. For examples and
62+
// discussion refer to issue correctcomputation/checkedc-clang#703.
63+
if (IsCheckedTypedef || Defn->getFV()) {
64+
// Generate the type string from PVC if we need to unmask a typedef, this is
65+
// a function pointer, or this is a constant size array. When unmasking a
66+
// typedef, the expansion of the typedef does not exist in the original
67+
// source, so it must be constructed. For function pointers, a function
68+
// pointer appearing in the unchecked portion of an itype must contain an
69+
// extra set of parenthesis (e.g. `void ((*f)())` instead of `void (f*)()`)
70+
// for the declaration to parse correctly. qtyToStr (which is used by
71+
// getOriginalTypeWithName) does not support adding these parentheses.
72+
Type = Defn->mkString(Info.getConstraints(),
73+
MKSTRING_OPTS(UnmaskTypedef = IsCheckedTypedef,
74+
ForItypeBase = true));
5675
} else {
57-
if (Defn->isTypedef() && !IsTypedefVarUnchecked)
58-
Type = Defn->mkString(Info.getConstraints(),
59-
MKSTRING_OPTS(UnmaskTypedef = true,
60-
ForItypeBase = true,
61-
EmitName = false));
76+
// In the remaining cases, the unchecked portion of the itype is just the
77+
// original type of the pointer. The first branch tries to generate the type
78+
// using the type and name for this specific declaration. This is important
79+
// because it avoids changing parameter names, particularly in cases where
80+
// multiple functions sharing the same name are defined in different
81+
// translation units.
82+
if (isa_and_nonnull<ParmVarDecl>(Decl) && !Decl->getName().empty())
83+
Type = qtyToStr(Decl->getType(), Decl->getNameAsString());
6284
else
63-
Type = Defn->getRewritableOriginalTy();
64-
65-
if (isa_and_nonnull<ParmVarDecl>(Decl)) {
66-
if (Decl->getName().empty())
67-
Type += Defn->getName();
68-
else
69-
Type += Decl->getNameAsString();
70-
} else {
71-
std::string Name = Defn->getName();
72-
if (Name != RETVAR)
73-
Type += Name;
74-
}
85+
Type = Defn->getOriginalTypeWithName();
7586
}
7687

7788
IType = " : itype(";
78-
79-
if (IsTypedefVarUnchecked) {
80-
// In -itypes-for-extern mode we do not rewrite typedefs to checked types.
81-
// They are given a checked itype instead. The unchecked portion of the
82-
// itype continues to use the original typedef, but the typedef in the
83-
// checked portion is expanded and rewritten to use a checked type. This
84-
// lets the typedef be used in unchecked code while still giving a checked
85-
// type to the declaration so that it can be used in checked code.
86-
// TODO: This could potentially be applied to typedef types even when the
87-
// flag is not passed to limit spread of wildness through typedefs.
88-
IType += Defn->mkString(Info.getConstraints(),
89-
MKSTRING_OPTS(EmitName = false, ForItype = true,
90-
UnmaskTypedef = true));
91-
} else {
92-
IType += Defn->mkString(Info.getConstraints(),
93-
MKSTRING_OPTS(EmitName = false, ForItype = true));
94-
}
89+
IType += Defn->mkString(Info.getConstraints(),
90+
MKSTRING_OPTS(EmitName = false, ForItype = true,
91+
UnmaskTypedef = IsUncheckedTypedef));
9592
IType += ")" + ABR.getBoundsString(Defn, Decl, true);
9693
}
9794

@@ -323,9 +320,8 @@ void DeclRewriter::rewriteSingleDecl(DeclReplacement *N, RSet &ToRewrite) {
323320
dyn_cast<TypedefDecl>(N->getDecl()) || isSingleDeclaration(N);
324321
assert("Declaration is not a single declaration." && IsSingleDecl);
325322
// This is the easy case, we can rewrite it locally, at the declaration.
326-
// TODO why do we call getDecl() and getSourceRange() directly,
327-
// TODO as opposed to getSourceRange()?
328-
SourceRange TR = N->getDecl()->getSourceRange();
323+
SourceManager &SM = N->getDecl()->getASTContext().getSourceManager();
324+
SourceRange TR = N->getSourceRange(SM);
329325
doDeclRewrite(TR, N);
330326
}
331327

0 commit comments

Comments
 (0)