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
13 changes: 13 additions & 0 deletions crates/rue-air/src/inference/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,19 @@ impl<'a> ConstraintGenerator<'a> {
InferType::Concrete(Type::NEVER)
}

// Accessor yield (ADR-0062): the yielded place must have the
// accessor's declared element type `T` (the function's return
// type); the `yield` itself diverges like `return`.
InstData::Yield(value) => {
let value_info = self.generate(*value, ctx);
self.add_constraint(Constraint::equal(
value_info.ty,
InferType::Concrete(ctx.return_type),
span,
));
InferType::Concrete(Type::NEVER)
}

// Function call
InstData::Call { name, args } => {
let alias_target = self.const_function_alias((span.file_id, *name));
Expand Down
28 changes: 26 additions & 2 deletions crates/rue-air/src/sema/aggregates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,15 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {
self.analyze_inst(air, field_value, ctx)?
};

// An accessor result is a second-class borrowed place (ADR-0062):
// capturing it as an aggregate member would store the borrow.
self.reject_accessor_result_escape(
field_value,
super::analysis::AccessorEscapeSite::Capture,
span,
ctx,
)?;

// Two-types model (ADR-0043, RUE-386): storing into a first-class
// `str` field must not smuggle a borrowed `str` view (a
// `borrow`/`inout str` parameter) into the aggregate — the view
Expand Down Expand Up @@ -1106,7 +1115,7 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {
// the comptime-only `type`, would reach the intern pool and panic
// (RUE-253).
for elem_ref in &elem_refs {
if let Some(elem_ty) = ctx.resolved_types.get(elem_ref).copied() {
if let Some(elem_ty) = ctx.resolved_type_of(*elem_ref) {
self.reject_non_runtime_array_element(elem_ty, span)?;
}
}
Expand Down Expand Up @@ -1168,6 +1177,14 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {
let mut air_elems = Vec::with_capacity(elem_refs.len());
for elem_ref in elem_refs {
let elem_result = self.analyze_inst(air, elem_ref, ctx)?;
// An accessor result cannot be captured as an array element
// (ADR-0062): the member would store a second-class borrow.
self.reject_accessor_result_escape(
elem_ref,
super::analysis::AccessorEscapeSite::Capture,
span,
ctx,
)?;
air_elems.push(elem_result.air_ref);
}

Expand Down Expand Up @@ -1201,7 +1218,7 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {
// runtime representation. Reject it (E1200 / E0206) before the preview
// gate below and before the comptime-only/module element type would
// reach the intern pool and panic (RUE-253, RUE-265).
if let Some(value_ty) = ctx.resolved_types.get(&value_ref).copied() {
if let Some(value_ty) = ctx.resolved_type_of(value_ref) {
self.reject_non_runtime_array_element(value_ty, span)?;
}

Expand Down Expand Up @@ -1249,6 +1266,13 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {

// Evaluate the repeated value exactly once.
let value_result = self.analyze_inst(air, value_ref, ctx)?;
// An accessor result cannot seed an array-repeat literal (ADR-0062).
self.reject_accessor_result_escape(
value_ref,
super::analysis::AccessorEscapeSite::Capture,
span,
ctx,
)?;

// Desugar to ArrayInit: `length` elements, each the single value.
let elem_refs = vec![value_result.air_ref; length as usize];
Expand Down
14 changes: 12 additions & 2 deletions crates/rue-air/src/sema/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2642,6 +2642,7 @@ fn analyze_function_bodies_lazy(sema: &mut BodySema<'_>) -> MultiErrorResult<Sem
*has_self,
*self_mode,
*self_is_mut,
method_info.returns_borrow,
);
sema.declaration_type_observer = previous_type_observer;
sema.body_dependency_observer = previous_body_observer;
Expand Down Expand Up @@ -3430,6 +3431,7 @@ fn check_exclusive_access_in<A>(
interner: &ThreadedRodeo,
args: A,
call_span: Span,
resolve_borrow_root: &dyn Fn(InstRef) -> Option<Spur>,
) -> CompileResult<()>
where
A: IntoIterator,
Expand All @@ -3440,7 +3442,15 @@ where

for arg in args {
let arg = &*arg;
let maybe_var_symbol = root_variable_of(rir, arg.value);
// A `-> borrow T` accessor call is a place for `borrow` arguments
// (ADR-0062): it roots at its receiver's root and joins the shared
// set. `inout` accessor results stay rejected as non-lvalues (the
// exclusive form is the RUE-1016 phase).
let maybe_var_symbol = root_variable_of(rir, arg.value).or_else(|| {
arg.is_borrow()
.then(|| resolve_borrow_root(arg.value))
.flatten()
});

// Check that inout/borrow arguments are lvalues
if arg.is_inout() && maybe_var_symbol.is_none() {
Expand Down Expand Up @@ -3528,7 +3538,7 @@ mod functions;
mod instructions;
mod intrinsics;
mod ownership;
pub(crate) use ownership::FirstClassStrSite;
pub(crate) use ownership::{AccessorEscapeSite, FirstClassStrSite};
mod pointers;
mod type_inference;

Expand Down
29 changes: 29 additions & 0 deletions crates/rue-air/src/sema/analysis/anon_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,26 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
has_self,
self_mode,
self_is_mut,
returns_borrow,
..
} = &method_inst.data
{
let key = (struct_id, *method_name);

// Place-returning accessors (ADR-0062) are phase-1 restricted
// to named structs: anonymous-struct method identity is
// signature-structural and the provider path registers these
// methods without body handles, so an accessor here could not
// inline. Rejected until a later phase widens the surface.
if *returns_borrow {
return Err(CompileError::new(
ErrorKind::AccessorRequiresBorrowSelf {
found: "a method on an anonymous struct type".to_string(),
},
method_inst.span,
));
}

// Check for duplicate methods
if self.has_method(key) {
let struct_def = self.type_pool.struct_def(struct_id);
Expand Down Expand Up @@ -87,6 +102,7 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
return_type: ret_type,
body: *body,
span: method_inst.span,
returns_borrow: false,
},
);
self.index_anonymous_callable_method(struct_id, *method_name, *has_self);
Expand Down Expand Up @@ -126,11 +142,18 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
has_self,
self_mode,
self_is_mut,
returns_borrow,
..
} = &method_inst.data
{
let key = (struct_id, *method_name);

// Accessors are not supported on anonymous structs (ADR-0062
// phase 1); fall back so the compile-time path reports it.
if *returns_borrow {
return None;
}

// Check for duplicate methods - return None in comptime context
if self.has_method(key) {
return None;
Expand Down Expand Up @@ -181,6 +204,7 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
return_type: ret_type,
body: *body,
span: method_inst.span,
returns_borrow: false,
},
);
self.index_anonymous_callable_method(struct_id, *method_name, *has_self);
Expand Down Expand Up @@ -227,6 +251,7 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
has_self,
self_mode,
self_is_mut,
returns_borrow,
..
} = &instruction.data
else {
Expand All @@ -247,6 +272,9 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
.all(|(parameter, comptime)| parameter.is_comptime == *comptime)
|| !seen.insert(*name)
|| self.has_method((struct_id, *name))
// Accessors are not supported on anonymous structs (ADR-0062
// phase 1).
|| *returns_borrow
{
return None;
}
Expand All @@ -272,6 +300,7 @@ impl<'a, D: crate::sema::DeclarationPhase> crate::sema::Sema<'a, D> {
return_type: materialize(&signature.return_type, struct_type)?,
body: *body,
span: instruction.span,
returns_borrow: false,
},
));
}
Expand Down
21 changes: 21 additions & 0 deletions crates/rue-air/src/sema/analysis/builtin_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,11 +467,32 @@ impl<H: OrdinaryBodyAnalysisHost> OrdinaryBodyEngine<'_, H> {
pub(crate) fn peek_place_type(&self, inst_ref: InstRef, ctx: &AnalysisContext) -> Option<Type> {
match &self.body_rir_ref().get(inst_ref).data {
InstData::VarRef { name, .. } => {
// An accessor-inline place alias (`self` inside an inlined
// accessor body, ADR-0062) shadows caller bindings.
if let Some(alias) = ctx.place_aliases.get(name) {
return Some(
alias
.projections
.last()
.map(|p| p.result_type)
.unwrap_or(alias.base_type),
);
}
if let Some(local) = ctx.locals.get(name) {
return Some(local.ty);
}
ctx.params.iter().find(|p| p.name == *name).map(|p| p.ty)
}
// A `-> borrow T` accessor call is a place of its element type
// (ADR-0062); any other method call is not a place.
InstData::MethodCall {
receiver, method, ..
} => {
let base_ty = self.peek_place_type(*receiver, ctx)?;
let struct_id = base_ty.as_struct()?;
let info = self.call_facts().method_info(struct_id, *method)?;
info.returns_borrow.then_some(info.return_type)
}
InstData::FieldGet { base, field } => {
let base_ty = self.peek_place_type(*base, ctx)?;
let struct_id = base_ty.as_struct()?;
Expand Down
Loading