Skip to content

Commit fe49027

Browse files
committed
Improve checks of conditional expressions
1 parent 62ede79 commit fe49027

File tree

5 files changed

+240
-18
lines changed

5 files changed

+240
-18
lines changed

src/parser/cxx/control.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,12 @@ auto Control::add_volatile(const Type* type) -> const Type* {
818818
return d->traits.add_volatile(type);
819819
}
820820

821+
auto Control::add_cv(const Type* type, CvQualifiers cv) -> const Type* {
822+
if (cxx::is_const(cv)) type = add_const(type);
823+
if (cxx::is_volatile(cv)) type = add_volatile(type);
824+
return type;
825+
}
826+
821827
auto Control::get_cv_qualifiers(const Type* type) -> CvQualifiers {
822828
if (auto qualType = type_cast<QualType>(type))
823829
return qualType->cvQualifiers();

src/parser/cxx/control.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ class Control {
282282
[[nodiscard]] auto add_const_ref(const Type* type) -> const Type*;
283283
[[nodiscard]] auto add_const(const Type* type) -> const Type*;
284284
[[nodiscard]] auto add_volatile(const Type* type) -> const Type*;
285+
[[nodiscard]] auto add_cv(const Type* type, CvQualifiers cv) -> const Type*;
285286
[[nodiscard]] auto get_cv_qualifiers(const Type* type) -> CvQualifiers;
286287

287288
// pointers

src/parser/cxx/parser.cc

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3172,6 +3172,8 @@ void Parser::parse_condition(ExpressionAST*& yyast, const ExprContext& ctx) {
31723172
ast->initializer = initializer;
31733173
ast->symbol = symbol;
31743174

3175+
check(ast);
3176+
31753177
return true;
31763178
};
31773179

@@ -5892,10 +5894,7 @@ auto Parser::parse_brace_or_equal_initializer(ExpressionAST*& yyast) -> bool {
58925894
parse_error("expected an intializer");
58935895
}
58945896

5895-
if (ast->expression) {
5896-
ast->type = ast->expression->type;
5897-
ast->valueCategory = ast->expression->valueCategory;
5898-
}
5897+
check(ast);
58995898

59005899
return true;
59015900
}
@@ -5956,23 +5955,19 @@ auto Parser::parse_braced_init_list(BracedInitListAST*& ast,
59565955

59575956
expect(TokenKind::T_RBRACE, ast->rbraceLoc);
59585957

5959-
return true;
5960-
}
5961-
5962-
if (match(TokenKind::T_COMMA, ast->commaLoc)) {
5958+
} else if (match(TokenKind::T_COMMA, ast->commaLoc)) {
59635959
expect(TokenKind::T_RBRACE, ast->rbraceLoc);
59645960

5965-
return true;
5966-
}
5967-
5968-
if (!match(TokenKind::T_RBRACE, ast->rbraceLoc)) {
5961+
} else if (!match(TokenKind::T_RBRACE, ast->rbraceLoc)) {
59695962
if (!parse_initializer_list(ast->expressionList, ctx)) {
59705963
parse_error("expected initializer list");
59715964
}
59725965

59735966
expect(TokenKind::T_RBRACE, ast->rbraceLoc);
59745967
}
59755968

5969+
check(ast);
5970+
59765971
return true;
59775972
}
59785973

