Document number P****R0
Date 2022-09-27
Reply-to

Jarrad J. Waterloo <descender76 at gmail dot com>

Audience Evolution Working Group (EWG)

C++ is the next C++

Table of contents

Abstract

Programmer’s, Businesses and Government(s) want C++ to be safer and simpler. This has led some C++ programmers to create new programming languages or preprocessors which again is a new language. This paper discusses using static analysis to make the C++ language itself safer and simpler.

Motivating Examples

Following is a wishlist. Most are optional. While, they all would be of benefit. It all starts with a new repeatable module level attribute that would preferably be applied once in the primary module interface unit and would automatically apply to it and all module implementation unit(s). It could also be applied to a module implementation unit but that would generally be less useful. However, it might aid in gradual migration.

export module some_module_name [[static_analysis("")]];// primary module interface unit
// or
module some_module_name [[static_analysis("")]];// module implementation unit

The name of the static analyzer are dotted. Unscoped or names that start with std., c++., cpp., cxx. or c. are reserved for standardization.

This proposal wants to stardardize two overarching static analyzer names; safer and modern.

[[static_analysis("safer")]]

The safer analyzer is for safety, primarily memory related. It is for those businesses and programmers who must conform to safety standards.

[[static_analysis("modern")]]

The safer analyzer is a subset of modern analyzer. The modern analyzer goes beyond just memory and safety concerns. It can be thought of as bleeding edge. It is for those businesses and programmers who commit to safety and higher quality modern code.

Neither is concerned about formatting or nitpicking. Both static analyzers only produce errors. They both represent +∞. These are meant for programmers, businesses and governments in which safety takes precedence. When a new version of the standard is released and adds new sub static analyzers than everyone’s code is broken until their code is fixed. These sub static analyzers usually consist of features that have been mostly replaced with some other feature. It would be ideal if the errors produced not only say that the code is wrong but also provide a link to html page(s) maintained by both the C++ teaching group, the authors of the C++ Core Guidelines [1] and compiler specific errors. These pages should provide example(s) of what is being replaced and by what was it replaced. Mentioning the version of the C++ standard would also be helpful.

All modules can be used even if they don’t use the static_analysis attribute as this allows gradual adoption.

What are the safer and modern analyzers composed of?

These overarching static analyzers are composed of multiple static analyzers which can be used individually to allow a degree of gradual adoption.

Use lvalue references

[[static_analysis("use_lvalue_references")]]

use_lvalue_references is a subset of safer.

WHY?

lvalue references

1985: Cfront 1.0 [2]

STL

1992 [2:1]

std::unique_ptr, std::shared_ptr, std::weak_ptr, std::reference_wrapper, std::make_shared

C++11

std::make_unique

C++14

std::string_view, std::optional, std::any, std::variant

C++17

std::make_shared support arrays, std::span

C++20

The C++ Core Guidelines [1:1] identifies issues that this feature helps to mitigate.

Gotchas

Usage of smart pointers

This static analyzer causes programmers to use 2 extra characters when using smart pointers: -> vs (*)..

smart_pointer->some_function();
(*smart_pointer).some_function();

the main function and environment variables

A shim module is needed in order to transform main and env functions into a more C++ friendly functions. These have been asked for years.

  1. A Modern C++ Signature for main [54]
  2. Desert Sessions: Improving hostile environment interactions [55]

No unsafe casts

[[static_analysis("no_unsafe_casts")]]

no_unsafe_casts is a subset of safer.

Why?

See the following:


No unions

[[static_analysis("no_union")]]

no_union is a subset of safer.

Using the union keyword produces an error. It was replaced by std::variant, which is safer.

See the following:


No mutable

[[static_analysis("no_mutable")]]

no_mutable is a subset of safer.

Using the mutable keyword produces an error. The programmer shall not lie to oneself. The mutable keyword violates the safety of const and is rarely used.


No new or delete

