Skip to content

Conversation

quickfur
Copy link
Member

data = 0UL;
sign = false;
assert(isZero());
return;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to just initialize a zero BigInt instead of throwing or asserting, as I didn't want to introduce possible breakage in nothrow code, and the original bug report was complaining about array overrun in -release (no-assert) mode.

@kyllingstad
Copy link
Contributor

In my opinion, it would have been better if the constructor threw, but I see your point about not wanting to break nothrow code. This is a drawback of deduced function attributes that I haven't thought of before. Interestingly, it seems that someone have already planned to make the constructor throw in some cases, cf. the comment a few lines up.

@kyllingstad
Copy link
Contributor

Also, if we define BigInt("") to be zero I guess it should be documented.

@quickfur
Copy link
Member Author

I noticed the comment too, but wasn't sure whether to actually make the constructor throwing. It does date from quite a few years back, and things have changed since.

Anyway, updated the ddoc comment. I changed it from /// style to /** */ style because for some strange reason dmd git HEAD seems to treat every line in /// style as a separate paragraph, whereas this particular comment really would look better with multiple sentences combined in a single paragraph.

@GassaFM
Copy link
Contributor

GassaFM commented Feb 17, 2016

I have little experience with nothrow, so I'd definitely make it throw.
What happens if I call BigInt ("ONE"), or BigInt ("qwerty"), does it not throw anyway?..

I see BigInt (string) as a counterpart to std.conv.to !(int) (string), which throws if something is wrong, and rightfully so.

Moreover, if the constructor throws, an important semantic invariant like text (BigInt (s)) == s will hold.
Hmm, or will it additionally convert "-0" to "0"?

@GassaFM
Copy link
Contributor

GassaFM commented Feb 17, 2016

Well, got my hands on a D compiler now to answer my own questions.

  1. auto a = BigInt ("ONE"); throws:
    std.conv.ConvException@std\internal\math\biguintcore.d(1606): invalid digit
  2. -0 is silently converted to 0, which may be a good thing for a general-purpose library.

@GassaFM
Copy link
Contributor

GassaFM commented Feb 17, 2016

Point 1 shows that the constructor is not nothrow anyway, so this is not a concern.

As to point 2, it already provides an undocumented exception to the semantic invariant.
Furthermore, for example, "00" also becomes "0".

I should note that converting "" to "0" is less innocent than converting "-0" or "00" to "0": additionally, it might break some tokenization routines when converting back and forth.

At any rate, I don't see a use case for silently handling "" in a defined way instead of issuing a prominent error.
But if you do, I'm fine with that.

@kyllingstad
Copy link
Contributor

@GassaFM: You are absolutely right, thanks for checking this! It seems that BigUint.fromHexString() is nothrow while BigUint.fromDecimalString() throws. Here are lines 398–340 of biguintcore.d (and note the comments):

// return true if OK; false if erroneous characters found
// FIXME: actually throws `ConvException` on error.
bool fromDecimalString(const(char)[] s) pure @trusted

It seems that this needs some more cleaning-up. Now I definitely think that the constructor should throw, and it should throw the same exception whether something is an empty string, an invalid hex string or an invalid decimal string.

@GassaFM
Copy link
Contributor

GassaFM commented Feb 17, 2016

From another point of view:

How can a BigInt (range-of-char) constructor be made nothrow or @nogc in principle?
At the core, it should allocate an unknown amount of memory for storage, which I'm sure can result in OutOfMemoryError or something similar.
Supplying a pre-allocated buffer as argument does not immediately help - what if the length is not enough?

@quickfur
Copy link
Member Author

Modified to throw exception on empty string instead.

H. S. Teoh added 2 commits February 17, 2016 10:17
Throws exception on empty string instead.

Update ddoc.

Also throw on invalid digit strings.
@quickfur
Copy link
Member Author

Also changed current code to throw on invalid digit string.

@quickfur
Copy link
Member Author

@GassaFM OutOfMemory errors, AFAIK, are Errors, not Exceptions, so they can be thrown even in nothrow code, and are generally fatal, just as AssertErrors are fatal. But in general, I agree that the ctor can't be nothrow, because there's no sane way of aborting construction of a BigInt when given a garbage input string. Except perhaps setting it to 0, which is probably a bad idea as it will hide bugs.

@quickfur
Copy link
Member Author

Weird autotester failure, doesn't seem related to this PR? Am I missing something here?

@quickfur
Copy link
Member Author

OK, seems to be passing now.

@quickfur
Copy link
Member Author

ping @kyllingstad
Any other comments? Is this ready to merge?

@kyllingstad
Copy link
Contributor

Auto-merge toggled on

@kyllingstad
Copy link
Contributor

Sorry, slipped my mind :-/

kyllingstad added a commit that referenced this pull request Feb 25, 2016
Issue 15678: BigInt("") should not break array bounds
@kyllingstad kyllingstad merged commit 3ad0489 into dlang:master Feb 25, 2016
@quickfur quickfur deleted the bigint_bounds branch February 25, 2016 21:27
@quickfur
Copy link
Member Author

No problem, thanks!

@braddr braddr mentioned this pull request Feb 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants