-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
parser-state: fix attribute merging #11294
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,6 +88,7 @@ struct ParserState | |
void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); | ||
void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); | ||
void addAttr(ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc); | ||
void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def); | ||
Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); | ||
Expr * stripIndentation(const PosIdx pos, | ||
std::vector<std::pair<PosIdx, std::variant<Expr *, StringToken>>> && es); | ||
|
@@ -120,64 +121,29 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const | |
// Checking attrPath validity. | ||
// =========================== | ||
for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { | ||
ExprAttrs * nested; | ||
if (i->symbol) { | ||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); | ||
if (j != attrs->attrs.end()) { | ||
if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { | ||
ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); | ||
if (!attrs2) dupAttr(attrPath, pos, j->second.pos); | ||
attrs = attrs2; | ||
} else | ||
nested = dynamic_cast<ExprAttrs *>(j->second.e); | ||
if (!nested) { | ||
attrPath.erase(i + 1, attrPath.end()); | ||
dupAttr(attrPath, pos, j->second.pos); | ||
} | ||
} else { | ||
ExprAttrs * nested = new ExprAttrs; | ||
nested = new ExprAttrs; | ||
attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); | ||
attrs = nested; | ||
} | ||
} else { | ||
ExprAttrs *nested = new ExprAttrs; | ||
nested = new ExprAttrs; | ||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); | ||
attrs = nested; | ||
} | ||
attrs = nested; | ||
} | ||
// Expr insertion. | ||
// ========================== | ||
if (i->symbol) { | ||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); | ||
if (j != attrs->attrs.end()) { | ||
// This attr path is already defined. However, if both | ||
// e and the expr pointed by the attr path are two attribute sets, | ||
// we want to merge them. | ||
// Otherwise, throw an error. | ||
auto ae = dynamic_cast<ExprAttrs *>(e); | ||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e); | ||
if (jAttrs && ae) { | ||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs) | ||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); | ||
for (auto & ad : ae->attrs) { | ||
auto j2 = jAttrs->attrs.find(ad.first); | ||
if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. | ||
dupAttr(ad.first, j2->second.pos, ad.second.pos); | ||
jAttrs->attrs.emplace(ad.first, ad.second); | ||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { | ||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e); | ||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e); | ||
from.displ += jAttrs->inheritFromExprs->size(); | ||
} | ||
} | ||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end()); | ||
if (ae->inheritFromExprs) { | ||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(), | ||
ae->inheritFromExprs->begin(), ae->inheritFromExprs->end()); | ||
} | ||
} else { | ||
dupAttr(attrPath, pos, j->second.pos); | ||
} | ||
} else { | ||
// This attr path is not defined. Let's create it. | ||
attrs->attrs.emplace(i->symbol, ExprAttrs::AttrDef(e, pos)); | ||
e->setName(i->symbol); | ||
} | ||
addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos)); | ||
} else { | ||
attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); | ||
} | ||
|
@@ -189,6 +155,60 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, const | |
} | ||
} | ||
|
||
/** | ||
* Precondition: attrPath is used for error messages and should already contain | ||
* symbol as its last element. | ||
*/ | ||
inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def) | ||
Mic92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol); | ||
if (j != attrs->attrs.end()) { | ||
// This attr path is already defined. However, if both | ||
// e and the expr pointed by the attr path are two attribute sets, | ||
// we want to merge them. | ||
// Otherwise, throw an error. | ||
auto ae = dynamic_cast<ExprAttrs *>(def.e); | ||
auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e); | ||
|
||
// N.B. In a world in which we are less bound by our past mistakes, we | ||
// would also test that jAttrs and ae are not recursive. The effect of | ||
// not doing so is that any `rec` marker on ae is discarded, and any | ||
// `rec` marker on jAttrs will apply to the attributes in ae. | ||
// See https://github.com/NixOS/nix/issues/9020. | ||
if (jAttrs && ae) { | ||
if (ae->inheritFromExprs && !jAttrs->inheritFromExprs) | ||
jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); | ||
for (auto & ad : ae->attrs) { | ||
if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { | ||
auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e); | ||
auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e); | ||
from.displ += jAttrs->inheritFromExprs->size(); | ||
} | ||
attrPath.emplace_back(AttrName(ad.first)); | ||
addAttr(jAttrs, attrPath, ad.first, std::move(ad.second)); | ||
attrPath.pop_back(); | ||
} | ||
ae->attrs.clear(); | ||
jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), | ||
std::make_move_iterator(ae->dynamicAttrs.begin()), | ||
std::make_move_iterator(ae->dynamicAttrs.end())); | ||
ae->dynamicAttrs.clear(); | ||
if (ae->inheritFromExprs) { | ||
jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(), | ||
std::make_move_iterator(ae->inheritFromExprs->begin()), | ||
std::make_move_iterator(ae->inheritFromExprs->end())); | ||
ae->inheritFromExprs = nullptr; | ||
} | ||
Comment on lines
+191
to
+201
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the end of this block, everything about I'm confused, though, about what good that doc comment stuff can do if it's being attached to an expression that essentially gets dropped on the floor. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe @roberth knows? |
||
} else { | ||
dupAttr(attrPath, def.pos, j->second.pos); | ||
} | ||
} else { | ||
// This attr path is not defined. Let's create it. | ||
attrs->attrs.emplace(symbol, def); | ||
def.e->setName(symbol); | ||
} | ||
} | ||
|
||
inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) | ||
{ | ||
std::sort(formals->formals.begin(), formals->formals.end(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
error: undefined variable 'd' | ||
at /pwd/lang/eval-fail-attrset-merge-drops-later-rec.nix:1:26: | ||
1| { a.b = 1; a = rec { c = d + 2; d = 3; }; }.c | ||
| ^ | ||
2| |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ a.b = 1; a = rec { c = d + 2; d = 3; }; }.c |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
6 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# This is for backwards compatibility, not because we like it. | ||
# See https://github.com/NixOS/nix/issues/9020. | ||
{ a = rec { b = c + 1; d = 2; }; a.c = d + 3; }.a.b |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
error: attribute 'z' already defined at «stdin»:3:16 | ||
at «stdin»:2:3: | ||
1| { | ||
error: attribute 'x.z' already defined at «stdin»:2:3 | ||
at «stdin»:3:16: | ||
2| x.z = 3; | ||
| ^ | ||
3| x = { y = 3; z = 3; }; | ||
| ^ | ||
4| } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
error: attribute 'y' already defined at «stdin»:3:9 | ||
at «stdin»:2:3: | ||
1| { | ||
error: attribute 'x.y.y' already defined at «stdin»:2:3 | ||
at «stdin»:3:9: | ||
2| x.y.y = 3; | ||
| ^ | ||
3| x = { y.y= 3; z = 3; }; | ||
| ^ | ||
4| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if this stuff should be moved to the new overload or if it should be left here; I don't really understand what it's doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What stuff are you referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was referring to the above five lines involving
lexerState.positionToDocComment
; it's the same ‘stuff’ I'm wondering about in #11294 (comment).