[[static_analysis("no_new_delete")]]

no_new_delete is a subset of safer.

Using the new and delete keywords to allocate and deallocate memory produces an error. It was replaced by std::make_unique and std::make_shared, which are safer.

See the following:


No deprecated

[[static_analysis("no_deprecated")]]

no_deprecated is a subset of modern.

Using anything that has the deprecated attribute on it produces an error.

What may safer and modern analyzers be composed of in the future?

No include

[[static_analysis("no_include")]]

no_include is a subset of modern.

The preprocessor directive #include has been replaced with import.
Don’t add the static analyzer until #embed is added.


No goto

[[static_analysis("no_goto")]]

no_goto is a subset of modern.

Using the goto keyword produces an error.
Don’t add until break and continue to a label is added. Also a finite state machine library may be needed.

See the following:

Summary

By adding static analysis to the C++ language we can make the language safer and easier to teach because we can restrict how much of the language we use. Human readable errors and references turns the compiler into a teacher freeing human teachers to focus on what the compiler doesn’t handle.

Frequently Asked Questions

Shouldn’t these be warnings instead of errors?

NO, otherwise we’ll be stuck with what we just have. C++ compilers produces plenty of warnings. C++ static analyzers produces plenty of warnings. However, when some one talks about creating a new language, then old language syntax becomes invalid i.e. errors. This is for programmers. Programmers and businesses rarely upgrade their code unless they are forced to. Businesses and Government(s) want errors as well in order to ensure code quality and the assurance that bad code doesn’t exist anywhere in the module. This is also important from a language standpoint because we are essentially pruning; somewhat. Keep in mind though that all of these pruned features still have use now and in the future as more constructs will be built upon them which is why they need to be part of the language just not a part of everyday usage of the language.

Why at the module level? Why not safe and unsafe blocks?

Programmers and businesses rarely upgrade their code unless they are forced to. New programmers need training wheels and some of us older programmers like them too. Due to the proliferation of government regulations and oversight, businesses have acquired software composition analysis services and tools. These services map security errors to specific versions of modules; specifically programming artifacts such as executables and libraries. As such, businesses want to know is a module reasonably safe.

You must really hate pointers?

Actually, I love C, C++ and pointers.

The fact is pointers, unsafe casts, union, mutable and goto are the engine of C++ change. As such it would be foolish to remove them but it is also unrealistic for users/drivers of a vehicle to have to drive with nothing between them and the engine without listening to them clamor for interior finishing.

C++ can’t standardize specific static analyzers

