Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model function calls as initializing expressions #3089

Merged
merged 29 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0400cdf
Rough semantic support for expression category conversions.
zygoloid Aug 11, 2023
c00e17c
Merge branch 'trunk' into toolchain-expression-category
zygoloid Aug 11, 2023
c99bec7
Add forgotten file.
zygoloid Aug 11, 2023
84e36ba
Merge branch 'trunk' into toolchain-expression-category
zygoloid Aug 11, 2023
1fa8ffb
Update tests.
zygoloid Aug 11, 2023
c5ccea5
Start tracking the initialization target as part of an initialization
zygoloid Aug 11, 2023
5d0873b
Model calls as initializing expressions rather than value expressions.
zygoloid Aug 12, 2023
1a236cc
Put return type IR in the same block as parameter IR.
zygoloid Aug 16, 2023
b75b2d5
Materialize temporaries before the calls to which we pass them as
zygoloid Aug 21, 2023
8bd62a0
Merge branch 'trunk' into toolchain-expression-category
zygoloid Aug 22, 2023
bcdfacd
Autoupdate after merge.
zygoloid Aug 22, 2023
c607569
Add computation of value representation for a type.
zygoloid Aug 22, 2023
4923843
Add computation of initializing representation for a type.
zygoloid Aug 22, 2023
babd124
Use initializing representation to pick whether to pass a value into a
zygoloid Aug 22, 2023
f213f17
Remove unused node InitializeFrom.
zygoloid Aug 22, 2023
f7c1f85
Fix cases where we'd make use of the return value of a function that
zygoloid Aug 22, 2023
6b31855
Merge branch 'trunk' into toolchain-expression-category
zygoloid Aug 22, 2023
c5c137d
Fix up and regenerate test expectations after merge.
zygoloid Aug 22, 2023
b8f6d4d
clang-format
zygoloid Aug 22, 2023
6f4a450
Update TODO to match discussion in #3133.
zygoloid Aug 22, 2023
ac1e73d
Rename value_binding -> bind_value.
zygoloid Aug 22, 2023
5fbbe65
Merge branch 'trunk' into toolchain-expression-category
zygoloid Aug 23, 2023
a45ceb5
Merge remote-tracking branch 'upstream/trunk' into toolchain-expressi…
zygoloid Aug 23, 2023
e172f4e
Autoupdate
zygoloid Aug 23, 2023
c8f9f1e
Merge remote-tracking branch 'upstream/trunk' into toolchain-expressi…
zygoloid Aug 23, 2023
dc447a2
Fix typo from merging the big namespace change.
zygoloid Aug 24, 2023
e7995d9
Merge remote-tracking branch 'upstream/trunk' into toolchain-expressi…
zygoloid Aug 24, 2023
13c3156
Remove redundant braces.
zygoloid Aug 24, 2023
1dee913
Remove redundant braces.
zygoloid Aug 24, 2023
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
27 changes: 27 additions & 0 deletions toolchain/lowering/lowering_handle_expression_category.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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/lowering/lowering_function_context.h"

namespace Carbon {

auto LoweringHandleInitializeFrom(LoweringFunctionContext& context,
SemanticsNodeId node_id, SemanticsNode node)
-> void {
context.SetLocal(node_id, context.GetLocal(node.GetAsInitializeFrom()));
}

auto LoweringHandleMaterializeTemporary(LoweringFunctionContext& context,
SemanticsNodeId node_id,
SemanticsNode node) -> void {
context.SetLocal(node_id, context.GetLocal(node.GetAsMaterializeTemporary()));
}

auto LoweringHandleValueBinding(LoweringFunctionContext& context,
SemanticsNodeId node_id, SemanticsNode node)
-> void {
context.SetLocal(node_id, context.GetLocalLoaded(node.GetAsValueBinding()));
}

} // namespace Carbon
3 changes: 2 additions & 1 deletion toolchain/lowering/testdata/function/call/var_param.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fn Main() {
// CHECK:STDOUT: %var = alloca i32, align 4
// CHECK:STDOUT: store i32 0, ptr %var, align 4
// CHECK:STDOUT: %1 = load i32, ptr %var, align 4
// CHECK:STDOUT: call void @DoNothing(i32 %1)
// CHECK:STDOUT: %2 = load i32, ptr %var, align 4
// CHECK:STDOUT: call void @DoNothing(i32 %2)
// CHECK:STDOUT: %call.result = alloca {}, align 8
// CHECK:STDOUT: ret void
// CHECK:STDOUT: }
70 changes: 63 additions & 7 deletions toolchain/semantics/semantics_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,62 @@ auto SemanticsContext::is_current_position_reachable() -> bool {
}
}

