Repository navigation
Conversation
4bc8c18 to
6211dad
Compare
cjdb
left a comment
There was a problem hiding this comment.
Thanks for implementing this. I have a few comments, but I'm confident we'll get it through in the next few days.
Additionally, would you mind confirming whether this was hand-written, or if an agent was involved in the development process, please?
| if (self.Size() != other.Size()) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
We can also short-circuit on self.ptr == other.ptr.
There was a problem hiding this comment.
I investigated adding if (self.ptr == other.ptr) for the fast-path, but in Carbon currently, pointer types (T* / Char*) do not implement EqWith, and there are no pointer comparison built-ins (pointer.eq / pointer.neq) in toolchain/sem_ir/builtin_function_kind.def. Attempting self.ptr == other.ptr results in:
error: cannot access member of interface `EqWith(char*)` in type `char*` that does not implement that interface [MissingImplInMemberAccess]
Would you prefer to keep this PR scoped strictly to the prelude String equality loop, or would you like a separate precursor PR adding pointer.eq built-ins and impl forall [T: type] T* as EqWith(Self) first?
There was a problem hiding this comment.
That's fine, let's leave it as-is for now.
| var i: i64 = 0; | ||
| while (i as u64 < self.Size()) { |
There was a problem hiding this comment.
| var i: i64 = 0; | |
| while (i as u64 < self.Size()) { | |
| let indices: IntRange(u64) = IntRange(i64).Make(0, self.Size()); | |
| for (i: i64 in indices) { |
This depends on #7614, but I think this will be cleaner.
There was a problem hiding this comment.
Still waiting on this change, I think?
| srcs = ["hello_world.carbon"], | ||
| ) | ||
|
|
||
| carbon_binary( |
There was a problem hiding this comment.
Can you move your test to testing/core instead, please? You'll need to create that directory, but we should have a dedicated place for testing the core library.
| // Test 1: Identical strings | ||
| var s1: Core.String = "hello"; | ||
| var s2: Core.String = "hello"; | ||
| if (not (s1 == s2)) { return 1; } |
There was a problem hiding this comment.
I'd prefer our tests print help messages, instead of returning an error code. You won't have robust infrastructure, but producing output that's similar to GoogleTest or Catch2 output would be great.
It will probably be worthwhile setting up a rudimentary test infrastructure in a separate PR. Happy to discuss ideas over in Discord.
|
|
||
| namespace Carbon::Lex { | ||
|
|
||
| class StringLiteral { |
There was a problem hiding this comment.
Why are the changes in this file relevant to this PR?
There was a problem hiding this comment.
I made a bit of a mess here, my apologies. I intend to separate the logic. I allowed my agent to not only leak in regressions but I also mixed commits(my fault for multi-tasking). I will make relevant corrections and remove the irrelevant bits.
| impl String as EqWith(String) { | ||
| fn Equal(self: String, other: String) -> bool { |
There was a problem hiding this comment.
IMHO we should eventually make this a built-in function, just like we did with IndexWith in #6329. Can we add a TODO comment for that?
There was a problem hiding this comment.
I think it would be best to open a question for the leads to address which Core types should have toolchain-implemented functionality before adding TODOs.
Implement EqWith(String) for Core.String in core/prelude/types/string.carbon. The equality comparison short-circuits on size inequality and compares character elements in an index loop. Add testing/core/string_eq_test.carbon to verify equality and inequality operators across identical, distinct, length-mismatched, and empty strings with structured test reporting. Fixes: carbon-language#7610
9cfb801 to
1f84164
Compare
cjdb
left a comment
There was a problem hiding this comment.
Only the while loop to go, then should be LGTM!
| if (self.Size() != other.Size()) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
That's fine, let's leave it as-is for now.
| var i: i64 = 0; | ||
| while (i as u64 < self.Size()) { |
|
Swapping with @dwblaikie so you get an approval after your update lands (I won't be able to press the button for a couple of days.) |
| return false; | ||
| } | ||
| var i: i64 = 0; | ||
| while ((i as u64) < self.Size()) { |
There was a problem hiding this comment.
| while ((i as u64) < self.Size()) { | |
| let indices: IntRange(u64) = IntRange(i64).Make(0, self.Size()); | |
| for (i: i64 in indices) { |
Previously suggested/still waiting on this (the comment got detached from the code due to other changes)
There was a problem hiding this comment.
Both changes are in, and the suggestion surfaced two adaptations worth explaining.
On the spelling, IntRange is parameterized by a bit-width literal (class IntRange(N: IntLiteral)), so IntRange(i64) doesn't type-check ("cannot implicitly convert expression of type type to Core.IntLiteral"), and Size() returns i64 since #7614. I used for (i: i64 in IntRange(64).Make(0, self.Size())).
On the imports, string.carbon needs both prelude/range (for IntRange) and prelude/iterate (for the for desugaring's Core.Iterate lookup, since prelude files get no implicit prelude import), and each closed a library cycle (types/string → range → iterate → types → types/string, rejected with ImportCycleDetected) because iterate export-imported the whole prelude/types umbrella. I narrowed that to the three libraries iterate actually uses (types/int, types/int_literal, types/optional), which changes nothing for consumers (prelude re-exports prelude/types itself and range already imports the trio directly) and unblocks iteration for any future prelude type library. The goldens pinning iterate.carbon line numbers move by +2.
I verified against the 2026.08.28 nightly toolchain with the modified prelude. The cycle reproduces without the iterate change, and with it the test binary builds and all checks pass. Happy to split the iterate change into a precursor PR if you'd rather review it separately.
`prelude/iterate` export-imported the whole `prelude/types` umbrella, which re-exports `prelude/types/string`. Any prelude type library wanting iteration (via `prelude/range` or `prelude/iterate`) therefore closed a library cycle onto itself, which the toolchain rejects with ImportCycleDetected across the entire prelude. Iterate uses only Int, IntLiteral, and Optional from the umbrella, so import exactly those three libraries. Consumer visibility is unchanged: `prelude` re-exports `prelude/types` itself, and `prelude/range` already imports the three directly. Golden files pinning iterate.carbon line numbers shift by +2 (range_for.carbon diagnostics, array/iterate.carbon debug metadata). Assisted-by: Claude (Fable 5)
Replace the manual index while-loop in String's EqWith impl with `for (i: i64 in IntRange(64).Make(0, self.Size()))`, as requested in review. IntRange(64) rather than the suggested IntRange(i64): IntRange is parameterized by a bit-width IntLiteral, not a type, and String.Size() returns i64 since carbon-language#7614. This also retires the stale `(i as u64)` cast from before carbon-language#7614, trims the test comment claiming a pointer-identity fast path that does not exist, and wraps one over-long test line. Assisted-by: Claude (Fable 5)
e2660f1 to
f722873
Compare
dwblaikie
left a comment
There was a problem hiding this comment.
Thanks! & thanks for splitting those review changes up - made it easier to review the import change separately from the IntRange change.
Implement
EqWith(String)forCore.Stringincore/prelude/types/string.carbonto support equality (==) and inequality (!=) comparison operators. The equality comparison short-circuits on size inequality and compares character elements sequentially in an index loop.Add
testing/core/string_eq_test.carbonto verify equality and inequality operators across identical, distinct, length-mismatched, and empty strings with structured test reporting.Fixes #7610
To satisfy the reviewed for-loop form,
prelude/iteratenow imports the specific type libraries it uses (types/int,types/int_literal,types/optional) instead of the wholeprelude/typesumbrella. The umbrella re-exportsprelude/types/string, soString's new imports ofprelude/rangeandprelude/iteratewould otherwise close a library import cycle (ImportCycleDetected). Consumer visibility is unchanged, and the golden files pinningiterate.carbonline numbers shift by +2. The equality loop is nowfor (i: i64 in IntRange(64).Make(0, self.Size())), usingIntRange(64)becauseIntRangetakes a bit-widthIntLiteralparameter.