mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 11:41:06 +01:00
Support complex type patterns in bindings. (#759)
Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Jon Meow
parent
2295658697
commit
7138ee400f
@@ -29,8 +29,6 @@ namespace Carbon {
|
||||
|
||||
State* state = nullptr;
|
||||
|
||||
auto PatternMatch(const Value* pat, const Value* val, Env,
|
||||
std::list<std::string>*, int) -> std::optional<Env>;
|
||||
void Step();
|
||||
//
|
||||
// Auxiliary Functions
|
||||
@@ -201,12 +199,17 @@ void CallFunction(int line_num, std::vector<const Value*> operas,
|
||||
case Value::Kind::FunctionValue: {
|
||||
const auto& fn = cast<FunctionValue>(*operas[0]);
|
||||
// Bind arguments to parameters
|
||||
std::list<std::string> params;
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(fn.Param(), operas[1], globals, ¶ms, line_num);
|
||||
PatternMatch(fn.Param(), operas[1], line_num);
|
||||
CHECK(matches) << "internal error in call_function, pattern match failed";
|
||||
// Create the new frame and push it on the stack
|
||||
auto* scope = global_arena->RawNew<Scope>(*matches, params);
|
||||
Env values = globals;
|
||||
std::list<std::string> params;
|
||||
for (const auto& [name, value] : *matches) {
|
||||
values.Set(name, value);
|
||||
params.push_back(name);
|
||||
}
|
||||
auto* scope = global_arena->RawNew<Scope>(values, params);
|
||||
auto* frame = global_arena->RawNew<Frame>(
|
||||
fn.Name(), Stack(scope),
|
||||
Stack<Action*>(global_arena->RawNew<StatementAction>(fn.Body())));
|
||||
@@ -266,20 +269,14 @@ void CreateTuple(Frame* frame, Action* act, const Expression* exp) {
|
||||
frame->todo.Push(global_arena->RawNew<ValAction>(tv));
|
||||
}
|
||||
|
||||
// Returns an updated environment that includes the bindings of
|
||||
// pattern variables to their matched values, if matching succeeds.
|
||||
//
|
||||
// The names of the pattern variables are added to the vars parameter.
|
||||
// Returns nullopt if the value doesn't match the pattern.
|
||||
auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
std::list<std::string>* vars, int line_num)
|
||||
auto PatternMatch(const Value* p, const Value* v, int line_num)
|
||||
-> std::optional<Env> {
|
||||
switch (p->Tag()) {
|
||||
case Value::Kind::BindingPlaceholderValue: {
|
||||
const auto& placeholder = cast<BindingPlaceholderValue>(*p);
|
||||
Env values;
|
||||
if (placeholder.Name().has_value()) {
|
||||
Address a = state->heap.AllocateValue(CopyVal(v, line_num));
|
||||
vars->push_back(*placeholder.Name());
|
||||
values.Set(*placeholder.Name(), a);
|
||||
}
|
||||
return values;
|
||||
@@ -290,22 +287,26 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
const auto& p_tup = cast<TupleValue>(*p);
|
||||
const auto& v_tup = cast<TupleValue>(*v);
|
||||
if (p_tup.Elements().size() != v_tup.Elements().size()) {
|
||||
FATAL_RUNTIME_ERROR(line_num)
|
||||
FATAL_PROGRAM_ERROR(line_num)
|
||||
<< "arity mismatch in tuple pattern match:\n pattern: "
|
||||
<< p_tup << "\n value: " << v_tup;
|
||||
}
|
||||
for (const TupleElement& pattern_element : p_tup.Elements()) {
|
||||
const Value* value_field = v_tup.FindField(pattern_element.name);
|
||||
if (value_field == nullptr) {
|
||||
FATAL_RUNTIME_ERROR(line_num)
|
||||
<< "field " << pattern_element.name << "not in " << *v;
|
||||
Env values;
|
||||
for (size_t i = 0; i < p_tup.Elements().size(); ++i) {
|
||||
if (p_tup.Elements()[i].name != v_tup.Elements()[i].name) {
|
||||
FATAL_PROGRAM_ERROR(line_num)
|
||||
<< "Tuple field name '" << v_tup.Elements()[i].name
|
||||
<< "' does not match pattern field name '"
|
||||
<< p_tup.Elements()[i].name << "'";
|
||||
}
|
||||
std::optional<Env> matches = PatternMatch(
|
||||
pattern_element.value, value_field, values, vars, line_num);
|
||||
p_tup.Elements()[i].value, v_tup.Elements()[i].value, line_num);
|
||||
if (!matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
values = *matches;
|
||||
for (const auto& [name, value] : *matches) {
|
||||
values.Set(name, value);
|
||||
}
|
||||
} // for
|
||||
return values;
|
||||
}
|
||||
@@ -321,12 +322,7 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
p_alt.AltName() != v_alt.AltName()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<Env> matches = PatternMatch(
|
||||
p_alt.Argument(), v_alt.Argument(), values, vars, line_num);
|
||||
if (!matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return *matches;
|
||||
return PatternMatch(p_alt.Argument(), v_alt.Argument(), line_num);
|
||||
}
|
||||
default:
|
||||
FATAL() << "expected a choice alternative in pattern, not " << *v;
|
||||
@@ -336,19 +332,32 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
case Value::Kind::FunctionType: {
|
||||
const auto& p_fn = cast<FunctionType>(*p);
|
||||
const auto& v_fn = cast<FunctionType>(*v);
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(p_fn.Param(), v_fn.Param(), values, vars, line_num);
|
||||
if (!matches) {
|
||||
std::optional<Env> param_matches =
|
||||
PatternMatch(p_fn.Param(), v_fn.Param(), line_num);
|
||||
if (!param_matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return PatternMatch(p_fn.Ret(), v_fn.Ret(), *matches, vars, line_num);
|
||||
std::optional<Env> ret_matches =
|
||||
PatternMatch(p_fn.Ret(), v_fn.Ret(), line_num);
|
||||
if (!ret_matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
Env values = *param_matches;
|
||||
for (const auto& [name, value] : *ret_matches) {
|
||||
values.Set(name, value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
case Value::Kind::AutoType:
|
||||
// `auto` matches any type, without binding any new names. We rely
|
||||
// on the typechecker to ensure that `v` is a type.
|
||||
return Env();
|
||||
default:
|
||||
if (ValueEqual(p, v, line_num)) {
|
||||
return values;
|
||||
return Env();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -913,12 +922,15 @@ void StepStmt() {
|
||||
} else { // try to match
|
||||
auto v = act->Results()[0];
|
||||
auto pat = act->Results()[clause_num + 1];
|
||||
auto values = CurrentEnv(state);
|
||||
std::list<std::string> vars;
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(pat, v, values, &vars, stmt->LineNumber());
|
||||
std::optional<Env> matches = PatternMatch(pat, v, stmt->LineNumber());
|
||||
if (matches) { // we have a match, start the body
|
||||
auto* new_scope = global_arena->RawNew<Scope>(*matches, vars);
|
||||
Env values = CurrentEnv(state);
|
||||
std::list<std::string> vars;
|
||||
for (const auto& [name, value] : *matches) {
|
||||
values.Set(name, value);
|
||||
vars.push_back(name);
|
||||
}
|
||||
auto* new_scope = global_arena->RawNew<Scope>(values, vars);
|
||||
frame->scopes.Push(new_scope);
|
||||
const Statement* body_block =
|
||||
global_arena->RawNew<Block>(stmt->LineNumber(), c->second);
|
||||
@@ -1025,13 +1037,14 @@ void StepStmt() {
|
||||
const Value* v = act->Results()[0];
|
||||
const Value* p = act->Results()[1];
|
||||
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(p, v, frame->scopes.Top()->values,
|
||||
&frame->scopes.Top()->locals, stmt->LineNumber());
|
||||
std::optional<Env> matches = PatternMatch(p, v, stmt->LineNumber());
|
||||
CHECK(matches)
|
||||
<< stmt->LineNumber()
|
||||
<< ": internal error in variable definition, match failed";
|
||||
frame->scopes.Top()->values = *matches;
|
||||
for (const auto& [name, value] : *matches) {
|
||||
frame->scopes.Top()->values.Set(name, value);
|
||||
frame->scopes.Top()->locals.push_back(name);
|
||||
}
|
||||
frame->todo.Pop(1);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user