Skip to content
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

Set.WHICH confused by spaces #5574

Open
p6rt opened this issue Aug 15, 2016 · 3 comments
Open

Set.WHICH confused by spaces #5574

p6rt opened this issue Aug 15, 2016 · 3 comments
Labels

Comments

@p6rt
Copy link

p6rt commented Aug 15, 2016

Migrated from rt.perl.org#128943 (status was 'new')

Searchable as RT128943$

@p6rt
Copy link
Author

p6rt commented Aug 15, 2016

From zefram@fysh.org

Set.new("a", "b", "c").WHICH
Set|Str|a Str|b Str|c
Set.new("a Str|b", "c").WHICH
Set|Str|a Str|b Str|c
Set.new("a","b Str|c").WHICH
Set|Str|a Str|b Str|c
Set.new("a Str|b Str|c").WHICH
Set|Str|a Str|b Str|c

These four sets are quite distinct (they have three different
cardinalities, for a start), but all get the same .WHICH string.
This happens because Set.WHICH concatenates its elements' .WHICH strings
with space separators, not bothering to quote the elements' .WHICH strings
or otherwise handle spaces in them. Presumably as a consequence, ===
actually says that the sets are identical​:

Set.new("a","b Str|c") === Set.new("a Str|b Str|c")
True

though the sets are actually iterable perfectly well, and === works fine
on sets that do not suffer a coincidence of .WHICH​:

Set.new("a Str|b Str|c").pairs.perl
("a Str|b Str|c" => Bool​::True,).Seq
Set.new("a","b Str|c").pairs.perl
(​:a, "b Str|c" => Bool​::True).Seq
Set.new("a", "b") === Set.new("a", "b")
True
Set.new("a", "b") === Set.new("a", "z")
False

In the above I've made the problem arise by deliberately constructing
strings containing spaces and parts of .WHICH syntax. But it can also
arise more innocently, by using elements of a class that puts spaces
into .WHICH itself, such as Set​:

Set.new(Set.new("a"), "b").WHICH
Set|Set|Str|a Str|b
Set.new(Set.new("a", "b")).WHICH
Set|Set|Str|a Str|b

The discussion we're having on [perl #​128931] about how .WHICH values
are meant to be distinct is relevant to this ticket. If I'm wrong about
the strings being intended to be different for distinct objects, then the
above bug report would be erroneous. But Set.WHICH doesn't incorporate
any part of its elements' .WHICH values other than the strings. So if
the strings are not sufficient to distinguish .WHICH values, Set.WHICH
is erroneous in relying on the strings to be distinct. Furthermore,
Set.WHICH doesn't actually return ObjAt objects, it returns plain strings
as Str objects (which may be a bug in its own right in any case).

-zefram

@p6rt
Copy link
Author

p6rt commented Aug 16, 2016

From zefram@fysh.org

Sketch of how to solve this kind of problem for all syntactically tricky
.WHICH cases​:

class ObjAt {
  method make(*@​items) {
  self.new(@​items.map({
  "(" ~
  (.^isa(ObjAt) ?? .Str !!
  .Str.subst(/<[\(\)\!]>/,
  { sprintf("!\%d", .ord - 32) }, :g))
  ~ ")"
  }).join(" "))
  }
  method WHICH(ObjAt​:D​:) { ObjAt.make(self.WHAT.WHICH, self) }
}

class Mu {
  method WHICH(Mu​:D​:) { ObjAt.make(self.WHAT.WHICH, nqp​::objectid(self)) }
}

class Str {
  method WHICH(Str​:D​:) { ObjAt.make(self.WHAT.WHICH, self) }
}

class Int {
  method WHICH(Int​:D​:) { ObjAt.make(self.WHAT.WHICH, self) }
}

class Pair {
  method WHICH(Pair​:D​:) {
  ObjAt.make(self.WHAT.WHICH, $!key.WHICH, $!value.VAR.WHICH)
  }
}

class Set {
  method WHICH(Set​:D​:) {
  ObjAt.make(self.WHAT.WHICH,
  %!elems.keys.sort.map({ ObjAt.new($_) }))
  }
}

In summary​: centralise the logic for unambiguously representing a
sequence of object identities and strings. Individual classes should
only have to present the right elements that will go into an identity,
not put it together themselves.

-zefram

@p6rt
Copy link
Author

p6rt commented Sep 5, 2017

From zefram@fysh.org

With commit 167a0edf the behaviour of Set.WHICH has changed in a
manner relevant to this ticket. The .WHICHes of the elements are no
longer concatenated with space separators, so spaces no longer confuse
Set.WHICH as they used to. The element .WHICHes are now concatenated
with '\0' separators, so that's the new confusing string. (The use of
the two-character string '\0' (backslash, zero) is probably a mistake
that was meant to be the one-character string "\0" (nul), but "\0" would
serve equally well to confuse Set.WHICH.) The concatenated string is
also hashed, obscuring what's going on.

So, updated demo of Set.WHICH getting confused​:

Set.new("a", "b").WHICH
Set|E223E33FFE3B07DC78B48AFABA0EF4041F3BA975
Set.new("a\\0Str|b").WHICH
Set|E223E33FFE3B07DC78B48AFABA0EF4041F3BA975
Set.new("a", "b").WHICH === Set.new("a\\0Str|b").WHICH
True

The way in which Set.WHICH could be confused by innocently-constructed
elements that are Sets no longer occurs, because of the hashing.

-zefram

@p6rt p6rt added the Bug label Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant