Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ data lambda_source: String = "module test.claim.tmp_fn_value_arrow_probe\nimport

data deep_nested_matching_source: String = "module test.claim.tmp_fn_value_arrow_probe\nimport std.types { Int, String }\nfn deep(g: fn(fn(fn(fn(fn(fn(fn(fn(fn(Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int { 0 }\nfn apply(f: fn(fn(fn(fn(fn(fn(fn(fn(fn(fn(Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int) -> Int { 0 }\nfn host() -> Int { apply(f: deep) }\n"

// A QUALIFIED function name in value position is the same function value as its bare spelling.
// Before the repair the seed typed it as the function's RETURN, so the matching cell refused
// (`expected Node(fn), got ...`) -- the defect that reddened
// v2.test.claim.long.live_read_classification through v2.lens.complexity_accumulator_copy.roster_gate
// -- and the mismatching cell is the control that the qualified value is judged, not waved through.
data qualified_matching_source: String = "module test.claim.tmp_fn_value_arrow_probe\nimport std.types { Int, String }\nfn inc(x: Int) -> Int { x + 1 }\nfn apply(f: fn(Int) -> Int) -> Int { f(1) }\nfn host() -> Int { apply(f: test.claim.tmp_fn_value_arrow_probe.inc) }\n"

data qualified_mismatched_parameter_source: String = "module test.claim.tmp_fn_value_arrow_probe\nimport std.types { Int, String }\nfn convert(s: String) -> String { s }\nfn apply(f: fn(Int) -> Int) -> Int { f(1) }\nfn host() -> Int { apply(f: test.claim.tmp_fn_value_arrow_probe.convert) }\n"


