Skip to content

Commit

Permalink
nixd/Sema: lowering for let ... in ...
Browse files Browse the repository at this point in the history
  • Loading branch information
inclyc committed Sep 21, 2023
1 parent 5d55537 commit 572ca09
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 8 deletions.
10 changes: 8 additions & 2 deletions nixd/include/nixd/Sema/Lowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ class ExprAttrsBuilder {
Lowering &LW;
nix::ExprAttrs *Result;

RangeIdx Range;

bool Recursive;

RangeIdx Range;
/// let ... in ...
/// It is not allowed to use dynamic binds here, so we want to give diagnostic
/// to each occurrence.
bool IsLet;

/// Nested attributes, we create a new builder for them, and collapse the map
/// while finishing
Expand All @@ -34,7 +39,8 @@ class ExprAttrsBuilder {
std::map<nix::Symbol, const syntax::Node *> Fields;

public:
ExprAttrsBuilder(Lowering &LW, bool Recursive, RangeIdx Range);
ExprAttrsBuilder(Lowering &LW, RangeIdx Range, bool Recursive, bool IsLet);

void addAttr(const syntax::Node *Attr, const syntax::Node *Body,
bool Recursive);
void addAttribute(const syntax::Attribute &A);
Expand Down
49 changes: 43 additions & 6 deletions nixd/lib/Sema/Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,9 @@ static syntax::Diagnostic mkAttrDupDiag(std::string Name, RangeIdx Range,
return Diag;
}

ExprAttrsBuilder::ExprAttrsBuilder(Lowering &LW, bool Recursive, RangeIdx Range)
: LW(LW), Recursive(Recursive), Range(Range) {
ExprAttrsBuilder::ExprAttrsBuilder(Lowering &LW, RangeIdx Range, bool Recursive,
bool IsLet)
: LW(LW), Range(Range), Recursive(Recursive), IsLet(IsLet) {
Result = LW.Ctx.Pool.record(new nix::ExprAttrs);
}

Expand Down Expand Up @@ -196,6 +197,15 @@ static Diagnostic mkRecDiag(RangeIdx ThisRange, RangeIdx MergingRange,
return Diag;
}

static Diagnostic mkLetDynamicDiag(RangeIdx Range) {
Diagnostic Diag;
Diag.Kind = Diagnostic::Error;
Diag.Msg = "dynamic attributes are not allowed in let ... in ... expression";
Diag.Range = Range;

return Diag;
}

void ExprAttrsBuilder::addAttrSet(const syntax::AttrSet &AS) {
if (AS.Recursive != Recursive) {
auto Diag = mkRecDiag(Range, AS.Range, Recursive, AS.Recursive);
Expand Down Expand Up @@ -233,7 +243,7 @@ void ExprAttrsBuilder::addAttr(const syntax::Node *Attr,
// because we have chance to merge it latter
const auto *BodyAttrSet = dynamic_cast<const syntax::AttrSet *>(Body);
Nested[Sym] = std::make_unique<ExprAttrsBuilder>(
LW, BodyAttrSet->Recursive, BodyAttrSet->Range);
LW, BodyAttrSet->Range, BodyAttrSet->Recursive, IsLet);
Nested[Sym]->addBinds(*BodyAttrSet->AttrBinds);
} else {
nix::ExprAttrs::AttrDef Def(LW.lower(Body), Attr->Range.Begin);
Expand All @@ -243,6 +253,11 @@ void ExprAttrsBuilder::addAttr(const syntax::Node *Attr,
break;
}
default: {
if (IsLet) {
// reject dynamic attrs in let expression
LW.Diags.emplace_back(mkLetDynamicDiag(Attr->Range));
return;
}
nix::Expr *NameExpr = LW.lower(Attr);
nix::Expr *ValueExpr = LW.lower(Body);
nix::ExprAttrs::DynamicAttrDef DDef(NameExpr, ValueExpr, Attr->Range.Begin);
Expand Down Expand Up @@ -281,15 +296,20 @@ void ExprAttrsBuilder::addAttribute(const syntax::Attribute &A) {
} else {
// create a new builder and select to it.
S->Nested[Sym] = std::make_unique<ExprAttrsBuilder>(
LW, /*Recursive=*/false, Name->Range);
LW, Name->Range, /*Recursive=*/false, IsLet);
S->Fields[Sym] = Name;
S = S->Nested[Sym].get();
}
break; // case Node::NK_Identifier:
}
default: {
if (IsLet) {
// reject dynamic attrs in let expression
LW.Diags.emplace_back(mkLetDynamicDiag(Name->Range));
return;
}
DynamicNested[Name] = std::make_unique<ExprAttrsBuilder>(
LW, /*Recursive=*/false, Name->Range);
LW, Name->Range, /*Recursive=*/false, IsLet);
S = DynamicNested[Name].get();
}
}
Expand Down Expand Up @@ -389,7 +409,8 @@ nix::Expr *Lowering::lower(const syntax::Node *Root) {
case Node::NK_AttrSet: {
const auto *AttrSet = dynamic_cast<const syntax::AttrSet *>(Root);
assert(AttrSet->AttrBinds && "null AttrBinds of the AttrSet!");
ExprAttrsBuilder Builder(*this, AttrSet->Recursive, AttrSet->Range);
ExprAttrsBuilder Builder(*this, AttrSet->Range, AttrSet->Recursive,
/*IsLet=*/false);
Builder.addBinds(*AttrSet->AttrBinds);
return Builder.finish();
}
Expand All @@ -399,6 +420,22 @@ nix::Expr *Lowering::lower(const syntax::Node *Root) {
Ctx.Pool.record(NixInt);
return NixInt;
}
case Node::NK_Let: {
const auto *Let = dynamic_cast<const syntax::Let *>(Root);
ExprAttrsBuilder Builder(*this, Let->Binds->Range, /*Recursive=*/true,
/*IsLet=*/true);
Builder.addBinds(*Let->Binds);
nix::ExprAttrs *Attrs = Builder.finish();
nix::Expr *Body = lower(Let->Body);

// special work for let expression, the expression there is implictly
// recursive, and we should not mark `rec` to the evaluator, because the
// official parser does not do this also.
Attrs->recursive = false;

auto *Ret = new nix::ExprLet(Attrs, Body);
return Ret;
}
}

return nullptr;
Expand Down
8 changes: 8 additions & 0 deletions nixd/tools/nixd-lint/test/let-dynamic.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# RUN: nixd-lint %s | FileCheck %s

# CHECK: dynamic attributes are not allowed in let ... in ... expression
let
${1} = 3;
a = 1;
in
1
9 changes: 9 additions & 0 deletions nixd/tools/nixd-lint/test/let-simple.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# RUN: nixd-lint -dump-nix-ast %s | FileCheck %s

# CHECK: ExprLet: (let x = 1; y = 2; z = 3; in 1)
let
x = 1;
y = 2;
z = 3;
in
1

0 comments on commit 572ca09

Please sign in to comment.