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
100 changes: 100 additions & 0 deletions src/parser/cxx/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<LvalueReferenceType>(ast->type)) {
ast->type = refType->elementType();
ast->valueCategory = ValueCategory::kLValue;
return;
}

if (auto rvalueRefType = type_cast<RvalueReferenceType>(ast->type)) {
ast->type = rvalueRefType->elementType();

if (type_cast<FunctionType>(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<QualType>(sourceType)) {
cv1 = qualType->cvQualifiers();
sourceType = qualType->elementType();
}

auto targetRefType = type_cast<LvalueReferenceType>(targetType);
if (!targetRefType) return false;

targetType = targetRefType->elementType();

CvQualifiers cv2 = CvQualifiers::kNone;
if (auto qualType = type_cast<QualType>(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<QualType>(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;
Expand Down
12 changes: 12 additions & 0 deletions src/parser/cxx/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions tests/unit_tests/sema/static_cast_01.cc
Original file line number Diff line number Diff line change
@@ -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>(x))) == false);

// lvalue if lvalue reference to object type
static_assert(__is_lvalue_reference(decltype(static_cast<X&>(x))));

// rvalue if rvalue reference to object type
static_assert(__is_rvalue_reference(decltype(static_cast<X&&>(x))));

// prvalue
static_assert(__is_reference(decltype(static_cast<F*>(f))) == false);

// lvalue if lvalue reference to function type
static_assert(__is_lvalue_reference(decltype(static_cast<F&>(f))));

// lvalue if rvalue reference to function type
static_assert(__is_lvalue_reference(decltype(static_cast<F&&>(f))));
}
26 changes: 26 additions & 0 deletions tests/unit_tests/sema/static_cast_02.cc
Original file line number Diff line number Diff line change
@@ -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<void>(b);
static_cast<const void>(b);
static_cast<volatile void>(b);
static_cast<void>(123);
static_cast<void>(cb);

// check casts to derived class
D& d = static_cast<D&>(b);
const D& cd1 = static_cast<const D&>(b);
const D& cd2 = static_cast<const D&>(cb);
const volatile D& cd3 = static_cast<const volatile D&>(cb);

D& d = static_cast<D&>(cb); // expected-error {{invalid static_cast}}

return 0;
}