@@ -6046,9 +6041,8 @@ auto Parser::parse_designated_initializer_clause(
60466041

60476042
void Parser::parse_expr_or_braced_init_list(ExpressionAST*& yyast,
60486043
const ExprContext& ctx) {
6049-
BracedInitListAST* bracedInitList = nullptr;
6050-
6051-
if (parse_braced_init_list(bracedInitList, ctx)) {
6044+
if (BracedInitListAST* bracedInitList = nullptr;
6045+
parse_braced_init_list(bracedInitList, ctx)) {
60526046
yyast = bracedInitList;
60536047
} else {
60546048
parse_expression(yyast, ctx);

src/parser/cxx/type_checker.cc

Lines changed: 173 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ struct TypeChecker::Visitor {
109109
ExpressionAST*& other)
110110
-> const Type*;
111111

112+
[[nodiscard]] auto composite_pointer_type(ExpressionAST*& expr,
113+
ExpressionAST*& other)
114+
-> const Type*;
115+
112116
[[nodiscard]] auto is_null_pointer_constant(ExpressionAST* expr) const
113117
-> bool;
114118

@@ -890,11 +894,135 @@ void TypeChecker::Visitor::operator()(BinaryExpressionAST* ast) {
890894
} // switch
891895
}
892896

893-
void TypeChecker::Visitor::operator()(ConditionalExpressionAST* ast) {}
897+
void TypeChecker::Visitor::operator()(ConditionalExpressionAST* ast) {
898+
auto check_void_type = [&] {
899+
if (!control()->is_void(ast->iftrueExpression->type) &&
900+
!control()->is_void(ast->iffalseExpression->type))
901+
return false;
902+
903+
// one of the two expressions is void
904+
if (ast_cast<ThrowExpressionAST>(
905+
strip_parentheses(ast->iftrueExpression))) {
906+
ast->type = ast->iffalseExpression->type;
907+
ast->valueCategory = ast->iffalseExpression->valueCategory;
908+
return true;
909+
}
910+
911+
if (ast_cast<ThrowExpressionAST>(
912+
strip_parentheses(ast->iffalseExpression))) {
913+
ast->type = ast->iftrueExpression->type;
914+
ast->valueCategory = ast->iftrueExpression->valueCategory;
915+
return true;
916+
}
917+
918+
if (!control()->is_same(ast->iftrueExpression->type,
919+
ast->iffalseExpression->type)) {
920+
error(ast->questionLoc,
921+
std::format(
922+
"left operand to ? is '{}', but right operand is of type '{}'",
923+
to_string(ast->iftrueExpression->type),
924+
to_string(ast->iffalseExpression->type)));
925+
}
926+
927+
ast->type = control()->getVoidType();
928+
ast->valueCategory = ValueCategory::kPrValue;
929+
930+
return true;
931+
};
932+
933+
auto check_same_type_and_value_category = [&] {
934+
if (ast->iftrueExpression->valueCategory !=
935+
ast->iffalseExpression->valueCategory) {
936+
return false;
937+
}
938+
939+
if (!control()->is_same(control()->remove_cv(ast->iftrueExpression->type),
940+
control()->remove_cv(ast->iffalseExpression->type)))
941+
return false;
942+
943+
ast->valueCategory = ast->iftrueExpression->valueCategory;
944+
ast->type = ast->iftrueExpression->type;
945+
946+
return true;
947+
};
948+
949+
auto check_arith_types = [&] {
950+
if (!control()->is_arithmetic_or_unscoped_enum(ast->iftrueExpression->type))
951+
return false;
952+
if (!control()->is_arithmetic_or_unscoped_enum(
953+
ast->iffalseExpression->type))
954+
return false;
955+
956+
ast->type = usual_arithmetic_conversion(ast->iftrueExpression,
957+
ast->iffalseExpression);
958+
959+
if (!ast->type) return false;
960+
961+
ast->valueCategory = ValueCategory::kPrValue;
962+
963+
return true;
964+
};
965+
966+
auto check_same_types = [&] {
967+
if (!control()->is_same(ast->iftrueExpression->type,
968+
ast->iffalseExpression->type))
969+
return false;
970+
971+
ast->type = ast->iftrueExpression->type;
972+
ast->valueCategory = ValueCategory::kPrValue;
973+
return true;
974+
};
975+
976+
auto check_compatible_pointers = [&] {
977+
if (!control()->is_pointer(ast->iftrueExpression->type) &&
978+
!control()->is_pointer(ast->iffalseExpression->type))
979+
return false;
980+
981+
ast->type =
982+
composite_pointer_type(ast->iftrueExpression, ast->iffalseExpression);
983+
984+
ast->valueCategory = ValueCategory::kPrValue;
985+
986+
if (!ast->type) return false;
987+
988+
return true;
989+
};
990+
991+
if (ast->iftrueExpression && ast->iffalseExpression) {
992+
if (check_void_type()) return;
993+
if (check_same_type_and_value_category()) return;
994+
995+
(void)array_to_pointer_conversion(ast->iftrueExpression);
996+
(void)function_to_pointer_conversion(ast->iftrueExpression);
997+
998+
(void)array_to_pointer_conversion(ast->iffalseExpression);
999+
(void)function_to_pointer_conversion(ast->iffalseExpression);
1000+
1001+
if (check_arith_types()) return;
1002+
if (check_same_types()) return;
1003+
if (check_compatible_pointers()) return;
1004+
}
1005+
1006+
if (!ast->type) {
1007+
auto iftrueType =
1008+
ast->iftrueExpression ? ast->iftrueExpression->type : nullptr;
1009+
1010+
auto iffalseType =
1011+
ast->iffalseExpression ? ast->iffalseExpression->type : nullptr;
1012+
1013+
error(ast->questionLoc,
1014+
std::format(
1015+
"left operand to ? is '{}', but right operand is of type '{}'",
1016+
to_string(iftrueType), to_string(iffalseType)));
1017+
}
1018+
}
8941019

8951020
void TypeChecker::Visitor::operator()(YieldExpressionAST* ast) {}
8961021

897-
void TypeChecker::Visitor::operator()(ThrowExpressionAST* ast) {}
1022+
void TypeChecker::Visitor::operator()(ThrowExpressionAST* ast) {
1023+
ast->type = control()->getVoidType();
1024+
ast->valueCategory = ValueCategory::kPrValue;
1025+
}
8981026

8991027
void TypeChecker::Visitor::operator()(AssignmentExpressionAST* ast) {
9001028
if (!ast->leftExpression) return;
@@ -914,7 +1042,10 @@ void TypeChecker::Visitor::operator()(TypeTraitExpressionAST* ast) {
9141042
ast->type = control()->getBoolType();
9151043
}
9161044

917-
void TypeChecker::Visitor::operator()(ConditionExpressionAST* ast) {}
1045+
void TypeChecker::Visitor::operator()(ConditionExpressionAST* ast) {
1046+
ast->type = control()->getBoolType();
1047+
ast->valueCategory = ValueCategory::kPrValue;
1048+
}
9181049

9191050
void TypeChecker::Visitor::operator()(EqualInitializerAST* ast) {
9201051
if (!ast->expression) return;
@@ -1605,6 +1736,45 @@ auto TypeChecker::Visitor::usual_arithmetic_conversion(ExpressionAST*& expr,
16051736
return control()->getIntType();
16061737
}
16071738

1739+
auto TypeChecker::Visitor::composite_pointer_type(ExpressionAST*& expr,
1740+
ExpressionAST*& other)
1741+
-> const Type* {
1742+
if (control()->is_null_pointer(expr->type) &&
1743+
control()->is_null_pointer(other->type))
1744+
return control()->getNullptrType();
1745+
1746+
if (is_null_pointer_constant(expr)) return other->type;
1747+
if (is_null_pointer_constant(other)) return expr->type;
1748+
1749+
auto is_pointer_to_cv_void = [this](const Type* type) {
1750+
if (!control()->is_pointer(type)) return false;
1751+
if (!control()->is_void(control()->get_element_type(type))) return false;
1752+
return true;
1753+
};
1754+
1755+
if (control()->is_pointer(expr->type) && control()->is_pointer(other->type)) {
1756+
auto t1 = control()->get_element_type(expr->type);
1757+
const auto cv1 = strip_cv(t1);
1758+
1759+
auto t2 = control()->get_element_type(other->type);
1760+
const auto cv2 = strip_cv(t2);
1761+
1762+
if (control()->is_void(t1)) {
1763+
return control()->getPointerType(control()->add_cv(t1, cv2));
1764+
}
1765+
1766+
if (control()->is_void(t2)) {
1767+
return control()->getPointerType(control()->add_cv(t2, cv1));
1768+
}
1769+
1770+
// TODO: check for noexcept function pointers
1771+
1772+
// TODO: check for reference related
1773+
}
1774+
1775+
return nullptr;
1776+
}
1777+
16081778
auto TypeChecker::Visitor::is_null_pointer_constant(ExpressionAST* expr) const
16091779
-> bool {
16101780
if (control()->is_null_pointer(expr->type)) return true;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %cxx -verify -fcheck %s
2+
3+
void returns_void();
4+
int returns_int();
5+
int& returns_int_ref();
6+
7+
void test_conditional(bool x) {
8+
// clang-format off
9+
10+
// expected-error@1 {{left operand to ? is 'void', but right operand is of type 'int'}}
11+
x ? returns_void() : returns_int();
12+
13+
// expected-error@1 {{left operand to ? is 'int', but right operand is of type 'void'}}
14+
x ? returns_int() : returns_void();
15+
16+
// clang-format on
17+
18+
static_assert(__is_same(void, decltype(x ? throw 0 : throw 0)));
19+
20+
static_assert(__is_same(int, decltype(x ? throw 0 : returns_int())));
21+
static_assert(__is_same(int, decltype(x ? returns_int() : throw 0)));
22+
23+
static_assert(__is_same(int&, decltype(x ? returns_int_ref() : throw 0)));
24+
static_assert(__is_same(int&, decltype(x ? throw 0 : returns_int_ref())));
25+
26+
static_assert(__is_same(int, decltype(false ? 1 : 0)));
27+
28+
void* void_ptr;
29+
int* int_ptr;
30+
const int* const_int_ptr;
31+
32+
static_assert(__is_same(decltype(nullptr), decltype(x ? nullptr : nullptr)));
33+
static_assert(__is_same(void*&, decltype(x ? void_ptr : throw 0)));
34+
static_assert(__is_same(int*, decltype(x ? int_ptr : nullptr)));
35+
static_assert(__is_same(int*, decltype(x ? nullptr : int_ptr)));
36+
37+
static_assert(__is_same(void*, decltype(x ? void_ptr : int_ptr)));
38+
static_assert(__is_same(void*, decltype(x ? int_ptr : void_ptr)));
39+
40+
static_assert(__is_same(const void*, decltype(x ? void_ptr : const_int_ptr)));
41+
static_assert(__is_same(const void*, decltype(x ? const_int_ptr : void_ptr)));
42+
43+
static_assert(__is_same(int, decltype(x ? 0 : '0')));
44+
static_assert(__is_same(unsigned, decltype(x ? 0 : 1u)));
45+
static_assert(__is_same(long, decltype(x ? 0 : 1l)));
46+
47+
char s[10];
48+
char* cp;
49+
50+
static_assert(__is_same(char*, decltype(x ? s : cp)));
51+
}

0 commit comments

Comments
 (0)