mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:10:13 +01:00
Detect control flow in entities nested inside functions (#5336)
Right now, return_scope_stack is being used to determine whether logic
is in a function scope. However, we need to handle nested entities
inside function scopes. For example where this crashes right now:
```
base class C(B:! bool) {}
fn F() {
class B {
extend base: C(true or false);
}
}
```
This is doing a few things to make this kind of code not crash:
- Split `scope_stack().Push` into `PushForDeclName`, `PushForEntity`,
`PushForExpr`, and `PushForFunction` so that better decisions can be
made about behaviors.
- Hide `return_scope_stack` in the API, instead using interfaces to get
at the underlying data.
- Also using `PushForFunction` to update it similar to the other stacks
that `ScopeStack` manages.
- Add `IsInFunctionScope` as the best way to determine presence in
function scope.
- Remove `PeekIsLexicalScope` since destruction really wants function
scope information anyways.
- Clean up `destroy_id_stack` handling to be for function scopes rather
than lexical scopes.
- Return after related `context.TODO`s in a couple more spots, so that
code doesn't proceed to add control flow in spite of the lack of
support.
---------
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
co-authored by
Geoff Romer
parent
51498547c9
commit
03e693873b
@@ -229,7 +229,9 @@ auto CheckUnit::ImportCurrentPackage(SemIR::InstId package_inst_id,
|
||||
unit_and_imports_->package_imports_map.Lookup(PackageNameId::None);
|
||||
if (!import_map_lookup) {
|
||||
// Push the scope; there are no names to add.
|
||||
context_.scope_stack().Push(package_inst_id, SemIR::NameScopeId::Package);
|
||||
context_.scope_stack().PushForEntity(
|
||||
package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None,
|
||||
/*lexical_lookup_has_load_error=*/false);
|
||||
return;
|
||||
}
|
||||
PackageImports& self_import =
|
||||
@@ -244,7 +246,7 @@ auto CheckUnit::ImportCurrentPackage(SemIR::InstId package_inst_id,
|
||||
CollectTransitiveImports(self_import.import_decl_id, &self_import,
|
||||
/*api_imports=*/nullptr));
|
||||
|
||||
context_.scope_stack().Push(
|
||||
context_.scope_stack().PushForEntity(
|
||||
package_inst_id, SemIR::NameScopeId::Package, SemIR::SpecificId::None,
|
||||
context_.name_scopes().Get(SemIR::NameScopeId::Package).has_error());
|
||||
}
|
||||
|
||||
@@ -118,10 +118,7 @@ class Context {
|
||||
|
||||
auto scope_stack() -> ScopeStack& { return scope_stack_; }
|
||||
|
||||
// Conveneicne functions for frequently-used `scope_stack` members.
|
||||
auto return_scope_stack() -> llvm::SmallVector<ScopeStack::ReturnScope>& {
|
||||
return scope_stack().return_scope_stack();
|
||||
}
|
||||
// Convenience functions for frequently-used `scope_stack` members.
|
||||
auto break_continue_stack()
|
||||
-> llvm::SmallVector<ScopeStack::BreakContinueScope>& {
|
||||
return scope_stack().break_continue_stack();
|
||||
|
||||
@@ -139,8 +139,8 @@ auto IsCurrentPositionReachable(Context& context) -> bool {
|
||||
|
||||
auto MaybeAddCleanupForInst(Context& context, SemIR::TypeId type_id,
|
||||
SemIR::InstId inst_id) -> void {
|
||||
if (!context.scope_stack().PeekIsLexicalScope()) {
|
||||
// Cleanup can only occur in lexical scopes.
|
||||
if (!context.scope_stack().IsInFunctionScope()) {
|
||||
// Cleanup can only occur in function scopes.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ auto DeclNameStack::PushScopeAndStartName() -> void {
|
||||
decl_name_stack_.push_back(MakeEmptyNameContext());
|
||||
|
||||
// Create a scope for any parameters introduced in this name.
|
||||
context_->scope_stack().Push();
|
||||
context_->scope_stack().PushForDeclName();
|
||||
}
|
||||
|
||||
auto DeclNameStack::FinishName(const NameComponent& name) -> NameContext {
|
||||
@@ -233,8 +233,8 @@ static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id,
|
||||
// providing the definition.
|
||||
StartGenericDecl(context);
|
||||
|
||||
context.scope_stack().Push(scope_inst_id, scope_id, self_specific_id,
|
||||
has_error);
|
||||
context.scope_stack().PushForEntity(scope_inst_id, scope_id, self_specific_id,
|
||||
has_error);
|
||||
|
||||
// An interface also introduces its 'Self' parameter into scope, despite it
|
||||
// not being redeclared as part of the qualifier.
|
||||
@@ -246,7 +246,7 @@ static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
|
||||
// Enter a parameter scope in case the qualified name itself has parameters.
|
||||
context.scope_stack().Push();
|
||||
context.scope_stack().PushForSameRegion();
|
||||
}
|
||||
|
||||
auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void {
|
||||
|
||||
@@ -108,8 +108,8 @@ auto HandleParseNode(Context& context, Parse::ChoiceDefinitionStartId node_id)
|
||||
mut_class.self_type_id = self_type_id;
|
||||
|
||||
// Enter the choice scope.
|
||||
context.scope_stack().Push(class_decl_id, class_info.scope_id,
|
||||
self_specific_id);
|
||||
context.scope_stack().PushForEntity(class_decl_id, class_info.scope_id,
|
||||
self_specific_id);
|
||||
// Checking the binding pattern for an alternative requires a non-empty stack.
|
||||
// We reuse the Choice token even though we're now checking an alternative
|
||||
// inside the Choice, since there's no better token to use.
|
||||
|
||||
@@ -278,7 +278,7 @@ auto HandleParseNode(Context& context, Parse::ClassDefinitionStartId node_id)
|
||||
StartClassDefinition(context, class_info, class_decl_id);
|
||||
|
||||
// Enter the class scope.
|
||||
context.scope_stack().Push(
|
||||
context.scope_stack().PushForEntity(
|
||||
class_decl_id, class_info.scope_id,
|
||||
context.generics().GetSelfSpecific(class_info.generic_id));
|
||||
StartGenericDefinition(context);
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Carbon::Check {
|
||||
auto HandleParseNode(Context& context, Parse::CodeBlockStartId node_id)
|
||||
-> bool {
|
||||
context.node_stack().Push(node_id);
|
||||
context.scope_stack().Push();
|
||||
context.scope_stack().PushForSameRegion();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -585,10 +585,9 @@ static auto HandleFunctionDefinitionAfterSignature(
|
||||
auto& function = context.functions().Get(function_id);
|
||||
|
||||
// Create the function scope and the entry block.
|
||||
context.return_scope_stack().push_back({.decl_id = decl_id});
|
||||
context.scope_stack().PushForFunctionBody(decl_id);
|
||||
context.inst_block_stack().Push();
|
||||
context.region_stack().PushRegion(context.inst_block_stack().PeekOrAdd());
|
||||
context.scope_stack().Push(decl_id);
|
||||
StartGenericDefinition(context);
|
||||
|
||||
CheckFunctionDefinitionSignature(context, function);
|
||||
@@ -645,9 +644,8 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id)
|
||||
}
|
||||
}
|
||||
|
||||
context.scope_stack().Pop();
|
||||
context.inst_block_stack().Pop();
|
||||
context.return_scope_stack().pop_back();
|
||||
context.scope_stack().Pop();
|
||||
context.decl_name_stack().PopScope();
|
||||
|
||||
auto& function = context.functions().Get(function_id);
|
||||
|
||||
@@ -64,10 +64,10 @@ auto HandleParseNode(Context& context, Parse::IfExprThenId node_id) -> bool {
|
||||
}
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::IfExprElseId node_id) -> bool {
|
||||
if (context.return_scope_stack().empty()) {
|
||||
context.TODO(node_id,
|
||||
"Control flow expressions are currently only supported inside "
|
||||
"functions.");
|
||||
if (!context.scope_stack().IsInFunctionScope()) {
|
||||
return context.TODO(node_id,
|
||||
"Control flow expressions are currently only supported "
|
||||
"inside functions.");
|
||||
}
|
||||
// Alias node_id for if/then/else consistency.
|
||||
auto& else_node = node_id;
|
||||
|
||||
@@ -537,7 +537,7 @@ auto HandleParseNode(Context& context, Parse::ImplDefinitionStartId node_id)
|
||||
context.name_scopes().Add(impl_decl_id, SemIR::NameId::None,
|
||||
context.decl_name_stack().PeekParentScopeId());
|
||||
|
||||
context.scope_stack().Push(
|
||||
context.scope_stack().PushForEntity(
|
||||
impl_decl_id, impl_info.scope_id,
|
||||
context.generics().GetSelfSpecific(impl_info.generic_id));
|
||||
StartGenericDefinition(context);
|
||||
|
||||
@@ -193,8 +193,8 @@ auto HandleParseNode(Context& context,
|
||||
interface_info.self_param_id);
|
||||
|
||||
// Enter the interface scope.
|
||||
context.scope_stack().Push(interface_decl_id, interface_info.scope_id,
|
||||
self_specific_id);
|
||||
context.scope_stack().PushForEntity(
|
||||
interface_decl_id, interface_info.scope_id, self_specific_id);
|
||||
|
||||
// TODO: Handle the case where there's control flow in the interface body. For
|
||||
// example:
|
||||
|
||||
@@ -398,10 +398,10 @@ auto HandleParseNode(Context& context, Parse::ShortCircuitOperandOrId node_id)
|
||||
// occurs during operand handling.
|
||||
static auto HandleShortCircuitOperator(Context& context, Parse::NodeId node_id)
|
||||
-> bool {
|
||||
if (context.return_scope_stack().empty()) {
|
||||
context.TODO(node_id,
|
||||
"Control flow expressions are currently only supported inside "
|
||||
"functions.");
|
||||
if (!context.scope_stack().IsInFunctionScope()) {
|
||||
return context.TODO(node_id,
|
||||
"Control flow expressions are currently only supported "
|
||||
"inside functions.");
|
||||
}
|
||||
auto [rhs_node, rhs_id] = context.node_stack().PopExprWithNodeId();
|
||||
auto short_circuit_result_id = context.node_stack().PopExpr();
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Carbon::Check {
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id)
|
||||
-> bool {
|
||||
context.scope_stack().Push();
|
||||
context.scope_stack().PushForSameRegion();
|
||||
context.node_stack().Push(node_id);
|
||||
context.struct_type_fields_stack().PushArray();
|
||||
context.param_and_arg_refs_stack().Push();
|
||||
@@ -23,7 +23,7 @@ auto HandleParseNode(Context& context, Parse::StructLiteralStartId node_id)
|
||||
|
||||
auto HandleParseNode(Context& context, Parse::StructTypeLiteralStartId node_id)
|
||||
-> bool {
|
||||
context.scope_stack().Push();
|
||||
context.scope_stack().PushForSameRegion();
|
||||
context.node_stack().Push(node_id);
|
||||
context.struct_type_fields_stack().PushArray();
|
||||
return true;
|
||||
|
||||
@@ -27,7 +27,7 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
|
||||
|
||||
// Introduce a name scope so that we can remove the `.Self` entry we are
|
||||
// adding to name lookup at the end of the `where` expression.
|
||||
context.scope_stack().Push();
|
||||
context.scope_stack().PushForSameRegion();
|
||||
// Introduce `.Self` as a symbolic binding. Its type is the value of the
|
||||
// expression to the left of `where`, so `MyInterface` in the example above.
|
||||
auto entity_name_id = context.entity_names().Add(
|
||||
|
||||
@@ -12,12 +12,11 @@ namespace Carbon::Check {
|
||||
|
||||
// Gets the function that lexically encloses the current location.
|
||||
auto GetCurrentFunctionForReturn(Context& context) -> SemIR::Function& {
|
||||
CARBON_CHECK(!context.return_scope_stack().empty(),
|
||||
CARBON_CHECK(context.scope_stack().IsInFunctionScope(),
|
||||
"Handling return but not in a function");
|
||||
auto function_id = context.insts()
|
||||
.GetAs<SemIR::FunctionDecl>(
|
||||
context.return_scope_stack().back().decl_id)
|
||||
.function_id;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -33,9 +32,9 @@ auto GetCurrentReturnSlot(Context& context) -> SemIR::InstId {
|
||||
// Gets the currently in scope `returned var`, if any, that would be returned
|
||||
// by a `return var;`.
|
||||
static auto GetCurrentReturnedVar(Context& context) -> SemIR::InstId {
|
||||
CARBON_CHECK(!context.return_scope_stack().empty(),
|
||||
CARBON_CHECK(context.scope_stack().IsInFunctionScope(),
|
||||
"Handling return but not in a function");
|
||||
return context.return_scope_stack().back().returned_var;
|
||||
return context.scope_stack().GetReturnedVar();
|
||||
}
|
||||
|
||||
// Produces a note that the given function has no explicit return type.
|
||||
|
||||
@@ -63,8 +63,6 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
|
||||
// specifics, we'll need to somehow track them in lookup.
|
||||
CARBON_CHECK(!specific_id.has_value(),
|
||||
"Lexical scope should not have an associated specific.");
|
||||
|
||||
destroy_id_stack_.PushArray();
|
||||
} else {
|
||||
non_lexical_scope_stack_.push_back({.scope_index = next_scope_index_,
|
||||
.name_scope_id = scope_id,
|
||||
@@ -79,6 +77,36 @@ auto ScopeStack::Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
|
||||
VerifyNextCompileTimeBindIndex("Push", scope_stack_.back());
|
||||
}
|
||||
|
||||
auto ScopeStack::PushForDeclName() -> void {
|
||||
Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None,
|
||||
/*lexical_lookup_has_load_error=*/false);
|
||||
MarkNestingIfInReturnScope();
|
||||
}
|
||||
|
||||
auto ScopeStack::PushForEntity(SemIR::InstId scope_inst_id,
|
||||
SemIR::NameScopeId scope_id,
|
||||
SemIR::SpecificId specific_id,
|
||||
bool lexical_lookup_has_load_error) -> void {
|
||||
CARBON_CHECK(scope_inst_id.has_value());
|
||||
CARBON_DCHECK(!sem_ir_->insts().Is<SemIR::FunctionDecl>(scope_inst_id));
|
||||
Push(scope_inst_id, scope_id, specific_id, lexical_lookup_has_load_error);
|
||||
MarkNestingIfInReturnScope();
|
||||
}
|
||||
|
||||
auto ScopeStack::PushForSameRegion() -> void {
|
||||
Push(SemIR::InstId::None, SemIR::NameScopeId::None, SemIR::SpecificId::None,
|
||||
/*lexical_lookup_has_load_error=*/false);
|
||||
}
|
||||
|
||||
auto ScopeStack::PushForFunctionBody(SemIR::InstId scope_inst_id) -> void {
|
||||
CARBON_DCHECK(sem_ir_->insts().Is<SemIR::FunctionDecl>(scope_inst_id));
|
||||
Push(scope_inst_id, SemIR::NameScopeId::None, SemIR::SpecificId::None,
|
||||
/*lexical_lookup_has_load_error=*/false);
|
||||
|
||||
return_scope_stack_.push_back({.decl_id = scope_inst_id});
|
||||
destroy_id_stack_.PushArray();
|
||||
}
|
||||
|
||||
auto ScopeStack::Pop() -> void {
|
||||
auto scope = scope_stack_.pop_back_val();
|
||||
|
||||
@@ -89,17 +117,27 @@ auto ScopeStack::Pop() -> void {
|
||||
lexical_results.pop_back();
|
||||
});
|
||||
|
||||
if (scope.is_lexical_scope()) {
|
||||
destroy_id_stack_.PopArray();
|
||||
} else {
|
||||
if (!scope.is_lexical_scope()) {
|
||||
CARBON_CHECK(non_lexical_scope_stack_.back().scope_index == scope.index);
|
||||
non_lexical_scope_stack_.pop_back();
|
||||
}
|
||||
|
||||
if (scope.has_returned_var) {
|
||||
CARBON_CHECK(!return_scope_stack_.empty());
|
||||
CARBON_CHECK(return_scope_stack_.back().returned_var.has_value());
|
||||
return_scope_stack_.back().returned_var = SemIR::InstId::None;
|
||||
if (!return_scope_stack_.empty()) {
|
||||
if (scope.has_returned_var) {
|
||||
CARBON_CHECK(return_scope_stack_.back().returned_var.has_value());
|
||||
return_scope_stack_.back().returned_var = SemIR::InstId::None;
|
||||
}
|
||||
|
||||
if (return_scope_stack_.back().decl_id == scope.scope_inst_id) {
|
||||
// Leaving the function scope.
|
||||
return_scope_stack_.pop_back();
|
||||
destroy_id_stack_.PopArray();
|
||||
} else if (return_scope_stack_.back().nested_scope_index == scope.index) {
|
||||
// Returned to a function scope from a non-function nested entity scope.
|
||||
return_scope_stack_.back().nested_scope_index = ScopeIndex::None;
|
||||
}
|
||||
} else {
|
||||
CARBON_CHECK(!scope.has_returned_var);
|
||||
}
|
||||
|
||||
VerifyNextCompileTimeBindIndex("Pop", scope);
|
||||
@@ -218,11 +256,7 @@ auto ScopeStack::Suspend() -> SuspendedScope {
|
||||
CARBON_CHECK(!scope_stack_.empty(), "No scope to suspend");
|
||||
SuspendedScope result = {.entry = scope_stack_.pop_back_val(),
|
||||
.suspended_items = {}};
|
||||
if (result.entry.is_lexical_scope()) {
|
||||
CARBON_CHECK(destroy_id_stack_.PeekArray().empty(),
|
||||
"Missing support to suspend scopes with destructed storage");
|
||||
destroy_id_stack_.PopArray();
|
||||
} else {
|
||||
if (!result.entry.is_lexical_scope()) {
|
||||
non_lexical_scope_stack_.pop_back();
|
||||
}
|
||||
auto peek_compile_time_bindings = compile_time_binding_stack_.PeekArray();
|
||||
@@ -265,9 +299,7 @@ auto ScopeStack::Restore(SuspendedScope scope) -> void {
|
||||
|
||||
VerifyNextCompileTimeBindIndex("Restore", scope.entry);
|
||||
|
||||
if (scope.entry.is_lexical_scope()) {
|
||||
destroy_id_stack_.PushArray();
|
||||
} else {
|
||||
if (!scope.entry.is_lexical_scope()) {
|
||||
non_lexical_scope_stack_.push_back(
|
||||
{.scope_index = scope.entry.index,
|
||||
.name_scope_id = scope.entry.scope_id,
|
||||
|
||||
@@ -31,17 +31,6 @@ class ScopeStack {
|
||||
SemIR::InstBlockId continue_target;
|
||||
};
|
||||
|
||||
// A scope in which `return` can be used.
|
||||
struct ReturnScope {
|
||||
// The declaration from which we can return. Inside a function, this will
|
||||
// be a `FunctionDecl`.
|
||||
SemIR::InstId decl_id;
|
||||
|
||||
// The value corresponding to the current `returned var`, if any. Will be
|
||||
// set and unset as `returned var`s are declared and go out of scope.
|
||||
SemIR::InstId returned_var = SemIR::InstId::None;
|
||||
};
|
||||
|
||||
// A non-lexical scope in which unqualified lookup may be required.
|
||||
struct NonLexicalScope {
|
||||
// The index of the scope in the scope stack.
|
||||
@@ -57,17 +46,24 @@ class ScopeStack {
|
||||
// Information about a scope that has been temporarily removed from the stack.
|
||||
struct SuspendedScope;
|
||||
|
||||
// Pushes a scope onto scope_stack_. NameScopeId::None is used for new
|
||||
// scopes. lexical_lookup_has_load_error is used to limit diagnostics when a
|
||||
// given namespace may contain a mix of both successful and failed name
|
||||
// imports.
|
||||
auto Push(SemIR::InstId scope_inst_id = SemIR::InstId::None,
|
||||
SemIR::NameScopeId scope_id = SemIR::NameScopeId::None,
|
||||
SemIR::SpecificId specific_id = SemIR::SpecificId::None,
|
||||
bool lexical_lookup_has_load_error = false) -> void;
|
||||
// Pushes a scope for a declaration name's parameters.
|
||||
auto PushForDeclName() -> void;
|
||||
|
||||
// Pops the top scope from scope_stack_, cleaning up names from
|
||||
// lexical_lookup_.
|
||||
// Pushes a non-function entity scope. Functions must use
|
||||
// `PushForFunctionBody` instead.
|
||||
auto PushForEntity(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
|
||||
SemIR::SpecificId specific_id,
|
||||
bool lexical_lookup_has_load_error = false) -> void;
|
||||
|
||||
// Pushes a scope which should be in the same region as the current scope.
|
||||
// These can be in a function without breaking `return` scoping. For example,
|
||||
// this is used by struct literals and code blocks.
|
||||
auto PushForSameRegion() -> void;
|
||||
|
||||
// Pushes a function scope.
|
||||
auto PushForFunctionBody(SemIR::InstId scope_inst_id) -> void;
|
||||
|
||||
// Pops the top scope from scope_stack_. Removes names from lexical_lookup_.
|
||||
auto Pop() -> void;
|
||||
|
||||
// Pops the top scope from scope_stack_ if it contains no names.
|
||||
@@ -98,8 +94,12 @@ class ScopeStack {
|
||||
return Peek().specific_id;
|
||||
}
|
||||
|
||||
// Returns true if current scope is lexical.
|
||||
auto PeekIsLexicalScope() const -> bool { return Peek().is_lexical_scope(); }
|
||||
// Returns true if the current scope is inside a function scope (either the
|
||||
// scope itself, or a lexical scope), without an intervening entity scope.
|
||||
auto IsInFunctionScope() const -> bool {
|
||||
return !return_scope_stack_.empty() &&
|
||||
!return_scope_stack_.back().nested_scope_index.has_value();
|
||||
}
|
||||
|
||||
// Returns the current scope, if it is of the specified kind. Otherwise,
|
||||
// returns nullopt.
|
||||
@@ -117,6 +117,19 @@ class ScopeStack {
|
||||
// is already a `returned var`, returns it instead.
|
||||
auto SetReturnedVarOrGetExisting(SemIR::InstId inst_id) -> SemIR::InstId;
|
||||
|
||||
// Returns the `returned var` instruction that's currently in scope, or `None`
|
||||
// if there isn't one.
|
||||
auto GetReturnedVar() -> SemIR::InstId {
|
||||
CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function");
|
||||
return return_scope_stack_.back().returned_var;
|
||||
}
|
||||
|
||||
// Returns the decl ID for the current return scope.
|
||||
auto GetReturnScopeDeclId() -> SemIR::InstId {
|
||||
CARBON_CHECK(IsInFunctionScope(), "Handling return but not in a function");
|
||||
return return_scope_stack_.back().decl_id;
|
||||
}
|
||||
|
||||
// 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.
|
||||
auto LookupInLexicalScopesWithin(SemIR::NameId name_id,
|
||||
@@ -160,10 +173,6 @@ class ScopeStack {
|
||||
// Runs verification that the processing cleanly finished.
|
||||
auto VerifyOnFinish() const -> void;
|
||||
|
||||
auto return_scope_stack() -> llvm::SmallVector<ReturnScope>& {
|
||||
return return_scope_stack_;
|
||||
}
|
||||
|
||||
auto break_continue_stack() -> llvm::SmallVector<BreakContinueScope>& {
|
||||
return break_continue_stack_;
|
||||
}
|
||||
@@ -219,6 +228,35 @@ class ScopeStack {
|
||||
Set<SemIR::NameId> names = {};
|
||||
};
|
||||
|
||||
// A scope in which `return` can be used.
|
||||
struct ReturnScope {
|
||||
// The `FunctionDecl`.
|
||||
SemIR::InstId decl_id;
|
||||
|
||||
// The value corresponding to the current `returned var`, if any. Will be
|
||||
// set and unset as `returned var`s are declared and go out of scope.
|
||||
SemIR::InstId returned_var = SemIR::InstId::None;
|
||||
|
||||
// When a nested scope interrupts a return scope, this is the index of the
|
||||
// outermost interrupting scope (the one closest to the function scope).
|
||||
// This can then be used to determine whether we're actually inside the most
|
||||
// recent `ReturnScope`, or inside a different entity scope.
|
||||
//
|
||||
// This won't be set for functions directly inside functions, because they
|
||||
// will have their own `ReturnScope`.
|
||||
|
||||
// For example, when a `class` is inside a `fn`, it interrupts the function
|
||||
// body by setting this on `PushEntity`; `Pop` will set it back to `None`.
|
||||
ScopeIndex nested_scope_index = ScopeIndex::None;
|
||||
};
|
||||
|
||||
// Pushes a scope onto scope_stack_. NameScopeId::None is used for new scopes.
|
||||
// lexical_lookup_has_load_error is used to limit diagnostics when a given
|
||||
// namespace may contain a mix of both successful and failed name imports.
|
||||
auto Push(SemIR::InstId scope_inst_id, SemIR::NameScopeId scope_id,
|
||||
SemIR::SpecificId specific_id, bool lexical_lookup_has_load_error)
|
||||
-> void;
|
||||
|
||||
auto Peek() const -> const ScopeStackEntry& { return scope_stack_.back(); }
|
||||
|
||||
// Returns whether lexical lookup currently has any load errors.
|
||||
@@ -227,6 +265,15 @@ class ScopeStack {
|
||||
scope_stack_.back().lexical_lookup_has_load_error;
|
||||
}
|
||||
|
||||
// If inside a return scope, marks a nested scope (see `nested_scope_index`).
|
||||
// Called after pushing the new scope.
|
||||
auto MarkNestingIfInReturnScope() -> void {
|
||||
if (!return_scope_stack_.empty() &&
|
||||
!return_scope_stack_.back().nested_scope_index.has_value()) {
|
||||
return_scope_stack_.back().nested_scope_index = scope_stack_.back().index;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks that the provided scope's `next_compile_time_bind_index` matches the
|
||||
// full size of the current `compile_time_binding_stack_`. The values should
|
||||
// always match, and this is used to validate the correspondence during
|
||||
@@ -246,8 +293,8 @@ class ScopeStack {
|
||||
// A stack for scope context.
|
||||
llvm::SmallVector<ScopeStackEntry> scope_stack_;
|
||||
|
||||
// A stack of `destroy` functions to call. This only has entries for lexical
|
||||
// scopes, because non-lexical scopes don't have destruction on scope exit.
|
||||
// A stack of `destroy` functions to call. This only has entries inside of
|
||||
// function bodies, where destruction on scope exit is required.
|
||||
ArrayStack<SemIR::InstId> destroy_id_stack_;
|
||||
|
||||
// Information about non-lexical scopes. This is a subset of the entries and
|
||||
|
||||
+73
-15
@@ -8,35 +8,93 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/alias/fail_control_flow.carbon
|
||||
|
||||
// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+16]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_simple.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_simple.carbon:[[@LINE+8]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: alias a = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+12]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: alias a = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+8]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: alias a = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_control_flow.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef]
|
||||
// CHECK:STDERR: fail_simple.carbon:[[@LINE+4]]:11: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: alias a = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
alias a = true or false;
|
||||
|
||||
// CHECK:STDOUT: --- fail_control_flow.carbon
|
||||
// --- fail_nested.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
base class C(B:! bool) {}
|
||||
|
||||
fn F() {
|
||||
class B {
|
||||
// CHECK:STDERR: fail_nested.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: extend base: C(true or false);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
extend base: C(true or false);
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_simple.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_nested.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %B.7dd: bool = bind_symbolic_name B, 0 [symbolic]
|
||||
// CHECK:STDOUT: %B.patt: bool = symbolic_binding_pattern B, 0 [symbolic]
|
||||
// CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete]
|
||||
// CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C, @C(%B.7dd) [symbolic]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %B.d97: type = class_type @B [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: %false: bool = bool_literal false [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generic class @C(<unexpected>.inst27.loc4_14: bool) {
|
||||
// CHECK:STDOUT: %B: bool = bind_symbolic_name B, 0 [symbolic = %B (constants.%B.7dd)]
|
||||
// CHECK:STDOUT: %B.patt.loc4_14.2: bool = symbolic_binding_pattern B, 0 [symbolic = %B.patt.loc4_14.2 (constants.%B.patt)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !definition:
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class {
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type]
|
||||
// CHECK:STDOUT: complete_type_witness = %complete_type
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%C
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %.loc27: bool = block_arg <unexpected instblockref inst_block5> [concrete = constants.%true]
|
||||
// CHECK:STDOUT: %a: <error> = bind_alias a, <error> [concrete = <error>]
|
||||
// CHECK:STDOUT: class @B {
|
||||
// CHECK:STDOUT: %C.ref: %C.type = name_ref C, <unexpected>.inst32.loc4_24 [concrete = constants.%C.generic]
|
||||
// CHECK:STDOUT: %true.loc12_20: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: %.loc12: bool = not %true.loc12_20 [concrete = constants.%false]
|
||||
// CHECK:STDOUT: %true.loc12_25: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: if %.loc12 br !or.rhs else br !or.result(%true.loc12_25)
|
||||
// CHECK:STDOUT: complete_type_witness = invalid
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%B.d97
|
||||
// CHECK:STDOUT: .C = <poisoned>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: specific @C(constants.%B.7dd) {
|
||||
// CHECK:STDOUT: %B => constants.%B.7dd
|
||||
// CHECK:STDOUT: %B.patt.loc4_14.2 => constants.%B.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
@@ -48,7 +48,6 @@ fn F() {
|
||||
var explicit_return: ExplicitReturn;
|
||||
var with_addr: WithAddr;
|
||||
if (true) {
|
||||
// TODO: Destroy in nested scope.
|
||||
var in_scope: NoAddr;
|
||||
}
|
||||
}
|
||||
@@ -476,22 +475,24 @@ fn G() { F({}); }
|
||||
// CHECK:STDOUT: !if.then:
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %in_scope.patt: %NoAddr = binding_pattern in_scope
|
||||
// CHECK:STDOUT: %.loc11: %NoAddr = var_pattern %in_scope.patt
|
||||
// CHECK:STDOUT: %.loc10_5.1: %NoAddr = var_pattern %in_scope.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %in_scope.var: ref %NoAddr = var in_scope
|
||||
// CHECK:STDOUT: %destroy.ref.4: %destroy.type.bc5 = name_ref destroy, imports.%Main.import_ref.7fe [concrete = constants.%destroy.60f]
|
||||
// CHECK:STDOUT: %destroy.bound.4: <bound method> = bound_method %in_scope.var, %destroy.ref.4
|
||||
// CHECK:STDOUT: %NoAddr.ref.loc11: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
|
||||
// CHECK:STDOUT: %NoAddr.ref.loc10: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
|
||||
// CHECK:STDOUT: %in_scope: ref %NoAddr = bind_name in_scope, %in_scope.var
|
||||
// CHECK:STDOUT: br !if.else
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.else:
|
||||
// CHECK:STDOUT: %.loc10_5.2: %NoAddr = bind_value %in_scope.var
|
||||
// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.4(%.loc10_5.2)
|
||||
// CHECK:STDOUT: %addr: %ptr.b4e = addr_of %with_addr.var
|
||||
// CHECK:STDOUT: %destroy.call.1: init %empty_tuple.type = call %destroy.bound.3(%addr)
|
||||
// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.3(%addr)
|
||||
// CHECK:STDOUT: %.loc7_3.2: %ExplicitReturn = bind_value %explicit_return.var
|
||||
// CHECK:STDOUT: %destroy.call.2: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2)
|
||||
// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.2(%.loc7_3.2)
|
||||
// CHECK:STDOUT: %.loc6_3.2: %NoAddr = bind_value %no_addr.var
|
||||
// CHECK:STDOUT: %destroy.call.3: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2)
|
||||
// CHECK:STDOUT: %destroy.call.4: init %empty_tuple.type = call %destroy.bound.1(%.loc6_3.2)
|
||||
// CHECK:STDOUT: return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+110
-77
@@ -8,133 +8,166 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/if_expr/fail_not_in_function.carbon
|
||||
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+16]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_basic.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_basic.carbon:[[@LINE+12]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: let x: i32 = if true then 1 else 0;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+12]]:22: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_basic.carbon:[[@LINE+8]]:22: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: let x: i32 = if true then 1 else 0;
|
||||
// CHECK:STDERR: ^~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_basic.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: let x: i32 = if true then 1 else 0;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: let x: i32 = if true then 1 else 0;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
let x: i32 = if true then 1 else 0;
|
||||
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_types.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_types.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:8: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var y: if true then i32 else f64;
|
||||
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_in_param.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_in_param.carbon:[[@LINE+4]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F(a: if true then i32 else f64);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:9: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fn F(a: if true then i32 else f64);
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F(a: if true then i32 else f64);
|
||||
|
||||
// --- fail_class.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
class C {
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+16]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_class.carbon:[[@LINE+12]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var n: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+12]]:18: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_class.carbon:[[@LINE+8]]:18: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var n: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+8]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_class.carbon:[[@LINE+4]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var n: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_not_in_function.carbon:[[@LINE+4]]:10: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var n: if true then i32 else f64;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var n: if true then i32 else f64;
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_not_in_function.carbon
|
||||
// --- fail_nested_class.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
base class C(T:! type) {}
|
||||
|
||||
fn F() {
|
||||
class B {
|
||||
// CHECK:STDERR: fail_nested_class.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: extend base: C(if true then i32 else i32);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
extend base: C(if true then i32 else i32);
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: --- fail_basic.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_types.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_in_param.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_class.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
|
||||
// CHECK:STDOUT: .Int = %Core.Int
|
||||
// CHECK:STDOUT: .ImplicitAs = %Core.ImplicitAs
|
||||
// CHECK:STDOUT: .Float = %Core.Float
|
||||
// CHECK:STDOUT: import Core//prelude
|
||||
// CHECK:STDOUT: import Core//prelude/...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .Core = imports.%Core
|
||||
// CHECK:STDOUT: .x = %x
|
||||
// CHECK:STDOUT: .y = %y
|
||||
// CHECK:STDOUT: .F = %F.decl
|
||||
// CHECK:STDOUT: .C = %C.decl
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import = import Core
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %x.patt: %i32 = binding_pattern x
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %.loc27: type = splice_block %i32 [concrete = constants.%i32] {
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x: %i32 = bind_name x, <unexpected>.inst1317.loc27_14
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %y.patt: %i32 = binding_pattern y
|
||||
// CHECK:STDOUT: %.loc37: %i32 = var_pattern %y.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %y.var: ref %i32 = var y
|
||||
// CHECK:STDOUT: %y: ref %i32 = bind_name y, %y.var
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %a.patt: %i32 = binding_pattern a
|
||||
// CHECK:STDOUT: %a.param_patt: %i32 = value_param_pattern %a.patt, call_param0
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %a.param: %i32 = value_param call_param0
|
||||
// CHECK:STDOUT: %a: %i32 = bind_name a, %a.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @C {
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else
|
||||
// CHECK:STDOUT: complete_type_witness = <unexpected>.inst1384.loc67_1
|
||||
// CHECK:STDOUT: complete_type_witness = invalid
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%C
|
||||
// CHECK:STDOUT: .n = <unexpected>.inst1381.loc66_8
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%a.param: %i32);
|
||||
// CHECK:STDOUT: --- fail_nested_class.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
|
||||
// CHECK:STDOUT: %C.type: type = generic_class_type @C [concrete]
|
||||
// CHECK:STDOUT: %C.generic: %C.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %C: type = class_type @C, @C(%T) [symbolic]
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete]
|
||||
// CHECK:STDOUT: %complete_type.357: <witness> = complete_type_witness %empty_struct_type [concrete]
|
||||
// CHECK:STDOUT: %B: type = class_type @B [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: generic class @C(<unexpected>.inst17.loc4_14: type) {
|
||||
// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)]
|
||||
// CHECK:STDOUT: %T.patt.loc4_14.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_14.2 (constants.%T.patt)]
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !definition:
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class {
|
||||
// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type]
|
||||
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %empty_struct_type [concrete = constants.%complete_type.357]
|
||||
// CHECK:STDOUT: complete_type_witness = %complete_type
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%C
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: class @B {
|
||||
// CHECK:STDOUT: %C.ref: %C.type = name_ref C, <unexpected>.inst21.loc4_24 [concrete = constants.%C.generic]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else
|
||||
// CHECK:STDOUT: complete_type_witness = invalid
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !members:
|
||||
// CHECK:STDOUT: .Self = constants.%B
|
||||
// CHECK:STDOUT: .C = <poisoned>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F();
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: specific @C(constants.%T) {
|
||||
// CHECK:STDOUT: %T => constants.%T
|
||||
// CHECK:STDOUT: %T.patt.loc4_14.2 => constants.%T.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+80
-114
@@ -8,155 +8,80 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/operators/builtin/fail_and_or_not_in_function.carbon
|
||||
|
||||
// --- fail_and.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// TODO: Make this a compile-time function.
|
||||
fn F(b: bool) -> type {
|
||||
return if b then i32 else f64;
|
||||
}
|
||||
|
||||
// TODO: Short-circuit operators should be permitted outside functions.
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+10]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_and.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var and_: F(true and true);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:11: error: cannot evaluate type expression [TypeExprEvaluationFailure]
|
||||
// CHECK:STDERR: var and_: F(true and true);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
var and_: F(true and true);
|
||||
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+12]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_and_val.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_and_val.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var and_val: bool = true and true;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var and_val: bool = true and true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+4]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_and_val.carbon:[[@LINE+4]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var and_val: bool = true and true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var and_val: bool = true and true;
|
||||
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+10]]:12: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_or.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// TODO: Make this a compile-time function.
|
||||
fn F(b: bool) -> type {
|
||||
return if b then i32 else f64;
|
||||
}
|
||||
|
||||
// CHECK:STDERR: fail_or.carbon:[[@LINE+4]]:12: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var or_: F(true or true);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+6]]:10: error: cannot evaluate type expression [TypeExprEvaluationFailure]
|
||||
// CHECK:STDERR: var or_: F(true or true);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR:
|
||||
var or_: F(true or true);
|
||||
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+12]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_or_val.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_or_val.carbon:[[@LINE+8]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var or_val: bool = true or true;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+8]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var or_val: bool = true or true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_and_or_not_in_function.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_or_val.carbon:[[@LINE+4]]:20: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var or_val: bool = true or true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var or_val: bool = true or true;
|
||||
|
||||
// CHECK:STDOUT: --- fail_and_or_not_in_function.carbon
|
||||
// CHECK:STDOUT: --- fail_and.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete]
|
||||
// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [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: %int_64: Core.IntLiteral = int_value 64 [concrete]
|
||||
// CHECK:STDOUT: %Float.type: type = fn_type @Float [concrete]
|
||||
// CHECK:STDOUT: %Float: %Float.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: %false: bool = bool_literal false [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
|
||||
// CHECK:STDOUT: .Bool = %Core.Bool
|
||||
// CHECK:STDOUT: .Int = %Core.Int
|
||||
// CHECK:STDOUT: .Float = %Core.Float
|
||||
// CHECK:STDOUT: import Core//prelude
|
||||
// CHECK:STDOUT: import Core//prelude/...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .Core = imports.%Core
|
||||
// CHECK:STDOUT: .F = %F.decl
|
||||
// CHECK:STDOUT: .and_ = %and_
|
||||
// CHECK:STDOUT: .and_val = %and_val
|
||||
// CHECK:STDOUT: .or_ = %or_
|
||||
// CHECK:STDOUT: .or_val = %or_val
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import = import Core
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {
|
||||
// CHECK:STDOUT: %b.patt: bool = binding_pattern b
|
||||
// CHECK:STDOUT: %b.param_patt: bool = value_param_pattern %b.patt, call_param0
|
||||
// CHECK:STDOUT: %return.patt: type = return_slot_pattern
|
||||
// CHECK:STDOUT: %return.param_patt: type = out_param_pattern %return.patt, call_param1
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %b.param: bool = value_param call_param0
|
||||
// CHECK:STDOUT: %.loc12_9.1: type = splice_block %.loc12_9.3 [concrete = bool] {
|
||||
// CHECK:STDOUT: %bool.make_type: init type = call constants.%Bool() [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc12_9.2: type = value_of_initializer %bool.make_type [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc12_9.3: type = converted %bool.make_type, %.loc12_9.2 [concrete = bool]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %b: bool = bind_name b, %b.param
|
||||
// CHECK:STDOUT: %return.param: ref type = out_param call_param1
|
||||
// CHECK:STDOUT: %return: ref type = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %and_.patt: <error> = binding_pattern and_
|
||||
// CHECK:STDOUT: %.loc27: <error> = var_pattern %and_.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %and_.var: ref <error> = var and_
|
||||
// CHECK:STDOUT: %and_: <error> = bind_name and_, <error>
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %and_val.patt: bool = binding_pattern and_val
|
||||
// CHECK:STDOUT: %.loc41_1: bool = var_pattern %and_val.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %and_val.var: ref bool = var and_val
|
||||
// CHECK:STDOUT: %.loc41_14.1: type = splice_block %.loc41_14.3 [concrete = bool] {
|
||||
// CHECK:STDOUT: %bool.make_type.loc41: init type = call constants.%Bool() [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc41_14.2: type = value_of_initializer %bool.make_type.loc41 [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc41_14.3: type = converted %bool.make_type.loc41, %.loc41_14.2 [concrete = bool]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %and_val: ref bool = bind_name and_val, %and_val.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %or_.patt: <error> = binding_pattern or_
|
||||
// CHECK:STDOUT: %.loc53: <error> = var_pattern %or_.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %or_.var: ref <error> = var or_
|
||||
// CHECK:STDOUT: %or_: <error> = bind_name or_, <error>
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %or_val.patt: bool = binding_pattern or_val
|
||||
// CHECK:STDOUT: %.loc67_1: bool = var_pattern %or_val.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %or_val.var: ref bool = var or_val
|
||||
// CHECK:STDOUT: %.loc67_13.1: type = splice_block %.loc67_13.3 [concrete = bool] {
|
||||
// CHECK:STDOUT: %bool.make_type.loc67: init type = call constants.%Bool() [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc67_13.2: type = value_of_initializer %bool.make_type.loc67 [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc67_13.3: type = converted %bool.make_type.loc67, %.loc67_13.2 [concrete = bool]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %or_val: ref bool = bind_name or_val, %or_val.var
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%b.param: bool) -> type {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %b.ref: bool = name_ref b, %b
|
||||
// CHECK:STDOUT: %b.ref: bool = name_ref b, <unexpected>.inst27.loc5_6
|
||||
// CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then:
|
||||
@@ -167,19 +92,60 @@ var or_val: bool = true or true;
|
||||
// CHECK:STDOUT: !if.expr.else:
|
||||
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
|
||||
// CHECK:STDOUT: %float.make_type: init type = call constants.%Float(%int_64) [concrete = f64]
|
||||
// CHECK:STDOUT: %.loc13_24.1: type = value_of_initializer %float.make_type [concrete = f64]
|
||||
// CHECK:STDOUT: %.loc13_24.2: type = converted %float.make_type, %.loc13_24.1 [concrete = f64]
|
||||
// CHECK:STDOUT: br !if.expr.result(%.loc13_24.2)
|
||||
// CHECK:STDOUT: %.loc6_24.1: type = value_of_initializer %float.make_type [concrete = f64]
|
||||
// CHECK:STDOUT: %.loc6_24.2: type = converted %float.make_type, %.loc6_24.1 [concrete = f64]
|
||||
// CHECK:STDOUT: br !if.expr.result(%.loc6_24.2)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.result:
|
||||
// CHECK:STDOUT: %.loc13_10: type = block_arg !if.expr.result
|
||||
// CHECK:STDOUT: return %.loc13_10
|
||||
// CHECK:STDOUT: %.loc6_10: type = block_arg !if.expr.result
|
||||
// CHECK:STDOUT: return %.loc6_10
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: --- fail_and_val.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_or.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
|
||||
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete]
|
||||
// CHECK:STDOUT: %Float.type: type = fn_type @Float [concrete]
|
||||
// CHECK:STDOUT: %Float: %Float.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F(%b.param: bool) -> type {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false]
|
||||
// CHECK:STDOUT: if %true br !and.rhs else br !and.result(%false)
|
||||
// CHECK:STDOUT: %b.ref: bool = name_ref b, <unexpected>.inst27.loc5_6
|
||||
// CHECK:STDOUT: if %b.ref br !if.expr.then else br !if.expr.else
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.then:
|
||||
// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32]
|
||||
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32]
|
||||
// CHECK:STDOUT: br !if.expr.result(%i32)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.else:
|
||||
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete = constants.%int_64]
|
||||
// CHECK:STDOUT: %float.make_type: init type = call constants.%Float(%int_64) [concrete = f64]
|
||||
// CHECK:STDOUT: %.loc6_24.1: type = value_of_initializer %float.make_type [concrete = f64]
|
||||
// CHECK:STDOUT: %.loc6_24.2: type = converted %float.make_type, %.loc6_24.1 [concrete = f64]
|
||||
// CHECK:STDOUT: br !if.expr.result(%.loc6_24.2)
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: !if.expr.result:
|
||||
// CHECK:STDOUT: %.loc6_10: type = block_arg !if.expr.result
|
||||
// CHECK:STDOUT: return %.loc6_10
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_or_val.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+44
-92
@@ -8,143 +8,95 @@
|
||||
// TIP: To dump output, run:
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/var/fail_todo_control_flow_init.carbon
|
||||
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+16]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_if_true.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_if_true.carbon:[[@LINE+12]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x: () = if true then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_if_true.carbon:[[@LINE+8]]:21: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x: () = if true then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_if_true.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x: () = if true then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:13: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x: () = if true then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var x: () = if true then () else ();
|
||||
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+16]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_if_false.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_if_false.carbon:[[@LINE+12]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x2: () = if false then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:23: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_if_false.carbon:[[@LINE+8]]:23: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x2: () = if false then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_if_false.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x2: () = if false then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:14: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var x2: () = if false then () else ();
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var x2: () = if false then () else ();
|
||||
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_true_or_false.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_true_or_false.carbon:[[@LINE+8]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y: bool = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y: bool = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_true_or_false.carbon:[[@LINE+4]]:15: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y: bool = true or false;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var y: bool = true or false;
|
||||
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+12]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// --- fail_false_or_true.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
// CHECK:STDERR: fail_false_or_true.carbon:[[@LINE+8]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y2: bool = false or true;
|
||||
// CHECK:STDERR: ^~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+8]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y2: bool = false or true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_todo_control_flow_init.carbon:[[@LINE+4]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: fail_false_or_true.carbon:[[@LINE+4]]:16: error: semantics TODO: `Control flow expressions are currently only supported inside functions.` [SemanticsTodo]
|
||||
// CHECK:STDERR: var y2: bool = false or true;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
var y2: bool = false or true;
|
||||
|
||||
// CHECK:STDOUT: --- fail_todo_control_flow_init.carbon
|
||||
// CHECK:STDOUT: --- fail_if_true.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete]
|
||||
// CHECK:STDOUT: %Bool.type: type = fn_type @Bool [concrete]
|
||||
// CHECK:STDOUT: %Bool: %Bool.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
|
||||
// CHECK:STDOUT: .Bool = %Core.Bool
|
||||
// CHECK:STDOUT: import Core//prelude
|
||||
// CHECK:STDOUT: import Core//prelude/...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_if_false.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
|
||||
// CHECK:STDOUT: .Core = imports.%Core
|
||||
// CHECK:STDOUT: .x = %x
|
||||
// CHECK:STDOUT: .x2 = %x2
|
||||
// CHECK:STDOUT: .y = %y
|
||||
// CHECK:STDOUT: .y2 = %y2
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Core.import = import Core
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x
|
||||
// CHECK:STDOUT: %.loc27_1: %empty_tuple.type = var_pattern %x.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x.var: ref %empty_tuple.type = var x
|
||||
// CHECK:STDOUT: %.loc27_9.1: type = splice_block %.loc27_9.3 [concrete = constants.%empty_tuple.type] {
|
||||
// CHECK:STDOUT: %.loc27_9.2: %empty_tuple.type = tuple_literal ()
|
||||
// CHECK:STDOUT: %.loc27_9.3: type = converted %.loc27_9.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x: ref %empty_tuple.type = bind_name x, %x.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %x2.patt: %empty_tuple.type = binding_pattern x2
|
||||
// CHECK:STDOUT: %.loc45_1: %empty_tuple.type = var_pattern %x2.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x2.var: ref %empty_tuple.type = var x2
|
||||
// CHECK:STDOUT: %.loc45_10.1: type = splice_block %.loc45_10.3 [concrete = constants.%empty_tuple.type] {
|
||||
// CHECK:STDOUT: %.loc45_10.2: %empty_tuple.type = tuple_literal ()
|
||||
// CHECK:STDOUT: %.loc45_10.3: type = converted %.loc45_10.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %x2: ref %empty_tuple.type = bind_name x2, %x2.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %y.patt: bool = binding_pattern y
|
||||
// CHECK:STDOUT: %.loc59_1: bool = var_pattern %y.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %y.var: ref bool = var y
|
||||
// CHECK:STDOUT: %.loc59_8.1: type = splice_block %.loc59_8.3 [concrete = bool] {
|
||||
// CHECK:STDOUT: %bool.make_type.loc59: init type = call constants.%Bool() [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc59_8.2: type = value_of_initializer %bool.make_type.loc59 [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc59_8.3: type = converted %bool.make_type.loc59, %.loc59_8.2 [concrete = bool]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %y: ref bool = bind_name y, %y.var
|
||||
// CHECK:STDOUT: name_binding_decl {
|
||||
// CHECK:STDOUT: %y2.patt: bool = binding_pattern y2
|
||||
// CHECK:STDOUT: %.loc73_1: bool = var_pattern %y2.patt
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %y2.var: ref bool = var y2
|
||||
// CHECK:STDOUT: %.loc73_9.1: type = splice_block %.loc73_9.3 [concrete = bool] {
|
||||
// CHECK:STDOUT: %bool.make_type.loc73: init type = call constants.%Bool() [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc73_9.2: type = value_of_initializer %bool.make_type.loc73 [concrete = bool]
|
||||
// CHECK:STDOUT: %.loc73_9.3: type = converted %bool.make_type.loc73, %.loc73_9.2 [concrete = bool]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %y2: ref bool = bind_name y2, %y2.var
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_true_or_false.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @__global_init() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true]
|
||||
// CHECK:STDOUT: if %true br !if.expr.then else br !if.expr.else
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: --- fail_false_or_true.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {}
|
||||
// CHECK:STDOUT:
|
||||
|
||||
Reference in New Issue
Block a user