From 8f1a23276f2aba3e4df500fed76a00376c914969 Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sat, 15 Feb 2025 11:47:34 +0100 Subject: [PATCH] Compute types of c++ cast expressions Signed-off-by: Roberto Raggi --- src/parser/cxx/parser.cc | 100 ++++++++++++++++++++++++ src/parser/cxx/parser.h | 12 +++ tests/unit_tests/sema/static_cast_01.cc | 31 ++++++++ tests/unit_tests/sema/static_cast_02.cc | 26 ++++++ 4 files changed, 169 insertions(+) create mode 100644 tests/unit_tests/sema/static_cast_01.cc create mode 100644 tests/unit_tests/sema/static_cast_02.cc diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 6464565a..717e892a 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -2601,9 +2601,109 @@ auto Parser::parse_cpp_cast_expression(ExpressionAST*& yyast, expect(TokenKind::T_RPAREN, ast->rparenLoc); + check_cpp_cast_expression(ast); + + switch (unit->tokenKind(ast->castLoc)) { + case TokenKind::T_STATIC_CAST: + if (check_static_cast(ast)) break; + if (config_.checkTypes) { + parse_error(ast->firstSourceLocation(), "invalid static_cast"); + } + break; + + default: + break; + } // switch + return true; } +void Parser::check_cpp_cast_expression(CppCastExpressionAST* ast) { + if (!ast->typeId) { + return; + } + + ast->type = ast->typeId->type; + + if (auto refType = type_cast(ast->type)) { + ast->type = refType->elementType(); + ast->valueCategory = ValueCategory::kLValue; + return; + } + + if (auto rvalueRefType = type_cast(ast->type)) { + ast->type = rvalueRefType->elementType(); + + if (type_cast(ast->type)) { + ast->valueCategory = ValueCategory::kLValue; + } else { + ast->valueCategory = ValueCategory::kXValue; + } + } +} + +auto Parser::check_static_cast(CppCastExpressionAST* ast) -> bool { + if (!ast->typeId) return false; + auto targetType = ast->typeId->type; + + if (control_->is_void(targetType)) return true; + + if (check_cast_to_derived(targetType, ast->expression)) return true; + + const auto cv1 = get_cv_qualifiers(ast->expression->type); + const auto cv2 = get_cv_qualifiers(targetType); + if (!check_cv_qualifiers(cv2, cv1)) return false; + + if (implicit_conversion(ast->expression, ast->type)) return true; + + return false; +}; + +auto Parser::check_cv_qualifiers(CvQualifiers target, CvQualifiers source) const + -> bool { + if (source == target) return true; + if (source == CvQualifiers::kNone) return true; + if (target == CvQualifiers::kConstVolatile) return true; + return false; +} + +auto Parser::check_cast_to_derived(const Type* targetType, + ExpressionAST* expression) -> bool { + if (!is_lvalue(expression)) return false; + + auto sourceType = expression->type; + + CvQualifiers cv1 = CvQualifiers::kNone; + if (auto qualType = type_cast(sourceType)) { + cv1 = qualType->cvQualifiers(); + sourceType = qualType->elementType(); + } + + auto targetRefType = type_cast(targetType); + if (!targetRefType) return false; + + targetType = targetRefType->elementType(); + + CvQualifiers cv2 = CvQualifiers::kNone; + if (auto qualType = type_cast(targetType)) { + cv2 = qualType->cvQualifiers(); + targetType = qualType->elementType(); + } + + if (!check_cv_qualifiers(cv2, cv1)) return false; + + if (!control_->is_base_of(sourceType, targetType)) return false; + + return true; +} + +auto Parser::get_cv_qualifiers(const Type* type) const -> CvQualifiers { + if (auto qualType = type_cast(type)) { + return qualType->cvQualifiers(); + } + return CvQualifiers::kNone; +} + auto Parser::parse_builtin_bit_cast_expression(ExpressionAST*& yyast, const ExprContext& ctx) -> bool { if (!lookat(TokenKind::T___BUILTIN_BIT_CAST)) return false; diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index 4ca2c5b0..f2d30ae1 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -872,6 +872,18 @@ class Parser final { [[nodiscard]] auto is_null_pointer_constant(ExpressionAST* expr) const -> bool; + void check_cpp_cast_expression(CppCastExpressionAST* ast); + + [[nodiscard]] auto check_static_cast(CppCastExpressionAST* ast) -> bool; + + [[nodiscard]] auto check_cv_qualifiers(CvQualifiers target, + CvQualifiers source) const -> bool; + + [[nodiscard]] auto check_cast_to_derived(const Type* targetType, + ExpressionAST* expression) -> bool; + + [[nodiscard]] auto get_cv_qualifiers(const Type* type) const -> CvQualifiers; + [[nodiscard]] auto is_prvalue(ExpressionAST* expr) const -> bool; [[nodiscard]] auto is_lvalue(ExpressionAST* expr) const -> bool; [[nodiscard]] auto is_xvalue(ExpressionAST* expr) const -> bool; diff --git a/tests/unit_tests/sema/static_cast_01.cc b/tests/unit_tests/sema/static_cast_01.cc new file mode 100644 index 00000000..cfce9969 --- /dev/null +++ b/tests/unit_tests/sema/static_cast_01.cc @@ -0,0 +1,31 @@ +// RUN: %cxx -verify -fcheck %s + +struct X {}; + +using F = int(); + +int f() { return 0; } + +auto main() -> int { + X x; + + F&& rf = f; + + // prvalue + static_assert(__is_reference(decltype(static_cast(x))) == false); + + // lvalue if lvalue reference to object type + static_assert(__is_lvalue_reference(decltype(static_cast(x)))); + + // rvalue if rvalue reference to object type + static_assert(__is_rvalue_reference(decltype(static_cast(x)))); + + // prvalue + static_assert(__is_reference(decltype(static_cast(f))) == false); + + // lvalue if lvalue reference to function type + static_assert(__is_lvalue_reference(decltype(static_cast(f)))); + + // lvalue if rvalue reference to function type + static_assert(__is_lvalue_reference(decltype(static_cast(f)))); +} \ No newline at end of file diff --git a/tests/unit_tests/sema/static_cast_02.cc b/tests/unit_tests/sema/static_cast_02.cc new file mode 100644 index 00000000..ddaa2e55 --- /dev/null +++ b/tests/unit_tests/sema/static_cast_02.cc @@ -0,0 +1,26 @@ +// RUN: %cxx -verify -fcheck -freport-missing-types %s + +struct B {}; +struct D : B {}; + +auto main() -> int { + B b; + const B cb; + + // check casts to void + static_cast(b); + static_cast(b); + static_cast(b); + static_cast(123); + static_cast(cb); + + // check casts to derived class + D& d = static_cast(b); + const D& cd1 = static_cast(b); + const D& cd2 = static_cast(cb); + const volatile D& cd3 = static_cast(cb); + + D& d = static_cast(cb); // expected-error {{invalid static_cast}} + + return 0; +} \ No newline at end of file