auto SemanticsContext::ConvertToInitializingExpression(SemanticsNodeId expr_id)
-> SemanticsNodeId {
SemanticsNode expr = semantics_ir().GetNode(expr_id);
switch (GetSemanticsExpressionCategory(semantics_ir(), expr_id)) {
case SemanticsExpressionCategory::NotExpression:
CARBON_FATAL() << "Converting non-expression node " << expr
<< " to initializing expression";

case SemanticsExpressionCategory::DurableReference:
case SemanticsExpressionCategory::EphemeralReference:
// The design uses a custom "copy initialization" process here. We model
// that as value binding followed by direct initialization.
//
// TODO: Determine whether this is observably different from the design,
// and change either the toolchain or the design so they match.
expr_id = AddNode(SemanticsNode::ValueBinding::Make(
expr.parse_node(), expr.type_id(), expr_id));
[[fallthrough]];

case SemanticsExpressionCategory::Value:
// TODO: For class types, use an interface to determine how to perform
// this operation.
return AddNode(SemanticsNode::InitializeFrom::Make(
expr.parse_node(), expr.type_id(), expr_id));

case SemanticsExpressionCategory::Initializing:
return expr_id;
}
}

auto SemanticsContext::ConvertToValueExpression(SemanticsNodeId expr_id)
-> SemanticsNodeId {
SemanticsNode expr = semantics_ir().GetNode(expr_id);
switch (GetSemanticsExpressionCategory(semantics_ir(), expr_id)) {
case SemanticsExpressionCategory::NotExpression:
CARBON_FATAL() << "Converting non-expression node " << expr
<< " to value expression";

case SemanticsExpressionCategory::Initializing:
// TODO: For class types, use an interface to determine how to perform
// this operation.
expr_id = AddNode(SemanticsNode::MaterializeTemporary::Make(
expr.parse_node(), expr.type_id(), expr_id));
[[fallthrough]];

case SemanticsExpressionCategory::DurableReference:
case SemanticsExpressionCategory::EphemeralReference:
// TODO: Support types with custom value representations.
return AddNode(SemanticsNode::ValueBinding::Make(
expr.parse_node(), expr.type_id(), expr_id));

case SemanticsExpressionCategory::Value:
return expr_id;
}
}

auto SemanticsContext::ImplicitAsForArgs(
SemanticsNodeBlockId arg_refs_id, ParseTree::Node param_parse_node,
SemanticsNodeBlockId param_refs_id,
Expand Down Expand Up @@ -314,6 +370,13 @@ auto SemanticsContext::ImplicitAsForArgs(
semantics_ir_->StringifyType(as_type_id));
return false;
}

// TODO: Convert to the proper expression category. For now, we assume
// parameters are all `let` bindings.
if (!diagnostic) {
// TODO: Insert the conversion in the proper place in the node block.
arg_refs[i] = ConvertToValueExpression(value_id);
}
}

return true;
Expand All @@ -340,13 +403,6 @@ auto SemanticsContext::ImplicitAsRequired(ParseTree::Node parse_node,
return output_value_id;
}

auto SemanticsContext::ImplicitAsBool(ParseTree::Node parse_node,
SemanticsNodeId value_id)
-> SemanticsNodeId {
return ImplicitAsRequired(parse_node, value_id,
CanonicalizeType(SemanticsNodeId::BuiltinBoolType));
}

auto SemanticsContext::ImplicitAsImpl(SemanticsNodeId value_id,
SemanticsTypeId as_type_id,
SemanticsNodeId* output_value_id)
Expand Down
38 changes: 33 additions & 5 deletions toolchain/semantics/semantics_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,38 @@ class SemanticsContext {
// Returns whether the current position in the current block is reachable.
auto is_current_position_reachable() -> bool;

// Convert the given expression to an initializing expression of the same
// type.
auto ConvertToInitializingExpression(SemanticsNodeId expr_id)
-> SemanticsNodeId;

// Convert the given expression to a value expression of the same type.
auto ConvertToValueExpression(SemanticsNodeId expr_id) -> SemanticsNodeId;

// Converts `value_id` to an initializing expression of type `type_id`.
auto ConvertToInitializerOfType(ParseTree::Node parse_node,
SemanticsNodeId value_id,
SemanticsTypeId type_id) -> SemanticsNodeId {
return ConvertToInitializingExpression(
ImplicitAsRequired(parse_node, value_id, type_id));
}

// Converts `value_id` to a value expression of type `type_id`.
auto ConvertToValueOfType(ParseTree::Node parse_node,
SemanticsNodeId value_id, SemanticsTypeId type_id)
-> SemanticsNodeId {
return ConvertToValueExpression(
ImplicitAsRequired(parse_node, value_id, type_id));
}

// Converts `value_id` to a value expression of type `bool`.
auto ConvertToBoolValue(ParseTree::Node parse_node, SemanticsNodeId value_id)
-> SemanticsNodeId {
return ConvertToValueOfType(
parse_node, value_id,
CanonicalizeType(SemanticsNodeId::BuiltinBoolType));
}

// Runs ImplicitAsImpl for a set of arguments and parameters.
//
// This will eventually need to support checking against multiple possible
Expand All @@ -129,10 +161,6 @@ class SemanticsContext {
auto ImplicitAsRequired(ParseTree::Node parse_node, SemanticsNodeId value_id,
SemanticsTypeId as_type_id) -> SemanticsNodeId;

// Runs ImplicitAsRequired for a conversion to `bool`.
auto ImplicitAsBool(ParseTree::Node parse_node, SemanticsNodeId value_id)
-> SemanticsNodeId;

// Canonicalizes a type which is tracked as a single node.
// TODO: This should eventually return a type ID.
auto CanonicalizeType(SemanticsNodeId node_id) -> SemanticsTypeId;
Expand Down Expand Up @@ -170,7 +198,7 @@ class SemanticsContext {
}

return CanonicalizeType(
ImplicitAsRequired(parse_node, value_id, SemanticsTypeId::TypeType));
ConvertToValueOfType(parse_node, value_id, SemanticsTypeId::TypeType));
}

