https://github.com/compiler-devel/llvm-project/commit/cfd497fadb8bae4c5428f40ea50cfc760649afa4 Skip to content Sign up * Product + Features + Mobile + Actions + Codespaces + Copilot + Packages + Security + Code review + Issues + Discussions + Integrations + GitHub Sponsors + Customer stories * Team * Enterprise * Explore + Explore GitHub + Learn and contribute + Topics + Collections + Trending + Skills + GitHub Sponsors + Open source guides + Connect with others + The ReadME Project + Events + Community forum + GitHub Education + GitHub Stars program * Marketplace * Pricing + Plans + Compare plans + Contact Sales + Education [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} compiler-devel / llvm-project Public forked from llvm/llvm-project * Notifications * Fork 5.9k * Star 3 * Code * Pull requests 0 * Actions * Projects 0 * Security * Insights More * Code * Pull requests * Actions * Projects * Security * Insights Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files Modified C++ Inspired by the paper "Some Were Meant for C" by Stephen Kell, I decided to show that it's possible to iterate C++ to be safer, more explicit, and less error-prone. Here's a possible starting point: I didn't invent a new language or compiler, but took the world's best compiler, clang, and modified it to begin iterating towards a new furture of C++. Naming things is hard, so I call this 'Modified C++'. Some of the following could be implemented as tooling in a linter or checker, but the idea is to update the compiler directly. I also wanted to learn more about clang. This compiler needs a flag to enable/disable this functionality so that existing library code can be used with a 'diagnostic ignored' pragma. You can build clang using the normal non-bootstrap process and you'll be left with a clang that compiles C++ but with the following modifications: - All basic types (excluding pointers and references) are const by default and may be marked 'mutable' to allow them to be changed after declaration - Lambda capture lists must be explicit (no [&] or [=], by themselves) - Braces are required for conditional statements, case and default statements within switches, and loops - Implicit conversions to bool are prohibited (e.g., pointers must be compared against nullptr/NULL) - No goto support - Explicit 'rule of six' for classes must be programmer-implemented (default, copy, and move c'tors, copy and move assignment, d'tor) - No C style casts Here's an example program that's valid in Modified C++: mutable int main(int, char**) { mutable int x = 0; return x; } Here's another that will fail to compile: mutable int main(int, char**) { int x = 1; x = 0; // x is constant return x; } I'd like your feedback. Future changes I'm thinking about are: - feature flag for modified c++ to enable/disable with 'diagnostic ignored' pragma, to support existing headers and libraries - support enum classes only - constructor declarations are explicit by default - namespaces within classes - normalize lambda and free function syntax - your ideas here * Loading branch information @compiler-devel compiler-devel committed Aug 17, 2022 1 parent 9fd54cf commit cfd497fadb8bae4c5428f40ea50cfc760649afa4 Show file tree Hide file tree Showing 12 changed files with 153 additions and 94 deletions. Split Unified [ ] * clang + include/clang o Basic # clang/include/clang/Basic/DiagnosticASTKinds.td DiagnosticASTKinds.td # clang/include/clang/Basic/DiagnosticParseKinds.td DiagnosticParseKinds.td # clang/include/clang/Basic/DiagnosticSemaKinds.td DiagnosticSemaKinds.td # clang/include/clang/Basic/TokenKinds.def TokenKinds.def o Parse # clang/include/clang/Parse/Parser.h Parser.h + lib o AST # clang/lib/AST/ExprConstant.cpp ExprConstant.cpp o Parse # clang/lib/Parse/ParseDecl.cpp ParseDecl.cpp # clang/lib/Parse/ParseExprCXX.cpp ParseExprCXX.cpp # clang/lib/Parse/ParseStmt.cpp ParseStmt.cpp o Sema # clang/lib/Sema/SemaCast.cpp SemaCast.cpp # clang/lib/Sema/SemaDeclCXX.cpp SemaDeclCXX.cpp # clang/lib/Sema/SemaStmt.cpp SemaStmt.cpp There are no files selected for viewing 2 clang/include/clang/Basic/DiagnosticASTKinds.td [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file Diff line number line Diff line change number @@ -600,4 +600,6 @@ def warn_unnecessary_packed : Warning< def warn_unaligned_access : Warning< "field %1 within %0 is less aligned than %2 and is usually due to %0 being " "packed, which can lead to unaligned accesses">, InGroup, DefaultIgnore; def implicit_cast_to_bool_disabled : Error <"implicit cast to bool disabled">; } 10 clang/include/clang/Basic/DiagnosticParseKinds.td [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file Diff line Diff line change line number number @@ -539,7 +539,7 @@ def err_invalid_operator_on_type : Error< def err_expected_unqualified_id : Error< "expected %select{identifier| unqualified-id}0">; def err_while_loop_outside_of_a_function : Error< "while loop outside of a function">; "while loop outside of a function">; def err_brackets_go_after_unqualified_id : Error< "brackets are not allowed here; to declare an array, " "place the brackets after the %select {identifier|name}0">; @@ -976,6 +976,8 @@ def err_sizeof_parameter_pack : Error< "expected parenthesized parameter pack name in 'sizeof...' expression">; // C++11 lambda expressions def err_default_capture_disallowed : Error< "'&' or '=' alone not allowed for lambda captures">; def err_expected_comma_or_rsquare : Error< "expected ',' or ']' in lambda capture list">; def err_this_captured_by_reference : Error< @@ -1610,4 +1612,10 @@ def ext_hlsl_access_specifiers : ExtWarn< "access specifiers are a clang HLSL extension">, InGroup; def err_conditional_braces_required : Error < "braces required for conditional statements">; def err_case_statements_require_braces : Error< "braces required for case statements">; } // end of Parser diagnostics 6 clang/include/clang/Basic/DiagnosticSemaKinds.td [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original Diff file line line Diff line change number number @@ -11649,4 +11649,10 @@ def err_non_designated_init_used : Error< "a randomized struct can only be initialized with a designated initializer">; def err_cast_from_randomized_struct : Error< "casting from randomized structure pointer type %0 to %1">; def err_rule_of_six : Error< "User-provided default constructor, copy constructor, move constructor, copy assignment, move assignment, and destructor required">; def err_c_style_casts_prohibited : Error< "C-style casts prohibited">; } // end of sema component. 1 clang/include/clang/Basic/TokenKinds.def [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file line Diff line Diff line change number number @@ -357,6 +357,7 @@ KEYWORD(typeid , KEYCXX) KEYWORD(using , KEYCXX) KEYWORD(virtual , KEYCXX) KEYWORD(wchar_t , WCHARSUPPORT) KEYWORD(mut , KEYCXX) // C++ 2.5p2: Alternative Representations. CXX_KEYWORD_OPERATOR(and , ampamp) 1 clang/include/clang/Parse/Parser.h [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file line Diff line Diff line change number number @@ -2103,7 +2103,6 @@ class Parser : public CodeCompletionHandler { StmtResult ParseWhileStatement (SourceLocation *TrailingElseLoc); StmtResult ParseDoStatement(); StmtResult ParseForStatement (SourceLocation *TrailingElseLoc); StmtResult ParseGotoStatement(); This comment has been minimized. Sign in to view Sorry, something went wrong. Copy link Quote reply @Rangi42 Rangi42 Aug 19, 2022 It's nice to remove the blunt instrument of goto, but there are a few idioms that become harder to express. The main one in C is for goto fail error management, but C++ has RAII and destructors for that. Another one, however, is breaking out of nested while/for/switch structures. Currently without goto this needs to be done with flag variables, or by factoring the structures out into a function or lambda so you can use return. There have been proposals to add break/continue N for breaking or continuing the Nth nested loop (with plain break/continue meaning break/continue 1, only constant values accepted); or break break, break continue, break break continue, etc; or break/continue to a label (the main objection being that this makes more sense when the beginning of the block is labeled rather than the end, which is not how C/C++ labels currently work). Whichever of those you prefer, if any, might be worth adding. For state machines, the other place where goto mostly gets used, the gcc extension goto case X could be added as continue X, since currently break is useful in switches but not continue. All reactions StmtResult ParseContinueStatement (); StmtResult ParseBreakStatement(); StmtResult ParseReturnStatement (); 28 clang/lib/AST/ExprConstant.cpp [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original Diff file line line Diff line change number number @@ -2466,22 +2466,9 @@ static bool CheckMemoryLeaks(EvalInfo &Info) { return true; } static bool EvalPointerValueAsBool(const APValue &Value, bool &Result) { // A null base expression indicates a null pointer. These are always // evaluatable, and they are false unless the offset is zero. if (!Value.getLValueBase()) { Result = !Value.getLValueOffset().isZero(); return true; } // We have a non-null base. These are generally known to be true, but if it's // a weak declaration it can be null at runtime. Result = true; const ValueDecl *Decl = Value.getLValueBase ().dyn_cast(); return !Decl || !Decl->isWeak(); } static bool HandleConversionToBool(const APValue &Val, bool &Result) { return false; // disable all conversions to bool #if 0 switch (Val.getKind()) { case APValue::None: case APValue::Indeterminate: @@ -2503,11 +2490,8 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { Result = !Val.getComplexFloatReal().isZero() || !Val.getComplexFloatImag().isZero(); return true; case APValue::LValue: return EvalPointerValueAsBool(Val, Result); case APValue::MemberPointer: Result = Val.getMemberPointerDecl(); return true; case APValue::LValue: // no pointer-to-bool conversion case APValue::MemberPointer: // same case APValue::Vector: case APValue::Array: case APValue::Struct: @@ -2517,6 +2501,7 @@ static bool HandleConversionToBool(const APValue &Val, bool &Result) { } llvm_unreachable("unknown APValue kind"); #endif } static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, @@ -15046,8 +15031,7 @@ bool Expr::EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); EvalResult Scratch; return EvaluateAsRValue(Scratch, Ctx, InConstantContext) && HandleConversionToBool(Scratch.Val, Result); return EvaluateAsRValue(Scratch, Ctx, InConstantContext); } bool Expr::EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, 61 clang/lib/Parse/ParseDecl.cpp [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original Diff file line line Diff line change number number @@ -3102,6 +3102,27 @@ static void SetupFixedPointError(const LangOptions & LangOpts, isInvalid = true; } namespace { void SetTypeSpecTypeAndQual(bool& isInvalid, unsigned int& markBasicTypeDeclConst, const LangOptions& langOpts, DeclSpec& DS, DeclSpec::TST typespec, SourceLocation Loc, const char* PrevSpec, unsigned DiagID, PrintingPolicy& Policy) { isInvalid = DS.SetTypeSpecType(typespec, Loc, PrevSpec, DiagID, Policy); if (markBasicTypeDeclConst == 0) { isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID, langOpts); } } } /// ParseDeclarationSpecifiers /// declaration-specifiers: [C99 6.7] /// storage-class-specifier declaration-specifiers[opt] @@ -3148,7 +3169,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ParsedAttributes attrs(AttrFactory); // We use Sema's policy to get bool macros right. PrintingPolicy Policy = Actions. getPrintingPolicy(); unsigned int markBasicTypeDeclConst = 0; while (true) { if (markBasicTypeDeclConst > 0) { --markBasicTypeDeclConst; } bool isInvalid = false; bool isStorageClass = false; const char *PrevSpec = nullptr; @@ -3797,8 +3823,9 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, Diag(Tok, diag::ext_auto_storage_class) << FixItHint::CreateRemoval(DS. getStorageClassSpecLoc()); } else isInvalid = DS.SetTypeSpecType (DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy); } else isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, PrevSpec, DiagID, Policy); @@ -3815,9 +3842,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, isStorageClass = true; break; case tok::kw_mutable: isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_mutable, Loc, PrevSpec, DiagID, Policy); isStorageClass = true; if (DSContext == DeclSpecContext::DSC_class) { isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_mutable, Loc, PrevSpec, DiagID, Policy); isStorageClass = true; } markBasicTypeDeclConst = 2; break; case tok::kw___thread: isInvalid = DS.SetStorageClassSpecThread (DeclSpec::TSCS___thread, Loc, @@ -3967,17 +3997,18 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DiagID); break; case tok::kw_void: isInvalid = DS.SetTypeSpecType( DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_char: isInvalid = DS.SetTypeSpecType( DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_int: isInvalid = DS.SetTypeSpecType( DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy); break; case tok::kw__ExtInt: case tok::kw__BitInt: { DiagnoseBitIntUse(Tok); @@ -4001,12 +4032,12 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, DiagID, Policy); break; case tok::kw_float: isInvalid = DS.SetTypeSpecType( DeclSpec::TST_float, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_float, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_double: isInvalid = DS.SetTypeSpecType( DeclSpec::TST_double, Loc, PrevSpec, DiagID, Policy); SetTypeSpecTypeAndQual(isInvalid, markBasicTypeDeclConst, getLangOpts(), DS, DeclSpec::TST_double, Loc, PrevSpec, DiagID, Policy); break; case tok::kw__Float16: isInvalid = DS.SetTypeSpecType (DeclSpec::TST_float16, Loc, PrevSpec, 18 clang/lib/Parse/ParseExprCXX.cpp [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file Diff line Diff line change line number number @@ -855,21 +855,11 @@ bool Parser::ParseLambdaIntroducer (LambdaIntroducer &Intro, }; // Parse capture-default. if (Tok.is(tok::amp) && if (Tok.isOneOf(tok::amp, tok::equal) && (NextToken().is(tok::comma) || NextToken(). is(tok::r_square))) { Intro.Default = LCD_ByRef; Intro.DefaultLoc = ConsumeToken(); First = false; if (!Tok.getIdentifierInfo()) { // This can only be a lambda; no need for tentative parsing any more. // '[[and]]' can still be an attribute, though. Tentative = nullptr; } } else if (Tok.is(tok::equal)) { Intro.Default = LCD_ByCopy; Intro.DefaultLoc = ConsumeToken(); First = false; Tentative = nullptr; return Invalid([&] { Diag(Tok.getLocation(), diag::err_default_capture_disallowed); }); } while (Tok.isNot(tok::r_square)) { 73 clang/lib/Parse/ParseStmt.cpp [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original Diff file line line Diff line change number number @@ -296,10 +296,6 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes ( case tok::kw_for: // C99 6.8.5.3: for-statement return ParseForStatement(TrailingElseLoc); case tok::kw_goto: // C99 6.8.6.1: goto-statement Res = ParseGotoStatement(); SemiError = "goto"; break; case tok::kw_continue: // C99 6.8.6.2: continue-statement Res = ParseContinueStatement(); SemiError = "continue"; @@ -811,6 +807,10 @@ StmtResult Parser::ParseCaseStatement(ParsedStmtContext StmtCtx, ColonProtection.restore(); if (TryConsumeToken(tok::colon, ColonLoc)) { if (!Tok.is(tok::l_brace)) { Diag(CaseLoc, diag::err_case_statements_require_braces); return StmtError(); } } else if (TryConsumeToken(tok::semi, ColonLoc) || TryConsumeToken(tok::coloncolon, ColonLoc)) { // Treat "case blah;" or "case blah::" as a typo for "case blah:". @@ -893,6 +893,10 @@ StmtResult Parser::ParseDefaultStatement(ParsedStmtContext StmtCtx) { SourceLocation ColonLoc; if (TryConsumeToken(tok::colon, ColonLoc)) { if (!Tok.is(tok::l_brace)) { Diag(DefaultLoc, diag::err_case_statements_require_braces); return StmtError(); } } else if (TryConsumeToken(tok::semi, ColonLoc)) { // Treat "default;" as a typo for "default:". Diag(ColonLoc, diag::err_expected_after) @@ -1479,6 +1483,11 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { } bool IsBracedThen = Tok.is(tok::l_brace); if (!IsBracedThen) { // Error as we're requiring braces for all conditional statements. Diag(Tok.getLocation(), diag::err_conditional_braces_required); return StmtError(); } // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this @@ -1540,6 +1549,13 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) { ElseLoc = ConsumeToken(); ElseStmtLoc = Tok.getLocation(); bool IsBracedElse = Tok.is(tok::l_brace); if (!IsBracedElse) { // Error as we're requiring braces for all conditional statements. Diag(Tok.getLocation(), diag::err_conditional_braces_required); return StmtError(); } // C99 6.8.4p3 - In C99, the body of the if statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do // this if the body isn't a compound statement to avoid push/pop in common @@ -1761,6 +1777,13 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) { /*MissingOK=*/false, &LParen, &RParen)) return StmtError(); const bool IsBraced = Tok.is(tok::l_brace); if (!IsBraced) { // Error as we're requiring braces for all conditional statements. Diag(Tok.getLocation(), diag::err_conditional_braces_required); return StmtError(); } // C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -1809,6 +1832,13 @@ StmtResult Parser::ParseDoStatement() { ParseScope DoScope(this, ScopeFlags); const bool IsBraced = Tok.is(tok::l_brace); if (!IsBraced) { // Error as we're requiring braces for all conditional statements. Diag(Tok.getLocation(), diag::err_conditional_braces_required); return StmtError(); } // C99 6.8.5p5 - In C99, the body of the do statement is a scope, even if // there is no compound stmt. C90 does not have this clause. We only do this // if the body isn't a compound statement to avoid push/pop in common cases. @@ -2269,41 +2299,6 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) { Body.get()); } /// ParseGotoStatement /// jump-statement: /// 'goto' identifier ';' /// [GNU] 'goto' '*' expression ';' /// /// Note: this lets the caller parse the end ';'. /// StmtResult Parser::ParseGotoStatement() { assert(Tok.is(tok::kw_goto) && "Not a goto stmt!" ); SourceLocation GotoLoc = ConsumeToken(); // eat the 'goto'. StmtResult Res; if (Tok.is(tok::identifier)) { LabelDecl *LD = Actions.LookupOrCreateLabel(Tok. getIdentifierInfo(), Tok.getLocation()); Res = Actions.ActOnGotoStmt(GotoLoc, Tok. getLocation(), LD); ConsumeToken(); } else if (Tok.is(tok::star)) { // GNU indirect goto extension. Diag(Tok, diag::ext_gnu_indirect_goto); SourceLocation StarLoc = ConsumeToken(); ExprResult R(ParseExpression()); if (R.isInvalid()) { // Skip to the semicolon, but don't consume it. SkipUntil(tok::semi, StopBeforeMatch); return StmtError(); } Res = Actions.ActOnIndirectGotoStmt(GotoLoc, StarLoc, R.get()); } else { Diag(Tok, diag::err_expected) << tok::identifier; return StmtError(); } return Res; } /// ParseContinueStatement /// jump-statement: /// 'continue' ';' 3 clang/lib/Sema/SemaCast.cpp [*] Show comments View file Edit file Delete file This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters Original file Diff line Diff line change line number number @@ -3245,8 +3245,11 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, Op.OpRange = SourceRange(LPLoc, CastExpr-> getEndLoc()); if (getLangOpts().CPlusPlus) { Diag(CastExpr->getExprLoc(), diag::err_c_style_casts_prohibited); #if 0 Op.CheckCXXCStyleCast(/*FunctionalCast=*/ false, isa(CastExpr)); #endif } else { Op.CheckCStyleCast(); } Oops, something went wrong. Retry Toggle all file notes Toggle all file annotations 0 comments on commit cfd497f Please sign in to comment. Footer (c) 2022 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.