References


  1. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines ↩︎ ↩︎

  2. https://en.cppreference.com/w/cpp/language/history ↩︎ ↩︎

  3. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p4-ideally-a-program-should-be-statically-type-safe ↩︎

  4. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p6-what-cannot-be-checked-at-compile-time-should-be-checkable-at-run-time ↩︎

  5. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p7-catch-run-time-errors-early ↩︎

  6. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p8-dont-leak-any-resources ↩︎

  7. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p11-encapsulate-messy-constructs-rather-than-spreading-through-the-code ↩︎

  8. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p12-use-supporting-tools-as-appropriate ↩︎

  9. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p13-use-support-libraries-as-appropriate ↩︎

  10. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i4-make-interfaces-precisely-and-strongly-typed ↩︎

  11. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i11-never-transfer-ownership-by-a-raw-pointer-t-or-reference-t ↩︎

  12. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i12-declare-a-pointer-that-must-not-be-null-as-not_null ↩︎

  13. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i13-do-not-pass-an-array-as-a-single-pointer ↩︎

  14. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i23-keep-the-number-of-function-arguments-low ↩︎

  15. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f7-for-general-use-take-t-or-t-arguments-rather-than-smart-pointers ↩︎

  16. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f15-prefer-simple-and-conventional-ways-of-passing-information ↩︎

  17. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f22-use-t-or-ownert-to-designate-a-single-object ↩︎

  18. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f23-use-a-not_nullt-to-indicate-that-null-is-not-a-valid-value ↩︎

  19. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f25-use-a-zstring-or-a-not_nullzstring-to-designate-a-c-style-string ↩︎

  20. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f26-use-a-unique_ptrt-to-transfer-ownership-where-a-pointer-is-needed ↩︎ ↩︎

  21. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f27-use-a-shared_ptrt-to-share-ownership ↩︎ ↩︎

  22. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f42-return-a-t-to-indicate-a-position-only ↩︎

  23. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f43-never-directly-or-indirectly-return-a-pointer-or-a-reference-to-a-local-object ↩︎

  24. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f55-dont-use-va_arg-arguments ↩︎

  25. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c31-all-resources-acquired-by-a-class-must-be-released-by-the-classs-destructor ↩︎

  26. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c32-if-a-class-has-a-raw-pointer-t-or-reference-t-consider-whether-it-might-be-owning ↩︎

  27. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c33-if-a-class-has-an-owning-pointer-member-define-a-destructor ↩︎

  28. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c149-use-unique_ptr-or-shared_ptr-to-avoid-forgetting-to-delete-objects-created-using-new ↩︎ ↩︎

  29. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c150-use-make_unique-to-construct-objects-owned-by-unique_ptrs ↩︎ ↩︎

  30. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c151-use-make_shared-to-construct-objects-owned-by-shared_ptrs ↩︎ ↩︎

  31. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r1-manage-resources-automatically-using-resource-handles-and-raii-resource-acquisition-is-initialization ↩︎

  32. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r2-in-interfaces-use-raw-pointers-to-denote-individual-objects-only ↩︎

  33. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning ↩︎

  34. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r5-prefer-scoped-objects-dont-heap-allocate-unnecessarily ↩︎

  35. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r10-avoid-malloc-and-free ↩︎

  36. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r11-avoid-calling-new-and-delete-explicitly ↩︎ ↩︎

  37. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r12-immediately-give-the-result-of-an-explicit-resource-allocation-to-a-manager-object ↩︎

  38. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r13-perform-at-most-one-explicit-resource-allocation-in-a-single-expression-statement ↩︎

  39. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r14-avoid--parameters-prefer-span ↩︎

  40. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r15-always-overload-matched-allocationdeallocation-pairs ↩︎

  41. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r20-use-unique_ptr-or-shared_ptr-to-represent-ownership ↩︎ ↩︎

  42. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r22-use-make_shared-to-make-shared_ptrs ↩︎ ↩︎

  43. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r23-use-make_unique-to-make-unique_ptrs ↩︎ ↩︎

  44. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es20-always-initialize-an-object ↩︎

  45. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es24-use-a-unique_ptrt-to-hold-pointers ↩︎

  46. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#-es34-dont-define-a-c-style-variadic-function ↩︎

  47. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es42-keep-use-of-pointers-simple-and-straightforward ↩︎

  48. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es47-use-nullptr-rather-than-0-or-null ↩︎

  49. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es60-avoid-new-and-delete-outside-resource-management-functions ↩︎ ↩︎

  50. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es61-delete-arrays-using-delete-and-non-arrays-using-delete ↩︎ ↩︎

  51. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es65-dont-dereference-an-invalid-pointer ↩︎

  52. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#e13-never-throw-while-being-the-direct-owner-of-an-object ↩︎

  53. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cpl1-prefer-c-to-c ↩︎

  54. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0781r0.html ↩︎

  55. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1275r0.html ↩︎

  56. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c146-use-dynamic_cast-where-class-hierarchy-navigation-is-unavoidable ↩︎

  57. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es48-avoid-casts ↩︎

  58. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es49-if-you-must-use-a-cast-use-a-named-cast ↩︎

  59. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es50-dont-cast-away-const ↩︎

  60. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c181-avoid-naked-unions ↩︎

  61. https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es76-avoid-goto ↩︎