-
Notifications
You must be signed in to change notification settings - Fork 79
Implicit inclusion of checked header files. #998
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
Conversation
I'm referencing checkedc/checkedc#431, which this addresses. |
…ay have clang-specific declarations.
…of checked headers.
clang/lib/Headers/inttypes.h
Outdated
@@ -18,6 +18,11 @@ | |||
#error MSVC does not have inttypes.h prior to Visual Studio 2013 | |||
#endif | |||
|
|||
#ifndef __cplusplus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this enabled even if the user compiles a C file with -fno-checkedc-extension
?
Maybe we can create a macro like __checkedc
which gets defined if Checked C is enabled. Then we can use that macro as guard here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we need to resolve this issue. We also need to consider the use case when inttypes_checked.h is explicitly included.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to creating a macro like __checkedc
. That would make it possible for the public headers of third-party libraries to automatically generate Checked C declarations when loaded by the Checked C compiler while remaining compatible with plain C compilers as well: in effect, supporting implicit inclusion of checked headers for third-party libraries, just as you are doing for the system headers in this PR. (I was thinking about filing a separate issue for this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have created the __checkedc
macro in the latest commit.
@@ -1,10 +1,8 @@ | |||
// RUN: %clang -target x86_64-unknown-unknown \ | |||
// RUN: -nostdlibinc -ffreestanding -fsyntax-only %s | |||
// RUN: %clang -target x86_64-unknown-unknown -nostdlibinc -ffreestanding -MM -MG %s | FileCheck -check-prefix=CHECK %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please explain why we need to change community tests?
Does this mean we are changing existing behavior, and that users would need to update their flags/makefiles? Changing community tests could also cause merge conflicts when we upgrade.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case is ensuring that the system stdlib.h
cannot be found if -nostdlibinc
is specified on the compilation commandline.
This can be ensured in more than one way - the approach chosen in this test case assumes that stdlib.h
is not present in <build-dir>/lib/clang/11.0.0/include
directory. This is an assumption because of the following reason:
The mechanism used by clang/llvm to add clang-specific declarations is to create a header file of the same name (ex.: float.h
, inttypes.h
- these are present in <src-dir>/clang/lib/Headers
), use #include_next <float.h>
to include the system header file, and add clang-specific declarations below this. Then they copy the header file from <src-dir>/clang/lib/Headers
to <build-dir>/lib/clang/11.0.0/include
. The only reason that stdlib.h
is not present in <build-dir/lib/clang/11.0.0/include
in the llvm upstream repo is because there has not been a need to add clang-specific declarations to stdlib.h
. Had there been, this test case would have to be modified anyway.
Now, that we have added stdlib.h
to <build-dir>/lib/clang/11.0.0/include
, we need to change the way of ensuring that the "system" stdlib.h
is not found when -nostdlibinc
is specified on the compilation command line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we create a new macro for Checked C and use it as guard maybe we can simply add -fno-checkedc-extension
to these tests to preserve community behavior without necessarily modifying the tests themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should create a new macro for Checked C and use it as a guard. But adding -fno-checkedc-extension
to this test case will not stop it from failing. In it's original form, this test case will fail because of the fact that a file named stdlib.h
exists in the <build-dir>/lib/clang/11.0.0/include
directory, irrespective of what the file contains.
The above modification will be required to make this test case pass even if we create the new macro.
- For a system header file that contains clang-specific declarations and also | ||
Checked-C-specific declarations (there is one such header file at present), | ||
we will do the following: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're willing to change the default include path in the driver to add a Checked-C-specific include directory (say lib/clang/VERSION/include-checkedc
) before the main one (lib/clang/VERSION/include
), then you won't need to do this. You can use the solution in the previous bullet point for all Checked C headers, and the #include_next <foo.h>
will automatically pick up either the system header or the Clang header (in which the #include_next <foo.h>
would then include the system header) as appropriate. This would also cleanly separate the Checked C headers from the Clang headers in different subdirectories of the installation directory, making it clearer for users during troubleshooting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have decided not to change the default include path in the driver at present.
// Force the inclusion of Checked-C-specific declarations | ||
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
#include <bar.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do keep this approach rather than taking my suggestion above, might the following be safer?
// Force the inclusion of Checked-C-specific declarations | |
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS | |
#include <bar.h> | |
#include <bar.h> | |
#include <bar_checked_internal.h> |
bar_checked_internal.h
might get scanned twice, so it would need a header guard and there might be a slight performance cost, but it would make it possible for a user to set NO_IMPLICIT_INCLUDE_CHECKED_HDRS
for one #include <foo.h>
directive in a translation unit without the risk of an #include <bar_checked.h>
earlier in the translation unit clobbering NO_IMPLICIT_INCLUDE_CHECKED_HDRS
(if that is a scenario you care about).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mattmccutchen-cci Thank you for your suggestion - you are right! I have incorporated it in the latest commit.
clang/lib/Headers/inttypes.h
Outdated
@@ -18,6 +18,11 @@ | |||
#error MSVC does not have inttypes.h prior to Visual Studio 2013 | |||
#endif | |||
|
|||
#ifndef __cplusplus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to creating a macro like __checkedc
. That would make it possible for the public headers of third-party libraries to automatically generate Checked C declarations when loaded by the Checked C compiler while remaining compatible with plain C compilers as well: in effect, supporting implicit inclusion of checked headers for third-party libraries, just as you are doing for the system headers in this PR. (I was thinking about filing a separate issue for this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for putting this together - I have some suggestions for the RFC.
## Problem Summary | ||
The existing approach for including checked header files, which is to explicitly | ||
replace an included header file with its checked counterpart, is inconvenient | ||
for automation by 3C. So we need a way to implicitly include checked header |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would talk about this in more general terms. 3C is representative of an approach to converting files. It is inconvenient for adoption of Checked C to have to alter include files throughout a project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your review! I have addressed all the review comments and made the requisite changes to the doc.
files. | ||
|
||
## Conditions that a Solution must Satisfy | ||
- The solution must not violate the "opt-in" philosophy that we advocate for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposed solution isn't opt-in, though.
clang picks it up from the above location. | ||
|
||
## Solution Space | ||
- Solution 1: Proposed by CCI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to give this a more general name, not "proposed by CCI".
## Proposed Solution: | ||
|
||
The proposed solution is as follows: | ||
- It is a hybrid solution based on ideas in Solutions 3 and 1 that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't t his be classified as another possible solution? It is a little confusing for the proposed solution to be described as a hybrid of other solutions.
|
||
// Force the inclusion of Checked-C-specific declarations | ||
#undef NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
#include <bar.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach of a slight alteration to the clang-specific header file looks good to me.
- The order in which the compiler driver searches the include paths must remain | ||
unaltered. | ||
- The solution must be able to accommodate any clang-specific declarations in | ||
system header files, if present (Ex.: inttypes.h). | ||
- The Checked-C-specific declarations must live in checkedc repository. | ||
- The Checked-C-specific declarations must live in the checkedc repository. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked-C-specific
--> Checked C-specific
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your review! I feel that Checked-C-specific
is a bit more clear - so I have retained the same.
@@ -53,14 +51,14 @@ files. | |||
clang picks it up from the above location. | |||
|
|||
## Solution Space | |||
- Solution 1: Proposed by CCI | |||
- Solution 1: | |||
- Let `foo.h` be a system header file with a checked counterpart called | |||
`foo_checked.h`. We add a new file, also called `foo.h`, in the same | |||
directory that contains `foo_checked.h`. This new `foo.h` should contain | |||
`#include_next <foo.h>` plus all the Checked-C-specific declarations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked-C-specific
--> Checked C-specific
4) It is convenient for automation. | ||
- **Cons**: 1) The solution does not provide a way to opt out of including | ||
checked headers. 2) While it is easy to accommodate clang-specific | ||
declarations in system header files (Checked-C-specific declarations can |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked-C-specific
--> Checked C-specific
drivers are needed to compile the sources. This may happen in a scenario | ||
where most source files want implicit inclusion but a few source files | ||
want explicit inclusion because they want to directly include some system | ||
header files in order to avoid Checked-C-specific declarations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked-C-specific
--> Checked C-specific
compilation flag NO_IMPLICIT_INCLUDE_CHECKED_HDRS to opt out of the implicit | ||
inclusion. | ||
- For header files that do not have clang-specific declarations, but have | ||
Checked-C-specific declarations (there are 13 such header files at present), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked-C-specific
--> Checked C-specific
clang/lib/Headers/inttypes.h
Outdated
#pragma CHECKED_SCOPE pop | ||
#endif | ||
|
||
#ifndef NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
// Compiling for Checked C and implicit inclusion of checked headers is enabled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Period needed at the end of comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// includes (via #include_next), is not found. | ||
// The -MM -MG preprocessor options list the header files not found. | ||
// | ||
// RUN: %clang -target x86_64-unknown-unknown -nostdlibinc -ffreestanding -MM -MG %s | FileCheck -check-prefix=CHECK %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, FileCheck
will always check the prefix CHECK
. So you can omit the --check-prefix=CHECK
part in this RUN
command.
See https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-filecheck-check-prefix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
…cket.h may not be found in all compilation environments (Ex. Windows). Therefore the includes in the wrapper header files need to be guarded by __has_include_next. This may impact the expected output of a testcase.
#if !defined __checkedc || defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
#include_next <foo.h> | ||
#else | ||
#include <foo_checked.h> | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I realize this can be substantially simplified if you don't mind some header files being read multiple times. They probably are or have to be guarded anyway, so the only cost I see is performance, unless there is a concern about a header file indirectly including itself recursively even if the recursion is immediately stopped by a guard.
#if !defined __checkedc || defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS | |
#include_next <foo.h> | |
#else | |
#include <foo_checked.h> | |
#endif | |
#include_next <foo.h> | |
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS | |
#include <foo_checked.h> | |
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that this suggestion has the advantage of a uniform approach for all header files but it relies on guard flags to avoid recursion. In order to keep the solution simple and maintainable, we do prefer to avoid a header file including itself recursively without relying on guard flags.
Additionally, in the implementation, there are more flags entering into the fray in order to handle the absence of some posix header files like unistd.h
, arpa/inet.h
and sys/socket.h
in the Windows environment. This makes it even more necessary to keep the solution simple and also comprehensible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I think about this now, I think I got a little carried away with my proposal and you were right to decline it. The resulting code looks simpler on the surface, but it's significantly more complicated to verify that it works correctly.
FWIW, an alternative way to achieve a uniform approach is to have each header foo.h
with no Clang-specific declarations use your existing design for headers with Clang-specific declarations, effectively as if Clang already had a foo.h
containing nothing but #include_next <foo.h>
followed by an empty set of Clang-specific declarations. You could decide whether the resulting short, boilerplate foo.h
goes in the checkedc-clang
or checkedc
repository; the former would achieve total uniformity. But this would increase the number of header files you have to maintain by about 50% (in addition to foo.h
and foo_checked.h
, you would have foo_checked_internal.h
) for minimal practical benefit, so I imagine it's not worth it. (The only practical benefit I see is that if the user had some problem with the system foo.h
but had another way to get the plain-C declarations they want, they could then get the Checked C declarations by including foo_checked_internal.h
with no risk of pulling in the system foo.h
. But I think this would be very unusual, and the user could use the hacky workaround of manually defining the header guard for foo.h
before including foo_checked.h
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we did earlier consider having the uniform approach of creating *_checked_internal.h
for all header files that have Checked-C-specific declarations. At present, there are 14 such header files of which inttypes.h
is the only file with clang-specific declarations. We decided not to create *_checked_internal.h
for all because 13 of these files will be redundant at present.
If a user wants custom plain-C declarations and also Checked C declarations for, say, foo.h
, under the present system he/she can 1) include either foo.h
(for implicit inclusion) or foo_checked.h
(for explicit inclusion) and 2) add -nostdlibinc
and the include directory containing custom plain-C declarations on the compilation command line.
At the end of this pre-existing `bar.h`, we will add the following: | ||
|
||
#if defined __checkedc && !defined NO_IMPLICIT_INCLUDE_CHECKED_HDRS | ||
#include <bar_checked_internal.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the same idea as my previous comment, this could be changed to just:
#include <bar_checked_internal.h> | |
#include <bar_checked.h> |
Then the format of bar.h
would be exactly the same as in the case of no Clang-specific declarations, except for the presence of the declarations between the #include_next <bar.h>
and the Checked-C-specific block at the bottom.
- All the Checked-C-specific declarations (that are currently present in | ||
`bar_checked.h`) will be moved to `bar_checked_internal.h`. The file | ||
`bar_checked.h` will be modified to contain just the following: | ||
|
||
#include <bar.h> | ||
#include <bar_checked_internal.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then:
- All the Checked-C-specific declarations (that are currently present in | |
`bar_checked.h`) will be moved to `bar_checked_internal.h`. The file | |
`bar_checked.h` will be modified to contain just the following: | |
#include <bar.h> | |
#include <bar_checked_internal.h> | |
- The file `bar_checked.h` will contain `#include <bar.h>` and the | |
Checked-C-specific declarations. |
#include <foo_checked.h> | ||
#endif | ||
|
||
- The file `foo_checked.h` will contain `#include_next <foo.h>` and the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could change the #include_next <foo.h>
to #include <foo.h>
(making it consistent with the case where Clang-specific declarations are present) if you add a guard to foo.h
to avoid direct recursion between the two files. (Otherwise, foo.h
does not need a guard because all it does is include two other files that presumably have their own guards. But if it's considered best practice for foo.h
to have a guard anyway, then you could adopt this suggestion.)
are crashing, in order to test all the other test cases on the ADO build machines.
@mattmccutchen-cci Thanks for you review! I will think about your suggestions. Meanwhile, there are five 3C test cases that are crashing on Windows - they all include |
I'll look into this. In the meantime, maybe you could work on #1009 since that's probably a blocker for turning on implicit checked header inclusion by default (it would break existing C code). |
Re the 3C tests crashing on Windows: just looking at the state at the time of the crash, it seems we are using a Clang API incorrectly and this change fixes the tests for me. We need to investigate the problem further (correctcomputation#509), but you can go ahead and use this fix as a basis for further development of this PR and (if need be) merge it even if we haven't completed our investigation yet. |
…hdrfiles.fix-windows-crash isFunctionProtoType should be used with getAs, not dyn_cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thank you! Please commit the changes in the other repos before this change, so automated testing does not fail.
@sulekhark I guess it's time for me to test this with the 3C benchmark test suite then? (I had been waiting for your confirmation.) I can do that later this morning. |
@mattmccutchen-cci Yes, please go ahead and test. |
default now that we've merged Microsoft's change for implicit checked header inclusion (checkedc#998). I filed #520 for follow-ups.
default now that we've merged Microsoft's change for implicit checked header inclusion (checkedc#998). I filed #520 for follow-ups. We don't have to change the benchmark workflow right now: it still passes --includeDir, but that option is ignored when #include updating is off.
This PR has previous discussion in PR #980.