From a9d78c61b0ec938a0c6c152ecb4d2e83bc47b8aa Mon Sep 17 00:00:00 2001 From: Roberto Raggi Date: Wed, 19 Feb 2025 20:33:13 +0100 Subject: [PATCH] Start to vove the type checker out of the parser This is needed to check the instantiation of templates. --- packages/cxx-gen-ast/src/gen_ast_h.ts | 12 + src/parser/cxx/ast.h | 12 + src/parser/cxx/parser.cc | 787 +-------------------- src/parser/cxx/parser.h | 40 +- src/parser/cxx/type_checker.cc | 944 +++++++++++++++++++++++++- src/parser/cxx/type_checker.h | 13 + 6 files changed, 1007 insertions(+), 801 deletions(-) diff --git a/packages/cxx-gen-ast/src/gen_ast_h.ts b/packages/cxx-gen-ast/src/gen_ast_h.ts index 2e75e478..a9f29f7a 100644 --- a/packages/cxx-gen-ast/src/gen_ast_h.ts +++ b/packages/cxx-gen-ast/src/gen_ast_h.ts @@ -228,6 +228,18 @@ private: ASTKind kind_; }; +template +auto make_node(Arena* arena) -> T* { + auto node = new (arena) T(); + return node; +} + +template +auto make_list_node(Arena* arena, T* element = nullptr) -> List* { + auto list = new (arena) List(element); + return list; +} + [[nodiscard]] inline auto firstSourceLocation(SourceLocation loc) -> SourceLocation { return loc; } template diff --git a/src/parser/cxx/ast.h b/src/parser/cxx/ast.h index 40102c93..c0240e1f 100644 --- a/src/parser/cxx/ast.h +++ b/src/parser/cxx/ast.h @@ -108,6 +108,18 @@ class AST : public Managed { ASTKind kind_; }; +template +auto make_node(Arena* arena) -> T* { + auto node = new (arena) T(); + return node; +} + +template +auto make_list_node(Arena* arena, T* element = nullptr) -> List* { + auto list = new (arena) List(element); + return list; +} + [[nodiscard]] inline auto firstSourceLocation(SourceLocation loc) -> SourceLocation { return loc; diff --git a/src/parser/cxx/parser.cc b/src/parser/cxx/parser.cc index b54f27a2..5a1f0508 100644 --- a/src/parser/cxx/parser.cc +++ b/src/parser/cxx/parser.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -46,18 +47,6 @@ namespace cxx { namespace { -template -auto make_node(Arena* arena) -> T* { - auto node = new (arena) T(); - return node; -} - -template -auto make_list_node(Arena* arena, T* element = nullptr) -> List* { - auto list = new (arena) List(element); - return list; -} - inline constexpr struct { auto operator()(const StringLiteral*) const -> bool { return true; } auto operator()(auto value) const -> bool { return !!value; } @@ -2031,29 +2020,7 @@ auto Parser::parse_this_expression(ExpressionAST*& yyast) -> bool { ast->thisLoc = thisLoc; ast->valueCategory = ValueCategory::kPrValue; - for (auto current = scope_; current; current = current->parent()) { - if (auto classSymbol = symbol_cast(current->owner())) { - // maybe a this expression in a field initializer - ast->type = control_->getPointerType(classSymbol->type()); - break; - } - - if (auto functionSymbol = symbol_cast(current->owner())) { - if (auto classSymbol = - symbol_cast(functionSymbol->enclosingSymbol())) { - auto functionType = type_cast(functionSymbol->type()); - const auto cv = functionType->cvQualifiers(); - if (cv != CvQualifiers::kNone) { - auto elementType = control_->getQualType(classSymbol->type(), cv); - ast->type = control_->getPointerType(elementType); - } else { - ast->type = control_->getPointerType(classSymbol->type()); - } - } - - break; - } - } + check(ast); return true; } @@ -2087,10 +2054,7 @@ auto Parser::parse_nested_expession(ExpressionAST*& yyast, expect(TokenKind::T_RPAREN, ast->rparenLoc); - if (ast->expression) { - ast->type = ast->expression->type; - ast->valueCategory = ast->expression->valueCategory; - } + check(ast); return true; } @@ -3386,7 +3350,7 @@ auto Parser::parse_sizeof_expression(ExpressionAST*& yyast, parse_error("expected an expression"); } - ast->type = control_->getSizeType(); + check(ast); return true; } @@ -3418,7 +3382,8 @@ auto Parser::parse_alignof_expression(ExpressionAST*& yyast, ast->lparenLoc = lparenLoc; ast->typeId = typeId; ast->rparenLoc = rparenLoc; - ast->type = control_->getSizeType(); + + check(ast); return true; }; @@ -3434,7 +3399,7 @@ auto Parser::parse_alignof_expression(ExpressionAST*& yyast, parse_error("expected an expression"); } - ast->type = control_->getSizeType(); + check(ast); return true; } @@ -3801,100 +3766,7 @@ auto Parser::parse_binary_expression_helper(ExpressionAST*& yyast, Prec minPrec, ast->rightExpression = rhs; ast->op = op; - if (ast->leftExpression && ast->rightExpression) { - switch (ast->op) { - case TokenKind::T_DOT_STAR: - break; - - case TokenKind::T_MINUS_GREATER_STAR: - break; - - case TokenKind::T_STAR: - case TokenKind::T_SLASH: - case TokenKind::T_PLUS: - case TokenKind::T_MINUS: - ast->type = usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - break; - - case TokenKind::T_PERCENT: - if (!control_->is_integral_or_unscoped_enum( - ast->leftExpression->type)) - break; - - if (!control_->is_integral_or_unscoped_enum( - ast->rightExpression->type)) - break; - - ast->type = usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - - break; - - case TokenKind::T_LESS_LESS: - case TokenKind::T_GREATER_GREATER: - if (!control_->is_integral_or_unscoped_enum( - ast->leftExpression->type)) - break; - - if (!control_->is_integral_or_unscoped_enum( - ast->rightExpression->type)) - break; - - (void)usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - - ast->type = ast->leftExpression->type; - break; - - case TokenKind::T_LESS_EQUAL_GREATER: - (void)usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - ast->type = control_->getIntType(); - break; - - case TokenKind::T_LESS_EQUAL: - case TokenKind::T_GREATER_EQUAL: - case TokenKind::T_LESS: - case TokenKind::T_GREATER: - case TokenKind::T_EQUAL_EQUAL: - case TokenKind::T_EXCLAIM_EQUAL: - (void)usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - ast->type = control_->getBoolType(); - break; - - case TokenKind::T_AMP: - case TokenKind::T_CARET: - case TokenKind::T_BAR: - if (!control_->is_integral_or_unscoped_enum( - ast->leftExpression->type)) - break; - - if (!control_->is_integral_or_unscoped_enum( - ast->rightExpression->type)) - break; - - ast->type = usual_arithmetic_conversion(ast->leftExpression, - ast->rightExpression); - - break; - - case TokenKind::T_AMP_AMP: - case TokenKind::T_BAR_BAR: - (void)implicit_conversion(ast->leftExpression, - control_->getBoolType()); - - (void)implicit_conversion(ast->rightExpression, - control_->getBoolType()); - - ast->type = control_->getBoolType(); - break; - - default: - cxx_runtime_error("invalid operator"); - } // switch - } + check(ast); yyast = ast; } @@ -4074,6 +3946,9 @@ auto Parser::parse_maybe_expression(ExpressionAST*& yyast, if (ast->rightExpression) { ast->type = ast->rightExpression->type; } + + check(ast); + yyast = ast; } @@ -6338,633 +6213,23 @@ auto Parser::merge_cv(CvQualifiers cv1, CvQualifiers cv2) const return cv; } -auto Parser::lvalue_to_rvalue_conversion(ExpressionAST*& expr) -> bool { - if (!is_glvalue(expr)) return false; - - auto unref = control_->remove_cvref(expr->type); - if (control_->is_function(unref)) return false; - if (control_->is_array(unref)) return false; - if (!control_->is_complete(unref)) return false; - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kLValueToRValueConversion; - cast->expression = expr; - cast->type = control_->remove_reference(expr->type); - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - return true; -} - -auto Parser::array_to_pointer_conversion(ExpressionAST*& expr) -> bool { - auto unref = control_->remove_reference(expr->type); - if (!control_->is_array(unref)) return false; - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kArrayToPointerConversion; - cast->expression = expr; - cast->type = control_->add_pointer(control_->remove_extent(unref)); - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - return true; -} - -auto Parser::function_to_pointer_conversion(ExpressionAST*& expr) -> bool { - auto unref = control_->remove_reference(expr->type); - if (!control_->is_function(unref)) return false; - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFunctionToPointerConversion; - cast->expression = expr; - cast->type = control_->add_pointer(unref); - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - return true; -} - -auto Parser::integral_promotion(ExpressionAST*& expr) -> bool { - if (!is_prvalue(expr)) return false; - - auto ty = control_->remove_cv(expr->type); - - if (!control_->is_integral(ty) && !control_->is_enum(ty)) return false; - - auto make_implicit_cast = [&](const Type* type) { - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kIntegralPromotion; - cast->expression = expr; - cast->type = type; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - }; - - // TODO: bit-fields - - switch (ty->kind()) { - case TypeKind::kChar: - case TypeKind::kSignedChar: - case TypeKind::kUnsignedChar: - case TypeKind::kShortInt: - case TypeKind::kUnsignedShortInt: { - make_implicit_cast(control_->getIntType()); - return true; - } - - case TypeKind::kChar8: - case TypeKind::kChar16: - case TypeKind::kChar32: - case TypeKind::kWideChar: { - make_implicit_cast(control_->getIntType()); - return true; - } - - case TypeKind::kBool: { - make_implicit_cast(control_->getIntType()); - return true; - } - - default: - break; - } // switch - - if (auto enumType = type_cast(ty)) { - auto type = enumType->underlyingType(); - - if (!type) { - // TODO: compute the from the value of the enumeration - type = control_->getIntType(); - } - - make_implicit_cast(type); - - return true; - } - - return false; -} - -auto Parser::floating_point_promotion(ExpressionAST*& expr) -> bool { - if (!is_prvalue(expr)) return false; - - auto ty = control_->remove_cv(expr->type); - - if (!control_->is_floating_point(ty)) return false; - - if (ty->kind() != TypeKind::kFloat) return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFloatingPointPromotion; - cast->expression = expr; - cast->type = control_->getDoubleType(); - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; -} - -auto Parser::integral_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - if (!control_->is_integral_or_unscoped_enum(expr->type)) return false; - if (!control_->is_integer(destinationType)) return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kIntegralConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; -} - -auto Parser::floating_point_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - if (!control_->is_floating_point(expr->type)) return false; - if (!control_->is_floating_point(destinationType)) return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFloatingPointConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; -} - -auto Parser::floating_integral_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - auto make_integral_conversion = [&] { - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFloatingIntegralConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - }; - - if (control_->is_integral_or_unscoped_enum(expr->type) && - control_->is_floating_point(destinationType)) { - make_integral_conversion(); - return true; - } - - if (!control_->is_floating_point(expr->type)) return false; - if (!control_->is_integer(destinationType)) return false; - - make_integral_conversion(); - - return true; -} - -auto Parser::pointer_to_member_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - if (!control_->is_member_pointer(destinationType)) return false; - - auto make_implicit_cast = [&] { - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kPointerToMemberConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - }; - - auto can_convert_null_pointer_constant = [&] { - if (!is_null_pointer_constant(expr)) return false; - - make_implicit_cast(); - - return true; - }; - - auto can_convert_member_object_pointer = [&] { - auto memberObjectPointerType = - type_cast(expr->type); - - if (!memberObjectPointerType) return false; - - auto destinationMemberObjectPointerType = - type_cast(destinationType); - - if (!destinationMemberObjectPointerType) return false; - - if (control_->get_cv_qualifiers(memberObjectPointerType->elementType()) != - control_->get_cv_qualifiers( - destinationMemberObjectPointerType->elementType())) - return false; - - if (!control_->is_base_of(destinationMemberObjectPointerType->classType(), - memberObjectPointerType->classType())) - return false; - - make_implicit_cast(); - - return true; - }; - - auto can_convert_member_function_pointer = [&] { - auto memberFunctionPointerType = - type_cast(expr->type); - - if (!memberFunctionPointerType) return false; - - auto destinationMemberFunctionPointerType = - type_cast(destinationType); - - if (!destinationMemberFunctionPointerType) return false; - - if (control_->get_cv_qualifiers( - memberFunctionPointerType->functionType()) != - control_->get_cv_qualifiers( - destinationMemberFunctionPointerType->functionType())) - return false; - - if (!control_->is_base_of(destinationMemberFunctionPointerType->classType(), - memberFunctionPointerType->classType())) - return false; - - make_implicit_cast(); - - return true; - }; - - if (can_convert_null_pointer_constant()) return true; - if (can_convert_member_object_pointer()) return true; - - return false; -} - -auto Parser::pointer_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - auto make_implicit_cast = [&] { - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kPointerConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - }; - - auto can_convert_null_pointer_literal = [&] { - if (!is_null_pointer_constant(expr)) return false; - - if (!control_->is_pointer(destinationType) && - !control_->is_null_pointer(destinationType)) - return false; - - make_implicit_cast(); - - return true; - }; - - auto can_convert_to_void_pointer = [&] { - const auto pointerType = type_cast(expr->type); - if (!pointerType) return false; - - const auto destinationPointerType = type_cast(destinationType); - if (!destinationPointerType) return false; - - if (control_->get_cv_qualifiers(pointerType->elementType()) != - control_->get_cv_qualifiers(destinationPointerType->elementType())) - return false; - - if (!control_->is_void( - control_->remove_cv(destinationPointerType->elementType()))) - return false; - - make_implicit_cast(); - - return true; - }; - - auto can_convert_to_base_class_pointer = [&] { - const auto pointerType = type_cast(expr->type); - if (!pointerType) return false; - - const auto destinationPointerType = type_cast(destinationType); - if (!destinationPointerType) return false; - - if (control_->get_cv_qualifiers(pointerType->elementType()) != - control_->get_cv_qualifiers(destinationPointerType->elementType())) - return false; - - if (!control_->is_base_of( - control_->remove_cv(destinationPointerType->elementType()), - control_->remove_cv(pointerType->elementType()))) - return false; - - make_implicit_cast(); - - return true; - }; - - if (can_convert_null_pointer_literal()) return true; - if (can_convert_to_void_pointer()) return true; - if (can_convert_to_base_class_pointer()) return true; - - return false; -} - -auto Parser::function_pointer_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!is_prvalue(expr)) return false; - - auto can_remove_noexcept_from_function = [&] { - const auto pointerType = type_cast(expr->type); - if (!pointerType) return false; - - const auto functionType = - type_cast(pointerType->elementType()); - - if (!functionType) return false; - - if (functionType->isNoexcept()) return false; - - const auto destinationPointerType = type_cast(destinationType); - if (!destinationPointerType) return false; - - const auto destinationFunctionType = - type_cast(destinationPointerType->elementType()); - - if (!destinationFunctionType) return false; - - if (!control_->is_same(control_->remove_noexcept(functionType), - destinationFunctionType)) - return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFunctionPointerConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; - }; - - auto can_remove_noexcept_from_member_function__pointer = [&] { - const auto memberFunctionPointer = - type_cast(expr->type); - - if (!memberFunctionPointer) return false; - - if (!memberFunctionPointer->functionType()->isNoexcept()) return false; - - const auto destinationMemberFunctionPointer = - type_cast(destinationType); - - if (!destinationMemberFunctionPointer) return false; - - if (destinationMemberFunctionPointer->functionType()->isNoexcept()) - return false; - - if (!control_->is_same( - control_->remove_noexcept(memberFunctionPointer->functionType()), - destinationMemberFunctionPointer->functionType())) - return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kFunctionPointerConversion; - cast->expression = expr; - cast->type = destinationType; - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; - }; - - if (can_remove_noexcept_from_function()) return true; - if (can_remove_noexcept_from_member_function__pointer()) return true; - - return false; -} - -auto Parser::boolean_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - if (!type_cast(control_->remove_cv(destinationType))) return false; - - if (!is_prvalue(expr)) return false; - - if (!control_->is_arithmetic_or_unscoped_enum(expr->type) && - !control_->is_pointer(expr->type) && - !control_->is_member_pointer(expr->type)) - return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kBooleanConversion; - cast->expression = expr; - cast->type = control_->getBoolType(); - cast->valueCategory = ValueCategory::kPrValue; - expr = cast; - - return true; -} - -auto Parser::temporary_materialization_conversion(ExpressionAST*& expr) - -> bool { - if (!is_prvalue(expr)) return false; - - auto cast = make_node(pool_); - cast->castKind = ImplicitCastKind::kTemporaryMaterializationConversion; - cast->expression = expr; - cast->type = control_->remove_reference(expr->type); - cast->valueCategory = ValueCategory::kXValue; - expr = cast; - - return true; -} - -auto Parser::qualification_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool { - return false; -} - auto Parser::ensure_prvalue(ExpressionAST*& expr) -> bool { - if (lvalue_to_rvalue_conversion(expr)) { - expr->valueCategory = ValueCategory::kPrValue; - return true; - } - - if (array_to_pointer_conversion(expr)) { - expr->valueCategory = ValueCategory::kPrValue; - return true; - } - - if (function_to_pointer_conversion(expr)) { - expr->valueCategory = ValueCategory::kPrValue; - return true; - } - - return false; + TypeChecker checker{unit}; + checker.setScope(scope_); + return checker.ensure_prvalue(expr); } auto Parser::implicit_conversion(ExpressionAST*& expr, const Type* destinationType) -> bool { - if (!expr || !expr->type) return false; - if (!destinationType) return false; - - if (control_->is_same(expr->type, destinationType)) return true; - - auto savedValueCategory = expr->valueCategory; - auto savedExpr = expr; - auto didConvert = false; - - if (lvalue_to_rvalue_conversion(expr)) { - didConvert = true; - } else if (array_to_pointer_conversion(expr)) { - didConvert = true; - } else if (function_to_pointer_conversion(expr)) { - didConvert = true; - } - - if (control_->is_scalar(expr->type)) { - expr->type = control_->remove_cv(expr->type); - } - - if (integral_promotion(expr)) return true; - if (floating_point_promotion(expr)) return true; - if (integral_conversion(expr, destinationType)) return true; - if (floating_point_conversion(expr, destinationType)) return true; - if (floating_integral_conversion(expr, destinationType)) return true; - if (pointer_conversion(expr, destinationType)) return true; - if (pointer_to_member_conversion(expr, destinationType)) return true; - if (boolean_conversion(expr, destinationType)) return true; - if (function_pointer_conversion(expr, destinationType)) return true; - if (qualification_conversion(expr, destinationType)) return true; - - if (didConvert) return true; - - expr = savedExpr; - expr->valueCategory = savedValueCategory; - - return false; -} - -auto Parser::usual_arithmetic_conversion(ExpressionAST*& expr, - ExpressionAST*& other) -> const Type* { - if (!expr || !expr->type) return nullptr; - if (!other || !other->type) return nullptr; - - ExpressionAST* savedExpr = expr; - ExpressionAST* savedOther = other; - - auto unmodifiedExpressions = [&]() -> const Type* { - expr = savedExpr; - other = savedOther; - return nullptr; - }; - - if (!control_->is_arithmetic_or_unscoped_enum(expr->type) && - !control_->is_arithmetic_or_unscoped_enum(other->type)) - return unmodifiedExpressions(); - - (void)lvalue_to_rvalue_conversion(expr); - (void)lvalue_to_rvalue_conversion(other); - - if (control_->is_scoped_enum(expr->type) || - control_->is_scoped_enum(other->type)) - return unmodifiedExpressions(); - - if (control_->is_floating_point(expr->type) || - control_->is_floating_point(other->type)) { - auto leftType = control_->remove_cv(expr->type); - auto rightType = control_->remove_cv(other->type); - - if (control_->is_same(leftType, rightType)) return leftType; - - if (!control_->is_floating_point(leftType)) { - if (floating_integral_conversion(expr, rightType)) return rightType; - return unmodifiedExpressions(); - } else if (!control_->is_floating_point(rightType)) { - if (floating_integral_conversion(other, leftType)) return leftType; - return unmodifiedExpressions(); - } else if (leftType->kind() == TypeKind::kLongDouble || - rightType->kind() == TypeKind::kLongDouble) { - (void)floating_point_conversion(expr, control_->getLongDoubleType()); - return control_->getLongDoubleType(); - } else if (leftType->kind() == TypeKind::kDouble || - rightType->kind() == TypeKind::kDouble) { - (void)floating_point_conversion(expr, control_->getDoubleType()); - return control_->getDoubleType(); - } - - return unmodifiedExpressions(); - } - - (void)integral_promotion(expr); - (void)integral_promotion(other); - - const auto leftType = control_->remove_cv(expr->type); - const auto rightType = control_->remove_cv(other->type); - - if (control_->is_same(leftType, rightType)) return leftType; - - auto match_integral_type = [&](const Type* type) -> bool { - if (leftType->kind() == type->kind() || rightType->kind() == type->kind()) { - (void)integral_conversion(expr, type); - (void)integral_conversion(other, type); - return true; - } - return false; - }; - - if (control_->is_signed(leftType) && control_->is_signed(rightType)) { - if (match_integral_type(control_->getLongLongIntType())) { - return control_->getLongLongIntType(); - } else if (match_integral_type(control_->getLongIntType())) { - return control_->getLongIntType(); - } else { - (void)integral_conversion(expr, control_->getIntType()); - (void)integral_conversion(other, control_->getIntType()); - return control_->getIntType(); - } - } - - if (control_->is_unsigned(leftType) && control_->is_unsigned(rightType)) { - if (match_integral_type(control_->getUnsignedLongLongIntType())) { - return control_->getUnsignedLongLongIntType(); - } else if (match_integral_type(control_->getUnsignedLongIntType())) { - return control_->getUnsignedLongIntType(); - } else { - (void)integral_conversion(expr, control_->getUnsignedIntType()); - return control_->getUnsignedIntType(); - } - } - - if (match_integral_type(control_->getUnsignedLongLongIntType())) { - return control_->getUnsignedLongLongIntType(); - } else if (match_integral_type(control_->getUnsignedLongIntType())) { - return control_->getUnsignedLongIntType(); - } else if (match_integral_type(control_->getUnsignedIntType())) { - return control_->getUnsignedIntType(); - } else if (match_integral_type(control_->getUnsignedShortIntType())) { - return control_->getUnsignedShortIntType(); - } else if (match_integral_type(control_->getUnsignedCharType())) { - return control_->getUnsignedCharType(); - } else if (match_integral_type(control_->getLongLongIntType())) { - return control_->getLongLongIntType(); - } else if (match_integral_type(control_->getLongIntType())) { - return control_->getLongIntType(); - } - - (void)integral_conversion(expr, control_->getIntType()); - (void)integral_conversion(other, control_->getIntType()); - return control_->getIntType(); + TypeChecker checker{unit}; + checker.setScope(scope_); + return checker.implicit_conversion(expr, destinationType); } -auto Parser::is_null_pointer_constant(ExpressionAST* expr) const -> bool { - if (control_->is_null_pointer(expr->type)) return true; - if (auto integerLiteral = ast_cast(expr)) { - return integerLiteral->literal->value() == "0"; - } - return false; +auto Parser::integral_promotion(ExpressionAST*& expr) -> bool { + TypeChecker checker{unit}; + checker.setScope(scope_); + return checker.integral_promotion(expr); } auto Parser::is_prvalue(ExpressionAST* expr) const -> bool { @@ -10768,6 +10033,8 @@ auto Parser::parse_constraint_logical_or_expression(ExpressionAST*& yyast, ast->op = TokenKind::T_BAR_BAR; ast->rightExpression = expression; yyast = ast; + + check(ast); } return true; @@ -10793,6 +10060,8 @@ auto Parser::parse_constraint_logical_and_expression(ExpressionAST*& yyast, ast->op = TokenKind::T_AMP_AMP; ast->rightExpression = expression; yyast = ast; + + check(ast); } return true; @@ -11776,6 +11045,12 @@ void Parser::completeFunctionDefinition(FunctionDefinitionAST* ast) { rewind(saved); } +void Parser::check(ExpressionAST* ast) { + TypeChecker check{unit}; + check.setScope(scope_); + check(ast); +} + auto Parser::convertName(UnqualifiedIdAST* id) -> const Name* { if (!id) return nullptr; return visit(ConvertToName{control_}, id); diff --git a/src/parser/cxx/parser.h b/src/parser/cxx/parser.h index db50d9a1..45fb901d 100644 --- a/src/parser/cxx/parser.h +++ b/src/parser/cxx/parser.h @@ -797,6 +797,8 @@ class Parser final { [[nodiscard]] auto declaringScope() const -> Scope*; + void check(ExpressionAST* ast); + // lookup [[nodiscard]] auto convertName(UnqualifiedIdAST* id) -> const Name*; @@ -842,48 +844,12 @@ class Parser final { [[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; - [[nodiscard]] auto array_to_pointer_conversion(ExpressionAST*& expr) -> bool; - [[nodiscard]] auto function_to_pointer_conversion(ExpressionAST*& expr) - -> bool; - [[nodiscard]] auto integral_promotion(ExpressionAST*& expr) -> bool; - [[nodiscard]] auto floating_point_promotion(ExpressionAST*& expr) -> bool; - [[nodiscard]] auto integral_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool; - [[nodiscard]] auto floating_point_conversion(ExpressionAST*& expr, - const Type* destinationType) - -> bool; - [[nodiscard]] auto floating_integral_conversion(ExpressionAST*& expr, - const Type* destinationType) - -> bool; - [[nodiscard]] auto pointer_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool; - [[nodiscard]] auto pointer_to_member_conversion(ExpressionAST*& expr, - const Type* destinationType) - -> bool; - [[nodiscard]] auto function_pointer_conversion(ExpressionAST*& expr, - const Type* destinationType) - -> bool; - [[nodiscard]] auto boolean_conversion(ExpressionAST*& expr, - const Type* destinationType) -> bool; - [[nodiscard]] auto temporary_materialization_conversion(ExpressionAST*& expr) - -> bool; - [[nodiscard]] auto qualification_conversion(ExpressionAST*& expr, - const Type* destinationType) - -> bool; - [[nodiscard]] auto ensure_prvalue(ExpressionAST*& expr) -> bool; [[nodiscard]] auto implicit_conversion(ExpressionAST*& expr, const Type* destinationType) -> bool; - [[nodiscard]] auto usual_arithmetic_conversion(ExpressionAST*& expr, - ExpressionAST*& other) - -> const Type*; - - [[nodiscard]] auto is_null_pointer_constant(ExpressionAST* expr) const - -> bool; + [[nodiscard]] auto integral_promotion(ExpressionAST*& expr) -> bool; void check_cpp_cast_expression(CppCastExpressionAST* ast); diff --git a/src/parser/cxx/type_checker.cc b/src/parser/cxx/type_checker.cc index 199b2621..022485ce 100644 --- a/src/parser/cxx/type_checker.cc +++ b/src/parser/cxx/type_checker.cc @@ -22,12 +22,87 @@ // cxx #include +#include +#include +#include +#include +#include +#include + +#include namespace cxx { struct TypeChecker::Visitor { TypeChecker& check; + [[nodiscard]] auto arena() const -> Arena* { return check.unit_->arena(); } + + [[nodiscard]] auto globalScope() const -> Scope* { + return check.unit_->globalScope(); + } + + [[nodiscard]] auto scope() const -> Scope* { return check.scope_; } + + [[nodiscard]] auto control() const -> Control* { + return check.unit_->control(); + } + + [[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; + [[nodiscard]] auto array_to_pointer_conversion(ExpressionAST*& expr) -> bool; + [[nodiscard]] auto function_to_pointer_conversion(ExpressionAST*& expr) + -> bool; + [[nodiscard]] auto integral_promotion(ExpressionAST*& expr) -> bool; + [[nodiscard]] auto floating_point_promotion(ExpressionAST*& expr) -> bool; + [[nodiscard]] auto integral_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool; + [[nodiscard]] auto floating_point_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool; + [[nodiscard]] auto floating_integral_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool; + [[nodiscard]] auto pointer_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool; + [[nodiscard]] auto pointer_to_member_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool; + [[nodiscard]] auto function_pointer_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool; + [[nodiscard]] auto boolean_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool; + [[nodiscard]] auto temporary_materialization_conversion(ExpressionAST*& expr) + -> bool; + [[nodiscard]] auto qualification_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool; + + [[nodiscard]] auto ensure_prvalue(ExpressionAST*& expr) -> bool; + + [[nodiscard]] auto implicit_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool; + + [[nodiscard]] auto usual_arithmetic_conversion(ExpressionAST*& expr, + ExpressionAST*& other) + -> const Type*; + + [[nodiscard]] auto is_null_pointer_constant(ExpressionAST* expr) const + -> bool; + + [[nodiscard]] auto is_prvalue(ExpressionAST* expr) const -> bool; + [[nodiscard]] auto is_lvalue(ExpressionAST* expr) const -> bool; + [[nodiscard]] auto is_xvalue(ExpressionAST* expr) const -> bool; + [[nodiscard]] auto is_glvalue(ExpressionAST* expr) const -> bool; + void operator()(GeneratedLiteralExpressionAST* ast); void operator()(CharLiteralExpressionAST* ast); void operator()(BoolLiteralExpressionAST* ast); @@ -106,11 +181,41 @@ void TypeChecker::Visitor::operator()(StringLiteralExpressionAST* ast) {} void TypeChecker::Visitor::operator()( UserDefinedStringLiteralExpressionAST* ast) {} -void TypeChecker::Visitor::operator()(ThisExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(ThisExpressionAST* ast) { + auto scope_ = check.scope_; + + for (auto current = scope_; current; current = current->parent()) { + if (auto classSymbol = symbol_cast(current->owner())) { + // maybe a this expression in a field initializer + ast->type = control()->getPointerType(classSymbol->type()); + break; + } + + if (auto functionSymbol = symbol_cast(current->owner())) { + if (auto classSymbol = + symbol_cast(functionSymbol->enclosingSymbol())) { + auto functionType = type_cast(functionSymbol->type()); + const auto cv = functionType->cvQualifiers(); + if (cv != CvQualifiers::kNone) { + auto elementType = control()->getQualType(classSymbol->type(), cv); + ast->type = control()->getPointerType(elementType); + } else { + ast->type = control()->getPointerType(classSymbol->type()); + } + } + + break; + } + } +} void TypeChecker::Visitor::operator()(NestedStatementExpressionAST* ast) {} -void TypeChecker::Visitor::operator()(NestedExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(NestedExpressionAST* ast) { + if (!ast->expression) return; + ast->type = ast->expression->type; + ast->valueCategory = ast->expression->valueCategory; +} void TypeChecker::Visitor::operator()(IdExpressionAST* ast) {} @@ -164,17 +269,27 @@ void TypeChecker::Visitor::operator()(UnaryExpressionAST* ast) {} void TypeChecker::Visitor::operator()(AwaitExpressionAST* ast) {} -void TypeChecker::Visitor::operator()(SizeofExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(SizeofExpressionAST* ast) { + ast->type = control()->getSizeType(); +} -void TypeChecker::Visitor::operator()(SizeofTypeExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(SizeofTypeExpressionAST* ast) { + ast->type = control()->getSizeType(); +} void TypeChecker::Visitor::operator()(SizeofPackExpressionAST* ast) {} -void TypeChecker::Visitor::operator()(AlignofTypeExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(AlignofTypeExpressionAST* ast) { + ast->type = control()->getSizeType(); +} -void TypeChecker::Visitor::operator()(AlignofExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(AlignofExpressionAST* ast) { + ast->type = control()->getSizeType(); +} -void TypeChecker::Visitor::operator()(NoexceptExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(NoexceptExpressionAST* ast) { + ast->type = control()->getBoolType(); +} void TypeChecker::Visitor::operator()(NewExpressionAST* ast) {} @@ -184,7 +299,107 @@ void TypeChecker::Visitor::operator()(CastExpressionAST* ast) {} void TypeChecker::Visitor::operator()(ImplicitCastExpressionAST* ast) {} -void TypeChecker::Visitor::operator()(BinaryExpressionAST* ast) {} +void TypeChecker::Visitor::operator()(BinaryExpressionAST* ast) { + if (ast->leftExpression && ast->rightExpression) { + switch (ast->op) { + case TokenKind::T_DOT_STAR: + break; + + case TokenKind::T_MINUS_GREATER_STAR: + break; + + case TokenKind::T_STAR: + case TokenKind::T_SLASH: + case TokenKind::T_PLUS: + case TokenKind::T_MINUS: + ast->type = usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + break; + + case TokenKind::T_PERCENT: + if (!control()->is_integral_or_unscoped_enum(ast->leftExpression->type)) + break; + + if (!control()->is_integral_or_unscoped_enum( + ast->rightExpression->type)) + break; + + ast->type = usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + + break; + + case TokenKind::T_LESS_LESS: + case TokenKind::T_GREATER_GREATER: + if (!control()->is_integral_or_unscoped_enum(ast->leftExpression->type)) + break; + + if (!control()->is_integral_or_unscoped_enum( + ast->rightExpression->type)) + break; + + (void)usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + + ast->type = ast->leftExpression->type; + break; + + case TokenKind::T_LESS_EQUAL_GREATER: + (void)usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + ast->type = control()->getIntType(); + break; + + case TokenKind::T_LESS_EQUAL: + case TokenKind::T_GREATER_EQUAL: + case TokenKind::T_LESS: + case TokenKind::T_GREATER: + case TokenKind::T_EQUAL_EQUAL: + case TokenKind::T_EXCLAIM_EQUAL: + (void)usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + ast->type = control()->getBoolType(); + break; + + case TokenKind::T_AMP: + case TokenKind::T_CARET: + case TokenKind::T_BAR: + if (!control()->is_integral_or_unscoped_enum(ast->leftExpression->type)) + break; + + if (!control()->is_integral_or_unscoped_enum( + ast->rightExpression->type)) + break; + + ast->type = usual_arithmetic_conversion(ast->leftExpression, + ast->rightExpression); + + break; + + case TokenKind::T_AMP_AMP: + case TokenKind::T_BAR_BAR: + (void)implicit_conversion(ast->leftExpression, + control()->getBoolType()); + + (void)implicit_conversion(ast->rightExpression, + control()->getBoolType()); + + ast->type = control()->getBoolType(); + break; + + case TokenKind::T_COMMA: + if (ast->rightExpression) { + ast->type = ast->rightExpression->type; + ast->valueCategory = ast->rightExpression->valueCategory; + } + break; + + default: + cxx_runtime_error( + std::format("invalid operator '{}'", Token::spell(ast->op))); + } // switch + } +} void TypeChecker::Visitor::operator()(ConditionalExpressionAST* ast) {} @@ -208,6 +423,706 @@ void TypeChecker::Visitor::operator()(BracedInitListAST* ast) {} void TypeChecker::Visitor::operator()(ParenInitializerAST* ast) {} +auto TypeChecker::Visitor::strip_parentheses(ExpressionAST* ast) + -> ExpressionAST* { + while (auto paren = ast_cast(ast)) { + ast = paren->expression; + } + return ast; +} + +auto TypeChecker::Visitor::strip_cv(const Type*& type) -> CvQualifiers { + if (auto qualType = type_cast(type)) { + auto cv = qualType->cvQualifiers(); + type = qualType->elementType(); + return cv; + } + return {}; +} + +auto TypeChecker::Visitor::is_const(CvQualifiers cv) const -> bool { + return cv == CvQualifiers::kConst || cv == CvQualifiers::kConstVolatile; +} + +auto TypeChecker::Visitor::is_volatile(CvQualifiers cv) const -> bool { + return cv == CvQualifiers::kVolatile || cv == CvQualifiers::kConstVolatile; +} + +auto TypeChecker::Visitor::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 TypeChecker::Visitor::lvalue_to_rvalue_conversion(ExpressionAST*& expr) + -> bool { + if (!is_glvalue(expr)) return false; + + auto unref = control()->remove_cvref(expr->type); + if (control()->is_function(unref)) return false; + if (control()->is_array(unref)) return false; + if (!control()->is_complete(unref)) return false; + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kLValueToRValueConversion; + cast->expression = expr; + cast->type = control()->remove_reference(expr->type); + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + return true; +} + +auto TypeChecker::Visitor::array_to_pointer_conversion(ExpressionAST*& expr) + -> bool { + auto unref = control()->remove_reference(expr->type); + if (!control()->is_array(unref)) return false; + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kArrayToPointerConversion; + cast->expression = expr; + cast->type = control()->add_pointer(control()->remove_extent(unref)); + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + return true; +} + +auto TypeChecker::Visitor::function_to_pointer_conversion(ExpressionAST*& expr) + -> bool { + auto unref = control()->remove_reference(expr->type); + if (!control()->is_function(unref)) return false; + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFunctionToPointerConversion; + cast->expression = expr; + cast->type = control()->add_pointer(unref); + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + return true; +} + +auto TypeChecker::Visitor::integral_promotion(ExpressionAST*& expr) -> bool { + if (!is_prvalue(expr)) return false; + + auto ty = control()->remove_cv(expr->type); + + if (!control()->is_integral(ty) && !control()->is_enum(ty)) return false; + + auto make_implicit_cast = [&](const Type* type) { + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kIntegralPromotion; + cast->expression = expr; + cast->type = type; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + }; + + // TODO: bit-fields + + switch (ty->kind()) { + case TypeKind::kChar: + case TypeKind::kSignedChar: + case TypeKind::kUnsignedChar: + case TypeKind::kShortInt: + case TypeKind::kUnsignedShortInt: { + make_implicit_cast(control()->getIntType()); + return true; + } + + case TypeKind::kChar8: + case TypeKind::kChar16: + case TypeKind::kChar32: + case TypeKind::kWideChar: { + make_implicit_cast(control()->getIntType()); + return true; + } + + case TypeKind::kBool: { + make_implicit_cast(control()->getIntType()); + return true; + } + + default: + break; + } // switch + + if (auto enumType = type_cast(ty)) { + auto type = enumType->underlyingType(); + + if (!type) { + // TODO: compute the from the value of the enumeration + type = control()->getIntType(); + } + + make_implicit_cast(type); + + return true; + } + + return false; +} + +auto TypeChecker::Visitor::floating_point_promotion(ExpressionAST*& expr) + -> bool { + if (!is_prvalue(expr)) return false; + + auto ty = control()->remove_cv(expr->type); + + if (!control()->is_floating_point(ty)) return false; + + if (ty->kind() != TypeKind::kFloat) return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFloatingPointPromotion; + cast->expression = expr; + cast->type = control()->getDoubleType(); + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; +} + +auto TypeChecker::Visitor::integral_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool { + if (!is_prvalue(expr)) return false; + + if (!control()->is_integral_or_unscoped_enum(expr->type)) return false; + if (!control()->is_integer(destinationType)) return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kIntegralConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; +} + +auto TypeChecker::Visitor::floating_point_conversion( + ExpressionAST*& expr, const Type* destinationType) -> bool { + if (!is_prvalue(expr)) return false; + + if (!control()->is_floating_point(expr->type)) return false; + if (!control()->is_floating_point(destinationType)) return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFloatingPointConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; +} + +auto TypeChecker::Visitor::floating_integral_conversion( + ExpressionAST*& expr, const Type* destinationType) -> bool { + if (!is_prvalue(expr)) return false; + + auto make_integral_conversion = [&] { + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFloatingIntegralConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + }; + + if (control()->is_integral_or_unscoped_enum(expr->type) && + control()->is_floating_point(destinationType)) { + make_integral_conversion(); + return true; + } + + if (!control()->is_floating_point(expr->type)) return false; + if (!control()->is_integer(destinationType)) return false; + + make_integral_conversion(); + + return true; +} + +auto TypeChecker::Visitor::pointer_to_member_conversion( + ExpressionAST*& expr, const Type* destinationType) -> bool { + if (!is_prvalue(expr)) return false; + + if (!control()->is_member_pointer(destinationType)) return false; + + auto make_implicit_cast = [&] { + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kPointerToMemberConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + }; + + auto can_convert_null_pointer_constant = [&] { + if (!is_null_pointer_constant(expr)) return false; + + make_implicit_cast(); + + return true; + }; + + auto can_convert_member_object_pointer = [&] { + auto memberObjectPointerType = + type_cast(expr->type); + + if (!memberObjectPointerType) return false; + + auto destinationMemberObjectPointerType = + type_cast(destinationType); + + if (!destinationMemberObjectPointerType) return false; + + if (control()->get_cv_qualifiers(memberObjectPointerType->elementType()) != + control()->get_cv_qualifiers( + destinationMemberObjectPointerType->elementType())) + return false; + + if (!control()->is_base_of(destinationMemberObjectPointerType->classType(), + memberObjectPointerType->classType())) + return false; + + make_implicit_cast(); + + return true; + }; + + auto can_convert_member_function_pointer = [&] { + auto memberFunctionPointerType = + type_cast(expr->type); + + if (!memberFunctionPointerType) return false; + + auto destinationMemberFunctionPointerType = + type_cast(destinationType); + + if (!destinationMemberFunctionPointerType) return false; + + if (control()->get_cv_qualifiers( + memberFunctionPointerType->functionType()) != + control()->get_cv_qualifiers( + destinationMemberFunctionPointerType->functionType())) + return false; + + if (!control()->is_base_of( + destinationMemberFunctionPointerType->classType(), + memberFunctionPointerType->classType())) + return false; + + make_implicit_cast(); + + return true; + }; + + if (can_convert_null_pointer_constant()) return true; + if (can_convert_member_object_pointer()) return true; + + return false; +} + +auto TypeChecker::Visitor::pointer_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool { + if (!is_prvalue(expr)) return false; + + auto make_implicit_cast = [&] { + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kPointerConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + }; + + auto can_convert_null_pointer_literal = [&] { + if (!is_null_pointer_constant(expr)) return false; + + if (!control()->is_pointer(destinationType) && + !control()->is_null_pointer(destinationType)) + return false; + + make_implicit_cast(); + + return true; + }; + + auto can_convert_to_void_pointer = [&] { + const auto pointerType = type_cast(expr->type); + if (!pointerType) return false; + + const auto destinationPointerType = type_cast(destinationType); + if (!destinationPointerType) return false; + + if (control()->get_cv_qualifiers(pointerType->elementType()) != + control()->get_cv_qualifiers(destinationPointerType->elementType())) + return false; + + if (!control()->is_void( + control()->remove_cv(destinationPointerType->elementType()))) + return false; + + make_implicit_cast(); + + return true; + }; + + auto can_convert_to_base_class_pointer = [&] { + const auto pointerType = type_cast(expr->type); + if (!pointerType) return false; + + const auto destinationPointerType = type_cast(destinationType); + if (!destinationPointerType) return false; + + if (control()->get_cv_qualifiers(pointerType->elementType()) != + control()->get_cv_qualifiers(destinationPointerType->elementType())) + return false; + + if (!control()->is_base_of( + control()->remove_cv(destinationPointerType->elementType()), + control()->remove_cv(pointerType->elementType()))) + return false; + + make_implicit_cast(); + + return true; + }; + + if (can_convert_null_pointer_literal()) return true; + if (can_convert_to_void_pointer()) return true; + if (can_convert_to_base_class_pointer()) return true; + + return false; +} + +auto TypeChecker::Visitor::function_pointer_conversion( + ExpressionAST*& expr, const Type* destinationType) -> bool { + if (!is_prvalue(expr)) return false; + + auto can_remove_noexcept_from_function = [&] { + const auto pointerType = type_cast(expr->type); + if (!pointerType) return false; + + const auto functionType = + type_cast(pointerType->elementType()); + + if (!functionType) return false; + + if (functionType->isNoexcept()) return false; + + const auto destinationPointerType = type_cast(destinationType); + if (!destinationPointerType) return false; + + const auto destinationFunctionType = + type_cast(destinationPointerType->elementType()); + + if (!destinationFunctionType) return false; + + if (!control()->is_same(control()->remove_noexcept(functionType), + destinationFunctionType)) + return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFunctionPointerConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; + }; + + auto can_remove_noexcept_from_member_function__pointer = [&] { + const auto memberFunctionPointer = + type_cast(expr->type); + + if (!memberFunctionPointer) return false; + + if (!memberFunctionPointer->functionType()->isNoexcept()) return false; + + const auto destinationMemberFunctionPointer = + type_cast(destinationType); + + if (!destinationMemberFunctionPointer) return false; + + if (destinationMemberFunctionPointer->functionType()->isNoexcept()) + return false; + + if (!control()->is_same( + control()->remove_noexcept(memberFunctionPointer->functionType()), + destinationMemberFunctionPointer->functionType())) + return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kFunctionPointerConversion; + cast->expression = expr; + cast->type = destinationType; + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; + }; + + if (can_remove_noexcept_from_function()) return true; + if (can_remove_noexcept_from_member_function__pointer()) return true; + + return false; +} + +auto TypeChecker::Visitor::boolean_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool { + if (!type_cast(control()->remove_cv(destinationType))) return false; + + if (!is_prvalue(expr)) return false; + + if (!control()->is_arithmetic_or_unscoped_enum(expr->type) && + !control()->is_pointer(expr->type) && + !control()->is_member_pointer(expr->type)) + return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kBooleanConversion; + cast->expression = expr; + cast->type = control()->getBoolType(); + cast->valueCategory = ValueCategory::kPrValue; + expr = cast; + + return true; +} + +auto TypeChecker::Visitor::temporary_materialization_conversion( + ExpressionAST*& expr) -> bool { + if (!is_prvalue(expr)) return false; + + auto cast = make_node(arena()); + cast->castKind = ImplicitCastKind::kTemporaryMaterializationConversion; + cast->expression = expr; + cast->type = control()->remove_reference(expr->type); + cast->valueCategory = ValueCategory::kXValue; + expr = cast; + + return true; +} + +auto TypeChecker::Visitor::qualification_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool { + return false; +} + +auto TypeChecker::Visitor::ensure_prvalue(ExpressionAST*& expr) -> bool { + if (lvalue_to_rvalue_conversion(expr)) { + expr->valueCategory = ValueCategory::kPrValue; + return true; + } + + if (array_to_pointer_conversion(expr)) { + expr->valueCategory = ValueCategory::kPrValue; + return true; + } + + if (function_to_pointer_conversion(expr)) { + expr->valueCategory = ValueCategory::kPrValue; + return true; + } + + return false; +} + +auto TypeChecker::Visitor::implicit_conversion(ExpressionAST*& expr, + const Type* destinationType) + -> bool { + if (!expr || !expr->type) return false; + if (!destinationType) return false; + + if (control()->is_same(expr->type, destinationType)) return true; + + auto savedValueCategory = expr->valueCategory; + auto savedExpr = expr; + auto didConvert = false; + + if (lvalue_to_rvalue_conversion(expr)) { + didConvert = true; + } else if (array_to_pointer_conversion(expr)) { + didConvert = true; + } else if (function_to_pointer_conversion(expr)) { + didConvert = true; + } + + if (control()->is_scalar(expr->type)) { + expr->type = control()->remove_cv(expr->type); + } + + if (integral_promotion(expr)) return true; + if (floating_point_promotion(expr)) return true; + if (integral_conversion(expr, destinationType)) return true; + if (floating_point_conversion(expr, destinationType)) return true; + if (floating_integral_conversion(expr, destinationType)) return true; + if (pointer_conversion(expr, destinationType)) return true; + if (pointer_to_member_conversion(expr, destinationType)) return true; + if (boolean_conversion(expr, destinationType)) return true; + if (function_pointer_conversion(expr, destinationType)) return true; + if (qualification_conversion(expr, destinationType)) return true; + + if (didConvert) return true; + + expr = savedExpr; + expr->valueCategory = savedValueCategory; + + return false; +} + +auto TypeChecker::Visitor::usual_arithmetic_conversion(ExpressionAST*& expr, + ExpressionAST*& other) + -> const Type* { + if (!expr || !expr->type) return nullptr; + if (!other || !other->type) return nullptr; + + ExpressionAST* savedExpr = expr; + ExpressionAST* savedOther = other; + + auto unmodifiedExpressions = [&]() -> const Type* { + expr = savedExpr; + other = savedOther; + return nullptr; + }; + + if (!control()->is_arithmetic_or_unscoped_enum(expr->type) && + !control()->is_arithmetic_or_unscoped_enum(other->type)) + return unmodifiedExpressions(); + + (void)lvalue_to_rvalue_conversion(expr); + (void)lvalue_to_rvalue_conversion(other); + + if (control()->is_scoped_enum(expr->type) || + control()->is_scoped_enum(other->type)) + return unmodifiedExpressions(); + + if (control()->is_floating_point(expr->type) || + control()->is_floating_point(other->type)) { + auto leftType = control()->remove_cv(expr->type); + auto rightType = control()->remove_cv(other->type); + + if (control()->is_same(leftType, rightType)) return leftType; + + if (!control()->is_floating_point(leftType)) { + if (floating_integral_conversion(expr, rightType)) return rightType; + return unmodifiedExpressions(); + } else if (!control()->is_floating_point(rightType)) { + if (floating_integral_conversion(other, leftType)) return leftType; + return unmodifiedExpressions(); + } else if (leftType->kind() == TypeKind::kLongDouble || + rightType->kind() == TypeKind::kLongDouble) { + (void)floating_point_conversion(expr, control()->getLongDoubleType()); + return control()->getLongDoubleType(); + } else if (leftType->kind() == TypeKind::kDouble || + rightType->kind() == TypeKind::kDouble) { + (void)floating_point_conversion(expr, control()->getDoubleType()); + return control()->getDoubleType(); + } + + return unmodifiedExpressions(); + } + + (void)integral_promotion(expr); + (void)integral_promotion(other); + + const auto leftType = control()->remove_cv(expr->type); + const auto rightType = control()->remove_cv(other->type); + + if (control()->is_same(leftType, rightType)) return leftType; + + auto match_integral_type = [&](const Type* type) -> bool { + if (leftType->kind() == type->kind() || rightType->kind() == type->kind()) { + (void)integral_conversion(expr, type); + (void)integral_conversion(other, type); + return true; + } + return false; + }; + + if (control()->is_signed(leftType) && control()->is_signed(rightType)) { + if (match_integral_type(control()->getLongLongIntType())) { + return control()->getLongLongIntType(); + } else if (match_integral_type(control()->getLongIntType())) { + return control()->getLongIntType(); + } else { + (void)integral_conversion(expr, control()->getIntType()); + (void)integral_conversion(other, control()->getIntType()); + return control()->getIntType(); + } + } + + if (control()->is_unsigned(leftType) && control()->is_unsigned(rightType)) { + if (match_integral_type(control()->getUnsignedLongLongIntType())) { + return control()->getUnsignedLongLongIntType(); + } else if (match_integral_type(control()->getUnsignedLongIntType())) { + return control()->getUnsignedLongIntType(); + } else { + (void)integral_conversion(expr, control()->getUnsignedIntType()); + return control()->getUnsignedIntType(); + } + } + + if (match_integral_type(control()->getUnsignedLongLongIntType())) { + return control()->getUnsignedLongLongIntType(); + } else if (match_integral_type(control()->getUnsignedLongIntType())) { + return control()->getUnsignedLongIntType(); + } else if (match_integral_type(control()->getUnsignedIntType())) { + return control()->getUnsignedIntType(); + } else if (match_integral_type(control()->getUnsignedShortIntType())) { + return control()->getUnsignedShortIntType(); + } else if (match_integral_type(control()->getUnsignedCharType())) { + return control()->getUnsignedCharType(); + } else if (match_integral_type(control()->getLongLongIntType())) { + return control()->getLongLongIntType(); + } else if (match_integral_type(control()->getLongIntType())) { + return control()->getLongIntType(); + } + + (void)integral_conversion(expr, control()->getIntType()); + (void)integral_conversion(other, control()->getIntType()); + return control()->getIntType(); +} + +auto TypeChecker::Visitor::is_null_pointer_constant(ExpressionAST* expr) const + -> bool { + if (control()->is_null_pointer(expr->type)) return true; + if (auto integerLiteral = ast_cast(expr)) { + return integerLiteral->literal->value() == "0"; + } + return false; +} + +auto TypeChecker::Visitor::is_prvalue(ExpressionAST* expr) const -> bool { + if (!expr) return false; + return expr->valueCategory == ValueCategory::kPrValue; +} + +auto TypeChecker::Visitor::is_lvalue(ExpressionAST* expr) const -> bool { + if (!expr) return false; + return expr->valueCategory == ValueCategory::kLValue; +} + +auto TypeChecker::Visitor::is_xvalue(ExpressionAST* expr) const -> bool { + if (!expr) return false; + return expr->valueCategory == ValueCategory::kXValue; +} + +auto TypeChecker::Visitor::is_glvalue(ExpressionAST* expr) const -> bool { + if (!expr) return false; + return expr->valueCategory == ValueCategory::kLValue || + expr->valueCategory == ValueCategory::kXValue; +} + TypeChecker::TypeChecker(TranslationUnit* unit) : unit_(unit) {} void TypeChecker::operator()(ExpressionAST* ast) { @@ -220,4 +1135,17 @@ void TypeChecker::check(ExpressionAST* ast) { visit(Visitor{*this}, ast); } +auto TypeChecker::integral_promotion(ExpressionAST*& expr) -> bool { + return Visitor{*this}.integral_promotion(expr); +} + +auto TypeChecker::ensure_prvalue(ExpressionAST*& expr) -> bool { + return Visitor{*this}.ensure_prvalue(expr); +} + +auto TypeChecker::implicit_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool { + return Visitor{*this}.implicit_conversion(expr, destinationType); +} + } // namespace cxx diff --git a/src/parser/cxx/type_checker.h b/src/parser/cxx/type_checker.h index 8863ff5c..d9d61108 100644 --- a/src/parser/cxx/type_checker.h +++ b/src/parser/cxx/type_checker.h @@ -19,6 +19,8 @@ // SOFTWARE. #include +#include +#include namespace cxx { @@ -32,13 +34,24 @@ class TypeChecker { return unit_; } + void setScope(Scope* scope) { scope_ = scope; } + void operator()(ExpressionAST* ast); + void check(ExpressionAST* ast); + [[nodiscard]] auto ensure_prvalue(ExpressionAST*& expr) -> bool; + + [[nodiscard]] auto implicit_conversion(ExpressionAST*& expr, + const Type* destinationType) -> bool; + + [[nodiscard]] auto integral_promotion(ExpressionAST*& expr) -> bool; + private: struct Visitor; TranslationUnit* unit_; + Scope* scope_ = nullptr; }; } // namespace cxx