mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:28:25 +00:00
Add support for -> auto deduced return types. (#7931)
The return type is deduced from the function's `return` statements (currently required to all have the same type), or from its first `returned var`. Calls to generic functions with deduced return types are not yet supported. When a function has a deduced return type, we defer the return type portion of the callee pattern match until deduction, and during deduction we extend the decl block and the pattern block with the return-related instructions. Conversions for `return expr;` statements are deferred until the return type has been deduced. The design requires that we take the common type of the returned types as the deduced return type, so we can't in general perform any conversions for returned expressions until we reach the end of the function. This is accomplished by inserting a `splice_block` where the conversions will go, and later updating that instruction to contain the conversion. Assisted-by: Claude Opus 5.5 via Antigravity
This commit is contained in:
1 parent
1c69fe3447
commit
bf5d2a2ace
41 files changed
+2631
-214
No files matched your search
@@ -249,6 +249,30 @@ auto PerformCallToFunction(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
auto& callee = context.functions().Get(callee_function.function_id);
|
||||
|
||||
// We can't call a function before its return type is known. This happens for
|
||||
// a function with a deduced return type that is called from within its own
|
||||
// body, or from the body of a class member function that precedes it.
|
||||
if (callee.has_pending_deduced_return_type()) {
|
||||
CARBON_DIAGNOSTIC(CallBeforeReturnTypeDeduced, Error,
|
||||
"call to `{0}` before its return type is deduced",
|
||||
SemIR::NameId);
|
||||
CARBON_DIAGNOSTIC(ReturnTypeDeducedFromDefinition, Note,
|
||||
"return type of `{0}` is deduced from its definition",
|
||||
SemIR::NameId);
|
||||
context.emitter()
|
||||
.Build(loc_id, CallBeforeReturnTypeDeduced, callee.name_id)
|
||||
.Note(callee.latest_decl_id(), ReturnTypeDeducedFromDefinition,
|
||||
callee.name_id)
|
||||
.Emit();
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
if (callee.has_deduced_return_type && callee_specific_id->has_value()) {
|
||||
// TODO: Resolve the specific definition to determine the return type.
|
||||
context.TODO(loc_id, "call to generic function with deduced return type");
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
auto return_type_id =
|
||||
callee.GetDeclaredReturnType(context.sem_ir(), *callee_specific_id);
|
||||
if (!return_type_id.has_value()) {
|
||||
|
||||
@@ -204,9 +204,10 @@ auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
auto AddReturnInstWithCleanups(Context& context,
|
||||
SemIR::LocIdAndInst loc_id_and_inst) -> void {
|
||||
SemIR::LocIdAndInst loc_id_and_inst)
|
||||
-> SemIR::InstId {
|
||||
AddCleanups(context, context.scope_stack().function_cleanup_scope_depth());
|
||||
AddInst(context, loc_id_and_inst);
|
||||
return AddInst(context, loc_id_and_inst);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
@@ -110,18 +110,20 @@ auto AddBranchWithCleanups(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
// Adds a return instruction, along with cleanups for all live variables or
|
||||
// temporaries in the current function. If no instruction is passed, it
|
||||
// defaults to `SemIR::Return{}`.
|
||||
// defaults to `SemIR::Return{}`. Returns the return instruction.
|
||||
auto AddReturnInstWithCleanups(Context& context,
|
||||
SemIR::LocIdAndInst loc_id_and_inst) -> void;
|
||||
SemIR::LocIdAndInst loc_id_and_inst)
|
||||
-> SemIR::InstId;
|
||||
|
||||
template <typename LocT>
|
||||
auto AddReturnInstWithCleanups(Context& context, LocT loc) -> void {
|
||||
AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, SemIR::Return{}));
|
||||
auto AddReturnInstWithCleanups(Context& context, LocT loc) -> SemIR::InstId {
|
||||
return AddReturnInstWithCleanups(context,
|
||||
SemIR::LocIdAndInst(loc, SemIR::Return{}));
|
||||
}
|
||||
template <typename LocT>
|
||||
auto AddReturnInstWithCleanups(Context& context, LocT loc,
|
||||
SemIR::ReturnExpr inst) -> void {
|
||||
AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, inst));
|
||||
SemIR::ReturnExpr inst) -> SemIR::InstId {
|
||||
return AddReturnInstWithCleanups(context, SemIR::LocIdAndInst(loc, inst));
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -1539,6 +1539,13 @@ static auto ExportGenericFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
static auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId callee_function_id)
|
||||
-> clang::NamedDecl* {
|
||||
if (const auto& callee = context.functions().Get(callee_function_id);
|
||||
callee.has_pending_deduced_return_type() ||
|
||||
(callee.has_deduced_return_type && callee.generic_id.has_value())) {
|
||||
context.TODO(loc_id, "export of function with deduced return type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto target = BuildFunctionInfo(context, loc_id, callee_function_id);
|
||||
if (!target) {
|
||||
return nullptr;
|
||||
|
||||
+114
-11
@@ -59,6 +59,23 @@ auto AddReturnPattern(Context& context, SemIR::LocId loc_id,
|
||||
.type_inst_id = form_expr.type_component_inst_id});
|
||||
}
|
||||
|
||||
auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo {
|
||||
auto [node_id, pattern_id] =
|
||||
context.node_stack()
|
||||
.PopWithNodeIdIf<Parse::NodeCategory::ReturnSpecifier>();
|
||||
if (!pattern_id) {
|
||||
return {};
|
||||
}
|
||||
if (*pattern_id == SemIR::AutoType::TypeInstId) {
|
||||
// For a deduced return type, `HandleReturnSpecifier` pushes `auto` in place
|
||||
// of a return pattern, and doesn't push a return form.
|
||||
return {.node_id = node_id, .is_deduced = true};
|
||||
}
|
||||
return {.node_id = node_id,
|
||||
.pattern_id = *pattern_id,
|
||||
.form = context.PopReturnForm()};
|
||||
}
|
||||
|
||||
auto IsValidBuiltinDeclaration(Context& context,
|
||||
const SemIR::Function& function,
|
||||
SemIR::BuiltinFunctionKind builtin_kind)
|
||||
@@ -359,6 +376,25 @@ auto CheckFunctionReturnPatternType(Context& context, SemIR::LocId loc_id,
|
||||
return arg_type_id;
|
||||
}
|
||||
|
||||
// Checks that the return type of a function definition is suitable for a
|
||||
// definition. `return_call_param` is the `Call` parameter for the return, if
|
||||
// any.
|
||||
static auto CheckDefinitionReturnType(Context& context,
|
||||
SemIR::InstId return_pattern_id,
|
||||
SemIR::InstId return_call_param) -> void {
|
||||
CheckFunctionReturnPatternType(context, SemIR::LocId(return_pattern_id),
|
||||
return_pattern_id, SemIR::SpecificId::None);
|
||||
|
||||
// `CheckFunctionReturnPatternType` should have diagnosed incomplete types,
|
||||
// so don't `RequireCompleteType` on the return type.
|
||||
if (return_call_param.has_value()) {
|
||||
// TODO: If the types are already checked for completeness then this does
|
||||
// nothing?
|
||||
TryToCompleteType(context, context.insts().Get(return_call_param).type_id(),
|
||||
SemIR::LocId(return_call_param));
|
||||
}
|
||||
}
|
||||
|
||||
auto CheckFunctionDefinitionSignature(Context& context,
|
||||
SemIR::FunctionId function_id) -> void {
|
||||
auto& function = context.functions().Get(function_id);
|
||||
@@ -394,19 +430,78 @@ auto CheckFunctionDefinitionSignature(Context& context,
|
||||
|
||||
// Check the return type is complete.
|
||||
if (function.return_pattern_id.has_value()) {
|
||||
CheckFunctionReturnPatternType(
|
||||
context, SemIR::LocId(function.return_pattern_id),
|
||||
function.return_pattern_id, SemIR::SpecificId::None);
|
||||
CheckDefinitionReturnType(context, function.return_pattern_id,
|
||||
return_call_param);
|
||||
}
|
||||
}
|
||||
|
||||
// `CheckFunctionReturnPatternType` should have diagnosed incomplete types,
|
||||
// so don't `RequireCompleteType` on the return type.
|
||||
if (return_call_param.has_value()) {
|
||||
// TODO: If the types are already checked for completeness then this does
|
||||
// nothing?
|
||||
TryToCompleteType(context,
|
||||
context.insts().Get(return_call_param).type_id(),
|
||||
SemIR::LocId(return_call_param));
|
||||
auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id,
|
||||
SemIR::LocId loc_id, SemIR::TypeId type_id) -> void {
|
||||
auto& function = context.functions().Get(function_id);
|
||||
CARBON_CHECK(function.has_pending_deduced_return_type());
|
||||
|
||||
// A function with a deduced return type can't be redeclared, so there is only
|
||||
// one declaration to complete.
|
||||
auto decl_id = function.latest_decl_id();
|
||||
auto decl = context.insts().GetAs<SemIR::FunctionDecl>(decl_id);
|
||||
|
||||
// Reopen the function's declaration and pattern blocks to finish building the
|
||||
// signature. Add the return form and return pattern, as
|
||||
// `HandleReturnSpecifier` would for an explicitly declared return type,
|
||||
// followed by the callee pattern-match IR for the return, as
|
||||
// `PopNameComponent` would.
|
||||
context.inst_block_stack().Push(
|
||||
SemIR::InstBlockId::None,
|
||||
context.inst_blocks().GetOrEmpty(decl.decl_block_id));
|
||||
context.pattern_block_stack().Push(
|
||||
SemIR::InstBlockId::None,
|
||||
context.inst_blocks().GetOrEmpty(function.pattern_block_id));
|
||||
auto form = Context::FormExpr::Error;
|
||||
if (type_id != SemIR::ErrorInst::TypeId) {
|
||||
// Represent the return type as a type literal at the location it was
|
||||
// deduced from. This is a new instruction, rather than the canonical
|
||||
// instruction for the type, so that in a generic it is attached to the
|
||||
// current region, and can depend on anything declared before this point.
|
||||
auto type_inst_id = AddTypeInst<SemIR::TypeLiteral>(
|
||||
context, loc_id,
|
||||
{.type_id = SemIR::TypeType::TypeId,
|
||||
.value_id = context.types().GetTypeInstId(type_id)});
|
||||
auto form_inst_id = AddInst(
|
||||
context, SemIR::LocIdAndInst::RuntimeVerified(
|
||||
context.sem_ir(), loc_id,
|
||||
SemIR::InitForm{.type_id = SemIR::FormType::TypeId,
|
||||
.type_component_inst_id = type_inst_id}));
|
||||
form = {.form_inst_id = form_inst_id,
|
||||
.type_component_inst_id = type_inst_id,
|
||||
.type_component_id =
|
||||
context.types().GetTypeIdForTypeInstId(type_inst_id)};
|
||||
}
|
||||
auto return_pattern_id = AddReturnPattern(context, loc_id, form);
|
||||
auto results = CalleeReturnPatternMatch(
|
||||
context,
|
||||
{.call_param_patterns_id = function.call_param_patterns_id,
|
||||
.call_params_id = function.call_params_id,
|
||||
.param_ranges = function.call_param_ranges},
|
||||
return_pattern_id);
|
||||
function.pattern_block_id = context.pattern_block_stack().Pop();
|
||||
decl.decl_block_id = context.inst_block_stack().Pop();
|
||||
ReplaceInstPreservingConstantValue(context, decl_id, decl);
|
||||
|
||||
function.call_param_patterns_id = results.call_param_patterns_id;
|
||||
function.call_params_id = results.call_params_id;
|
||||
function.call_param_ranges = results.param_ranges;
|
||||
function.return_type_inst_id = form.type_component_inst_id;
|
||||
function.return_form_inst_id = form.form_inst_id;
|
||||
function.return_pattern_id = return_pattern_id;
|
||||
CARBON_CHECK(!function.has_pending_deduced_return_type());
|
||||
|
||||
if (type_id != SemIR::ErrorInst::TypeId) {
|
||||
auto return_call_param = SemIR::InstId::None;
|
||||
if (results.param_ranges.return_size() == 1) {
|
||||
return_call_param = context.inst_blocks().Get(
|
||||
results.call_params_id)[results.param_ranges.return_begin().index];
|
||||
}
|
||||
CheckDefinitionReturnType(context, return_pattern_id, return_call_param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,6 +730,14 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id,
|
||||
|
||||
auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id,
|
||||
SemIR::FunctionId function_id) -> void {
|
||||
// If the return type is deduced and we've not seen a `returned var`, the
|
||||
// return type is determined by the `return` statements in the body.
|
||||
if (context.functions().Get(function_id).has_pending_deduced_return_type() &&
|
||||
!DeduceReturnTypeAtEndOfBody(context, function_id)) {
|
||||
// There are no `return` statements, which has already been diagnosed.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the `}` of the function is reachable, reject if we need a return value
|
||||
// and otherwise add an implicit `return;`.
|
||||
if (IsCurrentPositionReachable(context)) {
|
||||
|
||||
@@ -29,6 +29,38 @@ auto FindSelfPattern(Context& context,
|
||||
auto AddReturnPattern(Context& context, SemIR::LocId loc_id,
|
||||
Context::FormExpr form_expr) -> SemIR::InstId;
|
||||
|
||||
// Information about the return specifier of a function, as popped by
|
||||
// `PopReturnSpecifier`.
|
||||
struct ReturnSpecifierInfo {
|
||||
// The return specifier, or `None` if there is none.
|
||||
Parse::NodeId node_id = Parse::NoneNodeId();
|
||||
// Whether the return type is deduced from the body, as in `-> auto`.
|
||||
bool is_deduced = false;
|
||||
// The declared return pattern. `None` if there is no return specifier or
|
||||
// the return type is deduced.
|
||||
SemIR::InstId pattern_id = SemIR::InstId::None;
|
||||
// The declared return form. `None` if there is no return specifier or the
|
||||
// return type is deduced.
|
||||
Context::FormExpr form = Context::FormExpr::None;
|
||||
|
||||
auto has_return_specifier() const -> bool { return node_id.has_value(); }
|
||||
};
|
||||
|
||||
// Pops the return specifier of a function declaration or function expression
|
||||
// from the node stack, if it has one.
|
||||
auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo;
|
||||
|
||||
// Sets the return type of a function whose return type is deduced, once that
|
||||
// type is known. `type_id` is the deduced type, or the error type if deduction
|
||||
// failed, and `loc_id` is the location it was deduced from.
|
||||
//
|
||||
// This creates the function's return form, return pattern and return `Call`
|
||||
// parameter, just as if the return type had been declared explicitly. Within a
|
||||
// generic function's definition, the return type is computed in the
|
||||
// definition's eval block.
|
||||
auto SetDeducedReturnType(Context& context, SemIR::FunctionId function_id,
|
||||
SemIR::LocId loc_id, SemIR::TypeId type_id) -> void;
|
||||
|
||||
// Returns whether `function` is a valid declaration of `builtin_kind`.
|
||||
auto IsValidBuiltinDeclaration(Context& context,
|
||||
const SemIR::Function& function,
|
||||
@@ -136,7 +168,9 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id,
|
||||
SemIR::FunctionId function_id) -> void;
|
||||
|
||||
// Checks that a function definition has a `return` at the end if required, or
|
||||
// adds an implicit `return;` if reachable.
|
||||
// adds an implicit `return;` if reachable. If the function's return type is
|
||||
// deduced and hasn't been deduced yet, deduces it from the function's `return`
|
||||
// statements first.
|
||||
auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id,
|
||||
SemIR::FunctionId function_id) -> void;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/check/unused.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/lex/token_kind.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/builtin_function_kind.h"
|
||||
@@ -50,24 +51,30 @@ auto HandleParseNode(Context& context, Parse::FunctionIntroducerId node_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handles a `->` or `->?` return declaration.
|
||||
static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id)
|
||||
-> bool {
|
||||
// Handles a `->` or `->?` return specifier.
|
||||
static auto HandleReturnSpecifier(Context& context,
|
||||
Parse::AnyReturnSpecifierId node_id) -> bool {
|
||||
auto [expr_node_id, expr_inst_id] = context.node_stack().PopExprWithNodeId();
|
||||
Context::FormExpr form_expr = [&] {
|
||||
// `-> T` provides a type expression, whereas `-> val T`, `-> ref T`,
|
||||
// `-> var T`, and `->? F` provide a form.
|
||||
auto expr_kind = context.parse_tree().node_kind(expr_node_id);
|
||||
bool is_primitive_form = expr_kind == Parse::ValPrimitiveFormId::Kind ||
|
||||
expr_kind == Parse::RefPrimitiveFormId::Kind ||
|
||||
expr_kind == Parse::VarPrimitiveFormId::Kind;
|
||||
if (context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind &&
|
||||
!is_primitive_form) {
|
||||
return ReturnExprAsForm(context, expr_node_id, expr_inst_id);
|
||||
} else {
|
||||
return FormExprAsForm(context, expr_node_id, expr_inst_id);
|
||||
}
|
||||
}();
|
||||
// `-> T` provides a type expression, whereas `-> val T`, `-> ref T`,
|
||||
// `-> var T`, and `->? F` provide a form.
|
||||
auto expr_kind = context.parse_tree().node_kind(expr_node_id);
|
||||
bool is_primitive_form = expr_kind == Parse::ValPrimitiveFormId::Kind ||
|
||||
expr_kind == Parse::RefPrimitiveFormId::Kind ||
|
||||
expr_kind == Parse::VarPrimitiveFormId::Kind;
|
||||
bool is_return_type =
|
||||
context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind &&
|
||||
!is_primitive_form;
|
||||
if (is_return_type && context.parse_tree().node_kind(expr_node_id) ==
|
||||
Parse::NodeKind::AutoTypeLiteral) {
|
||||
// `-> auto`: the return type is deduced from the function body, so there is
|
||||
// no return form or pattern yet. Push a marker in place of the pattern; see
|
||||
// `PopReturnSpecifier`.
|
||||
context.node_stack().Push(node_id, SemIR::AutoType::TypeInstId);
|
||||
return true;
|
||||
}
|
||||
Context::FormExpr form_expr =
|
||||
is_return_type ? ReturnExprAsForm(context, expr_node_id, expr_inst_id)
|
||||
: FormExprAsForm(context, expr_node_id, expr_inst_id);
|
||||
context.PushReturnForm(form_expr);
|
||||
context.node_stack().Push(node_id,
|
||||
AddReturnPattern(context, node_id, form_expr));
|
||||
@@ -75,11 +82,11 @@ static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id)
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool {
|
||||
return HandleReturnDecl(context, node_id);
|
||||
return HandleReturnSpecifier(context, node_id);
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::ReturnFormId node_id) -> bool {
|
||||
return HandleReturnDecl(context, node_id);
|
||||
return HandleReturnSpecifier(context, node_id);
|
||||
}
|
||||
|
||||
// Diagnoses issues with the modifiers, removing modifiers that shouldn't be
|
||||
@@ -367,6 +374,67 @@ static auto RequestVtableIfVirtual(
|
||||
context.vtable_stack().AddInstId(decl_id);
|
||||
}
|
||||
|
||||
// Diagnoses if a deduced return type is not permitted for the given function
|
||||
// declaration, and returns whether it is permitted. If not, clears any virtual
|
||||
// modifier, because a virtual function needs a known return type.
|
||||
static auto CheckDeducedReturnTypeAllowed(
|
||||
Context& context, Parse::AnyFunctionDeclId node_id,
|
||||
Parse::NodeId return_node_id, bool is_definition, bool is_extern,
|
||||
SemIR::Function::VirtualModifier& virtual_modifier,
|
||||
const std::optional<SemIR::Inst>& parent_scope_inst) -> bool {
|
||||
enum class Reason : int8_t { Builtin, Extern, InterfaceMember, Virtual };
|
||||
std::optional<Reason> reason;
|
||||
if (context.parse_tree().node_kind(node_id) ==
|
||||
Parse::NodeKind::BuiltinFunctionDefinitionStart) {
|
||||
// A builtin function has no body to deduce from.
|
||||
reason = Reason::Builtin;
|
||||
} else if (is_extern) {
|
||||
// An `extern` function is declared elsewhere.
|
||||
reason = Reason::Extern;
|
||||
} else if (parent_scope_inst &&
|
||||
parent_scope_inst->Is<SemIR::InterfaceWithSelfDecl>()) {
|
||||
// An associated function's return type is part of the interface's contract
|
||||
// with its impls, so shouldn't depend on a `default` definition.
|
||||
// TODO: A `final` function can't be overridden, so deducing its return
|
||||
// type from its definition would be reasonable. Consider allowing that
|
||||
// once calls to generic functions with deduced return types are supported.
|
||||
reason = Reason::InterfaceMember;
|
||||
} else if (virtual_modifier != SemIR::Function::VirtualModifier::None) {
|
||||
// The vtable is built before the function body is checked.
|
||||
reason = Reason::Virtual;
|
||||
virtual_modifier = SemIR::Function::VirtualModifier::None;
|
||||
}
|
||||
if (reason) {
|
||||
CARBON_DIAGNOSTIC(DeducedReturnTypeNotAllowed, Error,
|
||||
"{0:=0:builtin function|=1:`extern` function|=2:"
|
||||
"interface member|=3:virtual function} with deduced "
|
||||
"return type",
|
||||
Diagnostics::IntAsSelect);
|
||||
context.emitter().Emit(return_node_id, DeducedReturnTypeNotAllowed,
|
||||
static_cast<int>(*reason));
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return type is deduced from the function body, so only a definition
|
||||
// can declare one. As a consequence, the function can't be redeclared.
|
||||
if (!is_definition) {
|
||||
CARBON_DIAGNOSTIC(DeducedReturnTypeNotDefinition, Error,
|
||||
"forward declaration of function with deduced return "
|
||||
"type");
|
||||
context.emitter().Emit(return_node_id, DeducedReturnTypeNotDefinition);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parent_scope_inst && parent_scope_inst->Is<SemIR::ImplDecl>()) {
|
||||
// TODO: The impl witness and any thunks are built before the function body
|
||||
// is checked.
|
||||
context.TODO(return_node_id, "deduced return type for `impl` member");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build a FunctionDecl describing the signature of a function. This
|
||||
// handles the common logic shared by function declaration syntax and function
|
||||
// definition syntax.
|
||||
@@ -374,20 +442,10 @@ static auto BuildFunctionDecl(Context& context,
|
||||
Parse::AnyFunctionDeclId node_id,
|
||||
bool is_definition)
|
||||
-> std::pair<SemIR::FunctionId, SemIR::InstId> {
|
||||
auto return_pattern_id = SemIR::InstId::None;
|
||||
auto return_type_inst_id = SemIR::TypeInstId::None;
|
||||
auto return_form_inst_id = SemIR::InstId::None;
|
||||
if (auto [return_node, maybe_return_pattern_id] =
|
||||
context.node_stack()
|
||||
.PopWithNodeIdIf<Parse::NodeCategory::ReturnDecl>();
|
||||
maybe_return_pattern_id) {
|
||||
return_pattern_id = *maybe_return_pattern_id;
|
||||
auto return_form = context.PopReturnForm();
|
||||
return_type_inst_id = return_form.type_component_inst_id;
|
||||
return_form_inst_id = return_form.form_inst_id;
|
||||
}
|
||||
auto return_specifier = PopReturnSpecifier(context);
|
||||
|
||||
auto name = PopNameComponent(context, return_pattern_id);
|
||||
auto name = PopNameComponent(context, return_specifier.pattern_id,
|
||||
return_specifier.has_return_specifier());
|
||||
auto name_context = context.decl_name_stack().FinishName(name);
|
||||
|
||||
context.node_stack()
|
||||
@@ -422,6 +480,14 @@ static auto BuildFunctionDecl(Context& context,
|
||||
auto evaluation_mode = GetEvaluationMode(introducer.modifier_set);
|
||||
auto interface_modifier = GetInterfaceModifier(introducer.modifier_set);
|
||||
|
||||
// If the return type is deduced in a context where that's not allowed, we
|
||||
// will treat the function as having an invalid return type.
|
||||
bool is_invalid_deduced_return =
|
||||
return_specifier.is_deduced &&
|
||||
!CheckDeducedReturnTypeAllowed(context, node_id, return_specifier.node_id,
|
||||
is_definition, is_extern, virtual_modifier,
|
||||
parent_scope_inst);
|
||||
|
||||
// Add the function declaration.
|
||||
SemIR::FunctionDecl function_decl = {SemIR::TypeId::None,
|
||||
SemIR::FunctionId::None,
|
||||
@@ -430,21 +496,22 @@ static auto BuildFunctionDecl(Context& context,
|
||||
|
||||
// Build the function entity. This will be merged into an existing function if
|
||||
// there is one, or otherwise added to the function store.
|
||||
auto function_info =
|
||||
SemIR::Function{name_context.MakeEntityWithParamsBase(
|
||||
name, decl_id, is_extern, introducer.extern_library),
|
||||
{
|
||||
.call_param_patterns_id = name.call_param_patterns_id,
|
||||
.call_params_id = name.call_params_id,
|
||||
.call_param_ranges = name.param_ranges,
|
||||
.return_type_inst_id = return_type_inst_id,
|
||||
.return_form_inst_id = return_form_inst_id,
|
||||
.return_pattern_id = return_pattern_id,
|
||||
.virtual_modifier = virtual_modifier,
|
||||
.evaluation_mode = evaluation_mode,
|
||||
.interface_modifier = interface_modifier,
|
||||
.self_param_id = self_param_id,
|
||||
}};
|
||||
auto function_info = SemIR::Function{
|
||||
name_context.MakeEntityWithParamsBase(name, decl_id, is_extern,
|
||||
introducer.extern_library),
|
||||
{
|
||||
.call_param_patterns_id = name.call_param_patterns_id,
|
||||
.call_params_id = name.call_params_id,
|
||||
.call_param_ranges = name.param_ranges,
|
||||
.return_type_inst_id = return_specifier.form.type_component_inst_id,
|
||||
.return_form_inst_id = return_specifier.form.form_inst_id,
|
||||
.return_pattern_id = return_specifier.pattern_id,
|
||||
.virtual_modifier = virtual_modifier,
|
||||
.evaluation_mode = evaluation_mode,
|
||||
.interface_modifier = interface_modifier,
|
||||
.has_deduced_return_type = return_specifier.is_deduced,
|
||||
.self_param_id = self_param_id,
|
||||
}};
|
||||
if (is_definition) {
|
||||
function_info.definition_id = decl_id;
|
||||
}
|
||||
@@ -480,6 +547,13 @@ static auto BuildFunctionDecl(Context& context,
|
||||
// Write the function ID into the FunctionDecl.
|
||||
ReplaceInstBeforeConstantUse(context, decl_id, function_decl);
|
||||
|
||||
// If we diagnosed the deduced return type, recover by giving the function an
|
||||
// invalid return type.
|
||||
if (is_invalid_deduced_return) {
|
||||
SetDeducedReturnType(context, function_decl.function_id,
|
||||
return_specifier.node_id, SemIR::ErrorInst::TypeId);
|
||||
}
|
||||
|
||||
// Diagnose 'definition of `abstract` function' using the canonical Function's
|
||||
// modifiers.
|
||||
if (is_definition &&
|
||||
@@ -495,8 +569,12 @@ static auto BuildFunctionDecl(Context& context,
|
||||
MaybeAddToNameLookup(context, name_context, introducer.modifier_set,
|
||||
name_context.parent_scope_id, decl_id);
|
||||
|
||||
ValidateForEntryPoint(context, node_id, function_decl.function_id,
|
||||
function_info);
|
||||
// If the return type is deduced, we validate the entry point at the end of
|
||||
// the definition, once the return type is known.
|
||||
if (!return_specifier.is_deduced) {
|
||||
ValidateForEntryPoint(context, node_id, function_decl.function_id,
|
||||
function_info);
|
||||
}
|
||||
|
||||
if (!is_definition && context.sem_ir().is_impl() && !is_extern) {
|
||||
context.definitions_required_by_decl().push_back(decl_id);
|
||||
@@ -621,11 +699,19 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionStartId node_id)
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id)
|
||||
-> bool {
|
||||
SemIR::FunctionId function_id =
|
||||
context.node_stack().Pop<Parse::NodeKind::FunctionDefinitionStart>();
|
||||
auto [start_node_id, function_id] =
|
||||
context.node_stack()
|
||||
.PopWithNodeId<Parse::NodeKind::FunctionDefinitionStart>();
|
||||
|
||||
CheckFunctionReturnOnFinish(context, node_id, function_id);
|
||||
FinishFunctionDefinition(context, function_id);
|
||||
|
||||
// Now that the return type has been deduced, we can validate the entry point.
|
||||
const auto& function = context.functions().Get(function_id);
|
||||
if (function.has_deduced_return_type) {
|
||||
ValidateForEntryPoint(context, start_node_id, function_id, function);
|
||||
}
|
||||
|
||||
context.decl_name_stack().PopScope(/*check_unused=*/true);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -27,18 +27,7 @@ auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id)
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id)
|
||||
-> bool {
|
||||
auto return_pattern_id = SemIR::InstId::None;
|
||||
auto return_type_inst_id = SemIR::TypeInstId::None;
|
||||
auto return_form_inst_id = SemIR::InstId::None;
|
||||
if (auto [return_node, maybe_return_pattern_id] =
|
||||
context.node_stack()
|
||||
.PopWithNodeIdIf<Parse::NodeCategory::ReturnDecl>();
|
||||
maybe_return_pattern_id) {
|
||||
return_pattern_id = *maybe_return_pattern_id;
|
||||
auto return_form = context.PopReturnForm();
|
||||
return_type_inst_id = return_form.type_component_inst_id;
|
||||
return_form_inst_id = return_form.form_inst_id;
|
||||
}
|
||||
auto return_specifier = PopReturnSpecifier(context);
|
||||
|
||||
Parse::NodeId first_param_node_id = Parse::NoneNodeId();
|
||||
Parse::NodeId last_param_node_id = Parse::NoneNodeId();
|
||||
@@ -73,12 +62,12 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id)
|
||||
|
||||
auto match_results =
|
||||
CalleePatternMatch(context, *implicit_param_patterns_id,
|
||||
*param_patterns_id, return_pattern_id);
|
||||
*param_patterns_id, return_specifier.pattern_id);
|
||||
context.full_pattern_stack().PopFullPattern();
|
||||
auto pattern_block_id = context.pattern_block_stack().Pop();
|
||||
if (!param_patterns_id->has_value() &&
|
||||
!implicit_param_patterns_id->has_value() &&
|
||||
!return_pattern_id.has_value()) {
|
||||
!return_specifier.has_return_specifier()) {
|
||||
pattern_block_id = SemIR::InstBlockId::None;
|
||||
}
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
@@ -119,9 +108,11 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id)
|
||||
.call_param_patterns_id = match_results.call_param_patterns_id,
|
||||
.call_params_id = match_results.call_params_id,
|
||||
.call_param_ranges = match_results.param_ranges,
|
||||
.return_type_inst_id = return_type_inst_id,
|
||||
.return_form_inst_id = return_form_inst_id,
|
||||
.return_pattern_id = return_pattern_id,
|
||||
.return_type_inst_id =
|
||||
return_specifier.form.type_component_inst_id,
|
||||
.return_form_inst_id = return_specifier.form.form_inst_id,
|
||||
.return_pattern_id = return_specifier.pattern_id,
|
||||
.has_deduced_return_type = return_specifier.is_deduced,
|
||||
}});
|
||||
context.inst_block_stack().AddInstId(decl_id);
|
||||
|
||||
|
||||
@@ -148,6 +148,19 @@ auto HandleParseNode(Context& context, Parse::TypeTypeLiteralId node_id)
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::AutoTypeLiteralId node_id)
|
||||
-> bool {
|
||||
// `auto` is currently only supported as the entire return type of a
|
||||
// function, as in `fn F() -> auto`. In that case, `auto` is the only child of
|
||||
// a `ReturnType` node, so is immediately followed by it in postorder. There
|
||||
// is always a following node, because the tree ends with `FileEnd`.
|
||||
//
|
||||
// `HandleReturnSpecifier` recognizes the `auto` by its parse node, so the
|
||||
// value we push here is just a placeholder.
|
||||
Parse::NodeId next_node_id(node_id.index + 1);
|
||||
if (context.parse_tree().node_kind(next_node_id) ==
|
||||
Parse::NodeKind::ReturnType) {
|
||||
context.node_stack().Push(node_id, SemIR::AutoType::TypeInstId);
|
||||
return true;
|
||||
}
|
||||
return context.TODO(node_id, "HandleAutoTypeLiteral");
|
||||
}
|
||||
|
||||
|
||||
@@ -2765,6 +2765,8 @@ static auto TryResolveTypedInst(ImportRefResolver& resolver,
|
||||
return_form_const_id);
|
||||
}
|
||||
new_function.return_pattern_id = AddLoadedImportRef(resolver, return_pattern);
|
||||
new_function.has_deduced_return_type =
|
||||
import_function.has_deduced_return_type;
|
||||
if (import_function.definition_id.has_value()) {
|
||||
new_function.definition_id = new_function.first_owning_decl_id;
|
||||
}
|
||||
|
||||
@@ -829,6 +829,20 @@ auto TryMergeRedecl(Context& context,
|
||||
return false;
|
||||
}
|
||||
} else if constexpr (IsFunction) {
|
||||
// A function with a deduced return type can only be declared once, by its
|
||||
// definition. Check this before comparing return types, because the return
|
||||
// type of the previous declaration might not be deduced yet.
|
||||
if (entity_info.new_entity.has_deduced_return_type ||
|
||||
prev_entity.has_deduced_return_type) {
|
||||
CARBON_DIAGNOSTIC(DeducedReturnTypeRedecl, Error,
|
||||
"redeclaration of function with deduced return type");
|
||||
context.emitter()
|
||||
.Build(entity_info.new_entity.latest_decl_id(),
|
||||
DeducedReturnTypeRedecl)
|
||||
.Note(prev_entity.latest_decl_id(), RedeclPrevDecl)
|
||||
.Emit();
|
||||
return false;
|
||||
}
|
||||
if (!CheckFunctionTypeMatches(context, entity_info.new_entity,
|
||||
prev_entity)) {
|
||||
// Mismatch is diagnosed already if found.
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
|
||||
-> NameComponent {
|
||||
auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id,
|
||||
bool has_return_specifier) -> NameComponent {
|
||||
CARBON_CHECK(has_return_specifier || !return_pattern_id.has_value());
|
||||
Parse::NodeId first_param_node_id = Parse::NoneNodeId();
|
||||
Parse::NodeId last_param_node_id = Parse::NoneNodeId();
|
||||
|
||||
@@ -49,8 +50,7 @@ auto PopNameComponent(Context& context, SemIR::InstId return_pattern_id)
|
||||
auto param_ranges = SemIR::Function::CallParamIndexRanges::Empty;
|
||||
auto pattern_block_id = SemIR::InstBlockId::None;
|
||||
if (param_patterns_id->has_value() ||
|
||||
implicit_param_patterns_id->has_value() ||
|
||||
return_pattern_id.has_value()) {
|
||||
implicit_param_patterns_id->has_value() || has_return_specifier) {
|
||||
auto results = CalleePatternMatch(context, *implicit_param_patterns_id,
|
||||
*param_patterns_id, return_pattern_id);
|
||||
call_param_patterns_id = results.call_param_patterns_id;
|
||||
|
||||
@@ -47,10 +47,13 @@ struct NameComponent {
|
||||
};
|
||||
|
||||
// Pops a name component from the node stack (and pattern block stack, if it has
|
||||
// parameters).
|
||||
// parameters or a return specifier). `return_pattern_id` is the pattern
|
||||
// declared by the return specifier, if any. `has_return_specifier` indicates
|
||||
// whether there is a return specifier; this can be true even if there is no
|
||||
// return pattern, if the return type is deduced.
|
||||
auto PopNameComponent(Context& context,
|
||||
SemIR::InstId return_pattern_id = SemIR::InstId::None)
|
||||
-> NameComponent;
|
||||
SemIR::InstId return_pattern_id = SemIR::InstId::None,
|
||||
bool has_return_specifier = false) -> NameComponent;
|
||||
|
||||
// Equivalent to PopNameComponent, but also diagnoses if the name component has
|
||||
// parameters. Sets `*diagnosed_params` to true when diagnosing parameters if
|
||||
|
||||
@@ -404,7 +404,7 @@ class NodeStack {
|
||||
Parse::NodeCategory::Statement |
|
||||
Parse::NodeCategory::Modifier,
|
||||
Id::Kind::None);
|
||||
set_id_if_category_is(Parse::NodeCategory::ReturnDecl,
|
||||
set_id_if_category_is(Parse::NodeCategory::ReturnSpecifier,
|
||||
Id::KindFor<SemIR::InstId>());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1133,6 +1133,39 @@ auto CalleePatternMatch(Context& context,
|
||||
.param_ranges = {implicit_end, explicit_end, return_end}};
|
||||
}
|
||||
|
||||
auto CalleeReturnPatternMatch(Context& context,
|
||||
const CalleePatternMatchResults& param_results,
|
||||
SemIR::InstId return_pattern_id)
|
||||
-> CalleePatternMatchResults {
|
||||
CARBON_CHECK(param_results.param_ranges.return_size() == 0);
|
||||
auto call_param_patterns =
|
||||
context.inst_blocks().GetOrEmpty(param_results.call_param_patterns_id);
|
||||
auto call_params =
|
||||
context.inst_blocks().GetOrEmpty(param_results.call_params_id);
|
||||
CARBON_CHECK(call_params.size() == call_param_patterns.size());
|
||||
auto explicit_end = param_results.param_ranges.explicit_end();
|
||||
CARBON_CHECK(static_cast<size_t>(explicit_end.index) == call_params.size());
|
||||
|
||||
CalleeState state = {
|
||||
.index = IndexSource(explicit_end),
|
||||
.call_params = llvm::SmallVector<SemIR::InstId>(call_params.begin(),
|
||||
call_params.end()),
|
||||
.call_param_patterns = llvm::SmallVector<SemIR::InstId>(
|
||||
call_param_patterns.begin(), call_param_patterns.end())};
|
||||
MatchContext match(context);
|
||||
match.Match(&state, {.pattern_id = return_pattern_id,
|
||||
.work = MatchContext::PreWork{.scrutinee_id =
|
||||
SemIR::InstId::None}});
|
||||
auto return_end = SemIR::CallParamIndex(state.call_params.size());
|
||||
CARBON_CHECK(state.call_params.size() == state.call_param_patterns.size());
|
||||
|
||||
return {.call_param_patterns_id =
|
||||
context.inst_blocks().Add(state.call_param_patterns),
|
||||
.call_params_id = context.inst_blocks().Add(state.call_params),
|
||||
.param_ranges = {param_results.param_ranges.implicit_end(),
|
||||
explicit_end, return_end}};
|
||||
}
|
||||
|
||||
auto ThunkPatternMatch(Context& context,
|
||||
llvm::ArrayRef<SemIR::InstId> param_pattern_ids,
|
||||
llvm::ArrayRef<SemIR::InstId> outer_call_args)
|
||||
|
||||
@@ -46,6 +46,19 @@ auto CalleePatternMatch(Context& context,
|
||||
SemIR::InstId return_pattern_id)
|
||||
-> CalleePatternMatchResults;
|
||||
|
||||
// Emits the callee pattern-match IR for `return_pattern_id`, for an entity
|
||||
// whose parameters were already matched by `CalleePatternMatch` without a
|
||||
// return pattern, producing `param_results`. This is used when the return
|
||||
// pattern is only formed after the parameters, as for a deduced return type.
|
||||
// Returns `param_results` extended with the return's `Call` parameters.
|
||||
//
|
||||
// As with `CalleePatternMatch`, the pattern block containing the return pattern
|
||||
// should be on top of `context.pattern_block_stack()`.
|
||||
auto CalleeReturnPatternMatch(Context& context,
|
||||
const CalleePatternMatchResults& param_results,
|
||||
SemIR::InstId return_pattern_id)
|
||||
-> CalleePatternMatchResults;
|
||||
|
||||
// Return type for ThunkPatternMatch.
|
||||
struct ThunkPatternMatchResults {
|
||||
// The syntactic argument list. If there is a self parameter, the first
|
||||
|
||||
+283
-90
@@ -8,20 +8,25 @@
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/control_flow.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/function.h"
|
||||
#include "toolchain/check/inst.h"
|
||||
#include "toolchain/sem_ir/expr_info.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Gets the function that lexically encloses the current location.
|
||||
auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& {
|
||||
// Gets the ID of the function that lexically encloses the current location.
|
||||
static auto GetCurrentFunctionIdForReturn(Context& context)
|
||||
-> SemIR::FunctionId {
|
||||
CARBON_CHECK(context.scope_stack().IsInFunctionScope(),
|
||||
"Handling return but not in a function");
|
||||
auto decl_id = context.scope_stack().GetReturnScopeDeclId();
|
||||
auto function_id =
|
||||
context.insts().GetAs<SemIR::FunctionDecl>(decl_id).function_id;
|
||||
return context.functions().Get(function_id);
|
||||
return context.insts().GetAs<SemIR::FunctionDecl>(decl_id).function_id;
|
||||
}
|
||||
|
||||
// Gets the function that lexically encloses the current location.
|
||||
auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& {
|
||||
return context.functions().Get(GetCurrentFunctionIdForReturn(context));
|
||||
}
|
||||
|
||||
auto GetReturnedVarParam(Context& context, const SemIR::Function& function)
|
||||
@@ -68,6 +73,15 @@ static auto NoteReturnType(DiagnosticBuilder& diag,
|
||||
function.return_type_inst_id);
|
||||
}
|
||||
|
||||
// Produces a note that the return type of the given function is deduced, for a
|
||||
// function whose return type has not been deduced yet.
|
||||
static auto NoteReturnTypeIsDeduced(DiagnosticBuilder& diag,
|
||||
const SemIR::Function& function) {
|
||||
CARBON_DIAGNOSTIC(ReturnTypeIsDeducedNote, Note,
|
||||
"return type of function is deduced");
|
||||
diag.Note(function.latest_decl_id(), ReturnTypeIsDeducedNote);
|
||||
}
|
||||
|
||||
// Produces a note pointing at the currently in scope `returned var`.
|
||||
static auto NoteReturnedVar(DiagnosticBuilder& diag,
|
||||
SemIR::InstId returned_var_id) {
|
||||
@@ -75,10 +89,187 @@ static auto NoteReturnedVar(DiagnosticBuilder& diag,
|
||||
diag.Note(returned_var_id, ReturnedVarHere);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The result of converting the operand of a `return` statement to the return
|
||||
// form of the function.
|
||||
struct ConvertedReturnExpr {
|
||||
// The converted expression.
|
||||
SemIR::InstId expr_id;
|
||||
// The return slot that the converted expression initializes in place, if
|
||||
// any.
|
||||
SemIR::InstId dest_id = SemIR::InstId::None;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Converts the operand of a `return` statement to the return form of
|
||||
// `function`, which is required to have been declared or deduced already.
|
||||
static auto ConvertReturnExpr(Context& context, SemIR::LocId loc_id,
|
||||
const SemIR::Function& function,
|
||||
SemIR::InstId expr_id) -> ConvertedReturnExpr {
|
||||
auto return_type_id =
|
||||
context.types().GetTypeIdForTypeInstId(function.return_type_inst_id);
|
||||
auto return_form_id = function.GetDeclaredReturnForm(context.sem_ir());
|
||||
auto return_form = context.insts().Get(return_form_id);
|
||||
CARBON_KIND_SWITCH(return_form) {
|
||||
case CARBON_KIND(SemIR::InitForm _): {
|
||||
if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id)
|
||||
.is_valid() ||
|
||||
return_type_id == SemIR::ErrorInst::TypeId) {
|
||||
// We already diagnosed that the return type is invalid.
|
||||
// Don't try to convert to it.
|
||||
return {.expr_id = SemIR::ErrorInst::InstId};
|
||||
}
|
||||
if (function.call_param_ranges.return_size() == 0) {
|
||||
return {.expr_id = expr_id};
|
||||
}
|
||||
CARBON_CHECK(function.call_param_ranges.return_size() == 1);
|
||||
auto call_params = context.inst_blocks().Get(function.call_params_id);
|
||||
auto out_param_id =
|
||||
call_params[function.call_param_ranges.return_begin().index];
|
||||
CARBON_CHECK(out_param_id.has_value());
|
||||
expr_id = InitializeExisting(context, loc_id, out_param_id, expr_id,
|
||||
/*for_return=*/true);
|
||||
if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id)
|
||||
.MightBeInPlace()) {
|
||||
out_param_id = SemIR::InstId::None;
|
||||
}
|
||||
return {.expr_id = expr_id, .dest_id = out_param_id};
|
||||
}
|
||||
case CARBON_KIND(SemIR::RefForm ref_form): {
|
||||
return {.expr_id =
|
||||
Convert(context, loc_id, expr_id,
|
||||
ConversionTarget{
|
||||
.kind = ConversionTarget::DurableRef,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(
|
||||
ref_form.type_component_inst_id)})};
|
||||
}
|
||||
case CARBON_KIND(SemIR::ValueForm value_form): {
|
||||
return {.expr_id =
|
||||
Convert(context, loc_id, expr_id,
|
||||
ConversionTarget{
|
||||
.kind = ConversionTarget::Value,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(
|
||||
value_form.type_component_inst_id)})};
|
||||
}
|
||||
case CARBON_KIND(SemIR::ErrorInst _): {
|
||||
return {.expr_id = SemIR::ErrorInst::InstId};
|
||||
}
|
||||
case CARBON_KIND(SemIR::SymbolicBinding _): {
|
||||
auto expr_form_info = SemIR::GetFormInfo(context.sem_ir(), expr_id);
|
||||
if (expr_form_info.kind != SemIR::FormInfo::Dependent) {
|
||||
context.TODO(loc_id,
|
||||
"support nontrivial conversions between symbolic forms");
|
||||
return {.expr_id = SemIR::ErrorInst::InstId};
|
||||
}
|
||||
auto expr_form_const_id = SemIR::GetConstantValueInSpecific(
|
||||
context.sem_ir(), SemIR::SpecificId::None,
|
||||
expr_form_info.form_inst_id);
|
||||
auto declared_form_const_id = SemIR::GetConstantValueInSpecific(
|
||||
context.sem_ir(), SemIR::SpecificId::None, return_form_id);
|
||||
if (expr_form_const_id != declared_form_const_id) {
|
||||
context.TODO(loc_id,
|
||||
"support nontrivial conversions between symbolic forms");
|
||||
return {.expr_id = SemIR::ErrorInst::InstId};
|
||||
}
|
||||
// expr_id's form is identical to the expected form, so we can use it
|
||||
// directly.
|
||||
return {.expr_id = expr_id};
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL("Unexpected inst kind: {0}", return_form);
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a `return` statement in a function whose return type has not been
|
||||
// deduced yet. Immediately builds control flow and cleanups, but defers the
|
||||
// conversion of the returned expression to the return type until the return
|
||||
// type is known; see `CompletePendingReturns`.
|
||||
static auto BuildPendingReturn(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId expr_id) -> void {
|
||||
// The conversion will be spliced in here. Until then, this placeholder is an
|
||||
// identity conversion. Because it precedes the cleanups, the return value is
|
||||
// initialized before any local variables are destroyed.
|
||||
auto splice_id = AddPlaceholderInst(
|
||||
context, loc_id,
|
||||
SemIR::SpliceBlock{.type_id = context.insts().Get(expr_id).type_id(),
|
||||
.block_id = SemIR::InstBlockId::Empty,
|
||||
.result_id = expr_id});
|
||||
auto return_id = AddReturnInstWithCleanups(
|
||||
context, loc_id,
|
||||
SemIR::ReturnExpr{.expr_id = splice_id, .dest_id = SemIR::InstId::None});
|
||||
context.scope_stack().AddPendingReturn({.expr_id = expr_id,
|
||||
.splice_id = splice_id,
|
||||
.return_inst_id = return_id});
|
||||
}
|
||||
|
||||
// Completes the `return` statements built by `BuildPendingReturn` in the
|
||||
// current function, once its return type has been deduced.
|
||||
static auto CompletePendingReturns(Context& context,
|
||||
SemIR::FunctionId function_id) -> void {
|
||||
auto pending_returns = context.scope_stack().TakePendingReturns();
|
||||
if (pending_returns.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& function = context.functions().Get(function_id);
|
||||
|
||||
// Any problems converting to the return type are probably due to the choice
|
||||
// of return type, so point out where it came from.
|
||||
Diagnostics::AnnotationScope annotate_diagnostics(
|
||||
&context.emitter(), [&](DiagnosticBuilder& builder) {
|
||||
// TODO: Track the instruction the return type was deduced for and use
|
||||
// TypeOfInstId to preserve sugar.
|
||||
CARBON_DIAGNOSTIC(ReturnTypeDeducedAs, Note,
|
||||
"return type deduced as {0}", InstIdAsType);
|
||||
builder.Note(function.return_type_inst_id, ReturnTypeDeducedAs,
|
||||
function.return_type_inst_id);
|
||||
});
|
||||
|
||||
for (const auto& pending_return : pending_returns) {
|
||||
auto loc_id = SemIR::LocId(pending_return.return_inst_id);
|
||||
|
||||
// Convert in a separate block that we splice in place of the placeholder,
|
||||
// with its own cleanup scope for any temporaries.
|
||||
context.inst_block_stack().Push();
|
||||
context.scope_stack().PushForSameRegion(
|
||||
ScopeStack::CleanupScopeKind::Owned);
|
||||
auto converted =
|
||||
ConvertReturnExpr(context, loc_id, function, pending_return.expr_id);
|
||||
AddAndDiscardScopeCleanups(context);
|
||||
context.scope_stack().Pop();
|
||||
auto block_id = context.inst_block_stack().Pop();
|
||||
|
||||
ReplaceInstBeforeConstantUse(
|
||||
context, pending_return.splice_id,
|
||||
SemIR::SpliceBlock{
|
||||
.type_id = context.insts().Get(converted.expr_id).type_id(),
|
||||
.block_id = block_id,
|
||||
.result_id = converted.expr_id});
|
||||
if (converted.dest_id.has_value()) {
|
||||
// The `return` statement has no type or constant value, and nothing
|
||||
// refers to it, so it's safe to replace it.
|
||||
auto return_inst = context.insts().GetAs<SemIR::ReturnExpr>(
|
||||
pending_return.return_inst_id);
|
||||
return_inst.dest_id = converted.dest_id;
|
||||
ReplaceInstBeforeConstantUse(context, pending_return.return_inst_id,
|
||||
return_inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node,
|
||||
Parse::NodeId type_node, SemIR::TypeId type_id,
|
||||
SemIR::InstId bind_id, SemIR::NameId name_id) -> void {
|
||||
auto& function = GetCurrentFunctionForReturn(context);
|
||||
auto function_id = GetCurrentFunctionIdForReturn(context);
|
||||
auto& function = context.functions().Get(function_id);
|
||||
if (function.has_pending_deduced_return_type()) {
|
||||
// In a function with a deduced return type, the first `returned var`
|
||||
// determines the return type, and any earlier `return`s are converted to
|
||||
// it.
|
||||
SetDeducedReturnType(context, function_id, type_node, type_id);
|
||||
CompletePendingReturns(context, function_id);
|
||||
}
|
||||
|
||||
auto return_type_id = function.GetDeclaredReturnType(context.sem_ir());
|
||||
|
||||
// A `returned var` requires an explicit return type.
|
||||
@@ -129,11 +320,20 @@ auto RegisterReturnedVar(Context& context, Parse::NodeId returned_node,
|
||||
|
||||
auto BuildReturnWithNoExpr(Context& context, SemIR::LocId loc_id) -> void {
|
||||
const auto& function = GetCurrentFunctionForReturn(context);
|
||||
auto return_type_id = function.GetDeclaredReturnType(context.sem_ir());
|
||||
CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error, "missing return value");
|
||||
|
||||
if (return_type_id.has_value()) {
|
||||
CARBON_DIAGNOSTIC(ReturnStatementMissingExpr, Error,
|
||||
"missing return value");
|
||||
// `-> auto` requires a returned value, just like an explicit return type.
|
||||
if (function.has_pending_deduced_return_type()) {
|
||||
auto diag = context.emitter().Build(loc_id, ReturnStatementMissingExpr);
|
||||
NoteReturnTypeIsDeduced(diag, function);
|
||||
diag.Emit();
|
||||
// Treat this as returning an erroneous value, which doesn't participate in
|
||||
// return type deduction.
|
||||
BuildPendingReturn(context, loc_id, SemIR::ErrorInst::InstId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (function.GetDeclaredReturnType(context.sem_ir()).has_value()) {
|
||||
auto diag = context.emitter().Build(loc_id, ReturnStatementMissingExpr);
|
||||
NoteReturnType(diag, function);
|
||||
diag.Emit();
|
||||
@@ -146,13 +346,20 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId expr_id) -> void {
|
||||
const auto& function = GetCurrentFunctionForReturn(context);
|
||||
auto returned_var_id = GetCurrentReturnedVar(context);
|
||||
auto out_param_id = SemIR::InstId::None;
|
||||
|
||||
if (function.has_pending_deduced_return_type()) {
|
||||
// Declaring a `returned var` would have deduced the return type.
|
||||
CARBON_CHECK(!returned_var_id.has_value());
|
||||
BuildPendingReturn(context, loc_id, expr_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto return_type_id = SemIR::TypeId::None;
|
||||
if (function.return_type_inst_id.has_value()) {
|
||||
return_type_id =
|
||||
context.types().GetTypeIdForTypeInstId(function.return_type_inst_id);
|
||||
}
|
||||
auto converted = ConvertedReturnExpr{.expr_id = SemIR::ErrorInst::InstId};
|
||||
if (!return_type_id.has_value()) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
ReturnStatementDisallowExpr, Error,
|
||||
@@ -160,7 +367,6 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id,
|
||||
auto diag = context.emitter().Build(loc_id, ReturnStatementDisallowExpr);
|
||||
NoteNoReturnTypeProvided(diag, function);
|
||||
diag.Emit();
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
} else if (returned_var_id.has_value()) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
ReturnExprWithReturnedVar, Error,
|
||||
@@ -168,86 +374,12 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id,
|
||||
auto diag = context.emitter().Build(loc_id, ReturnExprWithReturnedVar);
|
||||
NoteReturnedVar(diag, returned_var_id);
|
||||
diag.Emit();
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
} else {
|
||||
auto return_form_id = function.GetDeclaredReturnForm(context.sem_ir());
|
||||
auto return_form = context.insts().Get(return_form_id);
|
||||
CARBON_KIND_SWITCH(return_form) {
|
||||
case CARBON_KIND(SemIR::InitForm _): {
|
||||
if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id)
|
||||
.is_valid() ||
|
||||
return_type_id == SemIR::ErrorInst::TypeId) {
|
||||
// We already diagnosed that the return type is invalid.
|
||||
// Don't try to convert to it.
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
break;
|
||||
}
|
||||
auto call_params = context.inst_blocks().Get(function.call_params_id);
|
||||
if (function.call_param_ranges.return_size() == 0) {
|
||||
out_param_id = SemIR::InstId::None;
|
||||
break;
|
||||
}
|
||||
CARBON_CHECK(function.call_param_ranges.return_size() == 1);
|
||||
out_param_id =
|
||||
call_params[function.call_param_ranges.return_begin().index];
|
||||
CARBON_CHECK(out_param_id.has_value());
|
||||
expr_id = InitializeExisting(context, loc_id, out_param_id, expr_id,
|
||||
/*for_return=*/true);
|
||||
if (!SemIR::InitRepr::ForType(context.sem_ir(), return_type_id)
|
||||
.MightBeInPlace()) {
|
||||
out_param_id = SemIR::InstId::None;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::RefForm ref_form): {
|
||||
expr_id = Convert(
|
||||
context, loc_id, expr_id,
|
||||
ConversionTarget{.kind = ConversionTarget::DurableRef,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(
|
||||
ref_form.type_component_inst_id)});
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::ValueForm value_form): {
|
||||
expr_id = Convert(
|
||||
context, loc_id, expr_id,
|
||||
ConversionTarget{.kind = ConversionTarget::Value,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(
|
||||
value_form.type_component_inst_id)});
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::ErrorInst _): {
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
break;
|
||||
}
|
||||
case CARBON_KIND(SemIR::SymbolicBinding _): {
|
||||
auto expr_form_info = SemIR::GetFormInfo(context.sem_ir(), expr_id);
|
||||
if (expr_form_info.kind != SemIR::FormInfo::Dependent) {
|
||||
context.TODO(loc_id,
|
||||
"support nontrivial conversions between symbolic forms");
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
break;
|
||||
}
|
||||
auto expr_form_const_id = SemIR::GetConstantValueInSpecific(
|
||||
context.sem_ir(), SemIR::SpecificId::None,
|
||||
expr_form_info.form_inst_id);
|
||||
auto declared_form_const_id = SemIR::GetConstantValueInSpecific(
|
||||
context.sem_ir(), SemIR::SpecificId::None, return_form_id);
|
||||
if (expr_form_const_id != declared_form_const_id) {
|
||||
context.TODO(loc_id,
|
||||
"support nontrivial conversions between symbolic forms");
|
||||
expr_id = SemIR::ErrorInst::InstId;
|
||||
break;
|
||||
}
|
||||
// expr_id's form is identical to the expected form, so we can use it
|
||||
// directly.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CARBON_FATAL("Unexpected inst kind: {0}", return_form);
|
||||
}
|
||||
converted = ConvertReturnExpr(context, loc_id, function, expr_id);
|
||||
}
|
||||
AddReturnInstWithCleanups(context, loc_id,
|
||||
{.expr_id = expr_id, .dest_id = out_param_id});
|
||||
AddReturnInstWithCleanups(
|
||||
context, loc_id,
|
||||
{.expr_id = converted.expr_id, .dest_id = converted.dest_id});
|
||||
}
|
||||
|
||||
auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
|
||||
@@ -262,6 +394,13 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
|
||||
returned_var_id = SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
if (function.has_pending_deduced_return_type()) {
|
||||
// Declaring a `returned var` would have deduced the return type, so we
|
||||
// diagnosed above, and `returned_var_id` is `ErrorInst::InstId`.
|
||||
BuildPendingReturn(context, node_id, returned_var_id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto return_param_id = GetReturnedVarParam(context, function);
|
||||
|
||||
// Convert to a value expression in case the return logic needs a value, and
|
||||
@@ -273,4 +412,58 @@ auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id)
|
||||
{.expr_id = returned_var_id, .dest_id = return_param_id});
|
||||
}
|
||||
|
||||
auto DeduceReturnTypeAtEndOfBody(Context& context,
|
||||
SemIR::FunctionId function_id) -> bool {
|
||||
const auto& function = context.functions().Get(function_id);
|
||||
CARBON_CHECK(function.has_pending_deduced_return_type());
|
||||
auto pending_returns = context.scope_stack().PeekPendingReturns();
|
||||
auto decl_loc_id = SemIR::LocId(function.latest_decl_id());
|
||||
|
||||
if (pending_returns.empty()) {
|
||||
CARBON_DIAGNOSTIC(DeducedReturnTypeWithoutReturn, Error,
|
||||
"no `return` in function with deduced return type");
|
||||
context.emitter().Emit(decl_loc_id, DeducedReturnTypeWithoutReturn);
|
||||
SetDeducedReturnType(context, function_id, decl_loc_id,
|
||||
SemIR::ErrorInst::TypeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return type is the type of the returned expressions. Erroneous returned
|
||||
// expressions are ignored, to avoid follow-on diagnostics.
|
||||
// TODO: Use the common type of the returned expressions.
|
||||
auto first_expr_id = SemIR::InstId::None;
|
||||
auto return_type_id = SemIR::ErrorInst::TypeId;
|
||||
for (const auto& pending : pending_returns) {
|
||||
auto type_id = context.insts().Get(pending.expr_id).type_id();
|
||||
if (type_id == SemIR::ErrorInst::TypeId) {
|
||||
continue;
|
||||
}
|
||||
if (!first_expr_id.has_value()) {
|
||||
first_expr_id = pending.expr_id;
|
||||
return_type_id = type_id;
|
||||
} else if (type_id != return_type_id) {
|
||||
CARBON_DIAGNOSTIC(
|
||||
DeducedReturnTypeMismatch, Error,
|
||||
"deduced return type {0} does not match return type deduced earlier",
|
||||
TypeOfInstId);
|
||||
CARBON_DIAGNOSTIC(DeducedReturnTypeMismatchPrevious, Note,
|
||||
"return type previously deduced as {0} here",
|
||||
TypeOfInstId);
|
||||
context.emitter()
|
||||
.Build(pending.expr_id, DeducedReturnTypeMismatch, pending.expr_id)
|
||||
.Note(first_expr_id, DeducedReturnTypeMismatchPrevious, first_expr_id)
|
||||
.Emit();
|
||||
return_type_id = SemIR::ErrorInst::TypeId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetDeducedReturnType(
|
||||
context, function_id,
|
||||
first_expr_id.has_value() ? SemIR::LocId(first_expr_id) : decl_loc_id,
|
||||
return_type_id);
|
||||
CompletePendingReturns(context, function_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
@@ -36,6 +36,14 @@ auto BuildReturnWithExpr(Context& context, SemIR::LocId loc_id,
|
||||
// Checks and builds SemIR for a `return var;` statement.
|
||||
auto BuildReturnVar(Context& context, Parse::ReturnStatementId node_id) -> void;
|
||||
|
||||
// Deduces the return type of the current function, which must have an
|
||||
// undeduced return type, from the `return` statements in its body, and
|
||||
// completes the initialization of their return values. Called at the end of the
|
||||
// function body. Returns false if there are no `return` statements to deduce
|
||||
// from, after diagnosing that.
|
||||
auto DeduceReturnTypeAtEndOfBody(Context& context,
|
||||
SemIR::FunctionId function_id) -> bool;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_RETURN_H_
|
||||
@@ -26,6 +26,8 @@ auto ScopeStack::sem_ir() const -> const SemIR::File& {
|
||||
|
||||
auto ScopeStack::VerifyOnFinish() const -> void {
|
||||
CARBON_CHECK(return_scope_stack_.empty(), "{0}", return_scope_stack_.size());
|
||||
CARBON_CHECK(pending_return_stack_.empty(), "{0}",
|
||||
pending_return_stack_.all_values_size());
|
||||
CARBON_CHECK(break_continue_stack_.empty(), "{0}",
|
||||
break_continue_stack_.size());
|
||||
CARBON_CHECK(scope_stack_.empty(), "{0}", scope_stack_.size());
|
||||
@@ -136,6 +138,7 @@ auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void {
|
||||
|
||||
return_scope_stack_.push_back(
|
||||
{.decl_id = scope_inst_id, .cleanup_scope_depth = cleanup_scope_depth()});
|
||||
pending_return_stack_.PushArray();
|
||||
}
|
||||
|
||||
auto ScopeStack::Pop(bool check_unused) -> void {
|
||||
@@ -172,6 +175,9 @@ auto ScopeStack::Pop(bool check_unused) -> void {
|
||||
|
||||
if (return_scope_stack_.back().decl_id == scope.scope_inst_id) {
|
||||
// Leaving the function scope.
|
||||
CARBON_CHECK(pending_return_stack_.PeekArray().empty(),
|
||||
"Leaving function with pending `return`s");
|
||||
pending_return_stack_.PopArray();
|
||||
return_scope_stack_.pop_back();
|
||||
} else {
|
||||
if (return_scope_stack_.back().nested_scope_index == scope.index) {
|
||||
|
||||
@@ -191,6 +191,44 @@ class ScopeStack {
|
||||
return return_scope_stack_.back().decl_id;
|
||||
}
|
||||
|
||||
// A `return` statement in a function whose return type is being deduced.
|
||||
// Initialization of the return value is deferred until the return type is
|
||||
// known.
|
||||
struct PendingReturn {
|
||||
// The returned expression, before conversion to the return type.
|
||||
SemIR::InstId expr_id;
|
||||
// A placeholder `SpliceBlock` producing `expr_id`, to be replaced by the
|
||||
// conversion of `expr_id` to the return type.
|
||||
SemIR::InstId splice_id;
|
||||
// The `ReturnExpr` instruction. Its destination is set once the return
|
||||
// type is known.
|
||||
SemIR::InstId return_inst_id;
|
||||
};
|
||||
|
||||
// Adds a `return` statement in the current function whose initialization is
|
||||
// waiting for the return type to be deduced.
|
||||
auto AddPendingReturn(PendingReturn pending_return) -> void {
|
||||
CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function");
|
||||
pending_return_stack_.AppendToTop(pending_return);
|
||||
}
|
||||
|
||||
// Returns the `return` statements in the current function whose
|
||||
// initialization is waiting for the return type to be deduced.
|
||||
auto PeekPendingReturns() const -> llvm::ArrayRef<PendingReturn> {
|
||||
CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function");
|
||||
return pending_return_stack_.PeekArray();
|
||||
}
|
||||
|
||||
// Removes and returns the `return` statements in the current function whose
|
||||
// initialization is waiting for the return type to be deduced.
|
||||
auto TakePendingReturns() -> llvm::SmallVector<PendingReturn> {
|
||||
auto pending_returns = llvm::to_vector(PeekPendingReturns());
|
||||
// Leave an empty array for the current function.
|
||||
pending_return_stack_.PopArray();
|
||||
pending_return_stack_.PushArray();
|
||||
return pending_returns;
|
||||
}
|
||||
|
||||
// Looks up the name `name_id` in the current scope and enclosing scopes, but
|
||||
// do not look past `scope_index`. Returns the existing lookup result, if any.
|
||||
// If `use_loc_id` is specified, the name is marked as used at that location.
|
||||
@@ -432,6 +470,10 @@ class ScopeStack {
|
||||
// A stack of scopes from which we can `return`.
|
||||
llvm::SmallVector<ReturnScope> return_scope_stack_;
|
||||
|
||||
// For each entry in `return_scope_stack_`, the `return` statements whose
|
||||
// initialization is waiting for the function's return type to be deduced.
|
||||
ArrayStack<PendingReturn> pending_return_stack_;
|
||||
|
||||
// A stack of `break` and `continue` targets.
|
||||
llvm::SmallVector<BreakContinueScope> break_continue_stack_;
|
||||
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/basic.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/basic.carbon
|
||||
|
||||
// --- single_return.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F() -> auto {
|
||||
return 0 as i32;
|
||||
}
|
||||
|
||||
fn G() -> i32 {
|
||||
return F();
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- multiple_returns.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
return 1 as i32;
|
||||
}
|
||||
return 2 as i32;
|
||||
}
|
||||
|
||||
fn G() -> i32 {
|
||||
return F(true);
|
||||
}
|
||||
|
||||
// --- int_literal.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// Literal types are not decayed.
|
||||
fn F() -> auto {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn G() -> Core.IntLiteral {
|
||||
return F();
|
||||
}
|
||||
|
||||
// --- in_place.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F() -> auto {
|
||||
return {.a = 1, .b = 2} as C;
|
||||
}
|
||||
|
||||
fn G() -> C {
|
||||
return F();
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- type.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
eval fn F() -> auto {
|
||||
return i32;
|
||||
}
|
||||
|
||||
var n: F() = 0;
|
||||
|
||||
// --- nested.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto {
|
||||
fn G() -> auto {
|
||||
return 1 as i32;
|
||||
}
|
||||
return G();
|
||||
}
|
||||
|
||||
fn H() -> i32 {
|
||||
return F();
|
||||
}
|
||||
|
||||
// --- lambda.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> i32 {
|
||||
//@dump-sem-ir-begin
|
||||
alias lambda = fn (x: i32) -> auto { return x; };
|
||||
return lambda(1);
|
||||
//@dump-sem-ir-end
|
||||
}
|
||||
|
||||
// --- lambda_no_params.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> i32 {
|
||||
alias lambda = fn () -> auto { return 1 as i32; };
|
||||
return lambda();
|
||||
}
|
||||
|
||||
// --- member.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
fn F() -> auto { return 1 as i32; }
|
||||
fn G(self) -> auto { return self.n; }
|
||||
// The body of `F` is checked before the body of `H`.
|
||||
fn H() -> i32 { return F(); }
|
||||
var n: i32;
|
||||
}
|
||||
|
||||
fn Use(c: C) -> i32 {
|
||||
return c.G();
|
||||
}
|
||||
|
||||
// --- unreachable_end.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
return 1 as i32;
|
||||
} else {
|
||||
return 2 as i32;
|
||||
}
|
||||
}
|
||||
|
||||
fn G() -> i32 {
|
||||
return F(false);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- single_return.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %As.type.b93: type = facet_type <@As, @As(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.396: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.84a: %Core.IntLiteral.as.As.impl.Convert.type.396 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %As.impl_witness.e09: <witness> = impl_witness imports.%As.impl_witness_table.1c2, @Core.IntLiteral.as.As.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.type.2b0: type = fn_type @Core.IntLiteral.as.As.impl.Convert, @Core.IntLiteral.as.As.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.94f: %Core.IntLiteral.as.As.impl.Convert.type.2b0 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %As.facet: %As.type.b93 = facet_value Core.IntLiteral, (%As.impl_witness.e09) [concrete]
|
||||
// CHECK:STDOUT: %As.WithSelf.Convert.type.2be: type = fn_type @As.WithSelf.Convert, @As.WithSelf(%i32, %As.facet) [concrete]
|
||||
// CHECK:STDOUT: %.979: type = fn_type_with_self_type %As.WithSelf.Convert.type.2be, %As.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.bound: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.As.impl.Convert.94f [concrete]
|
||||
// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.As.impl.Convert.94f, @Core.IntLiteral.as.As.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.e88: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.As.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete]
|
||||
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %Copy.impl_witness.8af: <witness> = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete]
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
|
||||
// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.bound: <bound method> = bound_method %int_0.753, %Int.as.Copy.impl.Op.cae [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.149: <bound method> = bound_method %int_0.753, %Int.as.Copy.impl.Op.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %G.type: type = fn_type @G [concrete]
|
||||
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.9e0: @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert.type (%Core.IntLiteral.as.As.impl.Convert.type.396) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.As.impl.%Core.IntLiteral.as.As.impl.Convert (constants.%Core.IntLiteral.as.As.impl.Convert.84a)]
|
||||
// CHECK:STDOUT: %As.impl_witness_table.1c2 = impl_witness_table (%Core.import_ref.9e0), @Core.IntLiteral.as.As.impl [concrete]
|
||||
// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)]
|
||||
// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_12 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %i32.loc5_12: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc5_12.3: Core.Form = init_form %i32.loc5_12 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc8: Core.Form = init_form %i32 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %i32 [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %i32.loc5_15: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %impl.elem0.loc5_12.1: %.979 = impl_witness_access constants.%As.impl_witness.e09, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.94f]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.1: <bound method> = bound_method %int_0, %impl.elem0.loc5_12.1 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn.loc5_12.1: <specific function> = specific_function %impl.elem0.loc5_12.1, @Core.IntLiteral.as.As.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.2: <bound method> = bound_method %int_0, %specific_fn.loc5_12.1 [concrete = constants.%bound_method.e88]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.As.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_0) [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: %.loc5_12.1: %i32 = value_of_initializer %Core.IntLiteral.as.As.impl.Convert.call [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: %.loc5_12.2: %i32 = converted %int_0, %.loc5_12.1 [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: %.loc5_18: init %i32 = splice_block %Int.as.Copy.impl.Op.call [concrete = constants.%int_0.753] {
|
||||
// CHECK:STDOUT: %impl.elem0.loc5_12.2: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.3: <bound method> = bound_method %.loc5_12.2, %impl.elem0.loc5_12.2 [concrete = constants.%Int.as.Copy.impl.Op.bound]
|
||||
// CHECK:STDOUT: %specific_fn.loc5_12.2: <specific function> = specific_function %impl.elem0.loc5_12.2, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.4: <bound method> = bound_method %.loc5_12.2, %specific_fn.loc5_12.2 [concrete = constants.%bound_method.149]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_12.4(%.loc5_12.2) [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: return %.loc5_18
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() -> out %return.param: %i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %F.call: init %i32 = call %F.ref()
|
||||
// CHECK:STDOUT: return %F.call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- in_place.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
|
||||
// CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete]
|
||||
// CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.3bd: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %bound_method.a55: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_2.2e0: %i32 = int_value 2 [concrete]
|
||||
// CHECK:STDOUT: %C.val: %C = struct_value (%int_1.68a, %int_2.2e0) [concrete]
|
||||
// CHECK:STDOUT: %.a44: Core.Form = init_form %C [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.89a: %pattern_type.98b = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.e4a: %pattern_type.98b = return_slot_pattern %return.param_patt.89a, %C [concrete]
|
||||
// CHECK:STDOUT: %G.type: type = fn_type @G [concrete]
|
||||
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %.loc10_27.3 [concrete = constants.%return.patt.e4a]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %.loc10_27.3: type = type_literal constants.%C [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %.loc10_27.4: Core.Form = init_form %.loc10_27.3 [concrete = constants.%.a44]
|
||||
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %C.ref [concrete = constants.%return.patt.e4a]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %.loc13_11.2: Core.Form = init_form %C.ref [concrete = constants.%.a44]
|
||||
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %C [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
|
||||
// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
|
||||
// CHECK:STDOUT: %.loc10_25.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct]
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %impl.elem0.loc10_25.1: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc10_25.1: <bound method> = bound_method %int_1, %impl.elem0.loc10_25.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282]
|
||||
// CHECK:STDOUT: %specific_fn.loc10_25.1: <specific function> = specific_function %impl.elem0.loc10_25.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc10_25.2: <bound method> = bound_method %int_1, %specific_fn.loc10_25.1 [concrete = constants.%bound_method.3bd]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.1: init %i32 = call %bound_method.loc10_25.2(%int_1) [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc10_25.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.1 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc10_27.1: ref %C = splice_block %return.param {}
|
||||
// CHECK:STDOUT: %.loc10_25.3: ref %i32 = class_element_access %.loc10_27.1, element0
|
||||
// CHECK:STDOUT: %.loc10_25.4: init %i32 to %.loc10_25.3 = in_place_init %.loc10_25.2 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %impl.elem0.loc10_25.2: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc10_25.3: <bound method> = bound_method %int_2, %impl.elem0.loc10_25.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6]
|
||||
// CHECK:STDOUT: %specific_fn.loc10_25.2: <specific function> = specific_function %impl.elem0.loc10_25.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc10_25.4: <bound method> = bound_method %int_2, %specific_fn.loc10_25.2 [concrete = constants.%bound_method.a55]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.2: init %i32 = call %bound_method.loc10_25.4(%int_2) [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc10_25.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc10_25.2 [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc10_25.6: ref %i32 = class_element_access %.loc10_27.1, element1
|
||||
// CHECK:STDOUT: %.loc10_25.7: init %i32 to %.loc10_25.6 = in_place_init %.loc10_25.5 [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc10_25.8: init %C to %.loc10_27.1 = class_init (%.loc10_25.4, %.loc10_25.7) [concrete = constants.%C.val]
|
||||
// CHECK:STDOUT: %.loc10_27.2: init %C = converted %.loc10_25.1, %.loc10_25.8 [concrete = constants.%C.val]
|
||||
// CHECK:STDOUT: %.loc10_31: init %C = splice_block %.loc10_27.2 [concrete = constants.%C.val] {}
|
||||
// CHECK:STDOUT: return %.loc10_31 to %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @G() -> out %return.param: %C {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %.loc13_11.1: ref %C = splice_block %return.param {}
|
||||
// CHECK:STDOUT: %F.call: init %C to %.loc13_11.1 = call %F.ref()
|
||||
// CHECK:STDOUT: return %F.call to %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- lambda.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete]
|
||||
// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete]
|
||||
// CHECK:STDOUT: %fn.type: type = fn_type @fn [concrete]
|
||||
// CHECK:STDOUT: %fn: %fn.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %Copy.impl_witness.8af: <witness> = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete]
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete]
|
||||
// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete]
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)]
|
||||
// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete]
|
||||
// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %fn.decl: %fn.type = fn_decl @fn [concrete = constants.%fn] {
|
||||
// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt]
|
||||
// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_47 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %x.param: %i32 = value_param call_param0
|
||||
// CHECK:STDOUT: %i32.loc5_25: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param
|
||||
// CHECK:STDOUT: %i32.loc5_47: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc5_47: Core.Form = init_form %i32.loc5_47 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %lambda: %fn.type = alias_binding lambda, %fn.decl [concrete = constants.%fn]
|
||||
// CHECK:STDOUT: %lambda.ref: %fn.type = name_ref lambda, %lambda [concrete = constants.%fn]
|
||||
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
|
||||
// CHECK:STDOUT: %impl.elem0: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc6_17.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc6_17.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc6_17.2(%int_1) [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc6_17.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc6_17.2: %i32 = converted %int_1, %.loc6_17.1 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %fn.call: init %i32 = call %lambda.ref(%.loc6_17.2)
|
||||
// CHECK:STDOUT: return %fn.call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @fn(%x.param: %i32) -> out %return.param: %i32 [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x
|
||||
// CHECK:STDOUT: %.loc5_48: init %i32 = splice_block %Int.as.Copy.impl.Op.call {
|
||||
// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae]
|
||||
// CHECK:STDOUT: %bound_method.loc5_47.1: <bound method> = bound_method %x.ref, %impl.elem0
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc5_47.2: <bound method> = bound_method %x.ref, %specific_fn
|
||||
// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_47.2(%x.ref)
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: return %.loc5_48
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,88 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_member.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_member.carbon
|
||||
|
||||
// --- fail_interface.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_interface.carbon:[[@LINE+4]]:10: error: interface member with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto;
|
||||
}
|
||||
|
||||
// --- fail_interface_default.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
// CHECK:STDERR: fail_interface_default.carbon:[[@LINE+4]]:18: error: interface member with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: default fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
default fn F() -> auto { return 0 as i32; }
|
||||
}
|
||||
|
||||
// --- fail_virtual.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
base class B {
|
||||
// CHECK:STDERR: fail_virtual.carbon:[[@LINE+4]]:29: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: virtual fn F(unused self) -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
virtual fn F(unused self) -> auto { return 0 as i32; }
|
||||
}
|
||||
|
||||
// --- fail_abstract.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
abstract class A {
|
||||
// CHECK:STDERR: fail_abstract.carbon:[[@LINE+4]]:23: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: abstract fn F(self) -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
abstract fn F(self) -> auto;
|
||||
}
|
||||
|
||||
// --- fail_override.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
base class B {
|
||||
virtual fn F(unused self) -> i32 { return 0; }
|
||||
}
|
||||
|
||||
class D {
|
||||
extend base: B;
|
||||
// CHECK:STDERR: fail_override.carbon:[[@LINE+4]]:30: error: virtual function with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: override fn F(unused self) -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
override fn F(unused self) -> auto { return 0 as i32; }
|
||||
}
|
||||
|
||||
// --- fail_todo_impl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I {
|
||||
fn F() -> i32;
|
||||
}
|
||||
|
||||
class C {}
|
||||
|
||||
impl C as I {
|
||||
// CHECK:STDERR: fail_todo_impl.carbon:[[@LINE+4]]:10: error: semantics TODO: `deduced return type for `impl` member` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_redecl.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_redecl.carbon
|
||||
|
||||
// --- fail_forward_decl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_forward_decl.carbon:[[@LINE+4]]:8: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto;
|
||||
|
||||
// --- fail_forward_decl_then_def.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE+4]]:8: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto;
|
||||
// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_forward_decl_then_def.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- fail_decl_then_auto_def.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> i32;
|
||||
// CHECK:STDERR: fail_decl_then_auto_def.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_decl_then_auto_def.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- fail_auto_def_then_decl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: fail_auto_def_then_decl.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_auto_def_then_decl.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> i32;
|
||||
|
||||
// --- fail_redefinition.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_redefinition.carbon:[[@LINE-4]]:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- fail_member_out_of_line.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
// CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE+4]]:10: error: forward declaration of function with deduced return type [DeducedReturnTypeNotDefinition]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto;
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE+7]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn C.F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_member_out_of_line.carbon:[[@LINE-6]]:3: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> auto;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn C.F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- fail_builtin.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_builtin.carbon:[[@LINE+8]]:26: error: builtin function with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed";
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_builtin.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "int.make_type_signed" [InvalidBuiltinSignature]
|
||||
// CHECK:STDERR: fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed";
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F(n: Core.IntLiteral) -> auto = "int.make_type_signed";
|
||||
|
||||
// --- fail_extern.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_extern.carbon:[[@LINE+4]]:15: error: `extern` function with deduced return type [DeducedReturnTypeNotAllowed]
|
||||
// CHECK:STDERR: extern fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
extern fn F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- api_def.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
|
||||
// --- fail_api_def.impl.carbon
|
||||
impl library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE+12]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE-5]]:1: in import [InImport]
|
||||
// CHECK:STDERR: api_def.carbon:3:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_api_def.impl.carbon:[[@LINE+4]]:1: error: no definition found for declaration in impl file [MissingDefinitionInImpl]
|
||||
// CHECK:STDERR: fn F() -> i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> i32;
|
||||
|
||||
// --- api_decl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> i32;
|
||||
|
||||
// --- fail_api_decl.impl.carbon
|
||||
impl library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_api_decl.impl.carbon:[[@LINE+8]]:1: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_api_decl.impl.carbon:[[@LINE-5]]:1: in import [InImport]
|
||||
// CHECK:STDERR: api_decl.carbon:3:1: note: previously declared here [RedeclPrevDecl]
|
||||
// CHECK:STDERR: fn F() -> i32;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
@@ -0,0 +1,147 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/fail_return.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/fail_return.carbon
|
||||
|
||||
// --- fail_mismatch.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
return 1 as i32;
|
||||
}
|
||||
// CHECK:STDERR: fail_mismatch.carbon:[[@LINE+7]]:10: error: deduced return type `bool` does not match return type deduced earlier [DeducedReturnTypeMismatch]
|
||||
// CHECK:STDERR: return true;
|
||||
// CHECK:STDERR: ^~~~
|
||||
// CHECK:STDERR: fail_mismatch.carbon:[[@LINE-5]]:12: note: return type previously deduced as `i32` here [DeducedReturnTypeMismatchPrevious]
|
||||
// CHECK:STDERR: return 1 as i32;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- fail_todo_mismatch_int_vs_int_literal.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
return 1 as i32;
|
||||
}
|
||||
// TODO: This should work once we use CommonType in return type deduction.
|
||||
// CHECK:STDERR: fail_todo_mismatch_int_vs_int_literal.carbon:[[@LINE+7]]:10: error: deduced return type `Core.IntLiteral` does not match return type deduced earlier [DeducedReturnTypeMismatch]
|
||||
// CHECK:STDERR: return 0;
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR: fail_todo_mismatch_int_vs_int_literal.carbon:[[@LINE-6]]:12: note: return type previously deduced as `i32` here [DeducedReturnTypeMismatchPrevious]
|
||||
// CHECK:STDERR: return 1 as i32;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- fail_error_return_ignored.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// The erroneous `return` doesn't affect the deduced return type.
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
// CHECK:STDERR: fail_error_return_ignored.carbon:[[@LINE+4]]:12: error: name `Undeclared` not found [NameNotFound]
|
||||
// CHECK:STDERR: return Undeclared;
|
||||
// CHECK:STDERR: ^~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return Undeclared;
|
||||
}
|
||||
return 1 as i32;
|
||||
}
|
||||
|
||||
fn G() -> i32 {
|
||||
return F(true);
|
||||
}
|
||||
|
||||
// --- fail_return_no_value.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
// CHECK:STDERR: fail_return_no_value.carbon:[[@LINE+7]]:5: error: missing return value [ReturnStatementMissingExpr]
|
||||
// CHECK:STDERR: return;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR: fail_return_no_value.carbon:[[@LINE-5]]:1: note: return type of function is deduced [ReturnTypeIsDeducedNote]
|
||||
// CHECK:STDERR: fn F(b: bool) -> auto {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return;
|
||||
}
|
||||
return 1 as i32;
|
||||
}
|
||||
|
||||
// --- fail_return_no_value_only.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto {
|
||||
// CHECK:STDERR: fail_return_no_value_only.carbon:[[@LINE+7]]:3: error: missing return value [ReturnStatementMissingExpr]
|
||||
// CHECK:STDERR: return;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR: fail_return_no_value_only.carbon:[[@LINE-4]]:1: note: return type of function is deduced [ReturnTypeIsDeducedNote]
|
||||
// CHECK:STDERR: fn F() -> auto {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return;
|
||||
}
|
||||
|
||||
// --- fail_reachable_end.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
return 1 as i32;
|
||||
}
|
||||
// CHECK:STDERR: fail_reachable_end.carbon:[[@LINE+4]]:1: error: missing `return` at end of function with declared return type [MissingReturnStatement]
|
||||
// CHECK:STDERR: }
|
||||
// CHECK:STDERR: ^
|
||||
// CHECK:STDERR:
|
||||
}
|
||||
|
||||
// --- fail_no_return.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_no_return.carbon:[[@LINE+4]]:1: error: no `return` in function with deduced return type [DeducedReturnTypeWithoutReturn]
|
||||
// CHECK:STDERR: fn F() -> auto {}
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto {}
|
||||
|
||||
// --- fail_return_var_no_returned_var.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto {
|
||||
// CHECK:STDERR: fail_return_var_no_returned_var.carbon:[[@LINE+4]]:3: error: `return var;` with no `returned var` in scope [ReturnVarWithNoReturnedVar]
|
||||
// CHECK:STDERR: return var;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return var;
|
||||
}
|
||||
|
||||
// --- fail_incomplete.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C;
|
||||
|
||||
fn Make() -> C*;
|
||||
|
||||
fn F() -> auto {
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+7]]:10: error: function returns incomplete type `C` [IncompleteTypeInFunctionReturnType]
|
||||
// CHECK:STDERR: return *Make();
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-8]]:1: note: class was forward declared here [ClassForwardDeclaredHere]
|
||||
// CHECK:STDERR: class C;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return *Make();
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/generic.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/generic.carbon
|
||||
|
||||
// --- generic_definition.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F[T: Core.Copy](x: T) -> auto { return x; }
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- fail_todo_call_generic.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F[T: Core.Copy](x: T) -> auto { return x; }
|
||||
|
||||
// CHECK:STDERR: fail_todo_call_generic.carbon:[[@LINE+4]]:24: error: semantics TODO: `call to generic function with deduced return type` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn G() -> i32 { return F(0 as i32); }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn G() -> i32 { return F(0 as i32); }
|
||||
|
||||
// CHECK:STDOUT: --- generic_definition.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self]
|
||||
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.e81: type = pattern_type %Copy.type [concrete]
|
||||
// CHECK:STDOUT: %T.patt.fe6: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T.f84: %Copy.type = symbolic_binding T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T.as_type: type = facet_access_type %T.f84 [symbolic]
|
||||
// CHECK:STDOUT: %pattern_type.2c66b4.2: type = pattern_type %T.as_type [symbolic]
|
||||
// CHECK:STDOUT: %x.param_patt: %pattern_type.2c66b4.2 = value_param_pattern [symbolic]
|
||||
// CHECK:STDOUT: %x.patt: %pattern_type.2c66b4.2 = wrapper_binding_pattern x, %x.param_patt [symbolic]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %require_complete.d83: <witness> = require_complete_type %T.as_type [symbolic]
|
||||
// CHECK:STDOUT: %.1ce700.2: Core.Form = init_form %T.as_type [symbolic]
|
||||
// CHECK:STDOUT: %return.param_patt.bf4475.2: %pattern_type.2c66b4.2 = out_param_pattern [symbolic]
|
||||
// CHECK:STDOUT: %return.patt.f44: %pattern_type.2c66b4.2 = return_slot_pattern %return.param_patt.bf4475.2, %T.as_type [symbolic]
|
||||
// CHECK:STDOUT: %Copy.lookup_impl_witness.d9c: <witness> = lookup_impl_witness %T.f84, @Copy [symbolic]
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.type.d2f9c3.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.f84) [symbolic]
|
||||
// CHECK:STDOUT: %.8bf: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d2f9c3.2, %T.f84 [symbolic]
|
||||
// CHECK:STDOUT: %impl.elem0.bb5: %.8bf = impl_witness_access %Copy.lookup_impl_witness.d9c, element0 [symbolic]
|
||||
// CHECK:STDOUT: %specific_impl_fn.2ac: <specific function> = specific_impl_function %impl.elem0.bb5, @Copy.WithSelf.Op(%T.f84) [symbolic]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
|
||||
// CHECK:STDOUT: .Copy = %Core.Copy
|
||||
// CHECK:STDOUT: import Core//prelude
|
||||
// CHECK:STDOUT: import Core//prelude/...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.Copy: type = import_ref Core//prelude/parts/copy, Copy, loaded [concrete = constants.%Copy.type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %T.patt.loc4_7.1: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_7.2 (constants.%T.patt.fe6)]
|
||||
// CHECK:STDOUT: %x.param_patt.loc4_21.1: @F.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %x.param_patt.loc4_21.2 (constants.%x.param_patt)]
|
||||
// CHECK:STDOUT: %x.patt.loc4_21.1: @F.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern x, %x.param_patt.loc4_21.1 [symbolic = %x.patt.loc4_21.2 (constants.%x.patt)]
|
||||
// CHECK:STDOUT: %return.param_patt.loc4_43.1: @F.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc4_43.2 (constants.%return.param_patt.bf4475.2)]
|
||||
// CHECK:STDOUT: %return.patt.loc4_43.1: @F.%pattern_type (%pattern_type.2c66b4.2) = return_slot_pattern %return.param_patt.loc4_43.1, %.loc4_43.6 [symbolic = %return.patt.loc4_43.2 (constants.%return.patt.f44)]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %.loc4_13: type = splice_block %Copy.ref [concrete = constants.%Copy.type] {
|
||||
// CHECK:STDOUT: %.Self.frozen: type = symbolic_binding .Self [symbolic_self = constants.%.Self.frozen]
|
||||
// CHECK:STDOUT: %Core.ref: <namespace> = name_ref Core, imports.%Core [concrete = imports.%Core]
|
||||
// CHECK:STDOUT: %Copy.ref: type = name_ref Copy, imports.%Core.Copy [concrete = constants.%Copy.type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %T.loc4_7.2: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_7.1 (constants.%T.f84)]
|
||||
// CHECK:STDOUT: %x.param: @F.%T.as_type.loc4_23.1 (%T.as_type) = value_param call_param0
|
||||
// CHECK:STDOUT: %.loc4_23.1: type = splice_block %.loc4_23.2 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)] {
|
||||
// CHECK:STDOUT: %T.ref: %Copy.type = name_ref T, %T.loc4_7.2 [symbolic = %T.loc4_7.1 (constants.%T.f84)]
|
||||
// CHECK:STDOUT: %T.as_type.loc4_23.2: type = facet_access_type %T.ref [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)]
|
||||
// CHECK:STDOUT: %.loc4_23.2: type = converted %T.ref, %T.as_type.loc4_23.2 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x: @F.%T.as_type.loc4_23.1 (%T.as_type) = wrapper_binding x, %x.param
|
||||
// CHECK:STDOUT: %.loc4_43.6: type = type_literal constants.%T.as_type [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)]
|
||||
// CHECK:STDOUT: %.loc4_43.7: Core.Form = init_form %.loc4_43.6 [symbolic = %.loc4_43.4 (constants.%.1ce700.2)]
|
||||
// CHECK:STDOUT: %return.param: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = out_param call_param1
|
||||
// CHECK:STDOUT: %return: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generic fn @F(%T.loc4_7.2: %Copy.type) {
|
||||
// CHECK:STDOUT: %T.patt.loc4_7.2: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_7.2 (constants.%T.patt.fe6)]
|
||||
// CHECK:STDOUT: %T.loc4_7.1: %Copy.type = symbolic_binding T, 0 [symbolic = %T.loc4_7.1 (constants.%T.f84)]
|
||||
// CHECK:STDOUT: %T.as_type.loc4_23.1: type = facet_access_type %T.loc4_7.1 [symbolic = %T.as_type.loc4_23.1 (constants.%T.as_type)]
|
||||
// CHECK:STDOUT: %pattern_type: type = pattern_type %T.as_type.loc4_23.1 [symbolic = %pattern_type (constants.%pattern_type.2c66b4.2)]
|
||||
// CHECK:STDOUT: %x.param_patt.loc4_21.2: @F.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %x.param_patt.loc4_21.2 (constants.%x.param_patt)]
|
||||
// CHECK:STDOUT: %x.patt.loc4_21.2: @F.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern x, %x.param_patt.loc4_21.2 [symbolic = %x.patt.loc4_21.2 (constants.%x.patt)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !definition:
|
||||
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type %T.as_type.loc4_23.1 [symbolic = %require_complete (constants.%require_complete.d83)]
|
||||
// CHECK:STDOUT: %.loc4_43.4: Core.Form = init_form %T.as_type.loc4_23.1 [symbolic = %.loc4_43.4 (constants.%.1ce700.2)]
|
||||
// CHECK:STDOUT: %return.param_patt.loc4_43.2: @F.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc4_43.2 (constants.%return.param_patt.bf4475.2)]
|
||||
// CHECK:STDOUT: %return.patt.loc4_43.2: @F.%pattern_type (%pattern_type.2c66b4.2) = return_slot_pattern %return.param_patt.loc4_43.2, %T.as_type.loc4_23.1 [symbolic = %return.patt.loc4_43.2 (constants.%return.patt.f44)]
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.type: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.loc4_7.1) [symbolic = %Copy.WithSelf.Op.type (constants.%Copy.WithSelf.Op.type.d2f9c3.2)]
|
||||
// CHECK:STDOUT: %.loc4_43.5: type = fn_type_with_self_type %Copy.WithSelf.Op.type, %T.loc4_7.1 [symbolic = %.loc4_43.5 (constants.%.8bf)]
|
||||
// CHECK:STDOUT: %Copy.lookup_impl_witness: <witness> = lookup_impl_witness %T.loc4_7.1, @Copy [symbolic = %Copy.lookup_impl_witness (constants.%Copy.lookup_impl_witness.d9c)]
|
||||
// CHECK:STDOUT: %impl.elem0.loc4_43.2: @F.%.loc4_43.5 (%.8bf) = impl_witness_access %Copy.lookup_impl_witness, element0 [symbolic = %impl.elem0.loc4_43.2 (constants.%impl.elem0.bb5)]
|
||||
// CHECK:STDOUT: %specific_impl_fn.loc4_43.2: <specific function> = specific_impl_function %impl.elem0.loc4_43.2, @Copy.WithSelf.Op(%T.loc4_7.1) [symbolic = %specific_impl_fn.loc4_43.2 (constants.%specific_impl_fn.2ac)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn(%x.param: @F.%T.as_type.loc4_23.1 (%T.as_type)) -> out %return.param: @F.%T.as_type.loc4_23.1 (%T.as_type) [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %x.ref: @F.%T.as_type.loc4_23.1 (%T.as_type) = name_ref x, %x
|
||||
// CHECK:STDOUT: %.loc4_44: init @F.%T.as_type.loc4_23.1 (%T.as_type) = splice_block %Copy.WithSelf.Op.call {
|
||||
// CHECK:STDOUT: %impl.elem0.loc4_43.1: @F.%.loc4_43.5 (%.8bf) = impl_witness_access constants.%Copy.lookup_impl_witness.d9c, element0 [symbolic = %impl.elem0.loc4_43.2 (constants.%impl.elem0.bb5)]
|
||||
// CHECK:STDOUT: %bound_method.loc4_43.1: <bound method> = bound_method %x.ref, %impl.elem0.loc4_43.1
|
||||
// CHECK:STDOUT: %.loc4_43.1: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc4_7.1 (constants.%T.f84)]
|
||||
// CHECK:STDOUT: %.loc4_43.2: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc4_7.1 (constants.%T.f84)]
|
||||
// CHECK:STDOUT: %specific_impl_fn.loc4_43.1: <specific function> = specific_impl_function %impl.elem0.loc4_43.1, @Copy.WithSelf.Op(constants.%T.f84) [symbolic = %specific_impl_fn.loc4_43.2 (constants.%specific_impl_fn.2ac)]
|
||||
// CHECK:STDOUT: %bound_method.loc4_43.2: <bound method> = bound_method %x.ref, %specific_impl_fn.loc4_43.1
|
||||
// CHECK:STDOUT: %.loc4_43.3: ref @F.%T.as_type.loc4_23.1 (%T.as_type) = splice_block %return.param {}
|
||||
// CHECK:STDOUT: %Copy.WithSelf.Op.call: init @F.%T.as_type.loc4_23.1 (%T.as_type) to %.loc4_43.3 = call %bound_method.loc4_43.2(%x.ref)
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: return %.loc4_44 to %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: specific @F(constants.%T.f84) {
|
||||
// CHECK:STDOUT: %T.patt.loc4_7.2 => constants.%T.patt.fe6
|
||||
// CHECK:STDOUT: %T.loc4_7.1 => constants.%T.f84
|
||||
// CHECK:STDOUT: %T.as_type.loc4_23.1 => constants.%T.as_type
|
||||
// CHECK:STDOUT: %pattern_type => constants.%pattern_type.2c66b4.2
|
||||
// CHECK:STDOUT: %x.param_patt.loc4_21.2 => constants.%x.param_patt
|
||||
// CHECK:STDOUT: %x.patt.loc4_21.2 => constants.%x.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/import.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/import.carbon
|
||||
|
||||
// --- lib.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
|
||||
class C {
|
||||
fn G() -> auto { return 1 as i32; }
|
||||
}
|
||||
|
||||
// --- lib.impl.carbon
|
||||
impl library "[[@TEST_NAME]]";
|
||||
|
||||
fn H() -> i32 { return F(); }
|
||||
|
||||
// --- use.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import library "lib";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn I() -> i32 { return F(); }
|
||||
fn J() -> i32 { return C.G(); }
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// CHECK:STDOUT: --- use.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete]
|
||||
// CHECK:STDOUT: %I.type: type = fn_type @I [concrete]
|
||||
// CHECK:STDOUT: %I: %I.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %J.type: type = fn_type @J [concrete]
|
||||
// CHECK:STDOUT: %J: %J.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %C.G.type: type = fn_type @C.G [concrete]
|
||||
// CHECK:STDOUT: %C.G: %C.G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Main.F: %F.type = import_ref Main//lib, F, loaded [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %Main.C: type = import_ref Main//lib, C, loaded [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %Main.import_ref.341: %C.G.type = import_ref Main//lib, loc6_18, loaded [concrete = constants.%C.G]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %I.decl: %I.type = fn_decl @I [concrete = constants.%I] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc6: Core.Form = init_form %i32 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %J.decl: %J.type = fn_decl @J [concrete = constants.%J] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc7: Core.Form = init_form %i32 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @I() -> out %return.param: %i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F]
|
||||
// CHECK:STDOUT: %F.call: init %i32 = call %F.ref()
|
||||
// CHECK:STDOUT: return %F.call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @J() -> out %return.param: %i32 {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, imports.%Main.C [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %G.ref: %C.G.type = name_ref G, imports.%Main.import_ref.341 [concrete = constants.%C.G]
|
||||
// CHECK:STDOUT: %C.G.call: init %i32 = call %G.ref()
|
||||
// CHECK:STDOUT: return %C.G.call
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,450 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/returned_var.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/returned_var.carbon
|
||||
|
||||
// --- returned_var.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F() -> auto {
|
||||
returned var r: i32 = 0;
|
||||
return var;
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() -> i32 {
|
||||
return F();
|
||||
}
|
||||
|
||||
// --- returned_var_after_return.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
// This is converted to the type of the `returned var`.
|
||||
return 1;
|
||||
}
|
||||
returned var r: i32 = 0;
|
||||
return var;
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
fn G() -> i32 {
|
||||
return F(true);
|
||||
}
|
||||
|
||||
// --- return_after_returned_var.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
returned var r: i32 = 0;
|
||||
return var;
|
||||
}
|
||||
// This is converted to the type of the `returned var`.
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn G() -> i32 {
|
||||
return F(true);
|
||||
}
|
||||
|
||||
// --- returned_var_in_place.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F() -> auto {
|
||||
// The `returned var` is initialized in the return slot.
|
||||
returned var c: C = {.a = 1, .b = 2};
|
||||
return var;
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// --- fail_return_before_returned_var.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
// CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+6]]:5: error: cannot implicitly convert expression of type `bool` to `i32` [ConversionFailure]
|
||||
// CHECK:STDERR: return true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+3]]:5: note: type `bool` does not implement interface `Core.ImplicitAs(i32)` [MissingImplInMemberAccessInContext]
|
||||
// CHECK:STDERR: return true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~
|
||||
return true;
|
||||
}
|
||||
// CHECK:STDERR: fail_return_before_returned_var.carbon:[[@LINE+4]]:19: note: return type deduced as `i32` [ReturnTypeDeducedAs]
|
||||
// CHECK:STDERR: returned var r: i32 = 0;
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
returned var r: i32 = 0;
|
||||
return var;
|
||||
}
|
||||
|
||||
// --- fail_returned_var_mismatch.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
returned var r: i32 = 0;
|
||||
return var;
|
||||
}
|
||||
// CHECK:STDERR: fail_returned_var_mismatch.carbon:[[@LINE+7]]:19: error: type `bool` of `returned var` does not match return type of enclosing function [ReturnedVarWrongType]
|
||||
// CHECK:STDERR: returned var s: bool = true;
|
||||
// CHECK:STDERR: ^~~~
|
||||
// CHECK:STDERR: fail_returned_var_mismatch.carbon:[[@LINE-6]]:21: note: return type of function is `i32` [ReturnTypeHereNote]
|
||||
// CHECK:STDERR: returned var r: i32 = 0;
|
||||
// CHECK:STDERR: ^~~
|
||||
// CHECK:STDERR:
|
||||
returned var s: bool = true;
|
||||
return var;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- returned_var.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete]
|
||||
// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete]
|
||||
// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete]
|
||||
// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %self.param_patt.0c5: %pattern_type.d94 = ref_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %self.patt.749: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt.0c5 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc5 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc5 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc5 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generated {
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc5 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
|
||||
// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5]
|
||||
// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc5 [concrete = constants.%Destroy.WithSelf.Op.403] {
|
||||
// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5]
|
||||
// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_19.2 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %i32.loc5_19.2: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc5_19: Core.Form = init_form %i32.loc5_19.2 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %i32 [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %r.var: ref %i32 = var_storage %r.var_patt
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %impl.elem0: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.1: <bound method> = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
|
||||
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc5_12.2: <bound method> = bound_method %int_0, %specific_fn [concrete = constants.%bound_method]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc5_12.2(%int_0) [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: %.loc5_12: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: assign %r.var, %.loc5_12
|
||||
// CHECK:STDOUT: %i32.loc5_19.1: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %r: ref %i32 = wrapper_binding r, %r.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete = constants.%r.patt]
|
||||
// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete = constants.%r.var_patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc5_17: %i32 = acquire_value %r
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %r.var, constants.%Destroy.WithSelf.SelfDestruct.db3
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%r.var)
|
||||
// CHECK:STDOUT: return %.loc5_17
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc5(%self.param: ref %i32) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc5(%self.param: ref %i32) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc5(%self.param: ref %i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- returned_var_after_return.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete]
|
||||
// CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %b.patt: %pattern_type.831 = wrapper_binding_pattern b, %b.param_patt [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete]
|
||||
// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete]
|
||||
// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.3bd: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete]
|
||||
// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %bound_method.036: <bound method> = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_0.753: %i32 = int_value 0 [concrete]
|
||||
// CHECK:STDOUT: %self.param_patt.0c5: %pattern_type.d94 = ref_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %self.patt.749: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt.0c5 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfc: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc9 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01: %Destroy.WithSelf.SubobjectDestroy.type.dfc = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef0: type = fn_type @Destroy.WithSelf.Op.loc9 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.403: %Destroy.WithSelf.Op.type.ef0 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbc: type = fn_type @Destroy.WithSelf.SelfDestruct.loc9 [concrete]
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3: %Destroy.WithSelf.SelfDestruct.type.fbc = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generated {
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl: %Destroy.WithSelf.SubobjectDestroy.type.dfc = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc9 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01] {
|
||||
// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5]
|
||||
// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.decl: %Destroy.WithSelf.Op.type.ef0 = fn_decl @Destroy.WithSelf.Op.loc9 [concrete = constants.%Destroy.WithSelf.Op.403] {
|
||||
// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5]
|
||||
// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0
|
||||
// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %b.param_patt: %pattern_type.831 = value_param_pattern [concrete = constants.%b.param_patt]
|
||||
// CHECK:STDOUT: %b.patt: %pattern_type.831 = wrapper_binding_pattern b, %b.param_patt [concrete = constants.%b.patt]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc9_19.2 [concrete = constants.%return.patt.6fa]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %b.param: bool = value_param call_param0
|
||||
// CHECK:STDOUT: %.loc4: type = type_literal bool [concrete = bool]
|
||||
// CHECK:STDOUT: %b: bool = wrapper_binding b, %b.param
|
||||
// CHECK:STDOUT: %i32.loc9_19.2: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %.loc9_19: Core.Form = init_form %i32.loc9_19.2 [concrete = constants.%.0b3]
|
||||
// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1
|
||||
// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%b.param: bool) -> out %return.param: %i32 [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %b.ref: bool = name_ref b, %b
|
||||
// CHECK:STDOUT: if %b.ref br !if.then else br !if.else
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.then:
|
||||
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
|
||||
// CHECK:STDOUT: %.loc7_13.1: init %i32 = splice_block %.loc7_13.2 [concrete = constants.%int_1.68a] {
|
||||
// CHECK:STDOUT: %impl.elem0.loc7: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc7_13.1: <bound method> = bound_method %int_1, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282]
|
||||
// CHECK:STDOUT: %specific_fn.loc7: <specific function> = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc7_13.2: <bound method> = bound_method %int_1, %specific_fn.loc7 [concrete = constants.%bound_method.3bd]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_13.2(%int_1) [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc7_13.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: return %.loc7_13.1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else:
|
||||
// CHECK:STDOUT: %r.var: ref %i32 = var_storage %r.var_patt
|
||||
// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6]
|
||||
// CHECK:STDOUT: %impl.elem0.loc9: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc9_12.1: <bound method> = bound_method %int_0, %impl.elem0.loc9 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.762]
|
||||
// CHECK:STDOUT: %specific_fn.loc9: <specific function> = specific_function %impl.elem0.loc9, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc9_12.2: <bound method> = bound_method %int_0, %specific_fn.loc9 [concrete = constants.%bound_method.036]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9: init %i32 = call %bound_method.loc9_12.2(%int_0) [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: %.loc9_12: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc9 [concrete = constants.%int_0.753]
|
||||
// CHECK:STDOUT: assign %r.var, %.loc9_12
|
||||
// CHECK:STDOUT: %i32.loc9_19.1: type = type_literal constants.%i32 [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: %r: ref %i32 = wrapper_binding r, %r.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %r.patt: %pattern_type.d94 = ref_binding_pattern r [concrete = constants.%r.patt]
|
||||
// CHECK:STDOUT: %r.var_patt: %pattern_type.d94 = var_pattern %r.patt [concrete = constants.%r.var_patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc9_17: %i32 = acquire_value %r
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: <bound method> = bound_method %r.var, constants.%Destroy.WithSelf.SelfDestruct.db3
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%r.var)
|
||||
// CHECK:STDOUT: return %.loc9_17
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc9(%self.param: ref %i32) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc9(%self.param: ref %i32) = "no_op";
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc9(%self.param: ref %i32) {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl(%self.param)
|
||||
// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl(%self.param)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- returned_var_in_place.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: type: type = facet_type <type> [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %pattern_type.98b: type = pattern_type %C [concrete]
|
||||
// CHECK:STDOUT: %c.patt: %pattern_type.98b = ref_binding_pattern c [concrete]
|
||||
// CHECK:STDOUT: %.a44: Core.Form = init_form %C [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt.89a: %pattern_type.98b = out_param_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.patt.e4a: %pattern_type.98b = return_slot_pattern %return.param_patt.89a, %C [concrete]
|
||||
// CHECK:STDOUT: %c.var_patt: %pattern_type.98b = var_pattern %c.patt [concrete]
|
||||
// CHECK:STDOUT: %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %int_2.ecc: Core.IntLiteral = int_value 2 [concrete]
|
||||
// CHECK:STDOUT: %struct_type.a.b.cfd: type = struct_type {.a: Core.IntLiteral, .b: Core.IntLiteral} [concrete]
|
||||
// CHECK:STDOUT: %struct: %struct_type.a.b.cfd = struct_value (%int_1.5b8, %int_2.ecc) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete]
|
||||
// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete]
|
||||
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete]
|
||||
// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %bound_method.3bd: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_1.68a: %i32 = int_value 1 [concrete]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete]
|
||||
// CHECK:STDOUT: %bound_method.a55: <bound method> = bound_method %int_2.ecc, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
|
||||
// CHECK:STDOUT: %int_2.2e0: %i32 = int_value 2 [concrete]
|
||||
// CHECK:STDOUT: %C.val: %C = struct_value (%int_1.68a, %int_2.2e0) [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)]
|
||||
// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.98b = out_param_pattern [concrete = constants.%return.param_patt.89a]
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.98b = return_slot_pattern %return.param_patt, %.loc11_19.1 [concrete = constants.%return.patt.e4a]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %.loc11_19.1: type = type_literal constants.%C [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %.loc11_19.2: Core.Form = init_form %.loc11_19.1 [concrete = constants.%.a44]
|
||||
// CHECK:STDOUT: %return.param: ref %C = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %C = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() -> out %return.param: %C [deduced] {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
|
||||
// CHECK:STDOUT: %int_2: Core.IntLiteral = int_value 2 [concrete = constants.%int_2.ecc]
|
||||
// CHECK:STDOUT: %.loc11_38.1: %struct_type.a.b.cfd = struct_literal (%int_1, %int_2) [concrete = constants.%struct]
|
||||
// CHECK:STDOUT: %impl.elem0.loc11_38.1: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc11_38.1: <bound method> = bound_method %int_1, %impl.elem0.loc11_38.1 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.282]
|
||||
// CHECK:STDOUT: %specific_fn.loc11_38.1: <specific function> = specific_function %impl.elem0.loc11_38.1, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc11_38.2: <bound method> = bound_method %int_1, %specific_fn.loc11_38.1 [concrete = constants.%bound_method.3bd]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.1: init %i32 = call %bound_method.loc11_38.2(%int_1) [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc11_38.2: init %i32 = converted %int_1, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.1 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %.loc11_38.3: ref %i32 = class_element_access %return.param, element0
|
||||
// CHECK:STDOUT: %.loc11_38.4: init %i32 to %.loc11_38.3 = in_place_init %.loc11_38.2 [concrete = constants.%int_1.68a]
|
||||
// CHECK:STDOUT: %impl.elem0.loc11_38.2: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60]
|
||||
// CHECK:STDOUT: %bound_method.loc11_38.3: <bound method> = bound_method %int_2, %impl.elem0.loc11_38.2 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.8a6]
|
||||
// CHECK:STDOUT: %specific_fn.loc11_38.2: <specific function> = specific_function %impl.elem0.loc11_38.2, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
|
||||
// CHECK:STDOUT: %bound_method.loc11_38.4: <bound method> = bound_method %int_2, %specific_fn.loc11_38.2 [concrete = constants.%bound_method.a55]
|
||||
// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.2: init %i32 = call %bound_method.loc11_38.4(%int_2) [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc11_38.5: init %i32 = converted %int_2, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11_38.2 [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc11_38.6: ref %i32 = class_element_access %return.param, element1
|
||||
// CHECK:STDOUT: %.loc11_38.7: init %i32 to %.loc11_38.6 = in_place_init %.loc11_38.5 [concrete = constants.%int_2.2e0]
|
||||
// CHECK:STDOUT: %.loc11_38.8: init %C to %return.param = class_init (%.loc11_38.4, %.loc11_38.7) [concrete = constants.%C.val]
|
||||
// CHECK:STDOUT: %.loc11_12: init %C = converted %.loc11_38.1, %.loc11_38.8 [concrete = constants.%C.val]
|
||||
// CHECK:STDOUT: assign %return.param, %.loc11_12
|
||||
// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
|
||||
// CHECK:STDOUT: %c: ref %C = wrapper_binding c, %return.param
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %c.patt: %pattern_type.98b = ref_binding_pattern c [concrete = constants.%c.patt]
|
||||
// CHECK:STDOUT: %c.var_patt: %pattern_type.98b = var_pattern %c.patt [concrete = constants.%c.var_patt]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc11_17: %C = acquire_value %c
|
||||
// CHECK:STDOUT: return %.loc11_17 to %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/deduced_return/use_before_deduced.carbon
|
||||
|
||||
// --- fail_recursion.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
if (b) {
|
||||
// CHECK:STDERR: fail_recursion.carbon:[[@LINE+7]]:12: error: call to `F` before its return type is deduced [CallBeforeReturnTypeDeduced]
|
||||
// CHECK:STDERR: return F(false);
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR: fail_recursion.carbon:[[@LINE-5]]:1: note: return type of `F` is deduced from its definition [ReturnTypeDeducedFromDefinition]
|
||||
// CHECK:STDERR: fn F(b: bool) -> auto {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
return F(false);
|
||||
}
|
||||
return 0 as i32;
|
||||
}
|
||||
|
||||
// --- fail_member_order.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
// The body of `F` is checked after the body of `G`.
|
||||
// CHECK:STDERR: fail_member_order.carbon:[[@LINE+3]]:26: error: call to `F` before its return type is deduced [CallBeforeReturnTypeDeduced]
|
||||
// CHECK:STDERR: fn G() -> i32 { return F(); }
|
||||
// CHECK:STDERR: ^~~
|
||||
fn G() -> i32 { return F(); }
|
||||
// CHECK:STDERR: fail_member_order.carbon:[[@LINE+4]]:3: note: return type of `F` is deduced from its definition [ReturnTypeDeducedFromDefinition]
|
||||
// CHECK:STDERR: fn F() -> auto { return 0 as i32; }
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> auto { return 0 as i32; }
|
||||
}
|
||||
|
||||
// --- recursion_after_returned_var.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
fn F(b: bool) -> auto {
|
||||
returned var r: i32 = 0;
|
||||
if (b) {
|
||||
let unused n: i32 = F(false);
|
||||
}
|
||||
return var;
|
||||
}
|
||||
@@ -169,23 +169,30 @@ final impl forall [V: Core.Copy] V* as I where .T = V {
|
||||
fn F(self) -> V { return *self; }
|
||||
}
|
||||
|
||||
// --- fail_todo_poisoned_concrete_lookup_from_eval_fn.carbon
|
||||
// --- fail_poisoned_concrete_lookup_from_eval_fn.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
fn F() {
|
||||
class D {}
|
||||
|
||||
// CHECK:STDERR: fail_todo_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+4]]:22: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo]
|
||||
// CHECK:STDERR: musteval fn G() -> auto {
|
||||
// CHECK:STDERR: ^~~~
|
||||
// CHECK:STDERR:
|
||||
musteval fn G() -> auto {
|
||||
class C(T: type) {}
|
||||
impl forall [T: type] C(T) as Z {}
|
||||
// CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+3]]:5: error: found `impl` that would change the result of an earlier use of `C(D) as Z` [PoisonedImplLookupConcreteResult]
|
||||
// CHECK:STDERR: C(D) as Z;
|
||||
// CHECK:STDERR: ^~~~~~~~~
|
||||
C(D) as Z;
|
||||
return C(D);
|
||||
}
|
||||
|
||||
// TODO: error: found `impl` that would change the result of an earlier use of `C(D) as Z`.
|
||||
// CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE+7]]:3: note: the use would select the `impl` here but it was not found yet [PoisonedImplLookupConcreteResultNoteBadImpl]
|
||||
// CHECK:STDERR: impl G() as Z {}
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_poisoned_concrete_lookup_from_eval_fn.carbon:[[@LINE-11]]:5: note: the use had selected the `impl` here [PoisonedImplLookupConcreteResultNotePreviousImpl]
|
||||
// CHECK:STDERR: impl forall [T: type] C(T) as Z {}
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
impl G() as Z {}
|
||||
}
|
||||
+10
-8
@@ -2,7 +2,7 @@
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon
|
||||
// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
@@ -176,23 +176,25 @@ class C {
|
||||
// CHECK:STDERR:
|
||||
impl C as Z {}
|
||||
|
||||
// --- fail_todo_redeclaration_in_block_scope_is_separate_impl.carbon
|
||||
// --- fail_redeclaration_in_block_scope_is_separate_impl.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface Z {}
|
||||
|
||||
fn F() {
|
||||
// CHECK:STDERR: fail_todo_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE+4]]:22: error: semantics TODO: `HandleAutoTypeLiteral` [SemanticsTodo]
|
||||
// CHECK:STDERR: musteval fn G() -> auto {
|
||||
// CHECK:STDERR: ^~~~
|
||||
// CHECK:STDERR:
|
||||
musteval fn G() -> auto {
|
||||
class C {}
|
||||
impl C as Z {}
|
||||
return C;
|
||||
}
|
||||
// TODO: This is not a redeclaration, since it's in a different scope.
|
||||
// TODO: found non-final `impl` with the same type structure as another non-final `impl`.
|
||||
// This is not a redeclaration, since it's in a different scope.
|
||||
// CHECK:STDERR: fail_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE+7]]:3: error: found non-final `impl` with the same type structure as another non-final `impl` [ImplNonFinalSameTypeStructure]
|
||||
// CHECK:STDERR: impl G() as Z {}
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_redeclaration_in_block_scope_is_separate_impl.carbon:[[@LINE-7]]:5: note: other `impl` here [ImplNonFinalSameTypeStructureNote]
|
||||
// CHECK:STDERR: impl C as Z {}
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
impl G() as Z {}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/main_run/fail_mismatch_return_auto.carbon
|
||||
|
||||
// CHECK:STDERR: fail_mismatch_return_auto.carbon:[[@LINE+4]]:1: error: invalid return type for `Main.Run` function; expected `fn (...)` or `fn (...) -> i32` [InvalidMainRunReturnType]
|
||||
// CHECK:STDERR: fn Run() -> auto {
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn Run() -> auto {
|
||||
return 0.0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/main_run/return_auto.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/main_run/return_auto.carbon
|
||||
|
||||
fn Run() -> auto {
|
||||
return 0 as i32;
|
||||
}
|
||||
@@ -285,6 +285,7 @@ CARBON_DIAGNOSTIC_KIND(NamespaceDeclNotAtTopLevel)
|
||||
|
||||
// Function call checking.
|
||||
CARBON_DIAGNOSTIC_KIND(CallArgCountMismatch)
|
||||
CARBON_DIAGNOSTIC_KIND(CallBeforeReturnTypeDeduced)
|
||||
CARBON_DIAGNOSTIC_KIND(CallToNonCallable)
|
||||
CARBON_DIAGNOSTIC_KIND(CppCallArgTypeNotSupported)
|
||||
CARBON_DIAGNOSTIC_KIND(CppCallFieldNameNotSupported)
|
||||
@@ -296,10 +297,14 @@ CARBON_DIAGNOSTIC_KIND(InCallToEntity)
|
||||
CARBON_DIAGNOSTIC_KIND(InCallToFunctionParam)
|
||||
CARBON_DIAGNOSTIC_KIND(RefParamNoRefTag)
|
||||
CARBON_DIAGNOSTIC_KIND(RefTagNoRefParam)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnTypeDeducedFromDefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(SelfParameterNotAllowed)
|
||||
CARBON_DIAGNOSTIC_KIND(ValueForRefParam)
|
||||
|
||||
// Function declaration checking.
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeNotAllowed)
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeNotDefinition)
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeRedecl)
|
||||
CARBON_DIAGNOSTIC_KIND(DefinedAbstractFunction)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffers)
|
||||
CARBON_DIAGNOSTIC_KIND(FunctionRedeclReturnTypeDiffersNoReturn)
|
||||
@@ -560,6 +565,11 @@ CARBON_DIAGNOSTIC_KIND(ReturnStatementDisallowExpr)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnTypeHereNote)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnTypeOmittedNote)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnStatementMissingExpr)
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeMismatch)
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeMismatchPrevious)
|
||||
CARBON_DIAGNOSTIC_KIND(DeducedReturnTypeWithoutReturn)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnTypeDeducedAs)
|
||||
CARBON_DIAGNOSTIC_KIND(ReturnTypeIsDeducedNote)
|
||||
CARBON_DIAGNOSTIC_KIND(ConversionFailure)
|
||||
CARBON_DIAGNOSTIC_KIND(ConversionFailureNonTypeToFacet)
|
||||
CARBON_DIAGNOSTIC_KIND(ConversionFailureFacetToFacet)
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// 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-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
|
||||
//
|
||||
// AUTOUPDATE
|
||||
// TIP: To test this file alone, run:
|
||||
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/function/definition/deduced_return.carbon
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/function/definition/deduced_return.carbon
|
||||
|
||||
class C {
|
||||
var a: i32;
|
||||
var b: i32;
|
||||
}
|
||||
|
||||
fn Copy() -> auto {
|
||||
return 1 as i32;
|
||||
}
|
||||
|
||||
fn InPlace() -> auto {
|
||||
return {.a = 1, .b = 2} as C;
|
||||
}
|
||||
|
||||
fn ReturnedVar() -> auto {
|
||||
returned var c: C = {.a = 3, .b = 4};
|
||||
return var;
|
||||
}
|
||||
|
||||
fn Use() -> i32 {
|
||||
let unused c: C = InPlace();
|
||||
let unused d: C = ReturnedVar();
|
||||
return Copy();
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: ; ---
|
||||
// CHECK:STDOUT: ; ModuleID = 'deduced_return.carbon'
|
||||
// CHECK:STDOUT: source_filename = "deduced_return.carbon"
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: @C.val.32e.loc23_31 = internal constant { i32, i32 } { i32 1, i32 2 }
|
||||
// CHECK:STDOUT: @C.val.7af.loc27_12 = internal constant { i32, i32 } { i32 3, i32 4 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define i32 @_CCopy.Main() #0 !dbg !46 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret i32 1, !dbg !48
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CInPlace.Main(ptr sret({ i32, i32 }) %return) #0 !dbg !49 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc23_25.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !50
|
||||
// CHECK:STDOUT: %.loc23_25.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !50
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @C.val.32e.loc23_31, i64 8, i1 false), !dbg !51
|
||||
// CHECK:STDOUT: ret void, !dbg !51
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define void @_CReturnedVar.Main(ptr sret({ i32, i32 }) %return) #0 !dbg !52 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc27_38.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !54
|
||||
// CHECK:STDOUT: %.loc27_38.6.b = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !54
|
||||
// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %return, ptr align 4 @C.val.7af.loc27_12, i64 8, i1 false), !dbg !55
|
||||
// CHECK:STDOUT: ret void, !dbg !58
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define i32 @_CUse.Main() #0 !dbg !59 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc32_29.1.temp = alloca { i32, i32 }, align 4, !dbg !62
|
||||
// CHECK:STDOUT: %.loc33_33.1.temp = alloca { i32, i32 }, align 4, !dbg !65
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc32_29.1.temp), !dbg !62
|
||||
// CHECK:STDOUT: call void @_CInPlace.Main(ptr %.loc32_29.1.temp), !dbg !62
|
||||
// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc33_33.1.temp), !dbg !65
|
||||
// CHECK:STDOUT: call void @_CReturnedVar.Main(ptr %.loc33_33.1.temp), !dbg !65
|
||||
// CHECK:STDOUT: %Copy.call = call i32 @_CCopy.Main(), !dbg !68
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %.loc33_33.1.temp), !dbg !65
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %.loc32_29.1.temp), !dbg !62
|
||||
// CHECK:STDOUT: ret i32 %Copy.call, !dbg !69
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %self) #0 !dbg !70 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: ret void, !dbg !72
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define weak_odr void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !74 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc33_33.1.b = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !76
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.1.b), !dbg !76
|
||||
// CHECK:STDOUT: %.loc33_33.2.a = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !76
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.2.a), !dbg !76
|
||||
// CHECK:STDOUT: ret void, !dbg !76
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !78 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: call void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self), !dbg !80
|
||||
// CHECK:STDOUT: ret void, !dbg !80
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define weak_odr void @"_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core"(ptr %self) #0 !dbg !82 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: %.loc33_33.1.b = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !84
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.1.b), !dbg !84
|
||||
// CHECK:STDOUT: %.loc33_33.2.a = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !84
|
||||
// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc33_33.2.a), !dbg !84
|
||||
// CHECK:STDOUT: ret void, !dbg !84
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nounwind
|
||||
// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core"(ptr %self) #0 !dbg !86 {
|
||||
// CHECK:STDOUT: entry:
|
||||
// CHECK:STDOUT: call void @"_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core"(ptr %self), !dbg !88
|
||||
// CHECK:STDOUT: ret void, !dbg !88
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
|
||||
// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: ; uselistorder directives
|
||||
// CHECK:STDOUT: uselistorder ptr @"_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core", { 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.memcpy.p0.p0.i64, { 1, 0 }
|
||||
// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: attributes #0 = { nounwind }
|
||||
// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !llvm.dbg.cu = !{!35}
|
||||
// CHECK:STDOUT: !llvm.module.flags = !{!2, !3}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5}
|
||||
// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3}
|
||||
// CHECK:STDOUT: !34 = !DIFile(filename: "deduced_return.carbon", directory: "")
|
||||
// CHECK:STDOUT: !35 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !34, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
|
||||
// CHECK:STDOUT: !36 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
|
||||
// CHECK:STDOUT: !37 = !{!36}
|
||||
// CHECK:STDOUT: !38 = !DISubroutineType(types: !37)
|
||||
// CHECK:STDOUT: !39 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
|
||||
// CHECK:STDOUT: !40 = !{!39}
|
||||
// CHECK:STDOUT: !41 = !DISubroutineType(types: !40)
|
||||
// CHECK:STDOUT: !42 = !{null, !36}
|
||||
// CHECK:STDOUT: !43 = !DISubroutineType(types: !42)
|
||||
// CHECK:STDOUT: !44 = !{null, !39}
|
||||
// CHECK:STDOUT: !45 = !DISubroutineType(types: !44)
|
||||
// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Copy", linkageName: "_CCopy.Main", scope: null, file: !34, line: 18, type: !38, spFlags: DISPFlagDefinition, unit: !35)
|
||||
// CHECK:STDOUT: !48 = !DILocation(line: 19, column: 3, scope: !46)
|
||||
// CHECK:STDOUT: !49 = distinct !DISubprogram(name: "InPlace", linkageName: "_CInPlace.Main", scope: null, file: !34, line: 22, type: !41, spFlags: DISPFlagDefinition, unit: !35)
|
||||
// CHECK:STDOUT: !50 = !DILocation(line: 23, column: 10, scope: !49)
|
||||
// CHECK:STDOUT: !51 = !DILocation(line: 23, column: 3, scope: !49)
|
||||
// CHECK:STDOUT: !52 = distinct !DISubprogram(name: "ReturnedVar", linkageName: "_CReturnedVar.Main", scope: null, file: !34, line: 26, type: !41, spFlags: DISPFlagDefinition, unit: !35)
|
||||
// CHECK:STDOUT: !54 = !DILocation(line: 27, column: 23, scope: !52)
|
||||
// CHECK:STDOUT: !55 = !DILocation(line: 27, column: 12, scope: !52)
|
||||
// CHECK:STDOUT: !58 = !DILocation(line: 28, column: 3, scope: !52)
|
||||
// CHECK:STDOUT: !59 = distinct !DISubprogram(name: "Use", linkageName: "_CUse.Main", scope: null, file: !34, line: 31, type: !38, spFlags: DISPFlagDefinition, unit: !35)
|
||||
// CHECK:STDOUT: !62 = !DILocation(line: 32, column: 21, scope: !59)
|
||||
// CHECK:STDOUT: !65 = !DILocation(line: 33, column: 21, scope: !59)
|
||||
// CHECK:STDOUT: !68 = !DILocation(line: 34, column: 10, scope: !59)
|
||||
// CHECK:STDOUT: !69 = !DILocation(line: 34, column: 3, scope: !59)
|
||||
// CHECK:STDOUT: !70 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core", scope: null, file: !34, line: 33, type: !43, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !73)
|
||||
// CHECK:STDOUT: !71 = !DILocalVariable(arg: 1, scope: !70, type: !36)
|
||||
// CHECK:STDOUT: !72 = !DILocation(line: 33, column: 21, scope: !70)
|
||||
// CHECK:STDOUT: !73 = !{!71}
|
||||
// CHECK:STDOUT: !74 = distinct !DISubprogram(name: "SubobjectDestroy", linkageName: "_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !77)
|
||||
// CHECK:STDOUT: !75 = !DILocalVariable(arg: 1, scope: !74, type: !39)
|
||||
// CHECK:STDOUT: !76 = !DILocation(line: 33, column: 21, scope: !74)
|
||||
// CHECK:STDOUT: !77 = !{!75}
|
||||
// CHECK:STDOUT: !78 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.2508ed710019eba8:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !81)
|
||||
// CHECK:STDOUT: !79 = !DILocalVariable(arg: 1, scope: !78, type: !39)
|
||||
// CHECK:STDOUT: !80 = !DILocation(line: 33, column: 21, scope: !78)
|
||||
// CHECK:STDOUT: !81 = !{!79}
|
||||
// CHECK:STDOUT: !82 = distinct !DISubprogram(name: "SubobjectDestroy", linkageName: "_CSubobjectDestroy.08d9e38b928ba575:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !85)
|
||||
// CHECK:STDOUT: !83 = !DILocalVariable(arg: 1, scope: !82, type: !39)
|
||||
// CHECK:STDOUT: !84 = !DILocation(line: 33, column: 21, scope: !82)
|
||||
// CHECK:STDOUT: !85 = !{!83}
|
||||
// CHECK:STDOUT: !86 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.08d9e38b928ba575:core.Destroy.Core", scope: null, file: !34, line: 33, type: !45, spFlags: DISPFlagDefinition, unit: !35, retainedNodes: !89)
|
||||
// CHECK:STDOUT: !87 = !DILocalVariable(arg: 1, scope: !86, type: !39)
|
||||
// CHECK:STDOUT: !88 = !DILocation(line: 33, column: 21, scope: !86)
|
||||
// CHECK:STDOUT: !89 = !{!87}
|
||||
// CHECK:STDOUT:
|
||||
@@ -32,7 +32,7 @@ namespace Carbon::Parse {
|
||||
/* `require <type> impls` or just `require impls` */ \
|
||||
X(RequireImpls) \
|
||||
X(Requirement) \
|
||||
X(ReturnDecl) \
|
||||
X(ReturnSpecifier) \
|
||||
X(Statement)
|
||||
|
||||
// We expect this to grow, so are using a bigger size than needed.
|
||||
|
||||
@@ -117,7 +117,7 @@ using AnyObserveOperandId =
|
||||
NodeIdInCategory<NodeCategory::Expr | NodeCategory::ObserveOperator>;
|
||||
using AnyNonExprNameId = NodeIdInCategory<NodeCategory::NonExprName>;
|
||||
using AnyPackageNameId = NodeIdInCategory<NodeCategory::PackageName>;
|
||||
using AnyReturnDeclId = NodeIdInCategory<NodeCategory::ReturnDecl>;
|
||||
using AnyReturnSpecifierId = NodeIdInCategory<NodeCategory::ReturnSpecifier>;
|
||||
|
||||
namespace Internal {
|
||||
template <typename T>
|
||||
|
||||
@@ -128,7 +128,7 @@ using EmptyDecl =
|
||||
LeafNode<NodeKind::EmptyDecl, Lex::SemiTokenIndex, NodeCategory::Decl>;
|
||||
|
||||
// A name that may be immediately followed by a signature (i.e. parameter lists
|
||||
// and/or a return declaration). There may be false positives, because we make
|
||||
// and/or a return specifier). There may be false positives, because we make
|
||||
// this determination based on the context and a single token of lookahead.
|
||||
using IdentifierNameMaybeBeforeSignature =
|
||||
LeafNode<NodeKind::IdentifierNameMaybeBeforeSignature,
|
||||
@@ -136,7 +136,7 @@ using IdentifierNameMaybeBeforeSignature =
|
||||
NodeCategory::MemberName | NodeCategory::NonExprName>;
|
||||
|
||||
// A name that is known not to be immediately followed by a signature (i.e.
|
||||
// parameter lists and/or a return declaration).
|
||||
// parameter lists and/or a return specifier).
|
||||
using IdentifierNameNotBeforeSignature =
|
||||
LeafNode<NodeKind::IdentifierNameNotBeforeSignature,
|
||||
Lex::IdentifierTokenIndex,
|
||||
@@ -574,7 +574,7 @@ using FunctionIntroducer =
|
||||
// A return type: `-> i32`.
|
||||
struct ReturnType {
|
||||
static constexpr auto Kind = NodeKind::ReturnType.Define(
|
||||
{.category = NodeCategory::ReturnDecl, .child_count = 1});
|
||||
{.category = NodeCategory::ReturnSpecifier, .child_count = 1});
|
||||
|
||||
Lex::MinusGreaterTokenIndex token;
|
||||
AnyExprId type;
|
||||
@@ -583,7 +583,7 @@ struct ReturnType {
|
||||
// A return form: `->? form(var i32)`
|
||||
struct ReturnForm {
|
||||
static constexpr auto Kind = NodeKind::ReturnForm.Define(
|
||||
{.category = NodeCategory::ReturnDecl, .child_count = 1});
|
||||
{.category = NodeCategory::ReturnSpecifier, .child_count = 1});
|
||||
|
||||
Lex::MinusGreaterQuestionTokenIndex token;
|
||||
AnyExprId type;
|
||||
@@ -599,7 +599,7 @@ struct FunctionSignature {
|
||||
FunctionIntroducerId introducer;
|
||||
llvm::SmallVector<AnyModifierId> modifiers;
|
||||
DeclName name;
|
||||
std::optional<AnyReturnDeclId> return_type;
|
||||
std::optional<AnyReturnSpecifierId> return_type;
|
||||
TokenKind token;
|
||||
};
|
||||
|
||||
@@ -795,7 +795,7 @@ struct LambdaDefinitionStart {
|
||||
LambdaIntroducerId introducer;
|
||||
std::optional<ImplicitParamListId> implicit_params;
|
||||
std::optional<ExplicitParamListId> explicit_params;
|
||||
std::optional<AnyReturnDeclId> return_type;
|
||||
std::optional<AnyReturnSpecifierId> return_type;
|
||||
Lex::TokenIndex token;
|
||||
};
|
||||
|
||||
|
||||
@@ -571,9 +571,15 @@ auto Formatter::FormatFunction(FunctionId id, const Function& fn) -> void {
|
||||
|
||||
llvm::SaveAndRestore function_scope(scope_, inst_namer_.GetScopeFor(id));
|
||||
|
||||
// The return type might not have been deduced if checking stopped early.
|
||||
FormatFunctionSignature(fn.call_params_id,
|
||||
fn.call_param_ranges.return_begin(),
|
||||
fn.GetDeclaredReturnForm(*sem_ir_));
|
||||
fn.has_pending_deduced_return_type()
|
||||
? InstId::None
|
||||
: fn.GetDeclaredReturnForm(*sem_ir_));
|
||||
if (fn.has_deduced_return_type) {
|
||||
out() << " [deduced]";
|
||||
}
|
||||
|
||||
if (auto builtin_kind = fn.GetBuiltinFunctionKind(*sem_ir_);
|
||||
builtin_kind != BuiltinFunctionKind::None) {
|
||||
|
||||
@@ -176,6 +176,8 @@ auto Function::GetBuiltinFunctionKind(const File& file) const
|
||||
|
||||
auto Function::GetDeclaredReturnType(const File& file,
|
||||
SpecificId specific_id) const -> TypeId {
|
||||
CARBON_CHECK(!has_pending_deduced_return_type(),
|
||||
"Querying return type of function before it is deduced");
|
||||
if (!return_type_inst_id.has_value()) {
|
||||
return TypeId::None;
|
||||
}
|
||||
@@ -185,6 +187,8 @@ auto Function::GetDeclaredReturnType(const File& file,
|
||||
|
||||
auto Function::GetDeclaredReturnForm(const File& file,
|
||||
SpecificId specific_id) const -> InstId {
|
||||
CARBON_CHECK(!has_pending_deduced_return_type(),
|
||||
"Querying return form of function before it is deduced");
|
||||
if (!return_form_inst_id.has_value()) {
|
||||
// Treat as equivalent to `-> ()`.
|
||||
return InstId::None;
|
||||
|
||||
@@ -67,7 +67,10 @@ struct FunctionFields {
|
||||
enum class InterfaceModifier : uint8_t { None, Default, Final };
|
||||
|
||||
// The following members always have values, and do not change throughout the
|
||||
// lifetime of the function.
|
||||
// lifetime of the function. The exception is a function with a deduced return
|
||||
// type: its `Call` parameters, parameter pattern block and return
|
||||
// information are extended once, when the return type is deduced. See
|
||||
// `has_pending_deduced_return_type`.
|
||||
|
||||
// This block consists of references to the `*ParamPattern` insts that
|
||||
// represent the function's `Call` parameters. The "`Call` parameters" are the
|
||||
@@ -149,8 +152,8 @@ struct FunctionFields {
|
||||
InstId return_form_inst_id;
|
||||
|
||||
// The parameter pattern inst that is declared by the function's return
|
||||
// declaration. This will be a ReturnSlotPattern, or None if the function
|
||||
// doesn't have a return declaration. It may or may not be used, depending on
|
||||
// specifier. This will be a ReturnSlotPattern, or None if the function
|
||||
// doesn't have a return specifier. It may or may not be used, depending on
|
||||
// whether the type has an in-place initializing representation.
|
||||
//
|
||||
// TODO: Extend this to support composite return forms.
|
||||
@@ -180,6 +183,11 @@ struct FunctionFields {
|
||||
// Which, if any, interface modifier is applied to this function.
|
||||
InterfaceModifier interface_modifier = InterfaceModifier::None;
|
||||
|
||||
// Whether the function's return type is deduced from its body, as in
|
||||
// `-> auto`. Until the return type is deduced, the function has no return
|
||||
// information; see `has_pending_deduced_return_type`.
|
||||
bool has_deduced_return_type = false;
|
||||
|
||||
// The `self` parameter pattern, if any. This is the first pattern in
|
||||
// `param_patterns_id` (from EntityWithParamsBase).
|
||||
InstId self_param_id = InstId::None;
|
||||
@@ -264,6 +272,9 @@ struct Function : public EntityWithParamsBase,
|
||||
if (interface_modifier != InterfaceModifier::None) {
|
||||
out << ", interface_modifier: " << interface_modifier;
|
||||
}
|
||||
if (has_deduced_return_type) {
|
||||
out << ", has_deduced_return_type: true";
|
||||
}
|
||||
if (!body_block_ids.empty()) {
|
||||
out << llvm::formatv(
|
||||
", body: [{0}]",
|
||||
@@ -319,10 +330,20 @@ struct Function : public EntityWithParamsBase,
|
||||
// to both Builtin and Generated special functions.
|
||||
auto GetBuiltinFunctionKind(const File& file) const -> BuiltinFunctionKind;
|
||||
|
||||
// Returns whether this function's return type is to be deduced from its
|
||||
// body, but has not been deduced yet. This is the case only between the
|
||||
// function's declaration and the end of its body, or its first `returned
|
||||
// var`. In this state, the function has no return information, and
|
||||
// querying its return type or form is an error.
|
||||
auto has_pending_deduced_return_type() const -> bool {
|
||||
return has_deduced_return_type && !return_form_inst_id.has_value();
|
||||
}
|
||||
|
||||
// Gets the declared return type for a specific version of this function, or
|
||||
// the canonical return type for the original declaration no specific is
|
||||
// specified. Returns `None` if no return type was specified, in which
|
||||
// case the effective return type is an empty tuple.
|
||||
// case the effective return type is an empty tuple. The function must not
|
||||
// have an undeduced return type.
|
||||
auto GetDeclaredReturnType(const File& file,
|
||||
SpecificId specific_id = SpecificId::None) const
|
||||
-> TypeId;
|
||||
@@ -331,6 +352,7 @@ struct Function : public EntityWithParamsBase,
|
||||
// function, or for the original declaration if no specific is specified.
|
||||
// Returns `None` if the function was declared without a return form, in which
|
||||
// case the effective return form is an empty tuple initializing expression.
|
||||
// The function must not have an undeduced return type.
|
||||
auto GetDeclaredReturnForm(const File& file,
|
||||
SpecificId specific_id = SpecificId::None) const
|
||||
-> InstId;
|
||||
|
||||
@@ -1571,7 +1571,7 @@ using NamespaceType = SingletonTypeInst<InstKind::NamespaceType, "<namespace>">;
|
||||
// `form_id`. See `AnyFormParamAction` for member documentation.
|
||||
struct OutFormParamPatternAction {
|
||||
static constexpr auto Kind =
|
||||
// TODO: Use Parse::AnyReturnDeclId once we support passing node
|
||||
// TODO: Use Parse::AnyReturnSpecifierId once we support passing node
|
||||
// categories to Define.
|
||||
InstKind::OutFormParamPatternAction
|
||||
.Define<Parse::NodeIdOneOf<Parse::ReturnFormId, Parse::ReturnTypeId>>(
|
||||
|
||||
Reference in new issue
Block a user