diff --git a/src/parser/cxx/control.cc b/src/parser/cxx/control.cc index fea603b3..a028f276 100644 --- a/src/parser/cxx/control.cc +++ b/src/parser/cxx/control.cc @@ -851,4 +851,4 @@ auto Control::instantiate(TranslationUnit* unit, Symbol* symbol, return instantiate(symbol); } -} // namespace cxx \ No newline at end of file +} // namespace cxx diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index 4307b147..47b070ee 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -2508,14 +2508,14 @@ void Parser::check_member_expression(MemberExpressionAST* ast) { auto Parser::check_member_access(MemberExpressionAST* ast) -> bool { const Type* objectType = ast->baseExpression->type; - auto cv = strip_cv(objectType); + auto cv1 = 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); + cv1 = strip_cv(objectType); } auto classType = type_cast(objectType); @@ -2532,9 +2532,36 @@ auto Parser::check_member_access(MemberExpressionAST* ast) -> bool { if (symbol) { ast->type = symbol->type(); - } - // TODO: value category + if (symbol->isEnumerator()) { + ast->valueCategory = ValueCategory::kPrValue; + } else { + if (is_lvalue(ast->baseExpression)) { + ast->valueCategory = ValueCategory::kLValue; + } else { + ast->valueCategory = ValueCategory::kXValue; + } + + if (auto field = symbol_cast(symbol); + field && !field->isStatic()) { + auto cv2 = strip_cv(ast->type); + + auto vq = is_volatile(cv1) || is_volatile(cv2); + auto cq = is_const(cv1) || is_const(cv2); + + CvQualifiers cv = CvQualifiers::kNone; + if (vq) cv = CvQualifiers::kVolatile; + + if (!field->isMutable() && cq) { + cv = merge_cv(cv, CvQualifiers::kConst); + } + + if (cv != CvQualifiers::kNone) { + ast->type = control_->getQualType(ast->type, cv); + } + } + } + } return true; } @@ -6297,6 +6324,27 @@ auto Parser::strip_cv(const Type*& type) -> CvQualifiers { return {}; } +auto Parser::is_const(CvQualifiers cv) const -> bool { + return cv == CvQualifiers::kConst || cv == CvQualifiers::kConstVolatile; +} + +auto Parser::is_volatile(CvQualifiers cv) const -> bool { + return cv == CvQualifiers::kVolatile || cv == CvQualifiers::kConstVolatile; +} + +auto Parser::merge_cv(CvQualifiers cv1, CvQualifiers cv2) const + -> CvQualifiers { + CvQualifiers cv = CvQualifiers::kNone; + if (is_const(cv1) || is_const(cv2)) cv = CvQualifiers::kConst; + if (is_volatile(cv1) || is_volatile(cv2)) { + if (cv == CvQualifiers::kConst) + cv = CvQualifiers::kConstVolatile; + else + cv = CvQualifiers::kVolatile; + } + return cv; +} + auto Parser::lvalue_to_rvalue_conversion(ExpressionAST*& expr) -> bool { if (!is_glvalue(expr)) return false; @@ -10007,6 +10055,7 @@ auto Parser::declareField(DeclaratorAST* declarator, const Decl& decl) applySpecifiers(fieldSymbol, decl.specs); fieldSymbol->setName(name); fieldSymbol->setType(type); + fieldSymbol->setMutable(decl.specs.isMutable); if (auto alignment = control_->memoryLayout()->alignmentOf(type)) { fieldSymbol->setAlignment(alignment.value()); } diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index 2efbf98f..12295461 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -837,6 +837,10 @@ class Parser final { [[nodiscard]] auto strip_parentheses(ExpressionAST* ast) -> ExpressionAST*; [[nodiscard]] auto strip_cv(const Type*& type) -> CvQualifiers; + [[nodiscard]] auto merge_cv(CvQualifiers cv1, CvQualifiers cv2) const + -> CvQualifiers; + [[nodiscard]] auto is_const(CvQualifiers cv) const -> bool; + [[nodiscard]] auto is_volatile(CvQualifiers cv) const -> bool; // standard conversions [[nodiscard]] auto lvalue_to_rvalue_conversion(ExpressionAST*& expr) -> bool; diff --git a/src/parser/cxx/symbols.cc b/src/parser/cxx/symbols.cc index 9127d140..708cd858 100644 --- a/src/parser/cxx/symbols.cc +++ b/src/parser/cxx/symbols.cc @@ -564,6 +564,10 @@ auto FieldSymbol::isInline() const -> bool { return isInline_; } void FieldSymbol::setInline(bool isInline) { isInline_ = isInline; } +auto FieldSymbol::isMutable() const -> bool { return isMutable_; } + +void FieldSymbol::setMutable(bool isMutable) { isMutable_ = isMutable; } + auto FieldSymbol::offset() const -> int { return offset_; } void FieldSymbol::setOffset(int offset) { offset_ = offset; } diff --git a/src/parser/cxx/symbols.h b/src/parser/cxx/symbols.h index cdb127e7..e80e835a 100644 --- a/src/parser/cxx/symbols.h +++ b/src/parser/cxx/symbols.h @@ -575,6 +575,9 @@ class FieldSymbol final : public Symbol { [[nodiscard]] auto isInline() const -> bool; void setInline(bool isInline); + [[nodiscard]] auto isMutable() const -> bool; + void setMutable(bool isMutable); + [[nodiscard]] auto offset() const -> int; void setOffset(int offset); @@ -590,6 +593,7 @@ class FieldSymbol final : public Symbol { std::uint32_t isConstexpr_ : 1; std::uint32_t isConstinit_ : 1; std::uint32_t isInline_ : 1; + std::uint32_t isMutable_ : 1; }; }; int offset_{}; diff --git a/tests/unit_tests/sema/member_access_02.cc b/tests/unit_tests/sema/member_access_02.cc index f731e394..d8c451f4 100644 --- a/tests/unit_tests/sema/member_access_02.cc +++ b/tests/unit_tests/sema/member_access_02.cc @@ -6,6 +6,16 @@ struct X { static int s_value; }; +struct W { + int& r; + const int& cr; + + const int const_k = 0; + int k = 0; + + mutable int m = 0; +}; + auto main() -> int { X x; static_assert(__is_reference(decltype(x.kValue)) == false); @@ -27,5 +37,25 @@ auto main() -> int { static_assert(__is_reference(decltype(px->s_value)) == false); static_assert(__is_same(decltype(px->s_value), int)); + int i; + W w{i, i}; + + static_assert(__is_lvalue_reference(decltype(w.r))); + static_assert(__is_same(decltype(w.r), int&)); + + static_assert(__is_lvalue_reference(decltype(w.cr))); + static_assert(__is_same(decltype(w.cr), const int&)); + + static_assert(__is_same(decltype((w.const_k)), const int&)); + static_assert(__is_same(decltype((w.k)), int&)); + + const W cw{i, i}; + static_assert(__is_same(decltype(cw.const_k), const int)); + static_assert(__is_same(decltype(cw.k), int)); + + static_assert(__is_same(decltype((cw.const_k)), const int&)); + static_assert(__is_same(decltype((cw.k)), const int&)); + static_assert(__is_same(decltype((cw.m)), int&)); + return 0; }