mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Fix some missing module-not-found checks
This commit is contained in:
parent
f753b29c12
commit
2cbe9f0cd1
9 changed files with 106 additions and 19 deletions
|
|
@ -2865,18 +2865,18 @@ fn importAliased(
|
|||
_ = try current_scope.introduceImportedModule(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 9. Check that this module actually exists, and if not report an error
|
||||
// Only check if module_envs is provided - when it's null, we don't know what modules
|
||||
// exist yet (e.g., during standalone module canonicalization without full project context)
|
||||
// Also skip the check for platform modules (which have requires_types) since they can
|
||||
// import sibling modules that may not be in module_envs yet.
|
||||
const is_platform = self.env.requires_types.items.items.len > 0;
|
||||
if (self.module_envs) |envs_map| {
|
||||
if (!envs_map.contains(module_name)) {
|
||||
if (!is_platform and !envs_map.contains(module_name)) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
|
||||
// If this import satisfies an exposed type requirement (e.g., platform re-exporting
|
||||
|
|
@ -2934,18 +2934,18 @@ fn importWithAlias(
|
|||
_ = try current_scope.introduceImportedModule(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 8. Check that this module actually exists, and if not report an error
|
||||
// Only check if module_envs is provided - when it's null, we don't know what modules
|
||||
// exist yet (e.g., during standalone module canonicalization without full project context)
|
||||
// Also skip the check for platform modules (which have requires_types) since they can
|
||||
// import sibling modules that may not be in module_envs yet.
|
||||
const is_platform = self.env.requires_types.items.items.len > 0;
|
||||
if (self.module_envs) |envs_map| {
|
||||
if (!envs_map.contains(module_name)) {
|
||||
if (!is_platform and !envs_map.contains(module_name)) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
|
||||
// If this import satisfies an exposed type requirement (e.g., platform re-exporting
|
||||
|
|
@ -2996,18 +2996,18 @@ fn importUnaliased(
|
|||
_ = try current_scope.introduceImportedModule(self.env.gpa, module_name_text, module_import_idx);
|
||||
|
||||
// 6. Check that this module actually exists, and if not report an error
|
||||
// Only check if module_envs is provided - when it's null, we don't know what modules
|
||||
// exist yet (e.g., during standalone module canonicalization without full project context)
|
||||
// Also skip the check for platform modules (which have requires_types) since they can
|
||||
// import sibling modules that may not be in module_envs yet.
|
||||
const is_platform = self.env.requires_types.items.items.len > 0;
|
||||
if (self.module_envs) |envs_map| {
|
||||
if (!envs_map.contains(module_name)) {
|
||||
if (!is_platform and !envs_map.contains(module_name)) {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
} else {
|
||||
try self.env.pushDiagnostic(Diagnostic{ .module_not_found = .{
|
||||
.module_name = module_name,
|
||||
.region = import_region,
|
||||
} });
|
||||
}
|
||||
|
||||
// If this import satisfies an exposed type requirement (e.g., platform re-exporting
|
||||
|
|
@ -4061,6 +4061,18 @@ pub fn canonicalizeExpr(
|
|||
return CanonicalizedExpr{ .idx = expr_idx, .free_vars = if (free_vars_span.len > 0) free_vars_span else null };
|
||||
}
|
||||
|
||||
// Check if this is a required identifier from the platform's `requires` clause
|
||||
const requires_items = self.env.requires_types.items.items;
|
||||
for (requires_items, 0..) |req, idx| {
|
||||
if (req.ident == ident) {
|
||||
// Found a required identifier - create a lookup expression for it
|
||||
const expr_idx = try self.env.addExpr(CIR.Expr{ .e_lookup_required = .{
|
||||
.requires_idx = @intCast(idx),
|
||||
} }, region);
|
||||
return CanonicalizedExpr{ .idx = expr_idx, .free_vars = null };
|
||||
}
|
||||
}
|
||||
|
||||
// We did not find the ident in scope or as an exposed item, and forward refs not allowed
|
||||
return CanonicalizedExpr{
|
||||
.idx = try self.env.pushMalformed(Expr.Idx, Diagnostic{ .ident_not_in_scope = .{
|
||||
|
|
|
|||
|
|
@ -255,6 +255,9 @@ fn collectExprDependencies(
|
|||
// External lookups reference other modules - skip for now
|
||||
.e_lookup_external => {},
|
||||
|
||||
// Required lookups reference app-provided values - skip for dependency analysis
|
||||
.e_lookup_required => {},
|
||||
|
||||
.e_nominal_external => |nominal| {
|
||||
try collectExprDependencies(cir, nominal.backing_expr, dependencies, allocator);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -128,6 +128,18 @@ pub const Expr = union(enum) {
|
|||
target_node_idx: u16,
|
||||
region: Region,
|
||||
},
|
||||
/// Lookup of a required identifier from the platform's `requires` clause.
|
||||
/// This represents a value that the app provides to the platform.
|
||||
/// ```roc
|
||||
/// platform "..."
|
||||
/// requires {} { main! : () => {} }
|
||||
/// ...
|
||||
/// main_for_host! = main! # "main!" here is a required lookup
|
||||
/// ```
|
||||
e_lookup_required: struct {
|
||||
/// Index into env.requires_types for this required identifier
|
||||
requires_idx: u32,
|
||||
},
|
||||
/// A sequence of zero or more elements of the same type
|
||||
/// ```roc
|
||||
/// ["one", "two", "three"]
|
||||
|
|
@ -781,6 +793,22 @@ pub const Expr = union(enum) {
|
|||
|
||||
try tree.endNode(begin, attrs);
|
||||
},
|
||||
.e_lookup_required => |e| {
|
||||
const begin = tree.beginNode();
|
||||
try tree.pushStaticAtom("e-lookup-required");
|
||||
const region = ir.store.getExprRegion(expr_idx);
|
||||
try ir.appendRegionInfoToSExprTreeFromRegion(tree, region);
|
||||
const attrs = tree.beginNode();
|
||||
|
||||
const requires_items = ir.requires_types.items.items;
|
||||
if (e.requires_idx < requires_items.len) {
|
||||
const required_type = requires_items[e.requires_idx];
|
||||
const ident_name = ir.getIdent(required_type.ident);
|
||||
try tree.pushStringPair("required-ident", ident_name);
|
||||
}
|
||||
|
||||
try tree.endNode(begin, attrs);
|
||||
},
|
||||
.e_match => |e| {
|
||||
const begin = tree.beginNode();
|
||||
try tree.pushStaticAtom("e-match");
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ pub const Tag = enum {
|
|||
expr_field_access,
|
||||
expr_static_dispatch,
|
||||
expr_external_lookup,
|
||||
expr_required_lookup,
|
||||
expr_dot_access,
|
||||
expr_apply,
|
||||
expr_string,
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ pub fn relocate(store: *NodeStore, offset: isize) void {
|
|||
/// Count of the diagnostic nodes in the ModuleEnv
|
||||
pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 59;
|
||||
/// Count of the expression nodes in the ModuleEnv
|
||||
pub const MODULEENV_EXPR_NODE_COUNT = 36;
|
||||
pub const MODULEENV_EXPR_NODE_COUNT = 37;
|
||||
/// Count of the statement nodes in the ModuleEnv
|
||||
pub const MODULEENV_STATEMENT_NODE_COUNT = 16;
|
||||
/// Count of the type annotation nodes in the ModuleEnv
|
||||
|
|
@ -385,6 +385,12 @@ pub fn getExpr(store: *const NodeStore, expr: CIR.Expr.Idx) CIR.Expr {
|
|||
.region = store.getRegionAt(node_idx),
|
||||
} };
|
||||
},
|
||||
.expr_required_lookup => {
|
||||
// Handle required lookups (platform requires clause)
|
||||
return CIR.Expr{ .e_lookup_required = .{
|
||||
.requires_idx = node.data_1,
|
||||
} };
|
||||
},
|
||||
.expr_num => {
|
||||
// Get requirements
|
||||
const kind: CIR.NumKind = @enumFromInt(node.data_1);
|
||||
|
|
@ -1470,6 +1476,11 @@ pub fn addExpr(store: *NodeStore, expr: CIR.Expr, region: base.Region) Allocator
|
|||
node.data_1 = @intFromEnum(e.module_idx);
|
||||
node.data_2 = e.target_node_idx;
|
||||
},
|
||||
.e_lookup_required => |e| {
|
||||
// For required lookups (platform requires clause), store the index
|
||||
node.tag = .expr_required_lookup;
|
||||
node.data_1 = e.requires_idx;
|
||||
},
|
||||
.e_num => |e| {
|
||||
node.tag = .expr_num;
|
||||
|
||||
|
|
|
|||
|
|
@ -241,6 +241,11 @@ test "NodeStore round trip - Expressions" {
|
|||
.region = rand_region(),
|
||||
},
|
||||
});
|
||||
try expressions.append(gpa, CIR.Expr{
|
||||
.e_lookup_required = .{
|
||||
.requires_idx = rand.random().int(u32),
|
||||
},
|
||||
});
|
||||
try expressions.append(gpa, CIR.Expr{
|
||||
.e_list = .{
|
||||
.elems = CIR.Expr.Span{ .span = rand_span() },
|
||||
|
|
|
|||
|
|
@ -3021,6 +3021,22 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected)
|
|||
try self.unifyWith(expr_var, .err, env);
|
||||
}
|
||||
},
|
||||
.e_lookup_required => |req| {
|
||||
// Look up the type from the platform's requires clause
|
||||
const requires_items = self.cir.requires_types.items.items;
|
||||
if (req.requires_idx < requires_items.len) {
|
||||
const required_type = requires_items[req.requires_idx];
|
||||
const type_var = ModuleEnv.varFrom(required_type.type_anno);
|
||||
const instantiated_var = try self.instantiateVar(
|
||||
type_var,
|
||||
env,
|
||||
.{ .explicit = expr_region },
|
||||
);
|
||||
_ = try self.unify(expr_var, instantiated_var, env);
|
||||
} else {
|
||||
try self.unifyWith(expr_var, .err, env);
|
||||
}
|
||||
},
|
||||
// block //
|
||||
.e_block => |block| {
|
||||
const anno_free_vars_top = self.anno_free_vars.top();
|
||||
|
|
|
|||
|
|
@ -267,6 +267,10 @@ pub const ComptimeEvaluator = struct {
|
|||
// Nothing to evaluate at the declaration site for these;
|
||||
// by design, they cause crashes when lookups happen on them
|
||||
.e_anno_only => return EvalResult{ .success = null },
|
||||
// Required lookups reference values from the app's `main` that provides
|
||||
// values to the platform's `requires` clause. These values are not available
|
||||
// during compile-time evaluation of the platform - they will be linked at runtime.
|
||||
.e_lookup_required => return EvalResult{ .success = null },
|
||||
else => false,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2752,6 +2752,13 @@ pub const Interpreter = struct {
|
|||
self.triggerCrash("runtime error", false, roc_ops);
|
||||
return error.Crash;
|
||||
},
|
||||
.e_lookup_required => {
|
||||
// Required lookups reference values from the app that provides values to the
|
||||
// platform's `requires` clause. These are not available during compile-time
|
||||
// evaluation - they will be linked at runtime. Return TypeMismatch to signal
|
||||
// that this expression cannot be evaluated at compile time.
|
||||
return error.TypeMismatch;
|
||||
},
|
||||
// no if handling in minimal evaluator
|
||||
// no second e_binop case; handled above
|
||||
else => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue