From df1eab9c6b38e45c1b90ccf2c6a52011159edfad Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Sun, 16 Feb 2025 11:27:16 +0100 Subject: [PATCH] Compute types of psuedo destructor calls to scalar types. --- src/parser/cxx/parser.cc | 83 +++++++++++++++++++---- src/parser/cxx/parser.h | 5 ++ src/parser/cxx/type_traits.h | 4 +- tests/unit_tests/sema/member_access_01.cc | 44 ++++++++++++ 4 files changed, 121 insertions(+), 15 deletions(-) create mode 100644 tests/unit_tests/sema/member_access_01.cc diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 717e892a..54f2d986 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -1458,6 +1458,7 @@ auto Parser::parse_unqualified_id(UnqualifiedIdAST*& yyast, auto ast = make_node(pool_); yyast = ast; + ast->tildeLoc = tildeLoc; ast->id = decltypeName; return true; @@ -1469,6 +1470,7 @@ auto Parser::parse_unqualified_id(UnqualifiedIdAST*& yyast, auto ast = make_node(pool_); yyast = ast; + ast->tildeLoc = tildeLoc; ast->id = name; return true; @@ -2474,20 +2476,17 @@ auto Parser::parse_member_expression(ExpressionAST*& yyast) -> bool { ast->isTemplateIntroduced = match(TokenKind::T_TEMPLATE, ast->templateLoc); - const Type* objectType = nullptr; + if (SourceLocation completionLoc; parse_completion(completionLoc)) { + if (ast->baseExpression) { + // test if the base expression has a type + auto objectType = ast->baseExpression->type; - if (ast->baseExpression) { - // test if the base expression has a type - objectType = ast->baseExpression->type; - } - - if (SourceLocation completionLoc; - objectType && parse_completion(completionLoc)) { - // trigger the completion - config_.complete(MemberCompletionContext{ - .objectType = objectType, - .accessOp = ast->accessOp, - }); + // trigger the completion + config_.complete(MemberCompletionContext{ + .objectType = objectType, + .accessOp = ast->accessOp, + }); + } } if (!parse_unqualified_id(ast->unqualifiedId, ast->nestedNameSpecifier, @@ -2495,11 +2494,54 @@ auto Parser::parse_member_expression(ExpressionAST*& yyast) -> bool { /*inRequiresClause*/ false)) parse_error("expected an unqualified id"); + (void)check_psuedo_destructor_access(ast); + yyast = ast; return true; } +auto Parser::check_psuedo_destructor_access(MemberExpressionAST* ast) -> bool { + auto objectType = ast->baseExpression->type; + auto cv = strip_cv(objectType); + + if (ast->accessOp == TokenKind::T_MINUS_GREATER) { + auto pointerType = type_cast(objectType); + if (!pointerType) return false; + objectType = pointerType->elementType(); + cv = strip_cv(objectType); + } + + if (!control_->is_scalar(objectType)) { + // return false if the object type is not a scalar type + return false; + } + + // from this point on we are going to assume that we want a pseudo destructor + // to be called on a scalar type. + + auto dtor = ast_cast(ast->unqualifiedId); + if (!dtor) return true; + + auto name = ast_cast(dtor->id); + if (!name) return true; + + auto symbol = + Lookup{scope_}.lookupType(ast->nestedNameSpecifier, name->identifier); + if (!symbol) return true; + + if (!control_->is_same(symbol->type(), objectType)) { + parse_error(ast->unqualifiedId->firstSourceLocation(), + "the type of object expression does not match the type " + "being destroyed"); + return true; + } + + ast->symbol = symbol; + + return true; +} + auto Parser::parse_subscript_expression(ExpressionAST*& yyast, const ExprContext& ctx) -> bool { SourceLocation lbracketLoc; @@ -2548,6 +2590,12 @@ auto Parser::parse_call_expression(ExpressionAST*& yyast, argumentTypes.push_back(argumentType); } + if (auto access = ast_cast(ast->baseExpression)) { + if (ast_cast(access->unqualifiedId)) { + ast->type = control_->getVoidType(); + } + } + return true; } @@ -6201,6 +6249,15 @@ auto Parser::strip_parentheses(ExpressionAST* ast) -> ExpressionAST* { return ast; } +auto Parser::strip_cv(const Type*& type) -> CvQualifiers { + if (auto qualType = type_cast(type)) { + auto cv = qualType->cvQualifiers(); + type = qualType->elementType(); + return cv; + } + return {}; +} + auto Parser::lvalue_to_rvalue_conversion(ExpressionAST*& expr) -> bool { if (!is_glvalue(expr)) return false; diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index f2d30ae1..cedd8584 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -238,7 +238,11 @@ class Parser final { [[nodiscard]] auto parse_start_of_postfix_expression(ExpressionAST*& yyast, const ExprContext& ctx) -> bool; + [[nodiscard]] auto parse_member_expression(ExpressionAST*& yyast) -> bool; + [[nodiscard]] auto check_psuedo_destructor_access(MemberExpressionAST* ast) + -> bool; + [[nodiscard]] auto parse_subscript_expression(ExpressionAST*& yyast, const ExprContext& ctx) -> bool; [[nodiscard]] auto parse_call_expression(ExpressionAST*& yyast, @@ -828,6 +832,7 @@ class Parser final { void check_type_traits(); [[nodiscard]] auto strip_parentheses(ExpressionAST* ast) -> ExpressionAST*; + [[nodiscard]] auto strip_cv(const Type*& type) -> CvQualifiers; // standard conversions [[nodiscard]] auto lvalue_to_rvalue_conversion(ExpressionAST*& expr) -> bool; diff --git a/src/parser/cxx/type_traits.h b/src/parser/cxx/type_traits.h index 49c27c48..dc0ce333 100644 --- a/src/parser/cxx/type_traits.h +++ b/src/parser/cxx/type_traits.h @@ -114,8 +114,8 @@ class TypeTraits { } auto is_scalar(const Type* type) const -> bool { - return is_enum(type) || is_pointer(type) || is_member_pointer(type) || - is_null_pointer(type); + return is_arithmetic(type) || is_enum(type) || is_pointer(type) || + is_member_pointer(type) || is_null_pointer(type); } auto is_object(const Type* type) const -> bool { diff --git a/tests/unit_tests/sema/member_access_01.cc b/tests/unit_tests/sema/member_access_01.cc new file mode 100644 index 00000000..c7984f7d --- /dev/null +++ b/tests/unit_tests/sema/member_access_01.cc @@ -0,0 +1,44 @@ +// RUN: %cxx -verify -fcheck %s + +// clang-format off + +auto main() -> int { + using i32 = int; + using u32 = unsigned int; + + int i; + const int ci = 0; + int& ri = i; + const int& cri = ci; + + i.~i32(); + ci.~i32(); + ri.~i32(); + cri.~i32(); + + i.~u32(); // expected-error {{the type of object expression does not match the type being destroyed}} + + int* pi = nullptr; + + pi->~i32(); + pi->~u32(); // expected-error {{the type of object expression does not match the type being destroyed}} + + using nullptr_t = decltype(nullptr); + + (nullptr).~nullptr_t(); + + using int_ptr = i32*; + + int_ptr ip = nullptr; + ip.~int_ptr(); + ip.~nullptr_t(); // expected-error {{the type of object expression does not match the type being destroyed}} + + ip->~i32(); + ip->~u32(); // expected-error {{the type of object expression does not match the type being destroyed}} + + static_assert(__is_same(decltype(i.~i32()), void)); + + return 0; +} + +// clang-format on