fn refuses_as_type_mismatch(source: String) -> Bool {
any_class_count_for(source: source, wanted_class: "TypeMismatch") > 0
Expand Down Expand Up @@ -121,3 +130,11 @@ test fn lambda_accepted() -> Bool {
test fn deep_nested_matching_accepted() -> Bool {
total_blocking_count_for(source: deep_nested_matching_source) == 0
}

test fn qualified_matching_accepted() -> Bool {
total_blocking_count_for(source: qualified_matching_source) == 0
}

test fn qualified_mismatched_parameter_refuses() -> Bool {
refuses_as_type_mismatch(source: qualified_mismatched_parameter_source)
}
30 changes: 25 additions & 5 deletions src/v1/04_infer.dag
Original file line number Diff line number Diff line change
Expand Up @@ -6757,7 +6757,7 @@ fn spine_root_is_shadowed(scope: InferScope, root: String) -> Bool {
}
}

fn qualified_value_projection(texpr: Node, scope: InferScope, span: SourceSpan) -> InferResult? {
fn qualified_value_projection(texpr: Node, scope: InferScope, span: SourceSpan, expected: Node?) -> InferResult? {
match field_access_spine(texpr: texpr, source_indices: scope.type_env.source_indices) {
Absent => none
Present { value: spine } =>
Expand All @@ -6774,12 +6774,13 @@ fn qualified_value_projection(texpr: Node, scope: InferScope, span: SourceSpan)
Absent => decl
}
}
let value_type = qualify_borrowed_type_names(
let declared_type = qualify_borrowed_type_names(
n: raw_value_type,
owner_module_path: qualified_all_but_last(name: spine.dotted),
env: scope.type_env,
excluded: empty_map()
)
let value_type = qualified_function_value_type(decl: decl, declared: declared_type, expected: expected)
Present { value: ok_infer(texpr: make_named_expr_node(occurrence_identity: texpr.occurrence_identity,
name: spine.dotted,
expr_data: ExprVar { binding_kind: Present { value: FunctionValueBinding } },
Expand Down Expand Up @@ -6824,8 +6825,27 @@ fn qualified_value_projection(texpr: Node, scope: InferScope, span: SourceSpan)
// collect_typed_service_calls records it and emit_func_params renders the service parameter as
// `: &` with neither identifier nor type — a signature that is syntactically present and useless.
// qualified_value_projection beside this uses kernel_span for the same reason.
fn qualified_or_service_projection(texpr: Node, scope: InferScope, span: SourceSpan) -> InferResult? {
match qualified_value_projection(texpr: texpr, scope: scope, span: span) {
// A QUALIFIED FUNCTION NAME IN VALUE POSITION IS THE FUNCTION, TYPED BY ITS ARROW -- the same
// judgment the bare-name arm makes with resolved_callable_type. A function declaration's `inferred`
// is its RETURN type, so reading it as the value's type typed `pred: m.analyze.finding_is_suspect`
// as Bool; once direct_call_arg_type_mismatch began judging function-typed arguments (#12143) that
// became a refusal of a correct program, while the bare spelling of the same reference was
// accepted. The nullary rule is the bare arm's too: a nullary function where no arrow is expected
// keeps its existing reading, and where an arrow is expected it is the function itself.
fn qualified_function_value_type(decl: Node, declared: Node, expected: Node?) -> Node {
match decl.module_item_kind {
ModuleItemFunction =>
if ((decl.params |> count) == 0) && !expected_type_is_arrow(expected: expected) {
declared
} else {
resolved_callable_type(func_params: decl.params, ret: declared)
}
_ => declared
}
}

fn qualified_or_service_projection(texpr: Node, scope: InferScope, span: SourceSpan, expected: Node?) -> InferResult? {
match qualified_value_projection(texpr: texpr, scope: scope, span: span, expected: expected) {
Present { value: proj } => Present { value: proj }
Absent =>
match service_spine_projection(texpr: texpr, scope: scope) {
Expand Down Expand Up @@ -7294,7 +7314,7 @@ fn infer_expr_body(texpr: Node, scope: InferScope, expected: Node?) -> InferResu
let field_name = field_access_field_at(texpr: texpr, source_indices: scope.type_env.source_indices)
let span = texpr.span
let base_expr = field_access_base(texpr: texpr)
match qualified_or_service_projection(texpr: texpr, scope: scope, span: span) {
match qualified_or_service_projection(texpr: texpr, scope: scope, span: span, expected: expected) {
Present { value: proj } => proj
Absent =>
let base_result = infer_expr(texpr: base_expr, scope: scope, expected: none)
Expand Down
1 change: 0 additions & 1 deletion src/v1/stage0/src/v1_compiler_emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ pub use crate::v1_compiler_infer_env::UnitVariantContribution;
pub use crate::v1_compiler_infer_env::{authored_name, empty_symbol_index, lookup_type_for};
pub use crate::v1_compiler_infer_env::{GlobalBareLookupState, TypeBinding, TypeEnv};
pub use crate::v1_compiler_infer_items::item_is_effectful_callee;
pub use crate::v1_compiler_infer_items::item_resource_names;
pub use crate::v1_compiler_infer_items::{ItemInfo, ResolvedGraph, TypedModule};
pub use crate::v1_compiler_infer_lookup::lookup_func_sig;
pub use crate::v1_compiler_infer_service::{
Expand Down
37 changes: 34 additions & 3 deletions src/v1/stage0/src/v1_compiler_infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10341,6 +10341,7 @@ pub fn qualified_value_projection(
texpr: Rc<Node>,
scope: Rc<InferScope>,
span: Rc<SourceSpan>,
expected: Option<Rc<Node>>,
) -> Option<Rc<InferResult>> {
match crate::v1_std_core::field_access_spine(
texpr.clone(),
Expand All @@ -10362,12 +10363,17 @@ pub fn qualified_value_projection(
std::option::Option::None => decl.clone(),
},
};
let value_type = crate::v1_compiler_infer_env::qualify_borrowed_type_names(
let declared_type = crate::v1_compiler_infer_env::qualify_borrowed_type_names(
raw_value_type.clone(),
crate::v1_compiler_infer_env::qualified_all_but_last(spine.dotted.clone()),
scope.type_env.clone(),
v1_rt::rc_empty_map::<String, bool>(),
);
let value_type = qualified_function_value_type(
decl.clone(),
declared_type.clone(),
expected.clone(),
);
Some(ok_infer(crate::v1_std_core::make_named_expr_node(
texpr.occurrence_identity.clone(),
spine.dotted.clone(),
Expand All @@ -10387,12 +10393,32 @@ pub fn qualified_value_projection(
}
}

pub fn qualified_function_value_type(
decl: Rc<Node>,
declared: Rc<Node>,
expected: Option<Rc<Node>>,
) -> Rc<Node> {
match decl.module_item_kind.clone() {
ParsedModuleItemKind::ModuleItemFunction => {
if (((decl.params.clone().len() as i64) == 0)
&& !expected_type_is_arrow(expected.clone()))
{
declared.clone()
} else {
resolved_callable_type(decl.params.clone(), declared.clone())
}
}
_ => declared.clone(),
}
}

pub fn qualified_or_service_projection(
texpr: Rc<Node>,
scope: Rc<InferScope>,
span: Rc<SourceSpan>,
expected: Option<Rc<Node>>,
) -> Option<Rc<InferResult>> {
match qualified_value_projection(texpr.clone(), scope.clone(), span.clone()) {
match qualified_value_projection(texpr.clone(), scope.clone(), span.clone(), expected.clone()) {
Some(proj) => Some(proj.clone()),
std::option::Option::None => match service_spine_projection(texpr.clone(), scope.clone()) {
std::option::Option::None => std::option::Option::None,
Expand Down Expand Up @@ -11021,7 +11047,12 @@ Rc::new(InferResult {
);
let span = texpr.span.clone();
let base_expr = crate::v1_std_core::field_access_base(texpr.clone());
match qualified_or_service_projection(texpr.clone(), scope.clone(), span.clone()) {
match qualified_or_service_projection(
texpr.clone(),
scope.clone(),
span.clone(),
expected.clone(),
) {
Some(proj) => proj.clone(),
std::option::Option::None => {
let base_result =
Expand Down