Skip to content
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

-itypes-for-extern should be integrated into the constraint graph #740

Open
mattmccutchen-cci opened this issue Nov 15, 2021 · 0 comments
Open

Comments

@mattmccutchen-cci
Copy link
Member

mattmccutchen-cci commented Nov 15, 2021

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::buildItypeDecl use the actual internal PVConstraint? #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 Should DeclRewriter::buildItypeDecl use the actual internal PVConstraint? #704 is to just stop forcing the unchecked side to all-wild and use its actual solution. But -itypes-for-extern wants to keep the unchecked side all-wild. To do that without causing a compile error, -itypes-for-extern will 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_any and _Itype_for_any based on whether the itype string returned by FunctionDeclBuilder::buildDeclVar is nonempty:

    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-extern now because FunctionDeclBuilder::buildDeclVar checks for -itypes-for-extern and 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant