Skip to content

Commit 8170d2f

Browse files
mkString fixes (mostly moved from the multi-decl overhaul) (#714)
- Remove extra spaces after wild pointer `*` symbols in some cases. - Generate valid output in some more complex cases involving checked pointers, fixed-size arrays, and/or function pointers. - Remove now-obsolete code from mkString.
1 parent 2a0df40 commit 8170d2f

File tree

8 files changed

+79
-94
lines changed

8 files changed

+79
-94
lines changed

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,15 @@ typedef std::set<ConstraintKey> CVars;
4242
// Holds Atoms, one for each of the pointer (*) declared in the program.
4343
typedef std::vector<Atom *> CAtoms;
4444

45+
// Options for ConstraintVariable::mkString, using the code pattern described in
46+
// clang/include/clang/3C/OptionalParams.h. Use the MKSTRING_OPTS macro to
47+
// generate a MkStringOpts instance at a call site.
4548
struct MkStringOpts {
49+
// True when the generated string should include
50+
// the name of the variable, false for just the type.
4651
bool EmitName = true;
52+
// True when the generated string is expected
53+
// to be used inside an itype.
4754
bool ForItype = false;
4855
bool EmitPointee = false;
4956
bool UnmaskTypedef = false;
@@ -113,11 +120,12 @@ class ConstraintVariable {
113120
qtyToStr(QT, N == RETVAR ? "" : N)) {}
114121

115122
public:
116-
// Create a "for-rewriting" representation of this ConstraintVariable.
117-
// The 'emitName' parameter is true when the generated string should include
118-
// the name of the variable, false for just the type.
119-
// The 'forIType' parameter is true when the generated string is expected
120-
// to be used inside an itype.
123+
// Generate source code for the type and (in certain cases) the name of the
124+
// variable or function represented by this ConstraintVariable that reflects
125+
// any changes made by 3C and is suitable to insert during rewriting.
126+
//
127+
// This method is used in several contexts with special requirements, which
128+
// are addressed by the options in MkStringOpts; see the comments there.
121129
virtual std::string mkString(Constraints &CS,
122130
const MkStringOpts &Opts = {}) const = 0;
123131

clang/lib/3C/ConstraintVariables.cpp

Lines changed: 43 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -750,12 +750,19 @@ PointerVariableConstraint::mkString(Constraints &CS,
750750
UNPACK_OPTS(EmitName, ForItype, EmitPointee, UnmaskTypedef, UseName,
751751
ForItypeBase);
752752

753+
// This function has become pretty ad-hoc and has a number of known bugs: see
754+
// https://github.com/correctcomputation/checkedc-clang/issues/703. We hope to
755+
// overhaul it in the future.
756+
753757
// The name field encodes if this variable is the return type for a function.
754758
// TODO: store this information in a separate field.
755759
bool IsReturn = getName() == RETVAR;
756760

757-
if (UseName.empty())
761+
if (UseName.empty()) {
758762
UseName = getName();
763+
if (UseName.empty())
764+
EmitName = false;
765+
}
759766

760767
if (IsTypedef && !UnmaskTypedef) {
761768
std::string QualTypedef = gatherQualStrings() + TypedefString;
@@ -782,13 +789,9 @@ PointerVariableConstraint::mkString(Constraints &CS,
782789
bool EmittedBase = false;
783790
// Have we emitted the name of the variable yet?
784791
bool EmittedName = false;
785-
// Was the last variable an Array?
786-
bool PrevArr = false;
787-
// Is the entire type so far an array?
788-
bool AllArrays = true;
789792
if (!EmitName || IsReturn)
790793
EmittedName = true;
791-
uint32_t TypeIdx = 0;
794+
int TypeIdx = 0;
792795

793796
// If we've set a GenericIndex for void, it means we're converting it into
794797
// a generic function so give it the default generic type name.
@@ -802,16 +805,15 @@ PointerVariableConstraint::mkString(Constraints &CS,
802805
}
803806

804807
auto It = Vars.begin();
805-
auto I = 0;
806808
// Skip over first pointer level if only emitting pointee string.
807809
// This is needed when inserting type arguments.
808810
if (EmitPointee)
809811
++It;
810812
// Iterate through the vars(), but if we have an internal typedef, then stop
811813
// once you reach the typedef's level.
812814
for (; It != Vars.end() && IMPLIES(TypedefLevelInfo.HasTypedef,
813-
I < TypedefLevelInfo.TypedefLevel);
814-
++It, I++) {
815+
TypeIdx < TypedefLevelInfo.TypedefLevel);
816+
++It, TypeIdx++) {
815817
const auto &V = *It;
816818
ConstAtom *C = nullptr;
817819
if (ForItypeBase) {
@@ -833,21 +835,23 @@ PointerVariableConstraint::mkString(Constraints &CS,
833835
if (!ForItype && InferredGenericIndex == -1 && isVoidPtr())
834836
K = Atom::A_Wild;
835837

836-
if (PrevArr && ArrSizes.at(TypeIdx).first != O_SizedArray && !EmittedName) {
837-
EmittedName = true;
838+
// In a case like `_Ptr<TYPE> p[2]`, the ` p[2]` needs to end up _after_ the
839+
// `>`, so we need to push the ` p[2]` onto EndStrs _before_ the code below
840+
// pushes the `>`. In general, before we visit a checked pointer level (not
841+
// a checked array level), we need to transfer any pending array levels and
842+
// emit the name (if applicable).
843+
if (K != Atom::A_Wild && ArrSizes.at(TypeIdx).first != O_SizedArray) {
838844
addArrayAnnotations(ConstArrs, EndStrs);
839-
EndStrs.push_front(" " + UseName);
845+
if (!EmittedName) {
846+
EmittedName = true;
847+
EndStrs.push_front(" " + UseName);
848+
}
840849
}
841-
PrevArr = ArrSizes.at(TypeIdx).first == O_SizedArray;
842850

843851
switch (K) {
844852
case Atom::A_Ptr:
845853
getQualString(TypeIdx, Ss);
846854

847-
// We need to check and see if this level of variable
848-
// is constrained by a bounds safe interface. If it is,
849-
// then we shouldn't re-write it.
850-
AllArrays = false;
851855
EmittedBase = false;
852856
Ss << "_Ptr<";
853857
EndStrs.push_front(">");
@@ -861,38 +865,28 @@ PointerVariableConstraint::mkString(Constraints &CS,
861865
// we should substitute [K].
862866
if (emitArraySize(ConstArrs, TypeIdx, K))
863867
break;
864-
AllArrays = false;
865-
// We need to check and see if this level of variable
866-
// is constrained by a bounds safe interface. If it is,
867-
// then we shouldn't re-write it.
868868
EmittedBase = false;
869869
Ss << "_Array_ptr<";
870870
EndStrs.push_front(">");
871871
break;
872872
case Atom::A_NTArr:
873+
// If this is an NTArray.
874+
getQualString(TypeIdx, Ss);
873875
if (emitArraySize(ConstArrs, TypeIdx, K))
874876
break;
875-
AllArrays = false;
876-
// This additional check is to prevent fall-through from the array.
877-
if (K == Atom::A_NTArr) {
878-
// If this is an NTArray.
879-
getQualString(TypeIdx, Ss);
880877

881-
// We need to check and see if this level of variable
882-
// is constrained by a bounds safe interface. If it is,
883-
// then we shouldn't re-write it.
884-
EmittedBase = false;
885-
Ss << "_Nt_array_ptr<";
886-
EndStrs.push_front(">");
887-
break;
888-
}
889-
LLVM_FALLTHROUGH;
878+
EmittedBase = false;
879+
Ss << "_Nt_array_ptr<";
880+
EndStrs.push_front(">");
881+
break;
890882
// If there is no array in the original program, then we fall through to
891883
// the case where we write a pointer value.
892884
case Atom::A_Wild:
893885
if (emitArraySize(ConstArrs, TypeIdx, K))
894886
break;
895-
AllArrays = false;
887+
// FIXME: This code emits wild pointer levels with the outermost on the
888+
// left. The outermost should be on the right
889+
// (https://github.com/correctcomputation/checkedc-clang/issues/161).
896890
if (FV != nullptr) {
897891
FptrInner << "*";
898892
getQualString(TypeIdx, FptrInner);
@@ -912,35 +906,19 @@ PointerVariableConstraint::mkString(Constraints &CS,
912906
llvm_unreachable("impossible");
913907
break;
914908
}
915-
TypeIdx++;
916-
}
917-
918-
// If the previous variable was an array or
919-
// if we are leaving an array run, we need to emit the
920-
// annotation for a stack-array
921-
if (PrevArr && !ConstArrs.empty())
922-
addArrayAnnotations(ConstArrs, EndStrs);
923-
924-
// If the whole type is an array so far, and we haven't emitted
925-
// a name yet, then emit the name so that it appears before
926-
// the the stack array type.
927-
if (PrevArr && !EmittedName && AllArrays) {
928-
EmittedName = true;
929-
EndStrs.push_front(" " + UseName);
930909
}
931910

932911
if (!EmittedBase) {
933912
// If we have a FV pointer, then our "base" type is a function pointer type.
934913
if (FV) {
935-
if (Ss.str().empty()) {
936-
if (!EmittedName) {
937-
FptrInner << UseName;
938-
EmittedName = true;
939-
}
940-
for (std::string Str : EndStrs)
941-
FptrInner << Str;
942-
EndStrs.clear();
914+
if (!EmittedName) {
915+
FptrInner << UseName;
916+
EmittedName = true;
943917
}
918+
std::deque<std::string> FptrEndStrs;
919+
addArrayAnnotations(ConstArrs, FptrEndStrs);
920+
for (std::string Str : FptrEndStrs)
921+
FptrInner << Str;
944922
bool EmitFVName = !FptrInner.str().empty();
945923
if (EmitFVName)
946924
Ss << FV->mkString(CS, MKSTRING_OPTS(UseName = FptrInner.str(),
@@ -958,27 +936,20 @@ PointerVariableConstraint::mkString(Constraints &CS,
958936
}
959937
}
960938

961-
// Add closing elements to type
962-
for (std::string Str : EndStrs) {
963-
Ss << Str;
964-
}
965-
966939
// No space after itype.
967-
if (!EmittedName && !UseName.empty()) {
940+
if (!EmittedName) {
968941
if (!StringRef(Ss.str()).endswith("*"))
969942
Ss << " ";
970943
Ss << UseName;
971944
}
972945

973-
// Final array dropping.
974-
if (!ConstArrs.empty()) {
975-
std::deque<std::string> ArrStrs;
976-
addArrayAnnotations(ConstArrs, ArrStrs);
977-
for (std::string Str : ArrStrs)
978-
Ss << Str;
946+
// Add closing elements to type
947+
addArrayAnnotations(ConstArrs, EndStrs);
948+
for (std::string Str : EndStrs) {
949+
Ss << Str;
979950
}
980951

981-
if (IsReturn && !ForItype)
952+
if (IsReturn && !ForItype && !StringRef(Ss.str()).endswith("*"))
982953
Ss << " ";
983954

984955
return Ss.str();

clang/test/3C/compound_literal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void lists() {
6767

6868
int *d[2] = (int *[2]){&x, (int *)1};
6969
//CHECK_NOALL: int *d[2] = (int *[2]){&x, (int *)1};
70-
//CHECK_ALL: int * d _Checked[2] = (int * _Checked[2]){&x, (int *)1};
70+
//CHECK_ALL: int *d _Checked[2] = (int * _Checked[2]){&x, (int *)1};
7171
int *d0 = d[0];
7272
//CHECK: int *d0 = d[0];
7373

clang/test/3C/definedType.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ baz **f[1];
5757
// CHECK_ALL: _Ptr<_Ptr<baz>> f _Checked[1] = {((void *)0)};
5858
baz (*g)[1];
5959
// CHECK_ALL: _Ptr<baz _Checked[1]> g = ((void *)0);
60+
baz *(*g2)[1];
61+
// CHECK_ALL: _Ptr<_Ptr<baz> _Checked[1]> g2 = ((void *)0);
6062
baz h[1][1];
6163
// CHECK_ALL: baz h _Checked[1] _Checked[1];
6264

clang/test/3C/fptr_array.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99

1010
void (*fs[2])(int *);
1111
void (*f)(int *);
12-
//CHECK_NOALL: void (* fs[2])(_Ptr<int>) = {((void *)0)};
12+
//CHECK_NOALL: void (*fs[2])(_Ptr<int>) = {((void *)0)};
1313
//CHECK_NOALL: void (*f)(_Ptr<int>) = ((void *)0);
1414
//CHECK_ALL: _Ptr<void (_Ptr<int>)> fs _Checked[2] = {((void *)0)};
1515
//CHECK_ALL: _Ptr<void (_Ptr<int>)> f = ((void *)0);
1616

1717
void (*gs[2])(int *);
1818
void g_impl(int *x) { x = 1; }
1919
void (*g)(int *) = g_impl;
20-
//CHECK_NOALL: void (* gs[2])(int * : itype(_Ptr<int>)) = {((void *)0)};
20+
//CHECK_NOALL: void (*gs[2])(int * : itype(_Ptr<int>)) = {((void *)0)};
2121
//CHECK_NOALL: void g_impl(int *x : itype(_Ptr<int>)) { x = 1; }
2222
//CHECK_NOALL: void (*g)(int * : itype(_Ptr<int>)) = g_impl;
2323

@@ -30,21 +30,21 @@ void (*h)(void *);
3030

3131
int *(*is[2])(void);
3232
int *(*i)(void);
33-
//CHECK_NOALL: _Ptr<int> (* is[2])(void) = {((void *)0)};
33+
//CHECK_NOALL: _Ptr<int> (*is[2])(void) = {((void *)0)};
3434
//CHECK_NOALL: _Ptr<int> (*i)(void) = ((void *)0);
3535
//CHECK_ALL: _Ptr<_Ptr<int> (void)> is _Checked[2] = {((void *)0)};
3636
//CHECK_ALL: _Ptr<_Ptr<int> (void)> i = ((void *)0);
3737

3838
int *(**js[2])(void);
3939
int *(**j)(void);
40-
//CHECK_NOALL: _Ptr<int> (** js[2])(void) = {((void *)0)};
40+
//CHECK_NOALL: _Ptr<int> (**js[2])(void) = {((void *)0)};
4141
//CHECK_NOALL: _Ptr<int> (**j)(void) = ((void *)0);
4242
//CHECK_ALL: _Ptr<_Ptr<_Ptr<int> (void)>> js _Checked[2] = {((void *)0)};
4343
//CHECK_ALL: _Ptr<_Ptr<_Ptr<int> (void)>> j = ((void *)0);
4444

4545
int *(*ks[2][2])(void);
4646
int *(*k)(void);
47-
//CHECK_NOALL: _Ptr<int> (* ks[2][2])(void) = {((void *)0)};
47+
//CHECK_NOALL: _Ptr<int> (*ks[2][2])(void) = {((void *)0)};
4848
//CHECK_NOALL: _Ptr<int> (*k)(void) = ((void *)0);
4949
//CHECK_ALL: _Ptr<_Ptr<int> (void)> ks _Checked[2] _Checked[2] = {((void *)0)};
5050
//CHECK_ALL: _Ptr<_Ptr<int> (void)> k = ((void *)0);
@@ -53,6 +53,9 @@ void (* const l)(int *);
5353
//CHECK_NOALL: void (*const l)(_Ptr<int>) = ((void *)0);
5454
//CHECK_ALL: const _Ptr<void (_Ptr<int>)> l = ((void *)0);
5555

56+
#define UNWRITABLE_MS_DECL void (*ms[2])(int *)
57+
UNWRITABLE_MS_DECL;
58+
5659
void test(void){
5760
fs[1] = f;
5861
gs[1] = g;
@@ -63,4 +66,11 @@ void test(void){
6366
void (* const ls[1])(int *) = {l};
6467
//CHECK_NOALL: void (*const ls[1])(_Ptr<int>) = {l};
6568
//CHECK_ALL: const _Ptr<void (_Ptr<int>)> ls _Checked[1] = {l};
69+
70+
void (*(*pms)[2])(int *) = &ms;
71+
//CHECK: _Ptr<void (*[2])(int *)> pms = &ms;
72+
73+
void (*(*apms[1])[2])(int *) = {&ms};
74+
//CHECK_NOALL: void (*(*apms[1])[2])(int *) = {&ms};
75+
//CHECK_ALL: _Ptr<void (*[2])(int *)> apms _Checked[1] = {&ms};
6676
}

clang/test/3C/itype_typedef.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void test2(td2 a) {
3434
typedef int *td3;
3535
td3 test3() {
3636
//CHECK: typedef _Ptr<int> td3;
37-
//CHECK: int * test3(void) : itype(td3) {
37+
//CHECK: int *test3(void) : itype(td3) {
3838
return (int*) 1;
3939
}
4040

clang/test/3C/itypes_for_extern.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,8 @@ int *baz(int *a, int len, int *b) {
3030
return a;
3131
}
3232

33-
// FIXME: The space between after the first star is needed for the idempotence
34-
// test to pass. If there isn't a space there, 3c will add one when
35-
// re-run on its original output. This should be fixed ideally in two
36-
// ways. First, the space shouldn't be added when not present in the
37-
// original source, and second, the second conversion should not rewrite
38-
// the declaration.
39-
void buz(int * (*f)(int *, int *)) {}
40-
//CHECK: void buz(int * ((*f)(int *, int *)) : itype(_Ptr<_Ptr<int> (_Ptr<int>, _Ptr<int>)>)) _Checked {}
33+
void buz(int *(*f)(int *, int *)) {}
34+
//CHECK: void buz(int *((*f)(int *, int *)) : itype(_Ptr<_Ptr<int> (_Ptr<int>, _Ptr<int>)>)) _Checked {}
4135

4236
typedef int * int_star;
4337
void typedef_test(int_star p) {}

clang/test/3C/ptr_array.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void test1(int *a) {
2828

2929
int *b[1] = {a};
3030
//CHECK_NOALL: int *b[1] = {a};
31-
//CHECK_ALL: int * b _Checked[1] = {a};
31+
//CHECK_ALL: int *b _Checked[1] = {a};
3232
}
3333

3434
/* Example from from the issue */
@@ -40,7 +40,7 @@ int *foo() {
4040
int z = 3;
4141
int *ptrs[4] = {&x, &y, &z, (int *)5};
4242
//CHECK_NOALL: int *ptrs[4] = {&x, &y, &z, (int *)5};
43-
//CHECK_ALL: int * ptrs _Checked[4] = {&x, &y, &z, (int *)5};
43+
//CHECK_ALL: int *ptrs _Checked[4] = {&x, &y, &z, (int *)5};
4444
int *ret;
4545
//CHECK: int *ret;
4646
for (int i = 0; i < 4; i++) {

0 commit comments

Comments
 (0)