diff --git a/clang/docs/checkedc/3C/CONTRIBUTING.md b/clang/docs/checkedc/3C/CONTRIBUTING.md index 8443c201b5b9..ab534c153dd2 100644 --- a/clang/docs/checkedc/3C/CONTRIBUTING.md +++ b/clang/docs/checkedc/3C/CONTRIBUTING.md @@ -49,128 +49,18 @@ working, so you may have to wait for us to address 5C-specific problems arising from your 3C pull request and/or we may ask you to make specific changes to your pull request to accommodate 5C's code. -## Testing - -3C has a regression test suite located in `clang/test/3C`. At the -appropriate time during development of a pull request, please run it -and correct any failures. (For example, it may not make sense to run -it on a draft pull request containing an unfinished demonstration of -an idea.) The easiest way to run it is to run the following in your -build directory: - -``` -ninja check-3c -``` - -This command will build everything needed that hasn't already been -built, run the test suite, report success or failure (exit 0 or 1, so -you can use it in scripts), and display some information about any -failures, which may or may not be enough for you to understand what -went wrong. - -For deeper troubleshooting, run the following in your build directory -to build all dependencies of the test suite: - -``` -ninja check-3c-deps -``` - -Then run the following in the `clang/test/3C` directory: - -``` -llvm-lit -vv TEST.c -``` - -where `TEST.c` is the path of the test you want to run (you can also -specify more than one test). This assumes you've put the `bin` -subdirectory of your build directory on your `$PATH` or arranged some -other means of running `llvm-lit` from there. The first `-v` makes -`llvm-lit` display the stdout and stderr of failed tests; the second -makes it display the `RUN` commands as they execute so you can tell -which one failed. - -Every `.c` file under `clang/test/3C` is a test file. There are a few -in subdirectories, so `*.c` will not pick up all of them; instead you -can use `llvm-lit -vv .` to specify all test files under the current -directory. - -### Diagnostic verification - -3C supports the standard Clang diagnostic verifier -([`VerifyDiagnosticConsumer`](https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html#details)) -for testing errors and warnings reported by 3C via its main `DiagnosticsEngine`. -(Some 3C errors and warnings are reported via other means and cannot be tested -this way; the best solution we have for them right now is to `grep` the stderr -of 3C.) Diagnostic verification can be enabled via the usual `-Xclang -verify` -compiler option; other diagnostic verification options (`-Xclang --verify=PREFIX`, etc.) should also work as normal. These must be passed as -_compiler_ options, not `3c` options; for example, if you are using `--` on the -`3c` command line, these options must be passed _after_ the `--`. - -Some notes about diagnostic verification in the context of 3C: - -* Parsing of the source files uses some of the compiler logic and thus may - generate compiler warnings, just as if you ran `clang` on the code. These are - sent to the diagnostic verifier along with diagnostics generated by 3C's - analysis. If you find it distracting to have to include the compiler warnings - in the set of expected diagnostics for a test, you can turn them off via the - `-Wno-everything` compiler option (which does not affect diagnostics generated - by 3C's analysis). - -* The `3c` tool works in several passes, where each pass runs on all translation - units: first `3c` parses the source files, then it runs several passes of - analysis. If a pass encounters at least one error, `3c` exits at the end of - that pass. Diagnostic verification does not change the _point_ at which `3c` - exits, but it changes the exit _code_ to indicate the result of verification - rather than the presence of errors. The verification includes the diagnostics - from all passes up to the point at which `3c` exits (i.e., the same - diagnostics that would be displayed if verification were not used). However, - an error that doesn't go via the main `DiagnosticsEngine` will cause an - unsuccessful exit code regardless of diagnostic verification. (This is - typically the behavior you want for a test.) - -* Diagnostic verification is independent for each translation unit, so in tests - with multiple translation units, you'll have to be careful that preprocessing - of each translation unit sees the correct set of `expected-*` directives for - the diagnostics generated for that translation unit (or an - `expected-no-diagnostics` directive if that translation unit generates no - diagnostics, even if other translation units do generate diagnostics). Be - warned that since which translation unit generated a given diagnostic isn't - visible to a normal user, we don't put much work into coming up with sensible - rules for this, but it should at least be deterministic for testing. - -Note that some 3C tests use diagnostic verification on calls to `clang` rather -than `3c`, so if you see `expected-*` directives in a test, you can look at the -`RUN` commands to see which command has `-Xclang -verify` and is using the -directives. If you want to verify diagnostics of more than one `RUN` command in -the same test, you can use different directive prefixes (`-Xclang --verify=PREFIX`). - -## Coding guidelines - -Please follow [LLVM coding -standards](https://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly) -in your code. Specifically: - -* The maximum length of a line: 80 chars - -* All comments should start with a Capital letter. - -* All Local variables, including fields and parameters, should start - with a Capital letter (similar to PascalCase). Short names are - preferred. - -* A space between the conditional keyword and `(` i.e., `if (`, - `while (`, ``for (` etc. - -* Space after the type name, i.e., `Type *K` _not_ `Type* K`. - -* Space before and after `:` in iterators, i.e., `for (auto &k : List)` - -Our goal is that all files should be formatted with `clang-format` and -pass `clang-tidy` ([more information](clang-tidy.md)), and nonempty -files should have a final newline (surprisingly, `clang-format` cannot -enforce this). However, until we have better automation, we decided it -isn't reasonable to require contributors to manually run these tools -and fix style nits in each change; instead, we periodically run the -tools on the entire 3C codebase. +At the appropriate time during development of a pull request, please +run the [regression tests](development.md#regression-tests) and +correct any failures. (For example, it may not make sense to do this +on a draft pull request containing an unfinished demonstration of an +idea.) All regression tests must pass (or be disabled if appropriate) +before your pull request can be merged. If you're changing behavior +(as opposed to just cleaning up the code), we'll typically require you +to add or update enough tests to exercise the important behavior +changes (i.e., those tests fail before your code change and pass after +it). If there's a concern that your change might affect other cases +that are not adequately tested yet, we may ask you to add tests for +those cases as well. + +See the [developer's guide](development.md) for additional information +that may be helpful as you work on 3C. diff --git a/clang/docs/checkedc/3C/development.md b/clang/docs/checkedc/3C/development.md new file mode 100644 index 000000000000..97f899760910 --- /dev/null +++ b/clang/docs/checkedc/3C/development.md @@ -0,0 +1,144 @@ +# 3C Developer's Guide + +This file collects information that is important or useful to know +when developing 3C as well as some standards we generally expect the +code to follow. (Some other standards are covered in the [contribution +guide](CONTRIBUTING.md)). If you could use help with something that's +not documented here, feel free to ask. + +## Regression tests + +3C has a regression test suite located in `clang/test/3C`. The easiest +way to run it is to run the following in your build directory: + +``` +ninja check-3c +``` + +This command will build everything needed that hasn't already been +built, run the test suite, report success or failure (exit 0 or 1, so +you can use it in scripts), and display some information about any +failures, which may or may not be enough for you to understand what +went wrong. + +For deeper troubleshooting, run the following in your build directory +to build all dependencies of the test suite: + +``` +ninja check-3c-deps +``` + +Then run the following in the `clang/test/3C` directory: + +``` +llvm-lit -vv TEST.c +``` + +where `TEST.c` is the path of the test you want to run (you can also +specify more than one test). This assumes you've put the `bin` +subdirectory of your build directory on your `$PATH` or arranged some +other means of running `llvm-lit` from there. The first `-v` makes +`llvm-lit` display the stdout and stderr of failed tests; the second +makes it display the `RUN` commands as they execute so you can tell +which one failed. + +Every `.c` file under `clang/test/3C` is a test file. There are a few +in subdirectories, so `*.c` will not pick up all of them; instead you +can use `llvm-lit -vv .` to specify all test files under the current +directory. + +### Diagnostic verification + +3C supports the standard Clang diagnostic verifier +([`VerifyDiagnosticConsumer`](https://clang.llvm.org/doxygen/classclang_1_1VerifyDiagnosticConsumer.html#details)) +for testing errors and warnings reported by 3C via its main `DiagnosticsEngine`. +(Some 3C errors and warnings are reported via other means and cannot be tested +this way; the best solution we have for them right now is to `grep` the stderr +of 3C.) Diagnostic verification can be enabled via the usual `-Xclang -verify` +compiler option; other diagnostic verification options (`-Xclang +-verify=PREFIX`, etc.) should also work as normal. These must be passed as +_compiler_ options, not `3c` options; for example, if you are using `--` on the +`3c` command line, these options must be passed _after_ the `--`. + +Some notes about diagnostic verification in the context of 3C: + +* Parsing of the source files uses some of the compiler logic and thus may + generate compiler warnings, just as if you ran `clang` on the code. These are + sent to the diagnostic verifier along with diagnostics generated by 3C's + analysis. If you find it distracting to have to include the compiler warnings + in the set of expected diagnostics for a test, you can turn them off via the + `-Wno-everything` compiler option (which does not affect diagnostics generated + by 3C's analysis). + +* The `3c` tool works in several passes, where each pass runs on all translation + units: first `3c` parses the source files, then it runs several passes of + analysis. If a pass encounters at least one error, `3c` exits at the end of + that pass. Diagnostic verification does not change the _point_ at which `3c` + exits, but it changes the exit _code_ to indicate the result of verification + rather than the presence of errors. The verification includes the diagnostics + from all passes up to the point at which `3c` exits (i.e., the same + diagnostics that would be displayed if verification were not used). However, + an error that doesn't go via the main `DiagnosticsEngine` will cause an + unsuccessful exit code regardless of diagnostic verification. (This is + typically the behavior you want for a test.) + +* Diagnostic verification is independent for each translation unit, so in tests + with multiple translation units, you'll have to be careful that preprocessing + of each translation unit sees the correct set of `expected-*` directives for + the diagnostics generated for that translation unit (or an + `expected-no-diagnostics` directive if that translation unit generates no + diagnostics, even if other translation units do generate diagnostics). Be + warned that since which translation unit generated a given diagnostic isn't + visible to a normal user, we don't put much work into coming up with sensible + rules for this, but it should at least be deterministic for testing. + +Note that some 3C tests use diagnostic verification on calls to `clang` rather +than `3c`, so if you see `expected-*` directives in a test, you can look at the +`RUN` commands to see which command has `-Xclang -verify` and is using the +directives. If you want to verify diagnostics of more than one `RUN` command in +the same test, you can use different directive prefixes (`-Xclang +-verify=PREFIX`). + +## Debugging + +### AST dumping + +`3c` honors the usual `-Xclang -ast-dump` compiler option to dump the +AST to stdout before running its analysis. (This must be passed as a +compiler option, not a `3c` option, e.g., _after_ the `--` on the +command line if applicable.) The dump includes the AST node memory +addresses (e.g., `VarDecl 0x5569e5ef3988 ...`), so if you see in the +debugger that (for example) a `Decl *` variable in 3C has the value +`0x5569e5ef3988`, you can look for that memory address in the AST dump +to quickly see what AST node it is (modulo pointer adjustments for +multiple inheritance; adding a debugger watch with a cast to the +pointer type used in the AST dump can help with this). + +## Coding guidelines + +Please follow [LLVM coding +standards](https://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly) +in your code. Specifically: + +* The maximum length of a line: 80 chars + +* All comments should start with a Capital letter. + +* All Local variables, including fields and parameters, should start + with a Capital letter (similar to PascalCase). Short names are + preferred. + +* A space between the conditional keyword and `(` i.e., `if (`, + `while (`, ``for (` etc. + +* Space after the type name, i.e., `Type *K` _not_ `Type* K`. + +* Space before and after `:` in iterators, i.e., `for (auto &k : List)` + +Our goal is that all files should be formatted with `clang-format` and +pass `clang-tidy` ([more information](clang-tidy.md)), and nonempty +files should have a final newline (surprisingly, `clang-format` cannot +enforce this). However, until we have better automation, we decided it +isn't reasonable to require contributors to manually run these tools +and fix style nits in each change; instead, we periodically run the +tools on the entire 3C codebase. diff --git a/clang/include/clang/3C/3C.h b/clang/include/clang/3C/3C.h index 5352188ddbdb..7253ba252096 100644 --- a/clang/include/clang/3C/3C.h +++ b/clang/include/clang/3C/3C.h @@ -22,58 +22,6 @@ #include "clang/Tooling/CommonOptionsParser.h" #include -// Options used to initialize 3C tool. -// -// See clang/docs/checkedc/3C/clang-tidy.md#_3c-name-prefix -// NOLINTNEXTLINE(readability-identifier-naming) -struct _3COptions { - bool DumpIntermediate; - - bool Verbose; - - std::string OutputPostfix; - std::string OutputDir; - - std::string ConstraintOutputJson; - - bool DumpStats; - - std::string StatsOutputJson; - - std::string WildPtrInfoJson; - - std::string PerPtrInfoJson; - - std::vector AllocatorFunctions; - - bool HandleVARARGS; - - bool EnablePropThruIType; - - std::string BaseDir; - bool AllowSourcesOutsideBaseDir; - - bool EnableAllTypes; - - bool AddCheckedRegions; - - bool EnableCCTypeChecker; - - bool WarnRootCause; - - bool WarnAllRootCause; - -#ifdef FIVE_C - bool RemoveItypes; - bool ForceItypes; -#endif - - bool DumpUnwritableChanges; - bool AllowUnwritableChanges; - - bool AllowRewriteFailures; -}; - // The main interface exposed by the 3C to interact with the tool. // // See clang/docs/checkedc/3C/clang-tidy.md#_3c-name-prefix diff --git a/clang/include/clang/3C/3CGlobalOptions.h b/clang/include/clang/3C/3CGlobalOptions.h index ae5b38505e89..2615c13a9e75 100644 --- a/clang/include/clang/3C/3CGlobalOptions.h +++ b/clang/include/clang/3C/3CGlobalOptions.h @@ -15,28 +15,49 @@ #include "llvm/Support/CommandLine.h" -extern bool Verbose; -extern bool DumpIntermediate; -extern std::string OutputPostfix; -extern std::string OutputDir; -extern bool HandleVARARGS; -extern bool SeperateMultipleFuncDecls; -extern bool EnablePropThruIType; -extern bool ConsiderAllocUnsafe; -extern bool AllTypes; -extern bool NewSolver; -extern std::string BaseDir; -extern std::vector AllocatorFunctions; -extern bool AddCheckedRegions; -extern bool WarnRootCause; -extern bool WarnAllRootCause; -extern bool DumpUnwritableChanges; -extern bool AllowUnwritableChanges; -extern bool AllowRewriteFailures; - +// Options used to initialize 3C tool. +// +// See clang/docs/checkedc/3C/clang-tidy.md#_3c-name-prefix +// NOLINTNEXTLINE(readability-identifier-naming) +struct _3COptions { + bool DumpIntermediate; + bool Verbose; + std::string OutputPostfix; + std::string OutputDir; + std::string ConstraintOutputJson; + bool DumpStats; + std::string StatsOutputJson; + std::string WildPtrInfoJson; + std::string PerWildPtrInfoJson; + std::vector AllocatorFunctions; + bool HandleVARARGS; + std::string BaseDir; + bool AllowSourcesOutsideBaseDir; + bool AllTypes; + bool AddCheckedRegions; + bool EnableCCTypeChecker; + bool WarnRootCause; + bool WarnAllRootCause; + bool DumpUnwritableChanges; + bool AllowUnwritableChanges; + bool AllowRewriteFailures; + bool ItypesForExtern; + bool InferTypesForUndefs; + bool DebugSolver; + bool OnlyGreatestSol; + bool OnlyLeastSol; + bool DisableRDs; + bool DisableFunctionEdges; + bool DisableInfDecls; + bool DisableArrH; + bool DebugArrSolver; #ifdef FIVE_C -extern bool RemoveItypes; -extern bool ForceItypes; + bool RemoveItypes; + bool ForceItypes; #endif +}; + +// NOLINTNEXTLINE(readability-identifier-naming) +extern struct _3COptions _3COpts; #endif // LLVM_CLANG_3C_3CGLOBALOPTIONS_H diff --git a/clang/include/clang/3C/3CInteractiveData.h b/clang/include/clang/3C/3CInteractiveData.h index 2c72045fc575..ec66c5b263ab 100644 --- a/clang/include/clang/3C/3CInteractiveData.h +++ b/clang/include/clang/3C/3CInteractiveData.h @@ -16,18 +16,26 @@ #include "clang/3C/ConstraintVariables.h" #include "clang/3C/PersistentSourceLoc.h" -// Source info and reason for each wild pointer. -class WildPointerInferenceInfo { +// Source info and reason +class RootCauseDiagnostic { public: - WildPointerInferenceInfo(std::string Reason, const PersistentSourceLoc PSL) - : WildPtrReason(Reason), Location(PSL) {} + RootCauseDiagnostic() = default; + explicit RootCauseDiagnostic(ReasonLoc &Rsn) : Main(Rsn) {} - const std::string &getWildPtrReason() const { return WildPtrReason; } - const PersistentSourceLoc &getLocation() const { return Location; } + const std::string &getReason() { return Main.Reason; } + void setReason(const std::string &Rsn) { Main.Reason = Rsn; } + + const PersistentSourceLoc &getLocation() const { return Main.Location; } + + void addReason(const ReasonLoc &Rsn) { + Supplemental.push_back(Rsn); + } + + std::vector &additionalNotes() { return Supplemental; } private: - std::string WildPtrReason = ""; - PersistentSourceLoc Location; + ReasonLoc Main; + std::vector Supplemental; }; // Constraints information. @@ -44,13 +52,13 @@ class ConstraintsInfo { void printRootCauseStats(raw_ostream &O, Constraints &CS); int getNumPtrsAffected(ConstraintKey CK); - std::map RootWildAtomsWithReason; + std::map RootWildAtomsWithReason; CVars AllWildAtoms; CVars InSrcWildAtoms; CVars TotalNonDirectWildAtoms; CVars InSrcNonDirectWildAtoms; std::set ValidSourceFiles; - std::map AtomSourceMap; + std::map AtomSourceMap; private: // Root cause map: This is the map of a Constraint var and a set of diff --git a/clang/include/clang/3C/ABounds.h b/clang/include/clang/3C/ABounds.h index d65874b49d8a..015907eedbf6 100644 --- a/clang/include/clang/3C/ABounds.h +++ b/clang/include/clang/3C/ABounds.h @@ -32,17 +32,26 @@ class ABounds { CountPlusOneBoundKind, // Bounds that represent number of bytes. ByteBoundKind, - // Bounds that represent range. - RangeBoundKind, }; BoundsKind getKind() const { return Kind; } protected: + ABounds(BoundsKind K, BoundsKey L, BoundsKey B) : Kind(K), LengthKey(L), + LowerBoundKey(B) {} + ABounds(BoundsKind K, BoundsKey L) : ABounds(K, L, 0) {} + BoundsKind Kind; -protected: - ABounds(BoundsKind K) : Kind(K) {} - void addBoundsUsedKey(BoundsKey); + // Bounds key representing the length of the bounds from the base pointer of + // the range. The exact interpretation of this field varies by subclass. + BoundsKey LengthKey; + + // The base pointer representing the start of the range of the bounds. If this + // is not equal to 0, then this ABounds has a specific lower bound that should + // be used when emitting array pointer bounds. Otherwise, if it is 0, then the + // lower bound should implicitly be the pointer the bound is applied to. + BoundsKey LowerBoundKey; + // Get the variable name of the the given bounds key that corresponds // to the given declaration. static std::string getBoundsKeyStr(BoundsKey, AVarBoundsInfo *, @@ -51,15 +60,30 @@ class ABounds { public: virtual ~ABounds() {} - virtual std::string mkString(AVarBoundsInfo *, clang::Decl *D = nullptr) = 0; + // Make a string representation of this array bound. If the array has a + // defined lower bound pointer that is not the same as the pointer for which + // the bound string is being generated (passed as parameter BK), then a range + // bound is generated using that lower bound. Otherwise, a standard count + // bound is generated. + std::string + mkString(AVarBoundsInfo *ABI, clang::Decl *D = nullptr, BoundsKey BK = 0); + + // Make a string representation of this array bound always generating explicit + // lower bounds in range bounds expressions. + virtual std::string + mkStringWithLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) = 0; + + // Make a string representation of this array bound ignoring any lower bound + // information. A standard count bound is always generated. + virtual std::string + mkStringWithoutLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) = 0; + virtual bool areSame(ABounds *, AVarBoundsInfo *) = 0; - virtual BoundsKey getBKey() = 0; virtual ABounds *makeCopy(BoundsKey NK) = 0; - // Set that maintains all the bound keys that are used inin - // TODO: Is this still needed? - static std::set KeysUsedInBounds; - static bool isKeyUsedInBounds(BoundsKey ToCheck); + BoundsKey getLengthKey() const { return LengthKey; } + BoundsKey getLowerBoundKey() const { return LowerBoundKey; } + void setLowerBoundKey(BoundsKey LB) { LowerBoundKey = LB; } static ABounds *getBoundsInfo(AVarBoundsInfo *AVBInfo, BoundsExpr *BExpr, const ASTContext &C); @@ -67,34 +91,38 @@ class ABounds { class CountBound : public ABounds { public: - CountBound(BoundsKey Var) : ABounds(CountBoundKind), CountVar(Var) { - addBoundsUsedKey(Var); - } + CountBound(BoundsKey L, BoundsKey B) : ABounds(CountBoundKind, L, B) {} + CountBound(BoundsKey L) : ABounds(CountBoundKind, L) {} - virtual ~CountBound() {} + std::string + mkStringWithLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; + std::string + mkStringWithoutLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; - std::string mkString(AVarBoundsInfo *ABI, clang::Decl *D = nullptr) override; bool areSame(ABounds *O, AVarBoundsInfo *ABI) override; - BoundsKey getBKey() override; + ABounds *makeCopy(BoundsKey NK) override; static bool classof(const ABounds *S) { return S->getKind() == CountBoundKind; } - - BoundsKey getCountVar() { return CountVar; } - -protected: - BoundsKey CountVar; }; class CountPlusOneBound : public CountBound { public: - CountPlusOneBound(BoundsKey Var) : CountBound(Var) { + CountPlusOneBound(BoundsKey L, BoundsKey B) : CountBound(L, B) { this->Kind = CountPlusOneBoundKind; } - std::string mkString(AVarBoundsInfo *ABI, clang::Decl *D = nullptr) override; + CountPlusOneBound(BoundsKey L) : CountBound(L) { + this->Kind = CountPlusOneBoundKind; + } + + std::string + mkStringWithLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; + std::string + mkStringWithoutLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; + bool areSame(ABounds *O, AVarBoundsInfo *ABI) override; static bool classof(const ABounds *S) { @@ -104,54 +132,20 @@ class CountPlusOneBound : public CountBound { class ByteBound : public ABounds { public: - ByteBound(BoundsKey Var) : ABounds(ByteBoundKind), ByteVar(Var) { - addBoundsUsedKey(Var); - } + ByteBound(BoundsKey L, BoundsKey B) : ABounds(ByteBoundKind, L, B) {} + ByteBound(BoundsKey L) : ABounds(ByteBoundKind, L) {} - virtual ~ByteBound() {} + std::string + mkStringWithLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; + std::string + mkStringWithoutLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) override; - std::string mkString(AVarBoundsInfo *ABI, clang::Decl *D = nullptr) override; bool areSame(ABounds *O, AVarBoundsInfo *ABI) override; - BoundsKey getBKey() override; ABounds *makeCopy(BoundsKey NK) override; static bool classof(const ABounds *S) { return S->getKind() == ByteBoundKind; } - BoundsKey getByteVar() { return ByteVar; } - -private: - BoundsKey ByteVar; }; -class RangeBound : public ABounds { -public: - RangeBound(BoundsKey L, BoundsKey R) : ABounds(RangeBoundKind), LB(L), UB(R) { - addBoundsUsedKey(L); - addBoundsUsedKey(R); - } - - virtual ~RangeBound() {} - - std::string mkString(AVarBoundsInfo *ABI, clang::Decl *D = nullptr) override; - bool areSame(ABounds *O, AVarBoundsInfo *ABI) override; - - BoundsKey getBKey() override { - assert(false && "Not implemented."); - return 0; - } - - ABounds *makeCopy(BoundsKey NK) override { - assert(false && "Not Implemented"); - return nullptr; - } - - static bool classof(const ABounds *S) { - return S->getKind() == RangeBoundKind; - } - -private: - BoundsKey LB; - BoundsKey UB; -}; #endif // LLVM_CLANG_3C_ABOUNDS_H diff --git a/clang/include/clang/3C/AVarBoundsInfo.h b/clang/include/clang/3C/AVarBoundsInfo.h index 1786fd01af6a..34151501c63d 100644 --- a/clang/include/clang/3C/AVarBoundsInfo.h +++ b/clang/include/clang/3C/AVarBoundsInfo.h @@ -106,18 +106,18 @@ class AvarBoundsInference { // Infer bounds for the given key from the set of given ARR atoms. // The flag FromPB requests the inference to use potential length variables. - bool inferBounds(BoundsKey K, AVarGraph &BKGraph, bool FromPB = false); + bool inferBounds(BoundsKey K, const AVarGraph &BKGraph, bool FromPB = false); - // Get a consistent bound for all the arrays whose bounds have been - // inferred. - bool convergeInferredBounds(); + // Get a consistent bound for all the arrays whose bounds have been inferred. + void convergeInferredBounds(); private: // Find all the reachable variables form FromVarK that are visible // in DstScope bool getReachableBoundKeys(const ProgramVarScope *DstScope, BoundsKey FromVarK, std::set &PotK, - AVarGraph &BKGraph, bool CheckImmediate = false); + const AVarGraph &BKGraph, + bool CheckImmediate = false); // Check if bounds specified by Bnds are declared bounds of K. bool areDeclaredBounds( @@ -125,12 +125,12 @@ class AvarBoundsInference { const std::pair> &Bnds); // Get all the bounds of the given array i.e., BK - bool getRelevantBounds(BoundsKey BK, BndsKindMap &ResBounds); + void getRelevantBounds(BoundsKey BK, BndsKindMap &ResBounds); // Predict possible bounds for DstArrK from the bounds of Neighbours. // Return true if there is any change in the captured bounds information. - bool predictBounds(BoundsKey DstArrK, std::set &Neighbours, - AVarGraph &BKGraph); + bool predictBounds(BoundsKey DstArrK, const std::set &Neighbours, + const AVarGraph &BKGraph); void mergeReachableProgramVars(BoundsKey TarBK, std::set &AllVars); @@ -139,7 +139,7 @@ class AvarBoundsInference { // Set the given pointer to have impossible bounds. void setImpossibleBounds(BoundsKey BK); // Infer bounds of the given pointer key from potential bounds. - bool inferFromPotentialBounds(BoundsKey BK, AVarGraph &BKGraph); + bool inferFromPotentialBounds(BoundsKey BK, const AVarGraph &BKGraph); AVarBoundsInfo *BI; @@ -147,6 +147,8 @@ class AvarBoundsInference { std::map CurrIterInferBounds; // BoundsKey that failed the flow inference. std::set BKsFailedFlowInference; + + ABounds *getPreferredBound(BoundsKey BK); }; // Class that maintains information about potential bounds for @@ -179,7 +181,8 @@ class AVarBoundsInfo { public: AVarBoundsInfo() : ProgVarGraph(this), CtxSensProgVarGraph(this), - RevCtxSensProgVarGraph(this), CSBKeyHandler(this) { + RevCtxSensProgVarGraph(this), CSBKeyHandler(this), + LowerBoundGraph(this) { BCount = 1; PVarInfo.clear(); InProgramArrPtrBoundsKeys.clear(); @@ -195,6 +198,8 @@ class AVarBoundsInfo { bool isValidBoundVariable(clang::Decl *D); void insertDeclaredBounds(clang::Decl *D, ABounds *B); + void insertDeclaredBounds(BoundsKey BK, ABounds *B); + bool mergeBounds(BoundsKey L, BoundsPriority P, ABounds *B); bool removeBounds(BoundsKey L, BoundsPriority P = Invalid); bool replaceBounds(BoundsKey L, BoundsPriority P, ABounds *B); @@ -227,11 +232,9 @@ class AVarBoundsInfo { // Add Assignments between variables. These methods will add edges between // corresponding BoundsKeys - bool addAssignment(clang::Decl *L, clang::Decl *R); - bool addAssignment(clang::DeclRefExpr *L, clang::DeclRefExpr *R); bool addAssignment(BoundsKey L, BoundsKey R); - bool handlePointerAssignment(clang::Stmt *St, clang::Expr *L, clang::Expr *R, - ASTContext *C, ConstraintResolver *CR); + bool handlePointerAssignment(clang::Expr *L, clang::Expr *R, ASTContext *C, + ConstraintResolver *CR); bool handleAssignment(clang::Expr *L, const CVarSet &LCVars, const std::set &CSLKeys, clang::Expr *R, const CVarSet &RCVars, @@ -248,14 +251,43 @@ class AVarBoundsInfo { // for pointers that has pointer arithmetic performed on them. void recordArithmeticOperation(clang::Expr *E, ConstraintResolver *CR); - // Check if the given bounds key has a pointer arithmetic done on it. - bool hasPointerArithmetic(BoundsKey BK); + // Check if the given bounds key will need to be duplicated during rewriting + // to generate a fresh lower bound. This happens when a pointer is not a valid + // lower bounds due to pointer arithmetic, and lower bounds inference fails to + // find a consistent lower bound among existing pointers in the source code. + bool needsFreshLowerBound(BoundsKey BK); + bool needsFreshLowerBound(ConstraintVariable *CV); + + // Return true when a lower bound could be inferred for the array pointer + // corresponding to `BK`. This is the case either when `BK` was not + // invalidated as lower bound by pointer arithmetic meaning it is it's own + // lower bound, or when `BK` was invalidated, but a valid lower bound could be + // inferred. + bool hasLowerBound(BoundsKey BK); + + // Record that a pointer cannot be rewritten to use range bounds. This might + // be due to 3C rewriting limitations (assignments appearing inside macros), + // or it might be a Checked C limitation (the current style of range bounds + // can't properly initialized on global variables without error). + void markIneligibleForFreshLowerBound(BoundsKey BK); // Get the ProgramVar for the provided VarKey. - ProgramVar *getProgramVar(BoundsKey VK); + // This method can return `nullptr` if there is no corresponding ProgramVar. + // It's not obvious when a BoundsKey can be expected to have a ProgramVar, so + // callers should typically check for null. + ProgramVar *getProgramVar(BoundsKey VK) const; + + // Get the Scope of the provided BoundsKey. + // This method returns nullptr if `getProgramVar(BK)` would return nullptr. + const ProgramVarScope *getProgramVarScope(BoundsKey BK) const; + + // Return true when BoundsKey `To` can be accessed from the scope of `from`. + // Note that this returns false if either BoundsKey cannot be mapped to a + // ProgramVar (and therefore can't be mapped to a scope). + bool isInAccessibleScope(BoundsKey From, BoundsKey To); // Propagate the array bounds information for all array ptrs. - bool performFlowAnalysis(ProgramInfo *PI); + void performFlowAnalysis(ProgramInfo *PI); // Get the context sensitive BoundsKey for the given key at CallSite // located at PSL. @@ -288,6 +320,15 @@ class AVarBoundsInfo { // If yes, provide the index of the parameter. bool isFuncParamBoundsKey(BoundsKey BK, unsigned &PIdx); + void addConstantArrayBounds(ProgramInfo &I); + + // This is the main entry point to start lower bound inference. It populates + // the map LowerBounds and set NeedFreshLowerBounds with the result of the + // analysis. LowerBounds is accessed during the rest of bounds inference, so + // this method must be executed before performFlowAnalysis which handles the + // majority of the work for length inference. + void inferLowerBounds(ProgramInfo *PI); + private: friend class AvarBoundsInference; friend class CtxSensitiveBoundsKeyHandler; @@ -309,8 +350,17 @@ class AVarBoundsInfo { // Set that contains BoundsKeys of variables which have invalid bounds. std::set InvalidBounds; // These are the bounds key of the pointers that has arithmetic operations - // performed on them. + // performed on them. These pointers cannot have the standard `count(n)` + // bounds and instead must use range bounds with an explict lower bound + // e.g., `bounds(p, p + n)`. std::set ArrPointersWithArithmetic; + + // Some pointers, however, cannot be automatically given range bounds. This + // includes global variables and structure fields. If a pointer is in both the + // above pointer arithmetic set and this set, then it cannot be assigned any + // bound. + std::set IneligibleForFreshLowerBound; + // Set of BoundsKeys that correspond to pointers. std::set PointerBoundsKey; // Set of BoundsKey that correspond to array pointers. @@ -336,6 +386,9 @@ class AVarBoundsInfo { // BiMap of function keys and BoundsKey for function return values. BiMap, BoundsKey> FuncDeclVarMap; + PVConstraint * + getConstraintVariable(const ProgramInfo *PI, BoundsKey BK) const; + // Graph of all program variables. AVarGraph ProgVarGraph; // Graph that contains only edges from normal BoundsKey to @@ -350,6 +403,31 @@ class AVarBoundsInfo { // Context-sensitive bounds key handler CtxSensitiveBoundsKeyHandler CSBKeyHandler; + // This graph is used of for determining which pointers are valid lower + // bounds, and so are eligible for use as their own lower bound (implicitly as + // a count bounds) or as the lower bound for another pointer in a range bound. + // It is also used to infer lower bounds for the pointers that are not + // eligible to be their own lower bound. + AVarGraph LowerBoundGraph; + // In the LowerBoundGraph the constant 0 is used to represent the global + // singleton invalid pointer. + const BoundsKey InvalidLowerBoundKey = 0; + + // BoundsKeys that that cannot be used as a lower bound. These are used in an + // update such as `a = a + 1`, or are transitively assigned from such a + // pointer. + std::set InvalidLowerBounds; + + // Mapping from pointers to their inferred lower bounds. A pointer maps to + // itself if it can use a simple count bound. Missing pointers have no valid + // lower bound, so no length should be inferred during bounds inference. + std::map LowerBounds; + + // Some variables have to valid lower bound in the original source code, but + // we are able to insert a temporary pointer variable to be the lower bound. + // Keep track of these for special handling during rewriting. + std::set NeedFreshLowerBounds; + // BoundsKey helper function: These functions help in getting bounds key from // various artifacts. bool hasVarKey(PersistentSourceLoc &PSL); @@ -366,23 +444,48 @@ class AVarBoundsInfo { bool isFunctionReturn(BoundsKey BK); // Of all the pointer bounds key, find arr pointers. - void computerArrPointers(ProgramInfo *PI, std::set &Ret); + void computeArrPointers(const ProgramInfo *PI); // Get all the array pointers that need bounds. - void getBoundsNeededArrPointers(const std::set &ArrPtrs, - std::set &AB); + void getBoundsNeededArrPointers(std::set &AB) const; // Keep only highest priority bounds for all the provided BoundsKeys // returns true if any thing changed, else false. - bool keepHighestPriorityBounds(std::set &ArrPtrs); + bool keepHighestPriorityBounds(); // Perform worklist based inference on the requested array variables using // the provided graph and potential length variables. - bool performWorkListInference(const std::set &ArrNeededBounds, - AVarGraph &BKGraph, AvarBoundsInference &BI, - bool FromPB); + void performWorkListInference(const AVarGraph &BKGraph, + AvarBoundsInference &BI, bool FromPB); void insertParamKey(ParamDeclType ParamDecl, BoundsKey NK); + + void dumpBounds(); + + // Compute which array pointers are not valid lower bounds. This includes any + // pointers directly updated in pointer arithmetic expression, as well as any + // pointers transitively assigned to from these pointers. This is computed + // using essentially the same algorithm as is used for solving the checked + // type constraint graph. + void computeInvalidLowerBounds(ProgramInfo *PI); + + // During lower bound inference it may be necessary to generate temporary + // pointers to act as lower bounds for arrays that otherwise don't have a + // consistent lower bound. This method takes a bounds key for an array pointer + // and returns a fresh bounds key that can be used as the lower bound for the + // array bounds of that pointer. + BoundsKey getFreshLowerBound(BoundsKey Arr); + + // Return true if the scope of the BoundsKey is one in which lower bounds + // can be inserted. BoundsKeys in context sensitive scope should not get lower + // bounds. The corresponding non-context-sensitive BoundsKey should instead. + bool scopeCanHaveLowerBound(BoundsKey BK); + + // Check if a fresh lower bound can be be inserted by 3C for the pointer + // corresponding to the bounds key. When a pointer needs a fresh lower bound, + // it is possible that 3C will not support inserting the new declaration. + // No array bounds can be inferred for such pointers. + bool isEligibleForFreshLowerBound(BoundsKey BK); }; #endif // LLVM_CLANG_3C_AVARBOUNDSINFO_H diff --git a/clang/include/clang/3C/ArrayBoundsInferenceConsumer.h b/clang/include/clang/3C/ArrayBoundsInferenceConsumer.h index d6101f3f3f47..8d45045cf48b 100644 --- a/clang/include/clang/3C/ArrayBoundsInferenceConsumer.h +++ b/clang/include/clang/3C/ArrayBoundsInferenceConsumer.h @@ -25,7 +25,7 @@ class AllocBasedBoundsInference : public ASTConsumer { public: explicit AllocBasedBoundsInference(ProgramInfo &I, clang::ASTContext *C) : Info(I) {} - virtual void HandleTranslationUnit(ASTContext &Context); + void HandleTranslationUnit(ASTContext &Context) override; private: ProgramInfo &Info; @@ -69,11 +69,11 @@ class LocalVarABVisitor : public clang::RecursiveASTVisitor { bool VisitSwitchStmt(SwitchStmt *S); bool VisitBinaryOperator(BinaryOperator *O); bool VisitArraySubscriptExpr(ArraySubscriptExpr *E); - bool isNonLengthParameter(ParmVarDecl *PVD); + bool isNonLengthParameter(ParmVarDecl *PVD) const; private: void handleAssignment(BoundsKey LK, QualType LHSType, Expr *RHS); - void addUsedParmVarDecl(Expr *CE); + void addNonLengthParameter(Expr *CE); std::set NonLengthParameters; ASTContext *Context; ProgramInfo &Info; @@ -88,8 +88,6 @@ class LengthVarInference : public StmtVisitor { public: LengthVarInference(ProgramInfo &In, ASTContext *AC, FunctionDecl *F); - virtual ~LengthVarInference(); - void VisitStmt(Stmt *St); void VisitArraySubscriptExpr(ArraySubscriptExpr *ASE); @@ -98,11 +96,9 @@ class LengthVarInference : public StmtVisitor { std::map StMap; ProgramInfo &I; ASTContext *C; - FunctionDecl *FD; CFGBlock *CurBB; - ControlDependencyCalculator *CDG; - ConstraintResolver *CR; std::unique_ptr Cfg; + ControlDependencyCalculator CDG; }; void handleArrayVariablesBoundsDetection(ASTContext *C, ProgramInfo &I, diff --git a/clang/include/clang/3C/CastPlacement.h b/clang/include/clang/3C/CastPlacement.h index a822d2d172c5..994f700223c5 100644 --- a/clang/include/clang/3C/CastPlacement.h +++ b/clang/include/clang/3C/CastPlacement.h @@ -62,9 +62,12 @@ class CastPlacementVisitor : public RecursiveASTVisitor { ConstraintVariable *DstExt); std::pair getCastString(ConstraintVariable *Dst, + ConstraintVariable *TypeVar, CastNeeded CastKind); - void surroundByCast(ConstraintVariable *Dst, CastNeeded CastKind, Expr *E); + void surroundByCast(ConstraintVariable *Dst, + ConstraintVariable *TypeVar, + CastNeeded CastKind, Expr *E); void reportCastInsertionFailure(Expr *E, const std::string &CastStr); void updateRewriteStats(CastNeeded CastKind); }; diff --git a/clang/include/clang/3C/CheckedRegions.h b/clang/include/clang/3C/CheckedRegions.h index ef5316d6693f..04eba6c4ff61 100644 --- a/clang/include/clang/3C/CheckedRegions.h +++ b/clang/include/clang/3C/CheckedRegions.h @@ -81,7 +81,7 @@ class CheckedRegionFinder bool containsUncheckedPtr(clang::QualType Qt); bool containsUncheckedPtrAcc(clang::QualType Qt, std::set &Seen); bool isUncheckedStruct(clang::QualType Qt, std::set &Seen); - void emitCauseDiagnostic(PersistentSourceLoc *); + void emitCauseDiagnostic(PersistentSourceLoc); bool isWild(CVarOption CVar); clang::ASTContext *Context; @@ -89,7 +89,7 @@ class CheckedRegionFinder ProgramInfo &Info; std::set &Seen; std::map ⤅ - std::set Emitted; + std::set Emitted; bool EmitWarnings; }; diff --git a/clang/include/clang/3C/ConstraintBuilder.h b/clang/include/clang/3C/ConstraintBuilder.h index 924fc18e2e24..dc667824af89 100644 --- a/clang/include/clang/3C/ConstraintBuilder.h +++ b/clang/include/clang/3C/ConstraintBuilder.h @@ -23,7 +23,7 @@ class VariableAdderConsumer : public clang::ASTConsumer { explicit VariableAdderConsumer(ProgramInfo &I, clang::ASTContext *C) : Info(I) {} - virtual void HandleTranslationUnit(clang::ASTContext &); + void HandleTranslationUnit(clang::ASTContext &) override; private: ProgramInfo &Info; @@ -36,7 +36,7 @@ class ConstraintBuilderConsumer : public clang::ASTConsumer { explicit ConstraintBuilderConsumer(ProgramInfo &I, clang::ASTContext *C) : Info(I) {} - virtual void HandleTranslationUnit(clang::ASTContext &); + void HandleTranslationUnit(clang::ASTContext &) override; private: ProgramInfo &Info; diff --git a/clang/include/clang/3C/ConstraintResolver.h b/clang/include/clang/3C/ConstraintResolver.h index 6b98441333a1..8099590cbf80 100644 --- a/clang/include/clang/3C/ConstraintResolver.h +++ b/clang/include/clang/3C/ConstraintResolver.h @@ -42,6 +42,8 @@ class ConstraintResolver { CVarSet getCalleeConstraintVars(CallExpr *CE); + bool isCastofGeneric(CastExpr *C); + // Handle assignment of RHS expression to LHS expression using the // given action. void constrainLocalAssign(Stmt *TSt, Expr *LHS, Expr *RHS, @@ -69,15 +71,18 @@ class ConstraintResolver { CVarSet getInvalidCastPVCons(CastExpr *E); - // Update a PVConstraint with one additional level of indirection - PVConstraint *addAtom(PVConstraint *PVC, ConstAtom *NewA, Constraints &CS); - CVarSet addAtomAll(CVarSet CVS, ConstAtom *PtrTyp, Constraints &CS); + CVarSet addAtomAll(CVarSet CVS, ConstAtom *PtrTyp, + ReasonLoc &Rsn, Constraints &CS); CVarSet pvConstraintFromType(QualType TypE); CSetBkeyPair getAllSubExprConstraintVars(std::vector &Exprs); CVarSet getBaseVarPVConstraint(DeclRefExpr *Decl); PVConstraint *getRewritablePVConstraint(Expr *E); + + + bool isNonPtrType(QualType &TE); + }; #endif // LLVM_CLANG_3C_CONSTRAINTRESOLVER_H diff --git a/clang/include/clang/3C/ConstraintVariables.h b/clang/include/clang/3C/ConstraintVariables.h index 3a29abd0e7c3..fa1d9f14ef87 100644 --- a/clang/include/clang/3C/ConstraintVariables.h +++ b/clang/include/clang/3C/ConstraintVariables.h @@ -26,6 +26,8 @@ #define LLVM_CLANG_3C_CONSTRAINTVARIABLES_H #include "clang/3C/Constraints.h" +#include "clang/3C/MultiDecls.h" +#include "clang/3C/OptionalParams.h" #include "clang/3C/ProgramVar.h" #include "clang/AST/ASTContext.h" #include "clang/Lex/Lexer.h" @@ -41,6 +43,26 @@ typedef std::set CVars; // Holds Atoms, one for each of the pointer (*) declared in the program. typedef std::vector CAtoms; +// Options for ConstraintVariable::mkString, using the code pattern described in +// clang/include/clang/3C/OptionalParams.h. Use the MKSTRING_OPTS macro to +// generate a MkStringOpts instance at a call site. +struct MkStringOpts { + // True when the generated string should include + // the name of the variable, false for just the type. + bool EmitName = true; + // True when the generated string is expected + // to be used inside an itype. + bool ForItype = false; + bool EmitPointee = false; + bool UnmaskTypedef = false; + std::string UseName = ""; + bool ForItypeBase = false; +}; +#define MKSTRING_OPTS(...) PACK_OPTS(MkStringOpts, __VA_ARGS__) + +// Name for function return, for debugging +#define RETVAR "$ret" + // Base class for ConstraintVariables. A ConstraintVariable can either be a // PointerVariableConstraint or a FunctionVariableConstraint. The difference // is that FunctionVariableConstraints have constraints on the return value @@ -55,9 +77,23 @@ class ConstraintVariable { ConstraintVariableKind Kind; protected: + // A string representation for the type of this variable. Note that for + // complex types (e.g., function pointer, constant sized arrays), you cannot + // concatenate the type string with an identifier and expect to obtain a valid + // variable declaration. std::string OriginalType; - // Underlying name of the C variable this ConstraintVariable represents. + // Underlying name of the C variable this ConstraintVariable represents. This + // is not always a valid C identifier. It will be empty if no name was given + // (e.g., some parameter declarations). It will be the predefined string + // "$ret" when the ConstraintVariable represents a function return. It may + // take other values if the ConstraintVariable does not represent a C + // variable (e.g., explict casts and compound literals) . std::string Name; + // The combination of the type and name of the represented C variable. The + // combination is handled by clang library routines, so complex types + // like function pointers and constant size are handled correctly. See + // comments on Name for when name should be a valid identifier. + std::string OriginalTypeWithName; // Set of constraint variables that have been constrained due to a // bounds-safe interface (itype). They are remembered as being constrained // so that later on we do not introduce a spurious constraint @@ -74,20 +110,25 @@ class ConstraintVariable { bool IsForDecl; // Only subclasses should call this - ConstraintVariable(ConstraintVariableKind K, std::string T, std::string N) - : Kind(K), OriginalType(T), Name(N), HasEqArgumentConstraints(false), - ValidBoundsKey(false), IsForDecl(false) {} + ConstraintVariable(ConstraintVariableKind K, std::string T, std::string N, + std::string TN) + : Kind(K), OriginalType(T), Name(N), OriginalTypeWithName(TN), + HasEqArgumentConstraints(false), ValidBoundsKey(false), + IsForDecl(false) {} + + ConstraintVariable(ConstraintVariableKind K, QualType QT, std::string N) + : ConstraintVariable(K, qtyToStr(QT), N, + qtyToStr(QT, N == RETVAR ? "" : N)) {} public: - // Create a "for-rewriting" representation of this ConstraintVariable. - // The 'emitName' parameter is true when the generated string should include - // the name of the variable, false for just the type. - // The 'forIType' parameter is true when the generated string is expected - // to be used inside an itype. - virtual std::string mkString(Constraints &CS, bool EmitName = true, - bool ForItype = false, bool EmitPointee = false, - bool UnmaskTypedef = false, - std::string UseName = "") const = 0; + // Generate source code for the type and (in certain cases) the name of the + // variable or function represented by this ConstraintVariable that reflects + // any changes made by 3C and is suitable to insert during rewriting. + // + // This method is used in several contexts with special requirements, which + // are addressed by the options in MkStringOpts; see the comments there. + virtual std::string mkString(Constraints &CS, + const MkStringOpts &Opts = {}) const = 0; // Debug printing of the constraint variable. virtual void print(llvm::raw_ostream &O) const = 0; @@ -110,10 +151,7 @@ class ConstraintVariable { virtual bool solutionEqualTo(Constraints &, const ConstraintVariable *, bool ComparePtyp = true) const = 0; - virtual void constrainToWild(Constraints &CS, - const std::string &Rsn) const = 0; - virtual void constrainToWild(Constraints &CS, const std::string &Rsn, - PersistentSourceLoc *PL) const = 0; + virtual void constrainToWild(Constraints &CS, const ReasonLoc &Rsn) const = 0; // Return true if this variable was checked in the input. Checked variables // might solve to WILD, and unchecked variables might solve to checked. Use @@ -145,7 +183,7 @@ class ConstraintVariable { virtual bool hasNtArr(const EnvironmentMap &E, int AIdx = -1) const = 0; // Force use of equality constraints in function calls for this CV - virtual void equateArgumentConstraints(ProgramInfo &I) = 0; + virtual void equateArgumentConstraints(ProgramInfo &I, ReasonLoc &Rsn) = 0; // Internally combine the constraints and other data from the first parameter // with this constraint variable. Used with redeclarations, especially of @@ -157,6 +195,7 @@ class ConstraintVariable { // Get the original type string that can be directly // used for rewriting. std::string getRewritableOriginalTy() const; + std::string getOriginalTypeWithName() const; std::string getName() const { return Name; } void setValidDecl() { IsForDecl = true; } @@ -175,14 +214,25 @@ class ConstraintVariable { // achieve that, we additionally constrain the internal variables to not // change. // - // Some cases in which the itype must not change at all are indicated by - // passing a reason for the "root cause of wildness" as ReasonUnchangeable. - // Otherwise ReasonUnchangeable should be set to the empty string. + // Some cases in which the itype needs to be constrained not change at all are + // indicated by passing a non-default reason for the "root cause of wildness" + // in ReasonUnchangeable. If the reason is DEFAULT_REASON, this is a sentinel + // meaning that the caller is not requesting such a constraint. Other cases + // that need the constraint are detected within equateWithItype itself, and + // the appropriate reason is attached there. + // + // TODO: It looks like there may be some unusual cases in which + // equateWithType generates constraints using the reason from + // ReasonUnchangeable even if it is the DEFAULT_REASON sentinel. Rethink the + // equateWithItype design to figure out what reason should actually be used or + // if those constraints should be generated at all. virtual void equateWithItype(ProgramInfo &CS, - const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) = 0; + const ReasonLoc &ReasonUnchangeable) = 0; - virtual ConstraintVariable *getCopy(Constraints &CS) = 0; + // Copy this variable and replace all VarAtoms with fresh VarAtoms. Using + // fresh atoms allows the new variable to solve to different types than the + // original. + virtual ConstraintVariable *getCopy(ReasonLoc &Rsn, Constraints &CS) = 0; virtual ~ConstraintVariable(){}; }; @@ -194,15 +244,15 @@ enum ConsAction { Safe_to_Wild, Wild_to_Safe, Same_to_Same }; void constrainConsVarGeq(const std::set &LHS, const std::set &RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey = true); void constrainConsVarGeq(ConstraintVariable *LHS, const CVarSet &RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey = true); void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey = true); @@ -234,18 +284,34 @@ class PointerVariableConstraint : public ConstraintVariable { RestrictQualification }; + // Get a constraint variable representing a pointer which has been constrained + // to WILD for the specified reason. + // TODO: This method always returns a constraint variable containing one atom. + // This causes problems if the variable is later used as a deeper + // pointer type. See correctcomputation/checkedc-clang#673. static PointerVariableConstraint * - getWildPVConstraint(Constraints &CS, const std::string &Rsn, - PersistentSourceLoc *PSL = nullptr); - static PointerVariableConstraint *getPtrPVConstraint(Constraints &CS); + getWildPVConstraint(Constraints &CS, const ReasonLoc &Rsn); + + // Get constraint variables representing values that are not pointers. If a + // meaningful name can be assigned to the value, the second method should be + // used to get higher quality root-cause and debugging output. static PointerVariableConstraint *getNonPtrPVConstraint(Constraints &CS); static PointerVariableConstraint *getNamedNonPtrPVConstraint(StringRef Name, Constraints &CS); + // Given a constraint variable, return a new constraint variable with the same + // atoms as the original, but with one fresh atoms added to the front of the + // Vars vector. This effectively takes the address of the pointer represented + // by the original constraint variable. static PointerVariableConstraint * addAtomPVConstraint(PointerVariableConstraint *PVC, ConstAtom *PtrTyp, - Constraints &CS); + ReasonLoc &Rsn, Constraints &CS); + // Return a new constraint variable representing the result of dereferencing + // the input constraint variable. This is accomplished by first copying the + // parameter, and then removing the first element of the Vars vector in the + // copy. The remaining VarAtoms in the copy are the same as those in the + // original. static PointerVariableConstraint * derefPVConstraint(PointerVariableConstraint *PVC); @@ -290,16 +356,17 @@ class PointerVariableConstraint : public ConstraintVariable { // declaration's base type. To preserve macros, this we first try to take // the type directly from source code. Where that is not possible, the type // is regenerated from the type in the clang AST. - static std::string extractBaseType(DeclaratorDecl *D, TypeSourceInfo *TSI, - QualType QT, const Type *Ty, - const ASTContext &C); + static std::string extractBaseType(MultiDeclMemberDecl *MMD, + TypeSourceInfo *TSI, QualType QT, + const Type *Ty, const ASTContext &C, + ProgramInfo &Info); // Try to extract string representation of the base type for a declaration // from the source code. If the base type cannot be extracted from source, an // empty string is returned instead. - static std::string tryExtractBaseType(DeclaratorDecl *D, TypeSourceInfo *TSI, - QualType QT, const Type *Ty, - const ASTContext &C); + static std::string tryExtractBaseType(MultiDeclMemberDecl *MMD, + TypeSourceInfo *TSI, QualType QT, + const Type *Ty, const ASTContext &C); // Flag to indicate that this constraint is a part of function prototype // e.g., Parameters or Return. @@ -311,16 +378,23 @@ class PointerVariableConstraint : public ConstraintVariable { // Get solution for the atom of a pointer. const ConstAtom *getSolution(const Atom *A, const EnvironmentMap &E) const; - PointerVariableConstraint(PointerVariableConstraint *Ot, Constraints &CS); + // Construct a copy of this variable, reusing all VarAtoms. To instead obtains + // a copy of the constraint variable which contains fresh VarAtoms (allowing + // the new variable to solve to different type than the original), instead use + // the method getCopy. + PointerVariableConstraint(PointerVariableConstraint *Ot); PointerVariableConstraint *Parent; // String representing declared bounds expression. std::string BoundsAnnotationStr; + // TODO can we move this to an optional instead of the -1? // Does this variable represent a generic type? Which one (or -1 for none)? // Generic types can be used with fewer restrictions, so this field is used // stop assignments with generic variables from forcing constraint variables // to be wild. - int GenericIndex; + // Source is generated from the source code, Inferred is set internally + int SourceGenericIndex; + int InferredGenericIndex; // Empty array pointers are represented the same as standard pointers. This // lets pointers be passed to functions expecting a zero width array. This @@ -329,7 +403,7 @@ class PointerVariableConstraint : public ConstraintVariable { bool IsZeroWidthArray; bool IsTypedef = false; - TypedefNameDecl *TDT; + ConstraintVariable *TypedefVar; std::string TypedefString; // Does the type internally contain a typedef, and if so: at what level and // what is it's name? @@ -338,26 +412,26 @@ class PointerVariableConstraint : public ConstraintVariable { // Is this a pointer to void? Possibly with multiple levels of indirection. bool IsVoidPtr; - // Constructor for when we know a CVars and a type string. - PointerVariableConstraint(CAtoms V, std::vector SV, - std::string T, std::string Name, - FunctionVariableConstraint *F, std::string Is, - int Generic = -1) - : ConstraintVariable(PointerVariable, "" /*not used*/, Name), BaseType(T), - Vars(V), SrcVars(SV), FV(F), SrcHasItype(!Is.empty()), ItypeStr(Is), - PartOfFuncPrototype(false), Parent(nullptr), BoundsAnnotationStr(""), - GenericIndex(Generic), IsZeroWidthArray(false), IsVoidPtr(false) {} + // Construct and empty PointerVariableConstraint with only the name set. All + // other fields are initialized to default values. This is used to construct + // variables for non-pointer expressions. + PointerVariableConstraint(std::string Name) : + ConstraintVariable(PointerVariable, "", Name, ""), FV(nullptr), + SrcHasItype(false), PartOfFuncPrototype(false), Parent(nullptr), + SourceGenericIndex(-1), InferredGenericIndex(-1), + IsZeroWidthArray(false), IsTypedef(false), + TypedefLevelInfo({}), IsVoidPtr(false) {} public: std::string getTy() const { return BaseType; } - bool getArrPresent() const; // Check if the outermost pointer is an unsized array. - bool isTopCvarUnsizedArr() const; + bool isTopAtomUnsizedArr() const; // Check if any of the pointers is either a sized or unsized arr. bool hasSomeSizedArr() const; - bool isTypedef(void); - void setTypedef(TypedefNameDecl *T, std::string S); + bool isTypedef(void) const; + const ConstraintVariable *getTypedefVar() const; + void setTypedef(ConstraintVariable *TDVar, std::string S); // Return true if this constraint had an itype in the original source code. bool srcHasItype() const override { return SrcHasItype; } @@ -371,9 +445,12 @@ class PointerVariableConstraint : public ConstraintVariable { // Get bounds annotation. std::string getBoundsStr() const { return BoundsAnnotationStr; } - bool getIsGeneric() const { return GenericIndex >= 0; } - int getGenericIndex() const { return GenericIndex; } - + bool isGeneric() const { return InferredGenericIndex >= 0; } + int getGenericIndex() const { return InferredGenericIndex; } + void setGenericIndex(int idx) { InferredGenericIndex = idx; } + bool isGenericChanged() const { + return SourceGenericIndex != InferredGenericIndex; + } // Was this variable a checked pointer in the input program? // This is important for two reasons: (1) externs that are checked should be // kept that way during solving, (2) nothing that was originally checked @@ -416,6 +493,10 @@ class PointerVariableConstraint : public ConstraintVariable { // ForceGenericIndex: CheckedC supports generic types (_Itype_for_any) which // need less restrictive constraints. Set >= 0 to indicate // that this variable should be considered generic. + // PotentialGeneric: Whether this may become generic after analysis. Disables + // constraint to wild for non-generics. If you use this + // you'll have to add that constraint later if it is + // not generic. // TSI: TypeSourceInfo object gives access to information about the source // code representation of the type. Allows for more precise rewriting by // preserving the exact syntax used to write types that aren't rewritten @@ -425,6 +506,7 @@ class PointerVariableConstraint : public ConstraintVariable { const clang::ASTContext &C, std::string *InFunc = nullptr, int ForceGenericIndex = -1, + bool PotentialGeneric = false, bool VarAtomForChecked = false, TypeSourceInfo *TSI = nullptr, const clang::QualType &ItypeT = QualType()); @@ -441,10 +523,8 @@ class PointerVariableConstraint : public ConstraintVariable { std::string gatherQualStrings(void) const; - std::string mkString(Constraints &CS, bool EmitName = true, - bool ForItype = false, bool EmitPointee = false, - bool UnmaskTypedef = false, - std::string UseName = "") const override; + std::string mkString(Constraints &CS, + const MkStringOpts &Opts = {}) const override; FunctionVariableConstraint *getFV() const { return FV; } @@ -452,12 +532,11 @@ class PointerVariableConstraint : public ConstraintVariable { void dump() const override { print(llvm::errs()); } void dumpJson(llvm::raw_ostream &O) const override; - void constrainToWild(Constraints &CS, const std::string &Rsn) const override; - void constrainToWild(Constraints &CS, const std::string &Rsn, - PersistentSourceLoc *PL) const override; - void constrainOuterTo(Constraints &CS, ConstAtom *C, bool DoLB = false, - bool Soft = false); + void constrainToWild(Constraints &CS, const ReasonLoc &Rsn) const override; + void constrainOuterTo(Constraints &CS, ConstAtom *C, const ReasonLoc &Rsn, + bool DoLB = false, bool Soft = false); void constrainIdxTo(Constraints &CS, ConstAtom *C, unsigned int Idx, + const ReasonLoc &Rsn, bool DoLB = false, bool Soft = false); bool anyChanges(const EnvironmentMap &E) const override; bool anyArgumentIsWild(const EnvironmentMap &E); @@ -465,15 +544,20 @@ class PointerVariableConstraint : public ConstraintVariable { bool hasArr(const EnvironmentMap &E, int AIdx = -1) const override; bool hasNtArr(const EnvironmentMap &E, int AIdx = -1) const override; - void equateArgumentConstraints(ProgramInfo &I) override; + bool isNtConstantArr(const EnvironmentMap &E) const; + bool isConstantArr() const; + unsigned long getConstantArrSize() const; + + void equateArgumentConstraints(ProgramInfo &I, ReasonLoc &Rsn) override; bool isPartOfFunctionPrototype() const { return PartOfFuncPrototype; } // Add the provided constraint variable as an argument constraint. - bool addArgumentConstraint(ConstraintVariable *DstCons, ProgramInfo &Info); + bool addArgumentConstraint(ConstraintVariable *DstCons, + ReasonLoc &Rsn, ProgramInfo &Info); // Get the set of constraint variables corresponding to the arguments. const std::set &getArgumentConstraints() const; - PointerVariableConstraint *getCopy(Constraints &CS) override; + PointerVariableConstraint *getCopy(ReasonLoc &Rsn, Constraints &CS) override; // Retrieve the atom at the specified index. This function includes special // handling for generic constraint variables to create deeper pointers as @@ -482,13 +566,11 @@ class PointerVariableConstraint : public ConstraintVariable { ~PointerVariableConstraint() override{}; - void equateWithItype(ProgramInfo &CS, const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) override; + void equateWithItype(ProgramInfo &CS, + const ReasonLoc &ReasonUnchangeable) override; }; typedef PointerVariableConstraint PVConstraint; -// Name for function return, for debugging -#define RETVAR "$ret" // This class contains a pair of PVConstraints that represent an internal and // external view of a variable for use as the parameter and return constraints @@ -514,19 +596,21 @@ class FVComponentVariable { : InternalConstraint(nullptr), ExternalConstraint(nullptr), SourceDeclaration("") {} - FVComponentVariable(FVComponentVariable *Ot, Constraints &CS); + FVComponentVariable(FVComponentVariable *Ot, ReasonLoc &Rsn, Constraints &CS); FVComponentVariable(const clang::QualType &QT, const clang::QualType &ITypeT, clang::DeclaratorDecl *D, std::string N, ProgramInfo &I, const clang::ASTContext &C, std::string *InFunc, - bool HasItype); + bool PotentialGeneric, bool HasItype); void mergeDeclaration(FVComponentVariable *From, ProgramInfo &I, std::string &ReasonFailed); - std::string mkItypeStr(Constraints &CS) const; + std::string mkItypeStr(Constraints &CS, bool ForItypeBase = false) const; std::string mkTypeStr(Constraints &CS, bool EmitName, - std::string UseName = "") const; - std::string mkString(Constraints &CS, bool EmitName = true) const; + std::string UseName = "", + bool ForItypeBase = false) const; + std::string mkString(Constraints &CS, bool EmitName = true, + bool ForItypeBase = false) const; bool hasItypeSolution(Constraints &CS) const; bool hasCheckedSolution(Constraints &CS) const; @@ -534,8 +618,13 @@ class FVComponentVariable { PVConstraint *getInternal() const { return InternalConstraint; } PVConstraint *getExternal() const { return ExternalConstraint; } - void equateWithItype(ProgramInfo &CS, const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) const; + void setGenericIndex(int idx) { + ExternalConstraint->setGenericIndex(idx); + InternalConstraint->setGenericIndex(idx); + } + + void equateWithItype(ProgramInfo &CS, + const ReasonLoc &ReasonUnchangeable) const; bool solutionEqualTo(Constraints &CS, const FVComponentVariable *CV, bool ComparePtyp) const; @@ -545,7 +634,7 @@ class FVComponentVariable { // when a re-write of a function pointer is needed. class FunctionVariableConstraint : public ConstraintVariable { private: - FunctionVariableConstraint(FunctionVariableConstraint *Ot, Constraints &CS); + FunctionVariableConstraint(FunctionVariableConstraint *Ot); // N constraints on the return value of the function. FVComponentVariable ReturnVar; @@ -562,24 +651,21 @@ class FunctionVariableConstraint : public ConstraintVariable { // Flag to indicate whether this is a function pointer or not. bool IsFunctionPtr; - // Count of type parameters from `_Itype_for_any(...)`. + // Count of type parameters (originally from `_Itype_for_any(...)`). int TypeParams; - void equateFVConstraintVars(ConstraintVariable *CV, ProgramInfo &Info) const; + void equateFVConstraintVars(ConstraintVariable *CV, ProgramInfo &Info, + ReasonLoc &Rsn) const; public: - FunctionVariableConstraint() - : ConstraintVariable(FunctionVariable, "", ""), FileName(""), - Hasproto(false), Hasbody(false), IsStatic(false), Parent(nullptr), - IsFunctionPtr(false) {} - FunctionVariableConstraint(clang::DeclaratorDecl *D, ProgramInfo &I, const clang::ASTContext &C); FunctionVariableConstraint(clang::TypedefDecl *D, ProgramInfo &I, const clang::ASTContext &C); - FunctionVariableConstraint(const clang::Type *Ty, clang::DeclaratorDecl *D, - std::string N, ProgramInfo &I, - const clang::ASTContext &C, + + FunctionVariableConstraint(const clang::QualType Ty, + clang::DeclaratorDecl *D, std::string N, + ProgramInfo &I, const clang::ASTContext &C, TypeSourceInfo *TSI = nullptr); PVConstraint *getExternalReturn() const { @@ -624,39 +710,50 @@ class FunctionVariableConstraint : public ConstraintVariable { bool srcHasItype() const override; bool srcHasBounds() const override; + // The number of type variables + int getGenericParams() const { + return TypeParams; + } + // remove added generics + // use when we constrain a potential generic param to wild + void resetGenericParams() { + TypeParams = 0; + } + + // The type parameter index of the return int getGenericIndex() const { return ReturnVar.ExternalConstraint->getGenericIndex(); } + // Change the type parameter index of the return + void setGenericIndex(int idx) { + ReturnVar.ExternalConstraint->setGenericIndex(idx); + } bool solutionEqualTo(Constraints &CS, const ConstraintVariable *CV, bool ComparePtyp = true) const override; - std::string mkString(Constraints &CS, bool EmitName = true, - bool ForItype = false, bool EmitPointee = false, - bool UnmaskTypedef = false, - std::string UseName = "") const override; + std::string mkString(Constraints &CS, + const MkStringOpts &Opts = {}) const override; void print(llvm::raw_ostream &O) const override; void dump() const override { print(llvm::errs()); } void dumpJson(llvm::raw_ostream &O) const override; - void constrainToWild(Constraints &CS, const std::string &Rsn) const override; - void constrainToWild(Constraints &CS, const std::string &Rsn, - PersistentSourceLoc *PL) const override; + void constrainToWild(Constraints &CS, const ReasonLoc &Rsn) const override; bool anyChanges(const EnvironmentMap &E) const override; bool hasWild(const EnvironmentMap &E, int AIdx = -1) const override; bool hasArr(const EnvironmentMap &E, int AIdx = -1) const override; bool hasNtArr(const EnvironmentMap &E, int AIdx = -1) const override; - void equateArgumentConstraints(ProgramInfo &P) override; + void equateArgumentConstraints(ProgramInfo &P, ReasonLoc &Rsn) override; - FunctionVariableConstraint *getCopy(Constraints &CS) override; + FunctionVariableConstraint *getCopy(ReasonLoc &Rsn, Constraints &CS) override; bool isOriginallyChecked() const override; bool isSolutionChecked(const EnvironmentMap &E) const override; bool isSolutionFullyChecked(const EnvironmentMap &E) const override; - void equateWithItype(ProgramInfo &CS, const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) override; + void equateWithItype(ProgramInfo &CS, + const ReasonLoc &ReasonUnchangeable) override; ~FunctionVariableConstraint() override {} }; diff --git a/clang/include/clang/3C/Constraints.h b/clang/include/clang/3C/Constraints.h index 18a5b1025701..be46f093284a 100644 --- a/clang/include/clang/3C/Constraints.h +++ b/clang/include/clang/3C/Constraints.h @@ -8,7 +8,6 @@ // // This implements a simple constraint solver for expressions of the form: // a >= b -// a implies b // // The 3C tool performs type inference to identify locations // where a C type might be replaced with a Checked C type. This interface @@ -31,8 +30,20 @@ class Constraints; class PersistentSourceLoc; class ConstraintsGraph; +#define INTERNAL_USE_REASON "This reason should never be displayed" #define DEFAULT_REASON "UNKNOWN_REASON" #define POINTER_IS_ARRAY_REASON "Pointer is array but alltypes is disabled." +#define VOID_TYPE_REASON "Default void* type" +#define UNWRITABLE_REASON "Source code in non-writable file." +#define INNER_POINTER_REASON "Pointer is within an outer pointer" +#define ALLOCATOR_REASON "Return type from an allocator" +#define ARRAY_REASON "Lowerbounded to an array type" +#define NT_ARRAY_REASON "Lowerbounded to an nt_array type" +// SPECIAL_REASON("name_of_func") +#define SPECIAL_REASON(spec) (std::string("Special case for ") + (spec)) +#define STRING_LITERAL_REASON "The type of a string literal" +#define MACRO_REASON "Pointer in Macro declaration." +#define UNION_FIELD_REASON "Union field encountered" template struct PComp { bool operator()(const T Lhs, const T Rhs) const { return *Lhs < *Rhs; } @@ -106,25 +117,27 @@ class VarAtom : public Atom { return ""; } - void print(llvm::raw_ostream &O) const { + void print(llvm::raw_ostream &O) const override { O << varKindToStr(KindV) << Name << "_" << Loc; } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { + void dumpJson(llvm::raw_ostream &O) const override { O << "\"" << varKindToStr(KindV) << Name << "_" << Loc << "\""; } - bool operator==(const Atom &Other) const { + bool operator==(const Atom &Other) const override { if (const VarAtom *V = llvm::dyn_cast(&Other)) return V->Loc == Loc; return false; } - bool operator!=(const Atom &Other) const { return !(*this == Other); } + bool operator!=(const Atom &Other) const override { + return !(*this == Other); + } - bool operator<(const Atom &Other) const { + bool operator<(const Atom &Other) const override { if (const VarAtom *V = llvm::dyn_cast(&Other)) return Loc < V->Loc; return false; @@ -160,19 +173,21 @@ class NTArrAtom : public ConstAtom { static bool classof(const Atom *S) { return S->getKind() == A_NTArr; } - void print(llvm::raw_ostream &O) const { O << "NTARR"; } + void print(llvm::raw_ostream &O) const override { O << "NTARR"; } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { O << "\"NTARR\""; } + void dumpJson(llvm::raw_ostream &O) const override { O << "\"NTARR\""; } - bool operator==(const Atom &Other) const { + bool operator==(const Atom &Other) const override { return llvm::isa(&Other); } - bool operator!=(const Atom &Other) const { return !(*this == Other); } + bool operator!=(const Atom &Other) const override { + return !(*this == Other); + } - bool operator<(const Atom &Other) const { return !(*this == Other); } + bool operator<(const Atom &Other) const override { return !(*this == Other); } }; // This refers to the constant ARR. @@ -182,19 +197,21 @@ class ArrAtom : public ConstAtom { static bool classof(const Atom *S) { return S->getKind() == A_Arr; } - void print(llvm::raw_ostream &O) const { O << "ARR"; } + void print(llvm::raw_ostream &O) const override { O << "ARR"; } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { O << "\"ARR\""; } + void dumpJson(llvm::raw_ostream &O) const override { O << "\"ARR\""; } - bool operator==(const Atom &Other) const { + bool operator==(const Atom &Other) const override { return llvm::isa(&Other); } - bool operator!=(const Atom &Other) const { return !(*this == Other); } + bool operator!=(const Atom &Other) const override { + return !(*this == Other); + } - bool operator<(const Atom &Other) const { + bool operator<(const Atom &Other) const override { return !(llvm::isa(&Other) || *this == Other); } }; @@ -206,19 +223,21 @@ class PtrAtom : public ConstAtom { static bool classof(const Atom *S) { return S->getKind() == A_Ptr; } - void print(llvm::raw_ostream &O) const { O << "PTR"; } + void print(llvm::raw_ostream &O) const override { O << "PTR"; } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { O << "\"PTR\""; } + void dumpJson(llvm::raw_ostream &O) const override { O << "\"PTR\""; } - bool operator==(const Atom &Other) const { + bool operator==(const Atom &Other) const override { return llvm::isa(&Other); } - bool operator!=(const Atom &Other) const { return !(*this == Other); } + bool operator!=(const Atom &Other) const override { + return !(*this == Other); + } - bool operator<(const Atom &Other) const { + bool operator<(const Atom &Other) const override { return !(llvm::isa(&Other) || llvm::isa(&Other) || *this == Other); } @@ -231,43 +250,51 @@ class WildAtom : public ConstAtom { static bool classof(const Atom *S) { return S->getKind() == A_Wild; } - void print(llvm::raw_ostream &O) const { O << "WILD"; } + void print(llvm::raw_ostream &O) const override { O << "WILD"; } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { O << "\"WILD\""; } + void dumpJson(llvm::raw_ostream &O) const override { O << "\"WILD\""; } - bool operator==(const Atom &Other) const { + bool operator==(const Atom &Other) const override { return llvm::isa(&Other); } - bool operator!=(const Atom &Other) const { return !(*this == Other); } + bool operator!=(const Atom &Other) const override { + return !(*this == Other); + } - bool operator<(const Atom &Other) const { + bool operator<(const Atom &Other) const override { return !(llvm::isa(&Other) || llvm::isa(&Other) || llvm::isa(&Other) || *this == Other); } }; +// Helper struct requiring locations for every reason, that is, +// Constraints need reasons and locations to provide good user feedback +struct ReasonLoc { + ReasonLoc() : Reason(DEFAULT_REASON), Location(PersistentSourceLoc()) {} + ReasonLoc(std::string Reason, PersistentSourceLoc Loc) + : Reason(Reason), Location(Loc) {} + bool isDefault() const { return Reason == DEFAULT_REASON; } + std::string Reason; + PersistentSourceLoc Location; +}; + // Represents constraints of the form: // - a >= b -// - a ==> b class Constraint { public: - enum ConstraintKind { C_Geq, C_Imp }; + enum ConstraintKind { C_Geq }; private: const ConstraintKind Kind; - PersistentSourceLoc PL; + ReasonLoc Reason; + std::vector ExtraReasons; public: - std::string REASON = DEFAULT_REASON; - Constraint(ConstraintKind K) : Kind(K) {} - Constraint(ConstraintKind K, const std::string &Rsn) : Kind(K) { - REASON = Rsn; - } - Constraint(ConstraintKind K, const std::string &Rsn, PersistentSourceLoc *PL); + Constraint(ConstraintKind K, const ReasonLoc &Rsn) : Kind(K), Reason(Rsn) {} virtual ~Constraint() {} @@ -279,10 +306,24 @@ class Constraint { virtual bool operator==(const Constraint &Other) const = 0; virtual bool operator!=(const Constraint &Other) const = 0; virtual bool operator<(const Constraint &Other) const = 0; - virtual std::string getReason() { return REASON; } - virtual void setReason(const std::string &Rsn) { REASON = Rsn; } + virtual std::string getReasonText() const { return Reason.Reason; } + virtual const ReasonLoc &getReason() const { return Reason; } + // Alter the internal reason and remove any additional reasons + virtual void setReason(const ReasonLoc &Rsn) { + Reason = Rsn; + ExtraReasons.clear(); + } + virtual std::vector &additionalReasons() { return ExtraReasons; } + // include additional reasons that will appear in output as notes + virtual void addReason(const ReasonLoc &Rsn) { + ExtraReasons.push_back(Rsn); + } + + bool isUnwritable(void) const { + return getReasonText() == UNWRITABLE_REASON; + } - const PersistentSourceLoc &getLocation() const { return PL; } + const PersistentSourceLoc &getLocation() const { return Reason.Location; } }; // a >= b @@ -290,33 +331,24 @@ class Geq : public Constraint { friend class VarAtom; public: - Geq(Atom *Lhs, Atom *Rhs, bool IsCC = true, bool Soft = false) - : Constraint(C_Geq), Lhs(Lhs), Rhs(Rhs), IsCheckedConstraint(IsCC), - IsSoft(Soft) {} - - Geq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, bool IsCC = true, + Geq(Atom *Lhs, Atom *Rhs, const ReasonLoc &Rsn, bool IsCC = true, bool Soft = false) : Constraint(C_Geq, Rsn), Lhs(Lhs), Rhs(Rhs), IsCheckedConstraint(IsCC), IsSoft(Soft) {} - Geq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, PersistentSourceLoc *PL, - bool IsCC = true, bool Soft = false) - : Constraint(C_Geq, Rsn, PL), Lhs(Lhs), Rhs(Rhs), - IsCheckedConstraint(IsCC), IsSoft(Soft) {} - static bool classof(const Constraint *C) { return C->getKind() == C_Geq; } - void print(llvm::raw_ostream &O) const { + void print(llvm::raw_ostream &O) const override { Lhs->print(O); std::string Kind = IsCheckedConstraint ? " (C)>= " : " (P)>= "; O << Kind; Rhs->print(O); - O << ", Reason:" << REASON; + O << ", Reason: " << getReasonText(); } - void dump(void) const { print(llvm::errs()); } + void dump(void) const override { print(llvm::errs()); } - void dumpJson(llvm::raw_ostream &O) const { + void dumpJson(llvm::raw_ostream &O) const override { O << "{\"Geq\":{\"Atom1\":"; Lhs->dumpJson(O); O << ", \"Atom2\":"; @@ -324,7 +356,7 @@ class Geq : public Constraint { O << ", \"isChecked\":"; O << (IsCheckedConstraint ? "true" : "false"); O << ", \"Reason\":"; - llvm::json::Value ReasonVal(REASON); + llvm::json::Value ReasonVal(getReasonText()); O << ReasonVal; O << "}}"; } @@ -346,16 +378,18 @@ class Geq : public Constraint { bool constraintIsChecked(void) const { return IsCheckedConstraint; } - bool operator==(const Constraint &Other) const { + bool operator==(const Constraint &Other) const override { if (const Geq *E = llvm::dyn_cast(&Other)) return *Lhs == *E->Lhs && *Rhs == *E->Rhs && IsCheckedConstraint == E->IsCheckedConstraint; return false; } - bool operator!=(const Constraint &Other) const { return !(*this == Other); } + bool operator!=(const Constraint &Other) const override { + return !(*this == Other); + } - bool operator<(const Constraint &Other) const { + bool operator<(const Constraint &Other) const override { ConstraintKind K = Other.getKind(); if (K == C_Geq) { const Geq *E = llvm::dyn_cast(&Other); @@ -382,61 +416,6 @@ class Geq : public Constraint { bool IsSoft; }; -// a ==> b -class Implies : public Constraint { -public: - Implies(Geq *Premise, Geq *Conclusion) - : Constraint(C_Imp), Premise(Premise), Conclusion(Conclusion) {} - - static bool classof(const Constraint *C) { return C->getKind() == C_Imp; } - - Geq *getPremise() { return Premise; } - Geq *getConclusion() { return Conclusion; } - - void print(llvm::raw_ostream &O) const { - Premise->print(O); - O << " ==> "; - Conclusion->print(O); - } - - void dump(void) const { print(llvm::errs()); } - - void dumpJson(llvm::raw_ostream &O) const { - O << "{\"Implies\":{\"Premise\":"; - Premise->dumpJson(O); - O << ", \"Conclusion\":"; - Conclusion->dumpJson(O); - O << "}}"; - } - - bool operator==(const Constraint &Other) const { - if (const Implies *I = llvm::dyn_cast(&Other)) - return *Premise == *I->Premise && *Conclusion == *I->Conclusion; - return false; - } - - bool operator!=(const Constraint &Other) const { return !(*this == Other); } - - bool operator<(const Constraint &Other) const { - ConstraintKind K = Other.getKind(); - if (K == C_Imp) { - const Implies *I = llvm::dyn_cast(&Other); - assert(I != nullptr); - - if (*Premise == *I->Premise && *Conclusion == *I->Conclusion) - return false; - if (*Premise == *I->Premise && *Conclusion != *I->Conclusion) - return *Conclusion < *I->Conclusion; - return *Premise < *I->Premise; - } - return C_Imp < K; - } - -private: - Geq *Premise; - Geq *Conclusion; -}; - // This is the solution, the first item is Checked Solution and the second // is Ptr solution. typedef std::pair VarSolTy; @@ -507,17 +486,11 @@ class Constraints { void print(llvm::raw_ostream &) const; void dumpJson(llvm::raw_ostream &) const; - Geq *createGeq(Atom *Lhs, Atom *Rhs, bool IsCheckedConstraint = true, - bool Soft = false); - Geq *createGeq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, - bool IsCheckedConstraint = true); - Geq *createGeq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, - PersistentSourceLoc *PL, bool IsCheckedConstraint = true); - Implies *createImplies(Geq *Premise, Geq *Conclusion); + Geq *createGeq(Atom *Lhs, Atom *Rhs, ReasonLoc Rsn, + bool IsCheckedConstraint = true, bool Soft = false); VarAtom *createFreshGEQ(std::string Name, VarAtom::VarKind VK, ConstAtom *Con, - std::string Rsn = DEFAULT_REASON, - PersistentSourceLoc *PSL = nullptr); + ReasonLoc Rsn = ReasonLoc()); VarAtom *getFreshVar(std::string Name, VarAtom::VarKind VK); VarAtom *getOrCreateVar(ConstraintKey V, std::string Name, @@ -528,8 +501,8 @@ class Constraints { NTArrAtom *getNTArr() const; WildAtom *getWild() const; ConstAtom *getAssignment(Atom *A); - ConstraintsGraph &getChkCG(); - ConstraintsGraph &getPtrTypCG(); + const ConstraintsGraph &getChkCG() const; + const ConstraintsGraph &getPtrTypCG() const; void resetEnvironment(); bool checkInitialEnvSanity(); @@ -547,9 +520,6 @@ class Constraints { std::map ConstraintsByReason; ConstraintsEnv Environment; - // Confirm a constraint is well-formed - bool check(Constraint *C); - // Managing constraints based on the underlying reason. // add constraint to the map. bool addReasonBasedConstraint(Constraint *C); diff --git a/clang/include/clang/3C/ConstraintsGraph.h b/clang/include/clang/3C/ConstraintsGraph.h index 5b376e1b8490..deebf43f40f6 100644 --- a/clang/include/clang/3C/ConstraintsGraph.h +++ b/clang/include/clang/3C/ConstraintsGraph.h @@ -36,11 +36,12 @@ class DataNode : public llvm::DGNode, EdgeType> { DataType getData() const { return Data; } - void connectTo(NodeType &Other, bool SoftEdge = false) { - auto *BLR = new EdgeType(Other); + void connectTo(NodeType &Other, bool SoftEdge = false, + Constraint *C = nullptr) { + auto *BLR = new EdgeType(Other, C); BLR->IsSoft = SoftEdge; this->addEdge(*BLR); - auto *BRL = new EdgeType(*this); + auto *BRL = new EdgeType(*this, C); Other.addPredecessor(*BRL); } @@ -93,9 +94,12 @@ struct GraphTraits *> { template struct DataEdge : public llvm::DGEdge, DataEdge> { typedef llvm::DGEdge, DataEdge> SuperType; - explicit DataEdge(DataNode &Node) : SuperType(Node) {} - DataEdge(const DataEdge &E) : SuperType(E) {} + explicit DataEdge(DataNode &Node, Constraint *C = nullptr) + : SuperType(Node), EdgeConstraint(C) {} + DataEdge(const DataEdge &E, Constraint *C = nullptr) + : SuperType(E), EdgeConstraint(C) {} bool IsSoft = false; + Constraint *EdgeConstraint; }; class GraphVizOutputGraph; @@ -135,10 +139,10 @@ class DataGraph invalidateBFSCache(); } - void addEdge(Data L, Data R, bool SoftEdge = false) { + void addEdge(Data L, Data R, bool SoftEdge = false, Constraint *C = nullptr) { NodeType *BL = this->findOrCreateNode(L); NodeType *BR = this->findOrCreateNode(R); - BL->connectTo(*BR, SoftEdge); + BL->connectTo(*BR, SoftEdge, C); invalidateBFSCache(); } @@ -152,13 +156,15 @@ class DataGraph } } - bool getNeighbors(Data D, std::set &DataSet, bool Succ, - bool Append = false, bool IgnoreSoftEdges = false) { + // This version provides more info by returning graph edges + // rather than data items + bool getIncidentEdges(Data D, std::set &EdgeSet, bool Succ, + bool Append = false, bool IgnoreSoftEdges = false) const { NodeType *N = this->findNode(D); if (N == nullptr) return false; if (!Append) - DataSet.clear(); + EdgeSet.clear(); llvm::SetVector Edges; if (Succ) Edges = N->getEdges(); @@ -166,25 +172,49 @@ class DataGraph Edges = N->getPredecessors(); for (auto *E : Edges) if (!E->IsSoft || !IgnoreSoftEdges) - DataSet.insert(E->getTargetNode().getData()); + EdgeSet.insert(E); + return !EdgeSet.empty(); + } + + bool getNeighbors(Data D, std::set &DataSet, bool Succ, + bool Append = false, bool IgnoreSoftEdges = false) const { + if (!Append) + DataSet.clear(); + + std::set Edges; + getIncidentEdges(D, Edges, Succ, Append, IgnoreSoftEdges); + for (auto *E : Edges) + DataSet.insert(E->getTargetNode().getData()); return !DataSet.empty(); } - bool getSuccessors(Data D, std::set &DataSet, bool Append = false) { + bool getSuccessorsEdges(Atom *A, std::set &EdgeSet, + bool Append = false) { + return getIncidentEdges(A, EdgeSet, true, Append); + } + + bool getPredecessorsEdges(Atom *A, std::set &EdgeSet, + bool Append = false) { + return getIncidentEdges(A, EdgeSet, false, Append); + } + + bool + getSuccessors(Data D, std::set &DataSet, bool Append = false) const { return getNeighbors(D, DataSet, true, Append); } - bool getPredecessors(Data D, std::set &DataSet, bool Append = false) { + bool + getPredecessors(Data D, std::set &DataSet, bool Append = false) const { return getNeighbors(D, DataSet, false, Append); } - NodeType *findNode(Data D) { + NodeType *findNode(Data D) const { if (NodeSet.find(D) != NodeSet.end()) - return NodeSet[D]; + return NodeSet.at(D); return nullptr; } - void visitBreadthFirst(Data Start, llvm::function_ref Fn) { + void visitBreadthFirst(Data Start, llvm::function_ref Fn) const { NodeType *N = this->findNode(Start); if (N == nullptr) return; @@ -217,14 +247,16 @@ class DataGraph private: template friend struct llvm::GraphTraits; friend class GraphVizOutputGraph; - std::map> BFSCache; + mutable std::map> BFSCache; std::map NodeSet; void invalidateBFSCache() { BFSCache.clear(); } }; // Specialize the graph for the checked and pointer type constraint graphs. This -// graphs stores atoms at each node. +// graphs stores atoms at each node, and constraints on each edge. These edges +// are returned by the specialized `getNeighbors` function to provide constraint +// data to clients. class ConstraintsGraph : public DataGraph { public: // Add an edge to the graph according to the Geq constraint. This is an edge @@ -235,6 +267,7 @@ class ConstraintsGraph : public DataGraph { // be able to retrieve them from the graph. std::set &getAllConstAtoms(); + typedef DataEdge EdgeType; protected: // Add vertex is overridden to save const atoms as they are added to the graph // so that getAllConstAtoms can efficiently retrieve them. diff --git a/clang/include/clang/3C/CtxSensAVarBounds.h b/clang/include/clang/3C/CtxSensAVarBounds.h index 11b41a880c0c..f9d4d7705d86 100644 --- a/clang/include/clang/3C/CtxSensAVarBounds.h +++ b/clang/include/clang/3C/CtxSensAVarBounds.h @@ -92,6 +92,9 @@ class CtxSensitiveBoundsKeyHandler { std::map &BKMap, bool IsGlobal); + void contextualizeCVar(RecordDecl *RD, std::string AccessKey, bool IsGlobal, + ASTContext *C, ProgramInfo &I); + // Get string that represents a context sensitive key for the struct // member access ME. std::string getCtxStructKey(MemberExpr *ME, ASTContext *C); diff --git a/clang/include/clang/3C/DeclRewriter.h b/clang/include/clang/3C/DeclRewriter.h index cee885835b24..45f08a4b4310 100644 --- a/clang/include/clang/3C/DeclRewriter.h +++ b/clang/include/clang/3C/DeclRewriter.h @@ -26,54 +26,50 @@ using namespace clang; class DeclRewriter { public: - DeclRewriter(Rewriter &R, ASTContext &A, GlobalVariableGroups &GP) - : R(R), A(A), GP(GP), - VisitedMultiDeclMembers(DComp(A.getSourceManager())) {} + DeclRewriter(Rewriter &R, ProgramInfo &Info, ASTContext &A) + : R(R), Info(Info), A(A) {} // The publicly accessible interface for performing declaration rewriting. // All declarations for variables with checked types in the variable map of // Info parameter are rewritten. static void rewriteDecls(ASTContext &Context, ProgramInfo &Info, Rewriter &R); + static RewrittenDecl buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, ProgramInfo &Info, + ArrayBoundsRewriter &ABR, + bool GenerateSDecls, bool SDeclChecked); + + static RewrittenDecl + buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, ProgramInfo &Info, + ArrayBoundsRewriter &ABR, bool GenerateSDecls); + private: - static RecordDecl *LastRecordDecl; - static std::map VDToRDMap; - static std::set InlineVarDecls; Rewriter &R; + ProgramInfo &Info; ASTContext &A; - GlobalVariableGroups &GP; - // This set contains declarations that have already been rewritten as part of - // a prior declaration that was in the same multi-declaration. It is checked - // before rewriting in order to avoid rewriting a declaration more than once. - // It is not used with individual declarations outside of multi-declarations - // because these declarations are seen exactly once, rather than every time a - // declaration in the containing multi-decl is visited. - RSet VisitedMultiDeclMembers; + // List of TagDecls that were split from multi-decls and should be moved out + // of an enclosing RecordDecl to avoid a compiler warning. Filled during + // multi-decl rewriting and processed by denestTagDecls. + std::vector TagDeclsToDenest; // Visit each Decl in ToRewrite and apply the appropriate pointer type // to that Decl. ToRewrite is the set of all declarations to rewrite. void rewrite(RSet &ToRewrite); - // Rewrite a specific variable declaration using the replacement string in the - // DAndReplace structure. Each of these functions is specialized to handling - // one subclass of declarations. - void rewriteParmVarDecl(ParmVarDeclReplacement *N); - - template - void rewriteFieldOrVarDecl(DRType *N, RSet &ToRewrite); - void rewriteMultiDecl(DeclReplacement *N, RSet &ToRewrite, - std::vector SameLineDecls, - bool ContainsInlineStruct); - void rewriteSingleDecl(DeclReplacement *N, RSet &ToRewrite); + void rewriteMultiDecl(MultiDeclInfo &MDI, RSet &ToRewrite); void doDeclRewrite(SourceRange &SR, DeclReplacement *N); void rewriteFunctionDecl(FunctionDeclReplacement *N); - void rewriteTypedefDecl(TypedefDeclReplacement *TDT, RSet &ToRewrite); - void getDeclsOnSameLine(DeclReplacement *N, std::vector &Decls); - bool isSingleDeclaration(DeclReplacement *N); - bool areDeclarationsOnSameLine(DeclReplacement *N1, DeclReplacement *N2); - SourceRange getNextCommaOrSemicolon(SourceLocation L); - static void detectInlineStruct(Decl *D, SourceManager &SM); + // Emit supplementary declarations _after_ the token that begins at Loc. + // Inserts a newline before the first supplementary declaration but not after + // the last supplementary declaration. This is suitable if Loc is expected to + // be the last token on a line or if rewriteMultiDecl will insert a newline + // after the supplementary declarations later. + void emitSupplementaryDeclarations(const std::vector &SDecls, + SourceLocation Loc); + SourceLocation getNextCommaOrSemicolon(SourceLocation L); + void denestTagDecls(); }; // Visits function declarations and adds entries with their new rewritten @@ -101,32 +97,21 @@ class FunctionDeclBuilder : public RecursiveASTVisitor { // Get existing itype string from constraint variables. std::string getExistingIType(ConstraintVariable *DeclC); - virtual void buildDeclVar(const FVComponentVariable *CV, DeclaratorDecl *Decl, - std::string &Type, std::string &IType, - std::string UseName, bool &RewriteParm, - bool &RewriteRet); - void buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl, - std::string &Type, std::string &IType, - std::string UseName, bool &RewriteParm, - bool &RewriteRet); - void buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl, - std::string &Type, std::string &IType, bool &RewriteParm, - bool &RewriteRet); + virtual RewrittenDecl + buildDeclVar(const FVComponentVariable *CV, DeclaratorDecl *Decl, + std::string UseName, bool &RewriteGen, bool &RewriteParm, + bool &RewriteRet, bool StaticFunc, bool GenerateSDecls); - bool hasDeclWithTypedef(const FunctionDecl *FD); + RewrittenDecl + buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, bool &RewriteParm, bool &RewriteRet, + bool GenerateSDecls); - bool inParamMultiDecl(const ParmVarDecl *PVD); -}; + RewrittenDecl buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, bool &RewriteParm, + bool &RewriteRet, bool GenerateSDecls, + bool SDeclChecked); -class FieldFinder : public RecursiveASTVisitor { -public: - FieldFinder(GlobalVariableGroups &GVG) : GVG(GVG) {} - - bool VisitFieldDecl(FieldDecl *FD); - - static void gatherSameLineFields(GlobalVariableGroups &GVG, Decl *D); - -private: - GlobalVariableGroups &GVG; + bool inParamMultiDecl(const ParmVarDecl *PVD); }; #endif // LLVM_CLANG_3C_DECLREWRITER_H diff --git a/clang/include/clang/3C/IntermediateToolHook.h b/clang/include/clang/3C/IntermediateToolHook.h index d413c0f7f201..44b317694494 100644 --- a/clang/include/clang/3C/IntermediateToolHook.h +++ b/clang/include/clang/3C/IntermediateToolHook.h @@ -25,7 +25,7 @@ class IntermediateToolHook : public ASTConsumer { public: explicit IntermediateToolHook(ProgramInfo &I, clang::ASTContext *C) : Info(I) {} - virtual void HandleTranslationUnit(ASTContext &Context); + void HandleTranslationUnit(ASTContext &Context) override; private: ProgramInfo &Info; diff --git a/clang/include/clang/3C/LowerBoundAssignment.h b/clang/include/clang/3C/LowerBoundAssignment.h new file mode 100644 index 000000000000..23bb54476dd7 --- /dev/null +++ b/clang/include/clang/3C/LowerBoundAssignment.h @@ -0,0 +1,83 @@ +//=--LowerBoundAssignment.h---------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Contains classes for detection and rewriting of assignment expression that +// would invalidate the bounds of pointers rewritten to use range bounds. +// For pointers fattend to use a fresh lower bound +// (`bounds(__3c_lower_bound_p, __3c_lower_bound_p + n)`), an +// assignment `p = q` effectively changes the lower bound of the range +// bounds, so that the new bounds of `p` are `bounds(q, q + n)` +// (assuming `q` has the same size as `p`). For this to not invalidate the +// bound, `__3c_lower_bound_p` must also be updated to be equal to `q`. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_3C_LOWERBOUNDASSIGNMENT_H +#define LLVM_CLANG_3C_LOWERBOUNDASSIGNMENT_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Stmt.h" +#include "clang/3C/ConstraintResolver.h" + +// Return true if an assignment LHS=RHS is the value of RHS is not derived form +// LHS. For example, an assignment `p = q` will return true (we assume `q` +// doesn't alias `p`), while `p = p + 1` will return false. +bool isLowerBoundAssignment(clang::Expr *LHS, clang::Expr *RHS); + +// A class to visit all lower bound assignment expression as detected by +// isLowerBoundAssignment. This class should be extended with +// visitLowerBoundAssignment overridden. +class LowerBoundAssignmentVisitor + : public RecursiveASTVisitor { +public: + explicit LowerBoundAssignmentVisitor() {} + + bool VisitBinaryOperator(BinaryOperator *O); + + // Override this method to define the operation that should be performed on + // each assignment. The LHS and RHS of the assignment expression are passed + // through. + virtual void visitLowerBoundAssignment(Expr *LHS, Expr *RHS) = 0; +}; + +// Visit each lower bound pointer expression and, if the LHS is a pointer +// variable that was rewritten to use range bounds, rewrite the assignment so +// that it doesn't not invalidate the bounds. e.g.: +// q = p; +// becomes +// __3c_lower_bound_q = p, q = __3c_lower_bound_q; +class LowerBoundAssignmentUpdater : public LowerBoundAssignmentVisitor { +public: + explicit LowerBoundAssignmentUpdater(ASTContext *C, ProgramInfo &I, + Rewriter &R) : ABInfo( + I.getABoundsInfo()), CR(I, C), R(R) {} + + void visitLowerBoundAssignment(Expr *LHS, Expr *RHS) override; + +private: + AVarBoundsInfo &ABInfo; + ConstraintResolver CR; + Rewriter &R; +}; + +// Visit each lower bound assignment expression and, if it is inside a macro, +// mark the LHS pointer as ineligible for range bounds. This is required +// because, if the pointer is given range bounds, then the assignment expression +// would need to be rewritten. The expression is a macro, so it cannot be +// rewritten. +class LowerBoundAssignmentFinder : public LowerBoundAssignmentVisitor { +public: + explicit LowerBoundAssignmentFinder(ASTContext *C, ProgramInfo &I) : ABInfo( + I.getABoundsInfo()), CR(I, C), C(C) {} + + void visitLowerBoundAssignment(Expr *LHS, Expr *RHS) override; + +private: + AVarBoundsInfo &ABInfo; + ConstraintResolver CR; + ASTContext *C; +}; +#endif // LLVM_CLANG_3C_LOWERBOUNDASSIGNMENT_H diff --git a/clang/include/clang/3C/MappingVisitor.h b/clang/include/clang/3C/MappingVisitor.h index 71c9e07cf3e9..da6c8dae449f 100644 --- a/clang/include/clang/3C/MappingVisitor.h +++ b/clang/include/clang/3C/MappingVisitor.h @@ -21,37 +21,26 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" -typedef std::tuple StmtDecl; -typedef std::map SourceToDeclMapType; -typedef std::pair - MappingResultsType; +typedef std::map SourceToDeclMapType; class MappingVisitor : public clang::RecursiveASTVisitor { public: MappingVisitor(std::set S, clang::ASTContext &C) : SourceLocs(S), Context(C) {} - bool VisitDeclStmt(clang::DeclStmt *S); - bool VisitDecl(clang::Decl *D); - MappingResultsType getResults() { - return std::pair, - VariableDecltoStmtMap>(PSLtoSDT, DeclToDeclStmt); - } + const SourceToDeclMapType &getResults() { return PSLtoSDT; } private: - // A map from a PersistentSourceLoc to a tuple describing a statement, decl - // or type. + // A map from a PersistentSourceLoc to a Decl at that location. SourceToDeclMapType PSLtoSDT; // The set of PersistentSourceLoc's this instance of MappingVisitor is tasked - // with re-instantiating as either a Stmt, Decl or Type. + // with re-instantiating as a Decl. std::set SourceLocs; // The ASTContext for the particular AST that the MappingVisitor is // traversing. clang::ASTContext &Context; - // A mapping of individual Decls to the DeclStmt that contains them. - VariableDecltoStmtMap DeclToDeclStmt; }; #endif diff --git a/clang/include/clang/3C/MultiDecls.h b/clang/include/clang/3C/MultiDecls.h new file mode 100644 index 000000000000..b6f1e0eca4f3 --- /dev/null +++ b/clang/include/clang/3C/MultiDecls.h @@ -0,0 +1,216 @@ +//=--MultiDecls.h-------------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Code to deal with "multi-decls": constructs in which one or more identifiers +// are declared in a comma-separated list based on a single type "on the left". +// A simple example: +// +// struct my_struct x, *p; +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_3C_MULTIDECLS_H +#define LLVM_CLANG_3C_MULTIDECLS_H + +#include "clang/3C/PersistentSourceLoc.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/Optional.h" + +using namespace clang; + +// Some more information about multi-decls in the context of 3C: +// +// The "members" of a given multi-decl may be ordinary variables (VarDecls), +// struct/union fields (FieldDecls), or typedefs (TypedefDecls), but all members +// of a given multi-decl are of the same kind. +// +// If the "left type" of a multi-decl is a TagDecl, it may have an inline +// definition; if it does, then the TagDecl may be unnamed. Examples: +// +// struct my_struct { int *y; } x, *p; +// struct { int *y; } x, *p; +// +// Multi-decls (especially those with inline TagDecls) have historically been +// tricky for 3C to rewrite. If the type of one member becomes a _Ptr (or +// similar), then the left type of the members is no longer the same, so the +// multi-decl must be broken up, for example: +// +// struct my_struct x; +// _Ptr p; +// +// To keep the logic simpler, if 3C needs to change the type of at least one +// member of a multi-decl, it breaks up all members of the multi-decl into +// separate declarations. To preserve SourceLocations as much as possible and +// avoid interfering with rewrites to any other constructs in the multi-decl +// (e.g., within existing initializer expressions), this breakup is performed by +// replacing the commas with semicolons in place and inserting additional +// occurrences of the left type and any common qualifiers as needed. +// +// If there is an inline TagDecl, it is separated too and moved out of any +// containing RecordDecl to avoid a compiler warning, and if the TagDecl is +// unnamed, it is given an automatically generated name so that it can be +// referenced by the new, separate declarations of the multi-decl members. +// Example: +// +// static struct { int *y; } x, *p: +// +// -> +// +// struct x_struct_1 { _Ptr y; }; +// static struct x_struct_1 x; +// static _Ptr p; +// +// Exception: In a typedef multi-decl, if the _first_ member refers to the +// TagDecl itself (not a pointer to it, etc.), then 3C uses that name for the +// TagDecl rather than generating a new one. This produces nicer output for the +// idiom: +// +// typedef struct { int *y; } FOO, *PFOO; +// +// -> +// +// typedef struct { _Ptr y; } FOO; +// typedef _Ptr PFOO; +// +// The multi-decl code is used even for "multi-decls" of VarDecls, FieldDecls, +// or TypedefDecls that have only a single member to avoid having to maintain a +// separate code path for them. But a multi-decl always has at least one member; +// a pure TagDecl such as `struct my_struct { int *y; };` is _not_ considered a +// multi-decl. ParmVarDecls are handled differently. In fact, ParmVarDecls with +// inline TagDecls are known to be handled poorly, but that's a rare and poor +// practice and it's not easy to handle them better. + +// Currently, we automatically generate a name for every unnamed TagDecl defined +// in a multi-decl and use the name in ConstraintVariables, but we only insert +// the name into the definition if the multi-decl gets rewritten for some other +// reason. This solves the common case of allowing the types of all the +// multi-decl members to refer to the TagDecl, but it doesn't address cases in +// which 3C might need to insert a reference to the unnamed TagDecl elsewhere +// even if the multi-decl isn't being rewritten. In these cases, 3C typically +// uses the generated name even though it is not defined, causing a compile +// error that the user has to correct manually. The problematic cases include: +// +// - Type argument insertion. TypeVariableEntry has a check for +// `isTypeAnonymous`, but it has at least one bug (it misses double pointers). +// +// - Cast insertion, potentially. I was unable to find an example, but that +// doesn't mean it will never happen, especially with future changes to the +// code. +// +// - Typedef itype insertion. +// +// One approach to try to rule out all of these bugs at once would be to +// preemptively rewrite all multi-decls containing unnamed TagDecls, but those +// changes might be undesirable or could even cause errors in the presence of +// macros, etc. Or we could try to add the necessary code so that insertion of a +// reference to an unnamed TagDecl would trigger insertion of the name into the +// definition. For now, we don't deal with the problem. + +// Implementation note: The Clang AST does not represent multi-decls explicitly +// (except in functions, where they are represented by DeclStmts). In other +// contexts, we detect them based on the property that the beginning +// SourceLocation of all the members is the same. And as long as we are making +// this assumption, we use it in functions too rather than having a separate +// code path that looks for DeclStmts. + +// NamedDecl is the nearest common superclass of all Decl subtypes that can be +// multi-decl members. There is no enforcement that a MultiDeclMemberDecl is +// actually one of the allowed subtypes, so use of the MultiDeclMemberDecl +// typedef serves as documentation only. (If we wanted to enforce it, we'd need +// a wrapper object of some kind, which currently seems to be more trouble than +// it's worth.) +typedef NamedDecl MultiDeclMemberDecl; + +// Returns D if it can be a multi-decl member, otherwise null. +MultiDeclMemberDecl *getAsMultiDeclMember(Decl *D); + +// Helpers to cope with the different APIs to do corresponding things with a +// TypedefDecl or DeclaratorDecl. +QualType getTypeOfMultiDeclMember(MultiDeclMemberDecl *MMD); +TypeSourceInfo *getTypeSourceInfoOfMultiDeclMember(MultiDeclMemberDecl *MMD); + +struct MultiDeclInfo { + // The TagDecl that is defined inline in the multi-decl and needs to be split + // from it during rewriting, if any, otherwise null. In a case like + // `typedef struct { ... } T`, there is an inline tag definition but we don't + // need to split it out, so this will be null. + TagDecl *TagDefToSplit = nullptr; + + // True if the base type was an unnamed TagDecl defined inline for which we + // are using a new name. Note that TagDefToSplit can be nonnull and + // BaseTypeRenamed can be false if the inline TagDecl was named, and the + // reverse can occur in the `typedef struct { ... } T` case. + bool BaseTypeRenamed = false; + + // The members of the multi-decl in their original order. + std::vector Members; + + // Set by DeclRewriter::rewriteMultiDecl after it rewrites the entire + // multi-decl to ensure that it doesn't try to do so more than once if + // multiple members needed changes. + bool AlreadyRewritten = false; +}; + +struct TUMultiDeclsInfo { + // All multi-decls, keyed by the common beginning source location of their + // members. Note that the beginning source location of TagDefToSplit may be + // later if there is a keyword such as `static` or `typedef` in between. + std::map MultiDeclsByBeginLoc; + + // Map from a tag definition to its containing multi-decl (if it is part of + // one). Note that the TagDefToSplit of the MultiDeclInfo is not guaranteed to + // equal the TagDecl: it may be null in the `typedef struct { ... } T` case. + // + // Note that the MultiDeclInfo pointers remain valid for as long as the + // MultiDeclInfo objects remain in MultiDeclsByBeginLoc: see + // https://en.cppreference.com/w/cpp/container#Iterator_invalidation. + std::map ContainingMultiDeclOfTagDecl; +}; + +class ProgramMultiDeclsInfo { +private: + // Set of TagDecl names already used at least once in the program, so we can + // avoid colliding with them. + std::set UsedTagNames; + + // Information about an originally unnamed tag definition in a multi-decl for + // which we're using a new name. + struct RenamedTagDefInfo { + // The new string that should be used to refer to the type of the TagDecl. + // Unlike UsedTagNames, this includes the tag kind keyword (such as + // `struct`), except when we use an existing typedef (which doesn't require + // a tag keyword). + std::string AssignedTypeStr; + // Whether the TagDecl should be split from the multi-decl. True except when + // we use an existing typedef. + bool ShouldSplit; + }; + + // Map from PSL of a TagDecl to its RenamedTagDefInfo, to ensure that we + // handle the TagDecl consistently when 3C naively rewrites the same header + // file multiple times as part of different translation units (see + // https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-804283984). + std::map RenamedTagDefs; + + std::map TUInfos; + + // Recursive helpers. + void findUsedTagNames(DeclContext *DC); + void findMultiDecls(DeclContext *DC, ASTContext &Context); + +public: + void findUsedTagNames(ASTContext &Context); + void findMultiDecls(ASTContext &Context); + llvm::Optional getTypeStrOverride(const Type *Ty, + const ASTContext &C); + MultiDeclInfo *findContainingMultiDecl(MultiDeclMemberDecl *MMD); + MultiDeclInfo *findContainingMultiDecl(TagDecl *TD); + bool wasBaseTypeRenamed(Decl *D); +}; + +#endif // LLVM_CLANG_3C_MULTIDECLS_H diff --git a/clang/include/clang/3C/OptionalParams.h b/clang/include/clang/3C/OptionalParams.h new file mode 100644 index 000000000000..8d2c81738ac9 --- /dev/null +++ b/clang/include/clang/3C/OptionalParams.h @@ -0,0 +1,213 @@ +//=--3C.h---------------------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Definitions supporting a code pattern for functions that take _named_ +// optional parameters. For functions with multiple optional parameters, this +// pattern tends to be better than C++'s default arguments, which are positional +// and have the limitation that if a call site specifies a value for one +// argument, it must also specify values for all previous arguments. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_3C_OPTIONALPARAMS_H +#define LLVM_CLANG_3C_OPTIONALPARAMS_H + +// The basic ideas of the code pattern, for a function `foo`: +// +// 1. Define a struct `FooOpts` to hold the optional "parameters", and use +// default member initializers to specify the desired default values. +// 2. Declare `foo` to take a final parameter `const FooOpts &Opts = {}`. (The +// default argument `{}` corresponds to a `FooOpts` with all the default +// values.) +// 3. In the body of `foo`, use the `UNPACK_OPTS` macro to copy the fields of +// `Opts` to local variables for convenient access. +// 4. At call sites, use a macro to generate an immediately invoked lambda that +// generates a `FooOpts` instance with the desired values. We could +// alternatively use designated initializers if they were supported in our +// target C++ standard. +// +// In two places in the current pattern, optional parameters are simply copied +// by value, which gives rise to several limitations: we have to use pointers +// (`T *`) instead of references (`T &`), and there may be a performance cost to +// the copying. We may be able to overcome these limitations in the future with +// additional tricks such as using wrapper objects with an overloaded `=` +// operator for the members of `FooOpts`, but it doesn't seem worth it yet (as +// of 2021-07-09). +// +// Further design discussion: +// https://github.com/correctcomputation/checkedc-clang/issues/621 +// https://github.com/correctcomputation/checkedc-clang/pull/638 +// +// A simple example is included at the bottom of this file, after the +// implementation. That way, you can explore it in your IDE by changing the `#if +// 0` to `#if 1`, at the cost of having to scroll down to reach it. For an +// example of an actual use, see ConstraintVariable::mkString. + +// IMPLEMENTATION: + +// This currently supports up to 6 optional parameters for a given function. It +// should be clear how to extend the pattern of macros to increase the limit. + +// Our internal macro names are prefixed with `_OP_` (for "optional parameters") +// in an attempt to avoid accidental collisions with anything else in the LLVM +// monorepo. + +// Helpers: + +// Workaround for an MSVC bug where if a macro body passes __VA_ARGS__ to +// another macro (called M in this discussion), M receives a single argument +// containing commas instead of a variable number of arguments. For reasons we +// haven't researched, placing this wrapper around the entire macro body seems +// to avoid the problem. +// +// _OP_MSVC_VA_ARGS_WORKAROUND is needed only when M declares individual +// parameters and we need the arguments to be matched up with those parameters. +// If M just declares `...` and passes it on to another macro with __VA_ARGS__, +// it's generally harmless to let M receive a single argument containing commas; +// the argument will be correctly split up by _OP_MSVC_VA_ARGS_WORKAROUND later. +// +// We should be able to remove this workaround when the 3C Windows build moves +// to the "new" MSVC preprocessor. See +// https://developercommunity.visualstudio.com/t/-va-args-seems-to-be-trated-as-a-single-parameter/460154. +#define _OP_MSVC_VA_ARGS_WORKAROUND(_x) _x + +#define _OP_GET_ARG7(_1, _2, _3, _4, _5, _6, _7, ...) _7 +// Precondition: The number of arguments is between 1 and 6 inclusive. +// +// The `_dummy` is because according to the C++ standard, at least one argument +// must be passed to a `...` parameter +// (https://en.cppreference.com/w/cpp/preprocessor/replace). +#define _OP_COUNT_ARGS(...) \ + _OP_MSVC_VA_ARGS_WORKAROUND( \ + _OP_GET_ARG7(__VA_ARGS__, 6, 5, 4, 3, 2, 1, _dummy)) + +// Each _i argument is expected to be of the form `field = expr`, so we generate +// a statement of the form `_s.field = expr;` to set the struct field. +#define _OP_ASSIGN_FIELDS_1(_i1) _s._i1; +#define _OP_ASSIGN_FIELDS_2(_i1, _i2) \ + _s._i1; \ + _s._i2; +#define _OP_ASSIGN_FIELDS_3(_i1, _i2, _i3) \ + _s._i1; \ + _s._i2; \ + _s._i3; +#define _OP_ASSIGN_FIELDS_4(_i1, _i2, _i3, _i4) \ + _s._i1; \ + _s._i2; \ + _s._i3; \ + _s._i4; +#define _OP_ASSIGN_FIELDS_5(_i1, _i2, _i3, _i4, _i5) \ + _s._i1; \ + _s._i2; \ + _s._i3; \ + _s._i4; \ + _s._i5; +#define _OP_ASSIGN_FIELDS_6(_i1, _i2, _i3, _i4, _i5, _i6) \ + _s._i1; \ + _s._i2; \ + _s._i3; \ + _s._i4; \ + _s._i5; \ + _s._i6; +#define _OP_ASSIGN_FIELDS(_count, ...) \ + _OP_MSVC_VA_ARGS_WORKAROUND(_OP_ASSIGN_FIELDS_##_count(__VA_ARGS__)) +// In _OP_ASSIGN_FIELDS, ## takes precedence over the expansion of the _count +// argument, so if we let PACK_OPTS call `_OP_ASSIGN_FIELDS(_OP_COUNT_ARGS(foo), +// bar)` directly, we'd end up with `_OP_ASSIGN_FIELDS__COUNT_ARGS(foo)(bar)`. +// _OP_ASSIGN_FIELDS_WRAP expands its _count argument before calling +// _OP_ASSIGN_FIELDS, working around the problem. See the second example on +// https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html. +#define _OP_ASSIGN_FIELDS_WRAP(_count, ...) \ + _OP_ASSIGN_FIELDS(_count, __VA_ARGS__) + +// PACK_OPTS currently doesn't support passing an empty list of fields to the +// `...`, because in the current usage for optional parameters, we expect the +// whole struct to be omitted if no optional parameters need to be passed. +#define PACK_OPTS(_type, ...) \ + ([&] { \ + _type _s; \ + _OP_ASSIGN_FIELDS_WRAP(_OP_COUNT_ARGS(__VA_ARGS__), __VA_ARGS__) \ + return _s; \ + })() + +// `(void)_f` suppresses "unused variable" warnings: see the comment on +// LLVM_ATTRIBUTE_UNUSED in llvm/include/llvm/Support/Compiler.h. +#define _OP_UNPACK_OPT(_f) \ + auto _f = Opts._f; \ + (void)_f; +#define _OP_UNPACK_OPTS_1(_f1) _OP_UNPACK_OPT(_f1) +#define _OP_UNPACK_OPTS_2(_f1, _f2) _OP_UNPACK_OPT(_f1) _OP_UNPACK_OPT(_f2) +#define _OP_UNPACK_OPTS_3(_f1, _f2, _f3) \ + _OP_UNPACK_OPT(_f1) _OP_UNPACK_OPT(_f2) _OP_UNPACK_OPT(_f3) +#define _OP_UNPACK_OPTS_4(_f1, _f2, _f3, _f4) \ + _OP_UNPACK_OPT(_f1) \ + _OP_UNPACK_OPT(_f2) _OP_UNPACK_OPT(_f3) _OP_UNPACK_OPT(_f4) +#define _OP_UNPACK_OPTS_5(_f1, _f2, _f3, _f4, _f5) \ + _OP_UNPACK_OPT(_f1) \ + _OP_UNPACK_OPT(_f2) \ + _OP_UNPACK_OPT(_f3) _OP_UNPACK_OPT(_f4) _OP_UNPACK_OPT(_f5) +#define _OP_UNPACK_OPTS_6(_f1, _f2, _f3, _f4, _f5, _f6) \ + _OP_UNPACK_OPT(_f1) \ + _OP_UNPACK_OPT(_f2) \ + _OP_UNPACK_OPT(_f3) \ + _OP_UNPACK_OPT(_f4) _OP_UNPACK_OPT(_f5) _OP_UNPACK_OPT(_f6) +#define _OP_UNPACK_OPTS_IMPL(_count, ...) \ + _OP_MSVC_VA_ARGS_WORKAROUND(_OP_UNPACK_OPTS_##_count(__VA_ARGS__)) +// Ditto the comment on _OP_ASSIGN_FIELDS_WRAP. +#define _OP_UNPACK_OPTS_WRAP(_count, ...) \ + _OP_UNPACK_OPTS_IMPL(_count, __VA_ARGS__) +// UNPACK_OPTS doesn't support an empty list of parameters: the UNPACK_OPTS call +// should just be omitted in that case. +#define UNPACK_OPTS(...) \ + _OP_UNPACK_OPTS_WRAP(_OP_COUNT_ARGS(__VA_ARGS__), __VA_ARGS__) + +// EXAMPLE (change `#if 0` to `#if 1` to explore it in your IDE): + +#if 0 + +struct FooOpts { + std::string B = "hello"; + bool C = false; +}; + +#define FOO_OPTS(...) PACK_OPTS(FooOpts, __VA_ARGS__) + +void foo(int A, const FooOpts &Opts = {}) { + UNPACK_OPTS(B, C); +#if 0 // Avoid "duplicate local variable" errors in this illustration. + // Expansion: + auto B = Opts.B; + // Suppresses an "unused variable" warning if FooOpts is shared by several + // functions that take the same optional parameters (e.g., virtual overrides) + // and some of those functions don't actually read all the parameters. + (void)B; + auto C = Opts.C; + (void)C; +#endif + + llvm::errs() << "A is " << A << ", B is " << B << ", C is " << C << "\n"; +} + +void test() { + foo(1); + foo(2, FOO_OPTS(B = "goodbye")); + // Expansion (notice how we conveniently concatenated `_s.` and the argument + // `B = "goodbye"`): + foo(2, ([&] { + FooOpts _s; + _s.B = "goodbye"; + return _s; + })()); + + foo(3, FOO_OPTS(C = true)); + foo(4, FOO_OPTS(B = "goodbye", C = true)); +} + +#endif // example + +#endif // LLVM_CLANG_3C_OPTIONALPARAMS_H diff --git a/clang/include/clang/3C/PersistentSourceLoc.h b/clang/include/clang/3C/PersistentSourceLoc.h index d03fdfb6ee12..85dfa5b4bad6 100644 --- a/clang/include/clang/3C/PersistentSourceLoc.h +++ b/clang/include/clang/3C/PersistentSourceLoc.h @@ -66,20 +66,20 @@ class PersistentSourceLoc { void dump() const { print(llvm::errs()); } static PersistentSourceLoc mkPSL(const clang::Decl *D, - clang::ASTContext &Context); + const clang::ASTContext &Context); static PersistentSourceLoc mkPSL(const clang::Stmt *S, - clang::ASTContext &Context); + const clang::ASTContext &Context); static PersistentSourceLoc mkPSL(const clang::Expr *E, - clang::ASTContext &Context); + const clang::ASTContext &Context); private: // Create a PersistentSourceLoc based on absolute file path // from the given SourceRange and SourceLocation. static PersistentSourceLoc mkPSL(clang::SourceRange SR, clang::SourceLocation SL, - clang::ASTContext &Context); + const clang::ASTContext &Context); // The source file name. std::string FileName; // Starting line number. diff --git a/clang/include/clang/3C/ProgramInfo.h b/clang/include/clang/3C/ProgramInfo.h index 5e691c08bc77..02c73142ec97 100644 --- a/clang/include/clang/3C/ProgramInfo.h +++ b/clang/include/clang/3C/ProgramInfo.h @@ -16,6 +16,7 @@ #include "clang/3C/3CStats.h" #include "clang/3C/AVarBoundsInfo.h" #include "clang/3C/ConstraintVariables.h" +#include "clang/3C/MultiDecls.h" #include "clang/3C/PersistentSourceLoc.h" #include "clang/3C/Utils.h" #include "clang/AST/ASTConsumer.h" @@ -34,8 +35,8 @@ class ProgramVariableAdder { virtual bool seenTypedef(PersistentSourceLoc PSL) = 0; - virtual void addTypedef(PersistentSourceLoc PSL, bool CanRewriteDef, - TypedefDecl *TD, ASTContext &C) = 0; + virtual void addTypedef(PersistentSourceLoc PSL, TypedefDecl *TD, + ASTContext &C) = 0; protected: virtual AVarBoundsInfo &getABoundsInfo() = 0; @@ -43,14 +44,40 @@ class ProgramVariableAdder { typedef std::pair CSetBkeyPair; +// The pair of CVs are the type param constraint and an optional +// constraint used to get the generic index. A better solution would have +// generic constraints saved within ConstraintVariables, but those don't +// exist at this time. +struct TypeParamConstraint { + ConstraintVariable *MainConstraint; + ConstraintVariable *GenericAddition; + TypeParamConstraint() : + MainConstraint(nullptr), GenericAddition(nullptr) {} + TypeParamConstraint(ConstraintVariable *M, ConstraintVariable *G) : + MainConstraint(M), GenericAddition(G) {} + // Fast. Whether `getConstraint` will return something other than nullptr. + bool isConsistent() const { return MainConstraint != nullptr; } + // Provides generic information if available and safe. This is somewhat of + // a hack for nested generics and returns (the constraint for) a local + // parameter. Otherwise, returns the generated constraint, which can also be + // accessed as `MainConstraint`. + ConstraintVariable *getConstraint(const EnvironmentMap &E) { + if (MainConstraint != nullptr && GenericAddition != nullptr && + GenericAddition->isSolutionChecked(E)) { + return GenericAddition; + } else { + return MainConstraint; + } + } +}; + class ProgramInfo : public ProgramVariableAdder { public: + // This map holds similar information as the type variable map in // ConstraintBuilder.cpp, but it is stored in a form that is usable during // rewriting. - typedef std::map CallTypeParamBindingsT; - typedef std::map - TypeParamBindingsT; + typedef std::map CallTypeParamBindingsT; typedef std::map ExternalFunctionMapType; typedef std::map StaticFunctionMapType; @@ -90,12 +117,16 @@ class ProgramInfo : public ProgramVariableAdder { // Store CVarSet with an empty set of BoundsKey into persistent contents. void storePersistentConstraints(clang::Expr *E, const CVarSet &Vars, ASTContext *C); + void removePersistentConstraints(Expr *E, ASTContext *C); // Get constraint variable for the provided Decl CVarOption getVariable(clang::Decl *D, clang::ASTContext *C); // Retrieve a function's constraints by decl, or by name; nullptr if not found FVConstraint *getFuncConstraint(FunctionDecl *D, ASTContext *C) const; + FVConstraint * + getFuncConstraint(const std::string &FuncName, const std::string &FileName, + bool IsStatic) const; FVConstraint *getExtFuncDefnConstraint(std::string FuncName) const; FVConstraint *getStaticFuncConstraint(std::string FuncName, std::string FileName) const; @@ -107,7 +138,8 @@ class ProgramInfo : public ProgramVariableAdder { const VariableMap &getVarMap() const { return Variables; } Constraints &getConstraints() { return CS; } - AVarBoundsInfo &getABoundsInfo() { return ArrBInfo; } + const Constraints &getConstraints() const { return CS; } + AVarBoundsInfo &getABoundsInfo() override { return ArrBInfo; } PerformanceStats &getPerfStats() { return PerfS; } @@ -123,26 +155,35 @@ class ProgramInfo : public ProgramVariableAdder { } void setTypeParamBinding(CallExpr *CE, unsigned int TypeVarIdx, - ConstraintVariable *CV, ASTContext *C); + ConstraintVariable *CV, + ConstraintVariable* Ident, ASTContext *C); bool hasTypeParamBindings(CallExpr *CE, ASTContext *C) const; const CallTypeParamBindingsT &getTypeParamBindings(CallExpr *CE, ASTContext *C) const; void constrainWildIfMacro(ConstraintVariable *CV, SourceLocation Location, - PersistentSourceLoc *PSL = nullptr); + const ReasonLoc &Rsn); - void ensureNtCorrect(const QualType &QT, const ASTContext &C, + void ensureNtCorrect(const QualType &QT, const PersistentSourceLoc &PSL, PointerVariableConstraint *PV); - void unifyIfTypedef(const clang::Type *, clang::ASTContext &, - clang::DeclaratorDecl *, PVConstraint *); + void unifyIfTypedef(const QualType &QT, clang::ASTContext &, + PVConstraint *, ConsAction CA = Same_to_Same); CVarOption lookupTypedef(PersistentSourceLoc PSL); - bool seenTypedef(PersistentSourceLoc PSL); + bool seenTypedef(PersistentSourceLoc PSL) override; + + void addTypedef(PersistentSourceLoc PSL, TypedefDecl *TD, + ASTContext &C) override; + + // Store mapping from ASTContexts to a unique index in the ASTs vector in + // the ProgramInfo object. This function must be called prior to any AST + // traversals so that the map is populated. + void registerTranslationUnits( + const std::vector> &ASTs); - void addTypedef(PersistentSourceLoc PSL, bool CanRewriteDef, TypedefDecl *TD, - ASTContext &C); + ProgramMultiDeclsInfo TheMultiDeclsInfo; private: // List of constraint variables for declarations, indexed by their location in @@ -155,9 +196,11 @@ class ProgramInfo : public ProgramVariableAdder { // rewritten. std::map TypedefVars; - // A pair containing an AST node ID and the name of the main file in the - // translation unit. Used as a key to index expression in the following maps. - typedef std::pair IDAndTranslationUnit; + // A pair containing an AST node ID and an index that uniquely identifies the + // translation unit. Translation unit identifiers are drawn from the + // TranslationUnitIdxMap. Used as a key to index expression in the following + // maps. + typedef std::pair IDAndTranslationUnit; IDAndTranslationUnit getExprKey(clang::Expr *E, clang::ASTContext *C) const; // Map with the similar purpose as the Variables map. This stores a set of @@ -169,6 +212,14 @@ class ProgramInfo : public ProgramVariableAdder { // expected that multiple entries will map to the same source location. std::map ExprLocations; + // This map holds similar information as the type variable map in + // ConstraintBuilder.cpp, but it is stored in a form that is usable during + // rewriting. + typedef std::map + TypeParamBindingsT; + + std::map DeletedAtomLocations; + //Performance stats PerformanceStats PerfS; @@ -196,10 +247,14 @@ class ProgramInfo : public ProgramVariableAdder { // instantiated so they can be inserted during rewriting. TypeParamBindingsT TypeParamBindings; - // Special-case handling for decl introductions. For the moment this covers: - // * void-typed variables - // * va_list-typed variables - void specialCaseVarIntros(ValueDecl *D, ASTContext *Context); + // Maps each ASTContext to a unique index in the vector of ASTs being + // processed. This is used to uniquely determine the translation unit an AST + // belongs to given its corresponding ASTContext. By using this index instead + // of the name of the main file, this uniquely identifies the translation unit + // even when a file is the main file of multiple translation units. The values + // in this map are used as part of the IDAndTranslationUnit which is the type + // used as keys for maps from ASTNodes. + std::map TranslationUnitIdxMap; // Inserts the given FVConstraint set into the extern or static function map. // Returns the merged version if it was a redeclaration, or the constraint @@ -210,8 +265,7 @@ class ProgramInfo : public ProgramVariableAdder { // Retrieves a FVConstraint* from a Decl (which could be static, or global) FVConstraint *getFuncFVConstraint(FunctionDecl *FD, ASTContext *C); - void insertIntoPtrSourceMap(const PersistentSourceLoc *PSL, - ConstraintVariable *CV); + void insertIntoPtrSourceMap(PersistentSourceLoc PSL, ConstraintVariable *CV); void computePtrLevelStats(); @@ -220,7 +274,10 @@ class ProgramInfo : public ProgramVariableAdder { // For each pointer type in the declaration of D, add a variable to the // constraint system for that pointer type. - void addVariable(clang::DeclaratorDecl *D, clang::ASTContext *AstContext); + void addVariable(clang::DeclaratorDecl *D, + clang::ASTContext *AstContext) override; + + void linkFunction(FunctionVariableConstraint *FV); }; #endif diff --git a/clang/include/clang/3C/ProgramVar.h b/clang/include/clang/3C/ProgramVar.h index 0841809dcedd..d6da3d94e9e0 100644 --- a/clang/include/clang/3C/ProgramVar.h +++ b/clang/include/clang/3C/ProgramVar.h @@ -72,23 +72,25 @@ class PVSComp { class GlobalScope : public ProgramVarScope { public: GlobalScope() : ProgramVarScope(GlobalScopeKind) {} - virtual ~GlobalScope() {} + ~GlobalScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == GlobalScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { return clang::isa(&O); } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { return false; } + bool operator<(const ProgramVarScope &O) const override { return false; } - bool isInInnerScope(const ProgramVarScope &O) const { return false; } + bool isInInnerScope(const ProgramVarScope &O) const override { return false; } - std::string getStr() const { return "Global"; } + std::string getStr() const override { return "Global"; } static GlobalScope *getGlobalScope(); @@ -100,22 +102,24 @@ class StructScope : public ProgramVarScope { public: StructScope(std::string SN) : ProgramVarScope(StructScopeKind), StName(SN) {} - virtual ~StructScope() {} + ~StructScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == StructScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { if (auto *SS = clang::dyn_cast(&O)) { return SS->StName == StName; } return false; } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { + bool operator<(const ProgramVarScope &O) const override { if (clang::isa(&O)) { return true; } @@ -130,12 +134,12 @@ class StructScope : public ProgramVarScope { return false; } - bool isInInnerScope(const ProgramVarScope &O) const { + bool isInInnerScope(const ProgramVarScope &O) const override { // only global variables are visible here. return clang::isa(&O); } - std::string getStr() const { return "Struct_" + StName; } + std::string getStr() const override { return "Struct_" + StName; } std::string getSName() const { return this->StName; } @@ -155,22 +159,24 @@ class CtxStructScope : public StructScope { this->Kind = CtxStructScopeKind; } - virtual ~CtxStructScope() {} + ~CtxStructScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == CtxStructScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { if (auto *CS = clang::dyn_cast(&O)) { return CS->StName == StName && CS->ASKey == ASKey && CS->IsG == IsG; } return false; } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { + bool operator<(const ProgramVarScope &O) const override { if (clang::isa(&O) || clang::isa(&O)) { return true; } @@ -191,7 +197,7 @@ class CtxStructScope : public StructScope { return false; } - std::string getStr() const { + std::string getStr() const override { return "CtxStruct_" + ASKey + "_" + StName + "_" + std::to_string(IsG); } @@ -210,22 +216,24 @@ class FunctionParamScope : public ProgramVarScope { FunctionParamScope(const std::string &FN, bool IsSt) : ProgramVarScope(FunctionParamScopeKind), FName(FN), IsStatic(IsSt) {} - virtual ~FunctionParamScope() {} + ~FunctionParamScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == FunctionParamScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { if (auto *FPS = clang::dyn_cast(&O)) { return (FPS->FName == FName && FPS->IsStatic == IsStatic); } return false; } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { + bool operator<(const ProgramVarScope &O) const override { if (clang::isa(&O) || clang::isa(&O)) { return true; } @@ -243,12 +251,12 @@ class FunctionParamScope : public ProgramVarScope { return false; } - bool isInInnerScope(const ProgramVarScope &O) const { + bool isInInnerScope(const ProgramVarScope &O) const override { // only global variables are visible here. return clang::isa(&O); } - std::string getStr() const { return "FuncParm_" + FName; } + std::string getStr() const override { return "FuncParm_" + FName; } const llvm::StringRef getFName() const { return this->FName; } @@ -287,13 +295,13 @@ class CtxFunctionArgScope : public FunctionParamScope { this->Kind = CtxFunctionArgScopeKind; } - virtual ~CtxFunctionArgScope() {} + ~CtxFunctionArgScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == CtxFunctionArgScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { if (auto *FPS = clang::dyn_cast(&O)) { return (FPS->FName == FName && FPS->IsStatic == IsStatic && !(FPS->PSL < PSL || PSL < FPS->PSL)); @@ -301,9 +309,11 @@ class CtxFunctionArgScope : public FunctionParamScope { return false; } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { + bool operator<(const ProgramVarScope &O) const override { if (clang::isa(&O) || clang::isa(&O) || clang::isa(&O)) { return true; @@ -325,7 +335,7 @@ class CtxFunctionArgScope : public FunctionParamScope { return false; } - std::string getStr() const { return FName + "_Ctx_" + CtxIDStr; } + std::string getStr() const override { return FName + "_Ctx_" + CtxIDStr; } static const CtxFunctionArgScope * getCtxFunctionParamScope(const FunctionParamScope *FPS, @@ -342,25 +352,24 @@ class FunctionScope : public ProgramVarScope { FunctionScope(std::string FN, bool IsSt) : ProgramVarScope(FunctionScopeKind), FName(FN), IsStatic(IsSt) {} - virtual ~FunctionScope() {} + ~FunctionScope() override {} static bool classof(const ProgramVarScope *S) { return S->getKind() == FunctionScopeKind; } - bool operator==(const ProgramVarScope &O) const { + bool operator==(const ProgramVarScope &O) const override { if (auto *FS = clang::dyn_cast(&O)) { return (FS->FName == FName && FS->IsStatic == IsStatic); } - if (auto *FPS = clang::dyn_cast(&O)) { - return (FPS->FName == FName && FPS->IsStatic == IsStatic); - } return false; } - bool operator!=(const ProgramVarScope &O) const { return !(*this == O); } + bool operator!=(const ProgramVarScope &O) const override { + return !(*this == O); + } - bool operator<(const ProgramVarScope &O) const { + bool operator<(const ProgramVarScope &O) const override { if (clang::isa(&O) || clang::isa(&O) || clang::isa(&O) || clang::isa(&O)) { return true; @@ -378,9 +387,9 @@ class FunctionScope : public ProgramVarScope { return false; } - bool isInInnerScope(const ProgramVarScope &O) const; + bool isInInnerScope(const ProgramVarScope &O) const override; - std::string getStr() const { return "InFunc_" + FName; } + std::string getStr() const override { return "InFunc_" + FName; } static const FunctionScope *getFunctionScope(std::string FnName, bool IsSt); @@ -394,35 +403,55 @@ class FunctionScope : public ProgramVarScope { // Class that represents a program variable along with its scope. class ProgramVar { public: - const ProgramVarScope *getScope() { return VScope; } + const ProgramVarScope *getScope() const { return VScope; } void setScope(const ProgramVarScope *PVS) { this->VScope = PVS; } - BoundsKey getKey() { return K; } - bool isNumConstant() { return IsConstant; } - std::string mkString(bool GetKey = false); - std::string getVarName() { return VarName; } - std::string verboseStr(); - ProgramVar *makeCopy(BoundsKey NK); - virtual ~ProgramVar() {} + BoundsKey getKey() const { return K; } + const std::string &getVarName() const { return VarName; } + std::string verboseStr() const; + ProgramVar *makeCopy(BoundsKey NK) const; + + bool isNumConstant() const {return IsConstant; } + uint64_t getConstantVal() const { + assert("Can't get constant value for non-constant var." && IsConstant); + return ConstantVal; + } static ProgramVar *createNewProgramVar(BoundsKey VK, std::string VName, - const ProgramVarScope *PVS, - bool IsCons = false); + const ProgramVarScope *PVS); + + static ProgramVar *createNewConstantVar(BoundsKey VK, uint64_t Value); private: BoundsKey K; std::string VarName; const ProgramVarScope *VScope; - bool IsConstant; // is a literal integer, not a variable + + // Is a literal integer, not a variable. + bool IsConstant; + uint64_t ConstantVal; + // TODO: All the ProgramVars may not be used. We should try to figure out // a way to free unused program vars. - static std::set AllProgramVars; - - ProgramVar(BoundsKey VK, std::string VName, const ProgramVarScope *PVS, - bool IsCons) - : K(VK), VarName(VName), VScope(PVS), IsConstant(IsCons) {} + static std::set AllProgramVars; + + ProgramVar(BoundsKey K, const std::string &VarName, + const ProgramVarScope *VScope, bool IsConstant, + uint32_t ConstantVal) + : K(K), VarName(VarName), VScope(VScope), IsConstant(IsConstant), + ConstantVal(ConstantVal) { + // Constant variables should be a subclass of ProgramVariable. Until that + // change happens this should sanity check how ProgramVars are constructed. + assert("Constant value should not be set for non-constant variables." && + (IsConstant || ConstantVal == 0)); + AllProgramVars.insert(this); + } ProgramVar(BoundsKey VK, std::string VName, const ProgramVarScope *PVS) - : ProgramVar(VK, VName, PVS, false) {} + : ProgramVar(VK, VName, PVS, false, 0) {} + + ProgramVar(BoundsKey VK, uint32_t CVal) + : ProgramVar(VK, std::to_string(CVal), GlobalScope::getGlobalScope(), true, + CVal) {} }; #endif // LLVM_CLANG_3C_PROGRAMVAR_H diff --git a/clang/include/clang/3C/RewriteUtils.h b/clang/include/clang/3C/RewriteUtils.h index 8de42a0d50fb..5bb60aff7787 100644 --- a/clang/include/clang/3C/RewriteUtils.h +++ b/clang/include/clang/3C/RewriteUtils.h @@ -24,37 +24,36 @@ class DeclReplacement { public: virtual Decl *getDecl() const = 0; - DeclStmt *getStatement() const { return Statement; } - std::string getReplacement() const { return Replacement; } - virtual SourceRange getSourceRange(SourceManager &SM) const { - return getDecl()->getSourceRange(); - } + virtual SourceRange getSourceRange(SourceManager &SM) const; // Discriminator for LLVM-style RTTI (dyn_cast<> et al.). enum DRKind { - DRK_VarDecl, - DRK_ParmVarDecl, + DRK_MultiDeclMember, DRK_FunctionDecl, - DRK_FieldDecl, - DRK_TypedefDecl }; DRKind getKind() const { return Kind; } virtual ~DeclReplacement() {} -protected: - explicit DeclReplacement(DeclStmt *S, std::string R, DRKind K) - : Statement(S), Replacement(R), Kind(K) {} + const std::vector &getSupplementaryDecls() const { + return SupplementaryDecls; + } - // The Stmt, if it exists (may be nullptr). - DeclStmt *Statement; +protected: + explicit DeclReplacement(std::string R, std::vector SDecls, + DRKind K) : Replacement(R), + SupplementaryDecls(SDecls), Kind(K) {} // The string to replace the declaration with. std::string Replacement; + // A declaration might need to be replaced with more than a single new + // declaration. These extra declarations can be stored in this vector to be + // emitted after the original declaration. + std::vector SupplementaryDecls; private: const DRKind Kind; }; @@ -62,8 +61,9 @@ class DeclReplacement { template class DeclReplacementTempl : public DeclReplacement { public: - explicit DeclReplacementTempl(DeclT *D, DeclStmt *DS, std::string R) - : DeclReplacement(DS, R, K), Decl(D) {} + explicit DeclReplacementTempl(DeclT *D, std::string R, + std::vector SDecls) + : DeclReplacement(R, SDecls, K), Decl(D) {} DeclT *getDecl() const override { return Decl; } @@ -73,94 +73,97 @@ class DeclReplacementTempl : public DeclReplacement { DeclT *Decl; }; -typedef DeclReplacementTempl - VarDeclReplacement; -typedef DeclReplacementTempl - ParmVarDeclReplacement; -typedef DeclReplacementTempl - FieldDeclReplacement; -typedef DeclReplacementTempl - TypedefDeclReplacement; +typedef DeclReplacementTempl + MultiDeclMemberReplacement; class FunctionDeclReplacement : public DeclReplacementTempl { public: - explicit FunctionDeclReplacement(FunctionDecl *D, std::string R, bool Return, - bool Params) - : DeclReplacementTempl(D, nullptr, R), RewriteReturn(Return), - RewriteParams(Params) { + explicit FunctionDeclReplacement(FunctionDecl *D, std::string R, + std::vector SDecls, bool Return, + bool Params, bool Generic = false) + : DeclReplacementTempl(D, R, SDecls), RewriteGeneric(Generic), + RewriteReturn(Return), RewriteParams(Params) { assert("Doesn't make sense to rewrite nothing!" && - (RewriteReturn || RewriteParams)); + (RewriteGeneric || RewriteReturn || RewriteParams)); } SourceRange getSourceRange(SourceManager &SM) const override; private: // This determines if the full declaration or the return will be replaced. + bool RewriteGeneric; bool RewriteReturn; bool RewriteParams; SourceLocation getDeclBegin(SourceManager &SM) const; + SourceLocation getReturnBegin(SourceManager &SM) const; SourceLocation getParamBegin(SourceManager &SM) const; SourceLocation getReturnEnd(SourceManager &SM) const; SourceLocation getDeclEnd(SourceManager &SM) const; }; -// Compare two DeclReplacement values. The algorithm for comparing them relates -// their source positions. If two DeclReplacement values refer to overlapping -// source positions, then they are the same. Otherwise, they are ordered -// by their placement in the input file. -// -// There are two special cases: Function declarations, and DeclStmts. In turn: -// -// - Function declarations might either be a DeclReplacement describing the -// entire declaration, i.e. replacing "int *foo(void)" -// with "int *foo(void) : itype(_Ptr)". Or, it might describe just -// replacing only the return type, i.e. "_Ptr foo(void)". This is -// discriminated against with the 'fullDecl' field of the DeclReplacement -// type and the comparison function first checks if the operands are -// FunctionDecls and if the 'fullDecl' field is set. -// - A DeclStmt of mupltiple Decls, i.e. 'int *a = 0, *b = 0'. In this case, -// we want the DeclReplacement to refer only to the specific sub-region that -// would be replaced, i.e. '*a = 0' and '*b = 0'. To do that, we traverse -// the Decls contained in a DeclStmt and figure out what the appropriate -// source locations are to describe the positions of the independent -// declarations. -class DComp { -public: - DComp(SourceManager &S) : SM(S) {} - - bool operator()(DeclReplacement *Lhs, DeclReplacement *Rhs) const; - -private: - SourceManager &SM; - - SourceRange getReplacementSourceRange(DeclReplacement *D) const; - SourceLocation getDeclBegin(DeclReplacement *D) const; +typedef std::map RSet; + +// Represent a rewritten declaration split into three components. For a +// parameter or local variable declaration, concatenating Type and IType will +// give the full declaration. For a function return, Type should appear before +// the identifier and parameter list and itype should appear after. +struct RewrittenDecl { + explicit RewrittenDecl() : Type(), IType(), SupplementaryDecl() {} + explicit RewrittenDecl(std::string Type, std::string IType, + std::string SupplementaryDecl) + : Type(Type), IType(IType), SupplementaryDecl(SupplementaryDecl) {} + + // For function returns, the component of the declaration that appears before + // the identifier. For parameter and local variables, a prefix of the full + // declaration up to at least the identifier, but possibly omitting any itype + // or array bounds, which may be stored in the Itype field below. The + // identifier in this string is not always the same as the original identifier. + // If 3C generates a fresh lower bound (stored in the SupplementrayDecl + // string), then the identifier is changed to a temporary name. + std::string Type; + + // For function returns, the component of the rewritten declaration that + // appears after the parameter list. For parameter and local variables, some + // suffix of the full declaration, often any itype or bounds declaration, + // but also possibly empty. + std::string IType; + + // An additional declaration required as a result of the rewriting done to the + // original declaration. The additional declaration may refer to the original, + // so it must be emitted after the original declaration. + // This is currently only used to automatically fatten pointers to use fresh + // lower bound pointers. e.g., + // _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + n) + // If the declaration does not need a fresh lower bound, then this string is + // empty. + std::string SupplementaryDecl; }; -typedef std::set RSet; - -// This class is used to figure out which global variables are part of -// multi-variable declarations. For local variables, all variables in a single -// multi declaration are grouped together in a DeclStmt object. This is not the -// case for global variables, so this class is required to correctly group -// global variable declarations. Declarations in the same multi-declarations -// have the same beginning source locations, so it is used to group variables. -class GlobalVariableGroups { -public: - GlobalVariableGroups(SourceManager &SourceMgr) : SM(SourceMgr) {} - void addGlobalDecl(Decl *VD, std::vector *VDVec = nullptr); - - std::vector &getVarsOnSameLine(Decl *VD); - - virtual ~GlobalVariableGroups(); - -private: - SourceManager &SM; - std::map *> GlobVarGroups; -}; +// Generate a string for the declaration based on the given PVConstraint. +// Includes the storage qualifier, type, name, and bounds string (as +// applicable), or generates an itype declaration if required due to +// ItypesForExtern. Does not include a trailing semicolon or an initializer, so +// it can be used in combination with getDeclSourceRangeWithAnnotations with +// IncludeInitializer = false to preserve an existing initializer. +RewrittenDecl mkStringForPVDecl(MultiDeclMemberDecl *MMD, PVConstraint *PVC, + ProgramInfo &Info); + +// Generate a string like mkStringForPVDecl, but for a declaration whose type is +// known not to have changed (except possibly for a base type rename) and that +// may not have a PVConstraint if the type is not a pointer or array type. +// +// For similar reasons as in the comment in DeclRewriter::buildItypeDecl, this +// will get the string from Clang instead of mkString if the base type hasn't +// been renamed (hence the need to assume the rest of the type has not changed). +// Yet another possible approach would be to combine the new base type name with +// the original source for the rest of the declaration, but that may run into +// problems with macros and the like, so we might still need some fallback. For +// now, we don't implement this "original source" approach. +std::string mkStringForDeclWithUnchangedType(MultiDeclMemberDecl *D, + ProgramInfo &Info); // Class that handles rewriting bounds information for all the // detected array variables. @@ -169,7 +172,8 @@ class ArrayBoundsRewriter { ArrayBoundsRewriter(ProgramInfo &I) : Info(I) {} // Get the string representation of the bounds for the given variable. std::string getBoundsString(const PVConstraint *PV, Decl *D, - bool Isitype = false); + bool Isitype = false, + bool OmitLowerBound = false); // Check if the constraint variable has newly created bounds string. bool hasNewBoundsString(const PVConstraint *PV, Decl *D, @@ -183,7 +187,7 @@ class RewriteConsumer : public ASTConsumer { public: explicit RewriteConsumer(ProgramInfo &I) : Info(I) {} - virtual void HandleTranslationUnit(ASTContext &Context); + void HandleTranslationUnit(ASTContext &Context) override; private: ProgramInfo &Info; @@ -195,6 +199,12 @@ class RewriteConsumer : public ASTConsumer { static std::set EmittedDiagnostics; void emitRootCauseDiagnostics(ASTContext &Context); + + // Hack to avoid printing the main file to stdout multiple times in the edge + // case of a compilation database containing multiple translation units for + // the main file + // (https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-893612654). + bool StdoutModeEmittedMainFile = false; }; bool canRewrite(Rewriter &R, const SourceRange &SR); diff --git a/clang/include/clang/3C/StructInit.h b/clang/include/clang/3C/StructInit.h index 7bf6974e3244..5ee4cc3c14a0 100644 --- a/clang/include/clang/3C/StructInit.h +++ b/clang/include/clang/3C/StructInit.h @@ -34,11 +34,10 @@ class StructVariableInitializer explicit StructVariableInitializer(ASTContext *C, ProgramInfo &I, RSet &R) : Context(C), I(I), RewriteThese(R), RecordsWithCPointers() {} - bool VisitDeclStmt(DeclStmt *S); + bool VisitVarDecl(VarDecl *VD); private: bool hasCheckedMembers(DeclaratorDecl *DD); - void insertVarDecl(VarDecl *VD, DeclStmt *S); ASTContext *Context; ProgramInfo &I; diff --git a/clang/include/clang/3C/TypeVariableAnalysis.h b/clang/include/clang/3C/TypeVariableAnalysis.h index 31799a6d6923..416550a6f378 100644 --- a/clang/include/clang/3C/TypeVariableAnalysis.h +++ b/clang/include/clang/3C/TypeVariableAnalysis.h @@ -20,8 +20,9 @@ class TypeVariableEntry { public: // Note: does not initialize TyVarType! TypeVariableEntry() : IsConsistent(false), TypeParamConsVar(nullptr) {} - TypeVariableEntry(QualType Ty, std::set &CVs, - bool ForceInconsistent = false) + TypeVariableEntry(QualType Ty, std::set &CVs + , bool ForceInconsistent = false + , ConstraintVariable *IdentCV = nullptr) : TypeParamConsVar(nullptr) { // We'll need a name to provide the type arguments during rewriting, so no // anonymous types are allowed. @@ -30,16 +31,21 @@ class TypeVariableEntry { !isTypeAnonymous(Ty->getPointeeOrArrayElementType()); TyVarType = Ty; ArgConsVars = CVs; + GenArgumentCV = IdentCV; } bool getIsConsistent() const; + // Note: undefined behaviour if `getIsConsistent` is false QualType getType(); + // Note: undefined behaviour if `getIsConsistent` is false std::set &getConstraintVariables(); ConstraintVariable *getTypeParamConsVar(); + ConstraintVariable *getGenArgCV(); void insertConstraintVariables(std::set &CVs); void setTypeParamConsVar(ConstraintVariable *CV); - void updateEntry(QualType Ty, std::set &CVs); + void updateEntry(QualType Ty, std::set &CVs, + ConstraintVariable *IdentCV); private: // Is this type variable instantiated consistently. True when all uses have @@ -53,11 +59,18 @@ class TypeVariableEntry { // Collection of constraint variables generated for all uses of the type // variable. Also should not be used when IsConsistent is false. + // TODO: accessor methods don't enforce this? std::set ArgConsVars; // A single constraint variable for solving the checked type of the type // variable. It is constrained GEQ all elements of ArgConsVars. ConstraintVariable *TypeParamConsVar; + + // If an argument is a single identifier, store the constraint variable + // to recognize changes in type from inferred generics. Null otherwise. + // Meaningless if `TypeParamConsVar` has a basetype other than void, and + // when we have generic index constraints, those should be favored over this + ConstraintVariable *GenArgumentCV; }; // Stores the instantiated type for each type variables. This map has @@ -65,6 +78,7 @@ class TypeVariableEntry { // typed parameter. The values in the map are another maps from type variable // index in the called function's parameter list to the type the type variable // becomes (or null if it is not used consistently). +// TODO: use a better map implementation? typedef std::map> TypeVariableMapT; @@ -86,8 +100,9 @@ class TypeVarVisitor : public RecursiveASTVisitor, bool VisitCastExpr(CastExpr *CE); bool VisitCallExpr(CallExpr *CE); - void getConsistentTypeParams(CallExpr *CE, std::set &Types); - void setProgramInfoTypeVars(); + void getConsistentTypeParams(CallExpr *CE, + std::set &Types) override; + void setProgramInfoTypeVars() override; private: ASTContext *Context; @@ -95,8 +110,8 @@ class TypeVarVisitor : public RecursiveASTVisitor, ConstraintResolver CR; TypeVariableMapT TVMap; - void insertBinding(CallExpr *CE, const int TyIdx, QualType Ty, CVarSet &CVs, - bool ForceInconsistent = false); + void insertBinding(CallExpr *CE, const int TyIdx, QualType Ty, + CVarSet &CVs, ConstraintVariable *IdentCV = nullptr); }; bool typeArgsProvided(CallExpr *Call); diff --git a/clang/include/clang/3C/Utils.h b/clang/include/clang/3C/Utils.h index 8e64eec057ca..5cd8159fd82a 100644 --- a/clang/include/clang/3C/Utils.h +++ b/clang/include/clang/3C/Utils.h @@ -13,6 +13,7 @@ #include "clang/3C/PersistentSourceLoc.h" #include "clang/AST/Type.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" @@ -68,8 +69,8 @@ template class BiMap { ValToK.clear(); } - const std::map &left() { return KtoVal; } - const std::map &right() { return ValToK; } + const std::map &left() const { return KtoVal; } + const std::map &right() const { return ValToK; } private: std::map KtoVal; @@ -100,8 +101,6 @@ clang::FunctionDecl *getDefinition(clang::FunctionDecl *FD); clang::CheckedPointerKind getCheckedPointerKind(clang::InteropTypeExpr *ItypeExpr); -bool hasFunctionBody(clang::Decl *D); - std::string getStorageQualifierString(clang::Decl *D); std::error_code tryGetCanonicalFilePath(const std::string &FileName, @@ -129,6 +128,11 @@ bool isPointerType(clang::ValueDecl *VD); // Is this a pointer or array type? bool isPtrOrArrayType(const clang::QualType &QT); +// Is this an array type? Note that this includes pointer types decayed from +// array types (i.e., arrays in function parameters) but does not include +// Checked C checked array pointers (unless they decayed from a checked array). +bool isArrayType(const clang::QualType &QT); + // Is this a type that can go inside an _Nt_array_ptr? bool isNullableType(const clang::QualType &QT); @@ -182,6 +186,8 @@ bool isInSysHeader(clang::Decl *D); std::string getSourceText(const clang::SourceRange &SR, const clang::ASTContext &C); +std::string getSourceText(const clang::CharSourceRange &SR, + const clang::ASTContext &C); // Find the longest common subsequence. unsigned longestCommonSubsequence(const char *Str1, const char *Str2, @@ -227,4 +233,63 @@ void getPrintfStringArgIndices(const clang::CallExpr *CE, int64_t getStmtIdWorkaround(const clang::Stmt *St, const clang::ASTContext &Context); +clang::SourceLocation getCheckedCAnnotationsEnd(const clang::Decl *D); + + +clang::SourceLocation +getLocationAfterToken(clang::SourceLocation SL, const clang::SourceManager &SM, + const clang::LangOptions &LO); + + +// Get the source range for a declaration including Checked C annotations. +// Optionally, any initializer can be excluded from the range in order to avoid +// interfering with other rewrites inside an existing initializer +// (https://github.com/correctcomputation/checkedc-clang/issues/267). If the +// declaration has no initializer, then IncludeInitializer has no effect. +clang::SourceRange getDeclSourceRangeWithAnnotations(const clang::Decl *D, + bool IncludeInitializer); + +// Shortcut for the getCustomDiagID + Report sequence to report a custom +// diagnostic as we currently do in 3C. +// +// Unlike DiagnosticEngine::Report, to make it harder to forget to provide a +// source location when we intend to, we don't provide a version that doesn't +// take a source location; instead, the caller should just pass +// SourceLocation(). + +template +inline clang::DiagnosticBuilder reportCustomDiagnostic( + clang::DiagnosticsEngine &DE, + clang::DiagnosticsEngine::Level Level, + const char (&FormatString)[N], + clang::SourceLocation Loc) { + return DE.Report(Loc, DE.getCustomDiagID(Level, FormatString)); +} + +// For whatever reason, Clang provides << equivalents for many other +// DiagnosticBuilder::Add* methods but not this one, and we want it in a few +// places. +inline const clang::DiagnosticBuilder &operator<<( + const clang::DiagnosticBuilder &DB, clang::NamedDecl *ND) { + DB.AddTaggedVal(reinterpret_cast(ND), + clang::DiagnosticsEngine::ArgumentKind::ak_nameddecl); + return DB; +} + +// Marker for conditions that we might want to make into non-fatal assertions +// once we have an API design for them +// (https://github.com/correctcomputation/checkedc-clang/issues/745). An inline +// function would work just as well, but macros have an LLVM naming convention +// and syntax highlighting that make call sites easier to read, in Matt's +// opinion. +#define NONFATAL_ASSERT_PLACEHOLDER(_cond) (_cond) + +// Variant for conditions that the caller doesn't actually test because no +// separate recovery path is currently implemented. We want to check that the +// condition compiles but not evaluate it at runtime (until non-fatal assertions +// are actually implemented, and then only when assertions are enabled in the +// build configuration), and we don't want "unused code" compiler warnings. +// TODO: Is there a better way to achieve this? +#define NONFATAL_ASSERT_PLACEHOLDER_UNUSED(_cond) ((void)sizeof(_cond)) + #endif diff --git a/clang/lib/3C/3C.cpp b/clang/lib/3C/3C.cpp index bb7948bc48d3..c86211471619 100644 --- a/clang/lib/3C/3C.cpp +++ b/clang/lib/3C/3C.cpp @@ -11,10 +11,12 @@ //===----------------------------------------------------------------------===// #include "clang/3C/3C.h" +#include "clang/3C/3CGlobalOptions.h" #include "clang/3C/ArrayBoundsInferenceConsumer.h" #include "clang/3C/ConstraintBuilder.h" #include "clang/3C/IntermediateToolHook.h" #include "clang/3C/RewriteUtils.h" +#include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/VerifyDiagnosticConsumer.h" #include "clang/Tooling/ArgumentsAdjusters.h" #include "llvm/Support/TargetSelect.h" @@ -30,40 +32,9 @@ using namespace llvm; #define BEFORE_SOLVING_SUFFIX "_before_solving_" #define AFTER_SUBTYPING_SUFFIX "_after_subtyping_" -cl::OptionCategory ArrBoundsInferCat("Array bounds inference options"); -static cl::opt - DebugArrSolver("debug-arr-solver", - cl::desc("Dump array bounds inference graph"), - cl::init(false), cl::cat(ArrBoundsInferCat)); - -bool DumpIntermediate; -bool Verbose; -std::string OutputPostfix; -std::string OutputDir; -std::string ConstraintOutputJson; -std::vector AllocatorFunctions; -bool DumpStats; -bool HandleVARARGS; -bool EnablePropThruIType; -bool ConsiderAllocUnsafe; -std::string StatsOutputJson; -std::string WildPtrInfoJson; -std::string PerWildPtrInfoJson; -bool AllTypes; -std::string BaseDir; -bool AddCheckedRegions; -bool EnableCCTypeChecker; -bool WarnRootCause; -bool WarnAllRootCause; std::set FilePaths; -bool DumpUnwritableChanges; -bool AllowUnwritableChanges; -bool AllowRewriteFailures; -#ifdef FIVE_C -bool RemoveItypes; -bool ForceItypes; -#endif +struct _3COptions _3COpts; static CompilationDatabase *CurrCompDB = nullptr; static tooling::CommandLineArguments SourceFiles; @@ -159,7 +130,7 @@ class _3CDiagnosticConsumer : public DiagnosticConsumer { return UnderlyingConsumer->getNumErrors() == 0; } - ~_3CDiagnosticConsumer() { + ~_3CDiagnosticConsumer() override { // We considered asserting that the state is S_Done here, but if // ASTUnit::LoadFromCompilerInvocation fails and returns null, the // _3CDiagnosticConsumer may be destructed without reaching S_Done. However, @@ -193,7 +164,7 @@ class _3CASTBuilderAction : public ToolAction { // a LibTooling ArgumentsAdjuster, but we access the options in their parsed // data structure rather than as strings, so it is much more robust. - if (!EnableCCTypeChecker) + if (!_3COpts.EnableCCTypeChecker) // Corresponds to the -f3c-tool compiler option. Invocation->LangOpts->_3C = true; @@ -275,15 +246,63 @@ class _3CASTBuilderAction : public ToolAction { if (!AST) return false; + handleExtraProgramAction(Invocation->getFrontendOpts(), + AST->getASTContext()); + ASTs.push_back(std::move(AST)); return true; } + +private: + void handleExtraProgramAction(FrontendOptions &Opts, + ASTContext &C) { + // The Opts.ProgramAction field is normally used only by `clang -cc1` to + // select a FrontendAction (see CreateFrontendBaseAction in + // ExecuteCompilerInvocation.cpp) and is ignored by LibTooling tools, which + // perform a custom FrontendAction. But we want to support at least AST + // dumping (as an addition to 3C's normal workflow) since it's useful for + // debugging 3C, and we prefer to honor the standard `-Xclang -ast-dump` + // option rather than define our own tool-level option like clang-check + // does. We could add support for more `-ast-*` options here if desired. + switch (Opts.ProgramAction) { + case frontend::ParseSyntaxOnly: + // Nothing extra to do. + break; + case frontend::ASTDump: { + // Code copied from ASTDumpAction::CreateASTConsumer since we don't have a + // good way to actually use ASTDumpAction from here. :/ + // + // XXX: Maybe we'd prefer to output this somewhere other than stdout to + // separate it from the updated main file written to stdout? This doesn't + // look trivial because ASTPrinter requires ownership of the output + // stream, and it probably isn't important for the intended debugging use + // case. + std::unique_ptr Dumper = + CreateASTDumper(nullptr /*Dump to stdout.*/, Opts.ASTDumpFilter, + Opts.ASTDumpDecls, Opts.ASTDumpAll, + Opts.ASTDumpLookups, Opts.ASTDumpDeclTypes, + Opts.ASTDumpFormat); + // In principle, we should call all the ASTConsumer methods the same way + // the normal AST parsing process would, but there isn't an obvious way to + // do that when using ASTUnit. Instead, we rely on the assumption + // (apparently valid as of this writing) that the only ASTConsumer method + // that has a nonempty implementation in ASTPrinter is + // HandleTranslationUnit, and we just call HandleTranslationUnit manually. + Dumper->HandleTranslationUnit(C); + break; + } + default: + llvm::errs() << "Warning: The requested ProgramAction is not implemented " + "by 3C and will be ignored.\n"; + break; + } + } }; void dumpConstraintOutputJson(const std::string &PostfixStr, ProgramInfo &Info) { - if (DumpIntermediate) { - std::string FilePath = ConstraintOutputJson + PostfixStr + ".json"; + if (_3COpts.DumpIntermediate) { + std::string FilePath = _3COpts.ConstraintOutputJson + PostfixStr + ".json"; errs() << "Writing json output to:" << FilePath << "\n"; std::error_code Ec; llvm::raw_fd_ostream OutputJson(FilePath, Ec); @@ -299,7 +318,7 @@ void dumpConstraintOutputJson(const std::string &PostfixStr, void runSolver(ProgramInfo &Info, std::set &SourceFiles) { Constraints &CS = Info.getConstraints(); - if (Verbose) { + if (_3COpts.Verbose) { errs() << "Trying to capture Constraint Variables for all functions\n"; } @@ -310,7 +329,7 @@ void runSolver(ProgramInfo &Info, std::set &SourceFiles) { clock_t StartTime = clock(); CS.solve(); - if (Verbose) { + if (_3COpts.Verbose) { errs() << "Solver time:" << getTimeSpentInSeconds(StartTime) << "\n"; } } @@ -333,32 +352,8 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt, const std::vector &SourceFileList, CompilationDatabase *CompDB) { - DumpIntermediate = CCopt.DumpIntermediate; - Verbose = CCopt.Verbose; - OutputPostfix = CCopt.OutputPostfix; - OutputDir = CCopt.OutputDir; - ConstraintOutputJson = CCopt.ConstraintOutputJson; - StatsOutputJson = CCopt.StatsOutputJson; - WildPtrInfoJson = CCopt.WildPtrInfoJson; - PerWildPtrInfoJson = CCopt.PerPtrInfoJson; - DumpStats = CCopt.DumpStats; - HandleVARARGS = CCopt.HandleVARARGS; - EnablePropThruIType = CCopt.EnablePropThruIType; - BaseDir = CCopt.BaseDir; - AllTypes = CCopt.EnableAllTypes; - AddCheckedRegions = CCopt.AddCheckedRegions; - EnableCCTypeChecker = CCopt.EnableCCTypeChecker; - AllocatorFunctions = CCopt.AllocatorFunctions; - WarnRootCause = CCopt.WarnRootCause || CCopt.WarnAllRootCause; - WarnAllRootCause = CCopt.WarnAllRootCause; - DumpUnwritableChanges = CCopt.DumpUnwritableChanges; - AllowUnwritableChanges = CCopt.AllowUnwritableChanges; - AllowRewriteFailures = CCopt.AllowRewriteFailures; - -#ifdef FIVE_C - RemoveItypes = CCopt.RemoveItypes; - ForceItypes = CCopt.ForceItypes; -#endif + _3COpts = CCopt; + _3COpts.WarnRootCause |= _3COpts.WarnAllRootCause; llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); @@ -367,13 +362,14 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt, ConstraintsBuilt = false; - if (OutputPostfix != "-" && !OutputDir.empty()) { + if (_3COpts.OutputPostfix != "-" && !_3COpts.OutputDir.empty()) { errs() << "3C initialization error: Cannot use both -output-postfix and " "-output-dir\n"; ConstructionFailed = true; return; } - if (OutputPostfix == "-" && OutputDir.empty() && SourceFileList.size() > 1) { + if (_3COpts.OutputPostfix == "-" && _3COpts.OutputDir.empty() && + SourceFileList.size() > 1) { errs() << "3C initialization error: Cannot specify more than one input " "file when output is to stdout\n"; ConstructionFailed = true; @@ -383,40 +379,41 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt, std::string TmpPath; std::error_code EC; - if (BaseDir.empty()) { - BaseDir = "."; + if (_3COpts.BaseDir.empty()) { + _3COpts.BaseDir = "."; } // Get the canonical path of the base directory. - TmpPath = BaseDir; - EC = tryGetCanonicalFilePath(BaseDir, TmpPath); + TmpPath = _3COpts.BaseDir; + EC = tryGetCanonicalFilePath(_3COpts.BaseDir, TmpPath); if (EC) { errs() << "3C initialization error: Failed to canonicalize base directory " - << "\"" << BaseDir << "\": " << EC.message() << "\n"; + << "\"" << _3COpts.BaseDir << "\": " << EC.message() << "\n"; ConstructionFailed = true; return; } - BaseDir = TmpPath; + _3COpts.BaseDir = TmpPath; - if (!OutputDir.empty()) { + if (!_3COpts.OutputDir.empty()) { // tryGetCanonicalFilePath will fail if the output dir doesn't exist yet, so // create it first. - EC = llvm::sys::fs::create_directories(OutputDir); + EC = llvm::sys::fs::create_directories(_3COpts.OutputDir); if (EC) { errs() << "3C initialization error: Failed to create output directory \"" - << OutputDir << "\": " << EC.message() << "\n"; + << _3COpts.OutputDir << "\": " << EC.message() << "\n"; ConstructionFailed = true; return; } - TmpPath = OutputDir; - EC = tryGetCanonicalFilePath(OutputDir, TmpPath); + TmpPath = _3COpts.OutputDir; + EC = tryGetCanonicalFilePath(_3COpts.OutputDir, TmpPath); if (EC) { errs() << "3C initialization error: Failed to canonicalize output " - << "directory \"" << OutputDir << "\": " << EC.message() << "\n"; + << "directory \"" << _3COpts.OutputDir << "\": " << EC.message() + << "\n"; ConstructionFailed = true; return; } - OutputDir = TmpPath; + _3COpts.OutputDir = TmpPath; } SourceFiles = SourceFileList; @@ -432,25 +429,26 @@ _3CInterface::_3CInterface(const struct _3COptions &CCopt, continue; } FilePaths.insert(AbsPath); - if (!filePathStartsWith(AbsPath, BaseDir)) { + if (!filePathStartsWith(AbsPath, _3COpts.BaseDir)) { errs() << "3C initialization " - << (OutputDir != "" || !CCopt.AllowSourcesOutsideBaseDir ? "error" - : "warning") + << (_3COpts.OutputDir != "" || !_3COpts.AllowSourcesOutsideBaseDir + ? "error" + : "warning") << ": File \"" << AbsPath << "\" specified on the command line is outside the base directory\n"; SawInputOutsideBaseDir = true; } } if (SawInputOutsideBaseDir) { - errs() << "The base directory is currently \"" << BaseDir + errs() << "The base directory is currently \"" << _3COpts.BaseDir << "\" and can be changed with the -base-dir option.\n"; - if (OutputDir != "") { + if (_3COpts.OutputDir != "") { ConstructionFailed = true; errs() << "When using -output-dir, input files outside the base " "directory cannot be handled because there is no way to " "compute their output paths.\n"; - } else if (!CCopt.AllowSourcesOutsideBaseDir) { + } else if (!_3COpts.AllowSourcesOutsideBaseDir) { ConstructionFailed = true; errs() << "You can use the -allow-sources-outside-base-dir option to " "temporarily downgrade this error to a warning.\n"; @@ -509,6 +507,8 @@ bool _3CInterface::parseASTs() { int ToolExitStatus = Tool->run(&Action); HadNonDiagnosticError |= (ToolExitStatus != 0); + GlobalProgramInfo.registerTranslationUnits(ASTs); + return isSuccessfulSoFar(); } @@ -516,6 +516,18 @@ bool _3CInterface::addVariables() { std::lock_guard Lock(InterfaceMutex); + // Find multi-decls and assign names to unnamed inline TagDecls now so that + // the assigned type names are available when we construct ConstraintVariables + // for the multi-decl members in the "Add Variables" step below. + for (auto &TU : ASTs) + GlobalProgramInfo.TheMultiDeclsInfo.findUsedTagNames(TU->getASTContext()); + if (!isSuccessfulSoFar()) + return false; + for (auto &TU : ASTs) + GlobalProgramInfo.TheMultiDeclsInfo.findMultiDecls(TU->getASTContext()); + if (!isSuccessfulSoFar()) + return false; + // 1. Add Variables. VariableAdderConsumer VA = VariableAdderConsumer(GlobalProgramInfo, nullptr); for (auto &TU : ASTs) @@ -552,10 +564,10 @@ bool _3CInterface::solveConstraints() { assert(ConstraintsBuilt && "Constraints not yet built. We need to call " "build constraint before trying to solve them."); // 3. Solve constraints. - if (Verbose) + if (_3COpts.Verbose) errs() << "Solving constraints\n"; - if (DumpIntermediate) + if (_3COpts.DumpIntermediate) GlobalProgramInfo.dump(); auto &PStats = GlobalProgramInfo.getPerfStats(); @@ -564,20 +576,31 @@ bool _3CInterface::solveConstraints() { runSolver(GlobalProgramInfo, FilePaths); PStats.endConstraintSolverTime(); - if (Verbose) + if (_3COpts.Verbose) errs() << "Constraints solved\n"; - if (WarnRootCause) + if (_3COpts.WarnRootCause) GlobalProgramInfo.computeInterimConstraintState(FilePaths); - if (DumpIntermediate) + if (_3COpts.DumpIntermediate) dumpConstraintOutputJson(FINAL_OUTPUT_SUFFIX, GlobalProgramInfo); - if (AllTypes) { - if (DebugArrSolver) + if (_3COpts.AllTypes) { + // Add declared bounds for all constant sized arrays. This needs to happen + // after constraint solving because the bound added depends on whether the + // array is NTARR or ARR. + GlobalProgramInfo.getABoundsInfo().addConstantArrayBounds( + GlobalProgramInfo); + + if (_3COpts.DebugArrSolver) GlobalProgramInfo.getABoundsInfo().dumpAVarGraph( "arr_bounds_initial.dot"); + // Infer lower bounds for pointers that are not valid lower bounds. + // The result of this inference is required for length inference, so + // this call must be before the subsequent call to performFlowAnalysis. + GlobalProgramInfo.getABoundsInfo().inferLowerBounds(&GlobalProgramInfo); + // Propagate initial data-flow information for Array pointers from // bounds declarations. GlobalProgramInfo.getABoundsInfo().performFlowAnalysis(&GlobalProgramInfo); @@ -602,7 +625,7 @@ bool _3CInterface::solveConstraints() { if (!isSuccessfulSoFar()) return false; - if (AllTypes) { + if (_3COpts.AllTypes) { // Propagate data-flow information for Array pointers. GlobalProgramInfo.getABoundsInfo().performFlowAnalysis(&GlobalProgramInfo); @@ -658,33 +681,33 @@ bool _3CInterface::writeAllConvertedFilesToDisk() { } bool _3CInterface::dumpStats() { - if (AllTypes && DebugArrSolver) { + if (_3COpts.AllTypes && _3COpts.DebugArrSolver) { GlobalProgramInfo.getABoundsInfo().dumpAVarGraph("arr_bounds_final.dot"); } - if (DumpStats) { + if (_3COpts.DumpStats) { GlobalProgramInfo.printStats(FilePaths, llvm::errs(), true); GlobalProgramInfo.computeInterimConstraintState(FilePaths); std::error_code Ec; - llvm::raw_fd_ostream OutputJson(StatsOutputJson, Ec); + llvm::raw_fd_ostream OutputJson(_3COpts.StatsOutputJson, Ec); if (!OutputJson.has_error()) { GlobalProgramInfo.printStats(FilePaths, OutputJson, false, true); OutputJson.close(); } - std::string AggregateStats = StatsOutputJson + ".aggregate.json"; + std::string AggregateStats = _3COpts.StatsOutputJson + ".aggregate.json"; llvm::raw_fd_ostream AggrJson(AggregateStats, Ec); if (!AggrJson.has_error()) { GlobalProgramInfo.printAggregateStats(FilePaths, AggrJson); AggrJson.close(); } - llvm::raw_fd_ostream WildPtrInfo(WildPtrInfoJson, Ec); + llvm::raw_fd_ostream WildPtrInfo(_3COpts.WildPtrInfoJson, Ec); if (!WildPtrInfo.has_error()) { GlobalProgramInfo.getInterimConstraintState().printStats(WildPtrInfo); WildPtrInfo.close(); } - llvm::raw_fd_ostream PerWildPtrInfo(PerWildPtrInfoJson, Ec); + llvm::raw_fd_ostream PerWildPtrInfo(_3COpts.PerWildPtrInfoJson, Ec); if (!PerWildPtrInfo.has_error()) { GlobalProgramInfo.getInterimConstraintState().printRootCauseStats( PerWildPtrInfo, GlobalProgramInfo.getConstraints()); @@ -698,50 +721,10 @@ ConstraintsInfo &_3CInterface::getWildPtrsInfo() { return GlobalProgramInfo.getInterimConstraintState(); } -bool _3CInterface::makeSinglePtrNonWild(ConstraintKey TargetPtr) { - std::lock_guard Lock(InterfaceMutex); - CVars RemovePtrs; - RemovePtrs.clear(); - - auto &PtrDisjointSet = GlobalProgramInfo.getInterimConstraintState(); - auto &CS = GlobalProgramInfo.getConstraints(); - - // Get all the current WILD pointers. - CVars OldWildPtrs = PtrDisjointSet.AllWildAtoms; - - // Delete the constraint that make the provided targetPtr WILD. - VarAtom *VA = CS.getOrCreateVar(TargetPtr, "q", VarAtom::V_Other); - Geq NewE(VA, CS.getWild()); - Constraint *OriginalConstraint = *CS.getConstraints().find(&NewE); - CS.removeConstraint(OriginalConstraint); - VA->getAllConstraints().erase(OriginalConstraint); - delete (OriginalConstraint); - - // Reset the constraint system. - CS.resetEnvironment(); - - // Solve the constraints. - //assert (CS == GlobalProgramInfo.getConstraints()); - runSolver(GlobalProgramInfo, FilePaths); - - // Compute new disjoint set. - GlobalProgramInfo.computeInterimConstraintState(FilePaths); - - // Get new WILD pointers. - CVars &NewWildPtrs = PtrDisjointSet.AllWildAtoms; - - // Get the number of pointers that have now converted to non-WILD. - std::set_difference(OldWildPtrs.begin(), OldWildPtrs.end(), - NewWildPtrs.begin(), NewWildPtrs.end(), - std::inserter(RemovePtrs, RemovePtrs.begin())); - - return !RemovePtrs.empty(); -} - void _3CInterface::invalidateAllConstraintsWithReason( Constraint *ConstraintToRemove) { // Get the reason for the current constraint. - std::string ConstraintRsn = ConstraintToRemove->getReason(); + std::string ConstraintRsn = ConstraintToRemove->getReasonText(); Constraints::ConstraintSet ToRemoveConstraints; Constraints &CS = GlobalProgramInfo.getConstraints(); // Remove all constraints that have the reason. @@ -758,39 +741,3 @@ void _3CInterface::invalidateAllConstraintsWithReason( delete (ToDelCons); } } - -bool _3CInterface::invalidateWildReasonGlobally(ConstraintKey PtrKey) { - std::lock_guard Lock(InterfaceMutex); - - CVars RemovePtrs; - RemovePtrs.clear(); - - auto &PtrDisjointSet = GlobalProgramInfo.getInterimConstraintState(); - auto &CS = GlobalProgramInfo.getConstraints(); - - CVars OldWildPtrs = PtrDisjointSet.AllWildAtoms; - - // Delete ALL the constraints that have the same given reason. - VarAtom *VA = CS.getOrCreateVar(PtrKey, "q", VarAtom::V_Other); - Geq NewE(VA, CS.getWild()); - Constraint *OriginalConstraint = *CS.getConstraints().find(&NewE); - invalidateAllConstraintsWithReason(OriginalConstraint); - - // Reset constraint solver. - CS.resetEnvironment(); - - // Solve the constraints. - runSolver(GlobalProgramInfo, FilePaths); - - // Recompute the WILD pointer disjoint sets. - GlobalProgramInfo.computeInterimConstraintState(FilePaths); - - // Computed the number of removed pointers. - CVars &NewWildPtrs = PtrDisjointSet.AllWildAtoms; - - std::set_difference(OldWildPtrs.begin(), OldWildPtrs.end(), - NewWildPtrs.begin(), NewWildPtrs.end(), - std::inserter(RemovePtrs, RemovePtrs.begin())); - - return !RemovePtrs.empty(); -} diff --git a/clang/lib/3C/3CInteractiveData.cpp b/clang/lib/3C/3CInteractiveData.cpp index bc6d417f513a..3a9f039f645f 100644 --- a/clang/lib/3C/3CInteractiveData.cpp +++ b/clang/lib/3C/3CInteractiveData.cpp @@ -66,7 +66,7 @@ void ConstraintsInfo::printStats(llvm::raw_ostream &O) { std::map> RsnBasedWildCKeys; for (auto &PtrR : RootWildAtomsWithReason) { if (AllWildAtoms.find(PtrR.first) != AllWildAtoms.end()) { - RsnBasedWildCKeys[PtrR.second.getWildPtrReason()].insert(PtrR.first); + RsnBasedWildCKeys[PtrR.second.getReason()].insert(PtrR.first); } } bool AddComma = false; @@ -111,8 +111,8 @@ void ConstraintsInfo::printConstraintStats(llvm::raw_ostream &O, ConstraintKey Cause) { O << "{\"ConstraintKey\":" << Cause << ", "; O << "\"Name\":\"" << CS.getVar(Cause)->getStr() << "\", "; - WildPointerInferenceInfo PtrInfo = RootWildAtomsWithReason.at(Cause); - O << "\"Reason\":\"" << PtrInfo.getWildPtrReason() << "\", "; + RootCauseDiagnostic PtrInfo = RootWildAtomsWithReason.at(Cause); + O << "\"Reason\":\"" << PtrInfo.getReason() << "\", "; O << "\"InSrc\":" << (InSrcWildAtoms.find(Cause) != InSrcWildAtoms.end()) << ", "; O << "\"Location\":"; @@ -129,8 +129,24 @@ void ConstraintsInfo::printConstraintStats(llvm::raw_ostream &O, std::set PtrsAffected = PtrSrcWMap[Cause]; O << "\"PtrsAffected\":" << PtrsAffected.size() << ","; - O << "\"PtrsScore\":" << getPtrAffectedScore(PtrsAffected); - O << "}"; + O << "\"PtrsScore\":" << getPtrAffectedScore(PtrsAffected) << ","; + + O << "\"SubReasons\":" << "["; + bool AddComma = false; + for(const ReasonLoc &Rsn : PtrInfo.additionalNotes()) { + if (AddComma) O << ","; + O << "{"; + O << "\"Rsn\":\"" << Rsn.Reason << "\", "; + O << "\"Location\":"; + const PersistentSourceLoc &RPSL = Rsn.Location; + if (RPSL.valid()) + O << llvm::json::Value(RPSL.toString()); + else + O << "null"; + AddComma = true; + O << "}"; + } + O << "]}"; } int ConstraintsInfo::getNumPtrsAffected(ConstraintKey CK) { diff --git a/clang/lib/3C/ABounds.cpp b/clang/lib/3C/ABounds.cpp index 87198d59a215..b2fc9f9c5609 100644 --- a/clang/lib/3C/ABounds.cpp +++ b/clang/lib/3C/ABounds.cpp @@ -13,14 +13,6 @@ #include "clang/3C/ABounds.h" #include "clang/3C/AVarBoundsInfo.h" -std::set ABounds::KeysUsedInBounds; - -void ABounds::addBoundsUsedKey(BoundsKey BK) { KeysUsedInBounds.insert(BK); } - -bool ABounds::isKeyUsedInBounds(BoundsKey ToCheck) { - return KeysUsedInBounds.find(ToCheck) != KeysUsedInBounds.end(); -} - ABounds *ABounds::getBoundsInfo(AVarBoundsInfo *ABInfo, BoundsExpr *BExpr, const ASTContext &C) { ABounds *Ret = nullptr; @@ -30,7 +22,7 @@ ABounds *ABounds::getBoundsInfo(AVarBoundsInfo *ABInfo, BoundsExpr *BExpr, if (ABInfo->tryGetVariable(CBE->getCountExpr()->IgnoreParenCasts(), C, VK)) { ProgramVar *PV = ABInfo->getProgramVar(VK); - if (PV->isNumConstant() && PV->getVarName() == "0") { + if (PV->isNumConstant() && PV->getConstantVal() == 0) { // Invalid bounds. This is for functions like free. // Where the bounds is 0. Ret = nullptr; @@ -41,18 +33,6 @@ ABounds *ABounds::getBoundsInfo(AVarBoundsInfo *ABInfo, BoundsExpr *BExpr, } } } - // Disabling Range bounds as they are not currently supported! - /* - RangeBoundsExpr *RBE = dyn_cast(BExpr->IgnoreParenCasts()); - if (BExpr->isRange() && RBE) { - Expr *LHS = RBE->getLowerExpr()->IgnoreParenCasts(); - Expr *RHS = RBE->getUpperExpr()->IgnoreParenImpCasts(); - BoundsKey LV, RV; - if (ABInfo->tryGetVariable(LHS, C, LV) && - ABInfo->tryGetVariable(RHS, C, RV)) { - Ret = new RangeBound(LV, RV); - } - }*/ return Ret; } @@ -60,7 +40,7 @@ std::string ABounds::getBoundsKeyStr(BoundsKey BK, AVarBoundsInfo *ABI, Decl *D) { ProgramVar *PV = ABI->getProgramVar(BK); assert(PV != nullptr && "No Valid program var"); - std::string BKStr = PV->mkString(); + std::string BKStr = PV->getVarName(); unsigned PIdx = 0; auto *PVD = dyn_cast_or_null(D); // Does this belong to a function parameter? @@ -74,62 +54,81 @@ std::string ABounds::getBoundsKeyStr(BoundsKey BK, AVarBoundsInfo *ABI, // If the parameter in the new declaration does not have a name? // then use the old name. if (BKStr.empty()) - BKStr = PV->mkString(); + BKStr = PV->getVarName(); } } return BKStr; } -std::string CountBound::mkString(AVarBoundsInfo *ABI, clang::Decl *D) { - return "count(" + ABounds::getBoundsKeyStr(CountVar, ABI, D) + ")"; +std::string +ABounds::mkString(AVarBoundsInfo *ABI, clang::Decl *D, BoundsKey BK) { + if (LowerBoundKey != 0 && LowerBoundKey != BK) + return mkStringWithLowerBound(ABI, D); + return mkStringWithoutLowerBound(ABI, D); +} + +std::string +CountBound::mkStringWithLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) { + std::string LowerBound = ABounds::getBoundsKeyStr(LowerBoundKey, ABI, D); + // Assume that LowerBound is the same pointer type as this pointer this bound + // acts on, so pointer arithmetic works as expected. + return "bounds(" + LowerBound + ", " + LowerBound + " + " + + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + ")"; +} + +std::string +CountBound::mkStringWithoutLowerBound(AVarBoundsInfo *ABI, clang::Decl *D) { + return "count(" + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + ")"; } bool CountBound::areSame(ABounds *O, AVarBoundsInfo *ABI) { if (O != nullptr) { if (CountBound *OT = dyn_cast(O)) - return ABI->areSameProgramVar(this->CountVar, OT->CountVar); + return ABI->areSameProgramVar(this->LengthKey, OT->LengthKey); } return false; } -BoundsKey CountBound::getBKey() { return this->CountVar; } - ABounds *CountBound::makeCopy(BoundsKey NK) { return new CountBound(NK); } -std::string CountPlusOneBound::mkString(AVarBoundsInfo *ABI, clang::Decl *D) { - std::string CVar = ABounds::getBoundsKeyStr(CountVar, ABI, D); - return "count(" + CVar + " + 1)"; +std::string CountPlusOneBound::mkStringWithLowerBound(AVarBoundsInfo *ABI, + clang::Decl *D) { + + std::string LowerBound = ABounds::getBoundsKeyStr(LowerBoundKey, ABI, D); + return "bounds(" + LowerBound + ", " + LowerBound + " + " + + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + " + 1)"; +} + +std::string CountPlusOneBound::mkStringWithoutLowerBound(AVarBoundsInfo *ABI, + clang::Decl *D) { + return "count(" + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + " + 1)"; } bool CountPlusOneBound::areSame(ABounds *O, AVarBoundsInfo *ABI) { if (CountPlusOneBound *OT = dyn_cast_or_null(O)) - return ABI->areSameProgramVar(this->CountVar, OT->CountVar); + return ABI->areSameProgramVar(this->LengthKey, OT->LengthKey); return false; } -std::string ByteBound::mkString(AVarBoundsInfo *ABI, clang::Decl *D) { - return "byte_count(" + ABounds::getBoundsKeyStr(ByteVar, ABI, D) + ")"; +std::string ByteBound::mkStringWithLowerBound(AVarBoundsInfo *ABI, + clang::Decl *D) { + std::string LowerBound = ABounds::getBoundsKeyStr(LowerBoundKey, ABI, D); + // LowerBound will be a pointer to some type that is not necessarily char, so + // pointer arithmetic will not give the behavior needed for a byte count. + // First cast the pointer to a char pointer, and then add byte count. + return "bounds(((_Array_ptr)" + LowerBound + "), ((_Array_ptr)" + + LowerBound + ") + " + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + ")"; +} + +std::string ByteBound::mkStringWithoutLowerBound(AVarBoundsInfo *ABI, + clang::Decl *D) { + return "byte_count(" + ABounds::getBoundsKeyStr(LengthKey, ABI, D) + ")"; } bool ByteBound::areSame(ABounds *O, AVarBoundsInfo *ABI) { if (ByteBound *BB = dyn_cast_or_null(O)) - return ABI->areSameProgramVar(this->ByteVar, BB->ByteVar); + return ABI->areSameProgramVar(this->LengthKey, BB->LengthKey); return false; } -BoundsKey ByteBound::getBKey() { return this->ByteVar; } - ABounds *ByteBound::makeCopy(BoundsKey NK) { return new ByteBound(NK); } - -std::string RangeBound::mkString(AVarBoundsInfo *ABI, clang::Decl *D) { - std::string LBStr = ABounds::getBoundsKeyStr(LB, ABI, D); - std::string UBStr = ABounds::getBoundsKeyStr(UB, ABI, D); - return "bounds(" + LBStr + ", " + UBStr + ")"; -} - -bool RangeBound::areSame(ABounds *O, AVarBoundsInfo *ABI) { - if (RangeBound *RB = dyn_cast_or_null(O)) - return ABI->areSameProgramVar(this->LB, RB->LB) && - ABI->areSameProgramVar(this->UB, RB->UB); - return false; -} diff --git a/clang/lib/3C/AVarBoundsInfo.cpp b/clang/lib/3C/AVarBoundsInfo.cpp index 0f8d0b6df5cf..ebe09bd264cd 100644 --- a/clang/lib/3C/AVarBoundsInfo.cpp +++ b/clang/lib/3C/AVarBoundsInfo.cpp @@ -13,18 +13,13 @@ #include "clang/3C/AVarBoundsInfo.h" #include "clang/3C/ConstraintResolver.h" #include "clang/3C/ProgramInfo.h" +#include "clang/3C/3CGlobalOptions.h" #include +#include std::vector AVarBoundsInfo::PrioList{Declared, Allocator, FlowInferred, Heuristics}; -extern cl::OptionCategory ArrBoundsInferCat; -static cl::opt DisableInfDecls("disable-arr-missd", - cl::desc("Disable ignoring of missed " - "bounds from declarations."), - cl::init(false), - cl::cat(ArrBoundsInferCat)); - void AVarBoundsStats::print(llvm::raw_ostream &O, const std::set *InSrcArrs, bool JsonFormat) const { @@ -65,9 +60,9 @@ void AVarBoundsStats::print(llvm::raw_ostream &O, } } -bool hasArray(ConstraintVariable *CK, Constraints &CS) { +bool hasArray(const ConstraintVariable *CK, const Constraints &CS) { auto &E = CS.getVariables(); - if (PVConstraint *PV = dyn_cast(CK)) { + if (const auto *PV = dyn_cast(CK)) { if (PV->hasArr(E, 0) || PV->hasNtArr(E, 0)) { return true; } @@ -75,9 +70,9 @@ bool hasArray(ConstraintVariable *CK, Constraints &CS) { return false; } -bool hasOnlyNtArray(ConstraintVariable *CK, Constraints &CS) { +bool hasOnlyNtArray(const ConstraintVariable *CK, const Constraints &CS) { auto &E = CS.getVariables(); - if (PVConstraint *PV = dyn_cast(CK)) { + if (const auto *PV = dyn_cast(CK)) { if (PV->hasNtArr(E, 0)) { return true; } @@ -85,9 +80,9 @@ bool hasOnlyNtArray(ConstraintVariable *CK, Constraints &CS) { return false; } -bool isInSrcArray(ConstraintVariable *CK, Constraints &CS) { +bool isInSrcArray(const ConstraintVariable *CK, const Constraints &CS) { auto &E = CS.getVariables(); - if (PVConstraint *PV = dyn_cast(CK)) { + if (const auto *PV = dyn_cast(CK)) { if ((PV->hasArr(E, 0) || PV->hasNtArr(E, 0)) && PV->isForValidDecl()) { return true; } @@ -98,58 +93,62 @@ bool isInSrcArray(ConstraintVariable *CK, Constraints &CS) { // This class picks variables that are in the same scope as the provided scope. class ScopeVisitor { public: - ScopeVisitor(const ProgramVarScope *S, std::set &R, - std::set &VK, std::map &VarM, - std::set &P) - : TS(S), Res(R), VisibleKeys(VK), VM(VarM), PtrAtoms(P) {} - void visitBoundsKey(BoundsKey V) const { - // If the variable is non-pointer? - if (VM.find(V) != VM.end() && PtrAtoms.find(V) == PtrAtoms.end()) { - auto *S = VM[V]; + ScopeVisitor(const ProgramVarScope *S, AVarBoundsInfo *BI) + : Scope(S), InScopeKeys(), VisibleKeys(), BI(BI) {} + void visitBoundsKey(BoundsKey V) { + if (ProgramVar *S = BI->getProgramVar(V)) { // If the variable is constant or in the same scope? - if (S->isNumConstant() || (*(TS) == *(S->getScope()))) { - Res.insert(V); + if (S->isNumConstant() || (*Scope == *(S->getScope()))) { + InScopeKeys.insert(V); VisibleKeys.insert(V); - } else if (TS->isInInnerScope(*(S->getScope()))) { + } else if (Scope->isInInnerScope(*(S->getScope()))) { VisibleKeys.insert(V); } } } - const ProgramVarScope *TS; - std::set &Res; - std::set &VisibleKeys; - std::map &VM; - std::set &PtrAtoms; + const std::set &getInScopeKeys() const { return InScopeKeys; } + + const std::set &getVisibleKeys() const { return VisibleKeys; } + +private: + const ProgramVarScope *Scope; + + // Contains high priority bounds keys. These are either directly in the scope + // for this visitor, or they are constant bounds keys. + std::set InScopeKeys; + + // This set contains all keys in InScopeKeys, but also contains non-constant + // bounds keys from scopes where this scope is an inner scope. + std::set VisibleKeys; + + const AVarBoundsInfo *BI; }; void AvarBoundsInference::mergeReachableProgramVars( BoundsKey TarBK, std::set &AllVars) { if (AllVars.size() > 1) { - ProgramVar *BVar = nullptr; bool IsTarNTArr = BI->NtArrPointerBoundsKey.find(TarBK) != BI->NtArrPointerBoundsKey.end(); // First, find all variables that are in the SAME scope as TarBK. // If there is only one? Then use it. - std::set SameScopeVars; - ProgramVar *TarBVar = BI->getProgramVar(TarBK); - if (TarBVar != nullptr) { - for (auto TB : AllVars) { - if (*(BI->getProgramVar(TB)->getScope()) == *(TarBVar->getScope())) { + if (ProgramVar *TarBVar = BI->getProgramVar(TarBK)) { + std::set SameScopeVars; + for (auto TB : AllVars) + if (*(BI->getProgramVar(TB)->getScope()) == *(TarBVar->getScope())) SameScopeVars.insert(TB); - } - } - // There is only one same scope variable. - // Consider only that. + + // There is only one same scope variable. Consider only that. if (SameScopeVars.size() == 1) { - AllVars.clear(); - AllVars.insert(*SameScopeVars.begin()); + AllVars = SameScopeVars; return; } } + // We want to merge all bounds vars. We give preference to // non-constants if there are multiple non-constant variables, // we give up. + ProgramVar *BVar = nullptr; for (auto TmpBKey : AllVars) { // Convert the bounds key to corresponding program var. auto *TmpB = BI->getProgramVar(TmpBKey); @@ -163,8 +162,8 @@ void AvarBoundsInference::mergeReachableProgramVars( BVar = TmpB; } else { // If we need to merge two constants? - int CVal = std::stoi(BVar->getVarName()); - int TmpVal = std::stoi(TmpB->getVarName()); + uint64_t CVal = BVar->getConstantVal(); + uint64_t TmpVal = TmpB->getConstantVal(); if (IsTarNTArr) { // If this is an NTarr then the values should be same. if (TmpVal != CVal) { @@ -204,45 +203,68 @@ void AvarBoundsInference::mergeReachableProgramVars( } } -// Consider all pointers, each of which may have multiple bounds, -// and intersect these. If they all converge to one possibility, -// use that. If not, give up (no bounds). -bool AvarBoundsInference::convergeInferredBounds() { - bool FoundSome = false; +// Consider all pointers, each of which may have multiple bounds, and intersect +// these. If they all converge to one possibility, use that. If not, give up and +// don't assign any bounds to the pointer. +void AvarBoundsInference::convergeInferredBounds() { for (auto &CInfABnds : CurrIterInferBounds) { - auto *AB = BI->getBounds(CInfABnds.first); + BoundsKey PtrBoundsKey = CInfABnds.first; // If there are no bounds? - if (AB == nullptr) { - auto BTypeMap = CInfABnds.second; - for (auto &TySet : BTypeMap) { - mergeReachableProgramVars(CInfABnds.first, TySet.second); - } - // Order of preference: Count and Byte - if (BTypeMap.find(ABounds::CountBoundKind) != BTypeMap.end() && - !BTypeMap[ABounds::CountBoundKind].empty()) { - AB = new CountBound(*BTypeMap[ABounds::CountBoundKind].begin()); - } else if (BTypeMap.find(ABounds::ByteBoundKind) != BTypeMap.end() && - !BTypeMap[ABounds::ByteBoundKind].empty()) { - AB = new ByteBound(*BTypeMap[ABounds::ByteBoundKind].begin()); - } else if (BTypeMap.find(ABounds::CountPlusOneBoundKind) != - BTypeMap.end() && - !BTypeMap[ABounds::CountPlusOneBoundKind].empty()) { - AB = new CountPlusOneBound( - *BTypeMap[ABounds::CountPlusOneBoundKind].begin()); - } - + if (BI->getBounds(PtrBoundsKey) == nullptr) { + // Maps ABounds::BoundsKind to the set of possible bounds of this kind for + // the current PtrBoundsKey. + auto &BKindMap = CInfABnds.second; + for (auto &TySet : BKindMap) + mergeReachableProgramVars(PtrBoundsKey, TySet.second); + + ABounds *NewBound = getPreferredBound(PtrBoundsKey); // If we found any bounds? - if (AB != nullptr) { + if (NewBound != nullptr) { // Record that we inferred bounds using data-flow. - BI->BoundsInferStats.DataflowMatch.insert(CInfABnds.first); - BI->replaceBounds(CInfABnds.first, BoundsPriority::FlowInferred, AB); - FoundSome = true; + BI->BoundsInferStats.DataflowMatch.insert(PtrBoundsKey); + BI->replaceBounds(PtrBoundsKey, BoundsPriority::FlowInferred, NewBound); } else { - BKsFailedFlowInference.insert(CInfABnds.first); + BKsFailedFlowInference.insert(PtrBoundsKey); } } } - return FoundSome; +} + +// Construct an array bound with the most preferred kind from the bounds kind +// map. Count bounds have the highest priority, followed by byte count and then +// count-plus-one bounds. This function assumes that the BoundsKey sets in the +// map contain either zero or one BoundsKey. This is be achieved by first +// passing the sets to `mergeReachableProgramVars`. +ABounds *AvarBoundsInference::getPreferredBound(BoundsKey BK) { + BoundsKey BaseVar = 0; + bool NeedsBasePointer = + BI->InvalidLowerBounds.find(BK) != BI->InvalidLowerBounds.end(); + if (NeedsBasePointer && BI->LowerBounds.find(BK) != BI->LowerBounds.end()) + BaseVar = BI->LowerBounds[BK]; + + assert("Lower bound required but not available." && + (!NeedsBasePointer || BaseVar != 0)); + + const auto &BKindMap = CurrIterInferBounds[BK]; + // Utility to check if the map contains a non-empty set of bounds for a + // particular kind. This makes the following if statements much cleaner. + auto HasBoundKind = [&BKindMap](ABounds::BoundsKind Kind) { + return BKindMap.find(Kind) != BKindMap.end() && !BKindMap.at(Kind).empty(); + }; + + // Order of preference: Count, Byte, Count-plus-one + if (HasBoundKind(ABounds::CountBoundKind)) + return new CountBound(getOnly(BKindMap.at(ABounds::CountBoundKind)), + BaseVar); + + if (HasBoundKind(ABounds::ByteBoundKind)) + return new ByteBound(getOnly(BKindMap.at(ABounds::ByteBoundKind)), BaseVar); + + if (HasBoundKind(ABounds::CountPlusOneBoundKind)) + return new CountPlusOneBound( + getOnly(BKindMap.at(ABounds::CountPlusOneBoundKind)), BaseVar); + + return nullptr; } bool AvarBoundsInference::hasImpossibleBounds(BoundsKey BK) { @@ -261,14 +283,14 @@ void AvarBoundsInference::setImpossibleBounds(BoundsKey BK) { bool AvarBoundsInference::getReachableBoundKeys(const ProgramVarScope *DstScope, BoundsKey FromVarK, std::set &PotK, - AVarGraph &BKGraph, + const AVarGraph &BKGraph, bool CheckImmediate) { // First, find all the in-scope variable to which the SBKey flow to. - auto *SBVar = BI->getProgramVar(FromVarK); + auto *FromProgramVar = BI->getProgramVar(FromVarK); // If both are in the same scope? - if (*DstScope == *SBVar->getScope()) { + if (*DstScope == *FromProgramVar->getScope()) { PotK.insert(FromVarK); if (CheckImmediate) { return true; @@ -276,78 +298,58 @@ bool AvarBoundsInference::getReachableBoundKeys(const ProgramVarScope *DstScope, } // All constants are reachable! - if (SBVar->isNumConstant()) { + if (FromProgramVar->isNumConstant()) { PotK.insert(FromVarK); } - // Get all bounds key that are equivalent to FromVarK - std::set AllFKeys; - AllFKeys.clear(); - AllFKeys.insert(FromVarK); - - for (auto CurrVarK : AllFKeys) { - // Find all the in scope variables reachable from the CurrVarK - // bounds variable. - std::set InScopeKeys; - std::set VisibleKeys; - if (DstScope->isInInnerScope(*BI->getProgramVar(CurrVarK)->getScope())) - VisibleKeys.insert(CurrVarK); - ScopeVisitor TV(DstScope, InScopeKeys, VisibleKeys, BI->PVarInfo, - BI->PointerBoundsKey); - BKGraph.visitBreadthFirst(CurrVarK, - [&TV](BoundsKey BK) { TV.visitBoundsKey(BK); }); - // Prioritize in scope keys. - if (!InScopeKeys.empty()) { - PotK.insert(InScopeKeys.begin(), InScopeKeys.end()); - } else { - PotK.insert(VisibleKeys.begin(), VisibleKeys.end()); - } + // Find all in scope variables reachable from the FromVarK bounds variable. + ScopeVisitor TV(DstScope, BI); + BKGraph.visitBreadthFirst(FromVarK, + [&TV](BoundsKey BK) { TV.visitBoundsKey(BK); }); + // Prioritize in scope keys. + if (!TV.getInScopeKeys().empty()) { + PotK.insert(TV.getInScopeKeys().begin(), TV.getInScopeKeys().end()); + } else { + PotK.insert(TV.getVisibleKeys().begin(), TV.getVisibleKeys().end()); + + // This condition is necessary for array bounds using global variables. + // The bounds keys for global variable do not appear in the BKGraph array + // bounds graph, so breadth first search finds visits an empty set of nodes, + // not even visiting the initial bounds key. This ensures the global + // variable is added to the set of potential keys. + if (DstScope->isInInnerScope(*BI->getProgramVar(FromVarK)->getScope())) + PotK.insert(FromVarK); } // This is to get all the constants that are assigned to the variables // reachable from FromVarK. - if (!SBVar->isNumConstant()) { - std::set ReachableCons; - std::set Pre, CurrBK; + if (!FromProgramVar->isNumConstant()) { + std::set CurrBK; CurrBK.insert(PotK.begin(), PotK.end()); CurrBK.insert(FromVarK); for (auto CK : CurrBK) { - Pre.clear(); + std::set Pre; BKGraph.getPredecessors(CK, Pre); for (auto T : Pre) { auto *TVar = BI->getProgramVar(T); if (TVar != nullptr && TVar->isNumConstant()) { - ReachableCons.insert(T); + PotK.insert(T); } } } - PotK.insert(ReachableCons.begin(), ReachableCons.end()); } return !PotK.empty(); } -bool AvarBoundsInference::getRelevantBounds(BoundsKey BK, +void AvarBoundsInference::getRelevantBounds(BoundsKey BK, BndsKindMap &ResBounds) { - // Try to get the bounds of all RBKeys. - bool HasBounds = false; - // If this pointer is used in pointer arithmetic then there - // are no relevant bounds for this pointer. - if (!BI->hasPointerArithmetic(BK)) { - if (CurrIterInferBounds.find(BK) != CurrIterInferBounds.end()) { - // get the bounds inferred from the current iteration - ResBounds = CurrIterInferBounds[BK]; - HasBounds = true; - } else { - auto *PrevBounds = BI->getBounds(BK); - // Does the parent arr has bounds? - if (PrevBounds != nullptr) { - ResBounds[PrevBounds->getKind()].insert(PrevBounds->getBKey()); - HasBounds = true; - } - } + if (CurrIterInferBounds.find(BK) != CurrIterInferBounds.end()) { + // get the bounds inferred from the current iteration + ResBounds = CurrIterInferBounds[BK]; + } else if (ABounds *PrevBounds = BI->getBounds(BK)) { + ResBounds[PrevBounds->getKind()].insert(PrevBounds->getLengthKey()); } - return HasBounds; } bool AvarBoundsInference::areDeclaredBounds( @@ -360,7 +362,7 @@ bool AvarBoundsInference::areDeclaredBounds( if (DeclB && DeclB->getKind() == Bnds.first) { IsDeclaredB = true; for (auto TmpNBK : Bnds.second) { - if (!this->BI->areSameProgramVar(TmpNBK, DeclB->getBKey())) { + if (!this->BI->areSameProgramVar(TmpNBK, DeclB->getLengthKey())) { IsDeclaredB = false; break; } @@ -370,34 +372,34 @@ bool AvarBoundsInference::areDeclaredBounds( } bool AvarBoundsInference::predictBounds(BoundsKey K, - std::set &Neighbours, - AVarGraph &BKGraph) { - BndsKindMap NeighboursBnds, InferredKBnds; - // Bounds inferred from each of the neighbours. - std::map InferredNBnds; - bool IsChanged = false; + const std::set &Neighbours, + const AVarGraph &BKGraph) { bool ErrorOccurred = false; bool IsFuncRet = BI->isFunctionReturn(K); ProgramVar *KVar = this->BI->getProgramVar(K); - InferredNBnds.clear(); - // For reach of the Neighbour, try to infer possible bounds. + // Bounds inferred from each of the neighbours. + std::map InferredNBnds; + // For each of the Neighbour, try to infer possible bounds. for (auto NBK : Neighbours) { - NeighboursBnds.clear(); ErrorOccurred = false; - if (getRelevantBounds(NBK, NeighboursBnds) && !NeighboursBnds.empty()) { - std::set InfBK; + BndsKindMap NeighboursBnds; + getRelevantBounds(NBK, NeighboursBnds); + if (!NeighboursBnds.empty()) { for (auto &NKBChoice : NeighboursBnds) { - InfBK.clear(); - for (auto TmpNBK : NKBChoice.second) { - getReachableBoundKeys(KVar->getScope(), TmpNBK, InfBK, BKGraph); - } + ABounds::BoundsKind NeighbourKind = NKBChoice.first; + const std::set &NeighbourSet = NKBChoice.second; + + std::set InfBK; + for (BoundsKey NeighborBK : NeighbourSet) + getReachableBoundKeys(KVar->getScope(), NeighborBK, InfBK, BKGraph); + if (!InfBK.empty()) { - InferredNBnds[NBK][NKBChoice.first] = InfBK; + InferredNBnds[NBK][NeighbourKind] = InfBK; } else { bool IsDeclaredB = areDeclaredBounds(NBK, NKBChoice); - if (!IsDeclaredB || DisableInfDecls) { + if (!IsDeclaredB || _3COpts.DisableInfDecls) { // Oh, there are bounds for neighbour NBK but no bounds // can be inferred for K from it. InferredNBnds.clear(); @@ -426,38 +428,37 @@ bool AvarBoundsInference::predictBounds(BoundsKey K, } } + bool IsChanged = false; if (!InferredNBnds.empty()) { // All the possible inferred bounds for K. - InferredKBnds.clear(); - std::set TmpBKeys, AllKeys; + BndsKindMap InferredKBnds; // TODO: Figure out if there is a discrepancy and try to implement // root-cause analysis. // Find intersection of all bounds from neighbours. for (auto &IN : InferredNBnds) { for (auto &INB : IN.second) { - if (InferredKBnds.find(INB.first) == InferredKBnds.end()) { - InferredKBnds[INB.first] = INB.second; + ABounds::BoundsKind NeighbourKind = INB.first; + const std::set &NeighbourSet = INB.second; + if (InferredKBnds.find(NeighbourKind) == InferredKBnds.end()) { + InferredKBnds[NeighbourKind] = NeighbourSet; } else { - TmpBKeys.clear(); - AllKeys.clear(); - // Find intersection between the current bounds and the - // bounds propagated from current neighbour, i.e., INB.first. - auto &S1 = InferredKBnds[INB.first]; - auto &S2 = INB.second; - // Find intersection of bounds propagated from all neighbours. - findIntersection(S1, S2, TmpBKeys); - - AllKeys = S1; - AllKeys.insert(S2.begin(), S2.end()); - // Also, add all constants as potential bounds so that we can pick - // a constant with least value later. - for (auto CK : AllKeys) { + const std::set &KBoundsOfKind = InferredKBnds[NeighbourKind]; + // Keep the bounds in the intersection between the current bounds and + // the bounds from the neighbor. + std::set SharedBounds; + findIntersection(KBoundsOfKind, NeighbourSet, SharedBounds); + + // Also keep all constant bounds. Later on we will keep only the + // constant bound with the lowest value. + std::set AllBounds = KBoundsOfKind; + AllBounds.insert(NeighbourSet.begin(), NeighbourSet.end()); + for (auto CK : AllBounds) { auto *CKVar = this->BI->getProgramVar(CK); if (CKVar != nullptr && CKVar->isNumConstant()) - TmpBKeys.insert(CK); + SharedBounds.insert(CK); } - InferredKBnds[INB.first] = TmpBKeys; + InferredKBnds[NeighbourKind] = SharedBounds; } } } @@ -465,23 +466,25 @@ bool AvarBoundsInference::predictBounds(BoundsKey K, // Now from the newly inferred bounds i.e., InferredKBnds, check // if is is different from previously known bounds of K for (auto &IKB : InferredKBnds) { + ABounds::BoundsKind InferredKind = IKB.first; + std::set InferredSet = IKB.second; bool Handled = false; if (CurrIterInferBounds.find(K) != CurrIterInferBounds.end()) { - auto &BM = CurrIterInferBounds[K]; - if (BM.find(IKB.first) != BM.end()) { + BndsKindMap &CurrentBoundsMap = CurrIterInferBounds[K]; + if (CurrentBoundsMap.find(InferredKind) != CurrentBoundsMap.end()) { Handled = true; - if (BM[IKB.first] != IKB.second) { - BM[IKB.first] = IKB.second; - if (IKB.second.empty()) - BM.erase(IKB.first); + if (CurrentBoundsMap[InferredKind] != InferredSet) { + CurrentBoundsMap[InferredKind] = InferredSet; + if (InferredSet.empty()) + CurrentBoundsMap.erase(InferredKind); IsChanged = true; } } } if (!Handled) { - CurrIterInferBounds[K][IKB.first] = IKB.second; - if (IKB.second.empty()) { - CurrIterInferBounds[K].erase(IKB.first); + CurrIterInferBounds[K][InferredKind] = InferredSet; + if (InferredSet.empty()) { + CurrIterInferBounds[K].erase(InferredKind); } else { IsChanged = true; } @@ -494,42 +497,264 @@ bool AvarBoundsInference::predictBounds(BoundsKey K, } return IsChanged; } -bool AvarBoundsInference::inferBounds(BoundsKey K, AVarGraph &BKGraph, + +bool AvarBoundsInference::inferBounds(BoundsKey K, const AVarGraph &BKGraph, bool FromPB) { bool IsChanged = false; - if (BI->InvalidBounds.find(K) == BI->InvalidBounds.end()) { + // If a lower bound could not be inferred for a BoundsKey, then we refuse to + // infer an upper bound for it as well. This prevents inferring incorrect + // bounds when a bound would propagate through a pointer without a lower + // bound. + if (BI->hasLowerBound(K) && + BI->InvalidBounds.find(K) == BI->InvalidBounds.end()) { // Infer from potential bounds? if (FromPB) { IsChanged = inferFromPotentialBounds(K, BKGraph); } else { // Infer from the flow-graph. - std::set TmpBkeys; // Try to predict bounds from predecessors. - BKGraph.getPredecessors(K, TmpBkeys); - IsChanged = predictBounds(K, TmpBkeys, BKGraph); + std::set PredKeys; + BKGraph.getPredecessors(K, PredKeys); + IsChanged = predictBounds(K, PredKeys, BKGraph); } } return IsChanged; } + +void AVarBoundsInfo::computeInvalidLowerBounds(ProgramInfo *PI) { + // This will compute a breadth first search starting from the constant + // InvalidLowerBoundKey. Any reachable keys are also invalid lower bounds. + // This is essentially the same algorithm as is used for solving the checked + // type constraint graph. + assert(InvalidLowerBounds.empty()); + std::queue WorkList; + WorkList.push(InvalidLowerBoundKey); + + // To check if a bounds key is already invalidated we check if it is either in + // the set of invalidated keys, or is the constant InvalidLowerBoundKey. + auto IsInvalidated = [this](BoundsKey BK) { + return BK == InvalidLowerBoundKey || + InvalidLowerBounds.find(BK) != InvalidLowerBounds.end(); + }; + + while (!WorkList.empty()) { + BoundsKey Curr = WorkList.front(); + WorkList.pop(); + assert(IsInvalidated(Curr)); + + std::set Neighbors; + LowerBoundGraph.getSuccessors(Curr, Neighbors); + for (BoundsKey NK : Neighbors) { + // This is an awful hack to work around a problem during conversion phase + // two. A parameter would be given count bounds, with a local being + // created to hold the range bounds. A conversion is done with + // -itypes-for-extern` and the headers are copied over. The version of + // the header in the local directory now has count bounds on an itype. If + // we trust those bounds, then the next conversion does not emit range + // bounds. + PointerVariableConstraint *PVC = getConstraintVariable(PI, NK); + // Strictly speaking, this can occur outside of -itypes-for-extern, but + // it is unlikely, and I've decided that the risk of unintentionally + // changing other behavior is greater than the risk that this special + // case will be needed in some other circumstance. + bool IsItypeParam = _3COpts.ItypesForExtern && PVC && PVC->srcHasItype(); + + // The neighbors of an invalid lower bound are also invalid, with the + // exception that if there is a bound in the source code, then we assume + // the bound is correct, and so the pointer is a valid lower bound for + // itself. + bool HasDeclaredBounds = + getBounds(NK, BoundsPriority::Declared) != nullptr; + if ((IsItypeParam || !HasDeclaredBounds) && !IsInvalidated(NK)) { + InvalidLowerBounds.insert(NK); + WorkList.push(NK); + } + } + } +} + +void +AVarBoundsInfo::inferLowerBounds(ProgramInfo *PI) { + computeInvalidLowerBounds(PI); + + // This maps array pointers to a single consistent lower bound pointer, or + // possible the constant InvalidLowerBoundKey if no lower bound could be found + // or generated. Note that there can only be a single valid lower bound. + // Future work can extend this map to track sets if possible lower bounds. + std::map InfLBs; + + // Lower bound inference will proceed as a traversal of the LowerBoundGraph. + // The traversal starts at the direct predecessors of the pointers that need + // an inferred lower bound. + std::queue WorkList; + for (BoundsKey BK: InvalidLowerBounds) { + std::set Pred; + LowerBoundGraph.getPredecessors(BK, Pred); + for (BoundsKey Seed: Pred) { + if (Seed != InvalidLowerBoundKey && + InvalidLowerBounds.find(Seed) == InvalidLowerBounds.end()) { + // This pointer is a valid lower bound for itself, so add it to the + // worklist and initialize it in the map of inferred lower bounds. + InfLBs[Seed] = Seed; + WorkList.push(Seed); + } + } + } + + // It's possible for there to be invalid lower bounds that are not reachable + // from any valid lower bounds, so we also initialize the worklist with all + // invalid lower bounds. These come after the valid lower bounds so that is + // less likely a fresh lower bound will be generated but later thrown out, as + // that process is inefficient at least in the current implementation. + for (BoundsKey InvLB : InvalidLowerBounds) + WorkList.push(InvLB); + + // This set tracks the pointers for which we will need to generate a fresh + // lower bound pointer. These pointers do not have a single consistent lower + // bound in the source code, but 3C is able to insert a duplicate declaration + // to act as the lower bound. + std::set NeedFreshLB; + + // This set track the pointers that have multiple inconsistent lower bounds. + // This is used to differentiate pointers that need a fresh lower bound + // because no lower bounds could be found from pointers that need a fresh + // lower bound because there were multiple inconsistent lower bounds. Note + // that is not a subset of NeedFreshLB because a pointer may have conflicting + // bounds but be ineligible for a fresh lower bound. + std::set HasConflictingBounds; + + while (!WorkList.empty()) { + BoundsKey BK = WorkList.front(); + WorkList.pop(); + + if (isEligibleForFreshLowerBound(BK) && + (InfLBs.find(BK) == InfLBs.end() || + InfLBs[BK] == InvalidLowerBoundKey)) { + // We've reached an array pointer in the work list that either has not + // been assigned a lower bound, or has multiple conflicting lower bounds. + // We will generate a fresh lower bound. + assert( + "Generating fresh bound for pointer that can be its own lower bound." && + InvalidLowerBounds.find(BK) != InvalidLowerBounds.end()); + InfLBs[BK] = getFreshLowerBound(BK); + NeedFreshLB.insert(BK); + } + + std::set Succ; + LowerBoundGraph.getSuccessors(BK, Succ); + for (BoundsKey S : Succ) { + + // Do not process any array pointers that are valid lower bounds. They + // should just serve as their own lower bound. + if (InvalidLowerBounds.find(S) == InvalidLowerBounds.end()) + continue; + + if (InfLBs.find(S) == InfLBs.end()) { + // No prior lower bound known for `S`. Initialize it to use the same + // lower bound as `BK`, if this is possible given their scopes. + if (isInAccessibleScope(S, InfLBs[BK])) { + InfLBs[S] = InfLBs[BK]; + } else { + InfLBs[S] = InvalidLowerBoundKey; + } + WorkList.push(S); + } else if (InfLBs[BK] != InfLBs[S] && + HasConflictingBounds.find(S) == HasConflictingBounds.end()) { + // The lower bound of `BK` is not the same as the current inferred lower + // bounds of `S`. This is a problem, so we need to mark `S` as having + // conflicting lower bounds. We only do this invalidation step once. If + // the BoundsKey is already known to have conflicting bounds, then do + // not reset it again. Doing so can cause an infinite loop when there is + // a cycle of BoundsKeys needing a lower bound, where each BoundsKey in + // the cycle is eligible for a fresh lower bound. + HasConflictingBounds.insert(S); + + if (NeedFreshLB.find(S) != NeedFreshLB.end()) { + // This case handles when we a fresh lower bounds was created for `S` + // before any conflict was detected. It is possible that the conflict + // we detect here only exists between the fresh lower bound and the + // lower bound of `BK`. In this case, we can drop the fresh lower + // bounds to use the inferred lower bound. In order to fully drop the + // fresh bound, we must also drop it from all BoundsKey reachable from + // `S`, as these may have already had a lower bound inferred based on + // the fresh lower bound. + NeedFreshLB.erase(S); + BoundsKey SLB = InfLBs[S]; + // TODO: Erasing the bounds by a breadth first search from S is + // inefficient, probably resulting in quadratic worst case + // running time, but this hasn't shown up a real performance + // issue yet. + LowerBoundGraph.visitBreadthFirst(S, [this, SLB, &InfLBs, &WorkList]( + BoundsKey BK) { + if (InfLBs.find(BK) != InfLBs.end() && InfLBs[BK] == SLB) { + InfLBs.erase(BK); + std::set Pred; + LowerBoundGraph.getPredecessors(BK, Pred); + for (BoundsKey P : Pred) { + if (P != InvalidLowerBoundKey && + InfLBs.find(P) != InfLBs.end()) + WorkList.push(P); + } + } + }); + } else if (InfLBs[S] != InvalidLowerBoundKey) { + // If no fresh lower bound was generated, then things are much + // simpler. Just invalidate the lower bound of `S` and enqueue it. + InfLBs[S] = InvalidLowerBoundKey; + WorkList.push(S); + } + } + // Otherwise, the a lower bound exists for `S`, and it's the same lower + // bounds as `BK`. Nothing changes, so don't enqueue `S`. + } + } + + NeedFreshLowerBounds = NeedFreshLB; + LowerBounds = InfLBs; + + // This is an awful hack to work around a problem during conversion phase + // two. + if (_3COpts.ItypesForExtern) { + for (auto InferredLBPair : LowerBounds) { + if (BInfo[InferredLBPair.first][Declared]) { + BInfo[InferredLBPair.first][Declared]->setLowerBoundKey( + InferredLBPair.second); + } + } + } +} + +BoundsKey AVarBoundsInfo::getFreshLowerBound(BoundsKey Arr) { + ProgramVar *ArrVar = getProgramVar(Arr); + BoundsKey FreshLB = getRandomBKey(); + ProgramVar *FreshLBVar = + ProgramVar::createNewProgramVar(FreshLB, + "__3c_lower_bound_" + ArrVar->getVarName(), + ArrVar->getScope()); + insertProgramVar(FreshLB, FreshLBVar); + return FreshLB; +} + +bool AVarBoundsInfo::hasLowerBound(BoundsKey K) { + return InvalidLowerBounds.find(K) == InvalidLowerBounds.end() || + (LowerBounds.find(K) != LowerBounds.end() && + LowerBounds[K] != InvalidLowerBoundKey); +} + bool AvarBoundsInference::inferFromPotentialBounds(BoundsKey BK, - AVarGraph &BKGraph) { - bool IsChanged = false; - bool Handled = false; + const AVarGraph &BKGraph) { + // If we have any inferred bounds for K then ignore potential bounds. + bool HasInferredBound = false; if (CurrIterInferBounds.find(BK) != CurrIterInferBounds.end()) { auto &BM = CurrIterInferBounds[BK]; - // If we have any inferred bounds for K then ignore potential - // bounds. - for (auto &PosB : BM) { - if (!PosB.second.empty()) { - Handled = true; - break; - } - } + HasInferredBound = llvm::any_of(BM, [](auto InfB) { + return !InfB.second.empty(); + }); } - if (!Handled) { + if (!HasInferredBound) { auto &PotBDs = BI->PotBoundsInfo; // Here, the logic is: // We first try potential bounds and if there are no potential bounds? @@ -550,10 +775,10 @@ bool AvarBoundsInference::inferFromPotentialBounds(BoundsKey BK, } if (!PotentialB.empty()) { CurrIterInferBounds[BK][PotKind] = PotentialB; - IsChanged = true; + return true; } } - return IsChanged; + return false; } bool PotentialBoundsInfo::hasPotentialCountBounds(BoundsKey PtrBK) { @@ -592,27 +817,43 @@ void PotentialBoundsInfo::addPotentialBoundsPOne( } bool AVarBoundsInfo::isValidBoundVariable(clang::Decl *D) { + if (D == nullptr) + return false; + + // Any pointer declaration in an unwritable file without existing bounds + // annotations is not valid. This ensures we do not add bounds onto pointers + // where attempting to rewrite the variable to insert the bound would be an + // error. If there are existing bounds, no new bound will be inferred, so no + // rewriting will be attempted. By leaving existing bounds as valid, these + // bounds can be used infer bounds on other (writeable) declarations. + // Non-pointer types are also still valid because these will never need bounds + // expression, and they need to remain valid so that they can be used by + // existing array pointer bounds. + auto PSL = PersistentSourceLoc::mkPSL(D, D->getASTContext()); + if (!canWrite(PSL.getFileName())) { + if (auto *DD = dyn_cast(D)) + return !DD->getType()->isPointerType() || DD->hasBoundsExpr(); + return false; + } + // All return and field values are valid bound variables. - if (D && (isa(D) || isa(D))) + if (isa(D) || isa(D)) return true; // For Parameters, check if they belong to a valid function. // Function pointer types are not considered valid functions, so function - // pointer parameters are are disqualified as valid bound variables here. - if (auto *PD = dyn_cast_or_null(D)) + // pointer parameters are disqualified as valid bound variables here. + if (auto *PD = dyn_cast(D)) return PD->getParentFunctionOrMethod() != nullptr; - // For VarDecls, check if these are are not dummy and have a name. - if (auto *VD = dyn_cast_or_null(D)) + // For VarDecls, check if these are not dummy and have a name. + if (auto *VD = dyn_cast(D)) return !VD->getNameAsString().empty(); return false; } -void AVarBoundsInfo::insertDeclaredBounds(clang::Decl *D, ABounds *B) { - assert(isValidBoundVariable(D) && "Declaration not a valid bounds variable"); - BoundsKey BK; - tryGetVariable(D, BK); +void AVarBoundsInfo::insertDeclaredBounds(BoundsKey BK, ABounds *B) { if (B != nullptr) { // If there is already bounds information, release it. removeBounds(BK); @@ -625,6 +866,13 @@ void AVarBoundsInfo::insertDeclaredBounds(clang::Decl *D, ABounds *B) { } } +void AVarBoundsInfo::insertDeclaredBounds(clang::Decl *D, ABounds *B) { + assert(isValidBoundVariable(D) && "Declaration not a valid bounds variable"); + BoundsKey BK; + tryGetVariable(D, BK); + insertDeclaredBounds(BK, B); +} + bool AVarBoundsInfo::tryGetVariable(clang::Decl *D, BoundsKey &R) { bool RetVal = false; if (isValidBoundVariable(D)) { @@ -651,32 +899,33 @@ bool AVarBoundsInfo::tryGetVariable(clang::Decl *D, BoundsKey &R) { bool AVarBoundsInfo::tryGetVariable(clang::Expr *E, const ASTContext &C, BoundsKey &Res) { - Optional OptConsVal; - bool Ret = false; if (E != nullptr) { E = E->IgnoreParenCasts(); - if (E->getType()->isArithmeticType() && - (OptConsVal = E->getIntegerConstantExpr(C))) { + + // Get the BoundsKey for the constant value if the expression is a constant + // integer expression. + Optional OptConsVal = E->getIntegerConstantExpr(C); + if (E->getType()->isArithmeticType() && OptConsVal.hasValue()) { Res = getVarKey(*OptConsVal); - Ret = true; - } else if (DeclRefExpr *DRE = dyn_cast(E)) { - auto *D = DRE->getDecl(); - Ret = tryGetVariable(D, Res); - if (!Ret) { - assert(false && "Invalid declaration found inside bounds expression"); - } - } else if (MemberExpr *ME = dyn_cast(E)) { - return tryGetVariable(ME->getMemberDecl(), Res); - } else { - // assert(false && "Variable inside bounds declaration is an expression"); + return true; } + + // For declarations or struct member references, get the bounds key for the + // references variables or field. + if (auto *DRE = dyn_cast(E)) + return tryGetVariable(DRE->getDecl(), Res); + if (auto *ME = dyn_cast(E)) + return tryGetVariable(ME->getMemberDecl(), Res); } - return Ret; + return false; } // Merging bounds B with the present bounds of key L at the same priority P // Returns true if we update the bounds for L (with B) bool AVarBoundsInfo::mergeBounds(BoundsKey L, BoundsPriority P, ABounds *B) { + if (B->getLowerBoundKey() == InvalidLowerBoundKey && + LowerBounds.find(L) != LowerBounds.end()) + B->setLowerBoundKey(LowerBounds[L]); bool RetVal = false; if (BInfo.find(L) != BInfo.end() && BInfo[L].find(P) != BInfo[L].end()) { // If previous computed bounds are not same? Then release the old bounds. @@ -783,8 +1032,18 @@ BoundsKey AVarBoundsInfo::getVariable(clang::VarDecl *VD) { auto *PVar = ProgramVar::createNewProgramVar(NK, VD->getNameAsString(), PVS); insertProgramVar(NK, PVar); - if (isPtrOrArrayType(VD->getType())) + if (isPtrOrArrayType(VD->getType())) { PointerBoundsKey.insert(NK); + // Global variables cannot be given range bounds because it is not + // possible to initialize a duplicated pointer variable with the same + // value as the original. + // TODO: Followup issue: Implementing the rewriting here would be easy, + // but it would also require change the compiler to recognize + // dynamic bounds casts are constant expressions, which doesn't + // sound too hard. + if (!VD->isLocalVarDeclOrParm()) + markIneligibleForFreshLowerBound(NK); + } } return getVarKey(PSL); } @@ -802,16 +1061,43 @@ BoundsKey AVarBoundsInfo::getVariable(clang::ParmVarDecl *PVD) { const FunctionParamScope *FPS = FunctionParamScope::getFunctionParamScope( FD->getNameAsString(), FD->isStatic()); std::string ParamName = PVD->getNameAsString(); - // If this is a parameter without name!? - // Just get the name from argument number. - if (ParamName.empty()) - ParamName = "NONAMEPARAM_" + std::to_string(ParamIdx); + + if (ParamName.empty()) { + // The parameter declaration doesn't have a name. Try to get the + // corresponding function definition, and then the parameter declaration + // in that function. Use the name of that parameter. + // TODO: I think the declaration merging code written by kyle does a good + // job of handling missing/inconsistent parameter names. Can I just + // use that? + if (auto *FnDef = FD->getDefinition()) { + if (FnDef->getNumParams() >= ParamIdx) { + if (auto *DefPVD = FnDef->getParamDecl(ParamIdx)) { + ParamName = DefPVD->getNameAsString(); + } + } + } + // If we still couldn't find a name, then we make one up using the + // parameter index. + // TODO: Is this better than leaving the name string empty? There are + // situations where an identifier can be omitted without error, but + // using an undeclared identifier is an error. + if (ParamName.empty()) + ParamName = "NONAMEPARAM_" + std::to_string(ParamIdx); + } auto *PVar = ProgramVar::createNewProgramVar(NK, ParamName, FPS); insertProgramVar(NK, PVar); insertParamKey(ParamKey, NK); - if (isPtrOrArrayType(PVD->getType())) + if (isPtrOrArrayType(PVD->getType())) { PointerBoundsKey.insert(NK); + // We do not give range bounds to parameters with an array type. Doing + // this causes the local variable duplicate definition to have an array + // type, but pointer arithmetic on constant size arrays is not allowed. + // TODO: Follow up issue: Can we add some special logic in rewriting to + // emit the duplicate definition with an _Array_pointer type? + if (isArrayType(PVD->getType())) + markIneligibleForFreshLowerBound(NK); + } } return ParamDeclVarMap.left().at(ParamKey); } @@ -831,8 +1117,12 @@ BoundsKey AVarBoundsInfo::getVariable(clang::FunctionDecl *FD) { ProgramVar::createNewProgramVar(NK, FD->getNameAsString(), FPS); insertProgramVar(NK, PVar); FuncDeclVarMap.insert(FuncKey, NK); - if (isPtrOrArrayType(FD->getReturnType())) + if (isPtrOrArrayType(FD->getReturnType())) { PointerBoundsKey.insert(NK); + // Fresh lower bounds are not inserted for function returns. I don't see a + // way around this limitation. + markIneligibleForFreshLowerBound(NK); + } } return FuncDeclVarMap.left().at(FuncKey); } @@ -847,8 +1137,16 @@ BoundsKey AVarBoundsInfo::getVariable(clang::FieldDecl *FD) { const StructScope *SS = StructScope::getStructScope(StName); auto *PVar = ProgramVar::createNewProgramVar(NK, FD->getNameAsString(), SS); insertProgramVar(NK, PVar); - if (isPtrOrArrayType(FD->getType())) + if (isPtrOrArrayType(FD->getType())) { PointerBoundsKey.insert(NK); + // Fields are not rewritten with range bounds because we would need to + // duplicate the field and update all structure initializations to + // properly set the new field. + // TODO: Followup issue: Add the duplicate declaration as a new field in + // the struct and then also update all struct initializer to include + // the new field. + markIneligibleForFreshLowerBound(NK); + } } return getVarKey(PSL); } @@ -859,19 +1157,6 @@ BoundsKey AVarBoundsInfo::getRandomBKey() { return Ret; } -bool AVarBoundsInfo::addAssignment(clang::Decl *L, clang::Decl *R) { - BoundsKey BL, BR; - if (tryGetVariable(L, BL) && tryGetVariable(R, BR)) { - return addAssignment(BL, BR); - } - return false; -} - -bool AVarBoundsInfo::addAssignment(clang::DeclRefExpr *L, - clang::DeclRefExpr *R) { - return addAssignment(L->getDecl(), R->getDecl()); -} - bool AVarBoundsInfo::handleAssignment(clang::Expr *L, const CVarSet &LCVars, const std::set &CSLKeys, clang::Expr *R, const CVarSet &RCVars, @@ -926,10 +1211,13 @@ bool AVarBoundsInfo::addAssignment(BoundsKey L, BoundsKey R) { // So, if we create a edge from return to itself then we create a cyclic // dependency and never will be able to find the bounds for the return // value. - if (L != R) + if (L != R) { ProgVarGraph.addUniqueEdge(R, L); + LowerBoundGraph.addUniqueEdge(R, L); + } } else { ProgVarGraph.addUniqueEdge(R, L); + LowerBoundGraph.addUniqueEdge(R, L); ProgramVar *PV = getProgramVar(R); if (!(PV && PV->isNumConstant())) ProgVarGraph.addUniqueEdge(L, R); @@ -937,62 +1225,11 @@ bool AVarBoundsInfo::addAssignment(BoundsKey L, BoundsKey R) { return true; } -// Visitor to collect all the variables that are used during the life-time -// of the visitor. -// This class also has a flag that gets set when a variable is observed -// more than once. -class CollectDeclsVisitor : public RecursiveASTVisitor { -public: - std::set ObservedDecls; - std::set StructAccess; - - explicit CollectDeclsVisitor(ASTContext *Ctx) : C(Ctx) { - ObservedDecls.clear(); - StructAccess.clear(); - } - virtual ~CollectDeclsVisitor() { ObservedDecls.clear(); } - - bool VisitDeclRefExpr(DeclRefExpr *DRE) { - VarDecl *VD = dyn_cast_or_null(DRE->getDecl()); - if (VD != nullptr) { - ObservedDecls.insert(VD); - } - return true; - } - - // For a->b; We need to get `a->b` - bool VisitMemberExpr(MemberExpr *ME) { - std::string MAccess = getSourceText(ME->getSourceRange(), *C); - if (!MAccess.empty()) { - StructAccess.insert(MAccess); - } - return false; - } - -private: - ASTContext *C; -}; - -bool AVarBoundsInfo::handlePointerAssignment(clang::Stmt *St, clang::Expr *L, - clang::Expr *R, ASTContext *C, +bool AVarBoundsInfo::handlePointerAssignment(clang::Expr *L, clang::Expr *R, + ASTContext *C, ConstraintResolver *CR) { - CollectDeclsVisitor LVarVis(C); - LVarVis.TraverseStmt(L->getExprStmt()); - - CollectDeclsVisitor RVarVis(C); - RVarVis.TraverseStmt(R->getExprStmt()); - - std::set CommonVars; - std::set CommonStVars; - findIntersection(LVarVis.ObservedDecls, RVarVis.ObservedDecls, CommonVars); - findIntersection(LVarVis.StructAccess, RVarVis.StructAccess, CommonStVars); - - if (!CommonVars.empty() || CommonStVars.empty()) { - for (auto *LHSCVar : CR->getExprConstraintVarsSet(L)) { - if (LHSCVar->hasBoundsKey()) - ArrPointerBoundsKey.insert(LHSCVar->getBoundsKey()); - } - } + if (!isLowerBoundAssignment(L, R)) + recordArithmeticOperation(L, CR); return true; } @@ -1008,21 +1245,62 @@ void AVarBoundsInfo::recordArithmeticOperation(clang::Expr *E, ConstraintResolver *CR) { CVarSet CSet = CR->getExprConstraintVarsSet(E); for (auto *CV : CSet) { - if (CV->hasBoundsKey()) - ArrPointersWithArithmetic.insert(CV->getBoundsKey()); + if (CV->hasBoundsKey()) { + BoundsKey BK = CV->getBoundsKey(); + ArrPointersWithArithmetic.insert(BK); + LowerBoundGraph.addUniqueEdge(InvalidLowerBoundKey, BK); + } } } -bool AVarBoundsInfo::hasPointerArithmetic(BoundsKey BK) { - return ArrPointersWithArithmetic.find(BK) != ArrPointersWithArithmetic.end(); +bool AVarBoundsInfo::needsFreshLowerBound(BoundsKey BK) { + return NeedFreshLowerBounds.find(BK) != NeedFreshLowerBounds.end(); } -ProgramVar *AVarBoundsInfo::getProgramVar(BoundsKey VK) { - ProgramVar *Ret = nullptr; - if (PVarInfo.find(VK) != PVarInfo.end()) { - Ret = PVarInfo[VK]; - } - return Ret; +bool AVarBoundsInfo::isEligibleForFreshLowerBound(BoundsKey BK) { + return IneligibleForFreshLowerBound.find(BK) == + IneligibleForFreshLowerBound.end(); +} + +void AVarBoundsInfo::markIneligibleForFreshLowerBound(BoundsKey BK) { + IneligibleForFreshLowerBound.insert(BK); +} + +bool AVarBoundsInfo::needsFreshLowerBound(ConstraintVariable *CV) { + if (!CV->hasBoundsKey()) + return false; + BoundsKey BK = CV->getBoundsKey(); + // A pointer should get range bounds if it is computed by pointer arithmetic + // and would otherwise need bounds. Some pointers (global variables and struct + // fields) can't be rewritten to use range bounds (by 3C; Checked C does + // permit it), so we return false on these. + return needsFreshLowerBound(BK) && isEligibleForFreshLowerBound(BK) && + getBounds(BK) != nullptr; +} + +ProgramVar *AVarBoundsInfo::getProgramVar(BoundsKey VK) const { + if (PVarInfo.find(VK) != PVarInfo.end()) + return PVarInfo.at(VK); + return nullptr; +} + +const ProgramVarScope *AVarBoundsInfo::getProgramVarScope(BoundsKey BK) const{ + if (ProgramVar *Var = getProgramVar(BK)) + return Var->getScope(); + return nullptr; +} + +bool AVarBoundsInfo::isInAccessibleScope(BoundsKey From, BoundsKey To) { + const ProgramVarScope *FromScope = getProgramVarScope(From); + const ProgramVarScope *ToScope = getProgramVarScope(To); + return FromScope != nullptr && ToScope != nullptr && + (*FromScope == *ToScope || FromScope->isInInnerScope(*ToScope)); +} + +bool AVarBoundsInfo::scopeCanHaveLowerBound(BoundsKey BK) { + const ProgramVarScope *BKScope = getProgramVarScope(BK); + return BKScope != nullptr && !isa(BKScope) && + !isa(BKScope); } bool AVarBoundsInfo::hasVarKey(PersistentSourceLoc &PSL) { @@ -1037,9 +1315,7 @@ BoundsKey AVarBoundsInfo::getVarKey(PersistentSourceLoc &PSL) { BoundsKey AVarBoundsInfo::getConstKey(uint64_t Value) { if (ConstVarKeys.find(Value) == ConstVarKeys.end()) { BoundsKey NK = ++BCount; - std::string ConsString = std::to_string(Value); - ProgramVar *NPV = ProgramVar::createNewProgramVar( - NK, ConsString, GlobalScope::getGlobalScope(), true); + ProgramVar *NPV = ProgramVar::createNewConstantVar(NK, Value); insertProgramVar(NK, NPV); ConstVarKeys[Value] = NK; } @@ -1063,36 +1339,45 @@ void AVarBoundsInfo::insertProgramVar(BoundsKey NK, ProgramVar *PV) { PVarInfo[NK] = PV; } -bool AVarBoundsInfo::performWorkListInference( - const std::set &ArrNeededBounds, AVarGraph &BKGraph, - AvarBoundsInference &BI, bool FromPB) { - bool RetVal = false; - std::set WorkList; - std::set NextIterArrs; - WorkList.clear(); - WorkList.insert(ArrNeededBounds.begin(), ArrNeededBounds.end()); - bool Changed = true; - while (Changed) { - Changed = false; - NextIterArrs.clear(); - // Are there any ARR atoms that need bounds? - while (!WorkList.empty()) { - BoundsKey CurrArrKey = *WorkList.begin(); - // Remove the bounds key from the worklist. - WorkList.erase(CurrArrKey); - // Can we find bounds for this Arr? +void AVarBoundsInfo::performWorkListInference(const AVarGraph &BKGraph, + AvarBoundsInference &BI, + bool FromPB) { + + // BoundsKeys corresponding to array pointers that need bounds. This will seed + // the initial WorkList, and be used to ensure that only BoundsKeys needing + // bounds are added to the list in subsequent iterations. + std::set ArrNeededBounds; + getBoundsNeededArrPointers(ArrNeededBounds); + + std::set WorkList(ArrNeededBounds); + while (!WorkList.empty()) { + // This set will collect BoundsKeys which are successors of a BoundsKey that + // was assigned a bound in this iteration. These subset of these that need + // bounds will be used as the worklist in the next iteration. + std::set NextIterArrs; + + for (BoundsKey CurrArrKey : WorkList) { + // inferBounds will return true if a bound was found for CurrArrKey. If a + // bound can be found, queue the successor nodes for bounds inferences in + // the next iteration of the outer loop. if (BI.inferBounds(CurrArrKey, BKGraph, FromPB)) { - RetVal = true; - Changed = true; // Get all the successors of the ARR whose bounds we just found. + // Successor BoundsKeys are added into NextIterArrs without clearing the + // current contents. BKGraph.getSuccessors(CurrArrKey, NextIterArrs); } } - if (Changed) { - findIntersection(ArrNeededBounds, NextIterArrs, WorkList); - } + + // WorkList will be cleared by findIntersection before it is used to store + // the intersection of ArrNeededBounds and NextIterArrs. If NextIterArrs is + // empty, then the intersection will also be empty, and the loop will + // terminate. + findIntersection(ArrNeededBounds, NextIterArrs, WorkList); } - return RetVal; + + // From all the sets of bounds computed for various array variables. Intersect + // them and find the common bound variable. + BI.convergeInferredBounds(); } BoundsKey AVarBoundsInfo::getCtxSensCEBoundsKey(const PersistentSourceLoc &PSL, @@ -1100,99 +1385,30 @@ BoundsKey AVarBoundsInfo::getCtxSensCEBoundsKey(const PersistentSourceLoc &PSL, return CSBKeyHandler.getCtxSensCEBoundsKey(PSL, BK); } -void AVarBoundsInfo::computerArrPointers(ProgramInfo *PI, - std::set &ArrPointers) { - auto &CS = PI->getConstraints(); - for (auto Bkey : PointerBoundsKey) { - // Regular variables. - auto &BkeyToPSL = DeclVarMap.right(); - if (BkeyToPSL.find(Bkey) != BkeyToPSL.end()) { - auto &PSL = BkeyToPSL.at(Bkey); - if (hasArray(PI->getVarMap().at(PSL), CS)) { - ArrPointers.insert(Bkey); - } - // Does this array belong to a valid program variable? - if (isInSrcArray(PI->getVarMap().at(PSL), CS)) { - InProgramArrPtrBoundsKeys.insert(Bkey); - } - - if (hasOnlyNtArray(PI->getVarMap().at(PSL), CS)) { - NtArrPointerBoundsKey.insert(Bkey); - } - - continue; - } - - // Function parameters - auto &ParmBkeyToPSL = ParamDeclVarMap.right(); - if (ParmBkeyToPSL.find(Bkey) != ParmBkeyToPSL.end()) { - auto &ParmTup = ParmBkeyToPSL.at(Bkey); - std::string FuncName = std::get<0>(ParmTup); - std::string FileName = std::get<1>(ParmTup); - bool IsStatic = std::get<2>(ParmTup); - unsigned ParmNum = std::get<3>(ParmTup); - FVConstraint *FV = nullptr; - if (IsStatic || !PI->getExtFuncDefnConstraint(FuncName)) { - FV = PI->getStaticFuncConstraint(FuncName, FileName); - } else { - FV = PI->getExtFuncDefnConstraint(FuncName); - } - - if (hasArray(FV->getExternalParam(ParmNum), CS) || - hasArray(FV->getInternalParam(ParmNum), CS)) { - ArrPointers.insert(Bkey); - } - // Does this array belong to a valid program variable? - if (isInSrcArray(FV->getExternalParam(ParmNum), CS) || - isInSrcArray(FV->getInternalParam(ParmNum), CS)) { - InProgramArrPtrBoundsKeys.insert(Bkey); - } - - if (hasOnlyNtArray(FV->getExternalParam(ParmNum), CS) || - hasOnlyNtArray(FV->getInternalParam(ParmNum), CS)) { - NtArrPointerBoundsKey.insert(Bkey); - } +void AVarBoundsInfo::computeArrPointers(const ProgramInfo *PI) { + NtArrPointerBoundsKey.clear(); + ArrPointerBoundsKey.clear(); + for (auto BK : PointerBoundsKey) { + const PointerVariableConstraint *CV = getConstraintVariable(PI, BK); + if (CV == nullptr) continue; - } - // Function returns. - auto &FuncKeyToPSL = FuncDeclVarMap.right(); - if (FuncKeyToPSL.find(Bkey) != FuncKeyToPSL.end()) { - auto &FuncRet = FuncKeyToPSL.at(Bkey); - std::string FuncName = std::get<0>(FuncRet); - std::string FileName = std::get<1>(FuncRet); - bool IsStatic = std::get<2>(FuncRet); - const FVConstraint *FV = nullptr; - std::set Tmp; - Tmp.clear(); - if (IsStatic || !PI->getExtFuncDefnConstraint(FuncName)) { - Tmp.insert(PI->getStaticFuncConstraint(FuncName, FileName)); - FV = getOnly(Tmp); - } else { - Tmp.insert(PI->getExtFuncDefnConstraint(FuncName)); - FV = getOnly(Tmp); - } - - if (hasArray(FV->getExternalReturn(), CS) || - hasArray(FV->getInternalReturn(), CS)) { - ArrPointers.insert(Bkey); - } - // Does this array belongs to a valid program variable? - if (isInSrcArray(FV->getExternalReturn(), CS) || - isInSrcArray(FV->getInternalReturn(), CS)) { - InProgramArrPtrBoundsKeys.insert(Bkey); - } - if (hasOnlyNtArray(FV->getExternalReturn(), CS) || - hasOnlyNtArray(FV->getInternalReturn(), CS)) { - NtArrPointerBoundsKey.insert(Bkey); - // If the return value is an nt array pointer - // and there are no declared bounds? Then, we cannot - // find bounds for this pointer. - if (getBounds(Bkey) == nullptr) - PointersWithImpossibleBounds.insert(Bkey); - } - continue; + if (hasArray(CV, PI->getConstraints())) + ArrPointerBoundsKey.insert(BK); + + // Does this array belong to a valid program variable? + if (isInSrcArray(CV, PI->getConstraints())) + InProgramArrPtrBoundsKeys.insert(BK); + + if (hasOnlyNtArray(CV, PI->getConstraints())) { + NtArrPointerBoundsKey.insert(BK); + // If the return value is an nt array pointer and there are no declared + // bounds? Then, we cannot find bounds for this pointer. This avoids + // placing incorrect bounds on null terminated arrays as discussed in + // https://github.com/correctcomputation/checkedc-clang/issues/553 + if (CV->getName() == RETVAR && getBounds(BK) == nullptr) + PointersWithImpossibleBounds.insert(BK); } } @@ -1202,10 +1418,8 @@ void AVarBoundsInfo::computerArrPointers(ProgramInfo *PI, // of the regular bounds key, we just get the neighbours (predecessors // and successors) of the regular bounds key to get the context-sensitive // counterparts. - std::set CtxSensBKeys; - CtxSensBKeys.clear(); std::set TmpBKeys; - for (auto BK : ArrPointers) { + for (auto BK : ArrPointerBoundsKey) { CtxSensProgVarGraph.getSuccessors(BK, TmpBKeys, true); CtxSensProgVarGraph.getPredecessors(BK, TmpBKeys, true); RevCtxSensProgVarGraph.getSuccessors(BK, TmpBKeys, true); @@ -1213,24 +1427,24 @@ void AVarBoundsInfo::computerArrPointers(ProgramInfo *PI, } for (auto TBK : TmpBKeys) { - ProgramVar *TmpPVar = getProgramVar(TBK); - if (TmpPVar != nullptr) { - if (isa(TmpPVar->getScope())) { - CtxSensBKeys.insert(TBK); - } - if (isa(TmpPVar->getScope())) { - CtxSensBKeys.insert(TBK); - } + if (ProgramVar *TmpPVar = getProgramVar(TBK)) { + if (isa(TmpPVar->getScope())) + ArrPointerBoundsKey.insert(TBK); + if (isa(TmpPVar->getScope())) + ArrPointerBoundsKey.insert(TBK); } } - ArrPointers.insert(CtxSensBKeys.begin(), CtxSensBKeys.end()); + // All BoundsKey that have bounds are also array pointers. + for (auto &T : this->BInfo) + ArrPointerBoundsKey.insert(T.first); } -void AVarBoundsInfo::getBoundsNeededArrPointers( - const std::set &ArrPtrs, std::set &AB) { - // Next, get the ARR pointers that has bounds. - // These are pointers with bounds. +// Find the set of array pointers that need bounds. This is computed as all +// array pointers that do not currently have a bound, have an invalid bound, +// or have an impossible bound. +void AVarBoundsInfo::getBoundsNeededArrPointers(std::set &AB) const { + // Get the ARR pointers that have bounds. std::set ArrWithBounds; for (auto &T : BInfo) { ArrWithBounds.insert(T.first); @@ -1241,10 +1455,12 @@ void AVarBoundsInfo::getBoundsNeededArrPointers( ArrWithBounds.insert(PointersWithImpossibleBounds.begin(), PointersWithImpossibleBounds.end()); - // This are the array atoms that need bounds. - // i.e., AB = ArrPtrs - ArrPtrsWithBounds. - std::set_difference(ArrPtrs.begin(), ArrPtrs.end(), ArrWithBounds.begin(), - ArrWithBounds.end(), std::inserter(AB, AB.end())); + // Remove the above set of array pointers with bounds from the set of all + // array pointers to get the set of array pointers that need bounds. + // i.e., AB = ArrPointerBoundsKey - ArrPtrsWithBounds. + std::set_difference(ArrPointerBoundsKey.begin(), ArrPointerBoundsKey.end(), + ArrWithBounds.begin(), ArrWithBounds.end(), + std::inserter(AB, AB.end())); } // We first propagate all the bounds information from explicit @@ -1260,94 +1476,61 @@ void AVarBoundsInfo::getBoundsNeededArrPointers( // In the above case, we use n as a potential count bounds for arr. // Note: we only use potential bounds for a variable when none of its // predecessors have bounds. -bool AVarBoundsInfo::performFlowAnalysis(ProgramInfo *PI) { - bool RetVal = false; +void AVarBoundsInfo::performFlowAnalysis(ProgramInfo *PI) { auto &PStats = PI->getPerfStats(); - PStats.startArrayBoundsInferenceTime(); - AvarBoundsInference ABI(this); - // First get all the pointer vars which are ARRs - std::set ArrPointers; - NtArrPointerBoundsKey.clear(); - computerArrPointers(PI, ArrPointers); - // Repopulate array bounds key. - ArrPointerBoundsKey.clear(); - ArrPointerBoundsKey.insert(ArrPointers.begin(), ArrPointers.end()); - // All BoundsKey that has bounds are also array pointers. - for (auto &T : this->BInfo) { - ArrPointerBoundsKey.insert(T.first); - } + // First get all the pointer vars which are ARRs. Results is stored in the + // field ArrPointerBoundsKey. This also populates some other sets that seem to + // only be used for gather statistics. + computeArrPointers(PI); // Keep only highest priority bounds. - // Any thing changed? which means bounds of a variable changed - // Which means we need to recompute the flow based bounds for - // all arrays that have flow based bounds. - keepHighestPriorityBounds(ArrPointerBoundsKey); + keepHighestPriorityBounds(); + // Remove flow inferred bounds, if exist for all the array pointers. for (auto TBK : ArrPointerBoundsKey) removeBounds(TBK, FlowInferred); - std::set ArrNeededBounds, ArrNeededBoundsNew, TmpArrNeededBounds; - ArrNeededBounds.clear(); - - getBoundsNeededArrPointers(ArrPointers, ArrNeededBounds); - - bool OuterChanged, InnerChanged; - std::vector FromBVals; - // We first infer with using only flow information - // i.e., without using any potential bounds. - FromBVals.push_back(false); - // Next, we try using potential bounds. - FromBVals.push_back(true); + std::set ArrNeededBounds; + getBoundsNeededArrPointers(ArrNeededBounds); // Now compute the bounds information of all the ARR pointers that need it. // We iterate until there are no new array variables whose bounds are found. // The expectation is every iteration we will find bounds for at least one // array variable. - TmpArrNeededBounds = ArrNeededBounds; - OuterChanged = !ArrNeededBounds.empty(); + bool OuterChanged = !ArrNeededBounds.empty(); while (OuterChanged) { - TmpArrNeededBounds = ArrNeededBounds; - for (auto FromPB : FromBVals) { - InnerChanged = !ArrNeededBounds.empty(); + std::set TmpArrNeededBounds = ArrNeededBounds; + // We first infer with using only flow information i.e., without using any + // potential bounds. Next, we try using potential bounds. + // TODO: Doing this with a loop feels kind of silly. I should pull the while + // loop into a new function that takes a bool parameter and just call + // it twice. I'll do this if I can think of a meaningful name for the + // new function. + for (bool FromPB : std::vector({false, true})) { + bool InnerChanged = !ArrNeededBounds.empty(); while (InnerChanged) { - // Clear all inferred bounds. - ABI.clearInferredBounds(); + AvarBoundsInference ABI(this); // Regular flow inference (with no edges between callers and callees). - performWorkListInference(ArrNeededBounds, this->ProgVarGraph, ABI, - FromPB); - - // Converge using local bounds (i.e., within each function). - // From all the sets of bounds computed for various array variables. - // Intersect them and find the common bound variable. - ABI.convergeInferredBounds(); - - ArrNeededBoundsNew.clear(); - getBoundsNeededArrPointers(ArrPointers, ArrNeededBoundsNew); - // Now propagate the bounds information from context-sensitive keys - // to original keys (i.e., edges from callers to callees are present, - // but no local edges) - performWorkListInference(ArrNeededBoundsNew, this->CtxSensProgVarGraph, - ABI, FromPB); - - ABI.convergeInferredBounds(); + performWorkListInference(this->ProgVarGraph, ABI, FromPB); + + // Now propagate the bounds information from context-sensitive keys to + // original keys (i.e., edges from callers to callees are present, but no + // local edges). + performWorkListInference(this->CtxSensProgVarGraph, ABI, FromPB); + // Now clear all inferred bounds so that context-sensitive nodes do not // interfere with each other. ABI.clearInferredBounds(); - ArrNeededBoundsNew.clear(); - // Get array variables that still need bounds. - getBoundsNeededArrPointers(ArrPointers, ArrNeededBoundsNew); // Now propagate the bounds information from normal keys to // context-sensitive keys. - performWorkListInference(ArrNeededBoundsNew, - this->RevCtxSensProgVarGraph, ABI, FromPB); + performWorkListInference(this->RevCtxSensProgVarGraph, ABI, FromPB); - ABI.convergeInferredBounds(); - ArrNeededBoundsNew.clear(); // Get array variables that still need bounds. - getBoundsNeededArrPointers(ArrPointers, ArrNeededBoundsNew); + std::set ArrNeededBoundsNew; + getBoundsNeededArrPointers(ArrNeededBoundsNew); // Did we find bounds for new array variables? InnerChanged = (ArrNeededBounds != ArrNeededBoundsNew); @@ -1364,14 +1547,13 @@ bool AVarBoundsInfo::performFlowAnalysis(ProgramInfo *PI) { } PStats.endArrayBoundsInferenceTime(); - return RetVal; } -bool AVarBoundsInfo::keepHighestPriorityBounds(std::set &ArrPtrs) { - bool FoundBounds = false; + +bool AVarBoundsInfo::keepHighestPriorityBounds() { bool HasChanged = false; - for (auto BK : ArrPtrs) { - FoundBounds = false; + for (auto BK : ArrPointerBoundsKey) { + bool FoundBounds = false; for (BoundsPriority P : PrioList) { if (FoundBounds) { // We already found bounds. So delete these bounds. @@ -1384,11 +1566,29 @@ bool AVarBoundsInfo::keepHighestPriorityBounds(std::set &ArrPtrs) { return HasChanged; } +void AVarBoundsInfo::dumpBounds() { + llvm::errs() << "Current Array Bounds: \n"; + for (auto BK : ArrPointerBoundsKey) { + ProgramVar *PV = getProgramVar(BK); + ABounds *B = getBounds(BK); + std::string Name = PV ? PV->verboseStr() : "TMP"; + std::string Bounds = B ? B->mkString(this) : "NO_BOUNDS"; + llvm::errs() << Name << " " << Bounds << "\n"; + } + llvm::errs() << "\n"; +} + void AVarBoundsInfo::dumpAVarGraph(const std::string &DFPath) { - std::error_code Err; - llvm::raw_fd_ostream DotFile(DFPath, Err); - llvm::WriteGraph(DotFile, ProgVarGraph); - DotFile.close(); + auto DumpGraph = [DFPath](AVarGraph &G, std::string N) { + std::error_code Err; + llvm::raw_fd_ostream DotFile(N + "_" + DFPath, Err); + llvm::WriteGraph(DotFile, G); + DotFile.close(); + }; + DumpGraph(ProgVarGraph, "ProgVar"); + DumpGraph(CtxSensProgVarGraph, "CtxSen"); + DumpGraph(RevCtxSensProgVarGraph, "RevCtxSen"); + DumpGraph(LowerBoundGraph, "Invalid"); } bool AVarBoundsInfo::isFunctionReturn(BoundsKey BK) { @@ -1397,38 +1597,30 @@ bool AVarBoundsInfo::isFunctionReturn(BoundsKey BK) { void AVarBoundsInfo::printStats(llvm::raw_ostream &O, const CVarSet &SrcCVarSet, bool JsonFormat) const { - std::set InSrcBKeys, InSrcArrBKeys, Tmp; + std::set InSrcBKeys; for (auto *C : SrcCVarSet) { if (C->isForValidDecl() && C->hasBoundsKey()) InSrcBKeys.insert(C->getBoundsKey()); } std::set NTArraysReqBnds; - NTArraysReqBnds.clear(); - auto &NTA = NtArrPointerBoundsKey; - auto &APTRS = ArrPointerBoundsKey; - for (auto NTBK : NtArrPointerBoundsKey) { - - auto *PVG = const_cast(&ProgVarGraph); - - (*PVG).visitBreadthFirst( - NTBK, [NTBK, &NTA, &NTArraysReqBnds, &APTRS](BoundsKey BK) { - if (NTA.find(BK) == NTA.end() && APTRS.find(BK) != APTRS.end()) { - NTArraysReqBnds.insert(NTBK); - } - }); + ProgVarGraph.visitBreadthFirst(NTBK, [this, NTBK, &NTArraysReqBnds](BoundsKey BK) { + if (NtArrPointerBoundsKey.find(BK) == NtArrPointerBoundsKey.end() && + ArrPointerBoundsKey.find(BK) != ArrPointerBoundsKey.end()) + NTArraysReqBnds.insert(NTBK); + }); } std::set NTArrayReqNoBounds; - NTArrayReqNoBounds.clear(); - std::set_difference( NtArrPointerBoundsKey.begin(), NtArrPointerBoundsKey.end(), NTArraysReqBnds.begin(), NTArraysReqBnds.end(), std::inserter(NTArrayReqNoBounds, NTArrayReqNoBounds.begin())); + std::set InSrcArrBKeys; findIntersection(InProgramArrPtrBoundsKeys, InSrcBKeys, InSrcArrBKeys); + std::set Tmp; if (!JsonFormat) { findIntersection(ArrPointerBoundsKey, InSrcArrBKeys, Tmp); O << "NumPointersNeedBounds:" << Tmp.size() << ",\n"; @@ -1458,7 +1650,7 @@ bool AVarBoundsInfo::areSameProgramVar(BoundsKey B1, BoundsKey B2) { ProgramVar *P1 = getProgramVar(B1); ProgramVar *P2 = getProgramVar(B2); return P1->isNumConstant() && P2->isNumConstant() && - P1->getVarName() == P2->getVarName(); + P1->getConstantVal() == P2->getConstantVal(); } return B1 == B2; } @@ -1484,3 +1676,71 @@ std::set AVarBoundsInfo::getCtxSensFieldBoundsKey(Expr *E, } return Ret; } + +// Adds declared bounds for all constant sized arrays. This needs to happen +// after constraint solving because the bounds for a _Nt_checked array and a +// _Checked array are different even if they are written with the same length. +// The Checked C bounds for a _Nt_checked array do not include the null +// terminator, but the length as written in the source code does. +void AVarBoundsInfo::addConstantArrayBounds(ProgramInfo &I) { + for (auto VarEntry : I.getVarMap()) { + if (auto *VarPCV = dyn_cast(VarEntry.second)) { + if (VarPCV->hasBoundsKey() && VarPCV->isConstantArr()) { + // Lookup the declared size of the array. This is known because it is + // written in the source and was stored during constraint generation. + unsigned int ConstantCount = VarPCV->getConstantArrSize(); + + // Check if this array solved to NTARR. If it did, subtract one from the + // length to account for the null terminator. + const EnvironmentMap &Env = I.getConstraints().getVariables(); + if (VarPCV->isNtConstantArr(Env)) { + assert("Size zero constant array should not solve to NTARR" && + ConstantCount != 0); + ConstantCount--; + } + + // Insert this as a declared constant count bound for the constraint + // variable. + BoundsKey CBKey = getConstKey(ConstantCount); + ABounds *NB = new CountBound(CBKey); + insertDeclaredBounds(VarPCV->getBoundsKey(), NB); + } + } + } +} + +PVConstraint *AVarBoundsInfo::getConstraintVariable(const ProgramInfo *PI, + BoundsKey BK) const { + // Regular variables. + const auto &VariableMap = DeclVarMap.right(); + if (VariableMap.find(BK) != VariableMap.end()) { + const PersistentSourceLoc &PSL = VariableMap.at(BK); + return dyn_cast(PI->getVarMap().at(PSL)); + } + + // Function parameters + const auto &ParamMap = ParamDeclVarMap.right(); + if (ParamMap.find(BK) != ParamMap.end()) { + auto &ParmTup = ParamMap.at(BK); + std::string FuncName = std::get<0>(ParmTup); + std::string FileName = std::get<1>(ParmTup); + bool IsStatic = std::get<2>(ParmTup); + unsigned ParmNum = std::get<3>(ParmTup); + + FVConstraint *FV = PI->getFuncConstraint(FuncName, FileName, IsStatic); + return FV->getExternalParam(ParmNum); + } + + // Function returns. + const auto &ReturnMap = FuncDeclVarMap.right(); + if (ReturnMap.find(BK) != ReturnMap.end()) { + auto &FuncRet = ReturnMap.at(BK); + std::string FuncName = std::get<0>(FuncRet); + std::string FileName = std::get<1>(FuncRet); + bool IsStatic = std::get<2>(FuncRet); + + FVConstraint *FV = PI->getFuncConstraint(FuncName, FileName, IsStatic); + return FV->getExternalReturn(); + } + return nullptr; +} diff --git a/clang/lib/3C/AVarGraph.cpp b/clang/lib/3C/AVarGraph.cpp index c20ccf6eafac..3527d26c4381 100644 --- a/clang/lib/3C/AVarGraph.cpp +++ b/clang/lib/3C/AVarGraph.cpp @@ -62,7 +62,9 @@ llvm::DOTGraphTraits::getNodeLabel(const DataNode *Node, BoundsKey BK = Node->getData(); ProgramVar *Tmp = ABInfo->getProgramVar(BK); std::string LblStr = "Temp"; - if (Tmp != nullptr) + if (BK == 0) + LblStr = "Invalid"; + else if (Tmp != nullptr) LblStr = Tmp->verboseStr(); bool IsArrPtr = ABInfo->ArrPointerBoundsKey.find(BK) != ABInfo->ArrPointerBoundsKey.end(); diff --git a/clang/lib/3C/ArrayBoundsInferenceConsumer.cpp b/clang/lib/3C/ArrayBoundsInferenceConsumer.cpp index 2131fe51ee39..640a6fd24336 100644 --- a/clang/lib/3C/ArrayBoundsInferenceConsumer.cpp +++ b/clang/lib/3C/ArrayBoundsInferenceConsumer.cpp @@ -91,7 +91,7 @@ static bool needArrayBounds(const ConstraintVariable *CV, const EnvironmentMap &E) { if (CV->hasArr(E, 0)) { const PVConstraint *PV = dyn_cast(CV); - return !PV || PV->isTopCvarUnsizedArr(); + return !PV || PV->isTopAtomUnsizedArr(); } return false; } @@ -100,7 +100,7 @@ static bool needNTArrayBounds(const ConstraintVariable *CV, const EnvironmentMap &E) { if (CV->hasNtArr(E, 0)) { const PVConstraint *PV = dyn_cast(CV); - return !PV || PV->isTopCvarUnsizedArr(); + return !PV || PV->isTopAtomUnsizedArr(); } return false; } @@ -118,16 +118,20 @@ static bool needArrayBounds(Expr *E, ProgramInfo &Info, ASTContext *C) { return false; } +static bool +needArrayBounds(ConstraintVariable *CV, ProgramInfo &Info, bool IsNtArr) { + const auto &E = Info.getConstraints().getVariables(); + if (IsNtArr) + return needNTArrayBounds(CV, E); + return needArrayBounds(CV, E); +} + static bool needArrayBounds(Decl *D, ProgramInfo &Info, ASTContext *C, bool IsNtArr) { - const auto &E = Info.getConstraints().getVariables(); CVarOption CVar = Info.getVariable(D, C); if (CVar.hasValue()) { ConstraintVariable &CV = CVar.getValue(); - if ((!IsNtArr && needArrayBounds(&CV, E)) || - (IsNtArr && needNTArrayBounds(&CV, E))) - return true; - return false; + return needArrayBounds(&CV, Info, IsNtArr); } return false; } @@ -140,28 +144,16 @@ static bool needArrayBounds(Decl *D, ProgramInfo &Info, ASTContext *C) { // Map that contains association of allocator functions and indexes of // parameters that correspond to the size of the object being assigned. static std::map> AllocatorSizeAssoc = { - {"malloc", {0}}, {"calloc", {0, 1}}}; + {"malloc", {0}}, {"calloc", {0, 1}} , {"realloc" , {1}}}; // Get the name of the function called by this call expression. -static std::string getCalledFunctionName(const Expr *E) { - const CallExpr *CE = dyn_cast(E); - assert(CE && "The provided expression should be a call expression."); +static std::string getCalledFunctionName(const CallExpr *CE) { const FunctionDecl *CalleeDecl = dyn_cast(CE->getCalleeDecl()); if (CalleeDecl && CalleeDecl->getDeclName().isIdentifier()) return std::string(CalleeDecl->getName()); return ""; } -/*bool tryGetBoundsKeyVar(Expr *E, BoundsKey &BK, ProgramInfo &Info, - ASTContext *Context) { - ConstraintResolver CR(Info, Context); - CVarSet CVs = CR.getExprConstraintVarsSet(E); - auto &ABInfo = Info.getABoundsInfo(); - return CR.resolveBoundsKey(CVs, BK) || - ABInfo.tryGetVariable(E, *Context, BK); - -}*/ - bool tryGetBoundsKeyVar(Decl *D, BoundsKey &BK, ProgramInfo &Info, ASTContext *Context) { ConstraintResolver CR(Info, Context); @@ -189,24 +181,27 @@ bool tryGetValidBoundsKey(Expr *E, BoundsKey &BK, ProgramInfo &I, return Ret; } +bool hasValidBoundsKey(Expr *E, ProgramInfo &I, ASTContext *C) { + BoundsKey Unused; + return tryGetValidBoundsKey(E, Unused, I, C); +} + // Check if the provided expression E is a call to one of the known // memory allocators. Will only return true if the argument to the call // is a simple expression, and then organizes the ArgVals for determining // a possible bound -static bool isAllocatorCall(Expr *E, std::string &FName, ProgramInfo &I, - ASTContext *C, std::vector &ArgVals) { +static bool isAllocatorCall(Expr *E, ProgramInfo &I, ASTContext *C, + std::vector &ArgVals) { bool RetVal = false; if (CallExpr *CE = dyn_cast(removeAuxillaryCasts(E))) if (CE->getCalleeDecl() != nullptr) { // Is this a call to a named function? - FName = getCalledFunctionName(CE); + std::string FName = getCalledFunctionName(CE); // check if the called function is a known allocator? if (AllocatorSizeAssoc.find(FName) != AllocatorSizeAssoc.end()) { RetVal = true; - BoundsKey Tmp; // First get all base expressions. std::vector BaseExprs; - BaseExprs.clear(); for (auto Pidx : AllocatorSizeAssoc[FName]) { Expr *PExpr = CE->getArg(Pidx)->IgnoreParenCasts(); BinaryOperator *BO = dyn_cast(PExpr); @@ -217,7 +212,7 @@ static bool isAllocatorCall(Expr *E, std::string &FName, ProgramInfo &I, BaseExprs.push_back(BO->getRHS()); } else if (UExpr && UExpr->getKind() == UETT_SizeOf) { BaseExprs.push_back(UExpr); - } else if (tryGetValidBoundsKey(PExpr, Tmp, I, C)) { + } else if (hasValidBoundsKey(PExpr, I, C)) { BaseExprs.push_back(PExpr); } else { RetVal = false; @@ -227,13 +222,13 @@ static bool isAllocatorCall(Expr *E, std::string &FName, ProgramInfo &I, // Check if each of the expression is either sizeof or a DeclRefExpr if (RetVal && !BaseExprs.empty()) { - for (auto *TmpE : BaseExprs) { - TmpE = TmpE->IgnoreParenCasts(); + for (auto *BaseE : BaseExprs) { + BaseE = BaseE->IgnoreParenCasts(); UnaryExprOrTypeTraitExpr *UExpr = - dyn_cast(TmpE); + dyn_cast(BaseE); if ((UExpr && UExpr->getKind() == UETT_SizeOf) || - tryGetValidBoundsKey(TmpE, Tmp, I, C)) { - ArgVals.push_back(TmpE); + hasValidBoundsKey(BaseE, I, C)) { + ArgVals.push_back(BaseE); } else { RetVal = false; break; @@ -250,17 +245,18 @@ static void handleAllocatorCall(QualType LHSType, BoundsKey LK, Expr *E, auto &AVarBInfo = Info.getABoundsInfo(); auto &ABStats = AVarBInfo.getBStats(); ConstraintResolver CR(Info, Context); - std::string FnName; std::vector ArgVals; - // is the RHS expression a call to allocator function? - if (isAllocatorCall(E, FnName, Info, Context, ArgVals)) { + // Is the RHS expression a call to allocator function? isAllocatorCall + // mutates ArgVals, populating it with the argument expressions for the + // allocator call. + if (isAllocatorCall(E, Info, Context, ArgVals)) { BoundsKey RK; bool FoundSingleKeyInAllocExpr = false; // We consider everything as byte_count unless we see a sizeof // expression in which case if the type matches we use count bounds. bool IsByteBound = true; - for (auto *TmpE : ArgVals) { - UnaryExprOrTypeTraitExpr *Arg = dyn_cast(TmpE); + for (auto *ArgE : ArgVals) { + UnaryExprOrTypeTraitExpr *Arg = dyn_cast(ArgE); if (Arg && Arg->getKind() == UETT_SizeOf) { QualType STy = Context->getPointerType(Arg->getTypeOfArgument()); // This is a count bound. @@ -270,7 +266,7 @@ static void handleAllocatorCall(QualType LHSType, BoundsKey LK, Expr *E, FoundSingleKeyInAllocExpr = false; break; } - } else if (tryGetValidBoundsKey(TmpE, RK, Info, Context)) { + } else if (tryGetValidBoundsKey(ArgE, RK, Info, Context)) { // Is this variable? if (!FoundSingleKeyInAllocExpr) { FoundSingleKeyInAllocExpr = true; @@ -448,6 +444,11 @@ bool GlobalABVisitor::VisitFunctionDecl(FunctionDecl *FD) { std::map> ParamNtArrays; std::map> LengthParams; + // The FVConstraint is needed to access the external parameter + // ConstraintVariables. External CVs are required because these + // are there variables can solve to a checked array type when the + // parameter has an itype while the internals will solve to WILD. + FVConstraint *FV = Info.getFuncConstraint(FD, Context); for (unsigned I = 0; I < FT->getNumParams(); I++) { ParmVarDecl *PVD = FD->getParamDecl(I); BoundsKey PK; @@ -459,14 +460,14 @@ bool GlobalABVisitor::VisitFunctionDecl(FunctionDecl *FD) { // Here, we are using heuristics. So we only use heuristics when // there are no bounds already computed. if (!ABInfo.getBounds(PK)) { - if (needArrayBounds(PVD, Info, Context, true)) { - // Is this an NTArray? + PVConstraint *ParamCV = FV->getExternalParam(I); + const EnvironmentMap &Env = Info.getConstraints().getVariables(); + // Is this an NTArray? + if (needNTArrayBounds(ParamCV, Env)) ParamNtArrays[I] = PVal; - } - if (needArrayBounds(PVD, Info, Context, false)) { - // Is this an array? + // Is this an array? + if (needArrayBounds(ParamCV, Env)) ParamArrays[I] = PVal; - } } // If this is a length field? @@ -570,9 +571,6 @@ void LocalVarABVisitor::handleAssignment(BoundsKey LK, QualType LHSType, bool LocalVarABVisitor::handleBinAssign(BinaryOperator *O) { Expr *LHS = O->getLHS()->IgnoreParenCasts(); Expr *RHS = O->getRHS()->IgnoreParenCasts(); - ConstraintResolver CR(Info, Context); - std::string FnName; - std::vector ArgVals; BoundsKey LK; // is the RHS expression a call to allocator function? if (needArrayBounds(LHS, Info, Context) && @@ -580,15 +578,17 @@ bool LocalVarABVisitor::handleBinAssign(BinaryOperator *O) { handleAssignment(LK, LHS->getType(), RHS); } + // TODO: Why is this done only for the RHS of assignment? Why not place this + // in a VisitConditionalOperator function? // Any parameter directly used as a condition in ternary expression // cannot be length. if (ConditionalOperator *CO = dyn_cast(RHS)) - addUsedParmVarDecl(CO->getCond()); + addNonLengthParameter(CO->getCond()); return true; } -void LocalVarABVisitor::addUsedParmVarDecl(Expr *CE) { +void LocalVarABVisitor::addNonLengthParameter(Expr *CE) { if (DeclRefExpr *DRE = dyn_cast(CE->IgnoreParenCasts())) if (ParmVarDecl *PVD = dyn_cast(DRE->getDecl())) NonLengthParameters.insert(PVD); @@ -622,8 +622,8 @@ bool LocalVarABVisitor::VisitBinaryOperator(BinaryOperator *BO) { BinaryOperator::Opcode BOpcode = BO->getOpcode(); // Is this not a valid bin op for a potential length parameter? if (!isValidBinOpForLen(BOpcode)) { - addUsedParmVarDecl(BO->getLHS()); - addUsedParmVarDecl(BO->getRHS()); + addNonLengthParameter(BO->getLHS()); + addNonLengthParameter(BO->getRHS()); } if (BOpcode == BinaryOperator::Opcode::BO_Assign) { handleBinAssign(BO); @@ -632,7 +632,7 @@ bool LocalVarABVisitor::VisitBinaryOperator(BinaryOperator *BO) { } bool LocalVarABVisitor::VisitArraySubscriptExpr(ArraySubscriptExpr *E) { - addUsedParmVarDecl(E->getIdx()); + addNonLengthParameter(E->getIdx()); return true; } @@ -663,7 +663,7 @@ bool LocalVarABVisitor::VisitSwitchStmt(SwitchStmt *S) { } // Check if the provided parameter cannot be a length of an array. -bool LocalVarABVisitor::isNonLengthParameter(ParmVarDecl *PVD) { +bool LocalVarABVisitor::isNonLengthParameter(ParmVarDecl *PVD) const { if (PVD->getType().getTypePtr()->isEnumeralType()) return true; return NonLengthParameters.find(PVD) != NonLengthParameters.end(); @@ -698,19 +698,10 @@ void addMainFuncHeuristic(ASTContext *C, ProgramInfo &I, FunctionDecl *FD) { // Given a variable I, this visitor collects all the variables that are used as // RHS operand of < and I >= expression. // i.e., for all I < X expressions, it collects X. -class ComparisionVisitor : public RecursiveASTVisitor { +class ComparisonVisitor : public RecursiveASTVisitor { public: - explicit ComparisionVisitor(ProgramInfo &In, ASTContext *AC, BoundsKey I, - std::set &PossB) - : I(In), C(AC), IndxBKey(I), PB(PossB), CurrStmt(nullptr) { - CR = new ConstraintResolver(In, AC); - } - virtual ~ComparisionVisitor() { - if (CR != nullptr) { - delete (CR); - CR = nullptr; - } - } + explicit ComparisonVisitor(ProgramInfo &In, ASTContext *AC, BoundsKey I) + : I(In), C(AC), IndxBKey(I), PossibleBounds(), CurrStmt(nullptr) {} // Here, we save the most recent statement we have visited. // This is a way to keep track of the statement to which currently @@ -749,35 +740,36 @@ class ComparisionVisitor : public RecursiveASTVisitor { // return -1; // } // arr[i] = .. - IsRKeyBound &= (CurrStmt != nullptr && isa(CurrStmt)); + IsRKeyBound &= isa_and_nonnull(CurrStmt); } if (IsRKeyBound) - PB.insert(RKey); + PossibleBounds.insert(RKey); } } return true; } + const std::set &getPossibleBounds() const { + return PossibleBounds; + } + private: ProgramInfo &I; ASTContext *C; // Index variable used in dereference. - BoundsKey IndxBKey; - // Possible Bounds. - std::set &PB; - // Helper objects. - ConstraintResolver *CR; + const BoundsKey IndxBKey; + std::set PossibleBounds; // Current statement: The statement to which the processing // node belongs. This is to avoid walking the AST. Stmt *CurrStmt; }; LengthVarInference::LengthVarInference(ProgramInfo &In, ASTContext *AC, - FunctionDecl *F) - : I(In), C(AC), FD(F), CurBB(nullptr) { - - Cfg = CFG::buildCFG(nullptr, FD->getBody(), AC, CFG::BuildOptions()); + FunctionDecl *F) : + I(In), C(AC), CurBB(nullptr), + Cfg(CFG::buildCFG(nullptr, F->getBody(), AC, CFG::BuildOptions())), + CDG(Cfg.get()) { for (auto *CBlock : *(Cfg.get())) { for (auto &CfgElem : *CBlock) { if (CfgElem.getKind() == clang::CFGElement::Statement) { @@ -786,21 +778,6 @@ LengthVarInference::LengthVarInference(ProgramInfo &In, ASTContext *AC, } } } - - CDG = new ControlDependencyCalculator(Cfg.get()); - - CR = new ConstraintResolver(I, C); -} - -LengthVarInference::~LengthVarInference() { - if (CDG != nullptr) { - delete (CDG); - CDG = nullptr; - } - if (CR != nullptr) { - delete (CR); - CR = nullptr; - } } void LengthVarInference::VisitStmt(Stmt *St) { @@ -861,30 +838,27 @@ void LengthVarInference::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { VisitArraySubscriptExpr(SubASE); return; } - //auto BaseCVars = CR->getExprConstraintVars(BE); + // Next get the index used. Expr *IdxExpr = ASE->getIdx()->IgnoreParenCasts(); - //auto IdxCVars = CR->getExprConstraintVars(IdxExpr); - BoundsKey BasePtr, IdxKey; - auto &ABI = I.getABoundsInfo(); // Get the bounds key of the base and index. + BoundsKey BasePtr, IdxKey; if (tryGetValidBoundsKey(BE, BasePtr, I, C) && tryGetValidBoundsKey(IdxExpr, IdxKey, I, C)) { - std::set PossibleLens; - PossibleLens.clear(); - ComparisionVisitor CV(I, C, IdxKey, PossibleLens); - auto &CDNodes = CDG->getControlDependencies(CurBB); + auto &ABI = I.getABoundsInfo(); + auto &CDNodes = CDG.getControlDependencies(CurBB); if (!CDNodes.empty()) { // Next try to find all the nodes that the CurBB is // control dependent on. // For each of the control dependent node, check if we are comparing the // index variable with another variable. + ComparisonVisitor CV(I, C, IdxKey); for (auto &CDGNode : CDNodes) { // Collect the possible length bounds keys. CV.TraverseStmt(CDGNode->getTerminatorStmt()); } - ABI.updatePotentialCountBounds(BasePtr, PossibleLens); + ABI.updatePotentialCountBounds(BasePtr, CV.getPossibleBounds()); } else { ABI.updatePotentialCountPOneBounds(BasePtr, {IdxKey}); } @@ -893,26 +867,27 @@ void LengthVarInference::VisitArraySubscriptExpr(ArraySubscriptExpr *ASE) { void handleArrayVariablesBoundsDetection(ASTContext *C, ProgramInfo &I, bool UseHeuristics) { - // Run array bounds - for (auto FuncName : AllocatorFunctions) { + // This is adding function names provided to --use-malloc to the set of + // allocator functions. It assumes that the first argument is always the size, + // which should be correct if the function have the same interface as malloc. + for (auto FuncName : _3COpts.AllocatorFunctions) AllocatorSizeAssoc[FuncName] = {0}; - } + GlobalABVisitor GlobABV(C, I); + LocalVarABVisitor LocABV(C, I); TranslationUnitDecl *TUD = C->getTranslationUnitDecl(); - LocalVarABVisitor LFV = LocalVarABVisitor(C, I); - bool GlobalTraversed; // First visit all the structure members. for (const auto &D : TUD->decls()) { - GlobalTraversed = false; + bool GlobalTraversed = false; if (FunctionDecl *FD = dyn_cast(D)) { if (FD->hasBody() && FD->isThisDeclarationADefinition()) { // Try to guess the bounds information for function locals. Stmt *Body = FD->getBody(); - LFV.TraverseStmt(Body); + LocABV.TraverseStmt(Body); if (UseHeuristics) { // Set information collected after analyzing the function body. - GlobABV.setParamHeuristicInfo(&LFV); + GlobABV.setParamHeuristicInfo(&LocABV); GlobABV.TraverseDecl(D); } addMainFuncHeuristic(C, I, FD); diff --git a/clang/lib/3C/CMakeLists.txt b/clang/lib/3C/CMakeLists.txt index 91733f687ba9..11a38afa6782 100644 --- a/clang/lib/3C/CMakeLists.txt +++ b/clang/lib/3C/CMakeLists.txt @@ -43,7 +43,9 @@ add_clang_library(clang3C CtxSensAVarBounds.cpp DeclRewriter.cpp IntermediateToolHook.cpp + LowerBoundAssignment.cpp MappingVisitor.cpp + MultiDecls.cpp PersistentSourceLoc.cpp ProgramInfo.cpp ProgramVar.cpp diff --git a/clang/lib/3C/CastPlacement.cpp b/clang/lib/3C/CastPlacement.cpp index cbadd8133cbe..9d0ff2798209 100644 --- a/clang/lib/3C/CastPlacement.cpp +++ b/clang/lib/3C/CastPlacement.cpp @@ -53,20 +53,29 @@ bool CastPlacementVisitor::VisitCallExpr(CallExpr *CE) { // Avoid adding incorrect casts to generic function arguments by // removing implicit casts when on arguments with a consistently // used generic type. + ConstraintVariable *TypeVar = nullptr; Expr *ArgExpr = A; if (FD && PIdx < FD->getNumParams()) { const int TyVarIdx = FV->getExternalParam(PIdx)->getGenericIndex(); - if (TypeVars.find(TyVarIdx) != TypeVars.end() && - TypeVars[TyVarIdx] != nullptr) - ArgExpr = ArgExpr->IgnoreImpCasts(); + // Check if local type vars are available + if (TypeVars.find(TyVarIdx) != TypeVars.end()) { + TypeVar = TypeVars[TyVarIdx].getConstraint( + Info.getConstraints().getVariables()); + } } + if (TypeVar != nullptr) + ArgExpr = ArgExpr->IgnoreImpCasts(); CVarSet ArgConstraints = CR.getExprConstraintVarsSet(ArgExpr); for (auto *ArgC : ArgConstraints) { + // If the function takes a void *, we already know about the wildness, + // so allow the implicit cast. + if (TypeVar == nullptr && FV->getExternalParam(PIdx)->isVoidPtr()) + continue; CastNeeded CastKind = needCasting( ArgC, ArgC, FV->getInternalParam(PIdx), FV->getExternalParam(PIdx)); if (CastKind != NO_CAST) { - surroundByCast(FV->getExternalParam(PIdx), CastKind, A); + surroundByCast(FV->getExternalParam(PIdx), TypeVar, CastKind, A); ExprsWithCast.insert(ignoreCheckedCImplicit(A)); break; } @@ -91,7 +100,7 @@ bool CastPlacementVisitor::VisitCallExpr(CallExpr *CE) { FV->getExternalReturn(), DstC, DstC); if (ExprsWithCast.find(CE) == ExprsWithCast.end() && CastKind != NO_CAST) { - surroundByCast(DstC, CastKind, CE); + surroundByCast(DstC, nullptr, CastKind, CE); ExprsWithCast.insert(ignoreCheckedCImplicit(CE)); break; } @@ -151,40 +160,49 @@ CastPlacementVisitor::CastNeeded CastPlacementVisitor::needCasting( // when placed around the expression being cast. std::pair CastPlacementVisitor::getCastString(ConstraintVariable *Dst, + ConstraintVariable *TypeVar, CastNeeded CastKind) { switch (CastKind) { case CAST_NT_ARRAY: - return std::make_pair( - "((" + Dst->mkString(Info.getConstraints(), false) + ")", ")"); + return std::make_pair("((" + + Dst->mkString(Info.getConstraints(), + MKSTRING_OPTS(EmitName = false)) + + ")", + ")"); case CAST_TO_WILD: return std::make_pair("((" + Dst->getRewritableOriginalTy() + ")", ")"); case CAST_TO_CHECKED: { + // Needed as default to TypeVar branch below, reset otherwise. + std::string Type = "_Ptr<"; std::string Suffix = ")"; if (const auto *DstPVC = dyn_cast(Dst)) { assert("Checked cast not to a pointer" && !DstPVC->getCvars().empty()); ConstAtom *CA = Info.getConstraints().getAssignment(DstPVC->getCvars().at(0)); - // Writing an _Assume_bounds_cast to an array type requires inserting - // the bounds for destination array. These can come from the source - // code or the infered bounds. If neither source is available, use empty - // bounds. - if (isa(CA) || isa(CA)) { - std::string Bounds = ""; - if (DstPVC->srcHasBounds()) - Bounds = DstPVC->getBoundsStr(); - else if (DstPVC->hasBoundsKey()) - Bounds = ABRewriter.getBoundsString(DstPVC, nullptr, true); - if (Bounds.empty()) - Bounds = "byte_count(0)"; - - Suffix = ", " + Bounds + ")"; + // TODO: Writing an _Assume_bounds_cast to an array type requires + // inserting the bounds for destination array. But the names used in src + // and dest may be different, so we need more sophisticated code to + // convert to local variable names. Use unknown bounds for now. + if (isa(CA)) { + Type = "_Array_ptr<"; + Suffix = ", bounds(unknown))"; + } else if (isa(CA)) { + Type = "_Nt_array_ptr<"; + Suffix = ", bounds(unknown))"; } } - return std::make_pair("_Assume_bounds_cast<" + - Dst->mkString(Info.getConstraints(), false) + - ">(", - Suffix); + // The destination's type may be generic, which would have an out-of-scope + // type var, so use the already analysed local type var instead + if (TypeVar != nullptr) { + Type += + TypeVar->mkString(Info.getConstraints(), + MKSTRING_OPTS(EmitName = false, EmitPointee = true)) + + ">"; + } else { + Type = Dst->mkString(Info.getConstraints(), MKSTRING_OPTS(EmitName = false)); + } + return std::make_pair("_Assume_bounds_cast<" + Type + ">(", Suffix); } default: llvm_unreachable("No casting needed"); @@ -192,6 +210,7 @@ CastPlacementVisitor::getCastString(ConstraintVariable *Dst, } void CastPlacementVisitor::surroundByCast(ConstraintVariable *Dst, + ConstraintVariable *TypeVar, CastNeeded CastKind, Expr *E) { PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(E, *Context); if (!canWrite(PSL.getFileName())) { @@ -199,16 +218,16 @@ void CastPlacementVisitor::surroundByCast(ConstraintVariable *Dst, // unwritable files in common use cases. Until they are fixed, report a // warning rather than letting the main "unwritable change" error trigger // later. - clang::DiagnosticsEngine &DE = Writer.getSourceMgr().getDiagnostics(); - unsigned ErrorId = DE.getCustomDiagID( + reportCustomDiagnostic( + Writer.getSourceMgr().getDiagnostics(), DiagnosticsEngine::Warning, "3C internal error: tried to insert a cast into an unwritable file " - "(https://github.com/correctcomputation/checkedc-clang/issues/454)"); - DE.Report(E->getBeginLoc(), ErrorId); + "(https://github.com/correctcomputation/checkedc-clang/issues/454)", + E->getBeginLoc()); return; } - auto CastStrs = getCastString(Dst, CastKind); + auto CastStrs = getCastString(Dst, TypeVar, CastKind); // If E is already a cast expression, we will try to rewrite the cast instead // of adding a new expression. @@ -261,14 +280,13 @@ void CastPlacementVisitor::reportCastInsertionFailure( // FIXME: This is a warning rather than an error so that a new benchmark // failure is not introduced in Lua. // github.com/correctcomputation/checkedc-clang/issues/439 - clang::DiagnosticsEngine &DE = Context->getDiagnostics(); - unsigned ErrorId = DE.getCustomDiagID( - DiagnosticsEngine::Warning, "Unable to surround expression with cast.\n" - "Intended cast: \"%0\""); - auto ErrorBuilder = DE.Report(E->getExprLoc(), ErrorId); - ErrorBuilder.AddSourceRange( - Context->getSourceManager().getExpansionRange(E->getSourceRange())); - ErrorBuilder.AddString(CastStr); + reportCustomDiagnostic(Context->getDiagnostics(), + DiagnosticsEngine::Warning, + "Unable to surround expression with cast.\n" + "Intended cast: \"%0\"", + E->getExprLoc()) + << Context->getSourceManager().getExpansionRange(E->getSourceRange()) + << CastStr; } void CastPlacementVisitor::updateRewriteStats(CastNeeded CastKind) { diff --git a/clang/lib/3C/CheckedRegions.cpp b/clang/lib/3C/CheckedRegions.cpp index fef651e4e35a..7c6d36cebb93 100644 --- a/clang/lib/3C/CheckedRegions.cpp +++ b/clang/lib/3C/CheckedRegions.cpp @@ -230,7 +230,7 @@ bool CheckedRegionFinder::VisitCallExpr(CallExpr *C) { if (FD) { if (Info.hasTypeParamBindings(C, Context)) for (auto Entry : Info.getTypeParamBindings(C, Context)) - Wild |= (Entry.second == nullptr); + Wild |= !Entry.second.isConsistent(); auto Type = FD->getReturnType(); Wild |= (!(FD->hasPrototype() || FD->doesThisDeclarationHaveABody())) || containsUncheckedPtr(Type); @@ -339,7 +339,7 @@ bool CheckedRegionFinder::isInStatementPosition(CallExpr *C) { //TODO there are other statement positions // besides child of compound stmt auto PSL = PersistentSourceLoc::mkPSL(C, *Context); - emitCauseDiagnostic(&PSL); + emitCauseDiagnostic(PSL); return false; } @@ -430,21 +430,20 @@ void CheckedRegionFinder::markChecked(CompoundStmt *S, int Localwild) { Map[Id] = IsChecked ? IS_CHECKED : IS_UNCHECKED; } -void CheckedRegionFinder::emitCauseDiagnostic(PersistentSourceLoc *PSL) { +void CheckedRegionFinder::emitCauseDiagnostic(PersistentSourceLoc PSL) { if (Emitted.find(PSL) == Emitted.end()) { - clang::DiagnosticsEngine &DE = Context->getDiagnostics(); - unsigned ID = - DE.getCustomDiagID(DiagnosticsEngine::Warning, - "Root cause of unchecked region: Variadic Call"); SourceManager &SM = Context->getSourceManager(); llvm::ErrorOr File = - SM.getFileManager().getFile(PSL->getFileName()); + SM.getFileManager().getFile(PSL.getFileName()); if (File.getError()) return; SourceLocation SL = - SM.translateFileLineCol(*File, PSL->getLineNo(), PSL->getColSNo()); + SM.translateFileLineCol(*File, PSL.getLineNo(), PSL.getColSNo()); if (SL.isValid()) - DE.Report(SL, ID); + reportCustomDiagnostic(Context->getDiagnostics(), + DiagnosticsEngine::Warning, + "Root cause of unchecked region: Variadic Call", + SL); Emitted.insert(PSL); } } diff --git a/clang/lib/3C/ConstraintBuilder.cpp b/clang/lib/3C/ConstraintBuilder.cpp index 89db58b06cc2..cd043c32d3ce 100644 --- a/clang/lib/3C/ConstraintBuilder.cpp +++ b/clang/lib/3C/ConstraintBuilder.cpp @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// #include "clang/3C/ConstraintBuilder.h" +#include "clang/3C/LowerBoundAssignment.h" #include "clang/3C/3CGlobalOptions.h" #include "clang/3C/3CStats.h" #include "clang/3C/ArrayBoundsInferenceConsumer.h" @@ -21,139 +22,17 @@ using namespace llvm; using namespace clang; -// This class is intended to locate inline struct definitions -// in order to mark them wild or signal a warning as appropriate. -class InlineStructDetector { -public: - explicit InlineStructDetector() : LastRecordDecl(nullptr) {} - - void processRecordDecl(RecordDecl *Declaration, ProgramInfo &Info, - ASTContext *Context, ConstraintResolver CB) { - LastRecordDecl = Declaration; - if (RecordDecl *Definition = Declaration->getDefinition()) { - auto LastRecordLocation = Definition->getBeginLoc(); - FullSourceLoc FL = Context->getFullLoc(Definition->getBeginLoc()); - if (FL.isValid()) { - SourceManager &SM = Context->getSourceManager(); - FileID FID = FL.getFileID(); - const FileEntry *FE = SM.getFileEntryForID(FID); - - // Detect whether this RecordDecl is part of an inline struct. - bool IsInLineStruct = false; - Decl *D = Declaration->getNextDeclInContext(); - if (VarDecl *VD = dyn_cast_or_null(D)) { - auto VarTy = VD->getType(); - auto BeginLoc = VD->getBeginLoc(); - auto EndLoc = VD->getEndLoc(); - SourceManager &SM = Context->getSourceManager(); - IsInLineStruct = - !isPtrOrArrayType(VarTy) && !VD->hasInit() && - SM.isPointWithin(LastRecordLocation, BeginLoc, EndLoc); - } - - if (FE && FE->isValid()) { - // We only want to re-write a record if it contains - // any pointer types, to include array types. - for (const auto &F : Definition->fields()) { - auto FieldTy = F->getType(); - // If the RecordDecl is a union or in a system header - // and this field is a pointer, we need to mark it wild. - bool FieldInUnionOrSysHeader = - (FL.isInSystemHeader() || Definition->isUnion()); - // Mark field wild if the above is true and the field is a pointer. - if (isPtrOrArrayType(FieldTy) && - (FieldInUnionOrSysHeader || IsInLineStruct)) { - std::string Rsn = "Union or external struct field encountered"; - CVarOption CV = Info.getVariable(F, Context); - CB.constraintCVarToWild(CV, Rsn); - } - } - } - } - } - } - - void processVarDecl(VarDecl *VD, ProgramInfo &Info, ASTContext *Context, - ConstraintResolver CB) { - // If the last seen RecordDecl is non-null and coincides with the current - // VarDecl (i.e. via an inline struct), we proceed as follows: - // If the struct is named, do nothing. - // If the struct is anonymous: - // When alltypes is on, do nothing, but signal a warning to - // the user indicating its presence. - // When alltypes is off, mark the VarDecl WILD in order to - // ensure the converted program compiles. - if (LastRecordDecl != nullptr) { - auto LastRecordLocation = LastRecordDecl->getBeginLoc(); - auto BeginLoc = VD->getBeginLoc(); - auto EndLoc = VD->getEndLoc(); - auto VarTy = VD->getType(); - SourceManager &SM = Context->getSourceManager(); - bool IsInLineStruct = - SM.isPointWithin(LastRecordLocation, BeginLoc, EndLoc) && - isPtrOrArrayType(VarTy); - bool IsNamedInLineStruct = - IsInLineStruct && LastRecordDecl->getNameAsString() != ""; - if (IsInLineStruct && !IsNamedInLineStruct) { - if (!AllTypes) { - CVarOption CV = Info.getVariable(VD, Context); - CB.constraintCVarToWild(CV, "Inline struct encountered."); - } else { - clang::DiagnosticsEngine &DE = Context->getDiagnostics(); - unsigned InlineStructWarning = - DE.getCustomDiagID(DiagnosticsEngine::Warning, - "\n Rewriting failed" - "for %q0 because an inline " - "or anonymous struct instance " - "was detected.\n Consider manually " - "rewriting by inserting the struct " - "definition inside the _Ptr " - "annotation.\n " - "EX. struct {int *a; int *b;} x; " - "_Ptr b;}>;"); - const auto Pointer = reinterpret_cast(VD); - const auto Kind = - clang::DiagnosticsEngine::ArgumentKind::ak_nameddecl; - auto DiagBuilder = DE.Report(VD->getLocation(), InlineStructWarning); - DiagBuilder.AddTaggedVal(Pointer, Kind); - } - } - } - } - -private: - RecordDecl *LastRecordDecl; -}; - // This class visits functions and adds constraints to the // Constraints instance assigned to it. // Each VisitXXX method is responsible for looking inside statements // and imposing constraints on variables it uses class FunctionVisitor : public RecursiveASTVisitor { public: - explicit FunctionVisitor(ASTContext *C, ProgramInfo &I, FunctionDecl *FD, - TypeVarInfo &TVI) - : Context(C), Info(I), Function(FD), CB(Info, Context), TVInfo(TVI), - ISD() {} + explicit FunctionVisitor(ASTContext *C, ProgramInfo &I, FunctionDecl *FD) + : Context(C), Info(I), Function(FD), CB(Info, Context) {} // T x = e bool VisitDeclStmt(DeclStmt *S) { - // Introduce variables as needed. - for (const auto &D : S->decls()) { - if (RecordDecl *RD = dyn_cast(D)) { - ISD.processRecordDecl(RD, Info, Context, CB); - } - if (VarDecl *VD = dyn_cast(D)) { - if (VD->isLocalVarDecl()) { - FullSourceLoc FL = Context->getFullLoc(VD->getBeginLoc()); - SourceRange SR = VD->getSourceRange(); - if (SR.isValid() && FL.isValid() && isPtrOrArrayType(VD->getType())) { - ISD.processVarDecl(VD, Info, Context, CB); - } - } - } - } - // Process inits even for non-pointers because structs and union values // can contain pointers for (const auto &D : S->decls()) { @@ -171,7 +50,8 @@ class FunctionVisitor : public RecursiveASTVisitor { // Is cast compatible with LHS type? QualType SrcT = C->getSubExpr()->getType(); QualType DstT = C->getType(); - if (!isCastSafe(DstT, SrcT) && !Info.hasPersistentConstraints(C, Context)) { + if (!CB.isCastofGeneric(C) && !isCastSafe(DstT, SrcT) + && !Info.hasPersistentConstraints(C, Context)) { auto CVs = CB.getExprConstraintVarsSet(C->getSubExpr()); std::string Rsn = "Cast from " + SrcT.getAsString() + " to " + DstT.getAsString(); @@ -205,7 +85,8 @@ class FunctionVisitor : public RecursiveASTVisitor { if (FVCons.size() > 1) { PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(E->getCallee(), *Context); - constrainConsVarGeq(FVCons, FVCons, Info.getConstraints(), &PL, + auto Rsn = ReasonLoc("Multiple function variables", PL); + constrainConsVarGeq(FVCons, FVCons, Info.getConstraints(), Rsn, Same_to_Same, false, &Info); } @@ -217,9 +98,10 @@ class FunctionVisitor : public RecursiveASTVisitor { // Collect type parameters for this function call that are // consistently instantiated as single type in this function call. - std::set ConsistentTypeParams; - if (TFD != nullptr) - TVInfo.getConsistentTypeParams(E, ConsistentTypeParams); + auto ConsistentTypeParams = + Info.hasTypeParamBindings(E,Context) ? + Info.getTypeParamBindings(E,Context) : + ProgramInfo::CallTypeParamBindingsT(); // Now do the call: Constrain arguments to parameters (but ignore returns) if (FVCons.empty()) { @@ -242,13 +124,14 @@ class FunctionVisitor : public RecursiveASTVisitor { unsigned I = 0; for (const auto &A : E->arguments()) { CSetBkeyPair ArgumentConstraints; + auto ArgPSL = PersistentSourceLoc::mkPSL(A,*Context); + auto Rsn = ReasonLoc("Constrain arguments to parameters", ArgPSL); if (I < TargetFV->numParams()) { - // Remove casts to void* on polymorphic types that are used - // consistently. - const int TyIdx = - TargetFV->getExternalParam(I)->getGenericIndex(); - if (ConsistentTypeParams.find(TyIdx) != - ConsistentTypeParams.end()) + // When the function has a void* parameter, Clang will + // add an implicit cast to void* here. Generating constraints + // will add an extraneous wild constraint to void*. This + // unnecessarily complicates results and root causes. + if (TargetFV->getExternalParam(I)->isVoidPtr()) ArgumentConstraints = CB.getExprConstraintVars(A->IgnoreImpCasts()); else @@ -271,9 +154,10 @@ class FunctionVisitor : public RecursiveASTVisitor { // Do not handle bounds key here because we will be // doing context-sensitive assignment next. constrainConsVarGeq(ParameterDC, ArgumentConstraints.first, CS, - &PL, CA, false, &Info, false); + Rsn, CA, false, &Info, false); - if (AllTypes && TFD != nullptr && I < TFD->getNumParams()) { + if (_3COpts.AllTypes && TFD != nullptr && + I < TFD->getNumParams()) { auto *PVD = TFD->getParamDecl(I); auto &CSBI = Info.getABoundsInfo().getCtxSensBoundsHandler(); // Here, we need to handle context-sensitive assignment. @@ -283,7 +167,7 @@ class FunctionVisitor : public RecursiveASTVisitor { } } else { // The argument passed to a function ith varargs; make it wild - if (HandleVARARGS) { + if (_3COpts.HandleVARARGS) { CB.constraintAllCVarsToWild(ArgumentConstraints.first, "Passing argument to a function " "accepting var args.", @@ -294,9 +178,10 @@ class FunctionVisitor : public RecursiveASTVisitor { // In `printf("... %s ...", ...)`, the argument corresponding // to the `%s` should be an _Nt_array_ptr // (https://github.com/correctcomputation/checkedc-clang/issues/549). - constrainVarsTo(ArgumentConstraints.first, CS.getNTArr()); + constrainVarsTo(ArgumentConstraints.first, CS.getNTArr(), + ReasonLoc(NT_ARRAY_REASON,ArgPSL)); } - if (Verbose) { + if (_3COpts.Verbose) { std::string FuncName = TargetFV->getName(); errs() << "Ignoring function as it contains varargs:" << FuncName << "\n"; @@ -314,14 +199,15 @@ class FunctionVisitor : public RecursiveASTVisitor { // e1[e2] bool VisitArraySubscriptExpr(ArraySubscriptExpr *E) { Constraints &CS = Info.getConstraints(); - constraintInBodyVariable(E->getBase(), CS.getArr()); + auto PSL = PersistentSourceLoc::mkPSL(E,*Context); + constraintInBodyVariable(E->getBase(), CS.getArr(), + ReasonLoc(ARRAY_REASON,PSL)); return true; } // return e; bool VisitReturnStmt(ReturnStmt *S) { // Get function variable constraint of the body - PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(S, *Context); CVarOption CVOpt = Info.getVariable(Function, Context); // Constrain the value returned (if present) against the return value @@ -335,8 +221,10 @@ class FunctionVisitor : public RecursiveASTVisitor { if (FVConstraint *FV = dyn_cast(&CVOpt.getValue())) { // This is to ensure that the return type of the function is same // as the type of return expression. + PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(S, *Context); + auto Rsn = ReasonLoc("Return types must match", PL); constrainConsVarGeq(FV->getInternalReturn(), RconsVar, - Info.getConstraints(), &PL, Same_to_Same, false, + Info.getConstraints(), Rsn, Same_to_Same, false, &Info); } } @@ -378,18 +266,18 @@ class FunctionVisitor : public RecursiveASTVisitor { private: // Constraint all the provided vars to be // equal to the provided type i.e., (V >= type). - void constrainVarsTo(CVarSet &Vars, ConstAtom *CAtom) { + void constrainVarsTo(CVarSet &Vars, ConstAtom *CAtom, const ReasonLoc &Rsn) { Constraints &CS = Info.getConstraints(); for (const auto &I : Vars) if (PVConstraint *PVC = dyn_cast(I)) { - PVC->constrainOuterTo(CS, CAtom); + PVC->constrainOuterTo(CS, CAtom, Rsn); } } // Constraint helpers. - void constraintInBodyVariable(Expr *E, ConstAtom *CAtom) { + void constraintInBodyVariable(Expr *E, ConstAtom *CAtom, const ReasonLoc &Rsn) { CVarSet Var = CB.getExprConstraintVarsSet(E); - constrainVarsTo(Var, CAtom); + constrainVarsTo(Var, CAtom, Rsn); } // Constraint all the argument of the provided @@ -426,9 +314,11 @@ class FunctionVisitor : public RecursiveASTVisitor { std::string Rsn = "Pointer arithmetic performed on a function pointer."; CB.constraintAllCVarsToWild(Var, Rsn, E); } else { + auto PSL = PersistentSourceLoc::mkPSL(E,*Context); if (ModifyingExpr) Info.getABoundsInfo().recordArithmeticOperation(E, &CB); - constraintInBodyVariable(E, Info.getConstraints().getArr()); + constraintInBodyVariable(E, Info.getConstraints().getArr(), + ReasonLoc(ARRAY_REASON,PSL)); } } @@ -436,58 +326,14 @@ class FunctionVisitor : public RecursiveASTVisitor { ProgramInfo &Info; FunctionDecl *Function; ConstraintResolver CB; - TypeVarInfo &TVInfo; - InlineStructDetector ISD; -}; - -class PtrToStructDef : public RecursiveASTVisitor { -public: - explicit PtrToStructDef(TypedefDecl *TDT) : TDT(TDT) {} - - bool VisitPointerType(clang::PointerType *PT) { - IsPointer = true; - return true; - } - - bool VisitRecordType(RecordType *RT) { - auto *Decl = RT->getDecl(); - auto DeclRange = Decl->getSourceRange(); - auto TypedefRange = TDT->getSourceRange(); - bool DeclContained = (TypedefRange.getBegin() < DeclRange.getBegin()) && - !(TypedefRange.getEnd() < DeclRange.getEnd()); - if (DeclContained) { - StructDefInTD = true; - return false; - } - return true; - } - - bool VisitFunctionProtoType(FunctionProtoType *FPT) { - IsPointer = true; - return true; - } - - bool getResult(void) { return StructDefInTD; } - - static bool containsPtrToStructDef(TypedefDecl *TDT) { - PtrToStructDef Traverser(TDT); - Traverser.TraverseDecl(TDT); - return Traverser.getResult(); - } - -private: - TypedefDecl *TDT = nullptr; - bool IsPointer = false; - bool StructDefInTD = false; }; // This class visits a global declaration, generating constraints // for functions, variables, types, etc. that are visited. class ConstraintGenVisitor : public RecursiveASTVisitor { public: - explicit ConstraintGenVisitor(ASTContext *Context, ProgramInfo &I, - TypeVarInfo &TVI) - : Context(Context), Info(I), CB(Info, Context), TVInfo(TVI), ISD() {} + explicit ConstraintGenVisitor(ASTContext *Context, ProgramInfo &I) + : Context(Context), Info(I), CB(Info, Context) {} bool VisitVarDecl(VarDecl *G) { @@ -495,7 +341,6 @@ class ConstraintGenVisitor : public RecursiveASTVisitor { if (G->hasInit()) { CB.constrainLocalAssign(nullptr, G, G->getInit(), Same_to_Same); } - ISD.processVarDecl(G, Info, Context, CB); } return true; } @@ -520,15 +365,15 @@ class ConstraintGenVisitor : public RecursiveASTVisitor { bool VisitFunctionDecl(FunctionDecl *D) { FullSourceLoc FL = Context->getFullLoc(D->getBeginLoc()); - if (Verbose) + if (_3COpts.Verbose) errs() << "Analyzing function " << D->getName() << "\n"; if (FL.isValid()) { // TODO: When would this ever be false? if (D->hasBody() && D->isThisDeclarationADefinition()) { Stmt *Body = D->getBody(); - FunctionVisitor FV = FunctionVisitor(Context, Info, D, TVInfo); + FunctionVisitor FV = FunctionVisitor(Context, Info, D); FV.TraverseStmt(Body); - if (AllTypes) { + if (_3COpts.AllTypes) { // Only do this, if all types is enabled. LengthVarInference LVI(Info, Context, D); LVI.Visit(Body); @@ -536,23 +381,16 @@ class ConstraintGenVisitor : public RecursiveASTVisitor { } } - if (Verbose) + if (_3COpts.Verbose) errs() << "Done analyzing function\n"; return true; } - bool VisitRecordDecl(RecordDecl *Declaration) { - ISD.processRecordDecl(Declaration, Info, Context, CB); - return true; - } - private: ASTContext *Context; ProgramInfo &Info; ConstraintResolver CB; - TypeVarInfo &TVInfo; - InlineStructDetector ISD; }; // Some information about variables in the program is required by the type @@ -576,10 +414,8 @@ class VariableAdderVisitor : public RecursiveASTVisitor { // typedef map. If we have seen it before, and we need to preserve the // constraints contained within it if (!VarAdder.seenTypedef(PSL)) - // Add this typedef to the program info, if it contains a ptr to - // an anonymous struct we mark as not being rewritable - VarAdder.addTypedef(PSL, !PtrToStructDef::containsPtrToStructDef(TD), TD, - *Context); + // Add this typedef to the program info. + VarAdder.addTypedef(PSL, TD, *Context); return true; } @@ -625,7 +461,7 @@ class VariableAdderVisitor : public RecursiveASTVisitor { void VariableAdderConsumer::HandleTranslationUnit(ASTContext &C) { Info.enterCompilationUnit(C); - if (Verbose) { + if (_3COpts.Verbose) { SourceManager &SM = C.getSourceManager(); FileID MainFileId = SM.getMainFileID(); const FileEntry *FE = SM.getFileEntryForID(MainFileId); @@ -636,13 +472,15 @@ void VariableAdderConsumer::HandleTranslationUnit(ASTContext &C) { } VariableAdderVisitor VAV = VariableAdderVisitor(&C, Info); + LowerBoundAssignmentFinder LBF = LowerBoundAssignmentFinder(&C, Info); TranslationUnitDecl *TUD = C.getTranslationUnitDecl(); // Collect Variables. for (const auto &D : TUD->decls()) { VAV.TraverseDecl(D); + LBF.TraverseDecl(D); } - if (Verbose) + if (_3COpts.Verbose) errs() << "Done analyzing\n"; Info.exitCompilationUnit(); @@ -651,7 +489,7 @@ void VariableAdderConsumer::HandleTranslationUnit(ASTContext &C) { void ConstraintBuilderConsumer::HandleTranslationUnit(ASTContext &C) { Info.enterCompilationUnit(C); - if (Verbose) { + if (_3COpts.Verbose) { SourceManager &SM = C.getSourceManager(); FileID MainFileId = SM.getMainFileID(); const FileEntry *FE = SM.getFileEntryForID(MainFileId); @@ -669,7 +507,7 @@ void ConstraintBuilderConsumer::HandleTranslationUnit(ASTContext &C) { ConstraintResolver CSResolver(Info, &C); ContextSensitiveBoundsKeyVisitor CSBV = ContextSensitiveBoundsKeyVisitor(&C, Info, &CSResolver); - ConstraintGenVisitor GV = ConstraintGenVisitor(&C, Info, TV); + ConstraintGenVisitor GV = ConstraintGenVisitor(&C, Info); TranslationUnitDecl *TUD = C.getTranslationUnitDecl(); StatsRecorder SR(&C, &Info); @@ -681,14 +519,17 @@ void ConstraintBuilderConsumer::HandleTranslationUnit(ASTContext &C) { CSBV.TraverseDecl(D); TV.TraverseDecl(D); - GV.TraverseDecl(D); - SR.TraverseDecl(D); } // Store type variable information for use in rewriting TV.setProgramInfoTypeVars(); - if (Verbose) + for (const auto &D : TUD->decls()) { + GV.TraverseDecl(D); + SR.TraverseDecl(D); + } + + if (_3COpts.Verbose) errs() << "Done analyzing\n"; PStats.endConstraintBuilderTime(); diff --git a/clang/lib/3C/ConstraintResolver.cpp b/clang/lib/3C/ConstraintResolver.cpp index 30993f10df4d..e6e7d859d9d5 100644 --- a/clang/lib/3C/ConstraintResolver.cpp +++ b/clang/lib/3C/ConstraintResolver.cpp @@ -21,21 +21,20 @@ ConstraintResolver::~ConstraintResolver() {} void ConstraintResolver::constraintAllCVarsToWild(const CVarSet &CSet, const std::string &Rsn, Expr *AtExpr) { - PersistentSourceLoc Psl; - PersistentSourceLoc *PslP = nullptr; + PersistentSourceLoc PSL; if (AtExpr != nullptr) { - Psl = PersistentSourceLoc::mkPSL(AtExpr, *Context); - PslP = &Psl; + PSL = PersistentSourceLoc::mkPSL(AtExpr, *Context); } + auto Reason = ReasonLoc(Rsn, PSL); auto &CS = Info.getConstraints(); for (const auto &A : CSet) { - if (PVConstraint *PVC = dyn_cast(A)) - PVC->constrainToWild(CS, Rsn, PslP); + if (auto *PVC = dyn_cast(A)) + PVC->constrainToWild(CS, Reason); else { - FVConstraint *FVC = dyn_cast(A); + auto *FVC = dyn_cast(A); assert(FVC != nullptr); - FVC->constrainToWild(CS, Rsn, PslP); + FVC->constrainToWild(CS, Reason); } } } @@ -65,11 +64,11 @@ CVarSet ConstraintResolver::handleDeref(CVarSet T) { // of indirection (when the constraint is PVConstraint), or return the // constraint unchanged (when the constraint is a function constraint). CVarSet ConstraintResolver::addAtomAll(CVarSet CVS, ConstAtom *PtrTyp, - Constraints &CS) { + ReasonLoc &Rsn, Constraints &CS) { CVarSet Result; for (auto *CV : CVS) { if (PVConstraint *PVC = dyn_cast(CV)) { - Result.insert(PVConstraint::addAtomPVConstraint(PVC, PtrTyp, CS)); + Result.insert(PVConstraint::addAtomPVConstraint(PVC, PtrTyp, Rsn,CS)); } else { Result.insert(CV); } @@ -106,8 +105,9 @@ static ConstAtom *analyzeAllocExpr(CallExpr *CE, Constraints &CS, ConstAtom *Ret = CS.getPtr(); Expr *E; - if (std::find(AllocatorFunctions.begin(), AllocatorFunctions.end(), - FuncName) != AllocatorFunctions.end() || + if (std::find(_3COpts.AllocatorFunctions.begin(), + _3COpts.AllocatorFunctions.end(), + FuncName) != _3COpts.AllocatorFunctions.end() || FuncName.compare("malloc") == 0) E = CE->getArg(0); else { @@ -141,7 +141,7 @@ CVarSet ConstraintResolver::getInvalidCastPVCons(CastExpr *E) { PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(E, *Context); std::string Rsn = "Cast from " + SrcType.getAsString() + " to " + DstType.getAsString(); - P->constrainToWild(Info.getConstraints(), Rsn, &PL); + P->constrainToWild(Info.getConstraints(), ReasonLoc(Rsn, PL)); return {P}; } @@ -151,6 +151,28 @@ inline CSetBkeyPair pairWithEmptyBkey(const CVarSet &Vars) { return std::make_pair(Vars, EmptyBSet); } +// Get the return type of the function from the TypeVars, that is, from +// the local instantiation of a generic function. Or the regular return +// constraint if it's not generic +ConstraintVariable *localReturnConstraint( + FVConstraint *FV, + ProgramInfo::CallTypeParamBindingsT TypeVars, + Constraints &CS, + ProgramInfo &Info) { + int TyVarIdx = FV->getExternalReturn()->getGenericIndex(); + // Check if local type vars are available + if (TypeVars.find(TyVarIdx) != TypeVars.end() && + TypeVars[TyVarIdx].isConsistent()) { + ConstraintVariable *CV = TypeVars[TyVarIdx].getConstraint( + Info.getConstraints().getVariables()); + if (FV->getExternalReturn()->hasBoundsKey()) + CV->setBoundsKey(FV->getExternalReturn()->getBoundsKey()); + return CV; + } else { + return FV->getExternalReturn(); + } +} + // Returns a pair of set of ConstraintVariables and set of BoundsKey // after evaluating the expression E. Will explore E recursively, but will // ignore parts of it that do not contribute to the final result. @@ -162,9 +184,10 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { auto &CS = Info.getConstraints(); QualType TypE = E->getType(); E = E->IgnoreParens(); + auto ExprPSL = PersistentSourceLoc::mkPSL(E,*Context); // Non-pointer (int, char, etc.) types have a special base PVConstraint. - if (TypE->isRecordType() || TypE->isArithmeticType()) { + if (isNonPtrType(TypE)) { if (DeclRefExpr *DRE = dyn_cast(E)) { // If we have a DeclRef, the PVC can get a meaningful name return pairWithEmptyBkey(getBaseVarPVConstraint(DRE)); @@ -222,7 +245,8 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { SubTypE->isVoidPointerType()) && !isCastSafe(TypE, SubTypE)) { CVarSet WildCVar = getInvalidCastPVCons(IE); - constrainConsVarGeq(CVs.first, WildCVar, CS, nullptr, Safe_to_Wild, + auto Rsn = ReasonLoc("Unsafe cast",ExprPSL); + constrainConsVarGeq(CVs.first, WildCVar, CS, Rsn, Safe_to_Wild, false, &Info); Ret = std::make_pair(WildCVar, CVs.second); } else { @@ -235,11 +259,12 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { // Is cast internally safe? Return WILD if not. // If the cast is NULL, it will otherwise seem invalid, but we want to // handle it as usual so the type in the cast can be rewritten. + auto Rsn = ReasonLoc("Explicit cast", ExprPSL); if (!isNULLExpression(ECE, *Context) && TypE->isPointerType() && - !isCastSafe(TypE, TmpE->getType())) { + !isCastSafe(TypE, TmpE->getType()) && !isCastofGeneric(ECE)) { CVarSet Vars = getExprConstraintVarsSet(TmpE); Ret = pairWithEmptyBkey(getInvalidCastPVCons(ECE)); - constrainConsVarGeq(Vars, Ret.first, CS, nullptr, Safe_to_Wild, false, + constrainConsVarGeq(Vars, Ret.first, CS, Rsn, Safe_to_Wild, false, &Info); // NB: Expression ECE itself handled in // ConstraintBuilder::FunctionVisitor. @@ -253,7 +278,7 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { // constraining GEQ these vars would be the cast always be WILD. if (!isNULLExpression(ECE, *Context)) { PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(ECE, *Context); - constrainConsVarGeq(P, Vars, Info.getConstraints(), &PL, Same_to_Same, + constrainConsVarGeq(P, Vars, Info.getConstraints(), Rsn, Same_to_Same, false, &Info); } } @@ -355,14 +380,22 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { // does permit taking the address of an _Array_ptr when the array // pointer has no declared bounds. With this constraint added however, // 3C will not generate such code. - for (auto *CV : T.first) - if (auto *PCV = dyn_cast(CV)) + for (auto *CV : T.first) { + if (auto *PCV = dyn_cast(CV)) { // On the other hand, CheckedC does let you take the address of // constant sized arrays. - if (!PCV->getArrPresent()) - PCV->constrainOuterTo(CS, CS.getPtr(), true); + if (!PCV->isConstantArr()) { + auto Rsn = ReasonLoc( + "Operand of address-of has PTR lower bound", ExprPSL); + PCV->constrainOuterTo(CS, CS.getPtr(), Rsn, true); + } + } + } // Add a VarAtom to UOExpr's PVConstraint, for &. - Ret = std::make_pair(addAtomAll(T.first, CS.getPtr(), CS), T.second); + auto Rsn = ReasonLoc( + "Result of address-of has PTR lower bound",ExprPSL); + Ret = std::make_pair(addAtomAll(T.first, CS.getPtr(), + Rsn, CS), T.second); } break; } @@ -403,6 +436,10 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { CVarSet ReturnCVs; BKeySet ReturnBSet = EmptyBSet; + ProgramInfo::CallTypeParamBindingsT TypeVars; + if (Info.hasTypeParamBindings(CE, Context)) + TypeVars = Info.getTypeParamBindings(CE, Context); + // Here, we need to look up the target of the call and return the // constraints for the return value of that function. QualType ExprType = E->getType(); @@ -418,31 +455,34 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { for (ConstraintVariable *C : Tmp.first) { if (FVConstraint *FV = dyn_cast(C)) { - ReturnCVs.insert(FV->getExternalReturn()); + ReturnCVs.insert(localReturnConstraint(FV,TypeVars,CS,Info)); } else if (PVConstraint *PV = dyn_cast(C)) { if (FVConstraint *FV = PV->getFV()) - ReturnCVs.insert(FV->getExternalReturn()); + ReturnCVs.insert(localReturnConstraint(FV,TypeVars,CS,Info)); } } } else if (DeclaratorDecl *FD = dyn_cast(D)) { /* Allocator call */ if (isFunctionAllocator(std::string(FD->getName()))) { - bool DidInsert = false; IsAllocator = true; - if (CE->getNumArgs() > 0) { + ConstraintVariable *CV = nullptr; + if (TypeVars.find(0) != TypeVars.end() && + TypeVars[0].isConsistent()) { + CV = TypeVars[0].getConstraint( + Info.getConstraints().getVariables()); + } else if (CE->getNumArgs() > 0) { QualType ArgTy; std::string FuncName = FD->getNameAsString(); - ConstAtom *A; - A = analyzeAllocExpr(CE, CS, ArgTy, FuncName, Context); + ConstAtom *A = analyzeAllocExpr(CE, CS, ArgTy, FuncName, Context); if (A) { std::string N(FD->getName()); N = "&" + N; ExprType = Context->getPointerType(ArgTy); PVConstraint *PVC = new PVConstraint(ExprType, nullptr, N, Info, *Context, nullptr, 0); - PVC->constrainOuterTo(CS, A, true); - ReturnCVs.insert(PVC); - DidInsert = true; + PVC->constrainOuterTo(CS, A, + ReasonLoc(ALLOCATOR_REASON, ExprPSL), true); + CV = PVC; if (FuncName.compare("realloc") == 0) { // We will constrain the first arg to the return of // realloc, below @@ -452,11 +492,27 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { } } } - if (!DidInsert) { + if (CV == nullptr) { std::string Rsn = "Unsafe call to allocator function."; PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(CE, *Context); ReturnCVs.insert(PVConstraint::getWildPVConstraint( - Info.getConstraints(), Rsn, &PL)); + Info.getConstraints(), ReasonLoc(Rsn, PL))); + } else { + // The ConstraintVariable generated for allocator functions don't + // have associated BoundsKeys. This is problem for lower bound + // inference, because, without a BoundsKey, the algorithm cannot + // detect that the allocator represents a possible lower bound. If + // there is another possible lower bound, the conflict is not + // detected. To avoid this, we fill the bounds key with the bounds + // key for the allocators declaration. + // TODO: This is another place where fully de-specializing + // allocators will simplify code. + CVarOption FuncCV = Info.getVariable(FD, Context); + assert(FuncCV.hasValue() && "Function without constraint variable."); + FVConstraint *FVC = cast(&FuncCV.getValue()); + CV->setBoundsKey(FVC->getExternalReturn()->getBoundsKey()); + + ReturnCVs.insert(CV); } /* Normal function call */ @@ -465,17 +521,19 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { assert(CV.hasValue() && "Function without constraint variable."); /* Direct function call */ if (FVConstraint *FVC = dyn_cast(&CV.getValue())) - ReturnCVs.insert(FVC->getExternalReturn()); + ReturnCVs.insert(localReturnConstraint(FVC,TypeVars,CS,Info)); /* Call via function pointer */ else { PVConstraint *Tmp = dyn_cast(&CV.getValue()); assert(Tmp != nullptr); if (FVConstraint *FVC = Tmp->getFV()) - ReturnCVs.insert(FVC->getExternalReturn()); + ReturnCVs.insert(localReturnConstraint(FVC,TypeVars,CS,Info)); else { // No FVConstraint -- make WILD. - auto *TmpFV = new FVConstraint(); - ReturnCVs.insert(TmpFV); + std::string Rsn = "Can't get return variable of function call."; + PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(CE, *Context); + ReturnCVs.insert( + PVConstraint::getWildPVConstraint(CS, ReasonLoc(Rsn, PL))); } } } @@ -503,7 +561,8 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { if (PCV->hasBoundsKey()) NewCV->setBoundsKey(PCV->getBoundsKey()); } else { - NewCV = CV->getCopy(CS); + auto Rsn = ReasonLoc("Function return R-value", ExprPSL); + NewCV = CV->getCopy(Rsn, CS); } } else { // Allocator functions are treated specially, so they do not have @@ -511,25 +570,26 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { NewCV = CV; } - auto PSL = PersistentSourceLoc::mkPSL(CE, *Context); // Make the bounds key context sensitive. if (NewCV->hasBoundsKey()) { auto CSensBKey = - ABI.getCtxSensCEBoundsKey(PSL, NewCV->getBoundsKey()); + ABI.getCtxSensCEBoundsKey(ExprPSL, NewCV->getBoundsKey()); NewCV->setBoundsKey(CSensBKey); } if (NewCV != CV) { // If the call is in a macro, use Same_to_Same to force checked type // equality and avoid ever needing to insert a cast inside a macro. + auto Rsn = ReasonLoc("Macro call", ExprPSL); ConsAction CA = Rewriter::isRewritable(CE->getExprLoc()) ? Safe_to_Wild : Same_to_Same; - constrainConsVarGeq(NewCV, CV, CS, &PSL, CA, false, &Info); + constrainConsVarGeq(NewCV, CV, CS, Rsn, CA, false, &Info); } TmpCVs.insert(NewCV); // If this is realloc, constrain the first arg to flow to the return + auto Rsn = ReasonLoc("Flow from realloc", ExprPSL); if (!ReallocFlow.empty()) { - constrainConsVarGeq(NewCV, ReallocFlow, Info.getConstraints(), &PSL, + constrainConsVarGeq(NewCV, ReallocFlow, Info.getConstraints(), Rsn, Wild_to_Safe, false, &Info); } } @@ -549,7 +609,9 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { if (ILE->getType()->isArrayType()) { // Array initialization is similar AddrOf, so the same pattern is // used where a new indirection is added to constraint variables. - Ret = std::make_pair(addAtomAll(CVars.first, CS.getArr(), CS), + auto Rsn = ReasonLoc("Array initialization", ExprPSL); + Ret = std::make_pair(addAtomAll(CVars.first, CS.getArr(), + Rsn, CS), CVars.second); } else { // This branch should only be taken on compound literal expressions @@ -570,7 +632,8 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { PVConstraint *P = getRewritablePVConstraint(CLE); PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(CLE, *Context); - constrainConsVarGeq(P, Vars.first, Info.getConstraints(), &PL, + auto Rsn = ReasonLoc("Compound literal", PL); + constrainConsVarGeq(P, Vars.first, Info.getConstraints(), Rsn, Same_to_Same, false, &Info); CVarSet T = {P}; @@ -583,7 +646,8 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { // We create a new constraint variable and constraint it to an Nt_array. PVConstraint *P = new PVConstraint(Str, Info, *Context); - P->constrainOuterTo(CS, CS.getNTArr()); // NB: ARR already there. + P->constrainOuterTo(CS, CS.getNTArr(), + ReasonLoc(STRING_LITERAL_REASON,ExprPSL)); // NB: ARR already there. BoundsKey TmpKey = ABI.getRandomBKey(); P->setBoundsKey(TmpKey); @@ -611,10 +675,10 @@ CSetBkeyPair ConstraintResolver::getExprConstraintVars(Expr *E) { auto *P = new PVConstraint(VarArg, Info, *Context); PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(E, *Context); std::string Rsn = "Accessing VarArg parameter"; - P->constrainToWild(Info.getConstraints(), Rsn, &PL); + P->constrainToWild(Info.getConstraints(), ReasonLoc(Rsn, PL)); Ret = pairWithEmptyBkey({P}); } else { - if (Verbose) { + if (_3COpts.Verbose) { llvm::errs() << "WARNING! Initialization expression ignored: "; E->dump(llvm::errs(), *Context); llvm::errs() << "\n"; @@ -652,16 +716,17 @@ void ConstraintResolver::constrainLocalAssign(Stmt *TSt, Expr *LHS, Expr *RHS, CSetBkeyPair L = getExprConstraintVars(LHS); CSetBkeyPair R = getExprConstraintVars(RHS); bool HandleBoundsKey = L.second.empty() && R.second.empty(); - constrainConsVarGeq(L.first, R.first, Info.getConstraints(), &PL, CAction, + auto Rsn = ReasonLoc("Local Assignment", PL); + constrainConsVarGeq(L.first, R.first, Info.getConstraints(), Rsn, CAction, false, &Info, HandleBoundsKey); // Handle pointer arithmetic. auto &ABI = Info.getABoundsInfo(); - ABI.handlePointerAssignment(TSt, LHS, RHS, Context, this); + ABI.handlePointerAssignment(LHS, RHS, Context, this); // Only if all types are enabled and these are not pointers, then track // the assignment. - if (AllTypes) { + if (_3COpts.AllTypes) { if ((!containsValidCons(L.first) && !containsValidCons(R.first)) || !HandleBoundsKey) { ABI.handleAssignment(LHS, L.first, L.second, RHS, R.first, R.second, @@ -673,11 +738,11 @@ void ConstraintResolver::constrainLocalAssign(Stmt *TSt, Expr *LHS, Expr *RHS, void ConstraintResolver::constrainLocalAssign(Stmt *TSt, DeclaratorDecl *D, Expr *RHS, ConsAction CAction, bool IgnoreBnds) { - PersistentSourceLoc PL, *PLPtr = nullptr; + PersistentSourceLoc PSL; if (TSt != nullptr) { - PL = PersistentSourceLoc::mkPSL(TSt, *Context); - PLPtr = &PL; + PSL = PersistentSourceLoc::mkPSL(TSt, *Context); } + auto Rsn = ReasonLoc("Local Assignment", PSL); // Get the in-context local constraints. CVarOption V = Info.getVariable(D, Context); auto RHSCons = getExprConstraintVars(RHS); @@ -685,8 +750,8 @@ void ConstraintResolver::constrainLocalAssign(Stmt *TSt, DeclaratorDecl *D, if (V.hasValue()) constrainConsVarGeq(&V.getValue(), RHSCons.first, Info.getConstraints(), - PLPtr, CAction, false, &Info, HandleBoundsKey); - if (AllTypes && !IgnoreBnds) { + Rsn, CAction, false, &Info, HandleBoundsKey); + if (_3COpts.AllTypes && !IgnoreBnds) { if (!HandleBoundsKey || (!(V.hasValue() && isValidCons(&V.getValue())) && !containsValidCons(RHSCons.first))) { auto &ABI = Info.getABoundsInfo(); @@ -696,11 +761,15 @@ void ConstraintResolver::constrainLocalAssign(Stmt *TSt, DeclaratorDecl *D, } } +bool ConstraintResolver::isNonPtrType(QualType &TE) { + return TE->isRecordType() || TE->isArithmeticType() || TE->isVectorType(); +} + CVarSet ConstraintResolver::pvConstraintFromType(QualType TypE) { assert("Pointer type CVs should be obtained through getExprConstraintVars." && !TypE->isPointerType()); CVarSet Ret; - if (TypE->isRecordType() || TypE->isArithmeticType()) + if (isNonPtrType(TypE)) Ret.insert(PVConstraint::getNonPtrPVConstraint(Info.getConstraints())); else llvm::errs() << "Warning: Returning non-base, non-wild type"; @@ -711,8 +780,8 @@ CVarSet ConstraintResolver::getBaseVarPVConstraint(DeclRefExpr *Decl) { if (Info.hasPersistentConstraints(Decl, Context)) return Info.getPersistentConstraintsSet(Decl, Context); - assert(Decl->getType()->isRecordType() || - Decl->getType()->isArithmeticType()); + auto T = Decl->getType(); + assert(isNonPtrType(T)); CVarSet Ret; auto DN = Decl->getDecl()->getName(); @@ -736,13 +805,45 @@ CVarSet ConstraintResolver::getCalleeConstraintVars(CallExpr *CE) { return FVCons; } +// This serves as an exception to the unsafety of casting from void*. +// In most cases, CheckedC can handle generic casts, so we can ignore them. +// CheckedC can't handle allocators without checked headers, so we add them +// to this exception for when we're dealing with small examples. +bool ConstraintResolver::isCastofGeneric(CastExpr *C) { + Expr *SE = C->getSubExpr(); + if (CHKCBindTemporaryExpr *CE = dyn_cast(SE)) + SE = CE->getSubExpr(); + if (auto *CE = dyn_cast_or_null(SE)) { + // Check for built-in allocators, in case the standard headers are not used. + // This is a required exception, because allocators return void*, and casts + // from it are unsafe. We assume the cast is appropriate. With checked + // headers clang can figure out if it is safe. + if (auto *DD = dyn_cast_or_null(CE->getCalleeDecl())) { + std::string Name = DD->getNameAsString(); + if (isFunctionAllocator(Name)) + return true; + } + // Check for a generic function call. + CVarSet CVS = getCalleeConstraintVars(CE); + // If there are multiple constraints in the expression we + // can't guarantee safety, so only return true if the + // cast is directly on a function call. + if (CVS.size() == 1) { + if (auto *FVC = dyn_cast(*CVS.begin())) { + return FVC->getGenericParams() > 0; + } + } + } + return false; +} + // Construct a PVConstraint for an expression that can safely be used when // rewriting the expression later on. This is done by making the constraint WILD // if the expression is inside a macro. PVConstraint *ConstraintResolver::getRewritablePVConstraint(Expr *E) { PVConstraint *P = new PVConstraint(E, Info, *Context); auto PSL = PersistentSourceLoc::mkPSL(E, *Context); - Info.constrainWildIfMacro(P, E->getExprLoc(), &PSL); + Info.constrainWildIfMacro(P, E->getExprLoc(), ReasonLoc(MACRO_REASON, PSL)); return P; } diff --git a/clang/lib/3C/ConstraintVariables.cpp b/clang/lib/3C/ConstraintVariables.cpp index 1db4957565b9..c27f0df8076c 100644 --- a/clang/lib/3C/ConstraintVariables.cpp +++ b/clang/lib/3C/ConstraintVariables.cpp @@ -14,24 +14,12 @@ #include "clang/3C/ProgramInfo.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/CommandLine.h" #include using namespace clang; // Macro for boolean implication. #define IMPLIES(a, b) ((a) ? (b) : true) -static llvm::cl::OptionCategory OptimizationCategory("Optimization category"); -static llvm::cl::opt - DisableRDs("disable-rds", - llvm::cl::desc("Disable reverse edges for Checked Constraints."), - llvm::cl::init(false), llvm::cl::cat(OptimizationCategory)); - -static llvm::cl::opt DisableFunctionEdges( - "disable-fnedgs", - llvm::cl::desc("Disable reverse edges for external functions."), - llvm::cl::init(false), llvm::cl::cat(OptimizationCategory)); - std::string ConstraintVariable::getRewritableOriginalTy() const { std::string OrigTyString = getOriginalTy(); std::string SpaceStr = " "; @@ -46,119 +34,119 @@ std::string ConstraintVariable::getRewritableOriginalTy() const { return OrigTyString; } +std::string ConstraintVariable::getOriginalTypeWithName() const { + if (Name == RETVAR) + return getRewritableOriginalTy(); + return OriginalTypeWithName; +} + PointerVariableConstraint *PointerVariableConstraint::getWildPVConstraint( - Constraints &CS, const std::string &Rsn, PersistentSourceLoc *PSL) { - VarAtom *VA = - CS.createFreshGEQ("wildvar", VarAtom::V_Other, CS.getWild(), Rsn, PSL); - return new PointerVariableConstraint({VA}, {CS.getWild()}, "unsigned", - "wildvar", nullptr, ""); + Constraints &CS, const ReasonLoc &Rsn) { + auto *WildPVC = new PointerVariableConstraint("wildvar"); + VarAtom *VA = CS.createFreshGEQ("wildvar", VarAtom::V_Other, CS.getWild(), + Rsn); + WildPVC->Vars.push_back(VA); + WildPVC->SrcVars.push_back(CS.getWild()); + + // Mark this constraint variable as generic. This is done because we do not + // know the type of the constraint, and therefore we also don't know the + // number of atoms it needs to have. Fortunately, it's already WILD, so any + // constraint made with it will force the other constraint to WILD, safely + // handling assignment between incompatible pointer depths. + WildPVC->InferredGenericIndex = 0; + + return WildPVC; } PointerVariableConstraint * PointerVariableConstraint::getNonPtrPVConstraint(Constraints &CS) { static PointerVariableConstraint *GlobalNonPtrPV = nullptr; - if (GlobalNonPtrPV == nullptr) { - return new PointerVariableConstraint({}, {}, "unsigned", "basevar", nullptr, - ""); - } + if (GlobalNonPtrPV == nullptr) + GlobalNonPtrPV = new PointerVariableConstraint("basevar"); return GlobalNonPtrPV; } PointerVariableConstraint * PointerVariableConstraint::getNamedNonPtrPVConstraint(StringRef Name, Constraints &CS) { - return new PointerVariableConstraint({}, {}, "unsigned", std::string(Name), - nullptr, ""); + return new PointerVariableConstraint(std::string(Name)); } PointerVariableConstraint * PointerVariableConstraint::derefPVConstraint(PointerVariableConstraint *PVC) { - std::vector Vars = PVC->Vars; - std::vector SrcVars = PVC->SrcVars; - assert(!PVC->Vars.empty() && !SrcVars.empty()); - Vars.erase(Vars.begin()); - SrcVars.erase(SrcVars.begin()); - return new PointerVariableConstraint(Vars, SrcVars, PVC->getTy(), - PVC->getName(), PVC->getFV(), - PVC->getItype()); + // Make a copy of the PVConstraint using the same VarAtoms + auto *Copy = new PointerVariableConstraint(PVC); + + // Get rid of the outer atom to dereference the pointer. + assert(!Copy->Vars.empty() && !Copy->SrcVars.empty()); + Copy->Vars.erase(Copy->Vars.begin()); + Copy->SrcVars.erase(Copy->SrcVars.begin()); + + // This can't have bounds because bounds only apply to the top pointer level. + Copy->BKey = 0; + Copy->ValidBoundsKey = false; + + return Copy; } PointerVariableConstraint *PointerVariableConstraint::addAtomPVConstraint( - PointerVariableConstraint *PVC, ConstAtom *PtrTyp, Constraints &CS) { - VarAtom *NewA = CS.getFreshVar("&" + PVC->Name, VarAtom::V_Other); - CS.addConstraint(CS.createGeq(NewA, PtrTyp, false)); - std::vector Vars = PVC->Vars; - std::vector SrcVars = PVC->SrcVars; - if (!Vars.empty()) { - if (auto *VA = dyn_cast(*Vars.begin())) { - // If PVC is already a pointer, add implication forcing outermost one to - // be wild if this added one is. - auto *Prem = CS.createGeq(NewA, CS.getWild()); - auto *Conc = CS.createGeq(VA, CS.getWild()); - CS.addConstraint(CS.createImplies(Prem, Conc)); - } - } + PointerVariableConstraint *PVC, ConstAtom *PtrTyp, + ReasonLoc &Rsn, Constraints &CS) { + auto *Copy = new PointerVariableConstraint(PVC); + std::vector &Vars = Copy->Vars; + std::vector &SrcVars = Copy->SrcVars; + + VarAtom *NewA = CS.getFreshVar("&" + Copy->Name, VarAtom::V_Other); + CS.addConstraint(CS.createGeq(NewA, PtrTyp, Rsn, false)); + + // Add a constraint between the new atom and any existing atom for this + // pointer. This is the same constraint that is added between atoms of a + // pointer in the PointerVariableConstraint constructor. It forces all inner + // atoms to be wild if an outer atom is wild. + if (!Vars.empty()) + if (auto *VA = dyn_cast(*Vars.begin())) + CS.addConstraint(new Geq(VA, NewA, + ReasonLoc(INNER_POINTER_REASON, PersistentSourceLoc()))); Vars.insert(Vars.begin(), NewA); SrcVars.insert(SrcVars.begin(), PtrTyp); - return new PointerVariableConstraint(Vars, SrcVars, PVC->BaseType, PVC->Name, - PVC->FV, PVC->ItypeStr); + + return Copy; } PointerVariableConstraint::PointerVariableConstraint( - PointerVariableConstraint *Ot, Constraints &CS) - : ConstraintVariable(ConstraintVariable::PointerVariable, Ot->OriginalType, - Ot->Name), - FV(nullptr), PartOfFuncPrototype(Ot->PartOfFuncPrototype) { - this->ArrSizes = Ot->ArrSizes; - this->ArrSizeStrs = Ot->ArrSizeStrs; + PointerVariableConstraint *Ot) + : ConstraintVariable(ConstraintVariable::PointerVariable, Ot->OriginalType, + Ot->Name, Ot->OriginalTypeWithName), + BaseType(Ot->BaseType), Vars(Ot->Vars), SrcVars(Ot->SrcVars), FV(Ot->FV), + QualMap(Ot->QualMap), ArrSizes(Ot->ArrSizes), ArrSizeStrs(Ot->ArrSizeStrs), + SrcHasItype(Ot->SrcHasItype), ItypeStr(Ot->ItypeStr), + PartOfFuncPrototype(Ot->PartOfFuncPrototype), Parent(Ot), + BoundsAnnotationStr(Ot->BoundsAnnotationStr), + SourceGenericIndex(Ot->SourceGenericIndex), + InferredGenericIndex(Ot->InferredGenericIndex), + IsZeroWidthArray(Ot->IsZeroWidthArray), + IsTypedef(Ot->IsTypedef), TypedefString(Ot->TypedefString), + TypedefLevelInfo(Ot->TypedefLevelInfo), IsVoidPtr(Ot->IsVoidPtr) { + // These are fields of the super class Constraint Variable this->HasEqArgumentConstraints = Ot->HasEqArgumentConstraints; this->ValidBoundsKey = Ot->ValidBoundsKey; this->BKey = Ot->BKey; - - assert(Ot->Vars.size() == Ot->SrcVars.size()); - auto VAIt = Ot->Vars.begin(); - auto CAIt = Ot->SrcVars.begin(); - while (VAIt != Ot->Vars.end() && CAIt != Ot->SrcVars.end()) { - if (ConstAtom *CA = dyn_cast(*VAIt)) { - this->Vars.push_back(CA); - this->SrcVars.push_back(CA); - } else if (VarAtom *VA = dyn_cast(*VAIt)) { - VarAtom *FreshVA = CS.getFreshVar(VA->getName(), VA->getVarKind()); - this->Vars.push_back(FreshVA); - this->SrcVars.push_back(*CAIt); - if (!isa(*CAIt)) - CS.addConstraint(CS.createGeq(*CAIt, FreshVA, false)); - } - ++VAIt; - ++CAIt; - } - - if (Ot->FV != nullptr) { - this->FV = dyn_cast(Ot->FV->getCopy(CS)); - } - this->Parent = Ot; - this->GenericIndex = Ot->GenericIndex; - this->IsZeroWidthArray = Ot->IsZeroWidthArray; - this->BaseType = Ot->BaseType; - this->SrcHasItype = Ot->SrcHasItype; - this->IsVoidPtr = Ot->IsVoidPtr; - this->TypedefLevelInfo = Ot->TypedefLevelInfo; } PointerVariableConstraint::PointerVariableConstraint(DeclaratorDecl *D, ProgramInfo &I, const ASTContext &C) : PointerVariableConstraint(D->getType(), D, std::string(D->getName()), I, - C, nullptr, -1, false, D->getTypeSourceInfo()) { -} + C, nullptr, -1, false, false, + D->getTypeSourceInfo()) {} PointerVariableConstraint::PointerVariableConstraint(TypedefDecl *D, ProgramInfo &I, const ASTContext &C) : PointerVariableConstraint(D->getUnderlyingType(), nullptr, D->getNameAsString(), I, C, nullptr, -1, false, - D->getTypeSourceInfo()) {} + false, D->getTypeSourceInfo()) {} PointerVariableConstraint::PointerVariableConstraint(Expr *E, ProgramInfo &I, const ASTContext &C) @@ -212,11 +200,12 @@ class TypedefLevelFinder : public RecursiveASTVisitor { PointerVariableConstraint::PointerVariableConstraint( const QualType &QT, DeclaratorDecl *D, std::string N, ProgramInfo &I, const ASTContext &C, std::string *InFunc, int ForceGenericIndex, + bool PotentialGeneric, bool VarAtomForChecked, TypeSourceInfo *TSInfo, const QualType &ITypeT) - : ConstraintVariable(ConstraintVariable::PointerVariable, - tyToStr(QT.getTypePtr()), N), + : ConstraintVariable(ConstraintVariable::PointerVariable, QT, N), FV(nullptr), SrcHasItype(false), PartOfFuncPrototype(InFunc != nullptr), Parent(nullptr) { + PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(D, C); QualType QTy = QT; const Type *Ty = QTy.getTypePtr(); auto &CS = I.getConstraints(); @@ -288,7 +277,16 @@ PointerVariableConstraint::PointerVariableConstraint( SourceRange R = ITE->getSourceRange(); if (R.isValid()) { ItypeStr = getSourceText(R, C); - assert(ItypeStr.size() > 0); + } + + // ITE->isCompilerGenerated will be true when an itype expression is + // implied by a bounds expression on the declaration. When the itype + // expression is not generated by the compiler, but we failed to extract + // its string representation from the source, build the itype string + // from the AST. + if (!ITE->isCompilerGenerated() && ItypeStr.empty()) { + assert(!InteropType.getAsString().empty()); + ItypeStr = "itype(" + InteropType.getAsString() + ")"; } } } @@ -306,9 +304,9 @@ PointerVariableConstraint::PointerVariableConstraint( TypedefLevelInfo = TypedefLevelFinder::find(QTy); if (ForceGenericIndex >= 0) { - GenericIndex = ForceGenericIndex; + SourceGenericIndex = ForceGenericIndex; } else { - GenericIndex = -1; + SourceGenericIndex = -1; // This makes a lot of assumptions about how the AST will look, and limits // it to one level. // TODO: Enhance TypedefLevelFinder to get this info. @@ -317,15 +315,12 @@ PointerVariableConstraint::PointerVariableConstraint( if (auto *TypdefTy = dyn_cast_or_null(PtrTy)) { const auto *Tv = dyn_cast(TypdefTy->desugar()); if (Tv) - GenericIndex = Tv->GetIndex(); + SourceGenericIndex = Tv->GetIndex(); } } } + InferredGenericIndex = SourceGenericIndex; - bool VarCreated = false; - bool IsArr = false; - bool IsIncompleteArr = false; - bool IsTopMost = true; uint32_t TypeIdx = 0; std::string Npre = InFunc ? ((*InFunc) + ":") : ""; VarAtom::VarKind VK = @@ -347,11 +342,13 @@ PointerVariableConstraint::PointerVariableConstraint( TLoc = D->getTypeSourceInfo()->getTypeLoc(); while (Ty->isPointerType() || Ty->isArrayType()) { + bool VarCreated = false; + // Is this a VarArg type? std::string TyName = tyToStr(Ty); if (isVarArgType(TyName)) { // Variable number of arguments. Make it WILD. - std::string Rsn = "Variable number of arguments."; + auto Rsn = ReasonLoc("Variable number of arguments.", PSL); VarAtom *WildVA = CS.createFreshGEQ(Npre + N, VK, CS.getWild(), Rsn); Vars.push_back(WildVA); SrcVars.push_back(CS.getWild()); @@ -396,9 +393,6 @@ PointerVariableConstraint::PointerVariableConstraint( } if (Ty->isArrayType() || Ty->isIncompleteArrayType()) { - IsArr = true; - IsIncompleteArr = Ty->isIncompleteArrayType(); - // Boil off the typedefs in the array case. // TODO this will need to change to properly account for typedefs bool Boiling = true; @@ -437,13 +431,6 @@ PointerVariableConstraint::PointerVariableConstraint( ArrSizeStrs[TypeIdx] = SizeStr; } } - - // If this is the top-most pointer variable? - if (hasBoundsKey() && IsTopMost) { - BoundsKey CBKey = ABInfo.getConstKey(CAT->getSize().getZExtValue()); - ABounds *NB = new CountBound(CBKey); - ABInfo.insertDeclaredBounds(D, NB); - } } else { ArrSizes[TypeIdx] = std::pair(O_UnSizedArray, 0); @@ -471,28 +458,30 @@ PointerVariableConstraint::PointerVariableConstraint( } // This type is not a constant atom. We need to create a VarAtom for this. - if (!VarCreated) { VarAtom *VA = CS.getFreshVar(Npre + N, VK); Vars.push_back(VA); SrcVars.push_back(CS.getWild()); - // Incomplete arrays are lower bounded to ARR because the transformation - // int[] -> _Ptr is permitted while int[1] -> _Ptr is not. - if (IsIncompleteArr) - CS.addConstraint(CS.createGeq(VA, CS.getArr(), false)); - else if (IsArr) - CS.addConstraint(CS.createGeq(CS.getArr(), VA, false)); + // Incomplete arrays are not given ARR as an upper bound because the + // transformation int[] -> _Ptr is permitted but int[1] -> _Ptr + // is not. + auto Rsn = ReasonLoc("0-sized Array", PSL); + if (ArrSizes[TypeIdx].first == O_SizedArray) { + CS.addConstraint(CS.createGeq(CS.getArr(), VA, Rsn, false)); + // A constant array declared with size 0 cannot be _Nt_checked. Checked + // C requires that _Nt_checked arrays are not empty since the declared + // size of the array includes the null terminator. + if (ArrSizes[TypeIdx].second == 0) + CS.addConstraint(CS.createGeq(VA, CS.getArr(), Rsn, false)); + } } // Prepare for next level of pointer - VarCreated = false; - IsArr = false; TypeIdx++; Npre = Npre + "*"; - VK = VarAtom:: - V_Other; // only the outermost pointer considered a param/return - IsTopMost = false; + // Only the outermost pointer considered a param/return + VK = VarAtom::V_Other; if (!TLoc.isNull()) TLoc = TLoc.getNextTypeLoc(); } @@ -510,21 +499,27 @@ PointerVariableConstraint::PointerVariableConstraint( // tn fname = ..., // where tn is the typedef'ed type name. // There is possibly something more elegant to do in the code here. - FV = new FVConstraint(Ty, IsDeclTy ? D : nullptr, IsTypedef ? "" : N, I, C, + FV = new FVConstraint(QTy, IsDeclTy ? D : nullptr, IsTypedef ? "" : N, I, C, TSInfo); // Get a string representing the type without pointer and array indirection. - BaseType = extractBaseType(D, TSInfo, QT, Ty, C); - - IsVoidPtr = isTypeHasVoid(QT); - bool IsWild = !getIsGeneric() && (isVarArgType(BaseType) || IsVoidPtr); + BaseType = extractBaseType(D, TSInfo, QT, Ty, C, I); + + // check if the type is some depth of pointers to void + // TODO: is this what the field should mean? do we want to include other + // indirection options like arrays? + // https://github.com/correctcomputation/checkedc-clang/issues/648 + IsVoidPtr = QT->isPointerType() && isTypeHasVoid(QT); + // varargs are always wild, as are void pointers that are not generic + bool IsWild = isVarArgType(BaseType) || + (!(PotentialGeneric || isGeneric()) && IsVoidPtr); if (IsWild) { std::string Rsn = - IsVoidPtr ? "Default void* type" : "Default Var arg list type"; + IsVoidPtr ? VOID_TYPE_REASON : "Default Var arg list type"; // TODO: Github issue #61: improve handling of types for variable arguments. for (const auto &V : Vars) if (VarAtom *VA = dyn_cast(V)) - CS.addConstraint(CS.createGeq(VA, CS.getWild(), Rsn)); + CS.addConstraint(CS.createGeq(VA, CS.getWild(), ReasonLoc(Rsn,PSL))); } // Add qualifiers. @@ -532,43 +527,35 @@ PointerVariableConstraint::PointerVariableConstraint( getQualString(TypeIdx, QualStr); BaseType = QualStr.str() + BaseType; - // Here lets add implication that if outer pointer is WILD - // then make the inner pointers WILD too. + // If an outer pointer is wild, then the inner pointer must also be wild. if (Vars.size() > 1) { - bool UsedPrGeq = false; - for (auto VI = Vars.begin(), VE = Vars.end(); VI != VE; VI++) { - if (VarAtom *VIVar = dyn_cast(*VI)) { - // Premise. - Geq *PrGeq = new Geq(VIVar, CS.getWild()); - UsedPrGeq = false; - for (auto VJ = (VI + 1); VJ != VE; VJ++) { - if (VarAtom *VJVar = dyn_cast(*VJ)) { - // Conclusion. - Geq *CoGeq = new Geq(VJVar, CS.getWild()); - CS.addConstraint(CS.createImplies(PrGeq, CoGeq)); - UsedPrGeq = true; - } - } - // Delete unused constraint. - if (!UsedPrGeq) { - delete (PrGeq); - } - } + for (unsigned VarIdx = 0; VarIdx < Vars.size() - 1; VarIdx++) { + VarAtom *VI = dyn_cast(Vars[VarIdx]); + VarAtom *VJ = dyn_cast(Vars[VarIdx + 1]); + if (VI && VJ) + CS.addConstraint(new Geq(VJ, VI, ReasonLoc(INNER_POINTER_REASON, PSL))); } } } -std::string PointerVariableConstraint::tryExtractBaseType(DeclaratorDecl *D, - TypeSourceInfo *TSI, - QualType QT, - const Type *Ty, - const ASTContext &C) { - bool FoundBaseTypeInSrc = false; - if (D && !TSI) - TSI = D->getTypeSourceInfo(); - if (!QT->isOrContainsCheckedType() && !Ty->getAs() && D && TSI) { +std::string PointerVariableConstraint::tryExtractBaseType( + MultiDeclMemberDecl *D, TypeSourceInfo *TSI, QualType QT, const Type *Ty, + const ASTContext &C) { + // Implicit parameters declarations from typedef function declarations will + // still have valid and non-empty source ranges, but implicit declarations + // aren't written in the source, so extracting the base type from this range + // gives incorrect type strings. For example, the base type for the implicit + // parameter for `foo_decl` in `typedef void foo(int*); foo foo_decl;` would + // be extracted as "foo_decl", when it should be "int". + if (!D || D->isImplicit()) + return ""; + + if (!TSI) + TSI = getTypeSourceInfoOfMultiDeclMember(D); + if (!QT->isOrContainsCheckedType() && !Ty->getAs() && TSI) { // Try to extract the type from original source to preserve defines TypeLoc TL = TSI->getTypeLoc(); + bool FoundBaseTypeInSrc = false; if (isa(D)) { FoundBaseTypeInSrc = D->getAsFunction()->getReturnType() == QT; TL = getBaseTypeLoc(TL).getAs(); @@ -577,7 +564,7 @@ std::string PointerVariableConstraint::tryExtractBaseType(DeclaratorDecl *D, return ""; TL = TL.getAs().getReturnLoc(); } else { - FoundBaseTypeInSrc = D->getType() == QT; + FoundBaseTypeInSrc = getTypeOfMultiDeclMember(D) == QT; } if (!TL.isNull()) { TypeLoc BaseLoc = getBaseTypeLoc(TL); @@ -594,11 +581,13 @@ std::string PointerVariableConstraint::tryExtractBaseType(DeclaratorDecl *D, return ""; } -std::string PointerVariableConstraint::extractBaseType(DeclaratorDecl *D, - TypeSourceInfo *TSI, - QualType QT, - const Type *Ty, - const ASTContext &C) { +std::string PointerVariableConstraint::extractBaseType( + MultiDeclMemberDecl *D, TypeSourceInfo *TSI, QualType QT, const Type *Ty, + const ASTContext &C, ProgramInfo &Info) { + if (llvm::Optional TypeStrOverride = + Info.TheMultiDeclsInfo.getTypeStrOverride(Ty, C)) + return *TypeStrOverride; + std::string BaseTypeStr = tryExtractBaseType(D, TSI, QT, Ty, C); // Fall back to rebuilding the base type based on type passed to constructor if (BaseTypeStr.empty()) @@ -719,14 +708,20 @@ void PointerVariableConstraint::addArrayAnnotations( assert(ConstArrs.empty()); } -bool PointerVariableConstraint::isTypedef(void) { return IsTypedef; } +bool PointerVariableConstraint::isTypedef(void) const { return IsTypedef; } -void PointerVariableConstraint::setTypedef(TypedefNameDecl *T, std::string S) { +void PointerVariableConstraint::setTypedef(ConstraintVariable *TDVar, + std::string S) { IsTypedef = true; - TDT = T; + TypedefVar = TDVar; TypedefString = S; } +const ConstraintVariable *PointerVariableConstraint::getTypedefVar() const { + assert(isTypedef()); + return TypedefVar; +} + // Mesh resolved constraints with the PointerVariableConstraints set of // variables and potentially nested function pointer declaration. Produces a // string that can be replaced in the source code. @@ -737,21 +732,33 @@ std::string PointerVariableConstraint::gatherQualStrings(void) const { return S.str(); } -std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, - bool ForItype, bool EmitPointee, - bool UnmaskTypedef, - std::string UseName) const { +std::string +PointerVariableConstraint::mkString(Constraints &CS, + const MkStringOpts &Opts) const { + UNPACK_OPTS(EmitName, ForItype, EmitPointee, UnmaskTypedef, UseName, + ForItypeBase); + + // This function has become pretty ad-hoc and has a number of known bugs: see + // https://github.com/correctcomputation/checkedc-clang/issues/703. We hope to + // overhaul it in the future. // The name field encodes if this variable is the return type for a function. // TODO: store this information in a separate field. bool IsReturn = getName() == RETVAR; - if (UseName.empty()) + if (UseName.empty()) { UseName = getName(); + if (UseName.empty()) + EmitName = false; + } if (IsTypedef && !UnmaskTypedef) { - return gatherQualStrings() + TypedefString + - (EmitName && !IsReturn ? (" " + UseName) : " "); + std::string QualTypedef = gatherQualStrings() + TypedefString; + if (!ForItype) + QualTypedef += " "; + if (EmitName && !IsReturn) + QualTypedef += UseName; + return QualTypedef; } std::ostringstream Ss; @@ -770,16 +777,22 @@ std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, bool EmittedBase = false; // Have we emitted the name of the variable yet? bool EmittedName = false; - // Was the last variable an Array? - bool PrevArr = false; - // Is the entire type so far an array? - bool AllArrays = true; if (!EmitName || IsReturn) EmittedName = true; - uint32_t TypeIdx = 0; + int TypeIdx = 0; + + // If we've set a GenericIndex for void, it means we're converting it into + // a generic function so give it the default generic type name. + // Add more type names below if we expect to use a lot. + std::string BaseTypeName = BaseType; + if (InferredGenericIndex > -1 && isVoidPtr() && + isSolutionChecked(CS.getVariables())) { + assert(InferredGenericIndex < 3 + && "Trying to use an unexpected type variable name"); + BaseTypeName = std::begin({"T","U","V"})[InferredGenericIndex]; + } auto It = Vars.begin(); - auto I = 0; // Skip over first pointer level if only emitting pointee string. // This is needed when inserting type arguments. if (EmitPointee) @@ -787,11 +800,13 @@ std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, // Iterate through the vars(), but if we have an internal typedef, then stop // once you reach the typedef's level. for (; It != Vars.end() && IMPLIES(TypedefLevelInfo.HasTypedef, - I < TypedefLevelInfo.TypedefLevel); - ++It, I++) { + TypeIdx < TypedefLevelInfo.TypedefLevel); + ++It, TypeIdx++) { const auto &V = *It; ConstAtom *C = nullptr; - if (ConstAtom *CA = dyn_cast(V)) { + if (ForItypeBase) { + C = CS.getWild(); + } else if (ConstAtom *CA = dyn_cast(V)) { C = CA; } else { VarAtom *VA = dyn_cast(V); @@ -803,26 +818,28 @@ std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, Atom::AtomKind K = C->getKind(); - // If this is not an itype + // If this is not an itype or generic // make this wild as it can hold any pointer type. - if (!ForItype && BaseType == "void") + if (!ForItype && InferredGenericIndex == -1 && isVoidPtr()) K = Atom::A_Wild; - if (PrevArr && ArrSizes.at(TypeIdx).first != O_SizedArray && !EmittedName) { - EmittedName = true; + // In a case like `_Ptr p[2]`, the ` p[2]` needs to end up _after_ the + // `>`, so we need to push the ` p[2]` onto EndStrs _before_ the code below + // pushes the `>`. In general, before we visit a checked pointer level (not + // a checked array level), we need to transfer any pending array levels and + // emit the name (if applicable). + if (K != Atom::A_Wild && ArrSizes.at(TypeIdx).first != O_SizedArray) { addArrayAnnotations(ConstArrs, EndStrs); - EndStrs.push_front(" " + UseName); + if (!EmittedName) { + EmittedName = true; + EndStrs.push_front(" " + UseName); + } } - PrevArr = ArrSizes.at(TypeIdx).first == O_SizedArray; switch (K) { case Atom::A_Ptr: getQualString(TypeIdx, Ss); - // We need to check and see if this level of variable - // is constrained by a bounds safe interface. If it is, - // then we shouldn't re-write it. - AllArrays = false; EmittedBase = false; Ss << "_Ptr<"; EndStrs.push_front(">"); @@ -836,46 +853,36 @@ std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, // we should substitute [K]. if (emitArraySize(ConstArrs, TypeIdx, K)) break; - AllArrays = false; - // We need to check and see if this level of variable - // is constrained by a bounds safe interface. If it is, - // then we shouldn't re-write it. EmittedBase = false; Ss << "_Array_ptr<"; EndStrs.push_front(">"); break; case Atom::A_NTArr: + // If this is an NTArray. + getQualString(TypeIdx, Ss); if (emitArraySize(ConstArrs, TypeIdx, K)) break; - AllArrays = false; - // This additional check is to prevent fall-through from the array. - if (K == Atom::A_NTArr) { - // If this is an NTArray. - getQualString(TypeIdx, Ss); - // We need to check and see if this level of variable - // is constrained by a bounds safe interface. If it is, - // then we shouldn't re-write it. - EmittedBase = false; - Ss << "_Nt_array_ptr<"; - EndStrs.push_front(">"); - break; - } - LLVM_FALLTHROUGH; + EmittedBase = false; + Ss << "_Nt_array_ptr<"; + EndStrs.push_front(">"); + break; // If there is no array in the original program, then we fall through to // the case where we write a pointer value. case Atom::A_Wild: if (emitArraySize(ConstArrs, TypeIdx, K)) break; - AllArrays = false; + // FIXME: This code emits wild pointer levels with the outermost on the + // left. The outermost should be on the right + // (https://github.com/correctcomputation/checkedc-clang/issues/161). if (FV != nullptr) { FptrInner << "*"; getQualString(TypeIdx, FptrInner); } else { if (!EmittedBase) { - assert(!BaseType.empty()); + assert(!BaseTypeName.empty()); EmittedBase = true; - Ss << BaseType << " "; + Ss << BaseTypeName << " "; } Ss << "*"; getQualString(TypeIdx, Ss); @@ -887,109 +894,83 @@ std::string PointerVariableConstraint::mkString(Constraints &CS, bool EmitName, llvm_unreachable("impossible"); break; } - TypeIdx++; - } - - // If the previous variable was an array or - // if we are leaving an array run, we need to emit the - // annotation for a stack-array - if (PrevArr && !ConstArrs.empty()) - addArrayAnnotations(ConstArrs, EndStrs); - - // If the whole type is an array so far, and we haven't emitted - // a name yet, then emit the name so that it appears before - // the the stack array type. - if (PrevArr && !EmittedName && AllArrays) { - EmittedName = true; - EndStrs.push_front(" " + UseName); } if (!EmittedBase) { // If we have a FV pointer, then our "base" type is a function pointer type. if (FV) { - if (Ss.str().empty()) { - if (!EmittedName) { - FptrInner << UseName; - EmittedName = true; - } - for (std::string Str : EndStrs) - FptrInner << Str; - EndStrs.clear(); + if (!EmittedName) { + FptrInner << UseName; + EmittedName = true; } + std::deque FptrEndStrs; + addArrayAnnotations(ConstArrs, FptrEndStrs); + for (std::string Str : FptrEndStrs) + FptrInner << Str; bool EmitFVName = !FptrInner.str().empty(); if (EmitFVName) - Ss << FV->mkString(CS, true, false, false, false, FptrInner.str()); + Ss << FV->mkString(CS, MKSTRING_OPTS(UseName = FptrInner.str(), + ForItypeBase = ForItypeBase)); else - Ss << FV->mkString(CS, false); + Ss << FV->mkString( + CS, MKSTRING_OPTS(EmitName = false, ForItypeBase = ForItypeBase)); } else if (TypedefLevelInfo.HasTypedef) { std::ostringstream Buf; getQualString(TypedefLevelInfo.TypedefLevel, Buf); auto Name = TypedefLevelInfo.TypedefName; Ss << Buf.str() << Name; } else { - Ss << BaseType; + Ss << BaseTypeName; } } + // No space after itype. + if (!EmittedName) { + if (!StringRef(Ss.str()).endswith("*")) + Ss << " "; + Ss << UseName; + } + // Add closing elements to type + addArrayAnnotations(ConstArrs, EndStrs); for (std::string Str : EndStrs) { Ss << Str; } - // No space after itype. - if (!EmittedName && !UseName.empty()) - Ss << " " << UseName; - - // Final array dropping. - if (!ConstArrs.empty()) { - std::deque ArrStrs; - addArrayAnnotations(ConstArrs, ArrStrs); - for (std::string Str : ArrStrs) - Ss << Str; - } - - if (IsReturn && !ForItype) + if (IsReturn && !ForItype && !StringRef(Ss.str()).endswith("*")) Ss << " "; return Ss.str(); } bool PVConstraint::addArgumentConstraint(ConstraintVariable *DstCons, + ReasonLoc &Rsn, ProgramInfo &Info) { if (this->Parent == nullptr) { bool RetVal = false; if (isPartOfFunctionPrototype()) { RetVal = ArgumentConstraints.insert(DstCons).second; if (RetVal && this->HasEqArgumentConstraints) { - constrainConsVarGeq(DstCons, this, Info.getConstraints(), nullptr, + constrainConsVarGeq(DstCons, this, Info.getConstraints(), Rsn, Same_to_Same, true, &Info); } } return RetVal; } - return this->Parent->addArgumentConstraint(DstCons, Info); + return this->Parent->addArgumentConstraint(DstCons, Rsn, Info); } const CVarSet &PVConstraint::getArgumentConstraints() const { return ArgumentConstraints; } -FunctionVariableConstraint::FunctionVariableConstraint( - FunctionVariableConstraint *Ot, Constraints &CS) - : ConstraintVariable(ConstraintVariable::FunctionVariable, Ot->OriginalType, - Ot->getName()) { - this->IsStatic = Ot->IsStatic; - this->FileName = Ot->FileName; - this->Hasbody = Ot->Hasbody; - this->Hasproto = Ot->Hasproto; - this->HasEqArgumentConstraints = Ot->HasEqArgumentConstraints; - this->IsFunctionPtr = Ot->IsFunctionPtr; +FunctionVariableConstraint::FunctionVariableConstraint(FVConstraint *Ot) + : ConstraintVariable(ConstraintVariable::FunctionVariable, Ot->OriginalType, + Ot->getName(), Ot->OriginalTypeWithName), + ReturnVar(Ot->ReturnVar), ParamVars(Ot->ParamVars), FileName(Ot->FileName), + Hasproto(Ot->Hasproto), Hasbody(Ot->Hasbody), IsStatic(Ot->IsStatic), + Parent(Ot), IsFunctionPtr(Ot->IsFunctionPtr), TypeParams(Ot->TypeParams) { this->HasEqArgumentConstraints = Ot->HasEqArgumentConstraints; - this->ReturnVar = FVComponentVariable(&Ot->ReturnVar, CS); - // Make copy of ParameterCVs too. - for (auto &ParmPv : Ot->ParamVars) - this->ParamVars.push_back(FVComponentVariable(&ParmPv, CS)); - this->Parent = Ot; } // This describes a function, either a function pointer or a function @@ -1000,22 +981,23 @@ FunctionVariableConstraint::FunctionVariableConstraint(DeclaratorDecl *D, ProgramInfo &I, const ASTContext &C) : FunctionVariableConstraint( - D->getType().getTypePtr(), D, + D->getType(), D, D->getDeclName().isIdentifier() ? std::string(D->getName()) : "", I, C, D->getTypeSourceInfo()) {} FunctionVariableConstraint::FunctionVariableConstraint(TypedefDecl *D, ProgramInfo &I, const ASTContext &C) - : FunctionVariableConstraint(D->getUnderlyingType().getTypePtr(), nullptr, + : FunctionVariableConstraint(D->getUnderlyingType(), nullptr, D->getNameAsString(), I, C, D->getTypeSourceInfo()) {} FunctionVariableConstraint::FunctionVariableConstraint( - const Type *Ty, DeclaratorDecl *D, std::string N, ProgramInfo &I, + const QualType QT, DeclaratorDecl *D, std::string N, ProgramInfo &I, const ASTContext &Ctx, TypeSourceInfo *TSInfo) - : ConstraintVariable(ConstraintVariable::FunctionVariable, tyToStr(Ty), N), + : ConstraintVariable(ConstraintVariable::FunctionVariable, QT, N), Parent(nullptr) { + const Type *Ty = QT.getTypePtr(); QualType RT, RTIType; Hasproto = false; Hasbody = false; @@ -1023,6 +1005,7 @@ FunctionVariableConstraint::FunctionVariableConstraint( HasEqArgumentConstraints = false; IsFunctionPtr = true; TypeParams = 0; + PersistentSourceLoc PSL; // Metadata about function. FunctionDecl *FD = nullptr; @@ -1039,9 +1022,10 @@ FunctionVariableConstraint::FunctionVariableConstraint( Hasbody = true; IsStatic = !(FD->isGlobal()); ASTContext *TmpCtx = const_cast(&Ctx); - auto PSL = PersistentSourceLoc::mkPSL(D, *TmpCtx); + PSL = PersistentSourceLoc::mkPSL(D, *TmpCtx); FileName = PSL.getFileName(); IsFunctionPtr = false; + TypeParams = FD->getNumTypeVars(); } bool ReturnHasItype = false; @@ -1086,11 +1070,9 @@ FunctionVariableConstraint::FunctionVariableConstraint( ParmVD = FTL.getParam(J); std::string PName = ParmVD ? ParmVD->getName().str() : ""; - auto ParamVar = FVComponentVariable(QT, ITypeT, ParmVD, PName, I, Ctx, &N, - ParamHasItype); - int GenericIdx = ParamVar.ExternalConstraint->getGenericIndex(); - if (GenericIdx >= 0) - TypeParams = std::max(TypeParams, GenericIdx + 1); + auto ParamVar = + FVComponentVariable(QT, ITypeT, ParmVD, PName, I, Ctx, + &N, true, ParamHasItype); ParamVars.push_back(ParamVar); } @@ -1104,24 +1086,53 @@ FunctionVariableConstraint::FunctionVariableConstraint( } // ConstraintVariable for the return. - ReturnVar = - FVComponentVariable(RT, RTIType, D, RETVAR, I, Ctx, &N, ReturnHasItype); - int GenericIdx = ReturnVar.ExternalConstraint->getGenericIndex(); - if (GenericIdx >= 0) - TypeParams = std::max(TypeParams, GenericIdx + 1); -} - -void FunctionVariableConstraint::constrainToWild(Constraints &CS, - const std::string &Rsn) const { - constrainToWild(CS, Rsn, nullptr); + ReturnVar = FVComponentVariable(RT, RTIType, D, RETVAR, I, Ctx, + &N, true, ReturnHasItype); + + // Locate the void* params that were not marked wild above + // to either do so or use as generics + std::vector Voids; + auto Ext = ReturnVar.ExternalConstraint; + if(Ext->isVoidPtr() && !Ext->isGeneric()) { + Voids.push_back(-1); + } + for(unsigned i=0; i < ParamVars.size();i++) { + auto Ext = ParamVars[i].ExternalConstraint; + if(Ext->isVoidPtr() && !Ext->isGeneric()) { + Voids.push_back(i); + } + } + // Strategy: If there's one void*, turn this into a generic function. + // Otherwise, we need to constraint the void*'s to wild + // Exclude unwritables, external functions, + // function pointers, and source generics for now + // Also exclude params that are checked or have itypes + bool ConvertFunc = canWrite(FileName) && hasBody() && !IsFunctionPtr && + TypeParams == 0 && Voids.size() == 1; + bool DidConvert = false; + auto &CS = I.getConstraints(); + for(int idx : Voids) { + FVComponentVariable *FVCV = idx == -1 ? &ReturnVar : &ParamVars[idx]; + auto Ext = FVCV->ExternalConstraint; + if (ConvertFunc && !Ext->isOriginallyChecked() && !Ext->srcHasItype() && + Ext->getCvars().size() == 1) { + FVCV->setGenericIndex(0); + DidConvert = true; + } else { + if (!Ext->isOriginallyChecked()) { + Ext->constrainToWild(CS, ReasonLoc(VOID_TYPE_REASON, PSL)); + } + } + } + if (DidConvert) TypeParams = 1; } void FunctionVariableConstraint::constrainToWild( - Constraints &CS, const std::string &Rsn, PersistentSourceLoc *PL) const { - ReturnVar.ExternalConstraint->constrainToWild(CS, Rsn, PL); + Constraints &CS, const ReasonLoc &Rsn) const { + ReturnVar.ExternalConstraint->constrainToWild(CS, Rsn); for (const auto &V : ParamVars) - V.ExternalConstraint->constrainToWild(CS, Rsn, PL); + V.ExternalConstraint->constrainToWild(CS, Rsn); } bool FunctionVariableConstraint::anyChanges(const EnvironmentMap &E) const { return ReturnVar.ExternalConstraint->anyChanges(E) || @@ -1161,33 +1172,42 @@ bool FunctionVariableConstraint::hasNtArr(const EnvironmentMap &E, return ReturnVar.ExternalConstraint->hasNtArr(E, AIdx); } -FVConstraint *FunctionVariableConstraint::getCopy(Constraints &CS) { - return new FVConstraint(this, CS); +FVConstraint *FunctionVariableConstraint::getCopy(ReasonLoc &Rsn, + Constraints &CS) { + FunctionVariableConstraint *Copy = new FunctionVariableConstraint(this); + Copy->ReturnVar = FVComponentVariable(&Copy->ReturnVar, Rsn, CS); + // Make copy of ParameterCVs too. + std::vector FreshParams; + for (auto &ParmPv : Copy->ParamVars) + FreshParams.push_back(FVComponentVariable(&ParmPv, Rsn, CS)); + Copy->ParamVars = FreshParams; + return Copy; } -void PVConstraint::equateArgumentConstraints(ProgramInfo &Info) { +void PVConstraint::equateArgumentConstraints(ProgramInfo &Info, ReasonLoc &Rsn) { if (HasEqArgumentConstraints) { return; } HasEqArgumentConstraints = true; constrainConsVarGeq(this, this->ArgumentConstraints, Info.getConstraints(), - nullptr, Same_to_Same, true, &Info); + Rsn, Same_to_Same, true, &Info); if (this->FV != nullptr) { - this->FV->equateArgumentConstraints(Info); + this->FV->equateArgumentConstraints(Info, Rsn); } } void FunctionVariableConstraint::equateFVConstraintVars( - ConstraintVariable *CV, ProgramInfo &Info) const { + ConstraintVariable *CV, ProgramInfo &Info, ReasonLoc &Rsn) const { if (FVConstraint *FVCons = dyn_cast(CV)) { for (auto &PCon : FVCons->ParamVars) - PCon.InternalConstraint->equateArgumentConstraints(Info); - FVCons->ReturnVar.InternalConstraint->equateArgumentConstraints(Info); + PCon.InternalConstraint->equateArgumentConstraints(Info, Rsn); + FVCons->ReturnVar.InternalConstraint->equateArgumentConstraints(Info, Rsn); } } -void FunctionVariableConstraint::equateArgumentConstraints(ProgramInfo &Info) { +void FunctionVariableConstraint::equateArgumentConstraints(ProgramInfo &Info, + ReasonLoc &Rsn) { if (HasEqArgumentConstraints) { return; } @@ -1195,7 +1215,7 @@ void FunctionVariableConstraint::equateArgumentConstraints(ProgramInfo &Info) { HasEqArgumentConstraints = true; // Equate arguments and parameters vars. - this->equateFVConstraintVars(this, Info); + this->equateFVConstraintVars(this, Info, Rsn); // Is this not a function pointer? if (!IsFunctionPtr) { @@ -1211,18 +1231,12 @@ void FunctionVariableConstraint::equateArgumentConstraints(ProgramInfo &Info) { assert(DefnCons != nullptr); // Equate arguments and parameters vars. - this->equateFVConstraintVars(DefnCons, Info); + this->equateFVConstraintVars(DefnCons, Info, Rsn); } } void PointerVariableConstraint::constrainToWild(Constraints &CS, - const std::string &Rsn) const { - constrainToWild(CS, Rsn, nullptr); -} - -void PointerVariableConstraint::constrainToWild(Constraints &CS, - const std::string &Rsn, - PersistentSourceLoc *PL) const { + const ReasonLoc &Rsn) const { // Find the first VarAtom. All atoms before this are ConstAtoms, so // constraining them isn't useful; VarAtom *FirstVA = nullptr; @@ -1232,27 +1246,29 @@ void PointerVariableConstraint::constrainToWild(Constraints &CS, break; } - // Constrains the outer VarAtom to WILD. Inner pointer levels are - // implicitly WILD because of implication constraints. + // Constrains the outer VarAtom to WILD. All inner pointer levels will also be + // implicitly constrained to WILD because of GEQ constraints that exist + // between levels of a pointer. if (FirstVA) - CS.addConstraint(CS.createGeq(FirstVA, CS.getWild(), Rsn, PL, true)); + CS.addConstraint(CS.createGeq(FirstVA, CS.getWild(), Rsn, true)); if (FV) - FV->constrainToWild(CS, Rsn, PL); + FV->constrainToWild(CS, Rsn); } void PointerVariableConstraint::constrainIdxTo(Constraints &CS, ConstAtom *C, - unsigned int Idx, bool DoLB, - bool Soft) { + unsigned int Idx, + const ReasonLoc &Rsn, + bool DoLB, bool Soft) { assert(C == CS.getPtr() || C == CS.getArr() || C == CS.getNTArr()); if (Vars.size() > Idx) { Atom *A = Vars[Idx]; if (VarAtom *VA = dyn_cast(A)) { if (DoLB) - CS.addConstraint(CS.createGeq(VA, C, false, Soft)); + CS.addConstraint(CS.createGeq(VA, C, Rsn, false, Soft)); else - CS.addConstraint(CS.createGeq(C, VA, false, Soft)); + CS.addConstraint(CS.createGeq(C, VA, Rsn, false, Soft)); } else if (ConstAtom *CA = dyn_cast(A)) { if (DoLB) { if (*CA < *C) { @@ -1270,8 +1286,9 @@ void PointerVariableConstraint::constrainIdxTo(Constraints &CS, ConstAtom *C, } void PointerVariableConstraint::constrainOuterTo(Constraints &CS, ConstAtom *C, + const ReasonLoc &Rsn, bool DoLB, bool Soft) { - constrainIdxTo(CS, C, 0, DoLB, Soft); + constrainIdxTo(CS, C, 0, Rsn, DoLB, Soft); } bool PointerVariableConstraint::anyArgumentIsWild(const EnvironmentMap &E) { @@ -1308,14 +1325,43 @@ bool PointerVariableConstraint::anyChanges(const EnvironmentMap &E) const { PtrChanged |= SrcType != SolutionType; } + // also check if we've make this generic + PtrChanged |= isGenericChanged(); + if (FV) PtrChanged |= FV->anyChanges(E); return PtrChanged; } -PVConstraint *PointerVariableConstraint::getCopy(Constraints &CS) { - return new PointerVariableConstraint(this, CS); +PVConstraint *PointerVariableConstraint::getCopy(ReasonLoc &Rsn, + Constraints &CS) { + auto *Copy = new PointerVariableConstraint(this); + + // After the copy construct, the copy Vars vector holds exactly the same + // atoms as this. For this method, we want it to contain fresh VarAtoms. + std::vector FreshVars; + auto VAIt = Copy->Vars.begin(); + auto CAIt = Copy->SrcVars.begin(); + while (VAIt != Copy->Vars.end() && CAIt != Copy->SrcVars.end()) { + if (auto *CA = dyn_cast(*VAIt)) { + FreshVars.push_back(CA); + } else if (auto *VA = dyn_cast(*VAIt)) { + VarAtom *FreshVA = CS.getFreshVar(VA->getName(), VA->getVarKind()); + FreshVars.push_back(FreshVA); + if (!isa(*CAIt)){ + CS.addConstraint(CS.createGeq(*CAIt, FreshVA, Rsn, false)); + } + } + ++VAIt; + ++CAIt; + } + Copy->Vars = FreshVars; + + if (Copy->FV != nullptr) + Copy->FV = Copy->FV->getCopy(Rsn, CS); + + return Copy; } const ConstAtom * @@ -1386,12 +1432,31 @@ bool PointerVariableConstraint::hasNtArr(const EnvironmentMap &E, return false; } -bool PointerVariableConstraint::getArrPresent() const { - return llvm::any_of(ArrSizes, - [](auto E) { return E.second.first != O_Pointer; }); +bool PointerVariableConstraint::isNtConstantArr(const EnvironmentMap &E) const { + // First check that this pointer is a constant sized array. This function is + // only looking for constant sized arrays, so other array pointers should + // return false. + if (isConstantArr()) { + assert("Atoms vector for array type can't be empty." && !Vars.empty()); + // This is a constant sized array. It might have solved to WILD, ARR, or + // NTARR, but we should only return true if we know it's NTARR. + const ConstAtom *PtrType = getSolution(Vars[0], E); + return isa(PtrType); + } + return false; } -bool PointerVariableConstraint::isTopCvarUnsizedArr() const { +bool PointerVariableConstraint::isConstantArr() const { + return ArrSizes.find(0) != ArrSizes.end() && + ArrSizes.at(0).first == O_SizedArray; +} + +unsigned long PointerVariableConstraint::getConstantArrSize() const { + assert("Pointer must be a constant array to get size." && isConstantArr()); + return ArrSizes.at(0).second; +} + +bool PointerVariableConstraint::isTopAtomUnsizedArr() const { if (ArrSizes.find(0) != ArrSizes.end()) { return ArrSizes.at(0).first != O_SizedArray; } @@ -1414,7 +1479,7 @@ bool PointerVariableConstraint::solutionEqualTo(Constraints &CS, if (CV != nullptr) { if (const auto *PV = dyn_cast(CV)) { auto &OthCVars = PV->Vars; - if (getIsGeneric() || PV->getIsGeneric() || + if (isGeneric() || PV->isGeneric() || Vars.size() == OthCVars.size()) { Ret = true; @@ -1543,15 +1608,19 @@ bool FunctionVariableConstraint::solutionEqualTo(Constraints &CS, return Ret; } -std::string FunctionVariableConstraint::mkString(Constraints &CS, bool EmitName, - bool ForItype, - bool EmitPointee, - bool UnmaskTypedef, - std::string UseName) const { +std::string +FunctionVariableConstraint::mkString(Constraints &CS, + const MkStringOpts &Opts) const { + UNPACK_OPTS(EmitName, ForItype, EmitPointee, UnmaskTypedef, UseName, + ForItypeBase); if (UseName.empty()) UseName = Name; - std::string Ret = ReturnVar.mkTypeStr(CS, false); - std::string Itype = ReturnVar.mkItypeStr(CS); + std::string Ret = ReturnVar.mkTypeStr(CS, false, "", ForItypeBase); + std::string Itype = ReturnVar.mkItypeStr(CS, ForItypeBase); + // When a function pointer type is the base for an itype, the name and + // parameter list need to be surrounded in an extra set of parenthesis. + if (ForItypeBase) + Ret += "("; if (EmitName) { if (UnmaskTypedef) // This is done to rewrite the typedef of a function proto @@ -1562,7 +1631,7 @@ std::string FunctionVariableConstraint::mkString(Constraints &CS, bool EmitName, Ret = Ret + "("; std::vector ParmStrs; for (const auto &I : this->ParamVars) - ParmStrs.push_back(I.mkString(CS)); + ParmStrs.push_back(I.mkString(CS, true, ForItypeBase)); if (ParmStrs.size() > 0) { std::ostringstream Ss; @@ -1574,6 +1643,9 @@ std::string FunctionVariableConstraint::mkString(Constraints &CS, bool EmitName, Ret = Ret + Ss.str() + ")"; } else Ret = Ret + "void)"; + // Close the parenthesis required on the base for function pointer itypes. + if (ForItypeBase) + Ret += ")"; return Ret + Itype; } @@ -1593,9 +1665,8 @@ std::string FunctionVariableConstraint::mkString(Constraints &CS, bool EmitName, // CA |- R <: L // Action depends on the kind of constraint (checked, ptyp), // which is inferred from the atom type -static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, std::string &Rsn, - PersistentSourceLoc *PSL, ConsAction CAct, - bool DoEqType) { +static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, + const ReasonLoc &Rsn, ConsAction CAct, bool DoEqType) { ConstAtom *CAL, *CAR; VarAtom *VAL, *VAR; ConstAtom *Wild = CS.getWild(); @@ -1634,35 +1705,35 @@ static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, std::string &Rsn, switch (CAct) { case Same_to_Same: // Equality for checked. - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); // Not for ptyp. - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(L, R, Rsn, false)); // Unless indicated. if (DoEqType) - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(R, L, Rsn, false)); break; case Safe_to_Wild: - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, false)); if (DoEqType) { - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, false)); } break; case Wild_to_Safe: - if (!DisableRDs) { + if (!_3COpts.DisableRDs) { // Note: reversal. - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); } else { // Add edges both ways. - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); } - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(L, R, Rsn, false)); if (DoEqType) { - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, false)); } break; } @@ -1671,23 +1742,23 @@ static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, std::string &Rsn, if (CAL == Wild || CAR == Wild) { switch (CAct) { case Same_to_Same: - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); break; case Safe_to_Wild: - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); if (DoEqType) - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); break; case Wild_to_Safe: - if (!DisableRDs) { + if (!_3COpts.DisableRDs) { // Note: reversal. - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(R, L, Rsn, true)); } else { - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); } if (DoEqType) - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, true)); + CS.addConstraint(CS.createGeq(L, R, Rsn, true)); break; } } else { @@ -1696,9 +1767,9 @@ static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, std::string &Rsn, case Same_to_Same: case Safe_to_Wild: case Wild_to_Safe: - CS.addConstraint(CS.createGeq(L, R, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(L, R, Rsn, false)); if (DoEqType) - CS.addConstraint(CS.createGeq(R, L, Rsn, PSL, false)); + CS.addConstraint(CS.createGeq(R, L, Rsn, false)); break; } } @@ -1708,7 +1779,7 @@ static void createAtomGeq(Constraints &CS, Atom *L, Atom *R, std::string &Rsn, // Generate constraints according to CA |- RHS <: LHS. // If doEqType is true, then also do CA |- LHS <: RHS. void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey) { @@ -1716,12 +1787,13 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, // This can happen when a non-function pointer gets assigned to // a function pointer. if (LHS == nullptr || RHS == nullptr) { - std::string Rsn = "Assignment a non-pointer to a pointer"; + auto Reason = ReasonLoc("Assignment a non-pointer to a pointer", + Rsn.Location); if (LHS != nullptr) { - LHS->constrainToWild(CS, Rsn, PL); + LHS->constrainToWild(CS, Reason); } if (RHS != nullptr) { - RHS->constrainToWild(CS, Rsn, PL); + RHS->constrainToWild(CS, Reason); } return; } @@ -1733,16 +1805,17 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, // This is an assignment between function pointer and // function pointer or a function. // Force past/future callers of function to use equality constraints. - FCLHS->equateArgumentConstraints(*Info); - FCRHS->equateArgumentConstraints(*Info); + auto Reason = ReasonLoc("Assignment of function pointer", Rsn.Location); + FCLHS->equateArgumentConstraints(*Info, Reason); + FCRHS->equateArgumentConstraints(*Info, Reason); // Constrain the return values covariantly. // FIXME: Make neg(CA) here? Function pointers equated. constrainConsVarGeq(FCLHS->getExternalReturn(), - FCRHS->getExternalReturn(), CS, PL, Same_to_Same, + FCRHS->getExternalReturn(), CS, Reason, Same_to_Same, DoEqType, Info, HandleBoundsKey); constrainConsVarGeq(FCLHS->getInternalReturn(), - FCRHS->getInternalReturn(), CS, PL, Same_to_Same, + FCRHS->getInternalReturn(), CS, Reason, Same_to_Same, DoEqType, Info, HandleBoundsKey); // Constrain the parameters contravariantly. @@ -1751,20 +1824,21 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, ConstraintVariable *LHSV = FCLHS->getExternalParam(I); ConstraintVariable *RHSV = FCRHS->getExternalParam(I); // FIXME: Make neg(CA) here? Now: Function pointers equated. - constrainConsVarGeq(RHSV, LHSV, CS, PL, Same_to_Same, DoEqType, + constrainConsVarGeq(RHSV, LHSV, CS, Reason, Same_to_Same, DoEqType, Info, HandleBoundsKey); ConstraintVariable *LParam = FCLHS->getInternalParam(I); ConstraintVariable *RParam = FCRHS->getInternalParam(I); - constrainConsVarGeq(RParam, LParam, CS, PL, Same_to_Same, DoEqType, + constrainConsVarGeq(RParam, LParam, CS, Reason, Same_to_Same, DoEqType, Info, HandleBoundsKey); } } else { // Constrain both to be top. - std::string Rsn = - "Assigning from:" + FCRHS->getName() + " to " + FCLHS->getName(); - RHS->constrainToWild(CS, Rsn, PL); - LHS->constrainToWild(CS, Rsn, PL); + auto WildReason = ReasonLoc( + "Assigning from " + FCRHS->getName() + " to " + FCLHS->getName(), + Rsn.Location); + RHS->constrainToWild(CS, WildReason); + LHS->constrainToWild(CS, WildReason); } } else { llvm_unreachable("impossible"); @@ -1778,21 +1852,24 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, PCRHS->getBoundsKey()); } - std::string Rsn = ""; + auto Reason = ReasonLoc( + "Assigning from " + PCRHS->getName() + " to " + PCLHS->getName(), + Rsn.Location); // This is to handle function subtyping. Try to add LHS and RHS // to each others argument constraints. - PCLHS->addArgumentConstraint(PCRHS, *Info); - PCRHS->addArgumentConstraint(PCLHS, *Info); + PCLHS->addArgumentConstraint(PCRHS, Reason, *Info); + PCRHS->addArgumentConstraint(PCLHS, Reason, *Info); // Element-wise constrain PCLHS and PCRHS to be equal. CAtoms CLHS = PCLHS->getCvars(); CAtoms CRHS = PCRHS->getCvars(); // Only generate constraint if LHS is not a base type. if (CLHS.size() != 0) { - if (CLHS.size() == CRHS.size() || PCLHS->getIsGeneric() || - PCRHS->getIsGeneric()) { - unsigned Max = std::max(CLHS.size(), CRHS.size()); - for (unsigned N = 0; N < Max; N++) { + if (CLHS.size() == CRHS.size() || + (CLHS.size() < CRHS.size() && PCLHS->isGeneric()) || + (CLHS.size() > CRHS.size() && PCRHS->isGeneric())) { + unsigned Min = std::min(CLHS.size(), CRHS.size()); + for (unsigned N = 0; N < Min; N++) { Atom *IAtom = PCLHS->getAtom(N, CS); Atom *JAtom = PCRHS->getAtom(N, CS); if (IAtom == nullptr || JAtom == nullptr) @@ -1800,24 +1877,26 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, // Get outermost pointer first, using current ConsAction. if (N == 0) - createAtomGeq(CS, IAtom, JAtom, Rsn, PL, CA, DoEqType); + createAtomGeq(CS, IAtom, JAtom, Reason, CA, DoEqType); else { // Now constrain the inner ones as equal. - createAtomGeq(CS, IAtom, JAtom, Rsn, PL, CA, true); + createAtomGeq(CS, IAtom, JAtom, Reason, CA, true); } } // Unequal sizes means casting from (say) T** to T*; not safe. // unless assigning to a generic type. } else { // Constrain both to be top. - std::string Rsn = "Assigning from:" + std::to_string(CRHS.size()) + + auto WildReason = ReasonLoc( + "Assigning from " + std::to_string(CRHS.size()) + " depth pointer to " + - std::to_string(CLHS.size()) + " depth pointer."; - PCLHS->constrainToWild(CS, Rsn, PL); - PCRHS->constrainToWild(CS, Rsn, PL); + std::to_string(CLHS.size()) + " depth pointer.", + Rsn.Location); + PCLHS->constrainToWild(CS, WildReason); + PCRHS->constrainToWild(CS, WildReason); } // Equate the corresponding FunctionConstraint. - constrainConsVarGeq(PCLHS->getFV(), PCRHS->getFV(), CS, PL, CA, + constrainConsVarGeq(PCLHS->getFV(), PCRHS->getFV(), CS, Reason, CA, DoEqType, Info, HandleBoundsKey); } } else @@ -1826,42 +1905,48 @@ void constrainConsVarGeq(ConstraintVariable *LHS, ConstraintVariable *RHS, llvm_unreachable("unknown kind"); } else { // Assigning from a function variable to a pointer variable? + auto Reason = ReasonLoc( + "Assigning from a function variable to a pointer variable", + Rsn.Location); PVConstraint *PCLHS = dyn_cast(LHS); FVConstraint *FCRHS = dyn_cast(RHS); if (PCLHS && FCRHS) { if (FVConstraint *FCLHS = PCLHS->getFV()) { - constrainConsVarGeq(FCLHS, FCRHS, CS, PL, CA, DoEqType, Info, + constrainConsVarGeq(FCLHS, FCRHS, CS, Reason, CA, DoEqType, Info, HandleBoundsKey); } else { - std::string Rsn = "Function:" + FCRHS->getName() + - " assigned to non-function pointer."; - LHS->constrainToWild(CS, Rsn, PL); - RHS->constrainToWild(CS, Rsn, PL); + auto WildReason = ReasonLoc("Function:" + FCRHS->getName() + + " assigned to non-function pointer.", + Rsn.Location); + LHS->constrainToWild(CS, WildReason); + RHS->constrainToWild(CS, WildReason); } } else { // Constrain everything in both to wild. - std::string Rsn = "Assignment to functions from variables"; - LHS->constrainToWild(CS, Rsn, PL); - RHS->constrainToWild(CS, Rsn, PL); + auto WildReason = ReasonLoc( + "Assignment to functions from variables", + Rsn.Location); + LHS->constrainToWild(CS, WildReason); + RHS->constrainToWild(CS, WildReason); } } } void constrainConsVarGeq(ConstraintVariable *LHS, const CVarSet &RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey) { for (const auto &J : RHS) - constrainConsVarGeq(LHS, J, CS, PL, CA, DoEqType, Info, HandleBoundsKey); + constrainConsVarGeq(LHS, J, CS, Rsn, CA, DoEqType, Info, HandleBoundsKey); } // Given an RHS and a LHS, constrain them to be equal. void constrainConsVarGeq(const CVarSet &LHS, const CVarSet &RHS, - Constraints &CS, PersistentSourceLoc *PL, + Constraints &CS, const ReasonLoc &Rsn, ConsAction CA, bool DoEqType, ProgramInfo *Info, bool HandleBoundsKey) { for (const auto &I : LHS) - constrainConsVarGeq(I, RHS, CS, PL, CA, DoEqType, Info, HandleBoundsKey); + constrainConsVarGeq(I, RHS, CS, Rsn, CA, DoEqType, Info, HandleBoundsKey); } // True if [C] is a PVConstraint that contains at least one Atom (i.e., @@ -1909,8 +1994,10 @@ void PointerVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV, "Merging error, pointer depth change"); Vars = NewVAtoms; SrcVars = NewSrcAtoms; - if (Name.empty()) + if (Name.empty()) { Name = From->Name; + OriginalTypeWithName = From->OriginalTypeWithName; + } SrcHasItype = SrcHasItype || From->SrcHasItype; if (!From->ItypeStr.empty()) ItypeStr = From->ItypeStr; @@ -1921,8 +2008,10 @@ void PointerVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV, if (!From->BoundsAnnotationStr.empty()) BoundsAnnotationStr = From->BoundsAnnotationStr; - if (From->GenericIndex >= 0) - GenericIndex = From->GenericIndex; + if (From->SourceGenericIndex >= 0) { + SourceGenericIndex = From->SourceGenericIndex; + InferredGenericIndex = From->InferredGenericIndex; + } if (FV) { assert(From->FV); FV->mergeDeclaration(From->FV, Info, ReasonFailed); @@ -1934,39 +2023,23 @@ void PointerVariableConstraint::mergeDeclaration(ConstraintVariable *FromCV, } Atom *PointerVariableConstraint::getAtom(unsigned AtomIdx, Constraints &CS) { - if (AtomIdx < Vars.size()) { - // If index is in bounds, just return the atom. - return Vars[AtomIdx]; - } - if (getIsGeneric() && AtomIdx == Vars.size()) { - // Polymorphic types don't know how "deep" their pointers are beforehand so, - // we need to create new atoms for new pointer levels on the fly. - std::string Stars(Vars.size(), '*'); - Atom *A = CS.getFreshVar(Name + Stars, VarAtom::V_Other); - Vars.push_back(A); - SrcVars.push_back(CS.getWild()); - return A; - } - return nullptr; + assert(AtomIdx < Vars.size()); + return Vars[AtomIdx]; } void PointerVariableConstraint::equateWithItype( - ProgramInfo &I, const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) { + ProgramInfo &I, const ReasonLoc &ReasonUnchangeable) { Constraints &CS = I.getConstraints(); assert(SrcVars.size() == Vars.size()); for (unsigned VarIdx = 0; VarIdx < Vars.size(); VarIdx++) { ConstAtom *CA = SrcVars[VarIdx]; if (isa(CA)) - CS.addConstraint(CS.createGeq( - Vars[VarIdx], CA, - ReasonUnchangeable.empty() ? DEFAULT_REASON : ReasonUnchangeable, PSL, - true)); + CS.addConstraint(CS.createGeq(Vars[VarIdx], CA,ReasonUnchangeable,true)); else Vars[VarIdx] = SrcVars[VarIdx]; } if (FV) { - FV->equateWithItype(I, ReasonUnchangeable, PSL); + FV->equateWithItype(I, ReasonUnchangeable); } } @@ -2008,11 +2081,10 @@ bool FunctionVariableConstraint::isOriginallyChecked() const { } void FunctionVariableConstraint::equateWithItype( - ProgramInfo &I, const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) { - ReturnVar.equateWithItype(I, ReasonUnchangeable, PSL); + ProgramInfo &I, const ReasonLoc &ReasonUnchangeable) { + ReturnVar.equateWithItype(I, ReasonUnchangeable); for (auto Param : ParamVars) - Param.equateWithItype(I, ReasonUnchangeable, PSL); + Param.equateWithItype(I, ReasonUnchangeable); } void FVComponentVariable::mergeDeclaration(FVComponentVariable *From, @@ -2040,18 +2112,30 @@ void FVComponentVariable::mergeDeclaration(FVComponentVariable *From, ReasonFailed); } -std::string FVComponentVariable::mkString(Constraints &CS, - bool EmitName) const { - return mkTypeStr(CS, EmitName) + mkItypeStr(CS); +std::string FVComponentVariable::mkString(Constraints &CS, bool EmitName, + bool ForItypeBase) const { + return mkTypeStr(CS, EmitName, "", ForItypeBase) + + mkItypeStr(CS, ForItypeBase); } std::string FVComponentVariable::mkTypeStr(Constraints &CS, bool EmitName, - std::string UseName) const { + std::string UseName, + bool ForItypeBase) const { std::string Ret; - // if checked or given new name, generate type - if (hasCheckedSolution(CS) || (EmitName && !UseName.empty())) { - Ret = ExternalConstraint->mkString(CS, EmitName, false, false, false, - UseName); + // If checked or given new name, generate new type from constraint variables. + // We also call to mkString when ForItypeBase is true to work around an issue + // with the type returned by getRewritableOriginalTy for function pointers + // declared with itypes where the function pointer parameters have a checked + // type (e.g., `void ((*fn)(int *)) : itype(_Ptr))`). The + // original type for the function pointer parameter is stored as `_Ptr`, + // so emitting this string does guarantee that we emit the unchecked type. By + // instead calling mkString the type is generated from the external constraint + // variable. Because the call is passed ForItypeBase, it will emit an + // unchecked type instead of the solved type. + if (ForItypeBase || hasCheckedSolution(CS) || (EmitName && !UseName.empty())) { + Ret = ExternalConstraint->mkString( + CS, MKSTRING_OPTS(EmitName = EmitName, UseName = UseName, + ForItypeBase = ForItypeBase)); } else { // if no need to generate type, try to use source if (!SourceDeclaration.empty()) @@ -2074,9 +2158,13 @@ std::string FVComponentVariable::mkTypeStr(Constraints &CS, bool EmitName, return Ret; } -std::string FVComponentVariable::mkItypeStr(Constraints &CS) const { - if (hasItypeSolution(CS)) - return " : itype(" + ExternalConstraint->mkString(CS, false, true) + ")"; +std::string +FVComponentVariable::mkItypeStr(Constraints &CS, bool ForItypeBase) const { + if (!ForItypeBase && hasItypeSolution(CS)) + return " : itype(" + + ExternalConstraint->mkString( + CS, MKSTRING_OPTS(EmitName = false, ForItype = true)) + + ")"; return ""; } @@ -2107,62 +2195,98 @@ FVComponentVariable::FVComponentVariable(const QualType &QT, clang::DeclaratorDecl *D, std::string N, ProgramInfo &I, const ASTContext &C, - std::string *InFunc, bool HasItype) { + std::string *InFunc, + bool PotentialGeneric, + bool HasItype) { ExternalConstraint = - new PVConstraint(QT, D, N, I, C, InFunc, -1, HasItype, nullptr, ITypeT); - InternalConstraint = - new PVConstraint(QT, D, N, I, C, InFunc, -1, HasItype, nullptr, ITypeT); - bool EquateChecked = (QT->isVoidPointerType() || QT->isFunctionPointerType()); - linkInternalExternal(I, EquateChecked); + new PVConstraint(QT, D, N, I, C, InFunc, -1, PotentialGeneric, HasItype, + nullptr, ITypeT); + if (!HasItype && QT->isVoidPointerType()) { + InternalConstraint = ExternalConstraint; + } else { + InternalConstraint = + new PVConstraint(QT, D, N, I, C, InFunc, -1, PotentialGeneric, HasItype, + nullptr, ITypeT); + bool EquateChecked = QT->isVoidPointerType(); + linkInternalExternal(I, EquateChecked); + } // Save the original source for the declaration if this is a param // declaration. This lets us avoid macro expansion in function pointer // parameters similarly to how we do it for pointers in regular function // declarations. if (D && D->getType() == QT) { - SourceRange SR = D->getSourceRange(); - SourceDeclaration = SR.isValid() ? getSourceText(SR, C) : ""; + SourceRange DRange = D->getSourceRange(); + if (DRange.isValid()) { + const SourceManager &SM = C.getSourceManager(); + SourceLocation DLoc = D->getLocation(); + CharSourceRange CSR; + if (SM.isBeforeInTranslationUnit(DRange.getEnd(), DLoc)) { + // It's not clear to me why, but the end of the SourceRange for the + // declaration can come before the SourceLocation for the declaration. + // This can result in SourceDeclaration failing to capture the whole + // declaration string, causing rewriting errors. In this case it is also + // necessary to use CharSourceRange::getCharRange to work around some + // cases where CharSourceRange::getTokenRange (the default behavior of + // getSourceText when passed a SourceRange) would not get the full + // declaration. For example: a parameter declared without a name such as + // `_Ptr` was captured as `_Ptr`. + CSR = CharSourceRange::getCharRange(DRange.getBegin(), DLoc); + } else { + CSR = CharSourceRange::getTokenRange(DRange); + } + + SourceDeclaration = getSourceText(CSR , C); + } else { + SourceDeclaration = ""; + } } } void FVComponentVariable::equateWithItype(ProgramInfo &I, - const std::string &ReasonUnchangeable, - PersistentSourceLoc *PSL) const { + const ReasonLoc &ReasonUnchangeable) const { Constraints &CS = I.getConstraints(); - const std::string ReasonUnchangeable2 = - (ReasonUnchangeable.empty() && ExternalConstraint->getIsGeneric()) - ? "Internal constraint for generic function declaration, " - "for which 3C currently does not support re-solving." + const ReasonLoc &ReasonUnchangeable2 = + (ReasonUnchangeable.isDefault() && ExternalConstraint->isGeneric()) + ? ReasonLoc("Internal constraint for generic function declaration, " + "for which 3C currently does not support re-solving.", + // TODO: What PSL should we actually use here? + ReasonUnchangeable.Location) : ReasonUnchangeable; - bool HasBounds = ExternalConstraint->srcHasBounds(); bool HasItype = ExternalConstraint->srcHasItype(); // If the type cannot change at all (ReasonUnchangeable2 is set), then we - // constrain both the external and internal types to not change. Otherwise, if - // the variable has bounds, then we don't want the checked (external) portion - // of the type to change because that could blow away the bounds, but we still - // allow the internal type to change so that the type can change from an itype - // to fully checked. - bool MustConstrainInternalType = !ReasonUnchangeable2.empty(); - if (HasItype && (MustConstrainInternalType || HasBounds)) { - ExternalConstraint->equateWithItype(I, ReasonUnchangeable2, PSL); + // constrain both the external and internal types to not change. + bool MustConstrainInternalType = !ReasonUnchangeable2.isDefault(); + // Otherwise, if a pointer is an array pointer with declared bounds or is a + // constant size array, then we want to ensure the external type continues to + // solve to ARR or NTARR; see the comment on + // ConstraintVariable::equateWithItype re how this is achieved. This avoids + // losing bounds on array pointers, and converting constant sized arrays into + // pointers. We still allow the internal type to change so that the type can + // change from an itype to fully checked. + bool MustBeArray = + ExternalConstraint->srcHasBounds() || ExternalConstraint->hasSomeSizedArr(); + if (HasItype && (MustConstrainInternalType || MustBeArray)) { + ExternalConstraint->equateWithItype(I, ReasonUnchangeable2); if (ExternalConstraint != InternalConstraint) linkInternalExternal(I, false); if (MustConstrainInternalType) - InternalConstraint->constrainToWild(CS, ReasonUnchangeable2, PSL); + InternalConstraint->constrainToWild(CS, ReasonUnchangeable2); } } void FVComponentVariable::linkInternalExternal(ProgramInfo &I, bool EquateChecked) const { Constraints &CS = I.getConstraints(); + auto LinkReason = ReasonLoc("Function Internal/External Link", PersistentSourceLoc()); for (unsigned J = 0; J < InternalConstraint->getCvars().size(); J++) { Atom *InternalA = InternalConstraint->getCvars()[J]; Atom *ExternalA = ExternalConstraint->getCvars()[J]; if (isa(InternalA) || isa(ExternalA)) { // Equate pointer types for internal and external parameter constraint // variables. - CS.addConstraint(CS.createGeq(InternalA, ExternalA, false)); - CS.addConstraint(CS.createGeq(ExternalA, InternalA, false)); + CS.addConstraint(CS.createGeq(InternalA, ExternalA, LinkReason, false)); + CS.addConstraint(CS.createGeq(ExternalA, InternalA, LinkReason, false)); if (!isa(ExternalA)) { // Constrain Internal >= External. If external solves to wild, then so @@ -2170,22 +2294,24 @@ void FVComponentVariable::linkInternalExternal(ProgramInfo &I, // use causes the internal variable to be wild because the external // variable solves to WILD only when there is an unsafe use that // cannot be resolved by inserting casts. - CS.addConstraint(CS.createGeq(InternalA, ExternalA, true)); + CS.addConstraint(CS.createGeq(InternalA, ExternalA, + LinkReason, true)); // Atoms of return constraint variables are unified after the first // level. This is because CheckedC does not allow assignment from e.g. // a function return of type `int ** : itype(_Ptr<_Ptr>)` to a // variable with type `int **`. - if (DisableFunctionEdges || DisableRDs || EquateChecked || - (ExternalConstraint->getName() == RETVAR && J > 0)) - CS.addConstraint(CS.createGeq(ExternalA, InternalA, true)); + if (_3COpts.DisableFunctionEdges || _3COpts.DisableRDs || + EquateChecked || (ExternalConstraint->getName() == RETVAR && J > 0)) + CS.addConstraint(CS.createGeq(ExternalA, InternalA, + LinkReason, true)); } } } if (FVConstraint *ExtFV = ExternalConstraint->getFV()) { FVConstraint *IntFV = InternalConstraint->getFV(); assert(IntFV != nullptr); - constrainConsVarGeq(ExtFV, IntFV, CS, nullptr, Same_to_Same, true, &I); + constrainConsVarGeq(ExtFV, IntFV, CS, LinkReason, Same_to_Same, true, &I); } } @@ -2200,7 +2326,8 @@ bool FVComponentVariable::solutionEqualTo(Constraints &CS, } FVComponentVariable::FVComponentVariable(FVComponentVariable *Ot, + ReasonLoc &Rsn, Constraints &CS) { - InternalConstraint = Ot->InternalConstraint->getCopy(CS); - ExternalConstraint = Ot->ExternalConstraint->getCopy(CS); + InternalConstraint = Ot->InternalConstraint->getCopy(Rsn,CS); + ExternalConstraint = Ot->ExternalConstraint->getCopy(Rsn,CS); } diff --git a/clang/lib/3C/Constraints.cpp b/clang/lib/3C/Constraints.cpp index c9e6c36da121..04ab233a76f1 100644 --- a/clang/lib/3C/Constraints.cpp +++ b/clang/lib/3C/Constraints.cpp @@ -13,35 +13,11 @@ #include "clang/3C/3CGlobalOptions.h" #include "clang/3C/ConstraintVariables.h" #include "clang/3C/ConstraintsGraph.h" -#include "llvm/Support/CommandLine.h" #include #include using namespace llvm; -static cl::OptionCategory SolverCategory("solver options"); -static cl::opt DebugSolver("debug-solver", - cl::desc("Dump intermediate solver state"), - cl::init(false), cl::cat(SolverCategory)); -static cl::opt OnlyGreatestSol( - "only-g-sol", - cl::desc("Perform only greatest solution for Pty Constrains."), - cl::init(false), cl::cat(SolverCategory)); - -static cl::opt - OnlyLeastSol("only-l-sol", - cl::desc("Perform only least solution for Pty Constrains."), - cl::init(false), cl::cat(SolverCategory)); - -Constraint::Constraint(ConstraintKind K, const std::string &Rsn, - PersistentSourceLoc *PL) - : Constraint(K, Rsn) { - if (PL != nullptr && PL->valid()) - this->PL = *PL; - else - this->PL = PersistentSourceLoc(); -} - // Remove the constraint from the global constraint set. bool Constraints::removeConstraint(Constraint *C) { bool RetVal = false; @@ -64,7 +40,7 @@ bool Constraints::removeConstraint(Constraint *C) { // Check if we can add this constraint. This provides a global switch to // control what constraints we can add to our system. void Constraints::editConstraintHook(Constraint *C) { - if (!AllTypes) { + if (!_3COpts.AllTypes) { // Invalidate any pointer-type constraints. if (Geq *E = dyn_cast(C)) { if (!E->constraintIsChecked()) { @@ -77,13 +53,17 @@ void Constraints::editConstraintHook(Constraint *C) { if (RHSA) { if (!dyn_cast(E->getLHS())) { E->setChecked(getWild()); - E->setReason(POINTER_IS_ARRAY_REASON); + ReasonLoc Rsn = E->getReason(); + Rsn.Reason = POINTER_IS_ARRAY_REASON; + E->setReason(Rsn); } } else { assert(LHSA && "Adding constraint between constants?!"); if (!dyn_cast(E->getRHS())) { E->setChecked(getWild()); - E->setReason(POINTER_IS_ARRAY_REASON); + ReasonLoc Rsn = E->getReason(); + Rsn.Reason = POINTER_IS_ARRAY_REASON; + E->setReason(Rsn); } } } @@ -94,16 +74,12 @@ void Constraints::editConstraintHook(Constraint *C) { // Add a constraint to the set of constraints. If the constraint is already // present (by syntactic equality) return false. bool Constraints::addConstraint(Constraint *C) { - // Validate the constraint to be added. - if (!check(C)) { - C->dump(); - assert(false); - } - editConstraintHook(C); + auto Search = TheConstraints.find(C); + // Check if C is already in the set of constraints. - if (TheConstraints.find(C) == TheConstraints.end()) { + if (Search == TheConstraints.end()) { TheConstraints.insert(C); if (Geq *G = dyn_cast(C)) { @@ -122,23 +98,31 @@ bool Constraints::addConstraint(Constraint *C) { else if (VarAtom *VRhs = dyn_cast(E->getRHS())) { VRhs->Constraints.insert(C); } - } else if (Implies *I = dyn_cast(C)) { - Geq *PEQ = I->getPremise(); - if (VarAtom *VLhs = dyn_cast(PEQ->getLHS())) - VLhs->Constraints.insert(C); } else llvm_unreachable("unsupported constraint"); return true; } + // If the constraint being added is due to unwritability, + // propagate this reason to the existing constraint. + // This way we always prioritize the unwritability as the reason + // for wildness. + // This is needed as 3C will currently only report one cause of wildness + // (See https://github.com/correctcomputation/checkedc-clang/issues/664) + if (C->isUnwritable()) { + auto *StoredConstraint = *Search; + StoredConstraint->setReason(C->getReason()); + } + return false; } bool Constraints::addReasonBasedConstraint(Constraint *C) { // Only insert if this is an Eq constraint and has a valid reason. if (Geq *E = dyn_cast(C)) { - if (E->getReason() != DEFAULT_REASON && !E->getReason().empty()) - return this->ConstraintsByReason[E->getReason()].insert(E).second; + if (E->getReasonText() != DEFAULT_REASON && !E->getReasonText().empty() && + E->getReason().Location.valid()) + return this->ConstraintsByReason[E->getReasonText()].insert(E).second; } return false; } @@ -146,34 +130,13 @@ bool Constraints::addReasonBasedConstraint(Constraint *C) { bool Constraints::removeReasonBasedConstraint(Constraint *C) { if (Geq *E = dyn_cast(C)) { // Remove if the constraint is present. - if (this->ConstraintsByReason.find(E->getReason()) != + if (this->ConstraintsByReason.find(E->getReasonText()) != this->ConstraintsByReason.end()) - return this->ConstraintsByReason[E->getReason()].erase(E) > 0; + return this->ConstraintsByReason[E->getReasonText()].erase(E) > 0; } return false; } -// Checks to see if the constraint is of a form that we expect. -// The expected forms are the following: -// EQ : (q_i = q_k) -// GEQ : (q_i >= A) for A constant -// IMPLIES : (q_i >= A) => (q_k >= B) for A,B constant. -bool Constraints::check(Constraint *C) { - - if (Implies *I = dyn_cast(C)) { - Geq *P = I->getPremise(); - Geq *CO = I->getConclusion(); - if (!isa(P->getLHS()) || isa(P->getRHS()) || - !isa(CO->getLHS()) || isa(CO->getRHS())) - return false; - } else if (dyn_cast(C) != nullptr) { - // all good! - } else - return false; // Not Eq, Geq, or Implies; what is it?! - - return true; -} - // Make a graph G: //- with nodes for each variable k and each qualifier constant q. //- with edges Q --> Q’ for each constraint Q <: Q’ @@ -195,81 +158,54 @@ bool Constraints::check(Constraint *C) { static bool doSolve(ConstraintsGraph &CG, - std::set SavedImplies, // TODO: Can this be a ref? ConstraintsEnv &Env, Constraints *CS, bool DoLeastSolution, - std::set *InitVs, std::set &Conflicts) { + std::set *InitVs, + std::set &Conflicts) { std::vector WorkList; - std::set FiredImplies; // Initialize with seeded VarAtom set (pre-solved). if (InitVs != nullptr) WorkList.insert(WorkList.begin(), InitVs->begin(), InitVs->end()); - do { - // Initialize work list with ConstAtoms. - auto &InitC = CG.getAllConstAtoms(); - WorkList.insert(WorkList.begin(), InitC.begin(), InitC.end()); - - while (!WorkList.empty()) { - auto *Curr = *(WorkList.begin()); - // Remove the first element, get its solution. - WorkList.erase(WorkList.begin()); - ConstAtom *CurrSol = Env.getAssignment(Curr); - - // get its neighbors. - std::set Neighbors; - CG.getNeighbors(Curr, Neighbors, DoLeastSolution); - // update each successor's solution. - for (auto *NeighborA : Neighbors) { - bool Changed = false; - if (VarAtom *Neighbor = dyn_cast(NeighborA)) { - ConstAtom *NghSol = Env.getAssignment(Neighbor); - // update solution if doing so would change it - // checked? --- if sol(Neighbor) <> (sol(Neighbor) JOIN Cur) - // else --- if sol(Neighbor) <> (sol(Neighbor) MEET Cur) - if ((DoLeastSolution && *NghSol < *CurrSol) || - (!DoLeastSolution && *CurrSol < *NghSol)) { - // ---- set sol(k) := (sol(k) JOIN/MEET Q) - Changed = Env.assign(Neighbor, CurrSol); - assert(Changed); - WorkList.push_back(Neighbor); - } - } // ignore ConstAtoms for now; will confirm solution below - } - } - FiredImplies.clear(); - - // If there are some implications that we saved? Propagate them. - if (!SavedImplies.empty()) { - // Check if Premise holds. If yes then fire the conclusion. - for (auto *Imp : SavedImplies) { - Geq *Pre = Imp->getPremise(); - Geq *Con = Imp->getConclusion(); - ConstAtom *Cca = Env.getAssignment(Pre->getRHS()); - ConstAtom *Cva = Env.getAssignment(Pre->getLHS()); - // Premise is true, so fire the conclusion. - if (*Cca < *Cva || *Cca == *Cva) { - CG.addConstraint(Con, *CS); - // Keep track of fired constraints, so that we can delete them. - FiredImplies.insert(Imp); + // Initialize work list with ConstAtoms. + auto &InitC = CG.getAllConstAtoms(); + WorkList.insert(WorkList.begin(), InitC.begin(), InitC.end()); + + while (!WorkList.empty()) { + auto *Curr = *(WorkList.begin()); + // Remove the first element, get its solution. + WorkList.erase(WorkList.begin()); + ConstAtom *CurrSol = Env.getAssignment(Curr); + + // get its neighbors. + std::set Neighbors; + CG.getNeighbors(Curr, Neighbors, DoLeastSolution); + // update each successor's solution. + for (auto *NeighborA : Neighbors) { + if (VarAtom *Neighbor = dyn_cast(NeighborA)) { + ConstAtom *NghSol = Env.getAssignment(Neighbor); + // update solution if doing so would change it + // checked? --- if sol(Neighbor) <> (sol(Neighbor) JOIN Cur) + // else --- if sol(Neighbor) <> (sol(Neighbor) MEET Cur) + if ((DoLeastSolution && *NghSol < *CurrSol) || + (!DoLeastSolution && *CurrSol < *NghSol)) { + // ---- set sol(k) := (sol(k) JOIN/MEET Q) + bool Changed = Env.assign(Neighbor, CurrSol); + assert(Changed); + WorkList.push_back(Neighbor); } - } - // Erase all the fired implies. - for (auto *ToDel : FiredImplies) { - SavedImplies.erase(ToDel); - } + } // ignore ConstAtoms for now; will confirm solution below } - // Lets repeat if there are some fired constraints. - } while (!FiredImplies.empty()); + } // Check Upper/lower bounds hold; collect failures in conflicts set. - std::set Neighbors; + std::set IncidentEdges; bool Ok = true; for (ConstAtom *Cbound : CG.getAllConstAtoms()) { - if (CG.getNeighbors(Cbound, Neighbors, !DoLeastSolution)) { - for (Atom *A : Neighbors) { - VarAtom *VA = dyn_cast(A); + if (CG.getIncidentEdges(Cbound, IncidentEdges, !DoLeastSolution)) { + for (auto *E : IncidentEdges) { + VarAtom *VA = dyn_cast(E->getTargetNode().getData()); if (VA == nullptr) continue; ConstAtom *Csol = Env.getAssignment(VA); @@ -280,9 +216,9 @@ doSolve(ConstraintsGraph &CG, // wild after pointer type solving is finished. Checked types will // be resolved with this new constraint, transitively propagating the // new WILD-ness. - Conflicts.insert(VA); + Conflicts.insert(E); // Failure case. - if (Verbose) { + if (_3COpts.Verbose) { errs() << "Unsolvable constraints: "; VA->print(errs()); errs() << "="; @@ -369,11 +305,9 @@ static std::set findBounded(ConstraintsGraph &CG, } bool Constraints::graphBasedSolve() { - std::set Conflicts; + std::set Conflicts; ConstraintsGraph SolChkCG; ConstraintsGraph SolPtrTypCG; - std::set SavedImplies; - std::set Empty; ConstraintsEnv &Env = Environment; // Checked well-formedness. @@ -388,30 +322,23 @@ bool Constraints::graphBasedSolve() { // Need to copy whether or not this constraint into the new graph SolPtrTypCG.addConstraint(G, *this); } - // Save the implies to solve them later. - else if (Implies *Imp = dyn_cast(C)) { - assert(Imp->getConclusion()->constraintIsChecked() && - Imp->getPremise()->constraintIsChecked()); - SavedImplies.insert(Imp); - } } - if (DebugSolver) + if (_3COpts.DebugSolver) GraphVizOutputGraph::dumpConstraintGraphs("initial_constraints_graph.dot", SolChkCG, SolPtrTypCG); // Solve Checked/unchecked constraints first. Env.doCheckedSolve(true); - bool Res = - doSolve(SolChkCG, SavedImplies, Env, this, true, nullptr, Conflicts); + bool Res = doSolve(SolChkCG, Env, this, true, nullptr, Conflicts); // Now solve PtrType constraints - if (Res && AllTypes) { + if (Res && _3COpts.AllTypes) { Env.doCheckedSolve(false); - bool RegularSolve = !(OnlyGreatestSol || OnlyLeastSol); + bool RegularSolve = !(_3COpts.OnlyGreatestSol || _3COpts.OnlyLeastSol); - if (OnlyLeastSol) { + if (_3COpts.OnlyLeastSol) { // Do only least solution. // First reset ptr solution to NTArr. Env.resetSolution( @@ -420,14 +347,14 @@ bool Constraints::graphBasedSolve() { return true; }, getNTArr()); - Res = doSolve(SolPtrTypCG, Empty, Env, this, true, nullptr, Conflicts); - } else if (OnlyGreatestSol) { + Res = doSolve(SolPtrTypCG, Env, this, true, nullptr, Conflicts); + } else if (_3COpts.OnlyGreatestSol) { // Do only greatest solution - Res = doSolve(SolPtrTypCG, Empty, Env, this, false, nullptr, Conflicts); + Res = doSolve(SolPtrTypCG, Env, this, false, nullptr, Conflicts); } else { // Regular solve // Step 1: Greatest solution - Res = doSolve(SolPtrTypCG, Empty, Env, this, false, nullptr, Conflicts); + Res = doSolve(SolPtrTypCG, Env, this, false, nullptr, Conflicts); } // Step 2: Reset all solutions but for function params, @@ -473,7 +400,7 @@ bool Constraints::graphBasedSolve() { // a lower bound will be resolved in the final greatest solution. std::set LowerBounded = findBounded(SolPtrTypCG, &Rest, true); - Res = doSolve(SolPtrTypCG, Empty, Env, this, true, &Rest, Conflicts); + Res = doSolve(SolPtrTypCG, Env, this, true, &Rest, Conflicts); // Step 3: Reset local variable solutions, compute greatest if (Res) { @@ -486,33 +413,51 @@ bool Constraints::graphBasedSolve() { }, getPtr()); - Res = doSolve(SolPtrTypCG, Empty, Env, this, false, &Rest, Conflicts); + Res = doSolve(SolPtrTypCG, Env, this, false, &Rest, Conflicts); } } // If PtrType solving (partly) failed, make the affected VarAtoms wild. if (!Res) { std::set Rest; Env.doCheckedSolve(true); - for (VarAtom *VA : Conflicts) { - assert(VA != nullptr); - std::string Rsn = "Bad pointer type solution"; - Geq *ConflictConstraint = createGeq(VA, getWild(), Rsn); + for (auto *Conflict : Conflicts) { + Atom *ConflictAtom = Conflict->getTargetNode().getData(); + assert(ConflictAtom != nullptr); + ReasonLoc Rsn1 = Conflict->EdgeConstraint->getReason(); + // Determine a second from the constraints immediately incident to the + // conflicting atom. A future improvement should traverse the + // constraint graph to find the contradictory constraints to constant + // atoms. See correctcomputation/checkedc-clang#680. + auto Succs = Conflict->getTargetNode().getEdges(); + ReasonLoc Rsn2; + for (auto *Succ : Succs) { + if (auto *SuccGeq = dyn_cast(Succ->EdgeConstraint)) { + if (Env.getAssignment(ConflictAtom) == + Env.getAssignment(SuccGeq->getLHS()) || + Env.getAssignment(ConflictAtom) == + Env.getAssignment(SuccGeq->getRHS())) { + Rsn2 = Succ->EdgeConstraint->getReason(); + break; + } + } + } + auto Rsn = ReasonLoc("Inferred conflicting types", + PersistentSourceLoc()); + Geq *ConflictConstraint = createGeq(ConflictAtom, getWild(), Rsn); + ConflictConstraint->addReason(Rsn1); + ConflictConstraint->addReason(Rsn2); addConstraint(ConflictConstraint); SolChkCG.addConstraint(ConflictConstraint, *this); - Rest.insert(VA); + Rest.insert(cast(ConflictAtom)); } Conflicts.clear(); /* FIXME: Should we propagate the old res? */ - Res = doSolve(SolChkCG, SavedImplies, Env, this, true, &Rest, Conflicts); + Res = doSolve(SolChkCG, Env, this, true, &Rest, Conflicts); } // Final Step: Merge ptyp solution with checked solution. Env.mergePtrTypes(); } - if (DebugSolver) - GraphVizOutputGraph::dumpConstraintGraphs( - "implication_constraints_graph.dot", SolChkCG, SolPtrTypCG); - return Res; } @@ -521,13 +466,13 @@ bool Constraints::graphBasedSolve() { // an empty. If the system could not be solved, the constraints in conflict // are returned in the first position. void Constraints::solve() { - if (DebugSolver) { + if (_3COpts.DebugSolver) { errs() << "constraints beginning solve\n"; dump(); } graphBasedSolve(); - if (DebugSolver) { + if (_3COpts.DebugSolver) { errs() << "solution, when done solving\n"; Environment.dump(); } @@ -596,10 +541,9 @@ VarAtom *Constraints::getVar(ConstraintKey V) const { // should generally be used instead of using constant atoms directly if the the // VarAtom will be used in the variables vector of a PVConstraint. VarAtom *Constraints::createFreshGEQ(std::string Name, VarAtom::VarKind VK, - ConstAtom *Con, std::string Rsn, - PersistentSourceLoc *PSL) { + ConstAtom *Con, ReasonLoc Rsn) { VarAtom *VA = getFreshVar(Name, VK); - addConstraint(createGeq(VA, Con, Rsn, PSL)); + addConstraint(createGeq(VA, Con, Rsn)); return VA; } @@ -613,41 +557,27 @@ ConstAtom *Constraints::getAssignment(Atom *A) { return Environment.getAssignment(A); } -ConstraintsGraph &Constraints::getChkCG() { +const ConstraintsGraph &Constraints::getChkCG() const { assert(ChkCG != nullptr && "Checked Constraint graph cannot be nullptr"); return *ChkCG; } -ConstraintsGraph &Constraints::getPtrTypCG() { +const ConstraintsGraph &Constraints::getPtrTypCG() const { assert(PtrTypCG != nullptr && "Pointer type Constraint graph " "cannot be nullptr"); return *PtrTypCG; } -Geq *Constraints::createGeq(Atom *Lhs, Atom *Rhs, bool IsCheckedConstraint, - bool Soft) { - return new Geq(Lhs, Rhs, IsCheckedConstraint, Soft); -} - -Geq *Constraints::createGeq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, - bool IsCheckedConstraint) { - return new Geq(Lhs, Rhs, Rsn, IsCheckedConstraint); -} - -Geq *Constraints::createGeq(Atom *Lhs, Atom *Rhs, const std::string &Rsn, - PersistentSourceLoc *PL, bool IsCheckedConstraint) { - if (PL != nullptr && PL->valid()) { +Geq *Constraints::createGeq(Atom *Lhs, Atom *Rhs, ReasonLoc Rsn, + bool IsCheckedConstraint, bool Soft) { + if (Rsn.Location.valid()) { // Make this invalid, if the source location is not absolute path // this is to avoid crashes in clangd. - if (!llvm::sys::path::is_absolute(PL->getFileName())) - PL = nullptr; + if (!llvm::sys::path::is_absolute(Rsn.Location.getFileName())) + Rsn.Location = PersistentSourceLoc(); } assert("Shouldn't be constraining WILD >= VAR" && Lhs != getWild()); - return new Geq(Lhs, Rhs, Rsn, PL, IsCheckedConstraint); -} - -Implies *Constraints::createImplies(Geq *Premise, Geq *Conclusion) { - return new Implies(Premise, Conclusion); + return new Geq(Lhs, Rhs, Rsn, IsCheckedConstraint, Soft); } void Constraints::resetEnvironment() { diff --git a/clang/lib/3C/ConstraintsGraph.cpp b/clang/lib/3C/ConstraintsGraph.cpp index 6908c47dc17c..ed73c1d49909 100644 --- a/clang/lib/3C/ConstraintsGraph.cpp +++ b/clang/lib/3C/ConstraintsGraph.cpp @@ -32,7 +32,7 @@ void ConstraintsGraph::addConstraint(Geq *C, const Constraints &CS) { if (auto *VA2 = clang::dyn_cast(A2)) assert(CS.getVar(VA2->getLoc()) == VA2); - addEdge(A2, A1, C->isSoft()); + addEdge(A2, A1, C->isSoft(), C); } std::string llvm::DOTGraphTraits::getNodeLabel( diff --git a/clang/lib/3C/CtxSensAVarBounds.cpp b/clang/lib/3C/CtxSensAVarBounds.cpp index 0224f56cf169..f3916876fb64 100644 --- a/clang/lib/3C/CtxSensAVarBounds.cpp +++ b/clang/lib/3C/CtxSensAVarBounds.cpp @@ -10,7 +10,6 @@ // CtxSensAVarBounds.h // //===----------------------------------------------------------------------===// - #include "clang/3C/CtxSensAVarBounds.h" #include "clang/3C/AVarBoundsInfo.h" #include "clang/3C/ConstraintResolver.h" @@ -23,31 +22,30 @@ // We will ignore array indexing. class StructAccessVisitor : public RecursiveASTVisitor { public: - std::vector StructAccessStr; - bool IsGlobal = false; - - explicit StructAccessVisitor(ASTContext *Ctx) : C(Ctx) { - StructAccessStr.clear(); - } + explicit StructAccessVisitor(ASTContext *Ctx) : C(Ctx), StructAccessStr() {} virtual ~StructAccessVisitor() { StructAccessStr.clear(); } + bool isGlobal() const { return IsGlobal; } + void processVarDecl(VarDecl *VD) { - if (VD != nullptr && - (VD->getType()->isPointerType() || VD->getType()->isStructureType())) { + assert("StructAccessVisitor visiting null VarDecl" && VD != nullptr); + if (VD->getType()->isPointerType() || VD->getType()->isStructureType()) { + // If VD is a ParmVarDecl isLocalVarDecl will return false + // FIXME: This method can be called multiple times, and each time it will + // set the value of IsGlobal. This could be a problem if it is + // called on a global and then a local variable. e.g.: + // ( 0 ? global_struct_var : local_struct_var).array + // IsGlobal might be true or false depending on the order it + // encounters the variables. This would also change the order of + // entries in StructAccessStr. IsGlobal = !VD->isLocalVarDecl(); StructAccessStr.insert(StructAccessStr.begin(), VD->getNameAsString()); } } bool VisitDeclRefExpr(DeclRefExpr *DRE) { - ParmVarDecl *PD = dyn_cast_or_null(DRE->getDecl()); - if (PD != nullptr && - (PD->getType()->isPointerType() || PD->getType()->isStructureType())) { - StructAccessStr.insert(StructAccessStr.begin(), PD->getNameAsString()); - } else { - VarDecl *VD = dyn_cast_or_null(DRE->getDecl()); + if (auto *VD = dyn_cast_or_null(DRE->getDecl())) processVarDecl(VD); - } return true; } @@ -59,7 +57,7 @@ class StructAccessVisitor : public RecursiveASTVisitor { } // This gives us a string serves as a key for a struct member access. - std::string getStructAccessKey() { + std::string getStructAccessKey() const { std::string Ret = ""; for (auto CurrStr : StructAccessStr) { Ret = CurrStr + ":" + Ret; @@ -69,6 +67,8 @@ class StructAccessVisitor : public RecursiveASTVisitor { private: ASTContext *C; + bool IsGlobal = false; + std::vector StructAccessStr; }; void CtxSensitiveBoundsKeyHandler::insertCtxSensBoundsKey( @@ -78,6 +78,12 @@ void CtxSensitiveBoundsKeyHandler::insertCtxSensBoundsKey( ABI->insertProgramVar(NK, NKVar); ABI->RevCtxSensProgVarGraph.addUniqueEdge(OldPV->getKey(), NKVar->getKey()); ABI->CtxSensProgVarGraph.addUniqueEdge(NKVar->getKey(), OldPV->getKey()); + + // For context sensitive structure keys, I want the structure lower bound to + // be invalidated if any use is invalidated, but function parameter lower + // bounds should not be invalidated by invalidated arguments. + if (isa(NPS)) + ABI->LowerBoundGraph.addUniqueEdge(NKVar->getKey(), OldPV->getKey()); } void CtxSensitiveBoundsKeyHandler::createCtxSensBoundsKey( @@ -92,13 +98,14 @@ void CtxSensitiveBoundsKeyHandler::createCtxSensBoundsKey( BoundsPriority TP = Invalid; ABounds *CKBounds = ABI->getBounds(OK, Invalid, &TP); if (CKBounds != nullptr) { - BoundsKey NBK = CKBounds->getBKey(); - if (CBMap.find(NBK) == CBMap.end()) { + BoundsKey LenBK = CKBounds->getLengthKey(); + if (CBMap.find(LenBK) == CBMap.end()) { BoundsKey TmpBK = ++(ABI->BCount); - CBMap[NBK] = TmpBK; - insertCtxSensBoundsKey(CKVar, TmpBK, NPS); + CBMap[LenBK] = TmpBK; + ProgramVar *LenVar = ABI->getProgramVar(LenBK); + insertCtxSensBoundsKey(LenVar, TmpBK, NPS); } - CKBounds = CKBounds->makeCopy(CBMap[NBK]); + CKBounds = CKBounds->makeCopy(CBMap[LenBK]); ABI->replaceBounds(NK, TP, CKBounds); } } @@ -146,7 +153,7 @@ CtxStKeyMap *CtxSensitiveBoundsKeyHandler::getCtxStKeyMap(MemberExpr *ME, ASTContext *C) { StructAccessVisitor SKV(C); SKV.TraverseStmt(ME->getBase()->getExprStmt()); - CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.IsGlobal); + CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.isGlobal()); return MECSMap; } @@ -170,7 +177,7 @@ bool CtxSensitiveBoundsKeyHandler::tryGetFieldCSKey( FieldDecl *FD, CtxStKeyMap *CSK, const std::string &AK, ASTContext *C, ProgramInfo &I, BoundsKey &CSKey) { bool RetVal = false; - if (CSK->find(AK) != CSK->end()) { + if (ABI->isValidBoundVariable(FD) && CSK->find(AK) != CSK->end()) { CVarOption CV = I.getVariable(FD, C); BoundsKey OrigK; if (CV.hasValue() && CV.getValue().hasBoundsKey()) { @@ -225,44 +232,41 @@ void CtxSensitiveBoundsKeyHandler::contextualizeStructRecord( } } +void CtxSensitiveBoundsKeyHandler::contextualizeCVar(RecordDecl *RD, + std::string AccessKey, + bool IsGlobal, + ASTContext *C, + ProgramInfo &I) { + std::string FileName = PersistentSourceLoc::mkPSL(RD, *C).getFileName(); + if (canWrite(FileName)) { + // Context sensitive struct key map. + CtxStKeyMap *MECSMap = getCtxStKeyMap(IsGlobal); + auto &BKeyMap = (*MECSMap)[AccessKey]; + contextualizeStructRecord(I, C, RD, AccessKey, BKeyMap, IsGlobal); + } +} + void CtxSensitiveBoundsKeyHandler::contextualizeCVar(MemberExpr *ME, ASTContext *C, ProgramInfo &I) { FieldDecl *FD = dyn_cast_or_null(ME->getMemberDecl()); - if (FD != nullptr) { - RecordDecl *RD = FD->getParent(); - // If the base decl is not null. - if (RD != nullptr) { - // Get structure access key. - StructAccessVisitor SKV(C); - SKV.TraverseStmt(ME->getBase()->getExprStmt()); - std::string AK = SKV.getStructAccessKey(); - // Context sensitive struct key map. - CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.IsGlobal); - auto &BKeyMap = (*MECSMap)[AK]; - contextualizeStructRecord(I, C, RD, AK, BKeyMap, SKV.IsGlobal); - } + if (RecordDecl *RD = FD != nullptr ? FD->getParent() : nullptr) { + // Get structure access key. + StructAccessVisitor SKV(C); + SKV.TraverseStmt(ME->getBase()->getExprStmt()); + contextualizeCVar(RD, SKV.getStructAccessKey(), SKV.isGlobal(), C, I); } } void CtxSensitiveBoundsKeyHandler::contextualizeCVar(VarDecl *VD, ASTContext *C, ProgramInfo &I) { - const RecordType *RT = dyn_cast_or_null( - VD->getType()->getUnqualifiedDesugaredType()); - const RecordDecl *RD = nullptr; - if (RT != nullptr) { - RD = RT->getDecl(); - } - // If the base decl is not null. - if (RT != nullptr) { + const auto *RT = dyn_cast_or_null( + VD->getType()->getUnqualifiedDesugaredType()); + if (RecordDecl *RD = RT != nullptr ? RT->getDecl() : nullptr) { // Get structure access key. StructAccessVisitor SKV(C); SKV.processVarDecl(VD); - // Context sensitive struct key map. - CtxStKeyMap *MECSMap = getCtxStKeyMap(SKV.IsGlobal); - std::string AK = SKV.getStructAccessKey(); - auto &BKeyMap = (*MECSMap)[AK]; - contextualizeStructRecord(I, C, RD, AK, BKeyMap, SKV.IsGlobal); + contextualizeCVar(RD, SKV.getStructAccessKey(), SKV.isGlobal(), C, I); } } @@ -366,7 +370,7 @@ bool ContextSensitiveBoundsKeyVisitor::VisitDeclStmt(DeclStmt *DS) { // to various fields of the structure variable. const RecordDecl *Definition = ILE->getType()->getAsStructureType()->getDecl()->getDefinition(); - auto *CSKeyMap = CSBHandler.getCtxStKeyMap(SAV.IsGlobal); + auto *CSKeyMap = CSBHandler.getCtxStKeyMap(SAV.isGlobal()); unsigned int InitIdx = 0; const auto Fields = Definition->fields(); for (auto It = Fields.begin(); diff --git a/clang/lib/3C/DeclRewriter.cpp b/clang/lib/3C/DeclRewriter.cpp index bc5102954cf5..7b19b25cd6e6 100644 --- a/clang/lib/3C/DeclRewriter.cpp +++ b/clang/lib/3C/DeclRewriter.cpp @@ -27,6 +27,153 @@ using namespace llvm; using namespace clang; +static bool checkNeedsFreshLowerBound(PVConstraint *Defn, std::string UseName, + ProgramInfo &Info, + std::string &DeclName) { + bool NeedsFreshLowerBound = Info.getABoundsInfo().needsFreshLowerBound(Defn); + + if (NeedsFreshLowerBound) { + BoundsKey FreshLB = Info.getABoundsInfo() + .getBounds(Defn->getBoundsKey()) + ->getLowerBoundKey(); + DeclName = Info.getABoundsInfo().getProgramVar(FreshLB)->getVarName(); + } else { + DeclName = UseName; + } + + return NeedsFreshLowerBound; +} + +static std::string buildSupplementaryDecl(PVConstraint *Defn, + DeclaratorDecl *Decl, + ArrayBoundsRewriter &ABR, + ProgramInfo &Info, bool SDeclChecked, + std::string DeclName) { + std::string SDecl = Defn->mkString( + Info.getConstraints(), MKSTRING_OPTS(ForItypeBase = !SDeclChecked)); + if (SDeclChecked) + SDecl += ABR.getBoundsString(Defn, Decl); + SDecl += " = " + DeclName + ";"; + return SDecl; +} + +// Generate a new declaration for the PVConstraint using an itype where the +// unchecked portion of the type is the original type, and the checked portion +// is the taken from the constraint graph solution. +// +// If SDeclChecked = true, then any generated supplementary declaration uses the +// checked type; that's generally what you want if an itype is being used only +// because of -itypes-for-extern. If SDeclChecked = false, then the unchecked +// type is used; that's what you want when the supplementary declaration is +// standing in for a function parameter that got an itype because it is used +// unsafely inside the function. TODO: Instead of using an ad-hoc boolean +// parameter for this, maybe we could just pass in the internal PVConstraint and +// look at that +// (https://github.com/correctcomputation/checkedc-clang/issues/704). +RewrittenDecl +DeclRewriter::buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, ProgramInfo &Info, + ArrayBoundsRewriter &ABR, bool GenerateSDecls, + bool SDeclChecked) { + std::string DeclName; + bool NeedsFreshLowerBound = + checkNeedsFreshLowerBound(Defn, UseName, Info, DeclName); + + const EnvironmentMap &Env = Info.getConstraints().getVariables(); + // True when the type of this variable is defined by a typedef, and the + // constraint variable representing the typedef solved to an unchecked type. + // In these cases, the typedef should be used in the unchecked part of the + // itype. The typedef is expanded using checked pointer types for the checked + // portion. In ItypesForExtern mode, typedefs are treated as unchecked because + // 3C will not rewrite the typedef to a checked type. Even if it solves to a + // checked type, it is not rewritten, so it remains unchecked in the converted + // code. + bool IsUncheckedTypedef = + Defn->isTypedef() && (_3COpts.ItypesForExtern || + !Defn->getTypedefVar()->isSolutionChecked(Env)); + // True if this variable is defined by a typedef, and the constraint variable + // representing the typedef solves to a checked type. Notably not the negation + // of IsUncheckedTypedef because both require the variable type be defined + // by a typedef. The checked typedef is expanded using unchecked types in the + // unchecked portion of the itype. The typedef is used directly in the checked + // portion of the itype. TODO: Maybe we shouldn't do that if the solution for + // the typedef doesn't fully equal the solution for the variable + // (https://github.com/correctcomputation/checkedc-clang/issues/705)? + bool IsCheckedTypedef = Defn->isTypedef() && !IsUncheckedTypedef; + + bool BaseTypeRenamed = + Decl && Info.TheMultiDeclsInfo.wasBaseTypeRenamed(Decl); + + // It should in principle be possible to always generate the unchecked portion + // of the itype by going through mkString. However, mkString has bugs that + // lead to incorrect output in some less common cases + // (https://github.com/correctcomputation/checkedc-clang/issues/703). So we + // use the original type string generated by Clang (via qtyToStr or + // getOriginalTypeWithName) unless we know we have a special requirement that + // it doesn't meet, in which case we use mkString. Those cases are: + // - Unmasking a typedef. The expansion of the typedef does not exist in the + // original source, so it must be constructed. (TODO: Couldn't we just get + // the underlying type with TypedefDecl::getUnderlyingType and then use + // qtyToStr?) + // - A function pointer. For a function pointer with an itype to parse + // correctly, it needs an extra set of parentheses (e.g., + // `void ((*f)()) : itype(...)` instead of `void (*f)() : itype(...)`), and + // Clang won't know to add them. + // - When the base type is an unnamed TagDecl that 3C has renamed, Clang won't + // know the new name. + // - Possible future change: if the internal PVConstraint is partially checked + // and we want to use it + // (https://github.com/correctcomputation/checkedc-clang/issues/704). + std::string Type; + if (IsCheckedTypedef || Defn->getFV() || BaseTypeRenamed) { + Type = Defn->mkString(Info.getConstraints(), + MKSTRING_OPTS(UnmaskTypedef = IsCheckedTypedef, + ForItypeBase = true, + UseName = DeclName)); + } else { + // In the remaining cases, the unchecked portion of the itype is just the + // original type of the pointer. The first branch tries to generate the type + // using the type and name for this specific declaration. This is important + // because it avoids changing parameter names, particularly in cases where + // multiple functions sharing the same name are defined in different + // translation units. + if (isa_and_nonnull(Decl) && !DeclName.empty()) + Type = qtyToStr(Decl->getType(), DeclName); + else + Type = Defn->getOriginalTypeWithName(); + } + + std::string IType = " : itype(" + + Defn->mkString(Info.getConstraints(), + MKSTRING_OPTS(EmitName = false, ForItype = true, + UnmaskTypedef = IsUncheckedTypedef)) + ")"; + IType += ABR.getBoundsString(Defn, Decl, true, NeedsFreshLowerBound); + + std::string SDecl; + if (GenerateSDecls && NeedsFreshLowerBound) + SDecl = + buildSupplementaryDecl(Defn, Decl, ABR, Info, SDeclChecked, DeclName); + return RewrittenDecl(Type, IType, SDecl); +} + +RewrittenDecl +DeclRewriter::buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, ProgramInfo &Info, + ArrayBoundsRewriter &ABR, bool GenerateSDecls) { + std::string DeclName; + bool NeedsFreshLowerBound = + checkNeedsFreshLowerBound(Defn, UseName, Info, DeclName); + + std::string Type = + Defn->mkString(Info.getConstraints(), MKSTRING_OPTS(UseName = DeclName)); + std::string IType = + ABR.getBoundsString(Defn, Decl, false, NeedsFreshLowerBound); + std::string SDecl; + if (GenerateSDecls && NeedsFreshLowerBound) + SDecl = buildSupplementaryDecl(Defn, Decl, ABR, Info, true, DeclName); + return RewrittenDecl(Type, IType, SDecl); +} + // This function is the public entry point for declaration rewriting. void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, Rewriter &R) { @@ -35,7 +182,7 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, // Collect function and record declarations that need to be rewritten in a set // as well as their rewriten types in a map. - RSet RewriteThese(DComp(Context.getSourceManager())); + RSet RewriteThese; FunctionDeclBuilder *TRV = nullptr; #ifdef FIVE_C @@ -50,7 +197,11 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, for (const auto &D : Context.getTranslationUnitDecl()->decls()) { TRV->TraverseDecl(D); SVI.TraverseDecl(D); - if (const auto &TD = dyn_cast(D)) { + const auto &TD = dyn_cast(D); + // Don't convert typedefs when -itype-for-extern is passed. Typedefs will + // keep their unchecked type but function using the typedef will be given a + // checked itype. + if (!_3COpts.ItypesForExtern && TD) { auto PSL = PersistentSourceLoc::mkPSL(TD, Context); // Don't rewrite base types like int if (!TD->getUnderlyingType()->isBuiltinType()) { @@ -61,8 +212,10 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, if (Var.anyChanges(Env)) { std::string NewTy = getStorageQualifierString(D) + - Var.mkString(Info.getConstraints(), true, false, false, true); - RewriteThese.insert(new TypedefDeclReplacement(TD, nullptr, NewTy)); + Var.mkString(Info.getConstraints(), + MKSTRING_OPTS(UnmaskTypedef = true)); + RewriteThese.insert( + std::make_pair(TD, new MultiDeclMemberReplacement(TD, NewTy, {}))); } } } @@ -76,21 +229,11 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, for (const auto &I : Info.getVarMap()) Keys.insert(I.first); MappingVisitor MV(Keys, Context); - LastRecordDecl = nullptr; for (const auto &D : TUD->decls()) { MV.TraverseDecl(D); - detectInlineStruct(D, Context.getSourceManager()); - if (FunctionDecl *FD = dyn_cast(D)) { - if (FD->hasBody() && FD->isThisDeclarationADefinition()) { - for (auto &D : FD->decls()) { - detectInlineStruct(D, Context.getSourceManager()); - } - } - } } SourceToDeclMapType PSLMap; - VariableDecltoStmtMap VDLToStmtMap; - std::tie(PSLMap, VDLToStmtMap) = MV.getResults(); + PSLMap = MV.getResults(); // Add declarations from this map into the rewriting set for (const auto &V : Info.getVarMap()) { @@ -102,7 +245,7 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, // the variable name, not the typedef or #define that creates the // name of the type. PersistentSourceLoc PLoc = V.first; - if (Decl *D = std::get<1>(PSLMap[PLoc])) { + if (Decl *D = PSLMap[PLoc]) { ConstraintVariable *CV = V.second; PVConstraint *PV = dyn_cast(CV); bool PVChanged = @@ -110,268 +253,319 @@ void DeclRewriter::rewriteDecls(ASTContext &Context, ProgramInfo &Info, ABRewriter.hasNewBoundsString(PV, D)); if (PVChanged && !PV->isPartOfFunctionPrototype()) { // Rewrite a declaration, only if it is not part of function prototype. - DeclStmt *DS = nullptr; - if (VDLToStmtMap.find(D) != VDLToStmtMap.end()) - DS = VDLToStmtMap[D]; - - std::string NewTy = getStorageQualifierString(D) + - PV->mkString(Info.getConstraints()) + - ABRewriter.getBoundsString(PV, D); - if (auto *VD = dyn_cast(D)) - RewriteThese.insert(new VarDeclReplacement(VD, DS, NewTy)); - else if (auto *FD = dyn_cast(D)) - RewriteThese.insert(new FieldDeclReplacement(FD, DS, NewTy)); - else if (auto *PD = dyn_cast(D)) - RewriteThese.insert(new ParmVarDeclReplacement(PD, DS, NewTy)); - else - llvm_unreachable("Unrecognized declaration type."); + assert(!isa(D) && + "Got a PVConstraint for a ParmVarDecl where " + "isPartOfFunctionPrototype returns false?"); + MultiDeclMemberDecl *MMD = getAsMultiDeclMember(D); + assert(MMD && "Unrecognized declaration type."); + + RewrittenDecl RD = mkStringForPVDecl(MMD, PV, Info); + std::string ReplacementText = RD.Type + RD.IType; + std::vector SDecl; + if (!RD.SupplementaryDecl.empty()) + SDecl.push_back(RD.SupplementaryDecl); + RewriteThese.insert(std::make_pair( + MMD, new MultiDeclMemberReplacement(MMD, ReplacementText, SDecl))); } } } - // Build sets of variables that are declared in the same statement so we can - // rewrite things like int x, *y, **z; - GlobalVariableGroups GVG(R.getSourceMgr()); - for (const auto &D : TUD->decls()) { - GVG.addGlobalDecl(dyn_cast(D)); - //Search through the AST for fields that occur on the same line - FieldFinder::gatherSameLineFields(GVG, D); - } - // Do the declaration rewriting - DeclRewriter DeclR(R, Context, GVG); + DeclRewriter DeclR(R, Info, Context); DeclR.rewrite(RewriteThese); - for (const auto *R : RewriteThese) - delete R; + for (auto Pair : RewriteThese) + delete Pair.second; + + DeclR.denestTagDecls(); } void DeclRewriter::rewrite(RSet &ToRewrite) { - for (auto *const N : ToRewrite) { + for (auto Pair : ToRewrite) { + DeclReplacement *N = Pair.second; assert(N->getDecl() != nullptr); - if (Verbose) { + if (_3COpts.Verbose) { errs() << "Replacing type of decl:\n"; N->getDecl()->dump(); errs() << "with " << N->getReplacement() << "\n"; } // Exact rewriting procedure depends on declaration type - if (auto *PVR = dyn_cast(N)) { - assert(N->getStatement() == nullptr); - rewriteParmVarDecl(PVR); - } else if (auto *VR = dyn_cast(N)) { - rewriteFieldOrVarDecl(VR, ToRewrite); + if (auto *MR = dyn_cast(N)) { + MultiDeclInfo *MDI = + Info.TheMultiDeclsInfo.findContainingMultiDecl(MR->getDecl()); + assert("Missing MultiDeclInfo for multi-decl member" && MDI); + // A multi-decl can only be rewritten as a unit. If at least one member + // needs rewriting, then the first MultiDeclMemberReplacement in iteration + // order of ToRewrite (which need not have anything to do with member + // order of the multi-decl) triggers rewriting of the entire multi-decl, + // and rewriteMultiDecl checks ToRewrite for a MultiDeclMemberReplacement + // for each member of the multi-decl and applies it if found. + if (!MDI->AlreadyRewritten) + rewriteMultiDecl(*MDI, ToRewrite); } else if (auto *FR = dyn_cast(N)) { rewriteFunctionDecl(FR); - } else if (auto *FdR = dyn_cast(N)) { - rewriteFieldOrVarDecl(FdR, ToRewrite); - } else if (auto *TDR = dyn_cast(N)) { - rewriteTypedefDecl(TDR, ToRewrite); } else { assert(false && "Unknown replacement type"); } } } -void DeclRewriter::rewriteParmVarDecl(ParmVarDeclReplacement *N) { - // First, find all the declarations of the containing function. - DeclContext *DF = N->getDecl()->getParentFunctionOrMethod(); - assert(DF != nullptr && "no parent function or method for decl"); - FunctionDecl *FD = cast(DF); - - // For each function, determine which parameter in the declaration - // matches PV, then, get the type location of that parameter - // declaration and re-write. - unsigned int PIdx = getParameterIndex(N->getDecl(), FD); - - for (auto *CurFD = FD; CurFD != nullptr; CurFD = CurFD->getPreviousDecl()) - if (PIdx < CurFD->getNumParams()) { - ParmVarDecl *Rewrite = CurFD->getParamDecl(PIdx); - assert(Rewrite != nullptr); - SourceRange TR = Rewrite->getSourceRange(); - rewriteSourceRange(R, TR, N->getReplacement()); - } -} - -void DeclRewriter::rewriteTypedefDecl(TypedefDeclReplacement *TDR, - RSet &ToRewrite) { - rewriteSingleDecl(TDR, ToRewrite); -} - -// In alltypes mode we need to handle inline structs inside functions specially. -// Because both the recorddecl and vardecl are inside one DeclStmt, the -// SourceLocations will be generated incorrectly if we rewrite it as a -// normal multidecl. -bool isInlineStruct(std::vector &InlineDecls) { - if (InlineDecls.size() >= 2 && AllTypes) - return isa(InlineDecls[0]) && - std::all_of(InlineDecls.begin() + 1, InlineDecls.end(), - [](Decl *D) { return isa(D); }); - return false; -} - -template -void DeclRewriter::rewriteFieldOrVarDecl(DRType *N, RSet &ToRewrite) { - static_assert(std::is_same::value || - std::is_same::value, - "Method expects variable or field declaration replacement."); - - if (InlineVarDecls.find(N->getDecl()) != InlineVarDecls.end() && - VisitedMultiDeclMembers.find(N) == VisitedMultiDeclMembers.end()) { - std::vector SameLineDecls; - getDeclsOnSameLine(N, SameLineDecls); - if (std::find(SameLineDecls.begin(), SameLineDecls.end(), - VDToRDMap[N->getDecl()]) == SameLineDecls.end()) - SameLineDecls.insert(SameLineDecls.begin(), VDToRDMap[N->getDecl()]); - rewriteMultiDecl(N, ToRewrite, SameLineDecls, true); - } else if (isSingleDeclaration(N)) { - rewriteSingleDecl(N, ToRewrite); - } else if (VisitedMultiDeclMembers.find(N) == VisitedMultiDeclMembers.end()) { - std::vector SameLineDecls; - getDeclsOnSameLine(N, SameLineDecls); - if (isInlineStruct(SameLineDecls)) - SameLineDecls.erase(SameLineDecls.begin()); - rewriteMultiDecl(N, ToRewrite, SameLineDecls, false); - } else { - // Anything that reaches this case should be a multi-declaration that has - // already been rewritten. - assert("Declaration should have been rewritten." && - !isSingleDeclaration(N) && - VisitedMultiDeclMembers.find(N) != VisitedMultiDeclMembers.end()); +void DeclRewriter::denestTagDecls() { + // When there are multiple levels of nested TagDecls, we need to process + // all the children of a TagDecl TD before TD itself so that (1) the + // definitions of the children end up before the definition of TD (since the + // rewriter preserves order of insertions) and (2) the definitions of the + // children have been removed from the body of TD before we read the body of + // TD to move it. In effect, we want to process the TagDecls in postorder. + // The easiest way to achieve this is to process them in order of their _end_ + // locations. + std::sort(TagDeclsToDenest.begin(), TagDeclsToDenest.end(), + [&](TagDecl *TD1, TagDecl *TD2) { + return A.getSourceManager().isBeforeInTranslationUnit( + TD1->getEndLoc(), TD2->getEndLoc()); + }); + for (TagDecl *TD : TagDeclsToDenest) { + // rewriteMultiDecl replaced the final "}" in the original source range with + // "};\n", so the new content of the source range should include the ";\n", + // which is what we want here. Except the rewriter has a bug where it + // adjusts the token range to include the final token _after_ mapping the + // offset to account for previous edits (it should be before). We work + // around the bug by adjusting the token range before calling the rewriter + // at all. + CharSourceRange CSR = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(TD->getSourceRange()), R.getSourceMgr(), + R.getLangOpts()); + std::string DefinitionStr = R.getRewrittenText(CSR); + // Delete the definition from the old location. + rewriteSourceRange(R, CSR, ""); + // We want to find the highest ancestor DeclContext of TD that is a TagDecl + // (call it TopTagDecl) and insert TD just before TopTagDecl. + // + // As of this writing, it seems that if TD is named, `TD->getDeclContext()` + // returns the parent of TopTagDecl due to the code at + // https://github.com/correctcomputation/checkedc-clang/blob/fd4d8af4383d40af10ee8bc92b7bf88061a11035/clang/lib/Sema/SemaDecl.cpp#L16980-L16981, + // But that code doesn't run if TD is unnamed (which makes some sense + // because name visibility isn't an issue for TagDecls that have no name), + // and we want to de-nest TagDecls with names we assigned just like ones + // that were originally named, so we can't just use `TD->getDeclContext()`. + // In any event, maybe we wouldn't want to rely on this kind of internal + // Clang behavior. + TagDecl *TopTagDecl = TD; + while (TagDecl *ParentTagDecl = + dyn_cast(TopTagDecl->getLexicalDeclContext())) + TopTagDecl = ParentTagDecl; + // If TopTagDecl is preceded by qualifiers, ideally we'd like to insert TD + // before those qualifiers. If TopTagDecl is actually part of a multi-decl + // with at least one member, then we can just use the begin location of that + // multi-decl as the insertion point. + // + // If there are no members (so the qualifiers have no effect and generate a + // compiler warning), then there isn't an easy way for us to find the source + // location before the qualifiers. In that case, we just insert TD at the + // begin location of TopTagDecl (after the qualifiers) and hope for the + // best. In the cases we're aware of so far (storage qualifiers, including + // `typedef`), this just means that the qualifiers end up applying to the + // first TagDecl de-nested from TopTagDecl instead of to TopTagDecl itself, + // and they generate the same compiler warning as before but on a different + // TagDecl. However, we haven't confirmed that there aren't any obscure + // cases that could lead to an error, such as if a qualifier is valid on one + // kind of TagDecl but not another. + SourceLocation InsertLoc; + if (MultiDeclInfo *MDI = + Info.TheMultiDeclsInfo.findContainingMultiDecl(TopTagDecl)) + InsertLoc = MDI->Members[0]->getBeginLoc(); + else + InsertLoc = TopTagDecl->getBeginLoc(); + // TODO: Use a wrapper like rewriteSourceRange that tries harder with + // macros, reports failure, etc. + // (https://github.com/correctcomputation/checkedc-clang/issues/739) + R.InsertText(InsertLoc, DefinitionStr); } } -void DeclRewriter::rewriteSingleDecl(DeclReplacement *N, RSet &ToRewrite) { - bool IsSingleDecl = - dyn_cast(N->getDecl()) || isSingleDeclaration(N); - assert("Declaration is not a single declaration." && IsSingleDecl); - // This is the easy case, we can rewrite it locally, at the declaration. - // TODO why do we call getDecl() and getSourceRange() directly, - // TODO as opposed to getSourceRange()? - SourceRange TR = N->getDecl()->getSourceRange(); - doDeclRewrite(TR, N); -} - -void DeclRewriter::rewriteMultiDecl(DeclReplacement *N, RSet &ToRewrite, - std::vector SameLineDecls, - bool ContainsInlineStruct) { - // Rewriting is more difficult when there are multiple variables declared in a - // single statement. When this happens, we need to find all the declaration - // replacement for this statement and apply them at the same time. We also - // need to avoid rewriting any of these declarations twice by updating the - // Skip set to include the processed declarations. - - // Step 1: get declaration replacement in the same statement - RSet RewritesForThisDecl(DComp(R.getSourceMgr())); - auto I = ToRewrite.find(N); - while (I != ToRewrite.end()) { - if (areDeclarationsOnSameLine(N, *I)) { - assert("Unexpected DeclReplacement kind." && - (*I)->getKind() == N->getKind()); - RewritesForThisDecl.insert(*I); - } - ++I; - } - - // Step 2: For each decl in the original, build up a new string. If the - // original decl was re-written, write that out instead. Existing - // initializers are preserved, any declarations that an initializer to - // be valid checked-c are given one. +void DeclRewriter::rewriteMultiDecl(MultiDeclInfo &MDI, RSet &ToRewrite) { + // Rewrite a "multi-decl" consisting of one or more variables, fields, or + // typedefs declared in a comma-separated list based on a single type "on the + // left". See the comment at the top of clang/include/clang/3C/MultiDecls.h + // for a detailed description of the design that is implemented here. As + // mentioned in MultiDecls.h, this code is used even for "multi-decls" that + // have only a single member to avoid having to maintain a separate code path + // for them. + // + // Due to the overlap between members, a multi-decl can only be rewritten as a + // unit, visiting the members in source code order from left to right. For + // each member, we check whether it has a replacement in ToRewrite. If so, we + // use it; if not, we generate a declaration equivalent to the original. + // Existing initializers are preserved, and declarations that need an + // initializer to be valid Checked C are given one. + SourceManager &SM = A.getSourceManager(); bool IsFirst = true; SourceLocation PrevEnd; - for (const auto &DL : SameLineDecls) { - std::string ReplaceText = ";\n"; - // Find the declaration replacement object for the current declaration. - DeclReplacement *SameLineReplacement; - bool Found = false; - for (const auto &NLT : RewritesForThisDecl) - if (NLT->getDecl() == DL) { - SameLineReplacement = NLT; - Found = true; - break; + + if (MDI.TagDefToSplit != nullptr) { + TagDecl *TD = MDI.TagDefToSplit; + // `static struct T { ... } x;` -> `struct T { ... }; static struct T x;` + // A storage qualifier such as `static` applies to the members but is not + // meaningful on TD after it is split, and we need to remove it to avoid a + // compiler warning. The beginning location of the first member should be + // the `static` and the beginning location of TD should be the `struct`, so + // we just remove anything between those locations. (Can other things appear + // there? We hope it makes sense to remove them too.) + // + // We use `getCharRange` to get a range that excludes the first token of TD, + // unlike the default conversion of a SourceRange to a "token range", which + // would include it. + rewriteSourceRange(R, + CharSourceRange::getCharRange( + MDI.Members[0]->getBeginLoc(), TD->getBeginLoc()), + ""); + if (TD->getName().empty()) { + // If the record is unnamed, insert the name that we assigned it: + // `struct {` -> `struct T {` + PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(TD, A); + // This will assert if we can't find the new name. Is that what we want? + std::string NewTypeStr = *Info.TheMultiDeclsInfo.getTypeStrOverride( + A.getTagDeclType(TD).getTypePtr(), A); + // This token should be the tag kind, e.g., `struct`. + std::string ExistingToken = + getSourceText(SourceRange(TD->getBeginLoc()), A); + if (NONFATAL_ASSERT_PLACEHOLDER(ExistingToken == TD->getKindName())) { + rewriteSourceRange(R, TD->getBeginLoc(), NewTypeStr); } + } + // Make a note if the TagDecl needs to be de-nested later. + if (isa(TD->getLexicalDeclContext())) + TagDeclsToDenest.push_back(TD); + // `struct T { ... } foo;` -> `struct T { ... };\nfoo;` + rewriteSourceRange(R, TD->getEndLoc(), "};\n"); + IsFirst = false; + // Offset by one to skip past what we've just added so it isn't overwritten. + PrevEnd = TD->getEndLoc().getLocWithOffset(1); + } + + for (auto MIt = MDI.Members.begin(); MIt != MDI.Members.end(); MIt++) { + MultiDeclMemberDecl *DL = *MIt; + + // If we modify this member in any way, this is the original source range + // for the member that we expect to overwrite, before PrevEnd adjustment. + // + // We do want to overwrite existing Checked C annotations. Other than + // ItypesForExtern, 3C currently doesn't have real itype support for + // multi-decl members as opposed to function parameters and returns + // (https://github.com/correctcomputation/checkedc-clang/issues/744), but we + // are probably still better off overwriting the annotations with a + // complete, valid declaration than mixing and matching a declaration + // generated by 3C with existing annotations. + // + // If the variable has an initializer, we want this rewrite to end + // before the initializer to avoid interfering with any other rewrites + // that 3C needs to make inside the initializer expression + // (https://github.com/correctcomputation/checkedc-clang/issues/267). + SourceRange ReplaceSR = + getDeclSourceRangeWithAnnotations(DL, /*IncludeInitializer=*/false); + + // Look for a declaration replacement object for the current declaration. + MultiDeclMemberReplacement *Replacement = nullptr; + auto TRIt = ToRewrite.find(DL); + if (TRIt != ToRewrite.end()) { + Replacement = cast(TRIt->second); + // We can't expect multi-decl rewriting to work properly on a source range + // different from ReplaceSR above; for example, doDeclRewrite might insert + // an initializer in the wrong place. This assertion should pass as long + // as the implementation of DeclReplacement::getSourceRange matches + // ReplaceSR above. If someone changes DeclReplacement::getSourceRange, + // thinking that they can replace a different source range that way, we + // want to fail fast. + // + // This is awkward and makes me wonder if we should just remove + // DeclReplacement::getSourceRange since 3C currently only calls + // getSourceRange on an object already known to be a + // FunctionDeclReplacement. But after drafting that, I wasn't convinced + // that it was better than the status quo. + assert(Replacement->getSourceRange(SM) == ReplaceSR); + } - if (IsFirst && ContainsInlineStruct) { - // If it is an inline struct, the first thing we have to do - // is separate the RecordDecl from the VarDecl. - ReplaceText = "};\n"; - } else if (IsFirst) { + if (IsFirst) { // Rewriting the first declaration is easy. Nothing should change if its - // type does not to be rewritten. When rewriting is required, it is - // essentially the same as the single declaration case. + // type does not to be rewritten. IsFirst = false; - if (Found) { - SourceRange SR(DL->getBeginLoc(), DL->getEndLoc()); - doDeclRewrite(SR, SameLineReplacement); + if (Replacement) { + doDeclRewrite(ReplaceSR, Replacement); } } else { + // ReplaceSR.getBegin() is the beginning of the whole multi-decl. We only + // want to replace the text starting after the previous multi-decl member, + // which is given by PrevEnd. + ReplaceSR.setBegin(PrevEnd); + // The subsequent decls are more complicated because we need to insert a // type string even if the variables type hasn't changed. - if (Found) { + if (Replacement) { // If the type has changed, the DeclReplacement object has a replacement // string stored in it that should be used. - SourceRange SR(PrevEnd, DL->getEndLoc()); - doDeclRewrite(SR, SameLineReplacement); + doDeclRewrite(ReplaceSR, Replacement); } else { // When the type hasn't changed, we still need to insert the original // type for the variable. - - // This is a bit of trickery needed to get a string representation of - // the declaration without the initializer. We don't want to rewrite to - // initializer because this causes problems when rewriting casts and - // generic function calls later on. (issue 267) - auto *VD = dyn_cast(DL); - Expr *Init = nullptr; - if (VD && VD->hasInit()) { - Init = VD->getInit(); - VD->setInit(nullptr); - } - - // Dump the declaration (without the initializer) to a string. Printing - // the AST node gives the full declaration including the base type which - // is not present in the multi-decl source code. - std::string DeclStr = ""; - raw_string_ostream DeclStream(DeclStr); - DL->print(DeclStream); - assert("Original decl string empty." && !DeclStream.str().empty()); - - // Do the replacement. PrevEnd is setup to be the source location of the - // comma after the previous declaration in the multi-decl. getEndLoc is - // either the end of the declaration or just before the initializer if - // one is present. - SourceRange SR(PrevEnd, DL->getEndLoc()); - rewriteSourceRange(R, SR, DeclStream.str()); - - // Undo prior trickery. This need to happen so that the PSL for the decl - // is not changed since the PSL is used as a map key in a few places. - if (VD && Init) - VD->setInit(Init); + std::string NewDeclStr = mkStringForDeclWithUnchangedType(DL, Info); + rewriteSourceRange(R, ReplaceSR, NewDeclStr); } } - SourceRange End; - // In the event that IsFirst was not set to false, that implies we are - // separating the RecordDecl and VarDecl, so instead of searching for - // the next comma, we simply specify the end of the RecordDecl. - if (IsFirst) { - IsFirst = false; - End = DL->getEndLoc(); + // Processing related to the comma or semicolon ("terminator") that follows + // the multi-decl member. Members are separated by commas, and the last + // member is terminated by a semicolon. The rewritten decls are each + // terminated by a semicolon and are separated by newlines. + bool IsLast = (MIt + 1 == MDI.Members.end()); + bool HaveSupplementaryDecls = + (Replacement && !Replacement->getSupplementaryDecls().empty()); + // Unlike in ReplaceSR, we want to start searching for the terminator after + // the entire multi-decl member, including any existing initializer. + SourceRange FullSR = + getDeclSourceRangeWithAnnotations(DL, /*IncludeInitializer=*/true); + // Search for the terminator. + // + // FIXME: If the terminator is hidden inside a macro, + // getNextCommaOrSemicolon will continue scanning and may return a comma or + // semicolon later in the file (which has bizarre consequences if we try to + // use it to rewrite this multi-decl) or fail an assertion if it doesn't + // find one. As a stopgap for the existing regression test in + // macro_rewrite_error.c that has a semicolon inside a macro, we only search + // for the terminator if we actually need it. + SourceLocation Terminator; + if (!IsLast || HaveSupplementaryDecls) { + Terminator = getNextCommaOrSemicolon(FullSR.getEnd()); + } + if (!IsLast) { + // We expect the terminator to be a comma. Change it to a semicolon. + rewriteSourceRange(R, SourceRange(Terminator, Terminator), ";"); + } + if (HaveSupplementaryDecls) { + emitSupplementaryDeclarations(Replacement->getSupplementaryDecls(), + Terminator); + } + if (!IsLast) { + // Insert a newline between this multi-decl member and the next. The + // Rewriter preserves the order of insertions at the same location, so if + // there are supplementary declarations, this newline will go between them + // and the next member, which is what we want because + // emitSupplementaryDeclarations by itself doesn't add a newline after the + // supplementary declarations. + SourceLocation AfterTerminator = + getLocationAfterToken(Terminator, A.getSourceManager(), + A.getLangOpts()); + R.InsertText(AfterTerminator, "\n"); + // When rewriting the next member, start after the terminator. The + // Rewriter is smart enough not to mess with anything we already inserted + // at that location. + PrevEnd = AfterTerminator; } - // Variables in a mutli-decl are delimited by commas. The rewritten decls - // are separate statements separated by a semicolon and a newline. - else - End = getNextCommaOrSemicolon(DL->getEndLoc()); - rewriteSourceRange(R, End, ReplaceText); - // Offset by one to skip past what we've just added so it isn't overwritten. - PrevEnd = End.getEnd().getLocWithOffset(1); } - // Step 3: Be sure and skip all of the declarations that we just dealt with by - // adding them to the skip set. - for (const auto &TN : RewritesForThisDecl) - VisitedMultiDeclMembers.insert(TN); + MDI.AlreadyRewritten = true; } // Common rewriting logic used to replace a single decl either on its own or as @@ -379,17 +573,15 @@ void DeclRewriter::rewriteMultiDecl(DeclReplacement *N, RSet &ToRewrite, // invoking the rewriter) is to add any required initializer expression. void DeclRewriter::doDeclRewrite(SourceRange &SR, DeclReplacement *N) { std::string Replacement = N->getReplacement(); - if (isa(N->getDecl())) - Replacement = "typedef " + Replacement; if (auto *VD = dyn_cast(N->getDecl())) { - if (VD->hasInit()) { - // Make sure we preserve any existing initializer - SR.setEnd(VD->getInitializerStartLoc()); - Replacement += " ="; - } else { + if (!VD->hasInit()) { // There is no initializer. Add it if we need one. // MWH -- Solves issue 43. Should make it so we insert NULL if stdlib.h or // stdlib_checked.h is included + // TODO: Centralize initialization logic for all types: + // https://github.com/correctcomputation/checkedc-clang/issues/645#issuecomment-876474200 + // TODO: Don't add unnecessary initializers to global variables: + // https://github.com/correctcomputation/checkedc-clang/issues/741 if (VD->getStorageClass() != StorageClass::SC_Extern) { const std::string NullPtrStr = "((void *)0)"; if (isPointerType(VD)) { @@ -409,104 +601,49 @@ void DeclRewriter::doDeclRewrite(SourceRange &SR, DeclReplacement *N) { void DeclRewriter::rewriteFunctionDecl(FunctionDeclReplacement *N) { rewriteSourceRange(R, N->getSourceRange(A.getSourceManager()), N->getReplacement()); + if (N->getDecl()->isThisDeclarationADefinition()) { + Stmt *S = N->getDecl()->getBody(); + assert("Supplementary declarations should only exist on rewritings for " + "function definitions." && S != nullptr); + // Insert supplementary declarations after the opening curly brace of the + // function body. + emitSupplementaryDeclarations(N->getSupplementaryDecls(), + S->getBeginLoc()); + } } -// A function to detect the presence of inline struct declarations -// by tracking VarDecls and RecordDecls and populating data structures -// later used in rewriting. - -// These variables are duplicated in the header file and here because static -// vars need to be initialized in the cpp file where the class is defined. -/*static*/ RecordDecl *DeclRewriter::LastRecordDecl = nullptr; -/*static*/ std::map DeclRewriter::VDToRDMap; -/*static*/ std::set DeclRewriter::InlineVarDecls; -void DeclRewriter::detectInlineStruct(Decl *D, SourceManager &SM) { - RecordDecl *RD = dyn_cast(D); - if (RD != nullptr && - // With -fms-extensions (default on Windows), Clang injects an implicit - // `struct _GUID` with an invalid location, which would cause an assertion - // failure in SM.isPointWithin below. - RD->getBeginLoc().isValid()) { - LastRecordDecl = RD; - } - if (VarDecl *VD = dyn_cast(D)) { - if (LastRecordDecl != nullptr) { - auto LastRecordLocation = LastRecordDecl->getBeginLoc(); - auto Begin = VD->getBeginLoc(); - auto End = VD->getEndLoc(); - bool IsInLineStruct = SM.isPointWithin(LastRecordLocation, Begin, End); - bool IsNamedInLineStruct = - IsInLineStruct && LastRecordDecl->getNameAsString() != ""; - if (IsNamedInLineStruct) { - VDToRDMap[VD] = LastRecordDecl; - InlineVarDecls.insert(VD); - } - } - } +void DeclRewriter::emitSupplementaryDeclarations( + const std::vector &SDecls, SourceLocation Loc) { + // There are no supplementary declarations to emit. The AllDecls String + // will remain empty, so insertText should no-op, but it's still an error to + // insert an empty string at an invalid source location, so short circuit here + // to be safe. + if (SDecls.empty()) + return; + + std::string AllDecls; + for (std::string D : SDecls) + AllDecls += "\n" + D; + + R.InsertText(getLocationAfterToken(Loc, R.getSourceMgr(), R.getLangOpts()), + AllDecls); } // Uses clangs lexer to find the location of the next comma or semicolon after // the given source location. This is used to find the end of each declaration // within a multi-declaration. -SourceRange DeclRewriter::getNextCommaOrSemicolon(SourceLocation L) { +SourceLocation DeclRewriter::getNextCommaOrSemicolon(SourceLocation L) { SourceManager &SM = A.getSourceManager(); auto Tok = Lexer::findNextToken(L, SM, A.getLangOpts()); while (Tok.hasValue() && !Tok->is(clang::tok::eof)) { if (Tok->is(clang::tok::comma) || Tok->is(clang::tok::semi)) - return SourceRange(Tok->getLocation(), Tok->getLocation()); + return Tok->getLocation(); Tok = Lexer::findNextToken(Tok->getEndLoc(), A.getSourceManager(), A.getLangOpts()); } llvm_unreachable("Unable to find comma or semicolon at source location."); } -bool DeclRewriter::areDeclarationsOnSameLine(DeclReplacement *N1, - DeclReplacement *N2) { - Decl *D1 = N1->getDecl(); - Decl *D2 = N2->getDecl(); - if (D1 && D2) { - // In the event that this is a FieldDecl, - // these statements will always be null - DeclStmt *Stmt1 = N1->getStatement(); - DeclStmt *Stmt2 = N2->getStatement(); - if (Stmt1 == nullptr && Stmt2 == nullptr) { - auto &DGroup = GP.getVarsOnSameLine(D1); - return llvm::is_contained(DGroup, D2); - } - if (Stmt1 == nullptr || Stmt2 == nullptr) { - return false; - } - return Stmt1 == Stmt2; - } - return false; -} - -bool DeclRewriter::isSingleDeclaration(DeclReplacement *N) { - DeclStmt *Stmt = N->getStatement(); - if (Stmt == nullptr) { - auto &VDGroup = GP.getVarsOnSameLine(N->getDecl()); - return VDGroup.size() == 1; - } - return Stmt->isSingleDecl(); -} - -void DeclRewriter::getDeclsOnSameLine(DeclReplacement *N, - std::vector &Decls) { - if (N->getStatement() != nullptr) { - Decls.insert(Decls.begin(), N->getStatement()->decls().begin(), - N->getStatement()->decls().end()); - } else { - std::vector GlobalLine = GP.getVarsOnSameLine(N->getDecl()); - Decls.insert(Decls.begin(), GlobalLine.begin(), GlobalLine.end()); - } - - assert("Invalid ordering in same line decls" && - std::is_sorted(Decls.begin(), Decls.end(), [&](Decl *D0, Decl *D1) { - return A.getSourceManager().isBeforeInTranslationUnit( - D0->getEndLoc(), D1->getEndLoc()); - })); -} - // This function checks how to re-write a function declaration. bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { @@ -529,7 +666,14 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { // If this is an external function, there is no need to rewrite the // declaration. We cannot change the signature of external functions. - if (!FDConstraint->hasBody()) + // Under the flag -infer-types-for-undef, however, undefined functions do need + // to be rewritten. If the rest of the 3c inference and rewriting code is + // correct, short-circuiting here shouldn't be necessary; the rest of the + // logic in this function should successfully not rewrite undefined functions + // when -infer-types-for-undef is not passed. This assumption could be + // transformed into an assertion if we're confident it won't fail in too many + // places. + if (!_3COpts.InferTypesForUndefs && !FDConstraint->hasBody()) return true; // RewriteParams and RewriteReturn track if we will need to rewrite the @@ -541,8 +685,14 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { bool RewriteParams = false; bool RewriteReturn = false; + // RewriteGeneric is similar to the above, but we need to further check + // if the potential generic variables were set to wild by the constraint + // resolver. In that case don't rewrite. + bool RewriteGeneric = false; + bool DeclIsTypedef = false; - if (TypeSourceInfo *TS = FD->getTypeSourceInfo()) { + TypeSourceInfo *TS = FD->getTypeSourceInfo(); + if (TS != nullptr) { // This still could possibly be a typedef type if TS was NULL. // TypeSourceInfo is null for implicit function declarations, so if a // implicit declaration uses a typedef, it will be missed. That's fine @@ -551,10 +701,24 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { DeclIsTypedef = isa(TS->getType()); } + // If we've made this generic we need add "_For_any" or "_Itype_for_any" + if (FDConstraint->getGenericParams() > 0 + && !FD->isGenericFunction() && !FD->isItypeGenericFunction()) + RewriteGeneric = true; + + // This will keep track of the supplementary declarations that are required by + // function parameters. The new declarations will be emitted inside the + // function body in the order of function parameters that generated them. + std::vector SDecls; + bool GenerateSDecls = FD->isThisDeclarationADefinition(); + // Get rewritten parameter variable declarations. Try to use // the source for as much as possible. std::vector ParmStrs; + // Needed to distinguish between Itype_for_any and For_any + bool ProtoHasItype = false; + // Typedefs must be expanded for now, so allow interpret them as rewritable // by ignoring their special case code. // See the FIXME below for more info. @@ -566,21 +730,27 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { for (unsigned I = 0; I < FD->getNumParams(); ++I) { ParmVarDecl *PVDecl = FD->getParamDecl(I); const FVComponentVariable *CV = FDConstraint->getCombineParam(I); - std::string Type, IType; - this->buildDeclVar(CV, PVDecl, Type, IType, - PVDecl->getQualifiedNameAsString(), RewriteParams, - RewriteReturn); - ParmStrs.push_back(Type + IType); + RewrittenDecl RD = + this->buildDeclVar(CV, PVDecl, PVDecl->getQualifiedNameAsString(), + RewriteGeneric, RewriteParams, RewriteReturn, + FD->isStatic(), GenerateSDecls); + ParmStrs.push_back(RD.Type + RD.IType); + if (!RD.SupplementaryDecl.empty()) + SDecls.push_back(RD.SupplementaryDecl); + ProtoHasItype |= !RD.IType.empty(); } } else if (FDConstraint->numParams() != 0) { // lacking params but the constraint has them: mirror the constraint for (unsigned I = 0; I < FDConstraint->numParams(); ++I) { ParmVarDecl *PVDecl = nullptr; const FVComponentVariable *CV = FDConstraint->getCombineParam(I); - std::string Type, IType; - this->buildDeclVar(CV, PVDecl, Type, IType, "", RewriteParams, - RewriteReturn); - ParmStrs.push_back(Type + IType); + RewrittenDecl RD = + this->buildDeclVar(CV, PVDecl, "", RewriteGeneric, RewriteParams, + RewriteReturn, FD->isStatic(), GenerateSDecls); + ParmStrs.push_back(RD.Type + RD.IType); + if (!RD.SupplementaryDecl.empty()) + SDecls.push_back(RD.SupplementaryDecl); + ProtoHasItype |= !RD.IType.empty(); // FIXME: when the above FIXME is changed this condition will always // be true. This is correct, always rewrite if there were no params // in source but they exist in the constraint variable. @@ -588,20 +758,28 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { RewriteParams = true; } } else { - // No params and no param source: make explicit + // Functions in CheckedC need prototypes, so replace empty parameter lists + // with an explict (void). This updates the parameter list; the rewrite flag + // will be set once it is known if the return needs to be rewritten. ParmStrs.push_back("void"); - QualType ReturnTy = FD->getReturnType(); - QualType Ty = FD->getTypeSourceInfo()->getType(); - if (!Ty->isFunctionProtoType() && ReturnTy->isPointerType()) - RewriteParams = true; } // Get rewritten return variable. - std::string ReturnVar = "", ItypeStr = ""; // For now we still need to check if this needs rewriting, see FIXME below // if (!DeclIsTypedef) - this->buildDeclVar(FDConstraint->getCombineReturn(), FD, ReturnVar, ItypeStr, - "", RewriteParams, RewriteReturn); + RewrittenDecl RewrittenReturn = + this->buildDeclVar(FDConstraint->getCombineReturn(), FD, "", RewriteGeneric, + RewriteParams, RewriteReturn, FD->isStatic(), + GenerateSDecls); + assert("Supplementary declarations should not be generated for return." && + RewrittenReturn.SupplementaryDecl.empty()); + + ProtoHasItype |= !RewrittenReturn.IType.empty(); + + // Generic forany and return are in the same rewrite location, so + // we must rewrite the return if rewriting generic + if (RewriteGeneric) + RewriteReturn = true; // If the return is a function pointer, we need to rewrite the whole // declaration even if no actual changes were made to the parameters because @@ -611,6 +789,22 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { if (FD->getReturnType()->isFunctionPointerType() && RewriteReturn) RewriteParams = true; + // If we're making this into a generic function, we'll + // rewrite parameters in case there's an itype in there that won't trigger + // a normal rewrite. Temp fix for #678 in generics case. + if (RewriteGeneric) { + RewriteParams = true; + } + + // If this function was declared without a prototype, then we must add one + // to be able to give it a checked return type. This was done by adding "void" + // to the parameter list above. Here we indicate the parameter list should be + // rewritten to include "void" only if the return is already being rewritten. + // This avoids unnecessarily adding void to empty parameter lists on unchecked + // functions. + if (TS && !TS->getType()->isFunctionProtoType() && RewriteReturn) + RewriteParams = true; + // If the function is declared using a typedef for the function type, then we // need to rewrite parameters and the return if either would have been // rewritten. What this does is expand the typedef to the full function type @@ -625,11 +819,37 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { RewriteReturn = true; } + // Mirrors the check above that sets RewriteGeneric to true. + // If we've decided against making this generic, remove the generic params + // so later rewrites (of typeparams) don't happen + if (!RewriteGeneric && FDConstraint->getGenericParams() > 0 + && !FD->isGenericFunction() && !FD->isItypeGenericFunction()) + FDConstraint->resetGenericParams(); + + // If this was an itype but is now checked, we'll be changing + // "_Itype_for_any" to "_For_any" + if (!RewriteGeneric && FD->isItypeGenericFunction() && !ProtoHasItype) { + RewriteGeneric = true; + RewriteReturn = true; + } + // Combine parameter and return variables rewritings into a single rewriting // for the entire function declaration. std::string NewSig = ""; + if (RewriteGeneric) { + if (ProtoHasItype) + NewSig += "_Itype_for_any(T"; + else + NewSig += "_For_any(T"; + for (int i = 0; i < FDConstraint->getGenericParams() - 1; i++) { + assert(i < 2 && + "Need an unexpected number of type variables"); + NewSig += std::begin({",U",",V"})[i]; + } + NewSig += ") "; + } if (RewriteReturn) - NewSig = getStorageQualifierString(FD) + ReturnVar; + NewSig += getStorageQualifierString(FD) + RewrittenReturn.Type; if (RewriteReturn && RewriteParams) NewSig += FDConstraint->getName(); @@ -647,74 +867,72 @@ bool FunctionDeclBuilder::VisitFunctionDecl(FunctionDecl *FD) { NewSig += ", ..."; NewSig += ")"; } - if (!ItypeStr.empty()) - NewSig = NewSig + ItypeStr; + NewSig = NewSig + RewrittenReturn.IType; // Add new declarations to RewriteThese if it has changed if (RewriteReturn || RewriteParams) { - RewriteThese.insert( - new FunctionDeclReplacement(FD, NewSig, RewriteReturn, RewriteParams)); + auto *FDR = new FunctionDeclReplacement(FD, NewSig, SDecls, RewriteReturn, + RewriteParams, RewriteGeneric); + RewriteThese.insert(std::make_pair(FD, FDR)); } return true; } -void FunctionDeclBuilder::buildCheckedDecl( - PVConstraint *Defn, DeclaratorDecl *Decl, std::string &Type, - std::string &IType, std::string UseName, bool &RewriteParm, - bool &RewriteRet) { - Type = - Defn->mkString(Info.getConstraints(), true, false, false, false, UseName); - //IType = getExistingIType(Defn); - IType = ABRewriter.getBoundsString(Defn, Decl, !IType.empty()); - RewriteParm |= getExistingIType(Defn).empty() != IType.empty() || +RewrittenDecl +FunctionDeclBuilder::buildCheckedDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, bool &RewriteParm, + bool &RewriteRet, bool GenerateSDecls) { + RewrittenDecl RD = DeclRewriter::buildCheckedDecl(Defn, Decl, UseName, Info, + ABRewriter, GenerateSDecls); + RewriteParm |= getExistingIType(Defn).empty() != RD.IType.empty() || isa_and_nonnull(Decl); RewriteRet |= isa_and_nonnull(Decl); + return RD; } -void FunctionDeclBuilder::buildItypeDecl(PVConstraint *Defn, - DeclaratorDecl *Decl, - std::string &Type, std::string &IType, - bool &RewriteParm, bool &RewriteRet) { - Type = Defn->getRewritableOriginalTy(); - auto &PStats = Info.getPerfStats(); - if (isa_and_nonnull(Decl)) { - if (Decl->getName().empty()) - Type += Defn->getName(); - else - Type += Decl->getQualifiedNameAsString(); - } else { - std::string Name = Defn->getName(); - if (Name != RETVAR) - Type += Name; - } - IType = " : itype(" + Defn->mkString(Info.getConstraints(), false, true) + - ")" + ABRewriter.getBoundsString(Defn, Decl, true); - PStats.incrementNumITypes(); +RewrittenDecl +FunctionDeclBuilder::buildItypeDecl(PVConstraint *Defn, DeclaratorDecl *Decl, + std::string UseName, bool &RewriteParm, + bool &RewriteRet, bool GenerateSDecls, + bool SDeclChecked) { + Info.getPerfStats().incrementNumITypes(); + RewrittenDecl RD = DeclRewriter::buildItypeDecl( + Defn, Decl, UseName, Info, ABRewriter, GenerateSDecls, SDeclChecked); RewriteParm = true; RewriteRet |= isa_and_nonnull(Decl); + return RD; } // Note: For a parameter, Type + IType will give the full declaration (including // the name) but the breakdown between Type and IType is not guaranteed. For a // return, Type will be what goes before the name and IType will be what goes // after the parentheses. -void FunctionDeclBuilder::buildDeclVar(const FVComponentVariable *CV, - DeclaratorDecl *Decl, std::string &Type, - std::string &IType, std::string UseName, - bool &RewriteParm, bool &RewriteRet) { - if (CV->hasCheckedSolution(Info.getConstraints())) { - buildCheckedDecl(CV->getExternal(), Decl, Type, IType, UseName, RewriteParm, - RewriteRet); - return; +RewrittenDecl +FunctionDeclBuilder::buildDeclVar(const FVComponentVariable *CV, + DeclaratorDecl *Decl, std::string UseName, + bool &RewriteGen, bool &RewriteParm, + bool &RewriteRet, bool StaticFunc, + bool GenerateSDecls) { + + bool CheckedSolution = CV->hasCheckedSolution(Info.getConstraints()); + bool ItypeSolution = CV->hasItypeSolution(Info.getConstraints()); + if (ItypeSolution || + (CheckedSolution && _3COpts.ItypesForExtern && !StaticFunc)) { + return buildItypeDecl(CV->getExternal(), Decl, UseName, RewriteParm, + RewriteRet, GenerateSDecls, CheckedSolution); } - if (CV->hasItypeSolution(Info.getConstraints())) { - buildItypeDecl(CV->getExternal(), Decl, Type, IType, RewriteParm, - RewriteRet); - return; + if (CheckedSolution) { + return buildCheckedDecl(CV->getExternal(), Decl, UseName, RewriteParm, + RewriteRet, GenerateSDecls); } - // If the type of the pointer hasn't changed, then neither of the above + // Don't add generics if one of the potential generic params is wild, + // even if it could have an itype + if (!CheckedSolution && CV->getExternal()->isGenericChanged()) + RewriteGen = false; + +// If the type of the pointer hasn't changed, then neither of the above // branches will be taken, but it's still possible for the bounds of an array // pointer to change. if (ABRewriter.hasNewBoundsString(CV->getExternal(), Decl)) { @@ -726,27 +944,25 @@ void FunctionDeclBuilder::buildDeclVar(const FVComponentVariable *CV, // Variables that do not need to be rewritten fall through to here. // Try to use the source. + std::string Type, IType; ParmVarDecl *PVD = dyn_cast_or_null(Decl); if (PVD && !PVD->getName().empty()) { SourceRange Range = PVD->getSourceRange(); if (PVD->hasBoundsExpr()) Range.setEnd(PVD->getBoundsExpr()->getEndLoc()); - if (Range.isValid() && !inParamMultiDecl(PVD)) { + if (Range.isValid() && !inParamMultiDecl(PVD)) Type = getSourceText(Range, *Context); - if (!Type.empty()) { - IType = getExistingIType(CV->getExternal()) + BoundsStr; - return; - } - } // Otherwise, reconstruct the name and type, and reuse the code below for // the itype and bounds. // TODO: Do we care about `register` or anything else this doesn't handle? - Type = qtyToStr(PVD->getOriginalType(), PVD->getNameAsString()); + if (Type.empty()) + Type = qtyToStr(PVD->getOriginalType(), PVD->getNameAsString()); } else { Type = CV->mkTypeStr(Info.getConstraints(), true, CV->getExternal()->getName()); } IType = getExistingIType(CV->getExternal()) + BoundsStr; + return RewrittenDecl(Type, IType, ""); } std::string FunctionDeclBuilder::getExistingIType(ConstraintVariable *DeclC) { @@ -776,13 +992,3 @@ bool FunctionDeclBuilder::inParamMultiDecl(const ParmVarDecl *PVD) { } return false; } - -bool FieldFinder::VisitFieldDecl(FieldDecl *FD) { - GVG.addGlobalDecl(FD); - return true; -} - -void FieldFinder::gatherSameLineFields(GlobalVariableGroups &GVG, Decl *D) { - FieldFinder FF(GVG); - FF.TraverseDecl(D); -} diff --git a/clang/lib/3C/IntermediateToolHook.cpp b/clang/lib/3C/IntermediateToolHook.cpp index 69b7b77434f2..ce46f4af1022 100644 --- a/clang/lib/3C/IntermediateToolHook.cpp +++ b/clang/lib/3C/IntermediateToolHook.cpp @@ -10,21 +10,16 @@ #include "clang/3C/IntermediateToolHook.h" #include "clang/3C/ArrayBoundsInferenceConsumer.h" +#include "clang/3C/3CGlobalOptions.h" #include "clang/AST/RecursiveASTVisitor.h" using namespace llvm; using namespace clang; -extern cl::OptionCategory ArrBoundsInferCat; -static cl::opt - DisableArrH("disable-arr-hu", - cl::desc("Disable Array Bounds Inference Heuristics."), - cl::init(false), cl::cat(ArrBoundsInferCat)); - void IntermediateToolHook::HandleTranslationUnit(ASTContext &Context) { Info.enterCompilationUnit(Context); Info.getPerfStats().startArrayBoundsInferenceTime(); - handleArrayVariablesBoundsDetection(&Context, Info, !DisableArrH); + handleArrayVariablesBoundsDetection(&Context, Info, !_3COpts.DisableArrH); Info.getPerfStats().endArrayBoundsInferenceTime(); Info.exitCompilationUnit(); } diff --git a/clang/lib/3C/LowerBoundAssignment.cpp b/clang/lib/3C/LowerBoundAssignment.cpp new file mode 100644 index 000000000000..e21b60d2a4d3 --- /dev/null +++ b/clang/lib/3C/LowerBoundAssignment.cpp @@ -0,0 +1,151 @@ +//=--LowerBoundAssignment.cpp-------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// Implementation of methods in LowerBoundAssignment.h +//===----------------------------------------------------------------------===// + +#include "clang/3C/ProgramInfo.h" +#include "clang/AST/ASTContext.h" + +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/3C/LowerBoundAssignment.h" +#include "clang/3C/RewriteUtils.h" + +using namespace llvm; +using namespace clang; + +// Visitor to collect all the variables and structure member access that are +// used during the life-time of the visitor. +class CollectDeclsVisitor : public RecursiveASTVisitor { +public: + explicit CollectDeclsVisitor() : ObservedDecls(), ObservedStructAccesses() {} + + virtual ~CollectDeclsVisitor() {} + + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + if (auto *VD = dyn_cast_or_null(DRE->getDecl())) + ObservedDecls.insert(VD); + return true; + } + + // For `a->b` we need to get `a->b` rather than just `b`. This way assignment + // from a field in one instance of a structure to the same field in another + // instance is not treated as pointer arithmetic. + bool VisitMemberExpr(MemberExpr *ME) { + // TODO: Is this cast legit? `getMemberDecl()` returns a `ValueDecl`, but I + // think it can only be a `FieldDecl` for structs in C. + auto *FD = cast(ME->getMemberDecl()); + + CollectDeclsVisitor MEVis; + MEVis.TraverseStmt(ME->getBase()); + // Field access through variable. + for (auto *D : MEVis.getObservedDecls()) { + std::vector SingletonAccessList({FD}); + ObservedStructAccesses.insert(std::make_pair(D, SingletonAccessList)); + } + // Field access through other structure fields. + for (StructAccess SA : MEVis.getObservedStructAccesses()) { + SA.second.push_back(FD); + ObservedStructAccesses.insert(SA); + } + return false; + } + + bool VisitCallExpr(CallExpr *CE) { + // Stop the visitor when we hit a CallExpr. This stops us from treating a + // function call like `a = foo(a);` the same as `a = a + 1`. + return false; + } + + const std::set &getObservedDecls() { return ObservedDecls; } + + // A structure field access is represented + typedef std::pair> StructAccess; + const std::set &getObservedStructAccesses() { + return ObservedStructAccesses; + } + +private: + // Contains all VarDecls seen by this visitor + std::set ObservedDecls; + + // Contains the source representation of all record access (MemberExpression) + // seen by this visitor. + std::set ObservedStructAccesses; +}; + +bool isLowerBoundAssignment(Expr *LHS, Expr *RHS) { + CollectDeclsVisitor LVarVis; + LVarVis.TraverseStmt(LHS); + + CollectDeclsVisitor RVarVis; + RVarVis.TraverseStmt(RHS); + + std::set CommonVars; + std::set CommonStVars; + findIntersection(LVarVis.getObservedDecls(), RVarVis.getObservedDecls(), + CommonVars); + findIntersection(LVarVis.getObservedStructAccesses(), + RVarVis.getObservedStructAccesses(), CommonStVars); + + // If CommonVars is empty, then the same pointer does not appears on the LHS + // and RHS of the assignment. We say that the assignment is a lower bound + // update. + return CommonVars.empty() && CommonStVars.empty(); +} + +bool +LowerBoundAssignmentVisitor::VisitBinaryOperator(BinaryOperator *O) { + if (O->getOpcode() == clang::BO_Assign && + isLowerBoundAssignment(O->getLHS(), O->getRHS())) + visitLowerBoundAssignment(O->getLHS(), O->getRHS()); + return true; +} + +void LowerBoundAssignmentUpdater::visitLowerBoundAssignment(Expr *LHS, + Expr *RHS) { + CVarSet LHSCVs = CR.getExprConstraintVarsSet(LHS); + // It is possible for multiple ConstraintVariables to exist on the LHS + // of an assignment expression; e.g., `*(0 ? a : b) = 0`. If this + // happens, and one of those variables needed range bounds, then the + // following rewriting is not correct. I believe that it can only happen + // when the LHS is a pointer dereference or struct field access. + // Structure fields and inner pointer levels can never have range bounds + // so this case currently is not possible. + assert(LHSCVs.size() == 1 || llvm::count_if(LHSCVs, [this]( + ConstraintVariable *CV) { return ABInfo.needsFreshLowerBound(CV); }) == 0); + for (ConstraintVariable *CV: LHSCVs) { + if (ABInfo.needsFreshLowerBound(CV)) { + BoundsKey LBKey = ABInfo.getBounds( + CV->getBoundsKey())->getLowerBoundKey(); + assert( + "Should not be rewriting assignments for pointer without lower bound!" && + LBKey != 0); + std::string LBName = ABInfo.getProgramVar(LBKey)->getVarName(); + rewriteSourceRange(R, LHS->getSourceRange(), LBName); + R.InsertTextAfter( + getLocationAfterToken(RHS->getEndLoc(), R.getSourceMgr(), + R.getLangOpts()), + ", " + CV->getName() + " = " + LBName); + } + } +} + +void LowerBoundAssignmentFinder::visitLowerBoundAssignment(Expr *LHS, + Expr *RHS) { + SourceLocation RHSEnd = + getLocationAfterToken(RHS->getEndLoc(), C->getSourceManager(), + C->getLangOpts()); + SourceLocation LHSLoc = LHS->getExprLoc(); + if (!(LHSLoc.isValid() && Rewriter::isRewritable(LHSLoc)) || + !(RHSEnd.isValid() && Rewriter::isRewritable(RHSEnd))) { + CVarSet LHSCVs = CR.getExprConstraintVarsSet(LHS); + for (auto *CV: LHSCVs) + if (CV->hasBoundsKey()) + ABInfo.markIneligibleForFreshLowerBound(CV->getBoundsKey()); + } +} \ No newline at end of file diff --git a/clang/lib/3C/MappingVisitor.cpp b/clang/lib/3C/MappingVisitor.cpp index efc85254e89e..febf3ec7c7d2 100644 --- a/clang/lib/3C/MappingVisitor.cpp +++ b/clang/lib/3C/MappingVisitor.cpp @@ -14,58 +14,13 @@ using namespace clang; -bool MappingVisitor::VisitDeclStmt(DeclStmt *S) { - PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(S, Context); - - if (PSL.valid()) { - - // Check to see if the source location as described by the current location - // of S appears in the set of PersistentSourceLocs we are tasked to - // resolve. If it is, then create a mapping mapping the current - // PersistentSourceLocation to the Stmt object S. - std::set::iterator I = SourceLocs.find(PSL); - if (I != SourceLocs.end()) { - Decl *D = nullptr; - Stmt *So = nullptr; - std::tie(So, D) = PSLtoSDT[PSL]; - if (So != nullptr && Verbose) { - llvm::errs() << "\nOverriding "; - S->dump(); - llvm::errs() << "\n"; - llvm::errs() << "With "; - So->dump(); - llvm::errs() << "\n"; - llvm::errs() << " at "; - PSL.dump(); - llvm::errs() << "\n"; - } - - if (So == nullptr) - PSLtoSDT[PSL] = StmtDecl(S, D); - } - - if (DeclStmt *DL = dyn_cast(S)) { - if (DL->isSingleDecl()) { - if (VarDecl *VD = dyn_cast(DL->getSingleDecl())) - DeclToDeclStmt[VD] = DL; - } else - for (auto *I : DL->decls()) - DeclToDeclStmt[I] = DL; - } - } - - return true; -} - bool MappingVisitor::VisitDecl(Decl *D) { PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(D, Context); if (PSL.valid()) { std::set::iterator I = SourceLocs.find(PSL); if (I != SourceLocs.end()) { - Decl *Do = nullptr; - Stmt *S = nullptr; - std::tie(S, Do) = PSLtoSDT[PSL]; - if (Do != nullptr && Verbose) { + Decl *Do = PSLtoSDT[PSL]; + if (Do != nullptr && _3COpts.Verbose) { llvm::errs() << "Overriding "; Do->dump(); llvm::errs() << " with "; @@ -75,7 +30,7 @@ bool MappingVisitor::VisitDecl(Decl *D) { } if (Do == nullptr) - PSLtoSDT[PSL] = StmtDecl(S, D); + PSLtoSDT[PSL] = D; } } diff --git a/clang/lib/3C/MultiDecls.cpp b/clang/lib/3C/MultiDecls.cpp new file mode 100644 index 000000000000..26a76a7a92da --- /dev/null +++ b/clang/lib/3C/MultiDecls.cpp @@ -0,0 +1,296 @@ +//=--MultiDecls.cpp-----------------------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/3C/MultiDecls.h" +#include "clang/3C/Utils.h" + +MultiDeclMemberDecl *getAsMultiDeclMember(Decl *D) { + // XXX: Is this the best place for this check? + if (D->getLocation().isInvalid()) + return nullptr; + + // A FunctionDecl can be part of a multi-decl in C, but 3C currently doesn't + // handle this + // (https://github.com/correctcomputation/checkedc-clang/issues/659 part (a)). + + // While K&R parameter declarations can be in multi-decls in the input + // program, we don't use any of the regular multi-decl infrastructure for + // them; the rewriter blows them away and generates a prototype. + if (isa(D)) + return nullptr; + + if (VarDecl *VD = dyn_cast(D)) + return VD; + if (FieldDecl *FD = dyn_cast(D)) + return FD; + if (TypedefDecl *TD = dyn_cast(D)) + return TD; + return nullptr; +} + +QualType getTypeOfMultiDeclMember(MultiDeclMemberDecl *MMD) { + if (DeclaratorDecl *DD = dyn_cast(MMD)) + return DD->getType(); + if (TypedefDecl *TD = dyn_cast(MMD)) + return TD->getUnderlyingType(); + llvm_unreachable("Unexpected declaration type"); +} + +TypeSourceInfo *getTypeSourceInfoOfMultiDeclMember(MultiDeclMemberDecl *MMD) { + if (DeclaratorDecl *DD = dyn_cast(MMD)) + return DD->getTypeSourceInfo(); + if (TypedefDecl *TD = dyn_cast(MMD)) + return TD->getTypeSourceInfo(); + llvm_unreachable("Unexpected declaration type"); +} + +void ProgramMultiDeclsInfo::findUsedTagNames(DeclContext *DC) { + // We do our own traversal via `decls` rather than using RecursiveASTVisitor. + // This has the advantage of visiting TagDecls in function parameters, which + // RecursiveASTVisitor doesn't do by default, though such TagDecls are + // potentially problematic for 3C anyway. + for (Decl *D : DC->decls()) { + if (TagDecl *TD = dyn_cast(D)) { + if (!TD->getName().empty()) { + // Multiple TagDecls may have the same name if the same physical + // declaration is seen in multiple translation units or different + // TagDecls with the same name are used in different scopes. That is not + // a problem for us here: we're simply making a list of all the names we + // don't want to collide with. + UsedTagNames.insert(std::string(TD->getName())); + } + } + if (DeclContext *NestedDC = dyn_cast(D)) { + findUsedTagNames(NestedDC); + } + } +} + +void ProgramMultiDeclsInfo::findUsedTagNames(ASTContext &Context) { + findUsedTagNames(Context.getTranslationUnitDecl()); +} + +static const Type *unelaborateType(const Type *Ty) { + if (const ElaboratedType *ETy = dyn_cast(Ty)) { + QualType QT = ETy->getNamedType(); + Ty = QT.getTypePtr(); + // Can an ElaboratedType add qualifiers to its underlying type in C? I don't + // think so, but if it does, we don't want to silently lose them. + NONFATAL_ASSERT_PLACEHOLDER_UNUSED(QualType(Ty, 0) == QT); + } + return Ty; +} + +void ProgramMultiDeclsInfo::findMultiDecls(DeclContext *DC, + ASTContext &Context) { + // This will automatically create a new, empty map for the TU if needed. + TUMultiDeclsInfo &TUInfo = TUInfos[&Context]; + TagDecl *LastTagDef = nullptr; + + // Variables related to the current multi-decl. + MultiDeclInfo *CurrentMultiDecl = nullptr; + SourceLocation CurrentBeginLoc; + PersistentSourceLoc TagDefPSL; + bool TagDefNeedsName; + llvm::Optional RenameInfo; + bool AppliedRenameInfo; + + for (Decl *D : DC->decls()) { + TagDecl *TagD = dyn_cast(D); + if (TagD && TagD->isCompleteDefinition() && + // With -fms-extensions (default on Windows), Clang injects an implicit + // `struct _GUID` with an invalid location. + TagD->getBeginLoc().isValid()) { + LastTagDef = TagD; + } + if (MultiDeclMemberDecl *MMD = getAsMultiDeclMember(D)) { + if (CurrentMultiDecl == nullptr || + MMD->getBeginLoc() != CurrentBeginLoc) { + // We are starting a new multi-decl. + CurrentBeginLoc = MMD->getBeginLoc(); + CurrentMultiDecl = &TUInfo.MultiDeclsByBeginLoc[CurrentBeginLoc]; + assert(CurrentMultiDecl->Members.empty() && + "Multi-decl members are not consecutive in traversal order"); + TagDefNeedsName = false; + RenameInfo = llvm::None; + AppliedRenameInfo = false; + + // Check for an inline tag definition. + // Wanted: CurrentBeginLoc <= LastTagDef->getBeginLoc(). + // Implemented as: !(LastTagDef->getBeginLoc() < CurrentBeginLoc). + if (LastTagDef != nullptr && + !Context.getSourceManager().isBeforeInTranslationUnit( + LastTagDef->getBeginLoc(), CurrentBeginLoc)) { + CurrentMultiDecl->TagDefToSplit = LastTagDef; + TUInfo.ContainingMultiDeclOfTagDecl[LastTagDef] = CurrentMultiDecl; + + // Do we need to automatically name the TagDefToSplit? + if (LastTagDef->getName().empty()) { + // A RecordDecl that is declared as the type of one or more + // variables shouldn't be "anonymous", but if it somehow is, we + // don't want to try to give it a name. + NONFATAL_ASSERT_PLACEHOLDER_UNUSED( + !(isa(LastTagDef) && + cast(LastTagDef)->isAnonymousStructOrUnion())); + TagDefPSL = PersistentSourceLoc::mkPSL(LastTagDef, Context); + auto Iter = RenamedTagDefs.find(TagDefPSL); + if (Iter != RenamedTagDefs.end()) + RenameInfo = Iter->second; + else if (canWrite(TagDefPSL.getFileName())) + TagDefNeedsName = true; + } + } + } else { + // Adding another member to an existing multi-decl. + assert(Context.getSourceManager().isBeforeInTranslationUnit( + CurrentMultiDecl->Members.back()->getEndLoc(), + MMD->getEndLoc()) && + "Multi-decl traversal order inconsistent " + "with source location order"); + } + + CurrentMultiDecl->Members.push_back(MMD); + + std::string MemberName; + if (TagDefNeedsName && + NONFATAL_ASSERT_PLACEHOLDER( + !(MemberName = std::string(MMD->getName())).empty())) { + // Special case: If the first member of the multi-decl is a typedef + // whose type is exactly the TagDecl type (`typedef struct { ... } T`), + // then we refer to the TagDecl via that typedef. (The typedef must be + // the first member so that it is defined in time for other members to + // refer to it.) + // + // An argument could be made for using the typedef name in the types of + // other multi-decl members even if the TagDecl has a name: + // `typedef struct T_struct { ... } T, *PT;` would convert to + // `typedef struct T_struct { ... } T; typedef _Ptr PT;` instead of + // `struct T_struct { ... }; typedef struct T_struct T; + // typedef _Ptr PT;`. But it would be tricky to ensure + // that any existing references to `struct T_struct` aren't accidentally + // replaced with `T`, so absent a decision that this feature is + // important enough to justify either solving or ignoring this problem, + // we don't try to implement the feature. + TypedefDecl *TyD; + QualType Underlying; + if (CurrentMultiDecl->Members.size() == 1 && + (TyD = dyn_cast(MMD)) != nullptr && + // XXX: This is a terrible mess. Figure out how we should be + // handling the difference between Type and QualType. + !(Underlying = TyD->getUnderlyingType()).hasLocalQualifiers() && + QualType(unelaborateType(Underlying.getTypePtr()), 0) == + Context.getTagDeclType(LastTagDef)) { + // In this case, ShouldSplit = false: the tag definition should not be + // moved out of the typedef. + RenameInfo = RenamedTagDefInfo{MemberName, false}; + } else { + // Otherwise, just generate a new tag name based on the member name. + // Example: `struct { ... } foo;` -> + // `struct foo_struct_1 { ... }; struct foo_struct_1 foo;` + // If `foo_struct_1` is already taken, use `foo_struct_2`, etc. + std::string KindName = std::string(LastTagDef->getKindName()); + std::string NewName; + for (int Num = 1;; Num++) { + NewName = MemberName + "_" + KindName + "_" + std::to_string(Num); + if (UsedTagNames.find(NewName) == UsedTagNames.end()) + break; + } + RenameInfo = RenamedTagDefInfo{KindName + " " + NewName, true}; + // Consider this name taken and ensure that other automatically + // generated names do not collide with it. + // + // If the multi-decl doesn't end up getting rewritten, this name + // ultimately may not be used, creating a gap in the numbering in 3C's + // output. But this cosmetic inconsistency is a small price to pay for + // the architectural convenience of being able to store the assigned + // names in the PointerVariableConstraints when they are constructed + // rather than trying to assign and store the names after we know + // which multi-decls will be rewritten. + UsedTagNames.insert(NewName); + } + RenamedTagDefs.insert(std::make_pair(TagDefPSL, *RenameInfo)); + TagDefNeedsName = false; + } + + // To help avoid bugs, use the same code whether the RenameInfo was just + // assigned or was saved from a previous translation unit. + if (RenameInfo && !AppliedRenameInfo) { + CurrentMultiDecl->BaseTypeRenamed = true; + if (!RenameInfo->ShouldSplit) + CurrentMultiDecl->TagDefToSplit = nullptr; + AppliedRenameInfo = true; + } + } + + if (DeclContext *NestedDC = dyn_cast(D)) { + findMultiDecls(NestedDC, Context); + } + } +} + +void ProgramMultiDeclsInfo::findMultiDecls(ASTContext &Context) { + findMultiDecls(Context.getTranslationUnitDecl(), Context); +} + +llvm::Optional +ProgramMultiDeclsInfo::getTypeStrOverride(const Type *Ty, const ASTContext &C) { + Ty = unelaborateType(Ty); + if (const TagType *TTy = dyn_cast(Ty)) { + TagDecl *TD = TTy->getDecl(); + if (TD->getName().empty()) { + PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(TD, C); + auto Iter = RenamedTagDefs.find(PSL); + if (Iter != RenamedTagDefs.end()) + return Iter->second.AssignedTypeStr; + // We should have named all unnamed TagDecls in writable code. + NONFATAL_ASSERT_PLACEHOLDER_UNUSED(!canWrite(PSL.getFileName())); + } + } + return llvm::None; +} + +MultiDeclInfo * +ProgramMultiDeclsInfo::findContainingMultiDecl(MultiDeclMemberDecl *MMD) { + auto &MultiDeclsByLoc = TUInfos[&MMD->getASTContext()].MultiDeclsByBeginLoc; + // Look for a MultiDeclInfo for the beginning location of MMD, then check that + // the MultiDeclInfo actually contains MMD. + auto It = MultiDeclsByLoc.find(MMD->getBeginLoc()); + if (It == MultiDeclsByLoc.end()) + return nullptr; + MultiDeclInfo &MDI = It->second; + // Hope we don't have multi-decls with so many members that this becomes a + // performance problem. + if (std::find(MDI.Members.begin(), MDI.Members.end(), MMD) != + MDI.Members.end()) + return &MDI; + return nullptr; +} + +MultiDeclInfo *ProgramMultiDeclsInfo::findContainingMultiDecl(TagDecl *TD) { + auto &MDOfTD = TUInfos[&TD->getASTContext()].ContainingMultiDeclOfTagDecl; + auto It = MDOfTD.find(TD); + if (It == MDOfTD.end()) + return nullptr; + return It->second; +} + +bool ProgramMultiDeclsInfo::wasBaseTypeRenamed(Decl *D) { + // We assume that the base type was renamed if and only if D belongs to a + // multi-decl marked as having the base type renamed. It might be better to + // actually extract the base type from D and look it up in RenamedTagDefs, + // but that's more work. + MultiDeclMemberDecl *MMD = getAsMultiDeclMember(D); + if (!MMD) + return false; + MultiDeclInfo *MDI = findContainingMultiDecl(MMD); + // We expect to have a MultiDeclInfo for every MultiDeclMemberDecl in the + // program. + if (!NONFATAL_ASSERT_PLACEHOLDER(MDI)) + return false; + return MDI->BaseTypeRenamed; +} diff --git a/clang/lib/3C/PersistentSourceLoc.cpp b/clang/lib/3C/PersistentSourceLoc.cpp index 2fe317bda265..a37e52df92cd 100644 --- a/clang/lib/3C/PersistentSourceLoc.cpp +++ b/clang/lib/3C/PersistentSourceLoc.cpp @@ -19,20 +19,24 @@ using namespace llvm; // This currently the expansion location for the declarations source location. // If we want to add more complete source for macros in the future, I expect we // will need to the spelling location instead. -PersistentSourceLoc PersistentSourceLoc::mkPSL(const Decl *D, ASTContext &C) { +PersistentSourceLoc PersistentSourceLoc::mkPSL(const Decl *D, + const ASTContext &C) { + if (D == nullptr) return PersistentSourceLoc(); SourceLocation SL = C.getSourceManager().getExpansionLoc(D->getLocation()); return mkPSL(D->getSourceRange(), SL, C); } // Create a PersistentSourceLoc for a Stmt. PersistentSourceLoc PersistentSourceLoc::mkPSL(const Stmt *S, - ASTContext &Context) { + const ASTContext &Context) { + if (S == nullptr) return PersistentSourceLoc(); return mkPSL(S->getSourceRange(), S->getBeginLoc(), Context); } // Create a PersistentSourceLoc for an Expression. PersistentSourceLoc PersistentSourceLoc::mkPSL(const clang::Expr *E, - clang::ASTContext &Context) { + const clang::ASTContext &Context) { + if (E == nullptr) return PersistentSourceLoc(); return mkPSL(E->getSourceRange(), E->getBeginLoc(), Context); } @@ -40,8 +44,8 @@ PersistentSourceLoc PersistentSourceLoc::mkPSL(const clang::Expr *E, // line and column numbers for a SourceLocation. PersistentSourceLoc PersistentSourceLoc::mkPSL(clang::SourceRange SR, SourceLocation SL, - ASTContext &Context) { - SourceManager &SM = Context.getSourceManager(); + const ASTContext &Context) { + const SourceManager &SM = Context.getSourceManager(); PresumedLoc PL = SM.getPresumedLoc(SL); // If there is no PresumedLoc, create a nullary PersistentSourceLoc. diff --git a/clang/lib/3C/ProgramInfo.cpp b/clang/lib/3C/ProgramInfo.cpp index 53535d354699..883817899c3f 100644 --- a/clang/lib/3C/ProgramInfo.cpp +++ b/clang/lib/3C/ProgramInfo.cpp @@ -169,7 +169,8 @@ void ProgramInfo::printAggregateStats(const std::set &F, for (auto &I : Variables) { ConstraintVariable *C = I.second; std::string FileName = I.first.getFileName(); - if (F.count(FileName) || FileName.find(BaseDir) != std::string::npos) { + if (F.count(FileName) || + FileName.find(_3COpts.BaseDir) != std::string::npos) { if (C->isForValidDecl()) { FoundVars.clear(); getVarsFromConstraint(C, FoundVars, Visited); @@ -236,8 +237,8 @@ void ProgramInfo::printAggregateStats(const std::set &F, void ProgramInfo::printStats(const std::set &F, raw_ostream &O, bool OnlySummary, bool JsonFormat) { if (!OnlySummary && !JsonFormat) { - O << "Enable itype propagation:" << EnablePropThruIType << "\n"; - O << "Sound handling of var args functions:" << HandleVARARGS << "\n"; + O << "Sound handling of var args functions:" << _3COpts.HandleVARARGS + << "\n"; } std::map> FilesToVars; CVarSet InSrcCVars, Visited; @@ -247,7 +248,8 @@ void ProgramInfo::printStats(const std::set &F, raw_ostream &O, // First, build the map and perform the aggregation. for (auto &I : Variables) { std::string FileName = I.first.getFileName(); - if (F.count(FileName) || FileName.find(BaseDir) != std::string::npos) { + if (F.count(FileName) || + FileName.find(_3COpts.BaseDir) != std::string::npos) { int VarC = 0; int PC = 0; int NtaC = 0; @@ -352,7 +354,7 @@ void ProgramInfo::printStats(const std::set &F, raw_ostream &O, O << "}},\n"; } - if (AllTypes) { + if (_3COpts.AllTypes) { if (JsonFormat) { O << "\"BoundsStats\":"; } @@ -375,9 +377,12 @@ void ProgramInfo::printStats(const std::set &F, raw_ostream &O, bool ProgramInfo::link() { // For every global symbol in all the global symbols that we have found // go through and apply rules for whether they are functions or variables. - if (Verbose) + if (_3COpts.Verbose) llvm::errs() << "Linking!\n"; + auto Rsn = ReasonLoc("Linking global variables", + PersistentSourceLoc()); + // Equate the constraints for all global variables. // This is needed for variables that are defined as extern. for (const auto &V : GlobalVariableSymbols) { @@ -387,10 +392,10 @@ bool ProgramInfo::link() { std::set::iterator I = C.begin(); std::set::iterator J = C.begin(); ++J; - if (Verbose) + if (_3COpts.Verbose) llvm::errs() << "Global variables:" << V.first << "\n"; while (J != C.end()) { - constrainConsVarGeq(*I, *J, CS, nullptr, Same_to_Same, true, this); + constrainConsVarGeq(*I, *J, CS, Rsn, Same_to_Same, true, this); ++I; ++J; } @@ -402,56 +407,21 @@ bool ProgramInfo::link() { // constrain everything about it if (!V.second) { std::string VarName = V.first; - std::string Rsn = - "External global variable " + VarName + " has no definition"; + auto WildReason = ReasonLoc( + "External global variable " + VarName + " has no definition", + Rsn.Location); const std::set &C = GlobalVariableSymbols[VarName]; for (const auto &Var : C) { // TODO: Is there an easy way to get a PSL to attach to the constraint? - Var->constrainToWild(CS, Rsn); + Var->constrainToWild(CS, WildReason); } } } // For every global function that is an unresolved external, constrain // its parameter types to be wild. Unless it has a bounds-safe annotation. - for (const auto &U : ExternalFunctionFVCons) { - std::string FuncName = U.first; - FVConstraint *G = U.second; - - // If there was a checked type on a variable in the input program, it - // should stay that way. Otherwise, we shouldn't be adding a checked type - // to an extern function. - std::string Rsn = (G->hasBody() ? "" - : "Unchecked pointer in parameter or " - "return of external function " + - FuncName); - - // Handle the cases where itype parameters should not be treated as their - // unchecked type. - // TODO: Ditto re getting a PSL (in the case in which Rsn is non-empty and - // it is actually used). - G->equateWithItype(*this, Rsn, nullptr); - - // If we've seen this symbol, but never seen a body for it, constrain - // everything about it. - // Some global symbols we don't need to constrain to wild, like - // malloc and free. Check those here and skip if we find them. - if (!G->hasBody()) { - const FVComponentVariable *Ret = G->getCombineReturn(); - Ret->getInternal()->constrainToWild(CS, Rsn); - if (!Ret->getExternal()->srcHasItype() && - !Ret->getExternal()->getIsGeneric()) - Ret->getExternal()->constrainToWild(CS, Rsn); - - for (unsigned I = 0; I < G->numParams(); I++) { - const FVComponentVariable *Param = G->getCombineParam(I); - Param->getInternal()->constrainToWild(CS, Rsn); - if (!Param->getExternal()->srcHasItype() && - !Param->getExternal()->getIsGeneric()) - Param->getExternal()->constrainToWild(CS, Rsn); - } - } - } + for (const auto &U : ExternalFunctionFVCons) + linkFunction(U.second); // Repeat for static functions. // @@ -459,33 +429,47 @@ bool ProgramInfo::link() { // error during compilation. They may still be useful as code is developed, // so we treat them as if they are external, and constrain parameters // to wild as appropriate. - for (const auto &U : StaticFunctionFVCons) { - for (const auto &V : U.second) { + for (const auto &U : StaticFunctionFVCons) + for (const auto &V : U.second) + linkFunction(V.second); - std::string FileName = U.first; - std::string FuncName = V.first; - FVConstraint *G = V.second; - - std::string Rsn = (G->hasBody() ? "" - : "Unchecked pointer in parameter or " - "return of static function " + - FuncName + " in " + FileName); - - // TODO: Ditto re getting a PSL - G->equateWithItype(*this, Rsn, nullptr); + return true; +} - if (!G->hasBody()) { +void ProgramInfo::linkFunction(FunctionVariableConstraint *FV) { + // If there was a checked type on a variable in the input program, it + // should stay that way. Otherwise, we shouldn't be adding a checked type + // to an undefined function. DEFAULT_REASON is a sentinel for + // ConstraintVariable::equateWithItype; see the comment there. + std::string Rsn = (FV->hasBody() ? DEFAULT_REASON : "Unchecked pointer in parameter or " + "return of undefined function " + + FV->getName()); + + // Handle the cases where itype parameters should not be treated as their + // unchecked type. + // TODO: Ditto re getting a PSL (in the case in which Rsn is non-empty and + // it is actually used). + auto Reason = ReasonLoc(Rsn, PersistentSourceLoc()); + FV->equateWithItype(*this, Reason); + + // Used to apply constraints to parameters and returns for function without a + // body. In the default configuration, the function is fully constrained so + // that parameters and returns are considered unchecked. When 3C is run with + // --infer-types-for-undefs, only internal variables are constrained, allowing + // external variables to solve to checked types meaning the parameter will be + // rewritten to an itype. + auto LinkComponent = [this, Reason](const FVComponentVariable *FVC) { + FVC->getInternal()->constrainToWild(CS, Reason); + if (!_3COpts.InferTypesForUndefs && + !FVC->getExternal()->srcHasItype() && !FVC->getExternal()->isGeneric()) + FVC->getExternal()->constrainToWild(CS, Reason); + }; - if (!G->getExternalReturn()->getIsGeneric()) - G->getExternalReturn()->constrainToWild(CS, Rsn); - for (unsigned I = 0; I < G->numParams(); I++) - if (!G->getExternalParam(I)->getIsGeneric()) - G->getExternalParam(I)->constrainToWild(CS, Rsn); - } - } + if (!FV->hasBody()) { + LinkComponent(FV->getCombineReturn()); + for (unsigned I = 0; I < FV->numParams(); I++) + LinkComponent(FV->getCombineParam(I)); } - - return true; } // Populate Variables, VarDeclToStatement, RVariables, and DepthMap with @@ -567,46 +551,16 @@ ProgramInfo::insertNewFVConstraint(FunctionDecl *FD, FVConstraint *NewC, return (*Map)[FuncName]; // Error reporting - { // block to force DiagBuilder destructor and emit message - clang::DiagnosticsEngine &DE = C->getDiagnostics(); - unsigned FailID = DE.getCustomDiagID(DiagnosticsEngine::Fatal, - "merging failed for %q0 due to %1"); - const auto Pointer = reinterpret_cast(FD); - const auto Kind = clang::DiagnosticsEngine::ArgumentKind::ak_nameddecl; - auto DiagBuilder = DE.Report(FD->getLocation(), FailID); - DiagBuilder.AddTaggedVal(Pointer, Kind); - DiagBuilder.AddString(ReasonFailed); - } + reportCustomDiagnostic(C->getDiagnostics(), + DiagnosticsEngine::Fatal, + "merging failed for %q0 due to %1", + FD->getLocation()) + << FD << ReasonFailed; // A failed merge will provide poor data, but the diagnostic error report // will cause the program to terminate after the variable adder step. return (*Map)[FuncName]; } -void ProgramInfo::specialCaseVarIntros(ValueDecl *D, ASTContext *Context) { - // Special-case for va_list, constrain to wild. - bool IsGeneric = false; - PVConstraint *PVC = nullptr; - - CVarOption CVOpt = getVariable(D, Context); - if (CVOpt.hasValue()) { - ConstraintVariable &CV = CVOpt.getValue(); - PVC = dyn_cast(&CV); - } - - if (isa(D)) - IsGeneric = PVC && PVC->getIsGeneric(); - if (isVarArgType(D->getType().getAsString()) || - (hasVoidType(D) && !IsGeneric)) { - // Set the reason for making this variable WILD. - std::string Rsn = "Variable type void."; - PersistentSourceLoc PL = PersistentSourceLoc::mkPSL(D, *Context); - if (!D->getType()->isVoidType()) - Rsn = "Variable type is va_list."; - if (PVC != nullptr) - PVC->constrainToWild(CS, Rsn, &PL); - } -} - // For each pointer type in the declaration of D, add a variable to the // constraint system for that pointer type. void ProgramInfo::addVariable(clang::DeclaratorDecl *D, @@ -629,7 +583,7 @@ void ProgramInfo::addVariable(clang::DeclaratorDecl *D, // anyways. This happens if the name of the variable is a macro defined // differently is different parts of the program. std::string Rsn = "Duplicate source location. Possibly part of a macro."; - Variables[PLoc]->constrainToWild(CS, Rsn, &PLoc); + Variables[PLoc]->constrainToWild(CS, ReasonLoc(Rsn, PLoc)); } return; } @@ -652,7 +606,7 @@ void ProgramInfo::addVariable(clang::DeclaratorDecl *D, // function to the function map anyways. The function map indexes by // function name, so there's no collision. insertNewFVConstraint(FD, F, AstContext); - constrainWildIfMacro(F, FD->getLocation()); + constrainWildIfMacro(F, FD->getLocation(), ReasonLoc(MACRO_REASON, PLoc)); } else { // A function with the same name exists in the same source location. // This happens when a function is defined in a header file which is @@ -670,32 +624,36 @@ void ProgramInfo::addVariable(clang::DeclaratorDecl *D, NewCV = F; auto RetTy = FD->getReturnType(); - unifyIfTypedef(RetTy.getTypePtr(), *AstContext, FD, F->getExternalReturn()); - unifyIfTypedef(RetTy.getTypePtr(), *AstContext, FD, F->getInternalReturn()); - ensureNtCorrect(RetTy, *AstContext, F->getExternalReturn()); - ensureNtCorrect(RetTy, *AstContext, F->getInternalReturn()); + unifyIfTypedef(RetTy, *AstContext, F->getExternalReturn(), Wild_to_Safe); + unifyIfTypedef(RetTy, *AstContext, F->getInternalReturn(), Safe_to_Wild); + auto PSL = PersistentSourceLoc::mkPSL(FD,*AstContext); + ensureNtCorrect(RetTy, PSL, F->getExternalReturn()); + ensureNtCorrect(RetTy, PSL, F->getInternalReturn()); // Add mappings from the parameters PLoc to the constraint variables for // the parameters. for (unsigned I = 0; I < FD->getNumParams(); I++) { ParmVarDecl *PVD = FD->getParamDecl(I); - const Type *Ty = PVD->getType().getTypePtr(); + auto ParamPSL = PersistentSourceLoc::mkPSL(PVD,*AstContext); + QualType ParamTy = PVD->getType(); PVConstraint *PVInternal = F->getInternalParam(I); PVConstraint *PVExternal = F->getExternalParam(I); - unifyIfTypedef(Ty, *AstContext, PVD, PVInternal); - unifyIfTypedef(Ty, *AstContext, PVD, PVExternal); - ensureNtCorrect(PVD->getType(), *AstContext, PVInternal); - ensureNtCorrect(PVD->getType(), *AstContext, PVExternal); + unifyIfTypedef(ParamTy, *AstContext, PVExternal, Wild_to_Safe); + unifyIfTypedef(ParamTy, *AstContext, PVInternal, Safe_to_Wild); + ensureNtCorrect(ParamTy, ParamPSL, PVInternal); + ensureNtCorrect(ParamTy, ParamPSL, PVExternal); PVInternal->setValidDecl(); PersistentSourceLoc PSL = PersistentSourceLoc::mkPSL(PVD, *AstContext); // Constraint variable is stored on the parent function, so we need to // constrain to WILD even if we don't end up storing this in the map. - constrainWildIfMacro(PVExternal, PVD->getLocation()); - specialCaseVarIntros(PVD, AstContext); + constrainWildIfMacro(PVExternal, PVD->getLocation(), + ReasonLoc(MACRO_REASON, PSL)); // If this is "main", constrain its argv parameter to a nested arr - if (AllTypes && FuncName == "main" && FD->isGlobal() && I == 1) { - PVInternal->constrainOuterTo(CS, CS.getArr()); - PVInternal->constrainIdxTo(CS, CS.getNTArr(), 1); + if (_3COpts.AllTypes && FuncName == "main" && FD->isGlobal() && I == 1) { + PVInternal->constrainOuterTo(CS, CS.getArr(), + ReasonLoc(SPECIAL_REASON("main"), PSL)); + PVInternal->constrainIdxTo(CS, CS.getNTArr(), 1, + ReasonLoc(SPECIAL_REASON("main"), PSL)); } // It is possible to have a param decl in a macro when the function is // not. @@ -706,14 +664,15 @@ void ProgramInfo::addVariable(clang::DeclaratorDecl *D, } else if (VarDecl *VD = dyn_cast(D)) { assert(!isa(VD)); - const Type *Ty = VD->getTypeSourceInfo()->getTypeLoc().getTypePtr(); - if (Ty->isPointerType() || Ty->isArrayType()) { + QualType QT = VD->getTypeSourceInfo()->getTypeLoc().getType(); + if (QT->isPointerType() || QT->isArrayType()) { PVConstraint *P = new PVConstraint(D, *this, *AstContext); P->setValidDecl(); NewCV = P; std::string VarName(VD->getName()); - unifyIfTypedef(Ty, *AstContext, VD, P); - ensureNtCorrect(VD->getType(), *AstContext, P); + unifyIfTypedef(QT, *AstContext, P); + auto PSL = PersistentSourceLoc::mkPSL(VD, *AstContext); + ensureNtCorrect(VD->getType(), PSL, P); if (VD->hasGlobalStorage()) { // If we see a definition for this global variable, indicate so in // ExternGVars. @@ -727,61 +686,62 @@ void ProgramInfo::addVariable(clang::DeclaratorDecl *D, } GlobalVariableSymbols[VarName].insert(P); } - specialCaseVarIntros(D, AstContext); } } else if (FieldDecl *FlD = dyn_cast(D)) { - const Type *Ty = FlD->getTypeSourceInfo()->getTypeLoc().getTypePtr(); - if (Ty->isPointerType() || Ty->isArrayType()) { + QualType QT = FlD->getTypeSourceInfo()->getTypeLoc().getType(); + if (QT->isPointerType() || QT->isArrayType()) { PVConstraint *P = new PVConstraint(D, *this, *AstContext); - unifyIfTypedef(Ty, *AstContext, FlD, P); + unifyIfTypedef(QT, *AstContext, P); NewCV = P; NewCV->setValidDecl(); - specialCaseVarIntros(D, AstContext); + if (FlD->getParent()->isUnion()) { + auto Rsn = ReasonLoc(UNION_FIELD_REASON, PLoc); + NewCV->equateWithItype(*this, Rsn); + NewCV->constrainToWild(CS, Rsn); + } } } else llvm_unreachable("unknown decl type"); assert("We shouldn't be adding a null CV to Variables map." && NewCV); if (!canWrite(PLoc.getFileName())) { - NewCV->equateWithItype(*this, "Declaration in non-writable file", &PLoc); - NewCV->constrainToWild(CS, "Declaration in non-writable file", &PLoc); + auto Rsn = ReasonLoc(UNWRITABLE_REASON, PLoc); + NewCV->equateWithItype(*this, Rsn); + NewCV->constrainToWild(CS, Rsn); } - constrainWildIfMacro(NewCV, D->getLocation()); + constrainWildIfMacro(NewCV, D->getLocation(), ReasonLoc(MACRO_REASON, PLoc)); Variables[PLoc] = NewCV; } -void ProgramInfo::ensureNtCorrect(const QualType &QT, const ASTContext &C, +void ProgramInfo::ensureNtCorrect(const QualType &QT, + const PersistentSourceLoc &PSL, PointerVariableConstraint *PV) { - if (AllTypes && !canBeNtArray(QT)) { - PV->constrainOuterTo(CS, CS.getArr(), true, true); + if (_3COpts.AllTypes && !canBeNtArray(QT)) { + PV->constrainOuterTo(CS, CS.getArr(), + ReasonLoc(ARRAY_REASON, PSL), true, true); } } -void ProgramInfo::unifyIfTypedef(const Type *Ty, ASTContext &Context, - DeclaratorDecl *Decl, PVConstraint *P) { - if (const auto *TDT = dyn_cast(Ty)) { +void ProgramInfo::unifyIfTypedef(const QualType &QT, ASTContext &Context, + PVConstraint *P, ConsAction CA) { + if (const auto *TDT = dyn_cast(QT.getTypePtr())) { auto *TDecl = TDT->getDecl(); auto PSL = PersistentSourceLoc::mkPSL(TDecl, Context); auto O = lookupTypedef(PSL); + auto Rsn = ReasonLoc("typedef", PSL); if (O.hasValue()) { auto *Bounds = &O.getValue(); - P->setTypedef(TDecl, TDecl->getNameAsString()); - constrainConsVarGeq(P, Bounds, CS, &PSL, Same_to_Same, true, this); + P->setTypedef(Bounds, TDecl->getNameAsString()); + constrainConsVarGeq(P, Bounds, CS, Rsn, CA, false, this); } } } ProgramInfo::IDAndTranslationUnit ProgramInfo::getExprKey(Expr *E, ASTContext *C) const { - // TODO: Main file name can be shared by multiple translation units if on file - // is compiled multiple times with different defines - std::string Name = - C->getSourceManager() - .getFileEntryForID(C->getSourceManager().getMainFileID()) - ->getName() - .str(); - return std::make_pair(getStmtIdWorkaround(E, *C), Name); + return std::make_pair(getStmtIdWorkaround(E, *C), + TranslationUnitIdxMap.at(C)); } bool ProgramInfo::hasPersistentConstraints(Expr *E, ASTContext *C) const { @@ -822,13 +782,31 @@ void ProgramInfo::storePersistentConstraints(Expr *E, const CSetBkeyPair &Vars, auto PSL = PersistentSourceLoc::mkPSL(E, *C); if (PSL.valid() && !canWrite(PSL.getFileName())) for (ConstraintVariable *CVar : Vars.first) - CVar->constrainToWild(CS, "Expression in non-writable file", &PSL); + CVar->constrainToWild(CS, ReasonLoc(UNWRITABLE_REASON, PSL)); IDAndTranslationUnit Key = getExprKey(E, C); ExprConstraintVars[Key] = Vars; ExprLocations[Key] = PSL; } +void ProgramInfo::removePersistentConstraints(Expr *E, ASTContext *C) { + assert(hasPersistentConstraints(E, C) && + "Persistent constraints not present."); + + IDAndTranslationUnit Key = getExprKey(E, C); + + // Save VarAtom locations so they can be used to assign source locations to + // root causes. + for (auto *CV : ExprConstraintVars[Key].first) + if (auto *PVC = dyn_cast(CV)) + for (Atom *A : PVC->getCvars()) + if (auto *VA = dyn_cast(A)) + DeletedAtomLocations[VA->getLoc()] = ExprLocations[Key]; + + ExprConstraintVars.erase(Key); + ExprLocations.erase(Key); +} + // The Rewriter won't let us re-write things that are in macros. So, we // should check to see if what we just added was defined within a macro. // If it was, we should constrain it to top. This is sad. Hopefully, @@ -836,10 +814,9 @@ void ProgramInfo::storePersistentConstraints(Expr *E, const CSetBkeyPair &Vars, // in macros. void ProgramInfo::constrainWildIfMacro(ConstraintVariable *CV, SourceLocation Location, - PersistentSourceLoc *PSL) { - std::string Rsn = "Pointer in Macro declaration."; + const ReasonLoc &Rsn) { if (!Rewriter::isRewritable(Location)) - CV->constrainToWild(CS, Rsn, PSL); + CV->constrainToWild(CS, Rsn); } //std::string ProgramInfo::getUniqueDeclKey(Decl *D, ASTContext *C) { @@ -877,6 +854,14 @@ FVConstraint *ProgramInfo::getFuncConstraint(FunctionDecl *D, return getStaticFuncConstraint(FuncName, FileName); } +FVConstraint *ProgramInfo::getFuncConstraint(const std::string &FuncName, + const std::string &FileName, + bool IsStatic) const { + if (IsStatic || !getExtFuncDefnConstraint(FuncName)) + return getStaticFuncConstraint(FuncName, FileName); + return getExtFuncDefnConstraint(FuncName); +} + FVConstraint *ProgramInfo::getFuncFVConstraint(FunctionDecl *FD, ASTContext *C) { std::string FuncName = FD->getNameAsString(); @@ -1000,20 +985,6 @@ bool ProgramInfo::computeInterimConstraintState( std::set DirectWildVarAtoms; CS.getChkCG().getSuccessors(CS.getWild(), DirectWildVarAtoms); - // Maps each atom to the set of atoms which depend on it through an - // implication constraint. These atoms would not be associated with the - // correct root cause through a BFS because an explicit edge does not exist - // between the cause and these atoms. Implication firing adds an edge from - // WILD to the LHS conclusion ptr. The logical flow of WILDness, however, is - // from the premise LHS to conclusion LHS. - std::map> ImpMap; - for (auto *C : getConstraints().getConstraints()) - if (auto *Imp = dyn_cast(C)) { - auto *Pre = Imp->getPremise(); - auto *Con = Imp->getConclusion(); - ImpMap[Pre->getLHS()].insert(Con->getLHS()); - } - CVars TmpCGrp; CVars OnlyIndirect; for (auto *A : DirectWildVarAtoms) { @@ -1037,10 +1008,6 @@ bool ProgramInfo::computeInterimConstraintState( } }; CS.getChkCG().visitBreadthFirst(VA, BFSVisitor); - if (ImpMap.find(A) != ImpMap.end()) - for (Atom *ImpA : ImpMap[A]) - if (isa(ImpA)) - CS.getChkCG().visitBreadthFirst(ImpA, BFSVisitor); CState.TotalNonDirectWildAtoms.insert(OnlyIndirect.begin(), OnlyIndirect.end()); @@ -1061,23 +1028,32 @@ bool ProgramInfo::computeInterimConstraintState( // Variables before ExprConstraintVars and making insertIntoPtrSourceMap not // overwrite a PSL already recorded for a given atom. for (const auto &I : Variables) - insertIntoPtrSourceMap(&(I.first), I.second); + insertIntoPtrSourceMap(I.first, I.second); for (const auto &I : ExprConstraintVars) { - PersistentSourceLoc *PSL = &ExprLocations[I.first]; + PersistentSourceLoc PSL = ExprLocations[I.first]; for (auto *J : I.second.first) insertIntoPtrSourceMap(PSL, J); } + for (auto E : DeletedAtomLocations) + CState.AtomSourceMap.insert(std::make_pair(E.first, E.second)); auto &WildPtrsReason = CState.RootWildAtomsWithReason; - for (auto *CurrC : CS.getConstraints()) { + for (Constraint *CurrC : CS.getConstraints()) { if (Geq *EC = dyn_cast(CurrC)) { VarAtom *VLhs = dyn_cast(EC->getLHS()); if (EC->constraintIsChecked() && dyn_cast(EC->getRHS())) { PersistentSourceLoc PSL = EC->getLocation(); - const PersistentSourceLoc *APSL = CState.AtomSourceMap[VLhs->getLoc()]; - if (!PSL.valid() && APSL && APSL->valid()) - PSL = *APSL; - WildPointerInferenceInfo Info(EC->getReason(), PSL); + PersistentSourceLoc APSL = CState.AtomSourceMap[VLhs->getLoc()]; + if (!PSL.valid() && APSL.valid()) + PSL = APSL; + auto Rsn = ReasonLoc(EC->getReasonText(), PSL); + RootCauseDiagnostic Info(Rsn); + for (const auto &Reason : CurrC->additionalReasons()) { + PersistentSourceLoc P = Reason.Location; + if (!P.valid() && APSL.valid()) + P = APSL; + Info.addReason(ReasonLoc(Reason.Reason, P)); + } WildPtrsReason.insert(std::make_pair(VLhs->getLoc(), Info)); } } @@ -1087,9 +1063,9 @@ bool ProgramInfo::computeInterimConstraintState( return true; } -void ProgramInfo::insertIntoPtrSourceMap(const PersistentSourceLoc *PSL, +void ProgramInfo::insertIntoPtrSourceMap(PersistentSourceLoc PSL, ConstraintVariable *CV) { - std::string FilePath = PSL->getFileName(); + std::string FilePath = PSL.getFileName(); if (canWrite(FilePath)) CState.ValidSourceFiles.insert(FilePath); @@ -1157,30 +1133,32 @@ void ProgramInfo::computePtrLevelStats() { } void ProgramInfo::setTypeParamBinding(CallExpr *CE, unsigned int TypeVarIdx, - ConstraintVariable *CV, ASTContext *C) { + ConstraintVariable *CV, + ConstraintVariable *Ident, + ASTContext *C) { - auto PSL = PersistentSourceLoc::mkPSL(CE, *C); - auto CallMap = TypeParamBindings[PSL]; + auto Key = getExprKey(CE, C); + auto CallMap = TypeParamBindings[Key]; if (CallMap.find(TypeVarIdx) == CallMap.end()) { - TypeParamBindings[PSL][TypeVarIdx] = CV; + TypeParamBindings[Key][TypeVarIdx] = TypeParamConstraint(CV,Ident); } else { // If this CE/idx is at the same location, it's in a macro, // so mark it as inconsistent. - TypeParamBindings[PSL][TypeVarIdx] = nullptr; + TypeParamBindings[Key][TypeVarIdx] = TypeParamConstraint(nullptr,nullptr); } } bool ProgramInfo::hasTypeParamBindings(CallExpr *CE, ASTContext *C) const { - auto PSL = PersistentSourceLoc::mkPSL(CE, *C); - return TypeParamBindings.find(PSL) != TypeParamBindings.end(); + auto Key = getExprKey(CE, C); + return TypeParamBindings.find(Key) != TypeParamBindings.end(); } const ProgramInfo::CallTypeParamBindingsT & ProgramInfo::getTypeParamBindings(CallExpr *CE, ASTContext *C) const { - auto PSL = PersistentSourceLoc::mkPSL(CE, *C); + auto Key = getExprKey(CE, C); assert("Type parameter bindings could not be found." && - TypeParamBindings.find(PSL) != TypeParamBindings.end()); - return TypeParamBindings.at(PSL); + TypeParamBindings.find(Key) != TypeParamBindings.end()); + return TypeParamBindings.at(Key); } CVarOption ProgramInfo::lookupTypedef(PersistentSourceLoc PSL) { @@ -1191,20 +1169,28 @@ bool ProgramInfo::seenTypedef(PersistentSourceLoc PSL) { return TypedefVars.count(PSL) != 0; } -void ProgramInfo::addTypedef(PersistentSourceLoc PSL, bool CanRewriteDef, - TypedefDecl *TD, ASTContext &C) { +void ProgramInfo::addTypedef(PersistentSourceLoc PSL, TypedefDecl *TD, + ASTContext &C) { ConstraintVariable *V = nullptr; if (isa(TD->getUnderlyingType())) V = new FunctionVariableConstraint(TD, *this, C); else V = new PointerVariableConstraint(TD, *this, C); - auto *const Rsn = !CanRewriteDef - ? "Unable to rewrite a typedef with multiple names" - : "Declaration in non-writable file"; - if (!(CanRewriteDef && canWrite(PSL.getFileName()))) { - V->constrainToWild(this->getConstraints(), Rsn, &PSL); - } - constrainWildIfMacro(V, TD->getLocation(), &PSL); + if (!canWrite(PSL.getFileName())) + V->constrainToWild(this->getConstraints(), + ReasonLoc(UNWRITABLE_REASON, PSL)); + + constrainWildIfMacro(V, TD->getLocation(), ReasonLoc(MACRO_REASON, PSL)); this->TypedefVars[PSL] = {*V}; } + +void ProgramInfo::registerTranslationUnits( + const std::vector> &ASTs) { + assert(TranslationUnitIdxMap.empty()); + unsigned int Idx = 0; + for (const auto &AST : ASTs) { + TranslationUnitIdxMap[&(AST->getASTContext())] = Idx; + Idx++; + } +} diff --git a/clang/lib/3C/ProgramVar.cpp b/clang/lib/3C/ProgramVar.cpp index fd58f4edcb24..f3df0bdb5c94 100644 --- a/clang/lib/3C/ProgramVar.cpp +++ b/clang/lib/3C/ProgramVar.cpp @@ -94,35 +94,26 @@ const FunctionScope *FunctionScope::getFunctionScope(std::string FnName, return &FS; } -std::set ProgramVar::AllProgramVars; +std::set ProgramVar::AllProgramVars; -std::string ProgramVar::mkString(bool GetKey) { - std::string Ret = ""; - if (GetKey) { - Ret = std::to_string(K) + "_"; - } - if (GetKey && IsConstant) { +std::string ProgramVar::verboseStr() const { + std::string Ret = std::to_string(K) + "_"; + if (IsConstant) Ret += "Cons:"; - } - Ret += VarName; - return Ret; + return Ret + VarName + "(" + VScope->getStr() + ")"; } -ProgramVar *ProgramVar::makeCopy(BoundsKey NK) { - ProgramVar *NewPVar = - new ProgramVar(NK, this->VarName, this->VScope, this->IsConstant); - return NewPVar; +ProgramVar *ProgramVar::makeCopy(BoundsKey NK) const { + return new ProgramVar(NK, this->VarName, this->VScope, this->IsConstant, + this->ConstantVal); } -std::string ProgramVar::verboseStr() { - std::string Ret = mkString(true) + "(" + VScope->getStr() + ")"; - return Ret; +ProgramVar *ProgramVar::createNewConstantVar(BoundsKey VK, + uint64_t Value) { + return new ProgramVar(VK, Value); } ProgramVar *ProgramVar::createNewProgramVar(BoundsKey VK, std::string VName, - const ProgramVarScope *PVS, - bool IsCons) { - ProgramVar *NewPV = new ProgramVar(VK, VName, PVS, IsCons); - AllProgramVars.insert(NewPV); - return NewPV; -} + const ProgramVarScope *PVS) { + return new ProgramVar(VK, VName, PVS); +} \ No newline at end of file diff --git a/clang/lib/3C/RewriteUtils.cpp b/clang/lib/3C/RewriteUtils.cpp index e26069ad4448..cce423f8ad24 100644 --- a/clang/lib/3C/RewriteUtils.cpp +++ b/clang/lib/3C/RewriteUtils.cpp @@ -14,6 +14,7 @@ #include "clang/3C/CastPlacement.h" #include "clang/3C/CheckedRegions.h" #include "clang/3C/DeclRewriter.h" +#include "clang/3C/LowerBoundAssignment.h" #include "clang/3C/TypeVariableAnalysis.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Tooling/Transformer/SourceCode.h" @@ -21,108 +22,97 @@ using namespace llvm; using namespace clang; -SourceLocation DComp::getDeclBegin(DeclReplacement *D) const { - SourceLocation Begin = - (*D->getStatement()->decls().begin())->getSourceRange().getBegin(); - for (const auto &DT : D->getStatement()->decls()) { - if (DT == D->getDecl()) - return Begin; - Begin = DT->getSourceRange().getEnd(); - } - llvm_unreachable("Declaration not found in DeclStmt."); -} - -SourceRange DComp::getReplacementSourceRange(DeclReplacement *D) const { - SourceRange Range = D->getSourceRange(SM); - - // Also take into account whether or not there is a multi-statement - // decl, because the generated ranges will overlap. - DeclStmt *LhStmt = D->getStatement(); - if (LhStmt && !LhStmt->isSingleDecl()) { - SourceLocation NewBegin = getDeclBegin(D); - Range.setBegin(NewBegin); - // This is needed to make the subsequent test inclusive. - Range.setEnd(Range.getEnd().getLocWithOffset(-1)); +RewrittenDecl mkStringForPVDecl(MultiDeclMemberDecl *MMD, PVConstraint *PVC, + ProgramInfo &Info) { + // Currently, it's cheap to keep recreating the ArrayBoundsRewriter. If that + // ceases to be true, we should pass it along as another argument. + ArrayBoundsRewriter ABRewriter{Info}; + + RewrittenDecl RD; + bool IsExternGlobalVar = + isa(MMD) && + cast(MMD)->getFormalLinkage() == Linkage::ExternalLinkage; + if (_3COpts.ItypesForExtern && (isa(MMD) || IsExternGlobalVar) && + // isSolutionChecked can return false here when splitting out an unchanged + // multi-decl member. + PVC->isSolutionChecked(Info.getConstraints().getVariables())) { + // Give record fields and global variables itypes when using + // -itypes-for-extern. Note that we haven't properly implemented itypes for + // structures and globals + // (https://github.com/correctcomputation/checkedc-clang/issues/744). This + // just rewrites to an itype instead of a fully checked type when a checked + // type could have been used. This does provide most of the rewriting + // infrastructure that would be required to support these itypes if + // constraint generation is updated to handle structure/global itypes. + RD = DeclRewriter::buildItypeDecl(PVC, cast(MMD), + PVC->getName(), Info, ABRewriter, true, + true); + } else { + RD = DeclRewriter::buildCheckedDecl(PVC, cast(MMD), + PVC->getName(), Info, ABRewriter, + true); } + RD.Type = getStorageQualifierString(MMD) + RD.Type; - return Range; + return RD; } -bool DComp::operator()(DeclReplacement *Lhs, DeclReplacement *Rhs) const { - // Does the source location of the Decl in lhs overlap at all with - // the source location of rhs? - SourceRange SrLhs = getReplacementSourceRange(Lhs); - SourceRange SrRhs = getReplacementSourceRange(Rhs); - - SourceLocation X1 = SrLhs.getBegin(); - SourceLocation X2 = SrLhs.getEnd(); - SourceLocation Y1 = SrRhs.getBegin(); - SourceLocation Y2 = SrRhs.getEnd(); - - if (Lhs->getStatement() == nullptr && Rhs->getStatement() == nullptr) { - // These are global declarations. Get the source location - // and compare them lexicographically. - PresumedLoc LHsPLocB = SM.getPresumedLoc(X2); - PresumedLoc RHsPLocE = SM.getPresumedLoc(Y2); - - // Are both the source location valid? - if (LHsPLocB.isValid() && RHsPLocE.isValid()) { - // They are in same fine? - if (!strcmp(LHsPLocB.getFilename(), RHsPLocE.getFilename())) { - // Are they in same line? - if (LHsPLocB.getLine() == RHsPLocE.getLine()) - return LHsPLocB.getColumn() < RHsPLocE.getColumn(); - - return LHsPLocB.getLine() < RHsPLocE.getLine(); - } - return strcmp(LHsPLocB.getFilename(), RHsPLocE.getFilename()) > 0; - } - return LHsPLocB.isValid(); +std::string mkStringForDeclWithUnchangedType(MultiDeclMemberDecl *MMD, + ProgramInfo &Info) { + ASTContext &Context = MMD->getASTContext(); + + bool BaseTypeRenamed = Info.TheMultiDeclsInfo.wasBaseTypeRenamed(MMD); + if (!BaseTypeRenamed) { + // As far as we know, we can let Clang generate the declaration string. + PrintingPolicy Policy = Context.getPrintingPolicy(); + Policy.SuppressInitializers = true; + std::string DeclStr = ""; + raw_string_ostream DeclStream(DeclStr); + MMD->print(DeclStream, Policy); + assert("Original decl string empty." && !DeclStr.empty()); + return DeclStr; } - bool Contained = SM.isBeforeInTranslationUnit(X1, Y2) && - SM.isBeforeInTranslationUnit(Y1, X2); - - if (Contained) - return false; - return SM.isBeforeInTranslationUnit(X2, Y1); -} - -void GlobalVariableGroups::addGlobalDecl(Decl *VD, std::vector *VDVec) { - if (VD && GlobVarGroups.find(VD) == GlobVarGroups.end()) { - if (VDVec == nullptr) - VDVec = new std::vector(); - assert("Decls in group are not ordered correctly." && - (VDVec->empty() || - SM.isBeforeInTranslationUnit(VDVec->back()->getEndLoc(), - VD->getEndLoc()))); - VDVec->push_back(VD); - GlobVarGroups[VD] = VDVec; - // Process the next decl. - Decl *NDecl = VD->getNextDeclInContext(); - if (isa_and_nonnull(NDecl) || isa_and_nonnull(NDecl)) - if (VD->getBeginLoc() == NDecl->getBeginLoc()) - addGlobalDecl(dyn_cast(NDecl), VDVec); + // OK, we have to use mkString. + QualType DType = getTypeOfMultiDeclMember(MMD); + if (isPtrOrArrayType(DType)) { + CVarOption CVO = + (isa(MMD) + ? Info.lookupTypedef(PersistentSourceLoc::mkPSL(MMD, Context)) + : Info.getVariable(MMD, &Context)); + assert(CVO.hasValue() && + "Missing ConstraintVariable for unchanged multi-decl member"); + // A function currently can't be a multi-decl member, so this should always + // be a PointerVariableConstraint. + PVConstraint *PVC = cast(&CVO.getValue()); + // Currently, we benefit from the ItypesForExtern handling in + // mkStringForPVDecl in one very unusual case: an unchanged multi-decl + // member with a renamed TagDecl and an existing implicit itype coming from + // a bounds annotation will keep the itype and not be changed to a fully + // checked type. DeclRewriter::buildItypeDecl will detect the base type + // rename and generate the unchecked side using mkString instead of + // Decl::print in order to pick up the new name. + // + // As long as 3C lacks real support for itypes on variables, this is + // probably the behavior we want with -itypes-for-extern. If we don't care + // about this case, we could alternatively inline the few lines of + // mkStringForPVDecl that would still be relevant. + RewrittenDecl RD = mkStringForPVDecl(MMD, PVC, Info); + assert(RD.SupplementaryDecl.empty()); + return RD.Type + RD.IType; } -} - -std::vector &GlobalVariableGroups::getVarsOnSameLine(Decl *D) { - assert(GlobVarGroups.find(D) != GlobVarGroups.end() && - "Expected to find the group."); - return *(GlobVarGroups[D]); -} -GlobalVariableGroups::~GlobalVariableGroups() { - std::set *> VVisited; - // Free each of the group. - for (auto &CurrV : GlobVarGroups) { - // Avoid double free by caching deleted sets. - if (VVisited.find(CurrV.second) != VVisited.end()) - continue; - VVisited.insert(CurrV.second); - delete (CurrV.second); - } - GlobVarGroups.clear(); + // If the type is not a pointer or array, then it should just equal the base + // type except for top-level qualifiers, and it can't have itypes or bounds. + llvm::Optional BaseTypeNewNameOpt = + Info.TheMultiDeclsInfo.getTypeStrOverride(DType.getTypePtr(), Context); + assert(BaseTypeNewNameOpt && + "BaseTypeRenamed is true but we couldn't get the new name"); + std::string QualifierPrefix = DType.getQualifiers().getAsString(); + if (!QualifierPrefix.empty()) + QualifierPrefix += " "; + return getStorageQualifierString(MMD) + QualifierPrefix + + *BaseTypeNewNameOpt + " " + std::string(MMD->getName()); } // Test to see if we can rewrite a given SourceRange. @@ -139,13 +129,37 @@ void rewriteSourceRange(Rewriter &R, const SourceRange &Range, ErrFail); } +// Wrapper for Rewriter::ReplaceText(CharSourceRange, StringRef) that works +// around a bug that occurs when text has previously been inserted at the start +// location of the specified range. +// +// When Rewriter::ReplaceText(CharSourceRange, StringRef) computes the range of +// the rewrite buffer to replace, it sets the start location to be after the +// previous insertion (RewriteBuffer::ReplaceText calls getMappedOffset with +// AfterInserts = true) but sets the length to include the length of the +// previous insertion (it calls getRangeSize with the default RewriteOptions +// with IncludeInsertsAtBeginOfRange = true). This causes the range to extend +// beyond the intended end location by an amount equal to the length of the +// previous insertion. We avoid the problem by calling getRangeSize ourselves +// with IncludeInsertsAtBeginOfRange = false. +// +// TODO: File an upstream Clang bug report if appropriate. As of this writing +// (2021-11-24), we found some discussion of the problem +// (https://reviews.llvm.org/D107503) but not a real entry in the bug tracker. +static bool replaceTextWorkaround(Rewriter &R, const CharSourceRange &Range, + const std::string &NewText) { + Rewriter::RewriteOptions Opts; + Opts.IncludeInsertsAtBeginOfRange = false; + return R.ReplaceText(Range.getBegin(), R.getRangeSize(Range, Opts), NewText); +} + void rewriteSourceRange(Rewriter &R, const CharSourceRange &Range, const std::string &NewText, bool ErrFail) { // Attempt to rewrite the source range. First use the source range directly // from the parameter. bool RewriteSuccess = false; if (canRewrite(R, Range)) - RewriteSuccess = !R.ReplaceText(Range, NewText); + RewriteSuccess = !replaceTextWorkaround(R, Range, NewText); // If initial rewriting attempt failed (either because canRewrite returned // false or because ReplaceText failed (returning true), try rewriting again @@ -154,7 +168,7 @@ void rewriteSourceRange(Rewriter &R, const CharSourceRange &Range, CharSourceRange Expand = clang::Lexer::makeFileCharRange( Range, R.getSourceMgr(), R.getLangOpts()); if (canRewrite(R, Expand)) - RewriteSuccess = !R.ReplaceText(Expand, NewText); + RewriteSuccess = !replaceTextWorkaround(R, Expand, NewText); } // Emit an error if we were unable to rewrite the source range. This is more @@ -163,64 +177,62 @@ void rewriteSourceRange(Rewriter &R, const CharSourceRange &Range, // crashing with an assert fail. if (!RewriteSuccess) { clang::DiagnosticsEngine &DE = R.getSourceMgr().getDiagnostics(); - bool ReportError = ErrFail && !AllowRewriteFailures; - { - // Put this in a block because Clang only allows one DiagnosticBuilder to - // exist at a time. - unsigned ErrorId = DE.getCustomDiagID( - ReportError ? DiagnosticsEngine::Error : DiagnosticsEngine::Warning, - "Unable to rewrite converted source range. Intended rewriting: " - "\"%0\""); - auto ErrorBuilder = DE.Report(Range.getBegin(), ErrorId); - ErrorBuilder.AddSourceRange(R.getSourceMgr().getExpansionRange(Range)); - ErrorBuilder.AddString(NewText); - } + bool ReportError = ErrFail && !_3COpts.AllowRewriteFailures; + reportCustomDiagnostic( + DE, + ReportError ? DiagnosticsEngine::Error : DiagnosticsEngine::Warning, + "Unable to rewrite converted source range. Intended rewriting: " + "\"%0\"", + Range.getBegin()) + << R.getSourceMgr().getExpansionRange(Range) << NewText; if (ReportError) { - unsigned NoteId = DE.getCustomDiagID( - DiagnosticsEngine::Note, + reportCustomDiagnostic( + DE, DiagnosticsEngine::Note, "you can use the -allow-rewrite-failures option to temporarily " - "downgrade this error to a warning"); - // If we pass the location here, the macro call stack gets dumped again, - // which looks silly. - DE.Report(NoteId); + "downgrade this error to a warning", + // If we pass the location here, the macro call stack gets dumped + // again, which looks silly. + SourceLocation()); } } } -static void emit(Rewriter &R, ASTContext &C) { - if (Verbose) +static void emit(Rewriter &R, ASTContext &C, bool &StdoutModeEmittedMainFile) { + if (_3COpts.Verbose) errs() << "Writing files out\n"; - bool StdoutMode = (OutputPostfix == "-" && OutputDir.empty()); - bool StdoutModeSawMainFile = false; + bool StdoutMode = (_3COpts.OutputPostfix == "-" && _3COpts.OutputDir.empty()); SourceManager &SM = C.getSourceManager(); // Iterate over each modified rewrite buffer. for (auto Buffer = R.buffer_begin(); Buffer != R.buffer_end(); ++Buffer) { if (const FileEntry *FE = SM.getFileEntryForID(Buffer->first)) { assert(FE->isValid()); + // Used for diagnostics related to the file. + SourceLocation BeginningOfFileSourceLoc = + SM.translateFileLineCol(FE, 1, 1); DiagnosticsEngine &DE = C.getDiagnostics(); DiagnosticsEngine::Level UnwritableChangeDiagnosticLevel = - AllowUnwritableChanges ? DiagnosticsEngine::Warning - : DiagnosticsEngine::Error; + _3COpts.AllowUnwritableChanges ? DiagnosticsEngine::Warning + : DiagnosticsEngine::Error; auto PrintExtraUnwritableChangeInfo = [&]() { // With -dump-unwritable-changes and not -allow-unwritable-changes, we // want the -allow-unwritable-changes note before the dump. - if (!DumpUnwritableChanges) { - unsigned DumpNoteId = DE.getCustomDiagID( - DiagnosticsEngine::Note, + if (!_3COpts.DumpUnwritableChanges) { + reportCustomDiagnostic( + DE, DiagnosticsEngine::Note, "use the -dump-unwritable-changes option to see the new version " - "of the file"); - DE.Report(DumpNoteId); + "of the file", + SourceLocation()); } - if (!AllowUnwritableChanges) { - unsigned AllowNoteId = DE.getCustomDiagID( - DiagnosticsEngine::Note, + if (!_3COpts.AllowUnwritableChanges) { + reportCustomDiagnostic( + DE, DiagnosticsEngine::Note, "you can use the -allow-unwritable-changes option to temporarily " - "downgrade this error to a warning"); - DE.Report(AllowNoteId); + "downgrade this error to a warning", + SourceLocation()); } - if (DumpUnwritableChanges) { + if (_3COpts.DumpUnwritableChanges) { errs() << "=== Beginning of new version of " << FE->getName() << " ===\n"; Buffer->second.write(errs()); @@ -241,68 +253,63 @@ static void emit(Rewriter &R, ASTContext &C) { std::string FeAbsS = ""; std::error_code EC = tryGetCanonicalFilePath(ToConv, FeAbsS); if (EC) { - unsigned ErrorId = DE.getCustomDiagID( - UnwritableChangeDiagnosticLevel, + reportCustomDiagnostic( + DE, UnwritableChangeDiagnosticLevel, "3C internal error: not writing the new version of this file due " - "to failure to re-canonicalize the file path provided by Clang"); - DE.Report(SM.translateFileLineCol(FE, 1, 1), ErrorId); - { - // Put this in a block because Clang only allows one DiagnosticBuilder - // to exist at a time and the call to PrintExtraUnwritableChangeInfo - // below may create more DiagnosticBuilders. - unsigned NoteId = - DE.getCustomDiagID(DiagnosticsEngine::Note, - "file path from Clang was %0; error was: %1"); - auto ErrorBuilder = DE.Report(NoteId); - ErrorBuilder.AddString(ToConv); - ErrorBuilder.AddString(EC.message()); - } + "to failure to re-canonicalize the file path provided by Clang", + BeginningOfFileSourceLoc); + reportCustomDiagnostic( + DE, DiagnosticsEngine::Note, + "file path from Clang was %0; error was: %1", + SourceLocation()) + << ToConv << EC.message(); PrintExtraUnwritableChangeInfo(); continue; } if (FeAbsS != ToConv) { - unsigned ErrorId = DE.getCustomDiagID( - UnwritableChangeDiagnosticLevel, + reportCustomDiagnostic( + DE, UnwritableChangeDiagnosticLevel, "3C internal error: not writing the new version of this file " - "because the file path provided by Clang was not canonical"); - DE.Report(SM.translateFileLineCol(FE, 1, 1), ErrorId); - { - // Ditto re the block. - unsigned NoteId = DE.getCustomDiagID( - DiagnosticsEngine::Note, "file path from Clang was %0; " - "re-canonicalized file path is %1"); - auto ErrorBuilder = DE.Report(NoteId); - ErrorBuilder.AddString(ToConv); - ErrorBuilder.AddString(FeAbsS); - } + "because the file path provided by Clang was not canonical", + BeginningOfFileSourceLoc); + reportCustomDiagnostic( + DE, DiagnosticsEngine::Note, + "file path from Clang was %0; re-canonicalized file path is %1", + SourceLocation()) + << ToConv << FeAbsS; PrintExtraUnwritableChangeInfo(); continue; } if (!canWrite(FeAbsS)) { - unsigned ID = - DE.getCustomDiagID(UnwritableChangeDiagnosticLevel, + reportCustomDiagnostic(DE, UnwritableChangeDiagnosticLevel, "3C internal error: 3C generated changes to " "this file even though it is not allowed to " "write to the file " "(https://github.com/correctcomputation/" - "checkedc-clang/issues/387)"); - DE.Report(SM.translateFileLineCol(FE, 1, 1), ID); + "checkedc-clang/issues/387)", + BeginningOfFileSourceLoc); PrintExtraUnwritableChangeInfo(); continue; } if (StdoutMode) { if (Buffer->first == SM.getMainFileID()) { - // This is the new version of the main file. Print it to stdout. - Buffer->second.write(outs()); - StdoutModeSawMainFile = true; + // This is the new version of the main file. Print it to stdout, + // except in the edge case where we have a compilation database with + // multiple translation units with the same main file and we already + // emitted a copy of the main file for a previous translation unit + // (https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-893612654). + if (!StdoutModeEmittedMainFile) { + Buffer->second.write(outs()); + StdoutModeEmittedMainFile = true; + } } else { - unsigned ID = DE.getCustomDiagID( - UnwritableChangeDiagnosticLevel, + reportCustomDiagnostic( + DE, UnwritableChangeDiagnosticLevel, "3C generated changes to this file, which is under the base dir " "but is not the main file and thus cannot be written in stdout " - "mode"); - DE.Report(SM.translateFileLineCol(FE, 1, 1), ID); + "mode", + BeginningOfFileSourceLoc); PrintExtraUnwritableChangeInfo(); } continue; @@ -314,7 +321,7 @@ static void emit(Rewriter &R, ASTContext &C) { // because stdout mode is handled above. OutputPostfix defaults to "-" // when it's not provided, so any other value means that we should use // OutputPostfix. Otherwise, we must be in OutputDir mode. - if (OutputPostfix != "-") { + if (_3COpts.OutputPostfix != "-") { // That path should be the same as the old one, with a // suffix added between the file name and the extension. // For example \foo\bar\a.c should become \foo\bar\a.checked.c @@ -324,27 +331,28 @@ static void emit(Rewriter &R, ASTContext &C) { std::string FileName = sys::path::remove_leading_dotslash(PfName).str(); std::string Ext = sys::path::extension(FileName).str(); std::string Stem = sys::path::stem(FileName).str(); - NFile = Stem + "." + OutputPostfix + Ext; + NFile = Stem + "." + _3COpts.OutputPostfix + Ext; if (!DirName.empty()) NFile = DirName + sys::path::get_separator().str() + NFile; } else { - assert(!OutputDir.empty()); + assert(!_3COpts.OutputDir.empty()); // If this does not hold when OutputDir is set, it should have been a // fatal error in the _3CInterface constructor. - assert(filePathStartsWith(FeAbsS, BaseDir)); + assert(filePathStartsWith(FeAbsS, _3COpts.BaseDir)); // replace_path_prefix is not smart about separators, but this should be // OK because tryGetCanonicalFilePath should ensure that neither BaseDir // nor OutputDir has a trailing separator. SmallString<255> Tmp(FeAbsS); - llvm::sys::path::replace_path_prefix(Tmp, BaseDir, OutputDir); + llvm::sys::path::replace_path_prefix(Tmp, _3COpts.BaseDir, + _3COpts.OutputDir); NFile = std::string(Tmp.str()); EC = llvm::sys::fs::create_directories(sys::path::parent_path(NFile)); if (EC) { - unsigned ID = DE.getCustomDiagID( - DiagnosticsEngine::Error, - "failed to create parent directory of output file \"%0\""); - auto DiagBuilder = DE.Report(SM.translateFileLineCol(FE, 1, 1), ID); - DiagBuilder.AddString(NFile); + reportCustomDiagnostic( + DE, DiagnosticsEngine::Error, + "failed to create parent directory of output file \"%0\"", + BeginningOfFileSourceLoc) + << NFile; continue; } } @@ -352,14 +360,14 @@ static void emit(Rewriter &R, ASTContext &C) { raw_fd_ostream Out(NFile, EC, sys::fs::F_None); if (!EC) { - if (Verbose) + if (_3COpts.Verbose) errs() << "writing out " << NFile << "\n"; Buffer->second.write(Out); } else { - unsigned ID = DE.getCustomDiagID(DiagnosticsEngine::Error, - "failed to write output file \"%0\""); - auto DiagBuilder = DE.Report(SM.translateFileLineCol(FE, 1, 1), ID); - DiagBuilder.AddString(NFile); + reportCustomDiagnostic(DE, DiagnosticsEngine::Error, + "failed to write output file \"%0\"", + BeginningOfFileSourceLoc) + << NFile; // This is awkward. What to do? Since we're iterating, we could have // created other files successfully. Do we go back and erase them? Is // that surprising? For now, let's just keep going. @@ -367,9 +375,10 @@ static void emit(Rewriter &R, ASTContext &C) { } } - if (StdoutMode && !StdoutModeSawMainFile) { + if (StdoutMode && !StdoutModeEmittedMainFile) { // The main file is unchanged. Write out its original content. outs() << SM.getBufferOrFake(SM.getMainFileID()).getBuffer(); + StdoutModeEmittedMainFile = true; } } @@ -422,7 +431,8 @@ class TypeExprRewriter : public clang::RecursiveASTVisitor { // Replace the original type with this new one if the type has changed. if (CV->anyChanges(Vars)) { rewriteSourceRange(Writer, Range, - CV->mkString(Info.getConstraints(), false)); + CV->mkString(Info.getConstraints(), + MKSTRING_OPTS(EmitName = false))); PState.incrementNumFixedCasts(); } } @@ -437,22 +447,31 @@ class TypeArgumentAdder : public clang::RecursiveASTVisitor { : Context(C), Info(I), Writer(R) {} bool VisitCallExpr(CallExpr *CE) { - if (isa_and_nonnull(CE->getCalleeDecl())) { + if (auto *FD = dyn_cast_or_null(CE->getCalleeDecl())) { // If the function call already has type arguments, we'll trust that // they're correct and not add anything else. if (typeArgsProvided(CE)) return true; + // If the function is not generic, we have nothing to do. + // This could happen even if it has type param binding if we + // reset generics because of wildness + if (Info.getFuncConstraint(FD,Context)->getGenericParams() == 0 && + !FD->isItypeGenericFunction()) + return true; + if (Info.hasTypeParamBindings(CE, Context)) { // Construct a string containing concatenation of all type arguments for // the function call. std::string TypeParamString; bool AllInconsistent = true; for (auto Entry : Info.getTypeParamBindings(CE, Context)) - if (Entry.second != nullptr) { + if (Entry.second.isConsistent()) { AllInconsistent = false; - std::string TyStr = Entry.second->mkString(Info.getConstraints(), - false, false, true); + std::string TyStr = Entry.second.getConstraint( + Info.getConstraints().getVariables() + )->mkString(Info.getConstraints(), MKSTRING_OPTS( + EmitName = false, EmitPointee = true)); if (TyStr.back() == ' ') TyStr.pop_back(); TypeParamString += TyStr + ","; @@ -490,8 +509,14 @@ class TypeArgumentAdder : public clang::RecursiveASTVisitor { } }; +SourceRange DeclReplacement::getSourceRange(SourceManager &SM) const { + return getDeclSourceRangeWithAnnotations(getDecl(), + /*IncludeInitializer=*/false); +} + SourceRange FunctionDeclReplacement::getSourceRange(SourceManager &SM) const { - SourceLocation Begin = RewriteReturn ? getDeclBegin(SM) : getParamBegin(SM); + SourceLocation Begin = RewriteGeneric ? getDeclBegin(SM) : + (RewriteReturn ? getReturnBegin(SM) : getParamBegin(SM)); SourceLocation End = RewriteParams ? getDeclEnd(SM) : getReturnEnd(SM); // Begin can be equal to End if the SourceRange only contains one token. assert("Invalid FunctionDeclReplacement SourceRange!" && @@ -504,6 +529,22 @@ SourceLocation FunctionDeclReplacement::getDeclBegin(SourceManager &SM) const { return Begin; } +SourceLocation FunctionDeclReplacement::getReturnBegin(SourceManager &SM) const { + // TODO: more accuracy + // This code gets the point after a modifier like "static" + // But currently, that leads to multiple "static"s + // SourceRange ReturnSource = Decl->getReturnTypeSourceRange(); + // if (ReturnSource.isValid()) + // return ReturnSource.getBegin(); + + // Invalid return means we're in a macro or typedef, so just get the + // starting point. We may overwrite a _For_any(..), but those only + // exist in partially converted code, so we're relying on the user + // to have it correct anyway. + + return getDeclBegin(SM); +} + SourceLocation FunctionDeclReplacement::getParamBegin(SourceManager &SM) const { FunctionTypeLoc FTypeLoc = getFunctionTypeLoc(Decl); // If we can't get a FunctionTypeLoc instance, then we'll guess that the @@ -548,24 +589,12 @@ SourceLocation FunctionDeclReplacement::getDeclEnd(SourceManager &SM) const { } } - // If there's a bounds expression, this comes after the right paren of the - // function declaration parameter list. - if (auto *BoundsE = Decl->getBoundsExpr()) { - SourceLocation BoundsEnd = BoundsE->getEndLoc(); - if (BoundsEnd.isValid() && - (!End.isValid() || SM.isBeforeInTranslationUnit(End, BoundsEnd))) - End = BoundsEnd; - } - - // If there's an itype, this also comes after the right paren. In the case - // that there is both a bounds expression and an itype, we need check - // which is later in the file and use that as the declaration end. - if (auto *InteropE = Decl->getInteropTypeExpr()) { - SourceLocation InteropEnd = InteropE->getEndLoc(); - if (InteropEnd.isValid() && - (!End.isValid() || SM.isBeforeInTranslationUnit(End, InteropEnd))) - End = InteropEnd; - } + // If there's a bounds or interop type expression, this will come after the + // right paren of the function declaration parameter list. + SourceLocation AnnotationsEnd = getCheckedCAnnotationsEnd(Decl); + if (AnnotationsEnd.isValid() && + (!End.isValid() || SM.isBeforeInTranslationUnit(End, AnnotationsEnd))) + End = AnnotationsEnd; // SourceLocations are weird and turn up invalid for reasons I don't // understand. Fallback to extracting r paren location from source @@ -577,7 +606,8 @@ SourceLocation FunctionDeclReplacement::getDeclEnd(SourceManager &SM) const { } std::string ArrayBoundsRewriter::getBoundsString(const PVConstraint *PV, - Decl *D, bool Isitype) { + Decl *D, bool Isitype, + bool OmitLowerBound) { auto &ABInfo = Info.getABoundsInfo(); // Try to find a bounds key for the constraint variable. If we can't, @@ -595,9 +625,13 @@ std::string ArrayBoundsRewriter::getBoundsString(const PVConstraint *PV, if (ValidBKey && !PV->hasSomeSizedArr()) { ABounds *ArrB = ABInfo.getBounds(DK); - // Only we we have bounds and no pointer arithmetic on the variable. - if (ArrB != nullptr && !ABInfo.hasPointerArithmetic(DK)) { - BString = ArrB->mkString(&ABInfo, D); + // If we have pointer arithmetic and cannot add range bounds, then do not + // emit any bounds string. + if (ArrB != nullptr && ABInfo.hasLowerBound(DK)) { + if (OmitLowerBound) + BString = ArrB->mkStringWithoutLowerBound(&ABInfo, D); + else + BString = ArrB->mkString(&ABInfo, D, DK); if (!BString.empty()) BString = Pfix + BString; } @@ -624,8 +658,7 @@ void RewriteConsumer::emitRootCauseDiagnostics(ASTContext &Context) { SourceManager &SM = Context.getSourceManager(); for (auto &WReason : I.RootWildAtomsWithReason) { // Avoid emitting the same diagnostic message twice. - WildPointerInferenceInfo PtrInfo = WReason.second; - PersistentSourceLoc PSL = PtrInfo.getLocation(); + const PersistentSourceLoc& PSL = WReason.second.getLocation(); if (PSL.valid() && EmittedDiagnostics.find(PSL) == EmittedDiagnostics.end()) { @@ -636,18 +669,32 @@ void RewriteConsumer::emitRootCauseDiagnostics(ASTContext &Context) { if (!File.getError()) { SourceLocation SL = SM.translateFileLineCol(*File, PSL.getLineNo(), PSL.getColSNo()); - // Limit emitted root causes to those that effect more than one pointer - // or are in the main file of the TU. Alternatively, don't filter causes - // if -warn-all-root-cause is passed. + // Limit emitted root causes to those that effect at least one pointer. + // Alternatively, don't filter causes if -warn-all-root-cause is passed. int PtrCount = I.getNumPtrsAffected(WReason.first); - if (WarnAllRootCause || SM.isInMainFile(SL) || PtrCount > 1) { + if (_3COpts.WarnAllRootCause || PtrCount > 0) { // SL is invalid when the File is not in the current translation unit. if (SL.isValid()) { EmittedDiagnostics.insert(PSL); - auto DiagBuilder = DE.Report(SL, ID); - DiagBuilder.AddTaggedVal(PtrCount, - DiagnosticsEngine::ArgumentKind::ak_uint); - DiagBuilder.AddString(WReason.second.getWildPtrReason()); + DE.Report(SL, ID) << PtrCount << WReason.second.getReason(); + } + // if notes have sources in other files, these files may not + // be in the same TU and will not be displayed. At the initial + // time of this comment, that was expected to be very rare. + // see https://github.com/correctcomputation/checkedc-clang/pull/708#discussion_r716903129 + // for a discussion + for (auto &Note : WReason.second.additionalNotes()) { + PersistentSourceLoc NPSL = Note.Location; + llvm::ErrorOr NFile = + SM.getFileManager().getFile(NPSL.getFileName()); + if (!NFile.getError()) { + SourceLocation NSL = SM.translateFileLineCol( + *NFile, NPSL.getLineNo(), NPSL.getColSNo()); + if (NSL.isValid()) { + unsigned NID = DE.getCustomDiagID(DiagnosticsEngine::Note, "%0"); + DE.Report(NSL, NID) << Note.Reason; + } + } } } } @@ -660,7 +707,7 @@ void RewriteConsumer::HandleTranslationUnit(ASTContext &Context) { Info.getPerfStats().startRewritingTime(); - if (WarnRootCause) + if (_3COpts.WarnRootCause) emitRootCauseDiagnostics(Context); // Rewrite Variable declarations @@ -670,15 +717,17 @@ void RewriteConsumer::HandleTranslationUnit(ASTContext &Context) { // Take care of some other rewriting tasks std::set Seen; std::map NodeMap; - CheckedRegionFinder CRF(&Context, R, Info, Seen, NodeMap, WarnRootCause); + CheckedRegionFinder CRF(&Context, R, Info, Seen, NodeMap, + _3COpts.WarnRootCause); CheckedRegionAdder CRA(&Context, R, NodeMap, Info); CastLocatorVisitor CLV(&Context); CastPlacementVisitor ECPV(&Context, Info, R, CLV.getExprsWithCast()); TypeExprRewriter TER(&Context, Info, R); TypeArgumentAdder TPA(&Context, Info, R); + LowerBoundAssignmentUpdater AU(&Context, Info, R); TranslationUnitDecl *TUD = Context.getTranslationUnitDecl(); for (const auto &D : TUD->decls()) { - if (AddCheckedRegions) { + if (_3COpts.AddCheckedRegions) { // Adding checked regions enabled? // TODO: Should checked region finding happen somewhere else? This is // supposed to be rewriting. @@ -694,10 +743,11 @@ void RewriteConsumer::HandleTranslationUnit(ASTContext &Context) { CLV.TraverseDecl(D); ECPV.TraverseDecl(D); TPA.TraverseDecl(D); + AU.TraverseDecl(D); } // Output files. - emit(R, Context); + emit(R, Context, StdoutModeEmittedMainFile); Info.getPerfStats().endRewritingTime(); diff --git a/clang/lib/3C/StructInit.cpp b/clang/lib/3C/StructInit.cpp index 38cce593844e..cdc7690a5b6c 100644 --- a/clang/lib/3C/StructInit.cpp +++ b/clang/lib/3C/StructInit.cpp @@ -11,6 +11,7 @@ #include "clang/3C/StructInit.h" #include "clang/3C/MappingVisitor.h" +#include "clang/3C/RewriteUtils.h" #include "clang/Tooling/Transformer/SourceCode.h" #include @@ -53,30 +54,18 @@ bool StructVariableInitializer::hasCheckedMembers(DeclaratorDecl *DD) { // Insert the declaration and correct replacement text for the declaration into // the set of required rewritings. -void StructVariableInitializer::insertVarDecl(VarDecl *VD, DeclStmt *S) { +bool StructVariableInitializer::VisitVarDecl(VarDecl *VD) { // Check if we need to add an initializer. - bool IsVarExtern = VD->getStorageClass() == StorageClass::SC_Extern; - if (!IsVarExtern && !VD->hasInit() && hasCheckedMembers(VD)) { - // Create replacement declaration text with an initializer. - const clang::Type *Ty = VD->getType().getTypePtr(); - std::string TQ = VD->getType().getQualifiers().getAsString(); - if (!TQ.empty()) - TQ += " "; - std::string ToReplace = getStorageQualifierString(VD) + TQ + tyToStr(Ty) + - " " + VD->getName().str() + " = {}"; - RewriteThese.insert(new VarDeclReplacement(VD, S, ToReplace)); - } -} + // TODO: Centralize initialization logic for all types: + // https://github.com/correctcomputation/checkedc-clang/issues/645#issuecomment-876474200 -// Check to see if this variable require an initialization. -bool StructVariableInitializer::VisitDeclStmt(DeclStmt *S) { - if (S->isSingleDecl()) { - if (VarDecl *VD = dyn_cast(S->getSingleDecl())) - insertVarDecl(VD, S); - } else { - for (const auto &D : S->decls()) - if (VarDecl *VD = dyn_cast(D)) - insertVarDecl(VD, S); + // The first two conditions are the same as in Sema::ActOnUninitializedDecl. + if (VD->hasLocalStorage() && !isa(VD) && !VD->hasInit() && + hasCheckedMembers(VD)) { + // Create replacement declaration text with an initializer. + std::string ToReplace = mkStringForDeclWithUnchangedType(VD, I) + " = {}"; + RewriteThese.insert( + std::make_pair(VD, new MultiDeclMemberReplacement(VD, ToReplace, {}))); } return true; } diff --git a/clang/lib/3C/TypeVariableAnalysis.cpp b/clang/lib/3C/TypeVariableAnalysis.cpp index cd01625f526c..365dca6eecb4 100644 --- a/clang/lib/3C/TypeVariableAnalysis.cpp +++ b/clang/lib/3C/TypeVariableAnalysis.cpp @@ -31,7 +31,8 @@ void TypeVariableEntry::setTypeParamConsVar(ConstraintVariable *CV) { TypeParamConsVar = CV; } -void TypeVariableEntry::updateEntry(QualType Ty, CVarSet &CVs) { +void TypeVariableEntry::updateEntry(QualType Ty, CVarSet &CVs, + ConstraintVariable *IdentCV) { if (!(Ty->isArrayType() || Ty->isPointerType())) { // We need to have a pointer or an array type for an instantiation to make // sense. Anything else is treated as inconsistent. @@ -45,9 +46,20 @@ void TypeVariableEntry::updateEntry(QualType Ty, CVarSet &CVs) { getType()->getPointeeOrArrayElementType() != PtrTy)) IsConsistent = false; } + // If these came from two different type params originally and are both + // passed to the same type param, we have no way of knowing if they were + // the same and in general they will not always be, so this must be marked + // inconsistent. + if (auto PVC1 = dyn_cast_or_null(GenArgumentCV)) + if (auto PVC2 = dyn_cast_or_null(IdentCV)) + if (PVC1->getGenericIndex() != PVC2->getGenericIndex()) + IsConsistent = false; + // Record new constraints for the entry. These are used even when the variable // is not consistent. insertConstraintVariables(CVs); + if (!GenArgumentCV) + GenArgumentCV = IdentCV; } ConstraintVariable *TypeVariableEntry::getTypeParamConsVar() { @@ -57,6 +69,12 @@ ConstraintVariable *TypeVariableEntry::getTypeParamConsVar() { return TypeParamConsVar; } +ConstraintVariable *TypeVariableEntry::getGenArgCV() { + assert("Accessing constraint variable for inconsistent Type Variable." && + IsConsistent); + return GenArgumentCV; +} + QualType TypeVariableEntry::getType() { assert("Accessing Type for inconsistent Type Variable." && IsConsistent); return TyVarType; @@ -72,7 +90,7 @@ bool TypeVarVisitor::VisitCastExpr(CastExpr *CE) { if (CHKCBindTemporaryExpr *TempE = dyn_cast(SubExpr)) SubExpr = TempE->getSubExpr(); - if (auto *Call = dyn_cast(SubExpr)) + if (auto *Call = dyn_cast(SubExpr)) { if (auto *FD = dyn_cast_or_null(Call->getCalleeDecl())) { FunctionDecl *FDef = getDefinition(FD); if (FDef == nullptr) @@ -87,6 +105,7 @@ bool TypeVarVisitor::VisitCastExpr(CastExpr *CE) { } } } + } return true; } @@ -96,11 +115,6 @@ bool TypeVarVisitor::VisitCallExpr(CallExpr *CE) { if (FDef == nullptr) FDef = FD; if (auto *FVCon = Info.getFuncConstraint(FDef, Context)) { - // if we need to rewrite it but can't (macro, etc), it isn't safe - bool ForcedInconsistent = - !typeArgsProvided(CE) && - (!Rewriter::isRewritable(CE->getExprLoc()) || - !canWrite(PersistentSourceLoc::mkPSL(CE, *Context).getFileName())); // Visit each function argument, and if it use a type variable, insert it // into the type variable binding map. unsigned int I = 0; @@ -111,9 +125,20 @@ bool TypeVarVisitor::VisitCallExpr(CallExpr *CE) { const int TyIdx = FVCon->getExternalParam(I)->getGenericIndex(); if (TyIdx >= 0) { Expr *Uncast = A->IgnoreImpCasts(); - std::set CVs = - CR.getExprConstraintVarsSet(Uncast); - insertBinding(CE, TyIdx, Uncast->getType(), CVs, ForcedInconsistent); + std::set CVs = CR.getExprConstraintVarsSet(Uncast); + if (auto *DRE = dyn_cast(Uncast)){ + CVarOption Var = Info.getVariable(DRE->getFoundDecl(),Context); + if (Var.hasValue()) + if (PVConstraint *GenVar = + dyn_cast(&Var.getValue())) + if (GenVar->isGeneric()) { + insertBinding(CE,TyIdx,Uncast->getType(), + CVs,GenVar); + ++I; + continue; + } + } + insertBinding(CE, TyIdx, Uncast->getType(), CVs); } ++I; } @@ -127,16 +152,33 @@ bool TypeVarVisitor::VisitCallExpr(CallExpr *CE) { FD->getNameAsString() + "_tyarg_" + std::to_string(TVEntry.first); PVConstraint *P = new PVConstraint(TVEntry.second.getType(), nullptr, Name, Info, - *Context, nullptr, TVEntry.first); + *Context, nullptr); // Constrain this variable GEQ the function arguments using the type // variable so if any of them are wild, the type argument will also be - // an unchecked pointer. - constrainConsVarGeq(P, TVEntry.second.getConstraintVariables(), - Info.getConstraints(), nullptr, Safe_to_Wild, false, + // an unchecked pointer. Except for realloc, which has special casing + // elsewhere, especially `ConstraintResolver::getExprConstraintVars` + // using variable `ReallocFlow`. Because `realloc` can take a wild + // pointer and return a safe one. + auto PSL = PersistentSourceLoc::mkPSL(CE,*Context); + auto Rsn = ReasonLoc("Type variable", PSL); + if (FD->getNameAsString() == "realloc") { + constrainConsVarGeq(P, TVEntry.second.getConstraintVariables(), + Info.getConstraints(), Rsn, Wild_to_Safe, false, + &Info); + + } else { + constrainConsVarGeq(P, TVEntry.second.getConstraintVariables(), + Info.getConstraints(), Rsn, Safe_to_Wild, false, &Info); + } TVEntry.second.setTypeParamConsVar(P); + // Since we've changed the constraint variable for this context, we + // need to remove the cache from the old one. Our new info will be + // used next request. + if (Info.hasPersistentConstraints(CE,Context)) + Info.removePersistentConstraints(CE,Context); } else { // TODO: This might be too cautious. CR.constraintAllCVarsToWild(TVEntry.second.getConstraintVariables(), @@ -151,17 +193,24 @@ bool TypeVarVisitor::VisitCallExpr(CallExpr *CE) { // used and the index of the type variable type in the function declaration. void TypeVarVisitor::insertBinding(CallExpr *CE, const int TyIdx, clang::QualType Ty, CVarSet &CVs, - bool ForceInconsistent) { + ConstraintVariable *IdentCV) { + // if we need to rewrite it but can't (macro, etc), it isn't safe + bool ForceInconsistent = + !typeArgsProvided(CE) && + (!Rewriter::isRewritable(CE->getExprLoc()) || + !canWrite(PersistentSourceLoc::mkPSL(CE, *Context).getFileName())); + assert(TyIdx >= 0 && "Creating a type variable binding without a type variable."); auto &CallTypeVarMap = TVMap[CE]; if (CallTypeVarMap.find(TyIdx) == CallTypeVarMap.end()) { // If the type variable hasn't been seen before, add it to the map. - TypeVariableEntry TVEntry = TypeVariableEntry(Ty, CVs, ForceInconsistent); + TypeVariableEntry TVEntry = TypeVariableEntry(Ty, CVs, ForceInconsistent, + IdentCV); CallTypeVarMap[TyIdx] = TVEntry; } else { // Otherwise, update entry with new type and constraints. - CallTypeVarMap[TyIdx].updateEntry(Ty, CVs); + CallTypeVarMap[TyIdx].updateEntry(Ty, CVs, IdentCV); } } @@ -187,9 +236,11 @@ void TypeVarVisitor::setProgramInfoTypeVars() { if (TVCallEntry.second.getIsConsistent()) Info.setTypeParamBinding(TVEntry.first, TVCallEntry.first, TVCallEntry.second.getTypeParamConsVar(), + TVCallEntry.second.getGenArgCV(), Context); else - Info.setTypeParamBinding(TVEntry.first, TVCallEntry.first, nullptr, + Info.setTypeParamBinding(TVEntry.first, TVCallEntry.first, + nullptr, nullptr, Context); } } diff --git a/clang/lib/3C/Utils.cpp b/clang/lib/3C/Utils.cpp index 4eb7a65776ab..bb3b2507f822 100644 --- a/clang/lib/3C/Utils.cpp +++ b/clang/lib/3C/Utils.cpp @@ -15,6 +15,7 @@ #include "clang/Sema/Sema.h" #include "llvm/Support/Path.h" #include +#include using namespace llvm; using namespace clang; @@ -105,24 +106,6 @@ clang::CheckedPointerKind getCheckedPointerKind(InteropTypeExpr *ItypeExpr) { return CheckedPointerKind::Unchecked; } -// Check if function body exists for the -// provided declaration. -bool hasFunctionBody(clang::Decl *D) { - // If this a parameter? - if (ParmVarDecl *PD = dyn_cast(D)) { - if (DeclContext *DC = PD->getParentFunctionOrMethod()) { - FunctionDecl *FD = dyn_cast(DC); - if (getDefinition(FD) != nullptr) { - return true; - } - } - return false; - } - // Else this should be within body and - // the function body should exist. - return true; -} - static std::string storageClassToString(StorageClass SC) { switch (SC) { case StorageClass::SC_Static: @@ -138,7 +121,8 @@ static std::string storageClassToString(StorageClass SC) { } // This method gets the storage qualifier for the -// provided declaration i.e., static, extern, etc. +// provided declaration including any trailing space, i.e., "static ", +// "extern ", etc., or "" if none. std::string getStorageQualifierString(Decl *D) { if (FunctionDecl *FD = dyn_cast(D)) { return storageClassToString(FD->getStorageClass()); @@ -146,6 +130,12 @@ std::string getStorageQualifierString(Decl *D) { if (VarDecl *VD = dyn_cast(D)) { return storageClassToString(VD->getStorageClass()); } + if (isa(D)) { + // `typedef` goes in the same syntactic position as a storage qualifier and + // needs to be inserted when breaking up a multi-decl, just like a real + // storage qualifier. + return "typedef "; + } return ""; } @@ -200,8 +190,9 @@ bool functionHasVarArgs(clang::FunctionDecl *FD) { } bool isFunctionAllocator(std::string FuncName) { - return std::find(AllocatorFunctions.begin(), AllocatorFunctions.end(), - FuncName) != AllocatorFunctions.end() || + return std::find(_3COpts.AllocatorFunctions.begin(), + _3COpts.AllocatorFunctions.end(), + FuncName) != _3COpts.AllocatorFunctions.end() || llvm::StringSwitch(FuncName) .Cases("malloc", "calloc", "realloc", true) .Default(false); @@ -219,6 +210,20 @@ bool isPtrOrArrayType(const clang::QualType &QT) { return QT->isPointerType() || QT->isArrayType(); } +bool isArrayType(const QualType &QT) { + // This is an array type. Just return true. + if (QT->isArrayType()) + return true; + + // It might instead be an array which has decayed to a pointer. We still want + // to treat this as an array. + const DecayedType *T = QT->getAs(); + if (T && T->getOriginalType()->isArrayType()) + return true; + + return false; +} + bool isNullableType(const clang::QualType &QT) { if (QT.getTypePtrOrNull()) return QT->isPointerType() || QT->isArrayType() || QT->isIntegerType(); @@ -226,9 +231,12 @@ bool isNullableType(const clang::QualType &QT) { } bool canBeNtArray(const clang::QualType &QT) { - if (const auto &Ptr = dyn_cast(QT)) + // First get the canonical type so that the following checks will not have to + // account for ParenType, DecayedType, or other variants used by clang. + QualType Canon = QT.getCanonicalType(); + if (const auto &Ptr = dyn_cast(Canon)) return isNullableType(Ptr->getPointeeType()); - if (const auto &Arr = dyn_cast(QT)) + if (const auto &Arr = dyn_cast(Canon)) return isNullableType(Arr->getElementType()); return false; } @@ -322,8 +330,7 @@ bool hasVoidType(clang::ValueDecl *D) { return isTypeHasVoid(D->getType()); } // return D->isPointerType() == S->isPointerType(); //} -static bool castCheck(clang::QualType DstType, clang::QualType SrcType, - bool AllowVoidCast) { +static bool castCheck(clang::QualType DstType, clang::QualType SrcType) { // Check if both types are same. if (SrcType == DstType) @@ -339,9 +346,8 @@ static bool castCheck(clang::QualType DstType, clang::QualType SrcType, // Both are pointers? check their pointee if (SrcPtrTypePtr && DstPtrTypePtr) { - return (AllowVoidCast && SrcPtrTypePtr->isVoidPointerType()) || - castCheck(DstPtrTypePtr->getPointeeType(), - SrcPtrTypePtr->getPointeeType(), AllowVoidCast); + return castCheck(DstPtrTypePtr->getPointeeType(), + SrcPtrTypePtr->getPointeeType()); } if (SrcPtrTypePtr || DstPtrTypePtr) @@ -355,12 +361,11 @@ static bool castCheck(clang::QualType DstType, clang::QualType SrcType, return false; for (unsigned I = 0; I < SrcFnType->getNumParams(); I++) - if (!castCheck(SrcFnType->getParamType(I), DstFnType->getParamType(I), - false)) + if (!castCheck(SrcFnType->getParamType(I), + DstFnType->getParamType(I))) return false; - return castCheck(SrcFnType->getReturnType(), DstFnType->getReturnType(), - false); + return castCheck(SrcFnType->getReturnType(), DstFnType->getReturnType()); } // If both are not scalar types? Then the types must be exactly same. @@ -384,7 +389,7 @@ bool isCastSafe(clang::QualType DstType, clang::QualType SrcType) { dyn_cast(DstTypePtr); if (!DstPtrTypePtr) // Safe to cast to a non-pointer. return true; - return castCheck(DstType, SrcType, true); + return castCheck(DstType, SrcType); } bool canWrite(const std::string &FilePath) { @@ -393,7 +398,7 @@ bool canWrite(const std::string &FilePath) { return true; // Get the absolute path of the file and check that // the file path starts with the base directory. - return filePathStartsWith(FilePath, BaseDir); + return filePathStartsWith(FilePath, _3COpts.BaseDir); } bool isInSysHeader(clang::Decl *D) { @@ -407,11 +412,15 @@ bool isInSysHeader(clang::Decl *D) { std::string getSourceText(const clang::SourceRange &SR, const clang::ASTContext &C) { + return getSourceText(CharSourceRange::getTokenRange(SR), C); +} + +std::string getSourceText(const clang::CharSourceRange &SR, + const clang::ASTContext &C) { assert(SR.isValid() && "Invalid Source Range requested."); auto &SM = C.getSourceManager(); auto LO = C.getLangOpts(); - llvm::StringRef Srctxt = - Lexer::getSourceText(CharSourceRange::getTokenRange(SR), SM, LO); + llvm::StringRef Srctxt = Lexer::getSourceText(SR, SM, LO); return Srctxt.str(); } @@ -586,3 +595,72 @@ int64_t getStmtIdWorkaround(const Stmt *St, const ASTContext &Context) { // (alignof(Stmt) - 1) before dividing. return Context.getAllocator().identifyKnownObject(St); } + +// Get the SourceLocation for the end of any Checked C bounds or interop type +// annotations on a declaration. Returns an invalid source location if no +// Checked C annotations are present. +SourceLocation getCheckedCAnnotationsEnd(const Decl *D) { + SourceManager &SM = D->getASTContext().getSourceManager(); + SourceLocation End; + + // Update the current end SourceLocation to the new SourceLocation if the new + // location is valid and comes after the current end location. + auto UpdateEnd = [&SM, &End](SourceLocation SL) { + if (SL.isValid() && + (!End.isValid() || SM.isBeforeInTranslationUnit(End, SL))) + End = SL; + }; + + if (auto *DD = dyn_cast(D)) { + if (auto *InteropE = DD->getInteropTypeExpr()) + UpdateEnd(InteropE->getEndLoc()); + if (auto *BoundsE = DD->getBoundsExpr()) + UpdateEnd(BoundsE->getEndLoc()); + } + + return End; +} + +SourceLocation getLocationAfterToken(SourceLocation SL, const SourceManager &SM, + const LangOptions &LO) { + return Lexer::getLocForEndOfToken(SL, 0, SM, LO); +} + +SourceRange getDeclSourceRangeWithAnnotations(const clang::Decl *D, + bool IncludeInitializer) { + SourceManager &SM = D->getASTContext().getSourceManager(); + SourceRange SR; + const VarDecl *VD; + // Only a VarDecl can have an initializer. VarDecl's implementation of the + // getSourceRange virtual method includes the initializer, but we can manually + // call DeclaratorDecl's implementation, which excludes the initializer. + if (!IncludeInitializer && (VD = dyn_cast(D)) != nullptr) + SR = VD->DeclaratorDecl::getSourceRange(); + else + SR = D->getSourceRange(); + if (!SR.isValid()) + return SR; + SourceLocation DeclEnd = SR.getEnd(); + + // Partial workaround for a compiler bug where if D has certain checked + // pointer types such as `_Ptr` (seen in the partial_checked.c + // regression test), D->getSourceRange() returns only the _Ptr token. (As of + // this writing on 2021-11-18, no bug report has been filed against the + // compiler, but https://github.com/correctcomputation/checkedc-clang/pull/723 + // tracks our work on the bug.) + // + // Always extend the range at least through the name (given by + // D->getLocation()). That fixes the `_Ptr x` case but not cases + // with additional syntax after the name, such as `_Ptr x[10]`. + SourceLocation DeclLoc = D->getLocation(); + if (SM.isBeforeInTranslationUnit(DeclEnd, DeclLoc)) + DeclEnd = DeclLoc; + + SourceLocation AnnotationsEnd = getCheckedCAnnotationsEnd(D); + if (AnnotationsEnd.isValid() && + SM.isBeforeInTranslationUnit(DeclEnd, AnnotationsEnd)) + DeclEnd = AnnotationsEnd; + + SR.setEnd(DeclEnd); + return SR; +} diff --git a/clang/test/3C/arrboundsheuristicsitype.c b/clang/test/3C/arrboundsheuristicsitype.c new file mode 100644 index 000000000000..27e450d6d3a2 --- /dev/null +++ b/clang/test/3C/arrboundsheuristicsitype.c @@ -0,0 +1,35 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK,CHECK_NOALL" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK,CHECK_ALL" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/arrboundsheuristicsitype.c -- | diff %t.checked/arrboundsheuristicsitype.c - + +// Verify that array bounds heuristics are correctly applied to parameters with +// itypes. + +void foo(void *); + +void test0(int *a, int len) { +//CHECK_ALL: void test0(int *a : itype(_Array_ptr) count(len), int len) { +//CHECK_NOALL: void test0(int *a : itype(_Ptr), int len) { + foo(a); + (void) a[len - 1]; +} + +void test1(int *a : itype(_Array_ptr), int len) { +//CHECK_ALL: void test1(int *a : itype(_Array_ptr) count(len), int len) { +//CHECK_NOALL: void test1(int *a : itype(_Ptr), int len) { + foo(a); + (void) a[len - 1]; +} + +#include + +void test2(int *a, int len) { +//CHECK_ALL: void test2(int *a : itype(_Array_ptr) count(len), int len) { +//CHECK_NOALL: void test2(int *a : itype(_Ptr), int len) { + a = malloc(sizeof(int)*len); + foo(a); + (void) a[len - 1]; +} diff --git a/clang/test/3C/ast_dump.c b/clang/test/3C/ast_dump.c new file mode 100644 index 000000000000..3e5ae92d57c3 --- /dev/null +++ b/clang/test/3C/ast_dump.c @@ -0,0 +1,11 @@ +// RUN: rm -rf %t* + +// Turn off color in the AST dump so that we can more easily match it +// (https://stackoverflow.com/questions/32447542/how-do-i-get-clang-to-dump-the-ast-without-color). +// RUN: 3c -base-dir=%S %s -- -Xclang -ast-dump -fno-color-diagnostics | FileCheck -match-full-lines %s + +// RUN: 3c -base-dir=%S %s -- -Xclang -ast-list 2>%t.stderr +// RUN: grep 'The requested ProgramAction is not implemented by 3C' %t.stderr + +int *p; +// CHECK: `-VarDecl {{.*}} p 'int *' diff --git a/clang/test/3C/b_tests/b1_allsafe.c b/clang/test/3C/b_tests/b1_allsafe.c index 19f1af154241..9b2786683aed 100644 --- a/clang/test/3C/b_tests/b1_allsafe.c +++ b/clang/test/3C/b_tests/b1_allsafe.c @@ -31,7 +31,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -47,6 +47,6 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: _Ptr z = (sus(x, y)); - //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b23_explicitunsafecast.c b/clang/test/3C/b_tests/b23_explicitunsafecast.c index 8c5627e6160a..e406686ec11a 100644 --- a/clang/test/3C/b_tests/b23_explicitunsafecast.c +++ b/clang/test/3C/b_tests/b23_explicitunsafecast.c @@ -30,7 +30,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = (int *)sus(x, y); //CHECK_NOALL: _Ptr z = (_Ptr)sus(x, y); - //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -45,6 +45,6 @@ char *bar() { //CHECK: _Ptr y = &sy; char *z = (char *)(sus(x, y)); //CHECK_NOALL: char *z = (char *)(((int *)sus(x, y))); - //CHECK_ALL: char *z = (char *)(((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y))); + //CHECK_ALL: char *z = (char *)(((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y))); return z; } diff --git a/clang/test/3C/b_tests/b23_retswitchexplicit.c b/clang/test/3C/b_tests/b23_retswitchexplicit.c index dc1c305020ee..41573e1d60bc 100644 --- a/clang/test/3C/b_tests/b23_retswitchexplicit.c +++ b/clang/test/3C/b_tests/b23_retswitchexplicit.c @@ -30,7 +30,7 @@ char *foo() { //CHECK: _Ptr y = &sy; char *z = (int *)sus(x, y); //CHECK_NOALL: char *z = (int *)sus(x, y); - //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -45,6 +45,6 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (char *)(sus(x, y)); //CHECK_NOALL: int *z = (char *)(sus(x, y)); - //CHECK_ALL: int *z = (char *)(sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: int *z = (char *)(sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b24_implicitunsafecast.c b/clang/test/3C/b_tests/b24_implicitunsafecast.c index 95f1c228ef39..5306503b62ca 100644 --- a/clang/test/3C/b_tests/b24_implicitunsafecast.c +++ b/clang/test/3C/b_tests/b24_implicitunsafecast.c @@ -30,7 +30,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = (int *)sus(x, y); //CHECK_NOALL: _Ptr z = (_Ptr)sus(x, y); - //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -45,6 +45,6 @@ char *bar() { //CHECK: _Ptr y = &sy; char *z = sus(x, y); //CHECK_NOALL: char *z = ((int *)sus(x, y)); - //CHECK_ALL: char *z = ((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: char *z = ((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b24_retswitchimplicit.c b/clang/test/3C/b_tests/b24_retswitchimplicit.c index 97422ff7038a..efd2b99a964e 100644 --- a/clang/test/3C/b_tests/b24_retswitchimplicit.c +++ b/clang/test/3C/b_tests/b24_retswitchimplicit.c @@ -30,7 +30,7 @@ char *foo() { //CHECK: _Ptr y = &sy; char *z = (int *)sus(x, y); //CHECK_NOALL: char *z = (int *)sus(x, y); - //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -45,6 +45,6 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: int *z = ((char *)sus(x, y)); - //CHECK_ALL: int *z = ((char *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: int *z = ((char *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b25_castprotosafe.c b/clang/test/3C/b_tests/b25_castprotosafe.c index e3df2fc8d5f1..30836e7a982a 100644 --- a/clang/test/3C/b_tests/b25_castprotosafe.c +++ b/clang/test/3C/b_tests/b25_castprotosafe.c @@ -23,7 +23,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = (int *)sus(x, y); //CHECK_NOALL: _Ptr z = (_Ptr)sus(x, y); - //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -38,7 +38,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (int *)(sus(x, y)); //CHECK_NOALL: _Ptr z = (_Ptr)(sus(x, y)); - //CHECK_ALL: _Ptr z = (_Ptr)(sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Ptr z = (_Ptr)(sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b26_castprotounsafe.c b/clang/test/3C/b_tests/b26_castprotounsafe.c index 17d2c22fc3da..121075d90065 100644 --- a/clang/test/3C/b_tests/b26_castprotounsafe.c +++ b/clang/test/3C/b_tests/b26_castprotounsafe.c @@ -23,7 +23,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = (int *)sus(x, y); //CHECK_NOALL: _Ptr z = (_Ptr)sus(x, y); - //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -38,7 +38,7 @@ char *bar() { //CHECK: _Ptr y = &sy; char *z = (char *)(sus(x, y)); //CHECK_NOALL: char *z = (char *)(((int *)sus(x, y))); - //CHECK_ALL: char *z = (char *)(((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y))); + //CHECK_ALL: char *z = (char *)(((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y))); return z; } diff --git a/clang/test/3C/b_tests/b26_castprotounsafeimplicit.c b/clang/test/3C/b_tests/b26_castprotounsafeimplicit.c index b5138fff1d1f..c31ef1a89c40 100644 --- a/clang/test/3C/b_tests/b26_castprotounsafeimplicit.c +++ b/clang/test/3C/b_tests/b26_castprotounsafeimplicit.c @@ -23,7 +23,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = (int *)sus(x, y); //CHECK_NOALL: _Ptr z = (_Ptr)sus(x, y); - //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = (_Ptr)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -38,7 +38,7 @@ char *bar() { //CHECK: _Ptr y = &sy; char *z = (sus(x, y)); //CHECK_NOALL: char *z = (((int *)sus(x, y))); - //CHECK_ALL: char *z = (((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y))); + //CHECK_ALL: char *z = (((int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y))); return z; } diff --git a/clang/test/3C/b_tests/b26_castprotounsafeimplicitretswitch.c b/clang/test/3C/b_tests/b26_castprotounsafeimplicitretswitch.c index fe050ca58a77..562c1f79526a 100644 --- a/clang/test/3C/b_tests/b26_castprotounsafeimplicitretswitch.c +++ b/clang/test/3C/b_tests/b26_castprotounsafeimplicitretswitch.c @@ -23,7 +23,7 @@ char *foo() { //CHECK: _Ptr y = &sy; char *z = (int *)sus(x, y); //CHECK_NOALL: char *z = (int *)sus(x, y); - //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: char *z = (int *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -38,7 +38,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: int *z = (((char *)sus(x, y))); - //CHECK_ALL: int *z = (((char *)sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y))); + //CHECK_ALL: int *z = (((char *)sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y))); return z; } diff --git a/clang/test/3C/b_tests/b2_calleeunsafe.c b/clang/test/3C/b_tests/b2_calleeunsafe.c index 4ca1297289b7..c1df90e06978 100644 --- a/clang/test/3C/b_tests/b2_calleeunsafe.c +++ b/clang/test/3C/b_tests/b2_calleeunsafe.c @@ -32,7 +32,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -48,6 +48,6 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: _Ptr z = (sus(x, y)); - //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b3_onecallerunsafe.c b/clang/test/3C/b_tests/b3_onecallerunsafe.c index a4f88688c401..34697e0439ab 100644 --- a/clang/test/3C/b_tests/b3_onecallerunsafe.c +++ b/clang/test/3C/b_tests/b3_onecallerunsafe.c @@ -33,7 +33,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -50,7 +50,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: int *z = ((int *)sus(x, y)); - //CHECK_ALL: _Array_ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Array_ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); z += 2; *z = -17; return z; diff --git a/clang/test/3C/b_tests/b4_bothunsafe.c b/clang/test/3C/b_tests/b4_bothunsafe.c index 3f6bf0ad71c1..c90de3045d92 100644 --- a/clang/test/3C/b_tests/b4_bothunsafe.c +++ b/clang/test/3C/b_tests/b4_bothunsafe.c @@ -32,7 +32,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -48,7 +48,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Array_ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); z += 2; *z = -17; return z; diff --git a/clang/test/3C/b_tests/b5_calleeunsafeproto.c b/clang/test/3C/b_tests/b5_calleeunsafeproto.c index 0f27240a86ca..d7949a890323 100644 --- a/clang/test/3C/b_tests/b5_calleeunsafeproto.c +++ b/clang/test/3C/b_tests/b5_calleeunsafeproto.c @@ -24,7 +24,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -40,7 +40,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: _Ptr z = (sus(x, y)); - //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/b_tests/b6_callerunsafeproto.c b/clang/test/3C/b_tests/b6_callerunsafeproto.c index abf1ad882a0d..8e0ada35d2c5 100644 --- a/clang/test/3C/b_tests/b6_callerunsafeproto.c +++ b/clang/test/3C/b_tests/b6_callerunsafeproto.c @@ -24,7 +24,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -40,7 +40,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: int *z = (((int *)sus(x, y))); - //CHECK_ALL: _Array_ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Array_ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); z += 2; return z; } diff --git a/clang/test/3C/b_tests/b7_allsafeproto.c b/clang/test/3C/b_tests/b7_allsafeproto.c index 4db8908bb82a..ac31308eb3af 100644 --- a/clang/test/3C/b_tests/b7_allsafeproto.c +++ b/clang/test/3C/b_tests/b7_allsafeproto.c @@ -24,7 +24,7 @@ int *foo() { //CHECK: _Ptr y = &sy; int *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); + //CHECK_ALL: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); *z = *z + 1; return z; } @@ -40,7 +40,7 @@ int *bar() { //CHECK: _Ptr y = &sy; int *z = (sus(x, y)); //CHECK_NOALL: _Ptr z = (sus(x, y)); - //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y)); + //CHECK_ALL: _Ptr z = (sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y)); return z; } diff --git a/clang/test/3C/base_subdir/unwritable_bounds.c b/clang/test/3C/base_subdir/unwritable_bounds.c new file mode 100644 index 000000000000..7636ea1006f5 --- /dev/null +++ b/clang/test/3C/base_subdir/unwritable_bounds.c @@ -0,0 +1,74 @@ +// RUN: rm -rf %t* +// RUN: cd %S +// RUN: 3c -alltypes -addcr -output-dir=%t.checked/base_subdir %s -- + +// The functions called from this file would normally have bounds inferred if +// they were declared in a writable file. The file is not writable, so the new +// bounds must not be inferred. This possibility of this happening existed +// before 3C started inferring itypes on undefined functions, but it became a +// significant issue and was noticed with the introduction of this feature. + +#include "../unwritable_bounds.h" +void test_functions() { + { + char s[0]; + unwrite0(s); + } + { + char s[0]; + unwrite1(s); + } + { + char s[0]; + unwrite2(s); + } + { + char s[0]; + unwrite3(s); + } + { + char s[0]; + unwrite4(s); + } + { + char s[0]; + unwrite5(s); + } + { + char s[0]; + unwrite6(s); + } +} + +void test_struct() { + // There is a code path for variable declarations and a different path for + // uses of a variable. The initializer is required to test the declaration + // path. + struct arr_struct a = {}; + for (int i = 0; i < a.n; i++) + a.arr[i]; + + // I don't quite understand why this was a problem, but it caused an + // assertion to fail after apply the fix for the first struct test + // case. + union e { + float d + }; + int i = struct_ret()->c; + union e f; + f.d; +} + +void test_glob() { + for (int i = 0; i < 10; i++) + glob0[i]; + + for (int i = 0; i < 10; i++) + glob1[i]; + + for (int i = 0; i < 10; i++) + glob2[i]; + + for (int i = 0; i < 10; i++) + glob3[i]; +} diff --git a/clang/test/3C/base_subdir/unwritable_rootcauses.c b/clang/test/3C/base_subdir/unwritable_rootcauses.c new file mode 100644 index 000000000000..1d0bb33d23be --- /dev/null +++ b/clang/test/3C/base_subdir/unwritable_rootcauses.c @@ -0,0 +1,6 @@ +// RUN: cd %S +// RUN: 3c -use-malloc=my_malloc -alltypes -addcr -output-dir=%t.checked/base_subdir -warn-all-root-cause %s -- -Xclang -verify=unwritable-expected -Wno-everything +// RUN: 3c -use-malloc=my_malloc -alltypes -addcr -output-dir=%t.checked/base_subdir -warn-root-cause %s -- -Xclang -verify=not-all-unwritable-expected -Wno-everything +// not-all-unwritable-expected-no-diagnostics + +#include "../root_cause.c" diff --git a/clang/test/3C/basic.c b/clang/test/3C/basic.c index 7e82b61a8008..01e821b717d2 100644 --- a/clang/test/3C/basic.c +++ b/clang/test/3C/basic.c @@ -131,7 +131,7 @@ void basic_realloc(int count) { free(ptr); } //CHECK_NOALL: int *ptr = (int *)malloc(n1 * sizeof(int)); -//CHECK_ALL: _Array_ptr ptr : count(n1) = (_Array_ptr)malloc(n1 * sizeof(int)); +//CHECK_ALL: _Array_ptr ptr = (_Array_ptr)malloc(n1 * sizeof(int)); struct student { char name[30]; diff --git a/clang/test/3C/basic_checks.c b/clang/test/3C/basic_checks.c index 5ba1b44d9f80..7fcec48335ed 100644 --- a/clang/test/3C/basic_checks.c +++ b/clang/test/3C/basic_checks.c @@ -33,6 +33,7 @@ typedef struct _A { int a; int b; } A, *PA; +//CHECK: typedef _Ptr PA; void mut_pa(PA p) { p->a = 0; diff --git a/clang/test/3C/canonical_type_cast.c b/clang/test/3C/canonical_type_cast.c index c14c3d27b906..83fea53a5809 100644 --- a/clang/test/3C/canonical_type_cast.c +++ b/clang/test/3C/canonical_type_cast.c @@ -14,11 +14,9 @@ void f(int *p) { } void g(int p[]) { - //CHECK_NOALL: void g(int p[]) { - //CHECK_ALL: void g(_Ptr p) { + //CHECK: void g(_Ptr p) { int *x = (int *)p; - //CHECK_NOALL: int *x = (int *)p; - //CHECK_ALL: _Ptr x = (_Ptr)p; + //CHECK: _Ptr x = (_Ptr)p; } /* A very similar issue with function pointers */ diff --git a/clang/test/3C/cant_be_nt.c b/clang/test/3C/cant_be_nt.c index 250ed71ef5be..adf450d2d2c1 100644 --- a/clang/test/3C/cant_be_nt.c +++ b/clang/test/3C/cant_be_nt.c @@ -46,3 +46,27 @@ float* dar(struct foobar f) { //CHECK: _Ptr dar(struct foobar f) { return f.ptr; } + +// Test a bug I found in canBeNtArray triggered by some specific types. +// The P >= ARR constraint was incorrectly added to these variables, causing +// them to solve to WILD instead of NTARR. + +void force_nt_arr(char *s : itype(_Nt_array_ptr)); + +void decayed_type(char s[10]) { +//CHECK_NOALL: void decayed_type(char s[10]) { +//CHECK_ALL: void decayed_type(char s _Nt_checked[10]) { + force_nt_arr(s); +} + +void paren_type(char *(s)) { +//CHECK_NOALL: void paren_type(char *(s) : itype(_Ptr)) { +//CHECK_ALL: void paren_type(_Nt_array_ptr s) { + force_nt_arr(s); +} + +void decayed_paren_type(char (s)[10]) { +//CHECK_NOALL: void decayed_paren_type(char (s)[10]) { +//CHECK_ALL: void decayed_paren_type(char s _Nt_checked[10]) { + force_nt_arr(s); +} diff --git a/clang/test/3C/canwrite_constraints.h b/clang/test/3C/canwrite_constraints.h index cbcbd303c3dc..0d5758a41f61 100644 --- a/clang/test/3C/canwrite_constraints.h +++ b/clang/test/3C/canwrite_constraints.h @@ -6,11 +6,11 @@ // "@+1" means "on the next line". If we put the comment on the same line, it // breaks the CHECK_HIGHER. -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} inline void foo(int *p) {} // CHECK_HIGHER: inline void foo(_Ptr p) _Checked {} -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} int *foo_var = ((void *)0); // CHECK_HIGHER: _Ptr foo_var = ((void *)0); @@ -20,11 +20,11 @@ int *foo_var = ((void *)0); // Now that itypes can be re-solved, an itype in a non-writable file generates a // root cause warning just like a fully unchecked type. -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} void strtol_like(int *p : itype(_Ptr)); void atol_like() { - // expected-warning@+1 {{Expression in non-writable file}} + // expected-warning@+1 {{Source code in non-writable file}} strtol_like((int *)0); } @@ -36,7 +36,7 @@ inline void no_op() {} // In the lower case, this should stay wild // In the higher case, this should solve to checked -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} typedef int *intptr; // CHECK_HIGHER: typedef _Ptr intptr; @@ -46,12 +46,12 @@ typedef int *intptr; // (https://github.com/correctcomputation/checkedc-clang/issues/423) as an // example. -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} void unwritable_cast(void((*g)(int *q)) : itype(_Ptr)>)) { - // expected-warning@+1 {{Declaration in non-writable file}} int *p = 0; // Now 3C thinks it needs to insert _Assume_bounds_cast<_Ptr> around `p` // because it forgets that it is allowed to use the original type of `g`. + // expected-warning@+2 {{Source code in non-writable file}} // expected-warning@+1 {{3C internal error: tried to insert a cast into an unwritable file}} (*g)(p); } @@ -59,23 +59,29 @@ void unwritable_cast(void((*g)(int *q)) : itype(_Ptr)>)) { // Make sure that FVComponentVariable::equateWithItype prevents both of these // from being changed. -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} void unwritable_func_with_itype(int *p : itype(_Array_ptr)) {} -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} void unwritable_func_with_itype_and_bounds(int *p : itype(_Array_ptr) count(12)) { } // Test for https://github.com/correctcomputation/checkedc-clang/issues/580 -// expected-warning@+1 {{Declaration in non-writable file}} +// expected-warning@+1 {{Source code in non-writable file}} _Itype_for_any(T) void my_generic_function(void *p : itype(_Ptr)); +// expected-warning@+1 {{Source code in non-writable file}} +_Itype_for_any(T) void *my_generic_return(void) : itype(_Ptr); + void unwritable_type_argument() { int i; + int *b; // expected-warning {{Source code in non-writable file}} // This warning relates to the atom representing the temporary pointer of // `&i`. https://github.com/correctcomputation/checkedc-clang/issues/618 would // make 3C smarter to avoid the need to constrain the temporary pointer. - // expected-warning@+1 {{Expression in non-writable file}} + // expected-warning@+1 {{Source code in non-writable file}} my_generic_function(&i); + + b = my_generic_return(); // expected-warning {{Source code in non-writable file}} } diff --git a/clang/test/3C/cast.c b/clang/test/3C/cast.c index 301dad320d08..3b1c1de94572 100644 --- a/clang/test/3C/cast.c +++ b/clang/test/3C/cast.c @@ -22,3 +22,57 @@ void bar(void) { //CHECK: int *d = (int *)5; /*int *e = (int *)(a+5);*/ } + +// malloc and generics are exceptions to our policy of unsafe void* casts +#include +_Itype_for_any(T) +void *ident(void *i : itype(_Ptr)) +//CHECK: void *ident(void *i : itype(_Ptr)) +: itype(_Ptr) { return i;}; +//CHECK: : itype(_Ptr) { return i;}; +void test_generic_casts(void) { + int *ptr_cast = (int *)malloc(3 * sizeof(int)); + //CHECK_NOALL: int *ptr_cast = (int *)malloc(3 * sizeof(int)); + //CHECK_ALL: _Ptr ptr_cast = (_Ptr)malloc(3 * sizeof(int)); + int *ptr_nocast = malloc(3 * sizeof(int)); + //CHECK_NOALL: int *ptr_nocast = malloc(3 * sizeof(int)); + //CHECK_ALL: _Ptr ptr_nocast = malloc(3 * sizeof(int)); + char *ptr_badcast = (int *)malloc(3 * sizeof(int)); + //CHECK: char *ptr_badcast = (int *)malloc(3 * sizeof(int)); + int *ptr_custom = (int *)ident(ptr_cast); + //CHECK_NOALL: int *ptr_custom = (int *)ident(ptr_cast); + //CHECK_ALL: _Ptr ptr_custom = (_Ptr)ident(ptr_cast); + + int *ptr_cast_c = (int *)calloc(3, sizeof(int)); + //CHECK_NOALL: int *ptr_cast_c = (int *)calloc(3, sizeof(int)); + //CHECK_ALL: _Ptr ptr_cast_c = (_Ptr)calloc(3, sizeof(int)); + int *ptr_cast_r = (int *)realloc(ptr_cast_c, 3 * sizeof(int)); + //CHECK: _Ptr ptr_cast_r = (_Ptr)realloc(ptr_cast_c, 3 * sizeof(int)); +} + +// Check that casts to generics use the local type variables +int *mk_ptr(void); +//CHECK: int *mk_ptr(void); +_For_any(T) void free_ptr(_Ptr p_ptr) {} +//CHECK: _For_any(T) void free_ptr(_Ptr p_ptr) _Checked {} +void work (void) { + int *node = mk_ptr(); // wild because extern unavailable + free_ptr(node); + //CHECK: free_ptr(_Assume_bounds_cast<_Ptr>(node)); +} + +// cast from wild to checked needs to cast to the new destination type +_Itype_for_any(T) void has_itype(void *a : itype(_Array_ptr) byte_count(l), unsigned int l); +void infers_generic(void *a, unsigned int l) { + //CHECK_ALL: _Itype_for_any(T) void infers_generic(_Array_ptr a : byte_count(l), unsigned int l) { + has_itype(a, l); +} +void test_cast_to_inferred() { + int *p = 1; + int l; + + has_itype(p, l); + //CHECK: has_itype(p, l); + infers_generic(p, l); + //CHECK_ALL: infers_generic(_Assume_bounds_cast<_Array_ptr>(p, bounds(unknown)), l); +} diff --git a/clang/test/3C/compound_literal.c b/clang/test/3C/compound_literal.c index b749aeb3ad7d..d656369b3abe 100644 --- a/clang/test/3C/compound_literal.c +++ b/clang/test/3C/compound_literal.c @@ -67,7 +67,7 @@ void lists() { int *d[2] = (int *[2]){&x, (int *)1}; //CHECK_NOALL: int *d[2] = (int *[2]){&x, (int *)1}; - //CHECK_ALL: int * d _Checked[2] = (int * _Checked[2]){&x, (int *)1}; + //CHECK_ALL: int *d _Checked[2] = (int * _Checked[2]){&x, (int *)1}; int *d0 = d[0]; //CHECK: int *d0 = d[0]; diff --git a/clang/test/3C/definedType.c b/clang/test/3C/definedType.c index 0593f0849e47..344657841754 100644 --- a/clang/test/3C/definedType.c +++ b/clang/test/3C/definedType.c @@ -57,6 +57,8 @@ baz **f[1]; // CHECK_ALL: _Ptr<_Ptr> f _Checked[1] = {((void *)0)}; baz (*g)[1]; // CHECK_ALL: _Ptr g = ((void *)0); +baz *(*g2)[1]; +// CHECK_ALL: _Ptr<_Ptr _Checked[1]> g2 = ((void *)0); baz h[1][1]; // CHECK_ALL: baz h _Checked[1] _Checked[1]; diff --git a/clang/test/3C/dont_add_prototype.c b/clang/test/3C/dont_add_prototype.c new file mode 100644 index 000000000000..3007c57b2ad2 --- /dev/null +++ b/clang/test/3C/dont_add_prototype.c @@ -0,0 +1,34 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/dont_add_prototype.c -- | diff %t.checked/dont_add_prototype.c - + +// Don't add a void prototype on functions if we don't need to. This can +// potentially cause rewriting errors when attempting to rewrite functions +// defined outside the basedir or defined by a macro. + +// This function does not need to be rewritten. Its return type is unchecked +// after solving. It should not be rewritten to use a void prototype in order +// to avoid any potential rewritting issues. +void *test0() { return 1; } +//CHECK: void *test0() { return 1; } + +// Trying to add a prototype in these examples caused a rewriting error +// because the functions are defined by macros. + +#define test_macro0 int *test_macro0() +test_macro0 { +//CHECK: test_macro0 { + return 0; +} + +#define test_macro1 test_macro1() +int *test_macro1 { +//CHECK: int *test_macro1 { + return 0; +} + +// Force conversion output. +int *a; diff --git a/clang/test/3C/dont_rewrite_cflags.c b/clang/test/3C/dont_rewrite_cflags.c new file mode 100644 index 000000000000..7992230f7f55 --- /dev/null +++ b/clang/test/3C/dont_rewrite_cflags.c @@ -0,0 +1,10 @@ +// This tests a case of unwritable files being written under specific flags +// to the compiler, as seen in the run command below. It was originally noticed +// converting a header file to use generic variables. The solution is to check +// again that your change is in a writable file, as seen here: +// https://github.com/correctcomputation/checkedc-clang/commit/b6d8ed1dcfc9d4174dc87b1b34a5ff6def3f2d5c + +// RUN: 3c -base-dir=%S %s -- -O2 -D_FORTIFY_SOURCE=2 + +#include + diff --git a/clang/test/3C/extstructfields.c b/clang/test/3C/extstructfields.c index b342924fbdae..e4c11476b199 100644 --- a/clang/test/3C/extstructfields.c +++ b/clang/test/3C/extstructfields.c @@ -13,7 +13,7 @@ #include void vsf_sysutil_set_sighandler(int sig, void (*p_handlefunc)(int)) -//CHECK: void vsf_sysutil_set_sighandler(int sig, void (*p_handlefunc)(int)) +//CHECK: void vsf_sysutil_set_sighandler(int sig, void ((*p_handlefunc)(int)) : itype(_Ptr)) { int retval; struct sigaction sigact; diff --git a/clang/test/3C/fptr_array.c b/clang/test/3C/fptr_array.c index 2e6c22907303..8d12acc69153 100644 --- a/clang/test/3C/fptr_array.c +++ b/clang/test/3C/fptr_array.c @@ -9,7 +9,7 @@ void (*fs[2])(int *); void (*f)(int *); -//CHECK_NOALL: void (* fs[2])(_Ptr) = {((void *)0)}; +//CHECK_NOALL: void (*fs[2])(_Ptr) = {((void *)0)}; //CHECK_NOALL: void (*f)(_Ptr) = ((void *)0); //CHECK_ALL: _Ptr)> fs _Checked[2] = {((void *)0)}; //CHECK_ALL: _Ptr)> f = ((void *)0); @@ -17,7 +17,7 @@ void (*f)(int *); void (*gs[2])(int *); void g_impl(int *x) { x = 1; } void (*g)(int *) = g_impl; -//CHECK_NOALL: void (* gs[2])(int * : itype(_Ptr)) = {((void *)0)}; +//CHECK_NOALL: void (*gs[2])(int * : itype(_Ptr)) = {((void *)0)}; //CHECK_NOALL: void g_impl(int *x : itype(_Ptr)) { x = 1; } //CHECK_NOALL: void (*g)(int * : itype(_Ptr)) = g_impl; @@ -30,21 +30,21 @@ void (*h)(void *); int *(*is[2])(void); int *(*i)(void); -//CHECK_NOALL: _Ptr (* is[2])(void) = {((void *)0)}; +//CHECK_NOALL: _Ptr (*is[2])(void) = {((void *)0)}; //CHECK_NOALL: _Ptr (*i)(void) = ((void *)0); //CHECK_ALL: _Ptr<_Ptr (void)> is _Checked[2] = {((void *)0)}; //CHECK_ALL: _Ptr<_Ptr (void)> i = ((void *)0); int *(**js[2])(void); int *(**j)(void); -//CHECK_NOALL: _Ptr (** js[2])(void) = {((void *)0)}; +//CHECK_NOALL: _Ptr (**js[2])(void) = {((void *)0)}; //CHECK_NOALL: _Ptr (**j)(void) = ((void *)0); //CHECK_ALL: _Ptr<_Ptr<_Ptr (void)>> js _Checked[2] = {((void *)0)}; //CHECK_ALL: _Ptr<_Ptr<_Ptr (void)>> j = ((void *)0); int *(*ks[2][2])(void); int *(*k)(void); -//CHECK_NOALL: _Ptr (* ks[2][2])(void) = {((void *)0)}; +//CHECK_NOALL: _Ptr (*ks[2][2])(void) = {((void *)0)}; //CHECK_NOALL: _Ptr (*k)(void) = ((void *)0); //CHECK_ALL: _Ptr<_Ptr (void)> ks _Checked[2] _Checked[2] = {((void *)0)}; //CHECK_ALL: _Ptr<_Ptr (void)> k = ((void *)0); @@ -53,6 +53,9 @@ void (* const l)(int *); //CHECK_NOALL: void (*const l)(_Ptr) = ((void *)0); //CHECK_ALL: const _Ptr)> l = ((void *)0); +#define UNWRITABLE_MS_DECL void (*ms[2])(int *) +UNWRITABLE_MS_DECL; + void test(void){ fs[1] = f; gs[1] = g; @@ -63,4 +66,11 @@ void test(void){ void (* const ls[1])(int *) = {l}; //CHECK_NOALL: void (*const ls[1])(_Ptr) = {l}; //CHECK_ALL: const _Ptr)> ls _Checked[1] = {l}; + + void (*(*pms)[2])(int *) = &ms; + //CHECK: _Ptr pms = &ms; + + void (*(*apms[1])[2])(int *) = {&ms}; + //CHECK_NOALL: void (*(*apms[1])[2])(int *) = {&ms}; + //CHECK_ALL: _Ptr apms _Checked[1] = {&ms}; } diff --git a/clang/test/3C/generalize.c b/clang/test/3C/generalize.c new file mode 100644 index 000000000000..2f48e88769ed --- /dev/null +++ b/clang/test/3C/generalize.c @@ -0,0 +1,159 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/generalize.c -- | diff %t.checked/generalize.c - + +// Test the code that adds generics to replace void* + +// Basic functionality +void viewer(void *i) { return; } +// CHECK: _For_any(T) void viewer(_Ptr i) { return; } +void viewer_badnum(void *i, int *j) { +// CHECK: _Itype_for_any(T) void viewer_badnum(_Ptr i, int *j : itype(_Ptr)) { + j = (int*)3; + return; +} +void viewer_update(void *i) { +// CHECK: void viewer_update(void *i) { + i = 2; + return; +} +void *getNull() { return 0; } +// CHECK: _For_any(T) _Ptr getNull(void) { return 0; } +void *getOne() { return 1; } +// CHECK: void *getOne() { return 1; } +extern void *glob = 0; +void *getGlobal() { return glob; } +// CHECK: void *getGlobal() { return glob; } + +// check type parameters +void call_from_fn() { + // CHECK: void call_from_fn() _Checked { + int *i; + viewer(i); + // CHECK: viewer(i); +} +void call_from_gen_fn(void *i) { + // CHECK: _For_any(T) void call_from_gen_fn(_Ptr i) { + viewer(i); + // CHECK: viewer(i); +} + +// nameless decls use a different code path for rewriting, +// second param forces rewrite +// check to see that we don't rewrite unsafe vals. ie T* +void nameless(void *, char *); +void nameless(void *a, char *b) +{ + a = 1; // make it unsafe +} +// CHECK: void nameless(void *a, _Ptr b); +// CHECK: void nameless(void *a, _Ptr b) + +// Safe functions should be upgraded from "_Itype_for_any" to "_For_any" +_Itype_for_any(T) void has_safe_params(_Ptr i, int *t : itype(_Ptr)) {} +// CHECK: _For_any(T) void has_safe_params(_Ptr i, _Ptr t) _Checked {} + +// itypes are not converted to generics +int recv0(void *buf : itype(_Array_ptr) byte_count(n), int n) {} +// CHECK: int recv0(void *buf : itype(_Array_ptr) byte_count(n), int n) {} + +// greater depth pointers are not converted to generics +void double_ptr(void** dp) {} +// CHECK: void double_ptr(void** dp) {} + +// externs are not converted to generics +void elsewhere(void *x, int *y); +// CHECK: void elsewhere(void *x, int *y); + +// existing generics are not rewritten +_For_any(T) void forany(T* t, _Ptr p, void * v) {} +// CHECK: _For_any(T) void forany(_Ptr t, _Ptr p, void * v) {} + +// functions with multiple potential generics are not rewritten +void twoparams(void *a, void *b) {} +void* inout(void *in) {} +// CHECK: void twoparams(void *a, void *b) {} +// CHECK: void* inout(void *in) {} + +// safe pointers are not converted to generics +void safevoid(_Ptr s){} +// CHECK: void safevoid(_Ptr s){} + + + +// Code reduced from parsons +_Itype_for_any(T) void sys_free(void *free_ptr : itype(_Ptr)); +void extern_fp((*free_fun)(void*)); +static void wrap_free(void *wrap_ptr) { + sys_free(wrap_ptr); + // CHECK: sys_free(wrap_ptr); +} +void make_wild(void) { + extern_fp(wrap_free); +} + +// Code from vsftpd +#include +#include +extern void bug(char*); +extern void die(char*); + +// generics can be added, but not internally, so the result is unchecked +// and we don't add generics for unchecked potential params +void* vsf_sysutil_malloc(unsigned int size) +// CHECK: void* vsf_sysutil_malloc(unsigned int size) +{ + void* p_ret; + /* Paranoia - what if we got an integer overflow/underflow? */ + if (size == 0 || size > INT_MAX) + { + bug("zero or big size in vsf_sysutil_malloc"); + } + p_ret = malloc(size); + if (p_ret == NULL) + { + die("malloc"); + } + return p_ret; +} + +// current generics strategy only converts protos with 1 `void*` +void* vsf_sysutil_realloc(void* p_ptr, unsigned int size) +// CHECK: void* vsf_sysutil_realloc(void* p_ptr, unsigned int size) +{ + void* p_ret; + if (size == 0 || size > INT_MAX) + { + bug("zero or big size in vsf_sysutil_realloc"); + } + p_ret = realloc(p_ptr, size); + if (p_ret == NULL) + { + die("realloc"); + } + return p_ret; +} + +void +vsf_sysutil_free(void* p_ptr) +// CHECK: _For_any(T) void vsf_sysutil_free(_Ptr p_ptr) +{ + if (p_ptr == NULL) + { + bug("vsf_sysutil_free got a null pointer"); + } + free(p_ptr); +} + +void run_vsf_sysutil (void) { + typedef struct {char a; char b;} char_node; + char_node *node1; + // These currently return unsafe values, which hits a bug in 3c: + // https://github.com/correctcomputation/checkedc-clang/issues/622 + //node1 = vsf_sysutil_malloc(sizeof(*node1)); + //node1 = vsf_sysutil_realloc(node1, sizeof(*node1)); + vsf_sysutil_free(node1); + // CHECK: vsf_sysutil_free(node1); +} diff --git a/clang/test/3C/generated_tests/arrboth.c b/clang/test/3C/generated_tests/arrboth.c index 5ce4d8119577..b975e44dc238 100644 --- a/clang/test/3C/generated_tests/arrboth.c +++ b/clang/test/3C/generated_tests/arrboth.c @@ -107,11 +107,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrbothmulti2.c b/clang/test/3C/generated_tests/arrbothmulti2.c index 7e1f3f5bf93d..e032c28e377d 100644 --- a/clang/test/3C/generated_tests/arrbothmulti2.c +++ b/clang/test/3C/generated_tests/arrbothmulti2.c @@ -114,11 +114,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrcallee.c b/clang/test/3C/generated_tests/arrcallee.c index 4ff1ac043a6b..4f27cea0e703 100644 --- a/clang/test/3C/generated_tests/arrcallee.c +++ b/clang/test/3C/generated_tests/arrcallee.c @@ -107,11 +107,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrcalleemulti2.c b/clang/test/3C/generated_tests/arrcalleemulti2.c index 0780cdb00c1c..472effa99082 100644 --- a/clang/test/3C/generated_tests/arrcalleemulti2.c +++ b/clang/test/3C/generated_tests/arrcalleemulti2.c @@ -114,11 +114,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrcaller.c b/clang/test/3C/generated_tests/arrcaller.c index 2adb6c448150..ed1de3fccb9e 100644 --- a/clang/test/3C/generated_tests/arrcaller.c +++ b/clang/test/3C/generated_tests/arrcaller.c @@ -111,7 +111,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { @@ -142,7 +142,8 @@ int *bar() { //CHECK: _Ptr y = malloc(sizeof(int)); int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrcallermulti1.c b/clang/test/3C/generated_tests/arrcallermulti1.c index c2f5bc204629..c2d132324cbf 100644 --- a/clang/test/3C/generated_tests/arrcallermulti1.c +++ b/clang/test/3C/generated_tests/arrcallermulti1.c @@ -133,7 +133,8 @@ int *bar() { //CHECK: _Ptr y = malloc(sizeof(int)); int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrcallermulti2.c b/clang/test/3C/generated_tests/arrcallermulti2.c index 0d5870e7e507..35b3a330a851 100644 --- a/clang/test/3C/generated_tests/arrcallermulti2.c +++ b/clang/test/3C/generated_tests/arrcallermulti2.c @@ -118,7 +118,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrinstructboth.c b/clang/test/3C/generated_tests/arrinstructboth.c index 36d80ae409c1..8a5b6ed221de 100644 --- a/clang/test/3C/generated_tests/arrinstructboth.c +++ b/clang/test/3C/generated_tests/arrinstructboth.c @@ -131,7 +131,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -145,7 +145,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrinstructbothmulti1.c b/clang/test/3C/generated_tests/arrinstructbothmulti1.c index 3c05909763be..2684a417e625 100644 --- a/clang/test/3C/generated_tests/arrinstructbothmulti1.c +++ b/clang/test/3C/generated_tests/arrinstructbothmulti1.c @@ -120,7 +120,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -134,7 +134,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrinstructcallee.c b/clang/test/3C/generated_tests/arrinstructcallee.c index a2dfd8816e62..f91dcdda75bf 100644 --- a/clang/test/3C/generated_tests/arrinstructcallee.c +++ b/clang/test/3C/generated_tests/arrinstructcallee.c @@ -131,7 +131,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -144,6 +144,6 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } diff --git a/clang/test/3C/generated_tests/arrinstructcalleemulti1.c b/clang/test/3C/generated_tests/arrinstructcalleemulti1.c index 6e9e45634928..8f2393e06e98 100644 --- a/clang/test/3C/generated_tests/arrinstructcalleemulti1.c +++ b/clang/test/3C/generated_tests/arrinstructcalleemulti1.c @@ -120,7 +120,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -133,6 +133,6 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } diff --git a/clang/test/3C/generated_tests/arrinstructcaller.c b/clang/test/3C/generated_tests/arrinstructcaller.c index 995989752a08..f5b0e36430c1 100644 --- a/clang/test/3C/generated_tests/arrinstructcaller.c +++ b/clang/test/3C/generated_tests/arrinstructcaller.c @@ -130,7 +130,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -144,7 +144,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = ((struct warr *)sus(x, y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrinstructcallermulti1.c b/clang/test/3C/generated_tests/arrinstructcallermulti1.c index 4b03204ff246..0e047d5320f9 100644 --- a/clang/test/3C/generated_tests/arrinstructcallermulti1.c +++ b/clang/test/3C/generated_tests/arrinstructcallermulti1.c @@ -120,7 +120,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -134,7 +134,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = ((struct warr *)sus(x, y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrinstructprotoboth.c b/clang/test/3C/generated_tests/arrinstructprotoboth.c index a8101eb3516a..3431912cf4ac 100644 --- a/clang/test/3C/generated_tests/arrinstructprotoboth.c +++ b/clang/test/3C/generated_tests/arrinstructprotoboth.c @@ -117,7 +117,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -131,7 +131,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrinstructprotocallee.c b/clang/test/3C/generated_tests/arrinstructprotocallee.c index c40450f1840d..44ba0c399d4a 100644 --- a/clang/test/3C/generated_tests/arrinstructprotocallee.c +++ b/clang/test/3C/generated_tests/arrinstructprotocallee.c @@ -117,7 +117,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -130,7 +130,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } diff --git a/clang/test/3C/generated_tests/arrinstructprotocaller.c b/clang/test/3C/generated_tests/arrinstructprotocaller.c index 02f0c661c8fd..7d1e90653dee 100644 --- a/clang/test/3C/generated_tests/arrinstructprotocaller.c +++ b/clang/test/3C/generated_tests/arrinstructprotocaller.c @@ -117,7 +117,7 @@ struct warr *foo() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: _Ptr z = sus(x, y); - //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); return z; } @@ -131,7 +131,7 @@ struct warr *bar() { //CHECK_ALL: struct warr *y = malloc(sizeof(struct warr)); struct warr *z = sus(x, y); //CHECK_NOALL: struct warr *z = ((struct warr *)sus(x, y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, byte_count(0))); + //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrofstructboth.c b/clang/test/3C/generated_tests/arrofstructboth.c index 6f93aad9db47..eb07991eef34 100644 --- a/clang/test/3C/generated_tests/arrofstructboth.c +++ b/clang/test/3C/generated_tests/arrofstructboth.c @@ -108,7 +108,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructbothmulti2.c b/clang/test/3C/generated_tests/arrofstructbothmulti2.c index a7ea8805af6e..4511b2a23929 100644 --- a/clang/test/3C/generated_tests/arrofstructbothmulti2.c +++ b/clang/test/3C/generated_tests/arrofstructbothmulti2.c @@ -115,7 +115,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructcallee.c b/clang/test/3C/generated_tests/arrofstructcallee.c index 017fdbc4b3c3..bd4862c3adaf 100644 --- a/clang/test/3C/generated_tests/arrofstructcallee.c +++ b/clang/test/3C/generated_tests/arrofstructcallee.c @@ -108,7 +108,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructcalleemulti2.c b/clang/test/3C/generated_tests/arrofstructcalleemulti2.c index 1fae0a2b9a68..5545acd45e64 100644 --- a/clang/test/3C/generated_tests/arrofstructcalleemulti2.c +++ b/clang/test/3C/generated_tests/arrofstructcalleemulti2.c @@ -115,7 +115,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructcaller.c b/clang/test/3C/generated_tests/arrofstructcaller.c index 96fe2390f2bc..d87a8136125d 100644 --- a/clang/test/3C/generated_tests/arrofstructcaller.c +++ b/clang/test/3C/generated_tests/arrofstructcaller.c @@ -167,7 +167,8 @@ struct general **bar() { } struct general **z = sus(x, y); //CHECK_NOALL: struct general **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrofstructcallermulti1.c b/clang/test/3C/generated_tests/arrofstructcallermulti1.c index afb24d071058..84af896ac164 100644 --- a/clang/test/3C/generated_tests/arrofstructcallermulti1.c +++ b/clang/test/3C/generated_tests/arrofstructcallermulti1.c @@ -151,7 +151,8 @@ struct general **bar() { } struct general **z = sus(x, y); //CHECK_NOALL: struct general **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrofstructprotoboth.c b/clang/test/3C/generated_tests/arrofstructprotoboth.c index 922344e4a381..fdf49b838b36 100644 --- a/clang/test/3C/generated_tests/arrofstructprotoboth.c +++ b/clang/test/3C/generated_tests/arrofstructprotoboth.c @@ -165,7 +165,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructprotocallee.c b/clang/test/3C/generated_tests/arrofstructprotocallee.c index be3522fbe01a..b62d0e704bcc 100644 --- a/clang/test/3C/generated_tests/arrofstructprotocallee.c +++ b/clang/test/3C/generated_tests/arrofstructprotocallee.c @@ -164,7 +164,8 @@ struct general **sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; struct general **z = calloc(5, sizeof(struct general *)); //CHECK_NOALL: struct general **z = calloc(5, sizeof(struct general *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(struct general *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *curr = y; //CHECK_NOALL: struct general *curr = y; //CHECK_ALL: _Ptr curr = y; diff --git a/clang/test/3C/generated_tests/arrofstructprotocaller.c b/clang/test/3C/generated_tests/arrofstructprotocaller.c index e8b2f0f01ae4..c21ad70fc0dc 100644 --- a/clang/test/3C/generated_tests/arrofstructprotocaller.c +++ b/clang/test/3C/generated_tests/arrofstructprotocaller.c @@ -153,7 +153,8 @@ struct general **bar() { } struct general **z = sus(x, y); //CHECK_NOALL: struct general **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrprotoboth.c b/clang/test/3C/generated_tests/arrprotoboth.c index 888f692948e4..b3f1cb81bbf5 100644 --- a/clang/test/3C/generated_tests/arrprotoboth.c +++ b/clang/test/3C/generated_tests/arrprotoboth.c @@ -142,11 +142,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrprotocallee.c b/clang/test/3C/generated_tests/arrprotocallee.c index daab972232dc..6e05f211361d 100644 --- a/clang/test/3C/generated_tests/arrprotocallee.c +++ b/clang/test/3C/generated_tests/arrprotocallee.c @@ -141,11 +141,12 @@ int *sus(int *x, int *y) { //CHECK: x = (int *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrprotocaller.c b/clang/test/3C/generated_tests/arrprotocaller.c index 1bb5da125dca..d43c1e75e737 100644 --- a/clang/test/3C/generated_tests/arrprotocaller.c +++ b/clang/test/3C/generated_tests/arrprotocaller.c @@ -130,7 +130,8 @@ int *bar() { //CHECK: _Ptr y = malloc(sizeof(int)); int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } @@ -146,7 +147,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrprotosafe.c b/clang/test/3C/generated_tests/arrprotosafe.c index f23f81b52e6b..9eb627be85bd 100644 --- a/clang/test/3C/generated_tests/arrprotosafe.c +++ b/clang/test/3C/generated_tests/arrprotosafe.c @@ -144,7 +144,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrsafe.c b/clang/test/3C/generated_tests/arrsafe.c index 6f32b13da155..0ebcdf9fc554 100644 --- a/clang/test/3C/generated_tests/arrsafe.c +++ b/clang/test/3C/generated_tests/arrsafe.c @@ -110,7 +110,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrsafemulti2.c b/clang/test/3C/generated_tests/arrsafemulti2.c index ad9b1fbcff7c..c31ade211125 100644 --- a/clang/test/3C/generated_tests/arrsafemulti2.c +++ b/clang/test/3C/generated_tests/arrsafemulti2.c @@ -117,7 +117,7 @@ int *sus(int *x, int *y) { int i, fac; int *p; //CHECK_NOALL: int *p; - //CHECK_ALL: _Array_ptr p = ((void *)0); + //CHECK_ALL: _Array_ptr p : bounds(z, z + 5) = ((void *)0); for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_NOALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) { //CHECK_ALL: for (i = 0, p = z, fac = 1; i < 5; ++i, p++, fac *= i) _Checked { diff --git a/clang/test/3C/generated_tests/arrstructboth.c b/clang/test/3C/generated_tests/arrstructboth.c index 8f8d5712f83e..63831aeb3f1d 100644 --- a/clang/test/3C/generated_tests/arrstructboth.c +++ b/clang/test/3C/generated_tests/arrstructboth.c @@ -107,7 +107,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructbothmulti2.c b/clang/test/3C/generated_tests/arrstructbothmulti2.c index c27f62b9041a..50d5172eedc9 100644 --- a/clang/test/3C/generated_tests/arrstructbothmulti2.c +++ b/clang/test/3C/generated_tests/arrstructbothmulti2.c @@ -114,7 +114,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructcallee.c b/clang/test/3C/generated_tests/arrstructcallee.c index 43dabba34c05..a0c18b168681 100644 --- a/clang/test/3C/generated_tests/arrstructcallee.c +++ b/clang/test/3C/generated_tests/arrstructcallee.c @@ -107,7 +107,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructcalleemulti2.c b/clang/test/3C/generated_tests/arrstructcalleemulti2.c index 7bfa9f6024f3..bc34fdba0958 100644 --- a/clang/test/3C/generated_tests/arrstructcalleemulti2.c +++ b/clang/test/3C/generated_tests/arrstructcalleemulti2.c @@ -114,7 +114,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructcaller.c b/clang/test/3C/generated_tests/arrstructcaller.c index d96b34a027ac..812d95ceb782 100644 --- a/clang/test/3C/generated_tests/arrstructcaller.c +++ b/clang/test/3C/generated_tests/arrstructcaller.c @@ -160,7 +160,8 @@ int *bar() { } int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrstructcallermulti1.c b/clang/test/3C/generated_tests/arrstructcallermulti1.c index d02c6bee0552..180ee54753ed 100644 --- a/clang/test/3C/generated_tests/arrstructcallermulti1.c +++ b/clang/test/3C/generated_tests/arrstructcallermulti1.c @@ -151,7 +151,8 @@ int *bar() { } int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/arrstructprotoboth.c b/clang/test/3C/generated_tests/arrstructprotoboth.c index eddbf7d23bd3..b74573ab5b31 100644 --- a/clang/test/3C/generated_tests/arrstructprotoboth.c +++ b/clang/test/3C/generated_tests/arrstructprotoboth.c @@ -160,7 +160,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructprotocallee.c b/clang/test/3C/generated_tests/arrstructprotocallee.c index a125dfe5c41c..709a4825a7ff 100644 --- a/clang/test/3C/generated_tests/arrstructprotocallee.c +++ b/clang/test/3C/generated_tests/arrstructprotocallee.c @@ -159,7 +159,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/arrstructprotocaller.c b/clang/test/3C/generated_tests/arrstructprotocaller.c index 2666e0d85459..f0e03f3ee881 100644 --- a/clang/test/3C/generated_tests/arrstructprotocaller.c +++ b/clang/test/3C/generated_tests/arrstructprotocaller.c @@ -148,7 +148,8 @@ int *bar() { } int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/fptrarrboth.c b/clang/test/3C/generated_tests/fptrarrboth.c index 1ac9298c64f4..65a2bd69734c 100644 --- a/clang/test/3C/generated_tests/fptrarrboth.c +++ b/clang/test/3C/generated_tests/fptrarrboth.c @@ -108,7 +108,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrbothmulti2.c b/clang/test/3C/generated_tests/fptrarrbothmulti2.c index b13a262ce83b..b25892a3a555 100644 --- a/clang/test/3C/generated_tests/fptrarrbothmulti2.c +++ b/clang/test/3C/generated_tests/fptrarrbothmulti2.c @@ -115,7 +115,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrcallee.c b/clang/test/3C/generated_tests/fptrarrcallee.c index 5e5af7a46d3d..d32910c15ff4 100644 --- a/clang/test/3C/generated_tests/fptrarrcallee.c +++ b/clang/test/3C/generated_tests/fptrarrcallee.c @@ -108,7 +108,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrcalleemulti2.c b/clang/test/3C/generated_tests/fptrarrcalleemulti2.c index f32d91f2c797..afd62e0dea9e 100644 --- a/clang/test/3C/generated_tests/fptrarrcalleemulti2.c +++ b/clang/test/3C/generated_tests/fptrarrcalleemulti2.c @@ -115,7 +115,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrcaller.c b/clang/test/3C/generated_tests/fptrarrcaller.c index a37ff7c719f2..5a5048d11ac3 100644 --- a/clang/test/3C/generated_tests/fptrarrcaller.c +++ b/clang/test/3C/generated_tests/fptrarrcaller.c @@ -162,7 +162,8 @@ int **bar() { } int **z = sus(x, y); //CHECK_NOALL: int **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/fptrarrcallermulti1.c b/clang/test/3C/generated_tests/fptrarrcallermulti1.c index 9d76f7ba09bf..6223e3da0e65 100644 --- a/clang/test/3C/generated_tests/fptrarrcallermulti1.c +++ b/clang/test/3C/generated_tests/fptrarrcallermulti1.c @@ -150,7 +150,8 @@ int **bar() { } int **z = sus(x, y); //CHECK_NOALL: int **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/fptrarrprotoboth.c b/clang/test/3C/generated_tests/fptrarrprotoboth.c index bcf48a6a9e91..da3b11f84fb3 100644 --- a/clang/test/3C/generated_tests/fptrarrprotoboth.c +++ b/clang/test/3C/generated_tests/fptrarrprotoboth.c @@ -161,7 +161,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrprotocallee.c b/clang/test/3C/generated_tests/fptrarrprotocallee.c index 4de828f3dc14..f5c25d2dc569 100644 --- a/clang/test/3C/generated_tests/fptrarrprotocallee.c +++ b/clang/test/3C/generated_tests/fptrarrprotocallee.c @@ -160,7 +160,8 @@ int **sus(int *x, int *y) { //CHECK: x = (int *)5; int **z = calloc(5, sizeof(int *)); //CHECK_NOALL: int **z = calloc(5, sizeof(int *)); - //CHECK_ALL: _Array_ptr<_Ptr> z = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = calloc<_Ptr>(5, sizeof(int *)); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int *(*mul2ptr)(int *) = mul2; //CHECK: _Ptr<_Ptr (_Ptr)> mul2ptr = mul2; int i; diff --git a/clang/test/3C/generated_tests/fptrarrprotocaller.c b/clang/test/3C/generated_tests/fptrarrprotocaller.c index a6e7705c974d..b18faa7a468d 100644 --- a/clang/test/3C/generated_tests/fptrarrprotocaller.c +++ b/clang/test/3C/generated_tests/fptrarrprotocaller.c @@ -147,7 +147,8 @@ int **bar() { } int **z = sus(x, y); //CHECK_NOALL: int **z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/fptrsafeboth.c b/clang/test/3C/generated_tests/fptrsafeboth.c index 596d093e3235..9d21567937ba 100644 --- a/clang/test/3C/generated_tests/fptrsafeboth.c +++ b/clang/test/3C/generated_tests/fptrsafeboth.c @@ -108,7 +108,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/fptrsafebothmulti2.c b/clang/test/3C/generated_tests/fptrsafebothmulti2.c index 7b975cee340a..25c3906959b1 100644 --- a/clang/test/3C/generated_tests/fptrsafebothmulti2.c +++ b/clang/test/3C/generated_tests/fptrsafebothmulti2.c @@ -115,7 +115,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/fptrsafecallee.c b/clang/test/3C/generated_tests/fptrsafecallee.c index fc21ef661933..e657e5f3620f 100644 --- a/clang/test/3C/generated_tests/fptrsafecallee.c +++ b/clang/test/3C/generated_tests/fptrsafecallee.c @@ -108,7 +108,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/fptrsafecalleemulti2.c b/clang/test/3C/generated_tests/fptrsafecalleemulti2.c index 92a0d3a82aa2..1bf10ac6934d 100644 --- a/clang/test/3C/generated_tests/fptrsafecalleemulti2.c +++ b/clang/test/3C/generated_tests/fptrsafecalleemulti2.c @@ -115,7 +115,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/fptrsafeprotoboth.c b/clang/test/3C/generated_tests/fptrsafeprotoboth.c index 9647410f6dcb..59769f9788a0 100644 --- a/clang/test/3C/generated_tests/fptrsafeprotoboth.c +++ b/clang/test/3C/generated_tests/fptrsafeprotoboth.c @@ -169,7 +169,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/fptrsafeprotocallee.c b/clang/test/3C/generated_tests/fptrsafeprotocallee.c index 80d1aa3d0ba2..771e222f6426 100644 --- a/clang/test/3C/generated_tests/fptrsafeprotocallee.c +++ b/clang/test/3C/generated_tests/fptrsafeprotocallee.c @@ -168,7 +168,8 @@ int *sus(struct general *x, struct general *y) { //CHECK: x = (struct general *)5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; struct general *p = y; //CHECK: _Ptr p = y; int i; diff --git a/clang/test/3C/generated_tests/ptrTOptrboth.c b/clang/test/3C/generated_tests/ptrTOptrboth.c index 07745dc4f7c5..31a5bb631916 100644 --- a/clang/test/3C/generated_tests/ptrTOptrboth.c +++ b/clang/test/3C/generated_tests/ptrTOptrboth.c @@ -109,7 +109,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrbothmulti2.c b/clang/test/3C/generated_tests/ptrTOptrbothmulti2.c index d80ec2650645..a4c3e474f57b 100644 --- a/clang/test/3C/generated_tests/ptrTOptrbothmulti2.c +++ b/clang/test/3C/generated_tests/ptrTOptrbothmulti2.c @@ -116,7 +116,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrcallee.c b/clang/test/3C/generated_tests/ptrTOptrcallee.c index ea8ca5db3ead..330227fd2fcc 100644 --- a/clang/test/3C/generated_tests/ptrTOptrcallee.c +++ b/clang/test/3C/generated_tests/ptrTOptrcallee.c @@ -109,7 +109,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrcalleemulti2.c b/clang/test/3C/generated_tests/ptrTOptrcalleemulti2.c index 28bf64c42e7c..74a90017c49e 100644 --- a/clang/test/3C/generated_tests/ptrTOptrcalleemulti2.c +++ b/clang/test/3C/generated_tests/ptrTOptrcalleemulti2.c @@ -116,7 +116,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrcaller.c b/clang/test/3C/generated_tests/ptrTOptrcaller.c index 79d5d8aff072..d9a1189e4a53 100644 --- a/clang/test/3C/generated_tests/ptrTOptrcaller.c +++ b/clang/test/3C/generated_tests/ptrTOptrcaller.c @@ -146,7 +146,8 @@ char ***bar() { //CHECK: _Ptr<_Ptr<_Ptr>> y = malloc<_Ptr<_Ptr>>(sizeof(char **)); char ***z = sus(x, y); //CHECK_NOALL: char ***z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/ptrTOptrcallermulti1.c b/clang/test/3C/generated_tests/ptrTOptrcallermulti1.c index af71ef6e10c3..854cc02951e6 100644 --- a/clang/test/3C/generated_tests/ptrTOptrcallermulti1.c +++ b/clang/test/3C/generated_tests/ptrTOptrcallermulti1.c @@ -132,7 +132,8 @@ char ***bar() { //CHECK: _Ptr<_Ptr<_Ptr>> y = malloc<_Ptr<_Ptr>>(sizeof(char **)); char ***z = sus(x, y); //CHECK_NOALL: char ***z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/ptrTOptrprotoboth.c b/clang/test/3C/generated_tests/ptrTOptrprotoboth.c index b4e8154d1a0e..914fcc99c89a 100644 --- a/clang/test/3C/generated_tests/ptrTOptrprotoboth.c +++ b/clang/test/3C/generated_tests/ptrTOptrprotoboth.c @@ -144,7 +144,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrprotocallee.c b/clang/test/3C/generated_tests/ptrTOptrprotocallee.c index df95dd98e94d..0c26c7b1ec46 100644 --- a/clang/test/3C/generated_tests/ptrTOptrprotocallee.c +++ b/clang/test/3C/generated_tests/ptrTOptrprotocallee.c @@ -143,7 +143,8 @@ char ***sus(char ***x, char ***y) { *ch = 'A'; /*Capital A*/ char ***z = malloc(5 * sizeof(char **)); //CHECK_NOALL: char ***z = malloc(5 * sizeof(char **)); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = malloc<_Array_ptr>(5 * sizeof(char **)); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; for (int i = 0; i < 5; i++) { z[i] = malloc(5 * sizeof(char *)); //CHECK: z[i] = malloc(5 * sizeof(char *)); diff --git a/clang/test/3C/generated_tests/ptrTOptrprotocaller.c b/clang/test/3C/generated_tests/ptrTOptrprotocaller.c index 50e87d22e809..4e871ee23223 100644 --- a/clang/test/3C/generated_tests/ptrTOptrprotocaller.c +++ b/clang/test/3C/generated_tests/ptrTOptrprotocaller.c @@ -129,7 +129,8 @@ char ***bar() { //CHECK: _Ptr<_Ptr<_Ptr>> y = malloc<_Ptr<_Ptr>>(sizeof(char **)); char ***z = sus(x, y); //CHECK_NOALL: char ***z = sus(x, y); - //CHECK_ALL: _Array_ptr<_Array_ptr> z = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr<_Array_ptr> z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } diff --git a/clang/test/3C/generated_tests/safefptrargboth.c b/clang/test/3C/generated_tests/safefptrargboth.c index 72c66dc9bc32..5c84c793b721 100644 --- a/clang/test/3C/generated_tests/safefptrargboth.c +++ b/clang/test/3C/generated_tests/safefptrargboth.c @@ -101,14 +101,15 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { @@ -121,11 +122,11 @@ int *sus(int (*x)(int), int (*y)(int)) { } int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -137,10 +138,10 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargbothmulti1.c b/clang/test/3C/generated_tests/safefptrargbothmulti1.c index bbcaee35b521..44fab009f15d 100644 --- a/clang/test/3C/generated_tests/safefptrargbothmulti1.c +++ b/clang/test/3C/generated_tests/safefptrargbothmulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -128,10 +128,10 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargbothmulti2.c b/clang/test/3C/generated_tests/safefptrargbothmulti2.c index a30ac29e18cd..7d07840119a3 100644 --- a/clang/test/3C/generated_tests/safefptrargbothmulti2.c +++ b/clang/test/3C/generated_tests/safefptrargbothmulti2.c @@ -108,14 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/safefptrargcallee.c b/clang/test/3C/generated_tests/safefptrargcallee.c index 5ccf4e178fc2..b701fc4ff960 100644 --- a/clang/test/3C/generated_tests/safefptrargcallee.c +++ b/clang/test/3C/generated_tests/safefptrargcallee.c @@ -101,14 +101,15 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { @@ -121,11 +122,11 @@ int *sus(int (*x)(int), int (*y)(int)) { } int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -136,11 +137,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargcalleemulti1.c b/clang/test/3C/generated_tests/safefptrargcalleemulti1.c index ce77e82b56ff..0534035c491f 100644 --- a/clang/test/3C/generated_tests/safefptrargcalleemulti1.c +++ b/clang/test/3C/generated_tests/safefptrargcalleemulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -127,11 +127,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargcalleemulti2.c b/clang/test/3C/generated_tests/safefptrargcalleemulti2.c index 9979e179453d..4611401e5002 100644 --- a/clang/test/3C/generated_tests/safefptrargcalleemulti2.c +++ b/clang/test/3C/generated_tests/safefptrargcalleemulti2.c @@ -108,14 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/safefptrargcaller.c b/clang/test/3C/generated_tests/safefptrargcaller.c index ecfe63a03b85..071e985f6ea8 100644 --- a/clang/test/3C/generated_tests/safefptrargcaller.c +++ b/clang/test/3C/generated_tests/safefptrargcaller.c @@ -101,8 +101,8 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; @@ -120,11 +120,11 @@ int *sus(int (*x)(int), int (*y)(int)) { } int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -136,15 +136,16 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/safefptrargcallermulti1.c b/clang/test/3C/generated_tests/safefptrargcallermulti1.c index e88ef8be48e2..f4a3109837e9 100644 --- a/clang/test/3C/generated_tests/safefptrargcallermulti1.c +++ b/clang/test/3C/generated_tests/safefptrargcallermulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -128,15 +128,16 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/safefptrargcallermulti2.c b/clang/test/3C/generated_tests/safefptrargcallermulti2.c index 83599296517b..f4742f4075b5 100644 --- a/clang/test/3C/generated_tests/safefptrargcallermulti2.c +++ b/clang/test/3C/generated_tests/safefptrargcallermulti2.c @@ -108,8 +108,8 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/safefptrargprotoboth.c b/clang/test/3C/generated_tests/safefptrargprotoboth.c index d1d24aba3ad5..a8f3d6fd10ff 100644 --- a/clang/test/3C/generated_tests/safefptrargprotoboth.c +++ b/clang/test/3C/generated_tests/safefptrargprotoboth.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -125,10 +125,10 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -140,14 +140,15 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/safefptrargprotocallee.c b/clang/test/3C/generated_tests/safefptrargprotocallee.c index a94f78341f70..e6a1784435fa 100644 --- a/clang/test/3C/generated_tests/safefptrargprotocallee.c +++ b/clang/test/3C/generated_tests/safefptrargprotocallee.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -124,11 +124,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -139,14 +139,15 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/safefptrargprotocaller.c b/clang/test/3C/generated_tests/safefptrargprotocaller.c index 3eccfa4cd90e..2068133f4836 100644 --- a/clang/test/3C/generated_tests/safefptrargprotocaller.c +++ b/clang/test/3C/generated_tests/safefptrargprotocaller.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -125,23 +125,24 @@ int *foo() { int *bar() { //CHECK_NOALL: int *bar(void) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr bar(void) { + //CHECK_ALL: _Array_ptr bar(void) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, y); - //CHECK_ALL: _Array_ptr z = sus(x, y); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, y); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/safefptrargprotosafe.c b/clang/test/3C/generated_tests/safefptrargprotosafe.c index 27622b2f41df..fec6f240e449 100644 --- a/clang/test/3C/generated_tests/safefptrargprotosafe.c +++ b/clang/test/3C/generated_tests/safefptrargprotosafe.c @@ -104,15 +104,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -123,11 +123,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) : count(5) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -138,8 +138,8 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/safefptrargsafe.c b/clang/test/3C/generated_tests/safefptrargsafe.c index 90f68f43770c..38cfd230d37c 100644 --- a/clang/test/3C/generated_tests/safefptrargsafe.c +++ b/clang/test/3C/generated_tests/safefptrargsafe.c @@ -100,8 +100,8 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; @@ -119,11 +119,11 @@ int *sus(int (*x)(int), int (*y)(int)) { } int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -134,11 +134,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) : count(5) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargsafemulti1.c b/clang/test/3C/generated_tests/safefptrargsafemulti1.c index 934605103811..76aa79905d93 100644 --- a/clang/test/3C/generated_tests/safefptrargsafemulti1.c +++ b/clang/test/3C/generated_tests/safefptrargsafemulti1.c @@ -107,15 +107,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { - //CHECK_NOALL: _Ptr foo(void) { - //CHECK_ALL: _Array_ptr foo(void) : count(5) { + //CHECK_NOALL: _Ptr foo(void) _Checked { + //CHECK_ALL: _Array_ptr foo(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); @@ -126,11 +126,11 @@ int *foo() { } int *bar() { - //CHECK_NOALL: _Ptr bar(void) { - //CHECK_ALL: _Array_ptr bar(void) : count(5) { + //CHECK_NOALL: _Ptr bar(void) _Checked { + //CHECK_ALL: _Array_ptr bar(void) : count(5) _Checked { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = sub1; //CHECK: _Ptr y = sub1; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/safefptrargsafemulti2.c b/clang/test/3C/generated_tests/safefptrargsafemulti2.c index 492723da43b6..d14749edf035 100644 --- a/clang/test/3C/generated_tests/safefptrargsafemulti2.c +++ b/clang/test/3C/generated_tests/safefptrargsafemulti2.c @@ -107,8 +107,8 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/unsafefptrargboth.c b/clang/test/3C/generated_tests/unsafefptrargboth.c index 343f77a68665..138a9228fa26 100644 --- a/clang/test/3C/generated_tests/unsafefptrargboth.c +++ b/clang/test/3C/generated_tests/unsafefptrargboth.c @@ -101,14 +101,15 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { @@ -125,7 +126,7 @@ int *foo() { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -140,7 +141,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargbothmulti1.c b/clang/test/3C/generated_tests/unsafefptrargbothmulti1.c index 3ba8c4e2dcb2..8bcc04013348 100644 --- a/clang/test/3C/generated_tests/unsafefptrargbothmulti1.c +++ b/clang/test/3C/generated_tests/unsafefptrargbothmulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -131,7 +131,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargbothmulti2.c b/clang/test/3C/generated_tests/unsafefptrargbothmulti2.c index ffa51de39865..ae06fad85937 100644 --- a/clang/test/3C/generated_tests/unsafefptrargbothmulti2.c +++ b/clang/test/3C/generated_tests/unsafefptrargbothmulti2.c @@ -108,14 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/unsafefptrargcallee.c b/clang/test/3C/generated_tests/unsafefptrargcallee.c index 47827790a250..b35210963eaa 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcallee.c +++ b/clang/test/3C/generated_tests/unsafefptrargcallee.c @@ -101,14 +101,15 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { @@ -125,7 +126,7 @@ int *foo() { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -140,7 +141,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargcalleemulti1.c b/clang/test/3C/generated_tests/unsafefptrargcalleemulti1.c index 29990d3c3849..1d1c8f8b2104 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcalleemulti1.c +++ b/clang/test/3C/generated_tests/unsafefptrargcalleemulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -131,7 +131,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargcalleemulti2.c b/clang/test/3C/generated_tests/unsafefptrargcalleemulti2.c index b961998746b2..a3a5813f0401 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcalleemulti2.c +++ b/clang/test/3C/generated_tests/unsafefptrargcalleemulti2.c @@ -108,14 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/unsafefptrargcaller.c b/clang/test/3C/generated_tests/unsafefptrargcaller.c index caf419924272..2f3b68362b0f 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcaller.c +++ b/clang/test/3C/generated_tests/unsafefptrargcaller.c @@ -101,8 +101,8 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; @@ -124,7 +124,7 @@ int *foo() { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -139,12 +139,13 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, _Assume_bounds_cast<_Ptr>(y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/unsafefptrargcallermulti1.c b/clang/test/3C/generated_tests/unsafefptrargcallermulti1.c index fd46bfa4eb70..d266f90892fd 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcallermulti1.c +++ b/clang/test/3C/generated_tests/unsafefptrargcallermulti1.c @@ -108,15 +108,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -131,12 +131,13 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, _Assume_bounds_cast<_Ptr>(y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; diff --git a/clang/test/3C/generated_tests/unsafefptrargcallermulti2.c b/clang/test/3C/generated_tests/unsafefptrargcallermulti2.c index 28bf5da24b3a..840224f2a72b 100644 --- a/clang/test/3C/generated_tests/unsafefptrargcallermulti2.c +++ b/clang/test/3C/generated_tests/unsafefptrargcallermulti2.c @@ -108,8 +108,8 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/unsafefptrargprotoboth.c b/clang/test/3C/generated_tests/unsafefptrargprotoboth.c index 66187823b24b..4a3c60f6344d 100644 --- a/clang/test/3C/generated_tests/unsafefptrargprotoboth.c +++ b/clang/test/3C/generated_tests/unsafefptrargprotoboth.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -128,7 +128,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -140,14 +140,15 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/unsafefptrargprotocallee.c b/clang/test/3C/generated_tests/unsafefptrargprotocallee.c index 7a03290a6bdd..7955c54100a9 100644 --- a/clang/test/3C/generated_tests/unsafefptrargprotocallee.c +++ b/clang/test/3C/generated_tests/unsafefptrargprotocallee.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -128,7 +128,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -139,14 +139,15 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; int *z = calloc(5, sizeof(int)); //CHECK_NOALL: int *z = calloc(5, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = calloc(5, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; int i; for (i = 0; i < 5; i++) { //CHECK_NOALL: for (i = 0; i < 5; i++) { diff --git a/clang/test/3C/generated_tests/unsafefptrargprotocaller.c b/clang/test/3C/generated_tests/unsafefptrargprotocaller.c index 406a74202984..a4a8620c656f 100644 --- a/clang/test/3C/generated_tests/unsafefptrargprotocaller.c +++ b/clang/test/3C/generated_tests/unsafefptrargprotocaller.c @@ -105,15 +105,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -128,20 +128,21 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); //CHECK_NOALL: int *z = sus(x, _Assume_bounds_cast<_Ptr>(y)); - //CHECK_ALL: _Array_ptr z = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(5) = sus(x, _Assume_bounds_cast<_Ptr>(y)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 5) = __3c_lower_bound_z; z += 2; return z; } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/unsafefptrargprotosafe.c b/clang/test/3C/generated_tests/unsafefptrargprotosafe.c index 4d892e2c7d18..e01e0edb409f 100644 --- a/clang/test/3C/generated_tests/unsafefptrargprotosafe.c +++ b/clang/test/3C/generated_tests/unsafefptrargprotosafe.c @@ -104,15 +104,15 @@ int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -127,7 +127,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -138,8 +138,8 @@ int *bar() { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/generated_tests/unsafefptrargsafe.c b/clang/test/3C/generated_tests/unsafefptrargsafe.c index ad7ef51b9a42..2db8745bc8c7 100644 --- a/clang/test/3C/generated_tests/unsafefptrargsafe.c +++ b/clang/test/3C/generated_tests/unsafefptrargsafe.c @@ -100,8 +100,8 @@ int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; @@ -123,7 +123,7 @@ int *foo() { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -138,7 +138,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargsafemulti1.c b/clang/test/3C/generated_tests/unsafefptrargsafemulti1.c index da3f7c97c732..ddaddf1e9142 100644 --- a/clang/test/3C/generated_tests/unsafefptrargsafemulti1.c +++ b/clang/test/3C/generated_tests/unsafefptrargsafemulti1.c @@ -107,15 +107,15 @@ static int *mul2(int *x) { } int *sus(int (*)(int), int (*)(int)); -//CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr); -//CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5); +//CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr); +//CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5); int *foo() { //CHECK_NOALL: _Ptr foo(void) { //CHECK_ALL: _Array_ptr foo(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); @@ -130,7 +130,7 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) : count(5) { int (*x)(int) = add1; - //CHECK: int (*x)(int) = add1; + //CHECK: _Ptr x = add1; int (*y)(int) = mul2; //CHECK: int (*y)(int) = mul2; int *z = sus(x, y); diff --git a/clang/test/3C/generated_tests/unsafefptrargsafemulti2.c b/clang/test/3C/generated_tests/unsafefptrargsafemulti2.c index f11ae9ddb52f..bacc7d11b6fc 100644 --- a/clang/test/3C/generated_tests/unsafefptrargsafemulti2.c +++ b/clang/test/3C/generated_tests/unsafefptrargsafemulti2.c @@ -107,8 +107,8 @@ static int *mul2(int *x) { } int *sus(int (*x)(int), int (*y)(int)) { - //CHECK_NOALL: int *sus(int (*x)(int), _Ptr y) : itype(_Ptr) { - //CHECK_ALL: _Array_ptr sus(int (*x)(int), _Ptr y) : count(5) { + //CHECK_NOALL: int *sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : itype(_Ptr) { + //CHECK_ALL: _Array_ptr sus(int ((*x)(int)) : itype(_Ptr), _Ptr y) : count(5) { x = (int (*)(int))5; //CHECK: x = (int (*)(int))5; diff --git a/clang/test/3C/hash.c b/clang/test/3C/hash.c index 4bec73c58213..dc0d0135b9bb 100644 --- a/clang/test/3C/hash.c +++ b/clang/test/3C/hash.c @@ -13,7 +13,10 @@ _Itype_for_any(T) void vsf_sysutil_memclr(void *p_dest : itype(_Array_ptr) byte_count(size), unsigned int size) -// CHECK_ALL: _Itype_for_any(T) void vsf_sysutil_memclr(void *p_dest : itype(_Array_ptr) byte_count(size), unsigned int size) +// CHECK_ALL: _Itype_for_any(T) void vsf_sysutil_memclr(void *p_dest +// CHECK_ALL-NEXT: : itype(_Array_ptr) +// CHECK_ALL-NEXT: byte_count(size), +// CHECK_ALL-NEXT: unsigned int size) { /* Safety */ if (size == 0) { diff --git a/clang/test/3C/implicit_casts_root_cause.c b/clang/test/3C/implicit_casts_root_cause.c new file mode 100644 index 000000000000..e9ebadc1a47a --- /dev/null +++ b/clang/test/3C/implicit_casts_root_cause.c @@ -0,0 +1,26 @@ +// RUN: 3c -base-dir=%S -alltypes -warn-root-cause %s -- -Xclang -verify -Wno-everything + +// This test checks that void* casts of arguments are ignored by the root +// cause code (and handled by the function parameter causes), but other casts +// are still noted. + +void has_void(void* v); // expected-warning {{3 unchecked pointers: Default void* type}} +void test_no_cause() { + int *b, *c; + has_void(b); // specifically no warning here, since it shows up above + has_void(c); +} + +void has_float(float* v); // expected-warning {{Unchecked pointer in parameter or return of undefined function has_float}} +void test_float_cause() { + int *b, *c; + has_float(b); // expected-warning {{1 unchecked pointer: Cast from int * to float *}} + has_float(c); // expected-warning {{1 unchecked pointer: Cast from int * to float *}} +} + +void has_body(float* v){} +void test_only_args_cause() { + int *b, *c; + has_body(b); // expected-warning {{1 unchecked pointer: Cast from int * to float *}} + has_body(c); // expected-warning {{1 unchecked pointer: Cast from int * to float *}} +} diff --git a/clang/test/3C/inline_anon_structs.c b/clang/test/3C/inline_anon_structs.c index 9b1406157285..e26e64403e65 100644 --- a/clang/test/3C/inline_anon_structs.c +++ b/clang/test/3C/inline_anon_structs.c @@ -1,5 +1,6 @@ // RUN: rm -rf %t* // RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - // RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s // RUN: 3c -base-dir=%S -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - // RUN: 3c -base-dir=%S -output-dir=%t.checked %s -- @@ -11,13 +12,29 @@ an inlinestruct and its associated VarDecl have different locations*/ int valuable; +// When -alltypes is on, the addition of _Checked to the array triggers +// rewriting of the multi-decl, including splitting of the struct definition. +// When it is off, the multi-decl as such is not rewritten, but in either case, +// the fields of the struct are rewritten as appropriate. static struct foo { + // When an inline struct definition is separated from variable declarations, + // if there was a `static` keyword that applied to the variables, we should + // remove it from the separated struct (where it is not meaningful). + //CHECK_NOALL: static struct foo { + //CHECK_ALL: struct foo { const char *name; + // See https://github.com/correctcomputation/checkedc-clang/issues/470. //CHECK_NOALL: const char *name; //CHECK_ALL: _Ptr name; int *p_valuable; //CHECK: _Ptr p_valuable; } array[] = {{"mystery", &valuable}}; +// {{...}} in a CHECK directive delimits a regular expression +// (https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-regex-matching-syntax) +// and there isn't a way to escape that construct itself, so we use a regular +// expression and escape the contents as needed. +//CHECK_NOALL: {{\} array\[\] = \{\{"mystery", &valuable\}\};}} +//CHECK_ALL: {{static struct foo array _Checked\[\] = \{\{"mystery", &valuable\}\};}} /*This code is a series of more complex tests for inline structs*/ /* a, b, c below all stay as WILD pointers; d can be a _Ptr<...>*/ @@ -67,15 +84,14 @@ void foo(void) { /*This code tests anonymous structs */ struct { + //CHECK: struct x_struct_1 { /*the fields of the anonymous struct are free to be marked checked*/ int *data; //CHECK_NOALL: int *data; - - /* but the actual pointer can't be when alltypes is disabled */ - /* when alltypes is enabled, this whole structure is rewritten - improperly, but that's OK, because we signal a warning to the user*/ + //CHECK_ALL: _Array_ptr data : count(4); } * x; -//CHECK_ALL: _Ptr x = ((void *)0); +//CHECK: }; +//CHECK-NEXT: _Ptr x = ((void *)0); /*ensure trivial conversion*/ void foo1(int *w) { @@ -93,15 +109,25 @@ struct alpha *al[4]; //CHECK_NOALL: struct alpha *al[4]; //CHECK_ALL: _Ptr al _Checked[4] = {((void *)0)}; -/*be should be made wild, whereas a should be converted*/ +// A similar test with an unnamed struct. struct { int *a; - //CHECK_NOALL: _Ptr a; + //CHECK: _Ptr a; } * be[4]; +//CHECK_NOALL: } * be[4]; +//CHECK_ALL: }; +//CHECK_ALL-NEXT: _Ptr be _Checked[4] = {((void *)0)}; + +// The following is explained in second_tu_fn in inline_anon_structs_cross_tu.c. +struct { int x; } *cross_tu_numbering_test; +//CHECK_AB: struct cross_tu_numbering_test_struct_1 { int x; }; +//CHECK_AB-NEXT: _Ptr cross_tu_numbering_test = ((void *)0); +//CHECK_BA: struct cross_tu_numbering_test_struct_2 { int x; }; +//CHECK_BA-NEXT: _Ptr cross_tu_numbering_test = ((void *)0); /*this code checks inline structs withiin functions*/ void foo2(int *x) { - //CHECK: void foo2(_Ptr x) { + //CHECK: void foo2(_Ptr x) _Checked { struct bar { int *x; } *y = 0; @@ -110,29 +136,35 @@ void foo2(int *x) { //CHECK-NEXT: }; //CHECK-NEXT: _Ptr y = 0; - /*A non-pointer struct without an init will be marked wild*/ + // A similar test with an automatically added struct initializer. struct something { int *x; } z; //CHECK: struct something { - //CHECK-NEXT: int *x; - //CHECK-NEXT: } z; + //CHECK-NEXT: _Ptr x; + //CHECK-NEXT: }; + //CHECK-NEXT: struct something z = {}; - /*so will ones that are anonymous*/ + // Ditto with an anonymous struct. struct { int *x; } a; - //CHECK: struct { - //CHECK-NEXT: int *x; - //CHECK-NEXT: } a; + //CHECK: struct a_struct_1 { + //CHECK-NEXT: _Ptr x; + //CHECK-NEXT: }; + //CHECK-NEXT: struct a_struct_1 a = {}; - /*if it have an initializer, the rewriter won't have trouble*/ + // If the variable already has an initializer, then there is no initializer + // addition to trigger rewriting and splitting of the struct. struct { int *c; } b = {}; //CHECK: struct { //CHECK-NEXT: _Ptr c; //CHECK-NEXT: } b = {}; + + // Additional regression tests (only checking compilation with no crash) from + // https://github.com/correctcomputation/checkedc-clang/pull/497. struct { int *i; } * f; @@ -140,3 +172,184 @@ void foo2(int *x) { int *il } * g, *h, *i; } + +// Tests of new functionality from +// https://github.com/correctcomputation/checkedc-clang/pull/657. + +// Test that 3C doesn't mangle the code by attempting to split a forward +// declaration of a struct out of a multi-decl as if it were a definition +// (https://github.com/correctcomputation/checkedc-clang/issues/644). +struct fwd *p; +//CHECK: _Ptr p = ((void *)0); + +// Test the handling of inline TagDecls when the containing multi-decl is +// rewritten: +// - TagDecls are de-nested in postorder whether or not they were originally +// named, and this does not interfere with rewrites inside those TagDecls +// (https://github.com/correctcomputation/checkedc-clang/issues/531). +// - Storage and type qualifiers preceding an inline TagDecl are not copied to +// the split TypeDecl (where they wouldn't be meaningful) but remain +// associated with the fields to which they originally applied +// (https://github.com/correctcomputation/checkedc-clang/issues/647). +// - Unnamed TagDecls are automatically named +// (https://github.com/correctcomputation/checkedc-clang/issues/542). +// This big combined test may be a bit hard to understand, but it may provide +// better coverage of any interactions among these features than separate tests +// would. + +// Use up the c_struct_1 name. +struct c_struct_1 {}; + +static struct A { + const struct B { + struct { + int *c2i; + } *c; + } *ab, ab_arr[2]; + volatile struct D { + struct { + int *c3i; + } *c; + struct E { + int *ei; + } *de; + const enum F { F_0, F_1 } *df; + enum { G_0, G_1 } *dg; + struct H { + int *hi; + } *dh; + union U { + int *ui; + } *du; + union { + int *vi; + } *dv; + } *ad; +} *global_a, global_a_arr[2]; + +void constrain_dh(void) { + struct D d; + d.dh = (struct H *)1; +} + +// Points of note: +// - We have two unnamed inline structs that get automatically named after a +// field `c`, but the name `c_struct_1` is taken, so the names `c_struct_2` +// and `c_struct_3` are assigned. +// - All kinds of TagDecls (structs, unions, and enums) can be moved out of a +// containing struct and automatically named if needed. In principle, 3C +// should be able to move TagDecls out of a union, but I couldn't find any way +// to force a union field to be rewritten in order to demonstrate this, since +// 3C constrains union fields to wild. As far as I know, TagDecls can't be +// nested in an enum in C. +// - `struct H` does not get moved because there is nothing to trigger rewriting +// of the containing multi-decl since `dh` is constrained wild by +// `constrain_dh`. + +//CHECK: struct c_struct_2 { +//CHECK-NEXT: _Ptr c2i; +//CHECK-NEXT: }; +//CHECK-NEXT: struct B { +//CHECK-NEXT: _Ptr c; +//CHECK-NEXT: }; +//CHECK-NEXT: struct c_struct_3 { +//CHECK-NEXT: _Ptr c3i; +//CHECK-NEXT: }; +//CHECK-NEXT: struct E { +//CHECK-NEXT: _Ptr ei; +//CHECK-NEXT: }; +//CHECK-NEXT: enum F { F_0, F_1 }; +//CHECK-NEXT: enum dg_enum_1 { G_0, G_1 }; +//CHECK-NEXT: union U { +//CHECK-NEXT: int *ui; +//CHECK-NEXT: }; +//CHECK-NEXT: union dv_union_1 { +//CHECK-NEXT: int *vi; +//CHECK-NEXT: }; +//CHECK-NEXT: struct D { +//CHECK-NEXT: _Ptr c; +//CHECK-NEXT: _Ptr de; +//CHECK-NEXT: _Ptr df; +//CHECK-NEXT: _Ptr dg; +//CHECK-NEXT: struct H { +//CHECK-NEXT: _Ptr hi; +//CHECK-NEXT: } *dh; +//CHECK-NEXT: _Ptr du; +//CHECK-NEXT: _Ptr dv; +//CHECK-NEXT: }; +//CHECK-NEXT: struct A { +//CHECK-NEXT: _Ptr ab; +//CHECK_NOALL-NEXT: const struct B ab_arr[2]; +//CHECK_ALL-NEXT: const struct B ab_arr _Checked[2]; +//CHECK-NEXT: _Ptr ad; +//CHECK-NEXT: }; +//CHECK-NEXT: static _Ptr global_a = ((void *)0); +//CHECK_NOALL-NEXT: static struct A global_a_arr[2]; +//CHECK_ALL-NEXT: static struct A global_a_arr _Checked[2]; + +// This case is not intentionally supported but works "by accident" because 3C +// deletes everything between the start location of the first member and the +// start of the TagDecl (here, `_Ptr<`) and replaces everything between the end +// location of the TagDecl and the end of the first member (here, +// `> *chkptr_struct_var`) with the new text of the first member. It may well be +// the right decision to break this case in the future, but it would be nice to +// be aware that we're doing so, and we might want to keep a test that this case +// merely produces wrong output and doesn't crash the rewriter. +_Ptr *chkptr_struct_var; +//CHECK: struct chkptr_struct { _Ptr x; }; +//CHECK-NEXT: _Ptr<_Ptr> chkptr_struct_var = ((void *)0); + +// Tests of the special case where we use the first member of a typedef +// multi-decl as the name of the inline TagDecl. + +typedef struct { int *x; } SFOO, *PSFOO; +//CHECK: typedef struct { _Ptr x; } SFOO; +//CHECK-NEXT: typedef _Ptr PSFOO; + +// The other way around, we can't do it because SBAR would be defined too late +// to use it in the definition of PSBAR. +typedef struct { int *x; } *PSBAR, SBAR; +//CHECK: struct PSBAR_struct_1 { _Ptr x; }; +//CHECK-NEXT: typedef _Ptr PSBAR; +//CHECK-NEXT: typedef struct PSBAR_struct_1 SBAR; + +// Borderline case: Since SFOO_CONST has a qualifier, we don't use it as the +// name of the inline struct. If we did, we'd end up with `typedef +// _Ptr PSFOO_CONST`, which still expands to +// `_Ptr`, but the duplicate `const` may be a bit +// confusing to the reader, so we don't do that. +typedef const struct { int *x; } SFOO_CONST, *PSFOO_CONST; +//CHECK: struct SFOO_CONST_struct_1 { _Ptr x; }; +//CHECK-NEXT: typedef const struct SFOO_CONST_struct_1 SFOO_CONST; +//CHECK-NEXT: typedef _Ptr PSFOO_CONST; + +// Test that when the outer struct is preceded by a qualifier, de-nesting +// inserts inner structs before the qualifier, not between the qualifier and the +// outer `struct` keyword. This is moot if the outer struct is split as part of +// multi-decl rewriting because multi-decl rewriting will delete the qualifier +// and add it before the first member, but the problem can happen if the +// multi-decl isn't rewritten or the outer struct isn't split because we have a +// typedef for it. + +typedef struct { struct q_not_rewritten_inner {} *x; } *q_not_rewritten_outer; +q_not_rewritten_outer onr = (q_not_rewritten_outer)1; +//CHECK: struct q_not_rewritten_inner {}; +//CHECK-NEXT: typedef struct { _Ptr x; } *q_not_rewritten_outer; + +typedef struct { + struct q_typedef_inner {} *x; +} q_typedef_outer, *q_typedef_outer_trigger_rewrite; +//CHECK: struct q_typedef_inner {}; +//CHECK-NEXT: typedef struct { +//CHECK-NEXT: _Ptr x; +//CHECK-NEXT: } q_typedef_outer; +//CHECK-NEXT: typedef _Ptr q_typedef_outer_trigger_rewrite; + +// As noted in the comment in DeclRewriter::denestTagDecls, when the outer +// struct isn't part of a multi-decl, we don't have an easy way to find the +// location before the (useless) qualifier, so the output is a bit weird but +// still has only a compiler warning, though in a different place than it +// should. +typedef struct { struct q_pointless_inner {} *x; }; +//CHECK: typedef struct q_pointless_inner {}; +//CHECK-NEXT: struct { _Ptr x; }; diff --git a/clang/test/3C/inline_anon_structs_cross_tu.c b/clang/test/3C/inline_anon_structs_cross_tu.c new file mode 100644 index 000000000000..b83d2027c932 --- /dev/null +++ b/clang/test/3C/inline_anon_structs_cross_tu.c @@ -0,0 +1,69 @@ +// Regression tests for two bugs seen during the development of +// https://github.com/correctcomputation/checkedc-clang/pull/657 with multiple +// translation units: some information about automatically named TagDecls was +// not propagated across translation units. +// +// Our translation units are inline_anon_structs.c and +// inline_anon_structs_cross_tu.c; the latter `#include`s the former and has +// some code of its own. So inline_anon_structs.c effectively plays the role of +// a shared header file, but we just use the .c file instead of moving the code +// to a .h file and making another .c file that does nothing but `#include` the +// .h file. +// +// We test 3C on both orders of the translation units. Remember that when the +// same file is rewritten as part of multiple translation units, the last +// translation unit wins +// (https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-804283984). +// So the "inline_anon_structs.c, inline_anon_structs_cross_tu.c" order should +// catch most problems that occur when 3C makes the decisions in one translation +// unit but does the rewriting in another. We still test the other order for +// completeness. + +// RUN: rm -rf %t* + +// Tests of the "inline_anon_structs.c, inline_anon_structs_cross_tu.c" order +// (called "AB" for short). +// +// Note about check prefixes: We currently don't bother with a Cartesian product +// of {ALL,NOALL} x {AB,BA} because we don't have any tests whose results depend +// on both variables. We do pass CHECK_NOALL and CHECK_ALL to +// inline_anon_structs_cross_tu.c for uniformity, even though that file +// currently doesn't use either. +// +// RUN: 3c -base-dir=%S -addcr -alltypes -output-dir=%t.checkedALL_AB %S/inline_anon_structs.c %s -- +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK","CHECK_AB" --input-file %t.checkedALL_AB/inline_anon_structs.c %S/inline_anon_structs.c +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK","CHECK_AB" --input-file %t.checkedALL_AB/inline_anon_structs_cross_tu.c %s +// RUN: 3c -base-dir=%S -addcr -output-dir=%t.checkedNOALL_AB %S/inline_anon_structs.c %s -- +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK","CHECK_AB" --input-file %t.checkedNOALL_AB/inline_anon_structs.c %S/inline_anon_structs.c +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK","CHECK_AB" --input-file %t.checkedNOALL_AB/inline_anon_structs_cross_tu.c %s + +// Tests of the "inline_anon_structs_cross_tu.c, inline_anon_structs.c" order +// (called "BA"). +// +// RUN: 3c -base-dir=%S -addcr -alltypes -output-dir=%t.checkedALL_BA %s %S/inline_anon_structs.c -- +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK","CHECK_BA" --input-file %t.checkedALL_BA/inline_anon_structs.c %S/inline_anon_structs.c +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK","CHECK_BA" --input-file %t.checkedALL_BA/inline_anon_structs_cross_tu.c %s +// RUN: 3c -base-dir=%S -addcr -output-dir=%t.checkedNOALL_BA %s %S/inline_anon_structs.c -- +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK","CHECK_BA" --input-file %t.checkedNOALL_BA/inline_anon_structs.c %S/inline_anon_structs.c +// RUN: FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK","CHECK_BA" --input-file %t.checkedNOALL_BA/inline_anon_structs_cross_tu.c %s + +void second_tu_fn(void) { + // In the AB order, the global `cross_tu_numbering_test` variable in + // inline_anon_structs.c will be seen first and its struct will be named + // cross_tu_numbering_test_struct_1, and this struct will be named + // cross_tu_numbering_test_struct_2. In the BA order, this code is seen before + // the `#include "inline_anon_structs.c"`, so this struct will take the + // cross_tu_numbering_test_struct_1 name and the inline_anon_structs.c one + // will be cross_tu_numbering_test_struct_2. + // + // Note that in order to have two different variables with the same name (in + // order to produce a collision in struct names), we have to put the variables + // in different scopes: in this case, global and function-local. + struct { int y; } *cross_tu_numbering_test; + //CHECK_AB: struct cross_tu_numbering_test_struct_2 { int y; }; + //CHECK_AB-NEXT: _Ptr cross_tu_numbering_test = ((void *)0); + //CHECK_BA: struct cross_tu_numbering_test_struct_1 { int y; }; + //CHECK_BA-NEXT: _Ptr cross_tu_numbering_test = ((void *)0); +} + +#include "inline_anon_structs.c" diff --git a/clang/test/3C/itype_nt_arr_cast.c b/clang/test/3C/itype_nt_arr_cast.c index 45cdc290aacc..07fa0dd45b01 100644 --- a/clang/test/3C/itype_nt_arr_cast.c +++ b/clang/test/3C/itype_nt_arr_cast.c @@ -1,7 +1,6 @@ // RUN: rm -rf %t* // RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s // RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s -// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - // RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- // RUN: 3c -base-dir=%t.checked -alltypes %t.checked/itype_nt_arr_cast.c -- | diff %t.checked/itype_nt_arr_cast.c - @@ -59,6 +58,6 @@ char *caller4(char *c) { // works as is, and I don't want to waste time when this is just a workaround // for a CheckedC clang bug. fn4(c); - //CHECK: fn4(_Assume_bounds_cast<_Nt_array_ptr>(c, byte_count(0))); + //CHECK: fn4(_Assume_bounds_cast<_Nt_array_ptr>(c, bounds(unknown))); return c; } diff --git a/clang/test/3C/itype_typedef.c b/clang/test/3C/itype_typedef.c new file mode 100644 index 000000000000..6de35ae7c6bc --- /dev/null +++ b/clang/test/3C/itype_typedef.c @@ -0,0 +1,120 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/itype_typedef.c -- | diff %t.checked/itype_typedef.c - + +// Itype typedef on the paramter + +typedef int *td0; +void test0(td0 a) { +//CHECK: typedef _Ptr td0; +//CHECK: void test0(int *a : itype(td0)) { + a = 1; +} + +typedef int *td1; +void test1(td1 a) { +//CHECK: typedef int *td1; +//CHECK: void test1(td1 a : itype(_Ptr)) { + td1 b = a; + b = 1; +} + +typedef int *td2; +void test2(td2 a) { +//CHECK: typedef int *td2; +//CHECK: void test2(td2 a : itype(_Ptr)) { + td2 b = 1; +} + +// Itype typedef on the return + +typedef int *td3; +td3 test3() { +//CHECK: typedef _Ptr td3; +//CHECK: int *test3(void) : itype(td3) { + return (int*) 1; +} + +typedef int *td4; +td4 test4() { +//CHECK: typedef int *td4; +//CHECK: td4 test4(void) : itype(_Ptr) { + td4 a = 1; + return a; +} + +// Itype typedef with array bounds + +typedef int *td5; +void test5(td5 a, int n) { +//CHECK: typedef int *td5; +//CHECK_ALL: void test5(td5 a : itype(_Array_ptr) count(n), int n) { +//CHECK_NOALL: void test5(td5 a : itype(_Ptr), int n) { + for (int i = 0; i < n; i++) + a[i]; + td5 b = 1; +} + +typedef int *td6; +void test6(td6 a, int n) { +//CHECK_ALL: typedef _Array_ptr td6; +//CHECK_ALL: void test6(int *a : itype(td6) count(n), int n) { +//CHECK_NOALL: typedef _Ptr td6; +//CHECK_NOALL: void test6(int *a : itype(td6), int n) { + for (int i = 0; i < n; i++) + a[i]; + a = 1; +} + +// Itype typedef with type qualifiers + +typedef int *td7; +void test7(const td7 a) { +//CHECK: typedef _Ptr td7; +//CHECK: void test7(int *const a : itype(const td7)) { + int *b = a; + b = 1; +} + +typedef int *td8; +void test8(const td8 a) { +//CHECK: typedef int *td8; +//CHECK: void test8(const td8 a : itype(const _Ptr)) { + td8 b = a; + b = 1; +} + +typedef const int *td9; +void test9(td9 a) { +//CHECK: typedef _Ptr td9; +//CHECK: void test9(const int *a : itype(td9)) { + int *b = a; + b = 1; +} + +typedef const int *td10; +void test10(td10 a) { +//CHECK: typedef const int *td10; +//CHECK: void test10(td10 a : itype(_Ptr)) { + td10 b = a; + b = 1; +} + +// With functions pointers + +typedef int (*fp_td0)(int); +void fp_test0(fp_td0 a) { +//CHECK: typedef _Ptr fp_td0; +//CHECK: void fp_test0(int ((*a)(int)) : itype(fp_td0)) { + a = 1; +} + +typedef int (*fp_td1)(int); +void fp_test1(fp_td1 a) { +//CHECK: typedef int (*fp_td1)(int); +//CHECK: void fp_test1(fp_td1 a : itype(_Ptr)) { + fp_td1 b = 1; +} diff --git a/clang/test/3C/itype_undef.c b/clang/test/3C/itype_undef.c new file mode 100644 index 000000000000..e32006226f91 --- /dev/null +++ b/clang/test/3C/itype_undef.c @@ -0,0 +1,162 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S --infer-types-for-undefs -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S --infer-types-for-undefs -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S --infer-types-for-undefs -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S --infer-types-for-undefs -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked --infer-types-for-undefs -alltypes %t.checked/itype_undef.c -- | diff %t.checked/itype_undef.c - + +// Basic checks for adding itypes on undefined functions. +void test0(int *a); +//CHECK: void test0(int *a : itype(_Ptr)); + +int *test1(); +//CHECK: int *test1(void) : itype(_Ptr); + +void caller() { +//CHECK: void caller() _Checked { + int *a = 0; + //CHECK: _Ptr a = 0; + test0(a); + + // This block is left unchecked because it calls a function without either a + // defintion or a prototype even though a `void` prototype is added durring + // conversion. Checked region insertion could be improved to recognize this + // as safe. + { + // CHECK: _Unchecked { + int *b = test1(); + //CHECK: _Ptr b = test1(); + } +} + +// Check for undef functions with existing checked types/itypes. +void test2(int* a : itype(_Ptr)); +int *test3(void) : itype(_Ptr); +void test4(_Ptr a); +_Ptr test5(void); +int *test6(void) : count(10); +void test7(int * : count(10)); +//CHECK: void test2(int* a : itype(_Ptr)); +//CHECK: int *test3(void) : itype(_Ptr); +//CHECK: void test4(_Ptr a); +//CHECK: _Ptr test5(void); +//CHECK: int *test6(void) : count(10); +//CHECK: void test7(int * : count(10)); + +void checked_caller() { +//CHECK_NOALL: void checked_caller() { +//CHECK_ALL: void checked_caller() _Checked { + int *a; + //CHECK: _Ptr a = ((void *)0); + test2(a); + + int *b = test3(); + //CHECK: _Ptr b = test3(); + + int *c; + //CHECK: _Ptr c = ((void *)0); + test4(c); + + int *d = test5(); + //CHECK: _Ptr d = test5(); + + int *e = test6(); + //CHECK_NOALL: int *e = test6(); + //CHECK_ALL: _Array_ptr e : count(10) = test6(); + + int *f; + //CHECK_NOALL: int *f; + //CHECK_ALL: _Array_ptr f : count(10) = ((void *)0); + test7(f); + + // Get 3C to infer the correct length for f and e. + for(int i = 0; i < 10; i++) { + f[i]; + e[i]; + } +} + +// Void pointers should still be fully unchecked unless there is an existing +// checked type/itype. +void test_void0(void *); +void *test_void1(); +//CHECK: void test_void0(void *); +//CHECK: void *test_void1(); +void void_caller() { + void * a = 0; + test_void0(a); + void *b = test_void1(); +} + +void test_void_itype(void * : itype(_Array_ptr)); +void test_void_count(void * : byte_count(0)); +void test_void_checked(_Array_ptr); +//CHECK: void test_void_itype(void * : itype(_Array_ptr)); +//CHECK: void test_void_count(void * : byte_count(0)); +//CHECK: void test_void_checked(_Array_ptr); + +// Pointer type and bounds inference should still work. +int *test_arr0(int n); +//CHECK_ALL: int *test_arr0(int n) : itype(_Array_ptr) count(n); +//CHECK_NOALL: int *test_arr0(int n) : itype(_Ptr); + +void test_arr1(int *a, int n); +//CHECK: void test_arr1(int *a : itype(_Ptr), int n); + +void arr_caller(int s) { +//CHECK_ALL: void arr_caller(int s) _Checked { + // Returns variables use the least solution, so this will cause test_arr0 to + // solve to _Array_ptr. + int *b = test_arr0(s); + //CHECK_ALL: _Array_ptr b : count(s) = test_arr0(s); + //CHECK_NOALL: int *b = test_arr0(s); + for (int i = 0; i < s; i++) + b[i]; + + // Parameter variables use the greatest solution, so test_arr1 will still + // solve to _Ptr. It might be a good idea to change the solution used for + // undefined functions so that we can infer _Array_ptr here and infer it a + // bound. + int *c = 0; + //CHECK_ALL: _Array_ptr c : count(s) = 0; + //CHECK_NOALL: int *c = 0; + test_arr1(c, s); + for (int i = 0; i < s; i++) + c[i]; +} + +// If all uses of the typedef are checked, the unchecked internal constraint +// variable of the undefined function should not make the typedef unchecked. + +typedef int *td0; +void typedef0(td0 a); +void typedef_caller0() { +//CHECK: typedef _Ptr td0; +//CHECK: void typedef0(int *a : itype(td0)); +//CHECK: void typedef_caller0() _Checked { + td0 b = 0; + typedef0(b); +} + +// If the typedef is otherwise unchecked, we should still give a useful +// checked itype to the typedef'ed parameter. + +typedef int *td1; +void typedef1(td1 a); +void typedef_caller1() { +//CHECK: typedef int *td1; +//CHECK: void typedef1(td1 a : itype(_Ptr)); +//CHECK: void typedef_caller1() { + td1 b = 1; + typedef1(b); +} + +// As expected, function typedefs aren't handled well, but they should generate +// valid code. +typedef void fn_typedef0(int *); +fn_typedef0 fntd_decl0; +//CHECK: void fntd_decl0(int * : itype(_Ptr)); + +typedef int *fn_typedef1(); +fn_typedef1 fntd_decl1; +//CHECK: int *fntd_decl1(void) : itype(_Ptr); diff --git a/clang/test/3C/itypes_for_extern.c b/clang/test/3C/itypes_for_extern.c new file mode 100644 index 000000000000..14e189eb95af --- /dev/null +++ b/clang/test/3C/itypes_for_extern.c @@ -0,0 +1,166 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -itypes-for-extern -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -itypes-for-extern -alltypes %t.checked/itypes_for_extern.c -- | diff %t.checked/itypes_for_extern.c - + +// Simplest test case: a would normally get a checked type, but is given an +// itype because of the flag. +void foo(int *a) {} +//CHECK: void foo(int *a : itype(_Ptr)) _Checked {} + +// Since static function can't be included in other translation units, these +// can stay fully checked. +static void static_foo(int *a) {} +//CHECK: static void static_foo(_Ptr a) _Checked {} + +// Don't give a function an itype if it wouldn't normally be checked +void undef_foo(int *a); +//CHECK: void undef_foo(int *a); + +int *bar() { return 0; } +//CHECK: int *bar(void) : itype(_Ptr) _Checked { return 0; } + +int *baz(int *a, int len, int *b) { +//CHECK_ALL: int *baz(int *a : itype(_Array_ptr) count(len), int len, int *b : itype(_Ptr)) : itype(_Array_ptr) count(len) _Checked { +//CHECK_NOALL: int *baz(int *a : itype(_Ptr), int len, int *b : itype(_Ptr)) : itype(_Ptr) { + for (int i = 0; i < len; i++) + a[i]; + return a; +} + +void buz(int *(*f)(int *, int *)) {} +//CHECK: void buz(int *((*f)(int *, int *)) : itype(_Ptr<_Ptr (_Ptr, _Ptr)>)) _Checked {} + +typedef int * int_star; +void typedef_test(int_star p) {} +//CHECK: typedef int * int_star; +//CHECK: void typedef_test(int_star p : itype(_Ptr)) _Checked {} + +typedef void (*fn)(int *); +void fn_typedef_test(fn f) {} +//CHECK: typedef void (*fn)(int *); +//CHECK: void fn_typedef_test(fn f : itype(_Ptr)>)) _Checked {} + +struct foo { + int *a; + void (*fn)(int *); + int *b, **c; +}; +//CHECK: int *a : itype(_Ptr); +//CHECK: void ((*fn)(int *)) : itype(_Ptr)>); +//CHECK: int *b : itype(_Ptr); +//CHECK: int **c : itype(_Ptr<_Ptr>); + +int *glob = 0; +extern int *extern_glob = 0; +static int *static_glob = 0; +//CHECK: int *glob : itype(_Ptr) = 0; +//CHECK: extern int *extern_glob : itype(_Ptr) = 0; +//CHECK: static _Ptr static_glob = 0; + +void (*glob_fn)(int *) = 0; +extern void (*extern_glob_fn)(int *) = 0; +static void (*static_glob_fn)(int *) = 0; +//CHECK: void ((*glob_fn)(int *)) : itype(_Ptr)>) = 0; +//CHECK: extern void ((*extern_glob_fn)(int *)) : itype(_Ptr)>) = 0; +//CHECK: static _Ptr)> static_glob_fn = 0; + +int_star typedef_glob = 0; +fn typedef_fn_glob = 0; +//CHECK: int_star typedef_glob : itype(_Ptr) = 0; +//CHECK: fn typedef_fn_glob : itype(_Ptr)>) = 0; + +struct typedef_struct { + int_star a; + fn f; +}; +//CHECK: int_star a : itype(_Ptr); +//CHECK: fn f : itype(_Ptr)>); + +// Testing some cases where the itypes already exist. Earlier we made a change +// that lets itypes re-solve to checked types. That shouldn't happen with this +// flag. This is also covered by the idempotence check, but I wanted to make it +// explicit. + +void has_itype0(int *a : itype(_Ptr)) { a = 1; } +//CHECK: void has_itype0(int *a : itype(_Ptr)) { a = 1; } + +void has_itype1(int *a : itype(_Ptr)) { a = 0; } +//CHECK: void has_itype1(int *a : itype(_Ptr)) _Checked { a = 0; } + +// Test rewriting itypes for constant sized arrays. As with function pointers, +// part of the type (the array size) occurs after the name of the variable +// being declared. This complicates rewriting. These examples caused errors in +// libjpeg. +// +// multivardecls_complex_types.c has tests of some similar cases as part of +// multi-decls, with and without -itypes-for-extern. + +int const_arr0[10]; +//CHECK_ALL: int const_arr0[10] : itype(int _Checked[10]); +//CHECK_NOALL: int const_arr0[10]; + +int *const_arr1[10]; +//CHECK_ALL: int *const_arr1[10] : itype(_Ptr _Checked[10]) = {((void *)0)}; +//CHECK_NOALL: int *const_arr1[10]; + +int (*const_arr2)[10]; +//CHECK_ALL: int (*const_arr2)[10] : itype(_Ptr) = ((void *)0); +//CHECK_NOALL: int (*const_arr2)[10] : itype(_Ptr) = ((void *)0); + +// Itypes for constants sized arrays when there is a declaration with and +// without a parameter list take slightly different paths that need to be +// tested. If there is no parameter list, then the unchecked component of the +// itype can't be copied from the declaration, and it instead must be generated +// from the constraint variable. + +void const_arr_fn(); +void const_arr_fn(int a[10]) {} +//CHECK_ALL: void const_arr_fn(int *a : itype(int _Checked[10])); +//CHECK_ALL: void const_arr_fn(int *a : itype(int _Checked[10])) _Checked {} + +// Rewriting an existing itype or bounds expression on a global variable. Doing +// this correctly requires replacing text until the end of the Checked C +// annotation expression. The m_* and s_* tests in multivardecls_complex_types.c +// test some similar cases in combination with multi-decls. +int *a : itype(_Ptr); +int **b : itype(_Ptr); +int *c : count(2); +int **d : count(2); +int **e : itype(_Array_ptr) count(2); +int **f : count(2) itype(_Array_ptr); +int **g : count(2) itype(_Array_ptr) = 0; +//CHECK: int *a : itype(_Ptr); +//CHECK: int **b : itype(_Ptr<_Ptr>) = ((void *)0); +//CHECK: int *c : count(2); +//CHECK: int **d : itype(_Array_ptr<_Ptr>) count(2) = ((void *)0); +//CHECK: int **e : itype(_Array_ptr<_Ptr>) count(2) = ((void *)0); +//CHECK: int **f : itype(_Array_ptr<_Ptr>) count(2) = ((void *)0); +//CHECK: int **g : itype(_Array_ptr<_Ptr>) count(2) = 0; + +// `a` gets a fresh lower bound because of the update `a = a + 2`. The fresh +// bound for itype parameters uses an unchecked type outside of +// -itypes-for-extern, but needs to still use a checked type with +// -itypes-for-extern to avoid type errors on assignment to checked pointers +// inside the function. +void test_fresh_lower_bound(int *a, int l) { +// CHECK_ALL: void test_fresh_lower_bound(int *__3c_lower_bound_a : itype(_Array_ptr) count(l), int l) _Checked { +// CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l) = __3c_lower_bound_a; + for(int i = 0; i < l; i++) + a[i]; + a = a + 2; + int *b = a; + // CHECK_ALL: _Ptr b = a; +} + +void test_fresh_lower_bound_itype(int *a, int l) { +// CHECK_ALL: void test_fresh_lower_bound_itype(int *__3c_lower_bound_a : itype(_Array_ptr) count(l), int l) { +// CHECK_ALL: int *a = __3c_lower_bound_a; + for(int i = 0; i < l; i++) + a[i]; + a = a + 2; + a = 1; + int *b = a; +} diff --git a/clang/test/3C/json_formatting.c b/clang/test/3C/json_formatting.c index 2a060ea567cf..9b2a08a53607 100644 --- a/clang/test/3C/json_formatting.c +++ b/clang/test/3C/json_formatting.c @@ -12,3 +12,12 @@ int *a; int *b(int *c); static int *d() { return 0; } void e(int *f, int len) { f[0]; } + +// from root_cause.c, this has a conflicting constraint +// with additional reasons that add more json structure +int get_strlen(char *s : itype(_Nt_array_ptr)); +void test_conflict() { + char *c; + get_strlen(c); + char **cptr = &c; +} diff --git a/clang/test/3C/liberal_itypes_fp.c b/clang/test/3C/liberal_itypes_fp.c index 677823f40095..0b6f93088148 100644 --- a/clang/test/3C/liberal_itypes_fp.c +++ b/clang/test/3C/liberal_itypes_fp.c @@ -75,7 +75,7 @@ void fp_unsafe_return() { } void f_ptr_arg(int (*f)()) { - // CHECK: void f_ptr_arg(int (*f)()) { + // CHECK: void f_ptr_arg(int ((*f)(void)) : itype(_Ptr)) { f = 1; } @@ -86,7 +86,7 @@ void fpnc1(void *p1) {} void fpnc2() { fpnc0(fpnc1); } // CHECK: void fpnc2() { fpnc0(fpnc1); } void fpnc3(void (*fptr)(void *)) { fptr = 1; } -// CHECK: void fpnc3(void (*fptr)(void *)) { fptr = 1; } +// CHECK: void fpnc3(void ((*fptr)(void *)) : itype(_Ptr)) { fptr = 1; } void fpnc4(void *p1) {} // CHECK: void fpnc4(void *p1) {} void fpnc5() { fpnc3(fpnc4); } diff --git a/clang/test/3C/liberal_itypes_ptr.c b/clang/test/3C/liberal_itypes_ptr.c index 9af4eee80838..10aab8ebb7c0 100644 --- a/clang/test/3C/liberal_itypes_ptr.c +++ b/clang/test/3C/liberal_itypes_ptr.c @@ -1,7 +1,7 @@ // RUN: rm -rf %t* // RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s // RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s -// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - // RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- // RUN: 3c -base-dir=%t.checked -alltypes %t.checked/liberal_itypes_ptr.c -- | diff %t.checked/liberal_itypes_ptr.c - @@ -116,7 +116,8 @@ void bounds_fn(void *b : byte_count(1)); // CHECK: void bounds_fn(void *b : byte_count(1)); void bounds_call(void *p) { - // CHECK: void bounds_call(void *p) { + // CHECK_NOALL: void bounds_call(void *p) { + // CHECK_ALL: _Itype_for_any(T) void bounds_call(_Array_ptr p : byte_count(1)) { bounds_fn(p); // CHECK: bounds_fn(p); } diff --git a/clang/test/3C/linkedlist.c b/clang/test/3C/linkedlist.c index 20c9addd6866..e0329f0c4b54 100644 --- a/clang/test/3C/linkedlist.c +++ b/clang/test/3C/linkedlist.c @@ -37,6 +37,7 @@ struct node { struct list { Node *head; + //CHECK: _Ptr head; }; Node *createnode(int data); @@ -46,7 +47,7 @@ Node *createnode(int data) { //CHECK: _Ptr createnode(int data) { Node *newNode = malloc(sizeof(Node)); - //CHECK: _Ptr newNode = malloc(sizeof(Node)); + //CHECK: _Ptr newNode = malloc(sizeof(Node)); if (!newNode) { @@ -80,6 +81,7 @@ void display(List *list) { //CHECK: void display(_Ptr list) { Node *current = list->head; + //CHECK: _Ptr current = list->head; if (list->head == NULL) @@ -95,8 +97,10 @@ void add(int data, List *list) { //CHECK: void add(int data, _Ptr list) { Node *current = NULL; + //CHECK: _Ptr current = NULL; if (list->head == NULL) { + //CHECK: if (list->head == NULL) _Checked { list->head = createnode(data); @@ -107,6 +111,7 @@ void add(int data, List *list) { current = list->head; while (current->next != NULL) { + //CHECK: while (current->next != NULL) _Checked { current = current->next; } @@ -119,10 +124,13 @@ void delete (int data, List *list) { //CHECK: void delete (int data, _Ptr list) { Node *current = list->head; + //CHECK: _Ptr current = list->head; Node *previous = current; + //CHECK: _Ptr previous = current; while (current != NULL) { + //CHECK: while (current != NULL) _Checked { if (current->data == data) { @@ -147,12 +155,16 @@ void reverse(List *list) { //CHECK: void reverse(_Ptr list) { Node *reversed = NULL; + //CHECK: _Ptr reversed = NULL; Node *current = list->head; + //CHECK: _Ptr current = list->head; Node *temp = NULL; + //CHECK: _Ptr temp = NULL; while (current != NULL) { + //CHECK: while (current != NULL) _Checked { temp = current; @@ -167,13 +179,16 @@ void reverse(List *list) { } void destroy(List *list) { - //CHECK: void destroy(_Ptr list) { + //CHECK: void destroy(_Ptr list) { Node *current = list->head; + //CHECK: _Ptr current = list->head; Node *next = current; + //CHECK: _Ptr next = current; while (current != NULL) { + //CHECK: while (current != NULL) _Checked { next = current->next; free(current); current = next; diff --git a/clang/test/3C/macro_end_of_decl.c b/clang/test/3C/macro_end_of_decl.c index 8cc7790ef82d..d4e7daa0efd7 100644 --- a/clang/test/3C/macro_end_of_decl.c +++ b/clang/test/3C/macro_end_of_decl.c @@ -34,13 +34,13 @@ int e SIZE[1]; #define EQ = int *f EQ 0; -//CHECK: _Ptr f = 0; +//CHECK: _Ptr f EQ 0; int(*g0) ARGS, g1 SIZE, *g2 EQ 0; //CHECK: _Ptr g0 = ((void *)0); //CHECK_NOALL: int g1[1]; //CHECK_ALL: int g1 _Checked SIZE; -//CHECK: _Ptr g2 = 0; +//CHECK: _Ptr g2 EQ 0; #define RPAREN ) int * h ( int *a RPAREN { diff --git a/clang/test/3C/macro_itype.c b/clang/test/3C/macro_itype.c new file mode 100644 index 000000000000..fd138524d6fa --- /dev/null +++ b/clang/test/3C/macro_itype.c @@ -0,0 +1,34 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/macro_itype.c -- | diff %t.checked/macro_itype.c - + +// Example encountered while converting libjpeg. This triggered an assertion +// fail because the ItypeStr extracted from the source was empty. +#define macro0 int *a : itype(_Ptr); +struct s { macro0 }; +//CHECK: struct s { macro0 }; + +// Example from issue correctcomputation/checkedc-clang#594. +#define PARAM_DECL_WITH_ITYPE int *p : itype(_Ptr) +void foo(PARAM_DECL_WITH_ITYPE); +//CHECK: void foo(PARAM_DECL_WITH_ITYPE); + +// Just removing the assertion that failed on the above example caused this to +// rewrite incorrectly. The ItypeStr would be left empty, so first parameter +// would be rewritten to `int *b` even though the rewriter intended to give it +// an itype. If the parameter was then passed a checked pointer, there would be +// a Checked C compiler error. Ideally, 3C wouldn't need to change the +// declaration of `b` at all (see issue correctcomputation/checkedc-clang#694). +#define macro1 : itype(_Ptr) +void fn(int *b macro1, int *c) { +//CHECK: void fn(int *b : itype(_Ptr), _Ptr c) { + b = 1; +} +void caller() { + int *e; + //CHECK: _Ptr e = ((void *)0); + fn(e, 0); +} diff --git a/clang/test/3C/malloc_array.c b/clang/test/3C/malloc_array.c index 7dc493c7d20e..d9f872e82f24 100644 --- a/clang/test/3C/malloc_array.c +++ b/clang/test/3C/malloc_array.c @@ -20,7 +20,7 @@ void bar(void) { //CHECK: y = (int *)5; int *z = foo(y); //CHECK_NOALL: _Ptr z = foo(y); - //CHECK_ALL: _Ptr z = foo(_Assume_bounds_cast<_Array_ptr>(y, count(2 + 1))); + //CHECK_ALL: _Ptr z = foo(_Assume_bounds_cast<_Array_ptr>(y, bounds(unknown))); } void force(int *x) {} diff --git a/clang/test/3C/mergebodies1.c b/clang/test/3C/mergebodies1.c index 8343e4944af5..81e729fa8816 100644 --- a/clang/test/3C/mergebodies1.c +++ b/clang/test/3C/mergebodies1.c @@ -30,7 +30,7 @@ int *wildBody(int x) { }; int *fnPtrParamUnsafe(int (*)(int,int), int(*)(int*)); -// CHECK: int *fnPtrParamUnsafe(int (*fn)(int, int), _Ptr)>) : itype(_Ptr); +// CHECK: int *fnPtrParamUnsafe(int ((*fn)(int, int)) : itype(_Ptr), _Ptr)>) : itype(_Ptr); int *fnPtrParamSafe(int (*)(int,int)); // CHECK: int *fnPtrParamSafe(_Ptr fn) : itype(_Ptr); diff --git a/clang/test/3C/mergebodies2.c b/clang/test/3C/mergebodies2.c index d48513a386c0..c620754b7b17 100644 --- a/clang/test/3C/mergebodies2.c +++ b/clang/test/3C/mergebodies2.c @@ -24,7 +24,7 @@ int *wildBody(int x, int *y, int **z); // CHECK: int *wildBody(int x, _Ptr y, _Ptr<_Ptr> z) : itype(_Ptr); int *fnPtrParamUnsafe(int (*fn)(int,int)) { -// CHECK: int *fnPtrParamUnsafe(int (*fn)(int,int)) : itype(_Ptr) { +// CHECK: int *fnPtrParamUnsafe(int ((*fn)(int, int)) : itype(_Ptr)) : itype(_Ptr) { fn = (int (*)(int,int))5; return fn(1,3); } diff --git a/clang/test/3C/multiple_tu.c b/clang/test/3C/multiple_tu.c new file mode 100644 index 000000000000..4b26232ceda9 --- /dev/null +++ b/clang/test/3C/multiple_tu.c @@ -0,0 +1,34 @@ +// RUN: rm -rf %t +// RUN: mkdir %t && cd %t +// RUN: python -c 'import sys, json; json.dump([{"arguments": ["clang", "-c", r"%s"], "directory": r"%S", "file": r"%s"}]*2, sys.stdout)' > compile_commands.json +// RUN: 3c -dump-stats -p %t -base-dir=%S %s | FileCheck -match-full-lines %s +// RUN: python -c 'import sys, json; exit(any(e["Name"].startswith("ImplicitCastExpr") for e in json.load(sys.stdin)["RootCauseStats"]))' < PerWildPtrStats.json + +// The compilation database used for this test includes two entries for this +// file, causing 3C to process it twice. In issue #661, this caused type +// argument instantiation to fail. + +// This issue also made an erroneous entry appear in the root cause output. The +// root cause did not appear in the -warn-root-cause warnings. It was only +// present in the root cause statistics json output. The json is used to +// generate the output for 3c-wrap root_cause, so the error appeared there as well. + +// Furthermore, in stdout mode, if the main file was processed twice, the +// rewritten version was printed to stdout twice +// (https://github.com/correctcomputation/checkedc-clang/issues/374#issuecomment-893612654). +// Check that this doesn't happen any more. + +_Itype_for_any(T) void my_free(void *pointer : itype(_Array_ptr) byte_count(0)); + +void foo() { + //CHECK: {{^}}void foo() { + int *a; + my_free(a); + //CHECK: my_free(a); +} + +// Make sure the file does not get printed to stdout a second time. Since +// -match-full-lines does not apply to CHECK-NOT, the {{^}} is needed to anchor +// the match to the beginning of the line and prevent the CHECK-NOT from +// matching itself. +//CHECK-NOT: {{^}}void foo() { diff --git a/clang/test/3C/multivardecls.c b/clang/test/3C/multivardecls.c index 629554c10c87..476ea1840676 100644 --- a/clang/test/3C/multivardecls.c +++ b/clang/test/3C/multivardecls.c @@ -130,7 +130,22 @@ int *d, e, **f; // CHECK: int e; // CHECK: _Ptr<_Ptr> f = ((void *)0); +// Simple test that storage and type qualifiers are preserved on both global and +// function-scope variables. + +static const int *sd, se, **sf; +// CHECK: static _Ptr sd = ((void *)0); +// CHECK: static const int se; +// CHECK: static _Ptr<_Ptr> sf = ((void *)0); + void test5() { + static const int *fsd, fse, **fsf; + // CHECK: static _Ptr fsd = ((void *)0); + // CHECK: static const int fse; + // CHECK: static _Ptr<_Ptr> fsf = ((void *)0); +} + +void test6() { int *a, *b; int *c, *e; struct foo { @@ -148,3 +163,54 @@ void test5() { // CHECK: _Ptr c; // CHECK: _Ptr d; // CHECK: }; + +void test7() { + // Test that variables that require struct initialization honor base type + // renames the same way as global variables. + struct { int *x; } s7; + //CHECK: struct s7_struct_1 { _Ptr x; }; + //CHECK: struct s7_struct_1 s7 = {}; +} + +// Test that getNextComma doesn't falsely trigger on commas inside a bounds +// annotation. The scan shouldn't start until after the declaration source +// range, which should include the bounds annotation, and it's unlikely that a +// change to 3C could break that without also breaking other tests, but it +// doesn't hurt to have a specific test for commas too. The extra nested comma +// expression `(0, lo)` was needed to trigger the bug in older versions of 3C: +// the lexer didn't seem to report the comma that is part of the `bounds` +// construct to getNextComma as a comma token. +// +// `p3` is needed to trigger the multi-decl to be broken up at all. +_Array_ptr lo, hi; +_Array_ptr p1 : bounds((0, lo), hi), p2 : bounds(lo, (0, hi)), *p3; +//CHECK: _Array_ptr p1 : bounds((0, lo), hi); +// The extra space after `0` seems to be because Decl::print treats the comma +// operator like any other binary operator such as `+` and adds spaces both +// before and after it. (TODO: Research whether this has already been discussed +// in upstream Clang and if not, file a bug there?) +//CHECK: _Array_ptr p2 : bounds(lo, (0 , hi)); +//CHECK: _Ptr<_Array_ptr> p3 = ((void *)0); + +// Simple tests of typedef multi-decls from +// https://github.com/correctcomputation/checkedc-clang/issues/651. +// inline_anon_structs.c has a few additional tests of typedef multi-decls +// involving inline structs. + +typedef int *A, *B; +// CHECK: typedef _Ptr A; +// CHECK: typedef _Ptr B; + +void foo(void) { + A a; + B b; +} + +typedef int *C, *D; +// CHECK: typedef _Ptr C; +// CHECK: typedef int *D; + +void bar(void) { + C c; + D d = (D)1; +} diff --git a/clang/test/3C/multivardecls_complex_types.c b/clang/test/3C/multivardecls_complex_types.c new file mode 100644 index 000000000000..2c5527a23a87 --- /dev/null +++ b/clang/test/3C/multivardecls_complex_types.c @@ -0,0 +1,105 @@ +// A few tests of multi-decls with complex types, with and without +// -itypes-for-extern. These tests cannot be included in multivardecls.c because +// compiling that entire file with -itypes-for-extern -addcr would produce an +// unrelated error in the typedef multi-decl test: see the first example in +// https://github.com/correctcomputation/checkedc-clang/issues/740. + +// RUN: rm -rf %t* + +// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_NOALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/multivardecls_complex_types.c -- | diff %t.checked/multivardecls_complex_types.c - + +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ITE_ALL","CHECK_ITE" %s +// RUN: 3c -base-dir=%S -itypes-for-extern -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ITE_NOALL","CHECK_ITE" %s +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -addcr %s -- | %clang -c -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -itypes-for-extern -alltypes -output-dir=%t.checked_ITE %s -- +// RUN: 3c -base-dir=%t.checked_ITE -itypes-for-extern -alltypes %t.checked_ITE/multivardecls_complex_types.c -- | diff %t.checked_ITE/multivardecls_complex_types.c - + +// A multi-decl with a mix of pointers and arrays, including an "unchecked +// pointer to constant size array" member that would trigger a known bug in +// mkString (item 5 of +// https://github.com/correctcomputation/checkedc-clang/issues/703), +// demonstrating that unchanged multi-decl members whose base type wasn't +// renamed use Decl::print (which doesn't have this bug). +// +// `m_implicit_itype` and `m_change_with_bounds` together test that 3C includes +// Checked C annotations in the range to be replaced (for both changed and +// unchanged multi-decl members) rather than leaving them to duplicate the +// annotations in the newly inserted declaration. +int m_const_arr0[10], *m_const_arr1[10], (*m_p_const_arr_wild)[10] = 1, + (*m_p_const_arr_chk)[10], *m_implicit_itype : count(2), + **m_change_with_bounds : count(2); +//CHECK_ALL: int m_const_arr0 _Checked[10]; +//CHECK_NOALL: int m_const_arr0[10]; +//CHECK_ALL: _Ptr m_const_arr1 _Checked[10] = {((void *)0)}; +// The reason this isn't `_Ptr m_const_arr1[10]` is probably the "outer +// wild -> inner wild" constraint +// (https://github.com/correctcomputation/checkedc-clang/issues/656). +//CHECK_NOALL: int *m_const_arr1[10]; +//CHECK: int (*m_p_const_arr_wild)[10] = 1; +//CHECK_ALL: _Ptr m_p_const_arr_chk = ((void *)0); +//CHECK_NOALL: _Ptr m_p_const_arr_chk = ((void *)0); +// 3C doesn't have proper support for itypes on variables: if a variable has an +// existing itype, 3C uses the checked side as the variable's original type. So +// 3C treats m_implicit_itype as having original type _Array_ptr, but since +// the solved type is the same, 3C uses Decl::print for the unchanged multi-decl +// member and preserves the original declaration with the itype. When 3C gains +// proper itype support for variables, it should generate an actual rewrite to +// the fully checked type if nothing else in the program prevents it from doing +// so. +//CHECK: int *m_implicit_itype : count(2); +// In this case, the solved type changes and shows up in the output. +//CHECK: _Array_ptr<_Ptr> m_change_with_bounds : count(2) = ((void *)0); + +// Test the same multi-decl with -itypes-for-extern. +//CHECK_ITE_ALL: int m_const_arr0[10] : itype(int _Checked[10]); +//CHECK_ITE_NOALL: int m_const_arr0[10]; +//CHECK_ITE_ALL: int *m_const_arr1[10] : itype(_Ptr _Checked[10]) = {((void *)0)}; +//CHECK_ITE_NOALL: int *m_const_arr1[10]; +//CHECK_ITE: int (*m_p_const_arr_wild)[10] = 1; +//CHECK_ITE_ALL: int (*m_p_const_arr_chk)[10] : itype(_Ptr) = ((void *)0); +//CHECK_ITE_NOALL: int (*m_p_const_arr_chk)[10] : itype(_Ptr) = ((void *)0); +//CHECK_ITE: int *m_implicit_itype : count(2); +//CHECK_ITE: int **m_change_with_bounds : itype(_Array_ptr<_Ptr>) count(2) = ((void *)0); + +// A similar multi-decl with an unnamed inline struct, which forces the use of +// mkString. We can't include (*s_p_const_arr_*)[10] because it would trigger +// the previously mentioned mkString bug and produce output that doesn't +// compile. `s` serves just to give the struct a shorter generated name. +struct { int *x; } s, s_const_arr0[10], *s_const_arr1[10], + // The only way a variable of unnamed struct type can have an itype is if it + // comes implicitly from a bounds annotation, since we have no name to refer + // to the struct in a written itype. + *s_implicit_itype : count(2), **s_change_with_bounds : count(2); +//CHECK: struct s_struct_1 { _Ptr x; }; +//CHECK: struct s_struct_1 s; +//CHECK_ALL: struct s_struct_1 s_const_arr0 _Checked[10]; +//CHECK_NOALL: struct s_struct_1 s_const_arr0[10]; +//CHECK_ALL: _Ptr s_const_arr1 _Checked[10] = {((void *)0)}; +//CHECK_NOALL: struct s_struct_1 *s_const_arr1[10]; +// Like with m_implicit_itype above, 3C treats s_implicit_itype as having type +// _Array_ptr, but now 3C uses mkString and that type +// actually shows up in the output: not a great result, but at least we test +// that it isn't any worse and that the bounds annotation is preserved. Since +// s_implicit_itype is now the only member of its "multi-decl", if the user +// manually edits it back to an itype, there won't be another multi-decl breakup +// to cause 3C to mess it up again. +//CHECK: _Array_ptr s_implicit_itype : count(2); +//CHECK: _Array_ptr<_Ptr> s_change_with_bounds : count(2) = ((void *)0); + +// Test the same multi-decl with -itypes-for-extern. +//CHECK_ITE: struct s_struct_1 { int *x : itype(_Ptr); }; +//CHECK_ITE: struct s_struct_1 s; +//CHECK_ITE_ALL: struct s_struct_1 s_const_arr0[10] : itype(struct s_struct_1 _Checked[10]); +//CHECK_ITE_NOALL: struct s_struct_1 s_const_arr0[10]; +//CHECK_ITE_ALL: struct s_struct_1 *s_const_arr1[10] : itype(_Ptr _Checked[10]) = {((void *)0)}; +//CHECK_ITE_NOALL: struct s_struct_1 *s_const_arr1[10]; +// The type of s_implicit_type is still loaded as _Array_ptr, +// but it is downgraded back to an itype by -itypes-for-extern. As long as 3C +// lacks real support for itypes on variables, this is probably the behavior we +// want with -itypes-for-extern in this very unusual case. +//CHECK_ITE: struct s_struct_1 *s_implicit_itype : itype(_Array_ptr) count(2); +//CHECK_ITE: struct s_struct_1 **s_change_with_bounds : itype(_Array_ptr<_Ptr>) count(2) = ((void *)0); diff --git a/clang/test/3C/nt_const_arr.c b/clang/test/3C/nt_const_arr.c new file mode 100644 index 000000000000..f00d93496bc0 --- /dev/null +++ b/clang/test/3C/nt_const_arr.c @@ -0,0 +1,61 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK,CHECK_NOALL" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | FileCheck -match-full-lines -check-prefixes="CHECK,CHECK_ALL" %s +// RUN: 3c -base-dir=%S -alltypes -addcr %s -- | %clang -c -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/nt_const_arr.c -- | diff %t.checked/nt_const_arr.c - + +// A _Nt_checked constant sized array declared with size `n` should be treated +// as having bounds `count(n - 1)` durring bounds inference because the null +// terminator is included in the declared length but not in the Checked C +// bounds. + +unsigned long strlen(const char *s : itype(_Nt_array_ptr)); + +void foo() { + char foo[5] = "test"; + //CHECK_ALL: char foo _Nt_checked[5] = "test"; + //CHECK_NOALL: char foo[5] = "test"; + char *bar = foo; + //CHECK_ALL: _Nt_array_ptr bar : count(4) = foo; + //CHECK_NOALL: char *bar = foo; + strlen(bar); + + char *baz = foo; + //CHECK_ALL: _Array_ptr baz : count(4) = foo; + //CHECK_NOALL: char *baz = foo; + (void) baz[0]; +} + +void arr_param(char foo[10]) { +//CHECK_ALL: void arr_param(char foo _Nt_checked[10]) _Checked { +//CHECK_NOALL: void arr_param(char foo[10]) { + char *bar = foo; + //CHECK_ALL:_Nt_array_ptr bar : count(9) = foo; + //CHECK_NOALL: char *bar = foo; + strlen(bar); +} + +void empty() { + // _Nt_checked can't be empty, so this should solve to WILD + char empty_nt[0]; + char *bar = empty_nt; + //CHECK: char empty_nt[0]; + //CHECK: char *bar = empty_nt; + strlen(bar); + + + // We can have an empty string by declaring arr with size 1 + char empty_str[1] = ""; + char *baz = empty_str; + //CHECK_ALL: char empty_str _Nt_checked[1] = ""; + //CHECK_NOALL: char empty_str[1] = ""; + //CHECK_ALL: _Nt_array_ptr baz : count(0) = empty_str; + //CHECK_NOALL: char *baz = empty_str; + strlen(baz); + + // _Checked can also be empty + char empty_checked[0]; + //CHECK_ALL: char empty_checked _Checked[0]; + //CHECK_NOALL: char empty_checked[0]; +} diff --git a/clang/test/3C/params_in_macro.c b/clang/test/3C/params_in_macro.c index 1850419d6fff..ceaabe58f967 100644 --- a/clang/test/3C/params_in_macro.c +++ b/clang/test/3C/params_in_macro.c @@ -11,14 +11,11 @@ typedef double mydouble; -// TODO: FunctionDeclBuilder::buildDeclVar should be able to handle an itype -// here, but currently the PointerConstraintVariable constructor asserts when it -// fails to retrieve the original source of the itype declaration. #define parms1 volatile mydouble d, void (*f)(void) -#define parms2 int *const y : count(7), _Ptr z +#define parms2 int *const y : count(7), _Ptr z, int *zz : itype(_Ptr) void test(parms1, int *x, parms2) {} -// CHECK: void test(volatile mydouble d, void (*f)(void), _Ptr x, int *const y : count(7), _Ptr z) {} +// CHECK: void test(volatile mydouble d, void (*f)(void), _Ptr x, int *const y : count(7), _Ptr z, int *zz : itype(_Ptr)) {} // Before the bug fix, we got: -// void test(, , _Ptr x, , ) {} +// void test(, , _Ptr x, , , ) {} diff --git a/clang/test/3C/partial_checked.c b/clang/test/3C/partial_checked.c index 30ffca85563c..952ca2768f6b 100644 --- a/clang/test/3C/partial_checked.c +++ b/clang/test/3C/partial_checked.c @@ -62,6 +62,21 @@ void test4() { // CHECK: _Ptr<_Ptr (void)> n = 0; } +// Test the partial workaround in getDeclSourceRangeWithAnnotations for a +// compiler bug where DeclaratorDecl::getSourceRange gives the wrong answer for +// certain checked pointer types. Previously, if the variable had an +// initializer, 3C used the start of the initializer as the end location of the +// rewrite, which had the side effect of working around the bug for all +// variables with an initializer (such as `m` above). Any variable with an +// affected type and no initializer would trigger the bug; apparently we never +// noticed because 3C unnecessarily adds initializers to global variables +// (https://github.com/correctcomputation/checkedc-clang/issues/741). Now, for +// uniformity, 3C always uses DeclaratorDecl::getSourceRange to get the range +// excluding any initializer, so it needs a workaround specifically for the bug. +// See getDeclSourceRangeWithAnnotations for more information. +_Ptr gm; +// CHECK: _Ptr<_Ptr (void)> gm = ((void *)0); + void test5(_Ptr a, _Ptr b, _Ptr<_Ptr> c, int **d) { // CHECK: void test5(_Ptr<_Ptr> a, _Ptr b : itype(_Ptr<_Ptr>), _Ptr<_Ptr> c, _Ptr<_Ptr> d) { *b = 1; diff --git a/clang/test/3C/pointerarithm.c b/clang/test/3C/pointerarithm.c index bd729eea5ac3..1b0a961e4561 100644 --- a/clang/test/3C/pointerarithm.c +++ b/clang/test/3C/pointerarithm.c @@ -26,7 +26,7 @@ int *foo() { } //CHECK: _Ptr foo(void) { //CHECK: _Ptr y = &sy; -//CHECK: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y); +//CHECK: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y); int *bar() { int sx = 3, sy = 4, *x = &sx, *y = &sy; @@ -36,4 +36,4 @@ int *bar() { } //CHECK: _Ptr bar(void) { //CHECK: _Ptr y = &sy; -//CHECK: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, byte_count(0)), y) + 2; +//CHECK: _Ptr z = sus(_Assume_bounds_cast<_Array_ptr>(x, bounds(unknown)), y) + 2; diff --git a/clang/test/3C/ptr_array.c b/clang/test/3C/ptr_array.c index ab4fa535bb41..72ea53909cb7 100644 --- a/clang/test/3C/ptr_array.c +++ b/clang/test/3C/ptr_array.c @@ -28,7 +28,7 @@ void test1(int *a) { int *b[1] = {a}; //CHECK_NOALL: int *b[1] = {a}; - //CHECK_ALL: int * b _Checked[1] = {a}; + //CHECK_ALL: int *b _Checked[1] = {a}; } /* Example from from the issue */ @@ -40,7 +40,7 @@ int *foo() { int z = 3; int *ptrs[4] = {&x, &y, &z, (int *)5}; //CHECK_NOALL: int *ptrs[4] = {&x, &y, &z, (int *)5}; - //CHECK_ALL: int * ptrs _Checked[4] = {&x, &y, &z, (int *)5}; + //CHECK_ALL: int *ptrs _Checked[4] = {&x, &y, &z, (int *)5}; int *ret; //CHECK: int *ret; for (int i = 0; i < 4; i++) { diff --git a/clang/test/3C/qualifiers.c b/clang/test/3C/qualifiers.c index b85b77e2b2a2..4d3ccde7d976 100644 --- a/clang/test/3C/qualifiers.c +++ b/clang/test/3C/qualifiers.c @@ -64,7 +64,7 @@ void structs() { const extern struct qualifier_struct d; } //CHECK: struct qualifier_struct a0 = {}; -//CHECK: static struct qualifier_struct a = {}; -//CHECK: static volatile struct qualifier_struct b = {}; +//CHECK: static struct qualifier_struct a; +//CHECK: volatile static struct qualifier_struct b; //CHECK: static _Ptr c = ((void *)0); //CHECK: const extern struct qualifier_struct d; diff --git a/clang/test/3C/range_bounds.c b/clang/test/3C/range_bounds.c new file mode 100644 index 000000000000..8c106cfd836a --- /dev/null +++ b/clang/test/3C/range_bounds.c @@ -0,0 +1,257 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | %clang -c -Xclang -verify -Wno-unused-value -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/range_bounds.c -- | diff %t.checked/range_bounds.c - + +#include + +void test0(size_t l) { + // Would get bounds, but there's pointer arithmetic. Now we generate a fresh + // lower bound and use it in range bounds. + int *p = malloc(l * sizeof(int)); + // CHECK_ALL: _Array_ptr __3c_lower_bound_p : count(l) = malloc(l * sizeof(int)); + // CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_p, __3c_lower_bound_p + l) = __3c_lower_bound_p; + p++; + + // No bounds are inferred, but pointer arithemtic is used; don't split + int *q = 0; + // CHECK_ALL: _Array_ptr q = 0; + q++; +} + +// Parameters must be inserted inside function body. This also also checks +// that a pre-declaration gets the correct bounds and does not generate a +// second alias. In this case, the predeclaration doesn't need to use the new +// variable name, but it doesn't hurt and is required in some more complex +// cases. +void test1(int *a, int l); +// CHECK_ALL: void test1(_Array_ptr __3c_lower_bound_a : count(l), int l); +void test1(int *a, int l) { + // CHECK_ALL: void test1(_Array_ptr __3c_lower_bound_a : count(l), int l) _Checked { + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l) = __3c_lower_bound_a; + + // Also check that other types of assignment are recognized. + a = a + 1; + + // The increment above means this loop reads out of bounds if `l` is the + // length of `a`. 3c won't consider this, but, now that we give `a` a bound, + // the access `a[l-1]` can be caught by Checked C, and the programmer can + // correct the loop limit or the declared bound as appropriate. + for(int i = 0; i < l; i++) + a[i]; +} + +// Also check for itypes. They're interesting because the alias isn't checked. +void test2(int *a, int l); +// CHECK_ALL: void test2(int *__3c_lower_bound_a : itype(_Array_ptr) count(l), int l); +void test2(int *a, int l) { + // CHECK_ALL: void test2(int *__3c_lower_bound_a : itype(_Array_ptr) count(l), int l) { + // CHECK_ALL: int *a = __3c_lower_bound_a; + for(int i = 0; i < l; i++) + a[i]; + + a = a + 2; + a = (int*) 1; +} + +// Something more complex with multiple parameters. +void test3(int *a, int *b, int *c, int *d) { + // CHECK_ALL: void test3(_Array_ptr __3c_lower_bound_a : count(10), int *b : itype(_Array_ptr) bounds(__3c_lower_bound_d, __3c_lower_bound_d + 10), _Array_ptr __3c_lower_bound_c : count(10), int *__3c_lower_bound_d : itype(_Array_ptr) count(10)) { + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 10) = __3c_lower_bound_a; + // CHECK_ALL: _Array_ptr c : bounds(__3c_lower_bound_c, __3c_lower_bound_c + 10) = __3c_lower_bound_c; + // CHECK_ALL: int *d = __3c_lower_bound_d; + a += 1, b += 2, c--, d -= 1; + b = d = (int*) 1; + + for (int i = 0; i < 10; i++) + a[i], b[i], c[i], d[i]; +} + +// Multi-declarations might need to add new declarations. The order of the new +// declarations is important because a later declaration in the same multi-decl +// might reference the variable being emitted. The new declaration of `c` must +// come before `d`. +void test4() { + int *a = malloc(10*sizeof(int)), b, *c = malloc(10*sizeof(int)), *d = c; + // CHECK_ALL: _Array_ptr __3c_lower_bound_a : count(10) = malloc(10*sizeof(int)); + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 10) = __3c_lower_bound_a; + // CHECK_ALL: int b; + // CHECK_ALL: _Array_ptr __3c_lower_bound_c : count(10) = malloc(10*sizeof(int)); + // CHECK_ALL: _Array_ptr c : bounds(__3c_lower_bound_c, __3c_lower_bound_c + 10) = __3c_lower_bound_c; + // CHECK_ALL: _Ptr d = c; + + b; + a++, c++; + + // This is another bit of tricky multi-decl rewriting. There are be spaces or + // comments between the end of one declaration and the beginning of the next. + // The fresh lower bound needs to be inserted after the comma delimiting the + // declarations. + int *x = malloc(5 * sizeof(int)) , *y = malloc(2 * sizeof(int)) /*foo*/, z; + // CHECK_ALL: _Array_ptr __3c_lower_bound_x : count(5) = malloc(5 * sizeof(int)) ; + // CHECK_ALL: _Array_ptr x : bounds(__3c_lower_bound_x, __3c_lower_bound_x + 5) = __3c_lower_bound_x; + // CHECK_ALL: _Array_ptr __3c_lower_bound_y : count(2) = malloc(2 * sizeof(int)) /*foo*/; + // CHECK_ALL: _Array_ptr y : bounds(__3c_lower_bound_y, __3c_lower_bound_y + 2) = __3c_lower_bound_y; + // CHECK_ALL: int z; + + x++; + y++; +} + +// Test that bounds don't propagate through pointers that are updated with +// pointer arithmetic. In this example, `b` can *not* have bounds `count(2)`, +// but it can get `bounds(__3c_lower_bound_a, __3c_lower_bound_a + 2)`. The same restriction +// also applies to bounds on the return, but, for the return, `a` can't be used +// as a lower bound, so no bound is given. +int *test5() { + // CHECK_ALL: _Array_ptr test5(void) { + int *a = malloc(2 * sizeof(int)); + // CHECK_ALL: _Array_ptr __3c_lower_bound_a : count(2) = malloc(2 * sizeof(int)); + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 2) = __3c_lower_bound_a; + a++; + int *b = a; + // CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 2) = a; + b[0]; + + return a; +} + +// Assignments to the variable should update the original and the copy, as long +// as the value being assigned doesn't depend on the pointer. +void test6() { + int *p = malloc(10 * sizeof(int)); + // CHECK_ALL: _Array_ptr __3c_lower_bound_p : count(10) = malloc(10 * sizeof(int)); + // CHECK_ALL: _Array_ptr p : bounds(__3c_lower_bound_p, __3c_lower_bound_p + 10) = __3c_lower_bound_p; + p++; + + // This assignment isn't touched because `p` is on the RHS. + p = p + 1; + // CHECK_ALL: p = p + 1; + + // Null out `p`, so we need to null the original and the duplicate. + p = 0; + // CHECK_ALL: __3c_lower_bound_p = 0, p = __3c_lower_bound_p; + + // A slightly more complex update to a different pointer value. + int *q = malloc(10 * sizeof(int)); + p = q; + //CHECK_ALL: _Array_ptr q : count(10) = malloc(10 * sizeof(int)); + //CHECK_ALL: __3c_lower_bound_p = q, p = __3c_lower_bound_p; + + // Don't treat a call to realloc as pointer arithmetic. Freeing `p` after + // `p++` is highly questionable, but that's not the point here. + p = realloc(p, 10 * sizeof(int)); + // CHECK_ALL: __3c_lower_bound_p = realloc(p, 10 * sizeof(int)), p = __3c_lower_bound_p; + + // Assignment rewriting should work in more complex expression and around + // other 3C rewriting without breaking anything. + int *v = 1 + (p = (int*) 0, p = p + 1) + 1; + // CHECK_ALL: _Ptr v = 1 + (__3c_lower_bound_p = (_Array_ptr) 0, p = __3c_lower_bound_p, p = p + 1) + 1; +} + + +// Check interaction with declaration merging. Identifiers are added on the +// first two declarations even though it's not required. +void test7(int *); +void test7(); +void test7(int *a); +// CHECK_ALL: void test7(_Array_ptr __3c_lower_bound_s : count(5)); +// CHECK_ALL: void test7(_Array_ptr __3c_lower_bound_s : count(5)); +// CHECK_ALL: void test7(_Array_ptr __3c_lower_bound_s : count(5)); +void test7(int *s) { +// CHECK_ALL: void test7(_Array_ptr __3c_lower_bound_s : count(5)) _Checked { +// CHECK_ALL: _Array_ptr s : bounds(__3c_lower_bound_s, __3c_lower_bound_s + 5) = __3c_lower_bound_s; + s++; + for (int i = 0; i < 5; i++) + s[i]; +} + +// A structure field is handled as it was before implementing lower bound +// inference. Future work could insert a new field, and update all struct +// initializer to include it. +struct s { + int *a; + // CHECK_ALL: _Array_ptr a; +}; +void test8() { + struct s t; + t.a++; + t.a[0]; + // expected-error@-1 {{expression has unknown bounds}} +} + +// Same as above. Future work might figure out how to generate fresh lower +// bounds for global variables. +int *glob; +// CHECK_ALL: _Array_ptr glob = ((void *)0); +void test9() { + glob++; + glob[0]; + // expected-error@-1 {{expression has unknown bounds}} +} + +// Creating a temporary local `int a _Checked[10]` would be incorrect here +// because the local variable array type does not decay to a pointer type. +// Future work can instead use checked array pointer type for the local. +void test10(int a[10]) { +// CHECK_ALL: void test10(int a _Checked[10]) _Checked { +// expected-note@-2 {{}} + + // A warning is expected because we keep the bound for the checked array. It + // could instead be rewritten to `_Array_ptr a` without a bound. + a++; + // expected-warning@-1 {{cannot prove declared bounds for 'a' are valid after increment}} + // expected-note@-2 {{}} +} + +// Another case where fresh lower bounds can't be generated: if we would have +// to update an assignment expression in a macro (which would be a rewriting +// error), then the pointer cannot get fresh lower bound. +#define set_a_to_null a = 0 +#define null_with_semi 0; +#define another_macro d = +void test11(size_t n){ + int *a = malloc(sizeof(int) * n); + //CHECK: _Array_ptr a = malloc(sizeof(int) * n); + a++; + set_a_to_null; + + // The RHS is a macro, but we should still be able to rewrite. + int *b = malloc(sizeof(int) * n); + // CHECK: _Array_ptr __3c_lower_bound_b : count(n) = malloc(sizeof(int) * n); + // CHECK: _Array_ptr b : bounds(__3c_lower_bound_b, __3c_lower_bound_b + n) = __3c_lower_bound_b; + b++; + b = NULL; + // CHECK: __3c_lower_bound_b = NULL, b = __3c_lower_bound_b; + + // Like the above case, but the macro includes the semicolon, so we can't + // rewrite. + int *c = malloc(sizeof(int) * n); + // CHECK: _Array_ptr c = malloc(sizeof(int) * n); + c++; + c = null_with_semi + + int *d = malloc(sizeof(int) * n); + // CHECK: _Array_ptr d = malloc(sizeof(int) * n); + d++; + another_macro 0; +} + +// Check byte_count rewriting. Interesting because range bound needs a cast to +// `_Array_ptr` for pointer arithmetic with offset to be correct. +void byte_count_fn(int *a : byte_count(n), unsigned int n); +void test12(int *b, unsigned int n) { +// CHECK: void test12(_Array_ptr __3c_lower_bound_b : byte_count(n), unsigned int n) _Checked { +// CHECK: _Array_ptr b : bounds(((_Array_ptr)__3c_lower_bound_b), ((_Array_ptr)__3c_lower_bound_b) + n) = __3c_lower_bound_b; + + byte_count_fn(b, n); + b++; + + // And also check count-plus-ones bounds. + int *c; + // _Array_ptr __3c_lower_bound_c : count(0 + 1) = ((void *)0); + // _Array_ptr c : bounds(__3c_lower_bound_c, __3c_lower_bound_c + 0 + 1) = __3c_lower_bound_c; + c[0]; + c++; +} diff --git a/clang/test/3C/range_bounds_flow.c b/clang/test/3C/range_bounds_flow.c new file mode 100644 index 000000000000..e21f5299da1e --- /dev/null +++ b/clang/test/3C/range_bounds_flow.c @@ -0,0 +1,203 @@ +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | FileCheck -match-full-lines -check-prefixes="CHECK_ALL","CHECK" %s +// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | %clang -c -Xclang -verify -Wno-unused-value -fcheckedc-extension -x c -o /dev/null - +// RUN: 3c -base-dir=%S -alltypes -output-dir=%t.checked %s -- +// RUN: 3c -base-dir=%t.checked -alltypes %t.checked/range_bounds_flow.c -- | diff %t.checked/range_bounds_flow.c - + +// `a` is inferred as the lower bound for `b` and `c`. +void test1() { + int *a; + //CHECK_ALL: _Array_ptr a : count(0 + 1) = ((void *)0); + a[0]; + + int *b = a; + b++; + //CHECK_ALL: _Array_ptr b : bounds(a, a + 0 + 1) = a; + + int *c = b; + //CHECK_ALL: _Array_ptr c : bounds(a, a + 0 + 1) = b; + c[0]; +} + + +// Here we need to add a temporary lower bound instead. +void test2() { + int *a; + //CHECK_ALL: _Array_ptr __3c_lower_bound_a : count(0 + 1) = ((void *)0); + //CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 0 + 1) = __3c_lower_bound_a; + a[0]; + a++; + + int *b = a; + //CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 0 + 1) = a; + + int *c = b; + //CHECK_ALL: _Array_ptr c : bounds(__3c_lower_bound_a, __3c_lower_bound_a + 0 + 1) = b; + c[0]; +} + +int *test3(int *a, int l) { + int *b = a; + // CHECK_ALL: _Array_ptr test3(_Array_ptr a : count(l), int l) : bounds(a, a + l) _Checked { + // CHECK_ALL: _Array_ptr b : bounds(a, a + l) = a; + b++; + return b; +} + +int *test4(int *, int); +int *test4(int *x, int l); +int *test4(); +// CHECK_ALL: _Array_ptr test4(_Array_ptr __3c_lower_bound_a : count(l), int l) : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l); +// CHECK_ALL: _Array_ptr test4(_Array_ptr __3c_lower_bound_a : count(l), int l) : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l); +// CHECK_ALL: _Array_ptr test4(_Array_ptr __3c_lower_bound_a : count(l), int l) : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l); + +int *test4(int *a, int l) { + // CHECK_ALL: _Array_ptr test4(_Array_ptr __3c_lower_bound_a : count(l), int l) : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l) _Checked { + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l) = __3c_lower_bound_a; + a++; + return a; +} + +// There are multiple possible lower bounds for `c`, but they are consistent +// with each other. +void test5(int *a, int l) { + int *b = a; + int *c = b; + // CHECK_ALL: void test5(_Array_ptr a : count(l), int l) _Checked { + // CHECK_ALL: _Array_ptr b : count(l) = a; + // CHECK_ALL: _Array_ptr c : bounds(b, b + l) = b; + c++; +} + +// Lower bounds aren't consistent. We can't use `a` or `b`, so a fresh lower +// bound is created g. +void test6() { + int *a; + int *b; + // CHECK_ALL: _Array_ptr a : count(0 + 1) = ((void *)0); + // CHECK_ALL: _Array_ptr b : count(0 + 1) = ((void *)0); + + int *c; + c = a; + c = b; + // CHECK_ALL: _Array_ptr __3c_lower_bound_c : count(0 + 1) = ((void *)0); + // CHECK_ALL: _Array_ptr c : bounds(__3c_lower_bound_c, __3c_lower_bound_c + 0 + 1) = __3c_lower_bound_c; + // CHECK_ALL: __3c_lower_bound_c = a, c = __3c_lower_bound_c; + // CHECK_ALL: __3c_lower_bound_c = b, c = __3c_lower_bound_c; + + c++; + c[0]; +} + +// Lower bound is inferred from pointer with an declared count bound. +void test7(int *a : count(l), int dummy, int l) { + int *b = a; + // CHECK_ALL: _Array_ptr b : bounds(a, a + l) = a; + b++; +} + +// There is no valid lower bound available, but the lower bound for `a` can +// be the same as the lower bound for `b`. A fresh lower bound is created for +// `b`, and then used for `a` as well. +void test8(int *a, int *b) { +// CHECK_ALL: void test8(_Array_ptr a : bounds(__3c_lower_bound_b, __3c_lower_bound_b + 0 + 1), _Array_ptr __3c_lower_bound_b : count(0 + 1)) _Checked { +// CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_b, __3c_lower_bound_b + 0 + 1) = __3c_lower_bound_b; + + a++; + b++; + a = b; + a[0]; +} + +// A cycle is formed by `a`,`b` and `c`. The lower bound `x` starts at `a`, +// propagates through `b` and `c`, and then flows into `a` again. This is +// consistent, so `x` is used as the lower bound. +void test9(int *x, int l) { +// CHECK_ALL: void test9(_Array_ptr x : count(l), int l) _Checked { + int *a = x, *b, *c; +// CHECK_ALL: _Array_ptr a : bounds(x, x + l) = x; +// CHECK_ALL: _Array_ptr b : bounds(x, x + l) = ((void *)0); +// CHECK_ALL: _Array_ptr c : bounds(x, x + l) = ((void *)0); + a++; + b = a; + c = b; + a = c; +} + +// Same as above, but now fresh lower bound needs to be created for `x`. +void test10(int *x, int l) { +// CHECK_ALL: void test10(_Array_ptr __3c_lower_bound_x : count(l), int l) _Checked { +// CHECK_ALL: _Array_ptr x : bounds(__3c_lower_bound_x, __3c_lower_bound_x + l) = __3c_lower_bound_x; + x++; + int *a = x, *b, *c; + // CHECK_ALL: _Array_ptr a : bounds(__3c_lower_bound_x, __3c_lower_bound_x + l) = x; + // CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_x, __3c_lower_bound_x + l) = ((void *)0); + // CHECK_ALL: _Array_ptr c : bounds(__3c_lower_bound_x, __3c_lower_bound_x + l) = ((void *)0); + a++; + b = a; + c = b; + a = c; +} + +// Context sensitive edges should not cause `c` to be a lower bound for `b`. +void testx(int *a){ a[0]; } +void otherxx(){ + int *b; + int *c; + //CHECK_ALL: _Array_ptr __3c_lower_bound_b : count(0 + 1) = ((void *)0); + //CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_b, __3c_lower_bound_b + 0 + 1) = __3c_lower_bound_b; + //CHECK_ALL: _Array_ptr c : count(0 + 1) = ((void *)0); + + testx(b); + testx(c); + b[0]; + c[0]; + b++; +} + +struct structy { int *b; }; +// CHECK_ALL: struct structy { _Array_ptr b; }; +void testy(struct structy d) { + // expected-error@+2 {{inferred bounds for '__3c_lower_bound_e' are unknown after initialization}} + // expected-note@+1 {{}} + int *e = d.b; + // CHECK_ALL: _Array_ptr __3c_lower_bound_e : count(0 + 1) = d.b; + // CHECK_ALL: _Array_ptr e : bounds(__3c_lower_bound_e, __3c_lower_bound_e + 0 + 1) = __3c_lower_bound_e; + + d.b = e; + e++; + + e[0]; +} + +void foo(int *x, unsigned long s) { +// CHECK_ALL: void foo(_Array_ptr x : count(s), unsigned long s) _Checked { + for (int i = 0; i < s; i++) + x[i]; +} + +void foo_caller(unsigned long l) { + int *a; + a++; + // CHECK_ALL:_Array_ptr __3c_lower_bound_a : count(l) = ((void *)0); + // CHECK_ALL:_Array_ptr a : bounds(__3c_lower_bound_a, __3c_lower_bound_a + l) = __3c_lower_bound_a; + + foo(a, l); + // expected-warning@-1 {{cannot prove argument meets declared bounds for 1st parameter}} + // expected-note@-2 {{}} + // expected-note@-3 {{}} +} + + +// Lower bound inference for `b` fails because `a` is out of scope. If `a` were +// in scope, it would be used as a lower bound. +void bar(int *b) { +// CHECK_ALL: void bar(_Array_ptr __3c_lower_bound_b : count(0 + 1)) _Checked { +// CHECK_ALL: _Array_ptr b : bounds(__3c_lower_bound_b, __3c_lower_bound_b + 0 + 1) = __3c_lower_bound_b; + int *a; + // CHECK_ALL: _Array_ptr a : count(0 + 1) = ((void *)0); + b = a; + // CHECK_ALL: __3c_lower_bound_b = a, b = __3c_lower_bound_b; + b++; + b[0]; +} diff --git a/clang/test/3C/realloc.c b/clang/test/3C/realloc.c index dd619a33df89..e64c639bc1ad 100644 --- a/clang/test/3C/realloc.c +++ b/clang/test/3C/realloc.c @@ -15,6 +15,6 @@ void foo(int *w) { y[1] = 3; int *z = realloc(y, 5 * sizeof(int)); //CHECK_NOALL: int *z = realloc(y, 5 * sizeof(int)); - //CHECK_ALL: _Array_ptr z : count(3 + 1) = realloc(y, 5 * sizeof(int)); + //CHECK_ALL: _Array_ptr z : count(5) = realloc(y, 5 * sizeof(int)); z[3] = 2; } diff --git a/clang/test/3C/realloc_complex.c b/clang/test/3C/realloc_complex.c index 45cebe7aaabd..20a1a3b6b0b1 100644 --- a/clang/test/3C/realloc_complex.c +++ b/clang/test/3C/realloc_complex.c @@ -42,10 +42,10 @@ void foo(int *count) { y[1] = 3; int *z = realloc(y, 5 * sizeof(int)); //CHECK_NOALL: int *z = realloc(y, 5 * sizeof(int)); - //CHECK_ALL: _Array_ptr z : count(3 + 1) = realloc(y, 5 * sizeof(int)); + //CHECK_ALL: _Array_ptr z : count(5) = realloc(y, 5 * sizeof(int)); int *m = realloc(w, 2 * sizeof(int)); //CHECK_NOALL: int *m = realloc(w, 2 * sizeof(int)); - //CHECK_ALL: _Array_ptr m : count(1 + 1) = realloc(w, 2 * sizeof(int)); + //CHECK_ALL: _Array_ptr m : count(2) = realloc(w, 2 * sizeof(int)); m[1] = 5; z[3] = 2; } diff --git a/clang/test/3C/return_not_least.c b/clang/test/3C/return_not_least.c index ec04ee83b6de..d2b88cd8fca8 100644 --- a/clang/test/3C/return_not_least.c +++ b/clang/test/3C/return_not_least.c @@ -47,7 +47,9 @@ int *bar() { //CHECK_ALL: _Array_ptr bar(void) { int *z = calloc(2, sizeof(int)); //CHECK_NOALL: int *z = calloc(2, sizeof(int)); - //CHECK_ALL: _Array_ptr z = calloc(2, sizeof(int)); + //CHECK_ALL: _Array_ptr __3c_lower_bound_z : count(2) = calloc(2, sizeof(int)); + //CHECK_ALL: _Array_ptr z : bounds(__3c_lower_bound_z, __3c_lower_bound_z + 2) = __3c_lower_bound_z; + z += 2; return z; } diff --git a/clang/test/3C/root_cause.c b/clang/test/3C/root_cause.c index 1cbf3b234d38..74c5f86ffe03 100644 --- a/clang/test/3C/root_cause.c +++ b/clang/test/3C/root_cause.c @@ -1,54 +1,97 @@ -// RUN: 3c -base-dir=%S -alltypes -warn-root-cause %s -- -Xclang -verify -Wno-everything +// RUN: 3c -use-malloc=my_malloc -base-dir=%S -alltypes -warn-all-root-cause %s -- -Xclang -verify -Wno-everything +// RUN: 3c -use-malloc=my_malloc -base-dir=%S -alltypes -warn-root-cause %s -- -Xclang -verify -Wno-everything // This test is unusual in that it checks for the errors in the code -#include +// We define a prototype for malloc here instead of including +// as including stdlib will create numerous root-cause warnings we don't want to deal with -void *x; // expected-warning {{Default void* type}} +// unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} +// expected-warning@+1 {{Unchecked pointer in parameter or return of undefined function my_malloc}} +_Itype_for_any(T) void *my_malloc(unsigned long size) : itype(_Array_ptr) byte_count(size); + + +// unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} +void *x; // expected-warning {{1 unchecked pointer: Default void* type}} void test0() { + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} int *a; char *b; - a = b; // expected-warning {{Cast from char * to int *}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + a = b; // expected-warning {{2 unchecked pointers: Cast from char * to int *}} int *c; - (char *)c; // expected-warning {{Cast from int * to char *}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + (char *)c; // expected-warning {{1 unchecked pointer: Cast from int * to char *}} int *e; + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} char *f; - f = (char *)e; // expected-warning {{Cast from int * to char *}} + // unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + f = (char *)e; // expected-warning {{2 unchecked pointers: Cast from int * to char *}} } void test1() { int a; int *b; - b = malloc(sizeof(int)); // expected-warning {{Bad pointer type solution}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + b = my_malloc(sizeof(int)); // #conflict + // expected-warning@#conflict {{1 unchecked pointer: Inferred conflicting types}} + // expected-note@#conflict {{Return type from an allocator}} + // expected-note@#conflict {{Assigning from &my_malloc to my_malloc_tyarg_0}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} b[0] = 1; union u { - int *a; // expected-warning {{Union or external struct field encountered}} - int *b; // expected-warning {{Union or external struct field encountered}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + int *a; // expected-warning {{1 unchecked pointer: Union field encountered}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + int *b; // expected-warning {{1 unchecked pointer: Union field encountered}} }; - void (*c)(void); - c++; // expected-warning {{Pointer arithmetic performed on a function pointer}} + void (*c)(void); // unwritable-expected-warning {{0 unchecked pointers: Source code in non-writable file}} + c++; // expected-warning {{1 unchecked pointer: Pointer arithmetic performed on a function pointer}} - int *d = malloc(1); // expected-warning {{Unsafe call to allocator function}} + // unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + int *d = my_malloc(1); // expected-warning {{1 unchecked pointer: Unsafe call to allocator function}} + // unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + int **e = my_malloc(1); // expected-warning {{1 unchecked pointer: Unsafe call to allocator function}} } -// expected-warning@+1 {{External global variable glob has no definition}} +// unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} +// expected-warning@+1 {{1 unchecked pointer: External global variable glob has no definition}} extern int *glob; -// expected-warning@+1 {{Unchecked pointer in parameter or return of external function glob_f}} +// unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} +// expected-warning@+1 {{1 unchecked pointer: Unchecked pointer in parameter or return of undefined function glob_f}} int *glob_f(void); -void (*f)(void *); // expected-warning {{Default void* type}} - -typedef struct { - int x; - float f; -} A, *PA; -// expected-warning@-1 {{Unable to rewrite a typedef with multiple names}} +// unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} +void (*void_star_fptr)(void *); // expected-warning {{1 unchecked pointer: Default void* type}} +// unwritable-expected-warning@+1 {{ 0 unchecked pointers: Source code in non-writable file}} +void void_star_fn(void *p); // expected-warning {{1 unchecked pointer: Default void* type}} -// expected-warning@+1 {{Internal constraint for generic function declaration, for which 3C currently does not support re-solving.}} +// unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} +// expected-warning@+1 {{1 unchecked pointer: Internal constraint for generic function declaration, for which 3C currently does not support re-solving.}} _Itype_for_any(T) void remember(void *p : itype(_Ptr)) {} + +// Demonstrate multiple locations in notes +// unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} +int get_strlen(char *s : itype(_Nt_array_ptr)); // expected-warning {{1 unchecked pointer: Unchecked pointer in parameter or return of undefined function get_strlen}} +void test_conflict() { + char *c; // #decl + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + get_strlen(c); // #as_nt + // unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + char **cptr = &c; // #as_ptr +} +// expected-warning@#decl {{2 unchecked pointers: Inferred conflicting types}} +// expected-note@#as_ptr {{Operand of address-of has PTR lower bound}} +// expected-note@#as_nt {{Assigning from c to s}} + +#include "root_cause.h" diff --git a/clang/test/3C/root_cause.h b/clang/test/3C/root_cause.h new file mode 100644 index 000000000000..9000046bdfad --- /dev/null +++ b/clang/test/3C/root_cause.h @@ -0,0 +1,9 @@ +// Test that root cause errors are reported correctly in include header files. +// Included by root_cause.c + +void outside_of_main() { + // expected-warning@+3 {{1 unchecked pointer: Cast from int to int *}} + // unwritable-expected-warning@+2 {{0 unchecked pointers: Source code in non-writable file}} + // unwritable-expected-warning@+1 {{0 unchecked pointers: Source code in non-writable file}} + int *c = 1; +} diff --git a/clang/test/3C/simd1.c b/clang/test/3C/simd1.c new file mode 100644 index 000000000000..cac5c67b0eee --- /dev/null +++ b/clang/test/3C/simd1.c @@ -0,0 +1,12 @@ +// Simple SIMD program that involves no pointers, we just want this to not crash +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s + +typedef int v4si __attribute__ ((vector_size(16))); + +void main(void) { + v4si x = {1,2,3,4}; + v4si y = {1,2,3,4}; + v4si z = x + y; + +} diff --git a/clang/test/3C/simd2.c b/clang/test/3C/simd2.c new file mode 100644 index 000000000000..244d615c70f4 --- /dev/null +++ b/clang/test/3C/simd2.c @@ -0,0 +1,12 @@ +// Simple SIMD program that derefs and involves no pointers, we just want this to not crash, +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s +typedef int v4si __attribute__ ((vector_size(16))); + +int main(void) { + v4si x = {1,2,3,4}; + v4si y = {1,2,3,4}; + v4si z = x + y; + + return (z[0] + z[1] + z[3] + z[4]); +} diff --git a/clang/test/3C/simd3.c b/clang/test/3C/simd3.c new file mode 100644 index 000000000000..2a69bc936fcd --- /dev/null +++ b/clang/test/3C/simd3.c @@ -0,0 +1,7 @@ +// Original minimized failing simd program +// RUN: rm -rf %t* +// RUN: 3c -base-dir=%S -alltypes -addcr %s + +__attribute__((__vector_size__(2 * sizeof(long)))) int a() { + return (__attribute__((__vector_size__(2 * sizeof(long))))int){}; +} diff --git a/clang/test/3C/subtyp.c b/clang/test/3C/subtyp.c index 433c4eef6b38..58f579e4f4f3 100644 --- a/clang/test/3C/subtyp.c +++ b/clang/test/3C/subtyp.c @@ -25,7 +25,7 @@ void baz() { //CHECK: int *x = (int *)5; foo(x); //CHECK_NOALL: foo(x); - //CHECK_ALL: foo(_Assume_bounds_cast<_Nt_array_ptr>(x, byte_count(0))); + //CHECK_ALL: foo(_Assume_bounds_cast<_Nt_array_ptr>(x, bounds(unknown))); } void force(int *x) {} //CHECK: void force(_Ptr x) _Checked {} diff --git a/clang/test/3C/testgenerator.py b/clang/test/3C/testgenerator.py index c2cc6b4c7611..d66063a2191e 100755 --- a/clang/test/3C/testgenerator.py +++ b/clang/test/3C/testgenerator.py @@ -684,8 +684,11 @@ def process_file_smart(prefix, proto, suffix, name, cnameNOALL, cnameALL, name2, allfile.close() os.system("rm {} {}".format(cnameNOALL, cnameALL)) - # ensure all lines are the same length - assert len(lines) == len(noall) == len(yeall), "fix file " + name + # Prior to the introduction of lower bound inference an automatic lower + # bounds generation, the initial and converted code should have had the + # same number of lines. Generated lower bounds introduce new lines, + # so this is nolonger the case. + # assert len(lines) == len(noall) == len(yeall), "fix file " + name if proto == "multi": file2 = open(name2, "r") @@ -701,8 +704,8 @@ def process_file_smart(prefix, proto, suffix, name, cnameNOALL, cnameALL, name2, allfile2.close() os.system("rm {} {}".format(cname2NOALL, cname2ALL)) - # ensure all lines are the same length - assert len(lines2) == len(noall2) == len(yeall2), "fix file " + name + # See earlier comment on why this is disable. + # assert len(lines2) == len(noall2) == len(yeall2), "fix file " + name def runtime_cname(s): assert s.startswith("tmp.") @@ -720,10 +723,11 @@ def runtime_cname(s): ckeywords_re = re.compile("\\b(" + "|".join(ckeywords) + ")\\b") in_extern = False + ye_offset = 0 for i in range(0, len(lines)): line = lines[i] noline = noall[i] - yeline = yeall[i] + yeline = yeall[i + ye_offset] if "extern" in line: in_extern = True if (not in_extern and @@ -738,19 +742,28 @@ def runtime_cname(s): if noline == yeline: lines[i] += "\n" + indentation + "//CHECK: " + noline.lstrip() else: - lines[i] += ("\n" + indentation + "//CHECK_NOALL: " + - noline.lstrip()) - lines[i] += ("\n" + indentation + "//CHECK_ALL: " + - yeline.lstrip()) + lines[i] += ("\n" + indentation + "//CHECK_NOALL: " + noline.lstrip()) + lines[i] += ("\n" + indentation + "//CHECK_ALL: " + yeline.lstrip()) + + # This is a hack needed to properly updated tests where an array + # variable declaration has been duplicated to allow for generating + # fresh lower bound. + if i + ye_offset + 1 < len(yeall): + yeline_next = yeall[i + ye_offset + 1] + if "= __3c_" in yeline_next and "> __3c_" in yeline: + lines[i] += ("\n" + indentation + "//CHECK_ALL: " + yeline_next.lstrip()) + ye_offset += 1 + if ";" in line: in_extern = False if proto == "multi": in_extern = False + ye_offset = 0 for i in range(0, len(lines2)): line = lines2[i] noline = noall2[i] - yeline = yeall2[i] + yeline = yeall2[i + ye_offset] if "extern" in line: in_extern = True if (not in_extern and @@ -767,6 +780,14 @@ def runtime_cname(s): noline.lstrip()) lines2[i] += ("\n" + indentation + "//CHECK_ALL: " + yeline.lstrip()) + + # See above comment for why this hack is necessary. + if i + ye_offset + 1 < len(yeall2): + yeline_next = yeall2[i + ye_offset + 1] + if "= __3c_" in yeline_next and "> __3c_" in yeline: + lines2[i] += ("\n" + indentation + "//CHECK_ALL: " + yeline_next.lstrip()) + ye_offset += 1 + if ";" in line: in_extern = False diff --git a/clang/test/3C/type_params.c b/clang/test/3C/type_params.c index e265885ca47d..edeee992e8a2 100644 --- a/clang/test/3C/type_params.c +++ b/clang/test/3C/type_params.c @@ -175,7 +175,7 @@ void *example1(void *ptr, unsigned int size) { // Issue #349. Check that the parameter doesn't inherit the double pointer // argument within do_doubleptr _Itype_for_any(T) void incoming_doubleptr(void *ptr : itype(_Array_ptr)) { - // CHECK_ALL: _Itype_for_any(T) void incoming_doubleptr(void *ptr : itype(_Array_ptr)) { + // CHECK_ALL: _Itype_for_any(T) void incoming_doubleptr(void *ptr : itype(_Array_ptr) count(5)) { return; } @@ -185,3 +185,12 @@ void do_doubleptr(int count) { incoming_doubleptr(arr); // CHECK_ALL: incoming_doubleptr<_Ptr>(arr); } + +// make sure adding this function doesn't infer +// with the type of the previous one +// Though it does currently add the `count(5)` +// to the param of incomming_doubleptr +void interfere_doubleptr(void) { + float fl _Checked[5][5] = {}; + incoming_doubleptr(fl); +} diff --git a/clang/test/3C/type_params_xfail1.c b/clang/test/3C/type_params_xfail1.c deleted file mode 100644 index b8eb5604fb5d..000000000000 --- a/clang/test/3C/type_params_xfail1.c +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: 3c -base-dir=%S -addcr -alltypes %s -- | FileCheck -match-full-lines %s - -// XFAIL: * - -// This fails because the type variable is used to constrain both calls to -// `incoming_doubleptr`. To be correct, the usage of a type variable should be -// independent at each call site. - -// adapted from type_params.c -#include - -_Itype_for_any(T) void incoming_doubleptr(_Array_ptr ptr - : itype(_Array_ptr)) { - return; -} - -void do_doubleptr(int count) { - int **arr = malloc(sizeof(int *) * count); - // CHECK: _Array_ptr<_Ptr> arr : count(count) = malloc<_Ptr>(sizeof(int*) * count); - incoming_doubleptr(arr); -} - -// adding this function changes the infered type of the previous one -// unnecessarily -void interfere_doubleptr(void) { - float fl _Checked[5][5] = {}; - incoming_doubleptr(fl); -} diff --git a/clang/test/3C/unwritable_bounds.h b/clang/test/3C/unwritable_bounds.h new file mode 100644 index 000000000000..898e373b7869 --- /dev/null +++ b/clang/test/3C/unwritable_bounds.h @@ -0,0 +1,34 @@ +// Used by base_subdir/unwritable_bounds.c . + +void unwrite0(void *p : itype(_Array_ptr)); +void unwrite1(char *p); +void unwrite2(char *p : itype(_Array_ptr)); +void unwrite3(_Array_ptr p); + +void unwrite4(char *p) { + for (int i = 0; i < 10; i++) + p[i]; +} +void unwrite5(char *p : itype(_Array_ptr)) { + for (int i = 0; i < 10; i++) + p[i]; +} +void unwrite6(_Array_ptr p) { + for (int i = 0; i < 10; i++) + p[i]; +} + +struct arr_struct { + int *arr; + int n; +}; + +struct other_struct { + char *c; +}; +struct other_struct *struct_ret(); + +int *glob0; +int *glob1 : itype(_Array_ptr); +int *glob2 : count(10); +int *glob3 : itype(_Ptr); diff --git a/clang/tools/3c/3CStandalone.cpp b/clang/tools/3c/3CStandalone.cpp index 120bccdf3192..5a6b7f6573c4 100644 --- a/clang/tools/3c/3CStandalone.cpp +++ b/clang/tools/3c/3CStandalone.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/3C/3C.h" +#include "clang/3C/3CGlobalOptions.h" #include "clang/Tooling/CommonOptionsParser.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" @@ -74,15 +75,19 @@ are "#include"-d by that file and it is an error if any other file changes. // Skip the 2 initial newlines. static cl::extrahelp MoreHelp(MoreHelpStr + 2); -static cl::opt OptDumpIntermediate("dump-intermediate", - cl::desc("Dump intermediate " - "information"), - cl::init(false), cl::cat(_3CCategory)); +// Letting clang-format reflow these declarations gives very inconsistent +// formatting between options. +// clang-format off -static cl::opt OptVerbose("verbose", - cl::desc("Print verbose " - "information"), - cl::init(false), cl::cat(_3CCategory)); +static cl::opt OptDumpIntermediate( + "dump-intermediate", + cl::desc("Dump intermediate information"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptVerbose( + "verbose", + cl::desc("Print verbose information"), + cl::init(false), cl::cat(_3CCategory)); static cl::opt OptOutputPostfix( "output-postfix", @@ -97,82 +102,72 @@ static cl::opt OptOutputDir( "relative paths as the originals under the -base-dir"), cl::init(""), cl::cat(_3CCategory)); -static cl::opt - OptMalloc("use-malloc", - cl::desc("Allows for the usage of user-specified " - "versions of function allocators"), - cl::init(""), cl::cat(_3CCategory)); - -static cl::opt - OptConstraintOutputJson("constraint-output", - cl::desc("Path to the file where all the analysis " - "information will be dumped as json"), - cl::init("constraint_output.json"), - cl::cat(_3CCategory)); - -static cl::opt - OptStatsOutputJson("stats-output", - cl::desc("Path to the file where all the stats " - "will be dumped as json"), - cl::init("TotalConstraintStats.json"), - cl::cat(_3CCategory)); -static cl::opt - OptWildPtrInfoJson("wildptrstats-output", - cl::desc("Path to the file where all the info " - "related to WILD ptr grouped by reason" - " will be dumped as json"), - cl::init("WildPtrStats.json"), cl::cat(_3CCategory)); +static cl::opt OptMalloc( + "use-malloc", + cl::desc("Allows for the usage of user-specified versions of function " + "allocators"), + cl::init(""), cl::cat(_3CCategory)); + +static cl::opt OptConstraintOutputJson( + "constraint-output", + cl::desc("Path to the file where all the analysis information will be " + "dumped as json"), + cl::init("constraint_output.json"), cl::cat(_3CCategory)); + +static cl::opt OptStatsOutputJson( + "stats-output", + cl::desc("Path to the file where all the stats will be dumped as json"), + cl::init("TotalConstraintStats.json"), cl::cat(_3CCategory)); + +static cl::opt OptWildPtrInfoJson( + "wildptrstats-output", + cl::desc("Path to the file where all the info related to WILD ptr grouped " + "by reason will be dumped as json"), + cl::init("WildPtrStats.json"), cl::cat(_3CCategory)); static cl::opt OptPerPtrWILDInfoJson( "perptrstats-output", - cl::desc("Path to the file where all the info " - "related to each WILD ptr will be dumped as json"), + cl::desc("Path to the file where all the info related to each WILD ptr " + "will be dumped as json"), cl::init("PerWildPtrStats.json"), cl::cat(_3CCategory)); -static cl::opt OptDumpStats("dump-stats", cl::desc("Dump statistics"), - cl::init(false), cl::cat(_3CCategory)); - -static cl::opt OptHandleVARARGS("handle-varargs", - cl::desc("Enable handling of varargs " - "in a " - "sound manner"), - cl::init(false), cl::cat(_3CCategory)); - -static cl::opt - OptEnablePropThruIType("enable-itypeprop", - cl::desc("Enable propagation of " - "constraints through ityped " - "parameters/returns."), - cl::init(false), cl::cat(_3CCategory)); - -static cl::opt OptAllTypes("alltypes", - cl::desc("Consider all Checked C types for " - "conversion"), - cl::init(false), cl::cat(_3CCategory)); - -static cl::opt OptAddCheckedRegions("addcr", - cl::desc("Add Checked " - "Regions"), - cl::init(false), - cl::cat(_3CCategory)); +static cl::opt OptDumpStats( + "dump-stats", + cl::desc("Dump statistics"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptHandleVARARGS( + "handle-varargs", + cl::desc("Enable handling of varargs in a sound manner"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptAllTypes( + "alltypes", + cl::desc("Consider all Checked C types for conversion"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptAddCheckedRegions( + "addcr", + cl::desc("Add Checked Regions"), + cl::init(false), cl::cat(_3CCategory)); static cl::opt OptEnableCCTypeChecker( "enccty", cl::desc( - "Enable the Checked C type checker. 3c normally disables it (via the " - "equivalent of `clang -f3c-tool`) so that 3c can operate on partially " - "converted programs that may have Checked C type errors."), + "Enable the Checked C type checker. 3c normally disables it (via the " + "equivalent of `clang -f3c-tool`) so that 3c can operate on partially " + "converted programs that may have Checked C type errors."), cl::init(false), cl::cat(_3CCategory)); static cl::opt OptBaseDir( "base-dir", cl::desc( - "Ancestor directory defining the set of files that 3c " - "is allowed to modify (default: the working " - "directory). All source files specified on the command line must be " - "under this directory. You can use " - "this option to let 3c modify your project's own header files but not " - "those of libraries outside your control."), + "Ancestor directory defining the set of files that 3c " + "is allowed to modify (default: the working " + "directory). All source files specified on the command line must be " + "under this directory. You can use " + "this option to let 3c modify your project's own header files but not " + "those of libraries outside your control."), cl::init(""), cl::cat(_3CCategory)); static cl::opt OptAllowSourcesOutsideBaseDir( @@ -188,11 +183,11 @@ static cl::opt OptWarnRootCause( cl::desc("Emit warnings indicating root causes of unchecked pointers."), cl::init(false), cl::cat(_3CCategory)); -static cl::opt - OptWarnAllRootCause("warn-all-root-cause", - cl::desc("Emit warnings for all root causes, " - "even those unlikely to be interesting."), - cl::init(false), cl::cat(_3CCategory)); +static cl::opt OptWarnAllRootCause( + "warn-all-root-cause", + cl::desc("Emit warnings for all root causes, even those unlikely to be " + "interesting."), + cl::init(false), cl::cat(_3CCategory)); // In the future, we may enhance this to write the output to individual files. // For now, the user has to copy and paste the correct portions of stderr. @@ -227,6 +222,72 @@ static cl::opt OptAllowRewriteFailures( "affect common use cases."), cl::init(false), cl::cat(_3CCategory)); +static cl::opt OptItypesForExtern( + "itypes-for-extern", + cl::desc("All functions with external linkage will be rewritten to use " + "itypes instead checked types. This does not apply to static " + "functions which continue to have itypes only when the function " + "is internally unsafe."), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptInferTypesForUndef( + "infer-types-for-undefs", + cl::desc("Enable type inference for undefined functions. Under this flag, " + "types for undefined functions are inferred according to the same " + "rules as defined functions with the caveat that an undefined " + "function will only solve to an itype and not a fully checked " + "type. Because 3c is not able to examine the body of the " + "function, the inferred pointer types (and array bounds) may not " + "be consistent with the actual implementation. By default, the " + "Checked C compiler trusts the declared itypes and will not " + "detect a spatial memory safety violation if the function is used " + "in a way that is consistent with the itypes but not the " + "assumptions actually made by the implementation. Thus, if you " + "want to guarantee spatial memory safety, you must manually " + "check the inferred types against your understanding of what the " + "function actually does (or any available documentation)."), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptDebugSolver( + "debug-solver", + cl::desc("Dump intermediate solver state"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptOnlyGreatestSol( + "only-g-sol", + cl::desc("Perform only greatest solution for Pty Constrains."), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptOnlyLeastSol( + "only-l-sol", + cl::desc("Perform only least solution for Pty Constrains."), + cl::init(false), cl::cat(_3CCategory)); + +static llvm::cl::opt OptDisableRDs( + "disable-rds", + llvm::cl::desc("Disable reverse edges for Checked Constraints."), + llvm::cl::init(false), cl::cat(_3CCategory)); + +static llvm::cl::opt OptDisableFunctionEdges( + "disable-fnedgs", + llvm::cl::desc("Disable reverse edges for external functions."), + llvm::cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptDisableArrH( + "disable-arr-hu", + cl::desc("Disable Array Bounds Inference Heuristics."), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt DebugArrSolver( + "debug-arr-solver", + cl::desc("Dump array bounds inference graph"), + cl::init(false), cl::cat(_3CCategory)); + +static cl::opt OptDisableInfDecls( + "disable-arr-missd", + cl::desc("Disable ignoring of missed bounds from declarations."), + cl::init(false), cl::cat(_3CCategory)); + #ifdef FIVE_C static cl::opt OptRemoveItypes( "remove-itypes", @@ -239,6 +300,8 @@ static cl::opt OptForceItypes( cl::init(false), cl::cat(_3CCategory)); #endif +// clang-format on + int main(int argc, const char **argv) { sys::PrintStackTraceOnErrorSignal(argv[0]); @@ -273,7 +336,6 @@ int main(int argc, const char **argv) { struct _3COptions CcOptions; CcOptions.BaseDir = OptBaseDir.getValue(); CcOptions.AllowSourcesOutsideBaseDir = OptAllowSourcesOutsideBaseDir; - CcOptions.EnablePropThruIType = OptEnablePropThruIType; CcOptions.HandleVARARGS = OptHandleVARARGS; CcOptions.DumpStats = OptDumpStats; CcOptions.OutputPostfix = OptOutputPostfix.getValue(); @@ -283,15 +345,25 @@ int main(int argc, const char **argv) { CcOptions.ConstraintOutputJson = OptConstraintOutputJson.getValue(); CcOptions.StatsOutputJson = OptStatsOutputJson.getValue(); CcOptions.WildPtrInfoJson = OptWildPtrInfoJson.getValue(); - CcOptions.PerPtrInfoJson = OptPerPtrWILDInfoJson.getValue(); + CcOptions.PerWildPtrInfoJson = OptPerPtrWILDInfoJson.getValue(); CcOptions.AddCheckedRegions = OptAddCheckedRegions; - CcOptions.EnableAllTypes = OptAllTypes; + CcOptions.AllTypes = OptAllTypes; CcOptions.EnableCCTypeChecker = OptEnableCCTypeChecker; CcOptions.WarnRootCause = OptWarnRootCause; CcOptions.WarnAllRootCause = OptWarnAllRootCause; CcOptions.DumpUnwritableChanges = OptDumpUnwritableChanges; CcOptions.AllowUnwritableChanges = OptAllowUnwritableChanges; CcOptions.AllowRewriteFailures = OptAllowRewriteFailures; + CcOptions.ItypesForExtern = OptItypesForExtern; + CcOptions.InferTypesForUndefs = OptInferTypesForUndef; + CcOptions.DebugSolver = OptDebugSolver; + CcOptions.OnlyGreatestSol = OptOnlyGreatestSol; + CcOptions.OnlyLeastSol = OptOnlyLeastSol; + CcOptions.DisableRDs = OptDisableRDs; + CcOptions.DisableFunctionEdges = OptDisableFunctionEdges; + CcOptions.DisableInfDecls = OptDisableInfDecls; + CcOptions.DisableArrH = OptDisableArrH; + CcOptions.DebugArrSolver = DebugArrSolver; #ifdef FIVE_C CcOptions.RemoveItypes = OptRemoveItypes;