-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Currently, -itypes-for-extern is implemented as a bunch of special cases that modify the generated types at rewriting time. These extra cases are making it harder for me to verify that the code is correct, and the rest of 3C is unaware that this transformation is happening, which is liable to cause errors or other confusing behavior. Instead, -itypes-for-extern should be integrated into the constraint graph. To a first approximation, in the current 3C codebase, that would mean constraining the "unchecked" sides of itypes to wild, though 3C will probably have to evolve a bit before that approach becomes viable (see "Obstacles to the proposed change" below).
Problems with the current design
Here's an example I ran into today that illustrates the weakness of the current design well. 3c -itypes-for-extern -addcr on the following:
typedef int *p_int;
void foo(void) {
p_int x;
}produces:
typedef int *p_int;
void foo(void) _Checked {
p_int x = ((void *)0); // error: local variable in a checked scope must have a checked type
}The solved type of p_int is _Ptr<int>, so 3C assumed that x had a fully checked type and marked foo as _Checked. But -itypes-for-extern violated this assumption.
A few other problems with the current design:
-
In the example program in Should
DeclRewriter::buildItypeDecluse the actual internalPVConstraint? #704, forcing the unchecked side of an itype to all-wild causes a compile error in combination with 3C's solution for the rest of the program. The fix proposed in ShouldDeclRewriter::buildItypeDecluse the actual internalPVConstraint? #704 is to just stop forcing the unchecked side to all-wild and use its actual solution. But-itypes-for-externwants to keep the unchecked side all-wild. To do that without causing a compile error,-itypes-for-externwill need to add constraints to ensure that the rest of the solution is compatible with the all-wild unchecked side. -
Currently, when 3C infers a generic signature for a function, it decides between
_For_anyand_Itype_for_anybased on whether the itype string returned byFunctionDeclBuilder::buildDeclVaris nonempty:
checkedc-clang/clang/lib/3C/DeclRewriter.cpp
Lines 624 to 629 in 94cd56c
std::string Type, IType; this->buildDeclVar(CV, PVDecl, Type, IType, PVDecl->getQualifiedNameAsString(), RewriteGeneric, RewriteParams, RewriteReturn, FD->isStatic()); ParmStrs.push_back(Type + IType); ProtoHasItype |= !IType.empty();
That's a hack, but it does work correctly with-itypes-for-externnow becauseFunctionDeclBuilder::buildDeclVarchecks for-itypes-for-externand forces the generation of an itype if the flag is on. If we want to clean up the generic inference to be based on the constraint graph rather than a string generated during rewriting, then we need to have the information about the itype available in the constraint graph.
Obstacles to the proposed change
According to John, the main reason that -itypes-for-extern isn't integrated into the constraint graph now is that under the current design of liberal itypes for functions, the notion of the "internal" type is coupled to "unchecked side of the itype", and "external" to "checked side of the itype". Thus, in an example like:
void test(int *a) { int *b = a; }if -itypes-for-extern constrains the "unchecked" (i.e., "internal") side to wild, that will force b to be wild:
void test(int *a : itype(_Ptr<int>)) { int *b = a; }That's undesirable because Checked C actually allows either side of the itype to be used either inside or outside the function. We want -itypes-for-extern to be able to produce the following, as it does in the current hacky implementation:
void test(int *a : itype(_Ptr<int>)) { _Ptr<int> b = a; }We may be able to solve this by redesigning the constraint graph for liberal itypes to reflect the fact that either side of an itype can be used either inside or outside the function: see #743.