// Removes any top-level `const` qualifiers from a type.
Expand Down
10 changes: 8 additions & 2 deletions toolchain/semantics/semantics_handle_if_expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ auto SemanticsHandleIfExpressionIf(SemanticsContext& context,
context.node_stack().Push(if_node);

// Convert the condition to `bool`, and branch on it.
cond_value_id = context.ImplicitAsBool(if_node, cond_value_id);
cond_value_id = context.ConvertToBoolValue(if_node, cond_value_id);
auto then_block_id =
context.AddDominatedBlockAndBranchIf(if_node, cond_value_id);
auto else_block_id = context.AddDominatedBlockAndBranch(if_node);
Expand All @@ -28,6 +28,12 @@ auto SemanticsHandleIfExpressionIf(SemanticsContext& context,

auto SemanticsHandleIfExpressionThen(SemanticsContext& context,
ParseTree::Node then_node) -> bool {
// Convert the first operand to a value.
auto [then_value_node, then_value_id] =
context.node_stack().PopExpressionWithParseNode();
context.node_stack().Push(then_value_node,
context.ConvertToValueExpression(then_value_id));

context.node_stack().Push(then_node, context.node_block_stack().Pop());
context.AddCurrentCodeBlockToFunction();
return true;
Expand All @@ -47,7 +53,7 @@ auto SemanticsHandleIfExpressionElse(SemanticsContext& context,
// TODO: Find a common type, and convert both operands to it instead.
auto result_type_id = context.semantics_ir().GetNode(then_value_id).type_id();
else_value_id =
context.ImplicitAsRequired(else_node, else_value_id, result_type_id);
context.ConvertToValueOfType(else_node, else_value_id, result_type_id);
auto else_end_block_id = context.node_block_stack().Pop();

// Create a resumption block and branches to it.
Expand Down
2 changes: 1 addition & 1 deletion toolchain/semantics/semantics_handle_if_statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ auto SemanticsHandleIfCondition(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
// Convert the condition to `bool`.
auto cond_value_id = context.node_stack().PopExpression();
cond_value_id = context.ImplicitAsBool(parse_node, cond_value_id);
cond_value_id = context.ConvertToBoolValue(parse_node, cond_value_id);

// Create the then block and the else block, and branch to the right one. If
// there is no `else`, the then block will terminate with a branch to the
Expand Down
7 changes: 7 additions & 0 deletions toolchain/semantics/semantics_handle_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ auto SemanticsHandleMemberAccessExpression(SemanticsContext& context,
SemanticsStringId name_id = context.node_stack().Pop<ParseNodeKind::Name>();

auto base_id = context.node_stack().PopExpression();

// Materialize a temporary for the base expression if necessary.
if (GetSemanticsExpressionCategory(context.semantics_ir(), base_id) ==
SemanticsExpressionCategory::Initializing) {
base_id = context.ConvertToValueExpression(base_id);
}

auto base = context.semantics_ir().GetNode(base_id);
if (base.kind() == SemanticsNodeKind::Namespace) {
// For a namespace, just resolve the name.
Expand Down
12 changes: 7 additions & 5 deletions toolchain/semantics/semantics_handle_operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context,
case 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 = context.ImplicitAsRequired(
lhs_id = context.ConvertToValueOfType(
parse_node, lhs_id, context.semantics_ir().GetNode(rhs_id).type_id());
rhs_id = context.ConvertToValueExpression(rhs_id);

context.AddNodeAndPush(
parse_node,
Expand All @@ -32,7 +33,7 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context,
// 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 = context.ImplicitAsBool(parse_node, rhs_id);
rhs_id = context.ConvertToBoolValue(parse_node, rhs_id);

// When the second operand is evaluated, the result of `and` and `or` is
// its value.
Expand All @@ -59,7 +60,7 @@ auto SemanticsHandleInfixOperator(SemanticsContext& context,
"Expression is not assignable.");
context.emitter().Emit(lhs_node, AssignmentToNonAssignable);
}
context.ImplicitAsRequired(
rhs_id = context.ConvertToInitializerOfType(
parse_node, rhs_id, context.semantics_ir().GetNode(lhs_id).type_id());
context.AddNodeAndPush(
parse_node, SemanticsNode::Assign::Make(parse_node, lhs_id, rhs_id));
Expand Down Expand Up @@ -147,7 +148,7 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context,
}

case TokenKind::Not:
value_id = context.ImplicitAsBool(parse_node, value_id);
value_id = context.ConvertToBoolValue(parse_node, value_id);
context.AddNodeAndPush(
parse_node,
SemanticsNode::UnaryOperatorNot::Make(
Expand Down Expand Up @@ -180,6 +181,7 @@ auto SemanticsHandlePrefixOperator(SemanticsContext& context,
}
builder.Emit();
}
value_id = context.ConvertToValueExpression(value_id);
context.AddNodeAndPush(parse_node,
SemanticsNode::Dereference::Make(
parse_node, result_type_id, value_id));
Expand All @@ -195,7 +197,7 @@ auto SemanticsHandleShortCircuitOperand(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
// Convert the condition to `bool`.
auto cond_value_id = context.node_stack().PopExpression();
cond_value_id = context.ImplicitAsBool(parse_node, cond_value_id);
cond_value_id = context.ConvertToBoolValue(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`.
Expand Down
16 changes: 16 additions & 0 deletions toolchain/semantics/semantics_handle_paren.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,30 @@ auto SemanticsHandleParenExpressionOrTupleLiteralStart(
return true;
}

static auto HandleTupleLiteralElement(SemanticsContext& context) -> void {
// Convert the operand to a value.
// TODO: We need to decide how tuple literals interact with expression
// categories.
auto [value_node, value_id] =
context.node_stack().PopExpressionWithParseNode();
value_id = context.ConvertToValueExpression(value_id);
context.node_stack().Push(value_node, value_id);
}

auto SemanticsHandleTupleLiteralComma(SemanticsContext& context,
ParseTree::Node /*parse_node*/) -> bool {
HandleTupleLiteralElement(context);
context.ParamOrArgComma(/*for_args=*/true);
return true;
}

auto SemanticsHandleTupleLiteral(SemanticsContext& context,
ParseTree::Node parse_node) -> bool {
if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) !=
ParseNodeKind::ParenExpressionOrTupleLiteralStart) {
HandleTupleLiteralElement(context);
}

auto refs_id = context.ParamOrArgEnd(
/*for_args=*/true, ParseNodeKind::ParenExpressionOrTupleLiteralStart);

Expand Down
4 changes: 2 additions & 2 deletions toolchain/semantics/semantics_handle_statement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ auto SemanticsHandleReturnStatement(SemanticsContext& context,
.Note(fn_node.parse_node(), ReturnStatementImplicitNote)
.Emit();
} else {
arg =
context.ImplicitAsRequired(parse_node, arg, callable.return_type_id);
arg = context.ConvertToInitializerOfType(parse_node, arg,
callable.return_type_id);
}

context.AddNode(SemanticsNode::ReturnExpression::Make(parse_node, arg));
Expand Down
5 changes: 5 additions & 0 deletions toolchain/semantics/semantics_handle_struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ auto SemanticsHandleStructFieldValue(SemanticsContext& context,
context.node_stack().PopExpressionWithParseNode();
SemanticsStringId name_id = context.node_stack().Pop<ParseNodeKind::Name>();

// Convert the operand to a value.
// TODO: We need to decide how struct literals interact with expression
// categories.
value_node_id = context.ConvertToValueExpression(value_node_id);

// Store the name for the type.
auto type_block_id = context.args_type_info_stack().PeekForAdd();
context.semantics_ir().AddNode(
Expand Down
2 changes: 1 addition & 1 deletion toolchain/semantics/semantics_handle_variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ auto SemanticsHandleVariableDeclaration(SemanticsContext& context,
context.AddNameToLookup(binding.parse_node(), name_id, storage_id);
// If there was an initializer, assign it to storage.
if (has_init) {
auto cast_value_id = context.ImplicitAsRequired(
auto cast_value_id = context.ConvertToInitializerOfType(
parse_node, expr_node_id,
context.semantics_ir().GetNode(storage_id).type_id());
context.AddNode(
Expand Down
Loading