Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 70 additions & 13 deletions src/parser/cxx/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,7 @@ auto Parser::parse_unqualified_id(UnqualifiedIdAST*& yyast,

auto ast = make_node<DestructorIdAST>(pool_);
yyast = ast;
ast->tildeLoc = tildeLoc;
ast->id = decltypeName;

return true;
Expand All @@ -1469,6 +1470,7 @@ auto Parser::parse_unqualified_id(UnqualifiedIdAST*& yyast,

auto ast = make_node<DestructorIdAST>(pool_);
yyast = ast;
ast->tildeLoc = tildeLoc;
ast->id = name;

return true;
Expand Down Expand Up @@ -2474,32 +2476,72 @@ 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,
ast->isTemplateIntroduced,
/*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<PointerType>(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<DestructorIdAST>(ast->unqualifiedId);
if (!dtor) return true;

auto name = ast_cast<NameIdAST>(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;
Expand Down Expand Up @@ -2548,6 +2590,12 @@ auto Parser::parse_call_expression(ExpressionAST*& yyast,
argumentTypes.push_back(argumentType);
}

if (auto access = ast_cast<MemberExpressionAST>(ast->baseExpression)) {
if (ast_cast<DestructorIdAST>(access->unqualifiedId)) {
ast->type = control_->getVoidType();
}
}

return true;
}

Expand Down Expand Up @@ -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<QualType>(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;

Expand Down
5 changes: 5 additions & 0 deletions src/parser/cxx/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/parser/cxx/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
44 changes: 44 additions & 0 deletions tests/unit_tests/sema/member_access_01.cc
Original file line number Diff line number Diff line change
@@ -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