mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-03 21:05:50 +01:00
Combine the initialization, implicit conversion, and value category conversion functions into a single function. This substantially reduces the duplication between these steps, and ensures that we support the same set of conversions in all these contexts. This also fixes some issues where we would not use the proper value representation for tuples and structs after performing implicit conversions.
244 lines
9.9 KiB
C++
244 lines
9.9 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/check/context.h"
|
|
#include "toolchain/check/convert.h"
|
|
|
|
namespace Carbon::Check {
|
|
|
|
auto HandleInfixOperator(Context& context, Parse::Node parse_node) -> bool {
|
|
auto rhs_id = context.node_stack().PopExpression();
|
|
auto [lhs_node, lhs_id] = context.node_stack().PopExpressionWithParseNode();
|
|
|
|
// Figure out the operator for the token.
|
|
auto token = context.parse_tree().node_token(parse_node);
|
|
switch (auto token_kind = context.tokens().GetKind(token)) {
|
|
case Lex::TokenKind::Plus:
|
|
// TODO: This should search for a compatible interface. For now, it's a
|
|
// very trivial check of validity on the operation.
|
|
lhs_id = ConvertToValueOfType(
|
|
context, parse_node, lhs_id,
|
|
context.semantics_ir().GetNode(rhs_id).type_id());
|
|
rhs_id = ConvertToValueExpression(context, rhs_id);
|
|
|
|
context.AddNodeAndPush(
|
|
parse_node,
|
|
SemIR::Node::BinaryOperatorAdd::Make(
|
|
parse_node, context.semantics_ir().GetNode(lhs_id).type_id(),
|
|
lhs_id, rhs_id));
|
|
return true;
|
|
|
|
case Lex::TokenKind::And:
|
|
case Lex::TokenKind::Or: {
|
|
// The first operand is wrapped in a ShortCircuitOperand, which we
|
|
// already handled by creating a RHS block and a resumption block, which
|
|
// are the current block and its enclosing block.
|
|
rhs_id = ConvertToBoolValue(context, parse_node, rhs_id);
|
|
|
|
// When the second operand is evaluated, the result of `and` and `or` is
|
|
// its value.
|
|
auto resume_block_id = context.node_block_stack().PeekOrAdd(/*depth=*/1);
|
|
context.AddNode(SemIR::Node::BranchWithArg::Make(
|
|
parse_node, resume_block_id, rhs_id));
|
|
context.node_block_stack().Pop();
|
|
context.AddCurrentCodeBlockToFunction();
|
|
|
|
// Collect the result from either the first or second operand.
|
|
context.AddNodeAndPush(
|
|
parse_node,
|
|
SemIR::Node::BlockArg::Make(
|
|
parse_node, context.semantics_ir().GetNode(rhs_id).type_id(),
|
|
resume_block_id));
|
|
return true;
|
|
}
|
|
case Lex::TokenKind::Equal: {
|
|
// TODO: handle complex assignment expression such as `a += 1`.
|
|
if (SemIR::GetExpressionCategory(context.semantics_ir(), lhs_id) !=
|
|
SemIR::ExpressionCategory::DurableReference) {
|
|
CARBON_DIAGNOSTIC(AssignmentToNonAssignable, Error,
|
|
"Expression is not assignable.");
|
|
context.emitter().Emit(lhs_node, AssignmentToNonAssignable);
|
|
}
|
|
// TODO: Destroy the old value before reinitializing. This will require
|
|
// building the destruction code before we build the RHS subexpression.
|
|
rhs_id = Initialize(context, parse_node, lhs_id, rhs_id);
|
|
context.AddNode(SemIR::Node::Assign::Make(parse_node, lhs_id, rhs_id));
|
|
// We model assignment as an expression, so we need to push a value for
|
|
// it, even though it doesn't produce a value.
|
|
// TODO: Consider changing our parse tree to model assignment as a
|
|
// different kind of statement than an expression statement.
|
|
context.node_stack().Push(parse_node, lhs_id);
|
|
return true;
|
|
}
|
|
default:
|
|
return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind));
|
|
}
|
|
}
|
|
|
|
auto HandlePostfixOperator(Context& context, Parse::Node parse_node) -> bool {
|
|
auto value_id = context.node_stack().PopExpression();
|
|
|
|
// Figure out the operator for the token.
|
|
auto token = context.parse_tree().node_token(parse_node);
|
|
switch (auto token_kind = context.tokens().GetKind(token)) {
|
|
case Lex::TokenKind::Star: {
|
|
auto inner_type_id = ExpressionAsType(context, parse_node, value_id);
|
|
context.AddNodeAndPush(
|
|
parse_node, SemIR::Node::PointerType::Make(
|
|
parse_node, SemIR::TypeId::TypeType, inner_type_id));
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
CARBON_FATAL() << "Unexpected postfix operator " << token_kind;
|
|
}
|
|
}
|
|
|
|
auto HandlePrefixOperator(Context& context, Parse::Node parse_node) -> bool {
|
|
auto value_id = context.node_stack().PopExpression();
|
|
|
|
// Figure out the operator for the token.
|
|
auto token = context.parse_tree().node_token(parse_node);
|
|
switch (auto token_kind = context.tokens().GetKind(token)) {
|
|
case Lex::TokenKind::Amp: {
|
|
// Only durable reference expressions can have their address taken.
|
|
switch (SemIR::GetExpressionCategory(context.semantics_ir(), value_id)) {
|
|
case SemIR::ExpressionCategory::DurableReference:
|
|
break;
|
|
case SemIR::ExpressionCategory::EphemeralReference:
|
|
CARBON_DIAGNOSTIC(AddressOfEphemeralReference, Error,
|
|
"Cannot take the address of a temporary object.");
|
|
context.emitter().Emit(parse_node, AddressOfEphemeralReference);
|
|
break;
|
|
default:
|
|
CARBON_DIAGNOSTIC(
|
|
AddressOfNonReference, Error,
|
|
"Cannot take the address of non-reference expression.");
|
|
context.emitter().Emit(parse_node, AddressOfNonReference);
|
|
break;
|
|
}
|
|
context.AddNodeAndPush(
|
|
parse_node,
|
|
SemIR::Node::AddressOf::Make(
|
|
parse_node,
|
|
context.GetPointerType(
|
|
parse_node,
|
|
context.semantics_ir().GetNode(value_id).type_id()),
|
|
value_id));
|
|
return true;
|
|
}
|
|
|
|
case Lex::TokenKind::Const: {
|
|
// `const (const T)` is probably not what the developer intended.
|
|
// TODO: Detect `const (const T)*` and suggest moving the `*` inside the
|
|
// parentheses.
|
|
if (context.semantics_ir().GetNode(value_id).kind() ==
|
|
SemIR::NodeKind::ConstType) {
|
|
CARBON_DIAGNOSTIC(RepeatedConst, Warning,
|
|
"`const` applied repeatedly to the same type has no "
|
|
"additional effect.");
|
|
context.emitter().Emit(parse_node, RepeatedConst);
|
|
}
|
|
auto inner_type_id = ExpressionAsType(context, parse_node, value_id);
|
|
context.AddNodeAndPush(
|
|
parse_node, SemIR::Node::ConstType::Make(
|
|
parse_node, SemIR::TypeId::TypeType, inner_type_id));
|
|
return true;
|
|
}
|
|
|
|
case Lex::TokenKind::Not:
|
|
value_id = ConvertToBoolValue(context, parse_node, value_id);
|
|
context.AddNodeAndPush(
|
|
parse_node,
|
|
SemIR::Node::UnaryOperatorNot::Make(
|
|
parse_node, context.semantics_ir().GetNode(value_id).type_id(),
|
|
value_id));
|
|
return true;
|
|
|
|
case Lex::TokenKind::Star: {
|
|
auto type_id = context.GetUnqualifiedType(
|
|
context.semantics_ir().GetNode(value_id).type_id());
|
|
auto type_node = context.semantics_ir().GetNode(
|
|
context.semantics_ir().GetTypeAllowBuiltinTypes(type_id));
|
|
auto result_type_id = SemIR::TypeId::Error;
|
|
if (type_node.kind() == SemIR::NodeKind::PointerType) {
|
|
result_type_id = type_node.GetAsPointerType();
|
|
} else {
|
|
CARBON_DIAGNOSTIC(
|
|
DereferenceOfNonPointer, Error,
|
|
"Cannot dereference operand of non-pointer type `{0}`.",
|
|
std::string);
|
|
auto builder = context.emitter().Build(
|
|
parse_node, DereferenceOfNonPointer,
|
|
context.semantics_ir().StringifyType(type_id));
|
|
// TODO: Check for any facet here, rather than only a type.
|
|
if (type_id == SemIR::TypeId::TypeType) {
|
|
CARBON_DIAGNOSTIC(
|
|
DereferenceOfType, Note,
|
|
"To form a pointer type, write the `*` after the pointee type.");
|
|
builder.Note(parse_node, DereferenceOfType);
|
|
}
|
|
builder.Emit();
|
|
}
|
|
value_id = ConvertToValueExpression(context, value_id);
|
|
context.AddNodeAndPush(
|
|
parse_node,
|
|
SemIR::Node::Dereference::Make(parse_node, result_type_id, value_id));
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
return context.TODO(parse_node, llvm::formatv("Handle {0}", token_kind));
|
|
}
|
|
}
|
|
|
|
auto HandleShortCircuitOperand(Context& context, Parse::Node parse_node)
|
|
-> bool {
|
|
// Convert the condition to `bool`.
|
|
auto cond_value_id = context.node_stack().PopExpression();
|
|
cond_value_id = ConvertToBoolValue(context, parse_node, cond_value_id);
|
|
auto bool_type_id = context.semantics_ir().GetNode(cond_value_id).type_id();
|
|
|
|
// Compute the branch value: the condition for `and`, inverted for `or`.
|
|
auto token = context.parse_tree().node_token(parse_node);
|
|
SemIR::NodeId branch_value_id = SemIR::NodeId::Invalid;
|
|
auto short_circuit_result_id = SemIR::NodeId::Invalid;
|
|
switch (auto token_kind = context.tokens().GetKind(token)) {
|
|
case Lex::TokenKind::And:
|
|
branch_value_id = cond_value_id;
|
|
short_circuit_result_id = context.AddNode(SemIR::Node::BoolLiteral::Make(
|
|
parse_node, bool_type_id, SemIR::BoolValue::False));
|
|
break;
|
|
|
|
case Lex::TokenKind::Or:
|
|
branch_value_id = context.AddNode(SemIR::Node::UnaryOperatorNot::Make(
|
|
parse_node, bool_type_id, cond_value_id));
|
|
short_circuit_result_id = context.AddNode(SemIR::Node::BoolLiteral::Make(
|
|
parse_node, bool_type_id, SemIR::BoolValue::True));
|
|
break;
|
|
|
|
default:
|
|
CARBON_FATAL() << "Unexpected short-circuiting operator " << token_kind;
|
|
}
|
|
|
|
// Create a block for the right-hand side and for the continuation.
|
|
auto rhs_block_id =
|
|
context.AddDominatedBlockAndBranchIf(parse_node, branch_value_id);
|
|
auto end_block_id = context.AddDominatedBlockAndBranchWithArg(
|
|
parse_node, short_circuit_result_id);
|
|
|
|
// Push the resumption and the right-hand side blocks, and start emitting the
|
|
// right-hand operand.
|
|
context.node_block_stack().Pop();
|
|
context.node_block_stack().Push(end_block_id);
|
|
context.node_block_stack().Push(rhs_block_id);
|
|
context.AddCurrentCodeBlockToFunction();
|
|
|
|
// Put the condition back on the stack for HandleInfixOperator.
|
|
context.node_stack().Push(parse_node, cond_value_id);
|
|
return true;
|
|
}
|
|
|
|
} // namespace Carbon::Check
|