Recent Posts

Pages: 1 2 [3] 4 5 ... 10
21
Ideas / Re: Macro-system design
« Last post by bas on February 28, 2019, 08:26:30 AM »
I agree with https://npf.io/2018/09/go2-contracts-go-too-far. It's too complex IMHO.

When you want an argument to be of a specific type in C, don't you just say myfunc(Foo a), instead of requiring a to be
of type Foo later. The only thing you can't do that way is force multiple arguments to be of some Type 'T' that just supports
the required operations in the macro. Something Generics may solve. Also the typeof() may be needed as in your example.

I want to learn Nim macros more before building my opinion about them.. Today is Nim day ;)
22
Ideas / Re: Explicit non-null-ness
« Last post by bas on February 28, 2019, 08:03:49 AM »
In C++ a reference is also a pointer that's 'guaranteed' to be non-null. This can work because a function in C++ can return
an object itself that's turned into a reference. I don't see how this could work in C because C doesn't have copy-constructors etc.
23
General Discussion / Unsigned conversions
« Last post by lerno on February 20, 2019, 10:51:52 PM »
In C, comparing a i32 with an u32 will do the somewhat counterintuitive thing of promoting the i32 to u32.

This leads to the somewhat confusing situation that the following C code:

Code: [Select]
  int a = -1;
  unsigned int b = 1;
  unsigned short c = 1;
  if (a < b) printf("Less!\n");
  if (a > b) printf("More!\n");
  if (a < c) printf("Less short!\n");
  if (a > c) printf("More short!\n");

Prints:

More!
Less short!

The following rules might help:

1. The comparison operations < > <= >= are not allowed between signed and unsigned where the promotion would be to unsigned (that is, where the unsigned type is has greater or equal bit size of the signed). So i32 < u16 is fine, but not i32 < u32 or i16 < u32.

2. >= 0 is always an error for unsigned numbers. This would prevent the following bug: for (unsigned i = size; i >= 0; i--). The analyser should detect this condition as always being true and creating an *error* for it.

A more drastic change that could be considered instead of (1) would be to always do sign promotion whenever there is a comparison with signed and unsigned values.

So !i32 < u64) would cast both sides to i64. The downside of that (or any similar scheme) is that it differs from C in a critical, rarely learned part of the language. It feels dangerous. Prohibiting conversions feel more "safe". Note that comparisons (except for ==) is where the conversion from signed to unsigned is where the danger is. For example cast<i32>(-1) + cast<u64>(12) will still yield 11 as expected due to the behaviour of unsigned arithmetics.
24
Implementation Details / Re: C2 in C
« Last post by lerno on February 12, 2019, 01:17:55 AM »
My pure C implementation of C2 is moving forward slowly (at https://github.com/lerno/titanos - look at the development branch). It's using LLVM exclusively (no C gen).

I'm focusing on the areas where C2C is weak today, so that would both be LLVM and constant folding.

The compiler uses BigInt for integer constants (and really should do the same for floats, but right now it doesn't). I recently worked on the implicit casts and in order to make sense out of it I've changed a bit from C's implicit casting, mostly following Zig.

To recap C's casting rules:

  • with two operands where at least one is float, upgrade everything to the biggest float (float -> double -> long double)
  • if no float is found, int conversion resumes.
  • If both types are signed or both types are unsigned: promote to the largest of the (eg short -> int -> long etc) (unsigned short -> unsigned int -> unsigned long etc)
  • If they have different sign and the signed version can represent represent all numbers of the unsigned (e.g. u16 - i32), promote to the signed version.
  • If it's not possible to represent it, promote to the unsigned version of the type. (e.g. u32 - i32 => u32, u64 - i32 => u64)

For constant folding I've followed these rules instead:

  • Float conversion as with C
  • Folding an operation with bigint and bit limited int will convert the bigint to that bit size. If it's not possible to convert the constant, a compile time error will occur. E.g. const i8 c = 1; const i32 d = c + 200; is illegal since 200 cannot be converted to i8 without loss.
  • If two non bigints are folded, it's converted to the biggest type (like in C), for signed / unsigned conversion: if the unsigned constant may be contained in the signed type, then the conversion is valid, otherwise it is a compile time error. (e.g. cast<u8>(200) + cast<i8>(1) is a compile time error)
  • In addition, constant overflow is a compile time error. So cast<u8>(200) + cast<u8>(100) would be a compile time error as 300 does not fit in u8.

In order to make unsigned <=> signed conversions I'm thinking of some very simple lossy conversion, like

Code: [Select]
i8 a = -1;
u8 b = @ucast(a); // Bitcast of a
a = @scast(b); // Bitcast of b

Maybe even add a special assignment like (placeholder syntax!):

Code: [Select]
b u= a; // same as b = @ucast(a)
if (b u== a) ...; // same as if (b == @ucast(a)) ...
a s= b; // same as a = @scast(a)
if (b s== a) ...; // same as if (@scast(b) == a) ...
25
Implementation Details / Re: CTC (partial, full, none)
« Last post by lerno on February 12, 2019, 12:47:59 AM »
Of what use is CTC partial? I don't really see it.
26
Ideas / Explicit non-null-ness
« Last post by lerno on February 02, 2019, 12:28:24 PM »
I suggest we reuse & arg to mean a pointer that is guaranteed to be non-null.

Consider the following methods:

Code: [Select]
Foo& foo();
Foo* foo2();
void bar(Foo& f);
void bar2(Foo* f);

Unlike in C++, both Foo& and Foo* are pointer, the former guaranteed to be not null.

Code: [Select]
Foo *f1 = foo(); // Non null to nullable ok
Foo &f2 = foo2(); // Nullable to non null not allowed

A check allows conversion:

Code: [Select]
Foo *f = foo2();
assert(f);
Foo &f2 = f;

Or:

Code: [Select]
Foo *f = foo2();
Foo &f2 = f ? f : foo();

With the elvis operator:

Code: [Select]
Foo &f = foo2() ?: foo();

Using pointer without nullcheck is a warning:

Code: [Select]
Foo* f = foo2();
return f.a; // warn, f may be null.

Solution is adding the assert test, or supress null warning with an attribute

Code: [Select]
Foo *f = foo2();
return f.a @(notnull);
27
Implementation Details / Re: C2 in C
« Last post by lerno on February 01, 2019, 10:37:40 PM »
I know, but the parser is sometimes hardcoded to identify types (or sometimes even just built in types). This should be relaxed in order to allow more compile time evaluation – which in turn opens up for semantic macros.
28
Ideas / Re: Require explicit (un)initialization.
« Last post by lerno on January 31, 2019, 12:43:19 AM »
I like the reuse of void.

In regards to int a; initialized to zero implicitly - I understand the thought that this is counter to whar one usually expects. On the other habd this makes static and local variables behave exactly the same way and it simplifies how one reasons about struct init.

For example, it is more natural to expect Foo f = { .one_field = 1 } to actually zero out all other fields in the struct if we have implicit zeroing.

An alternative would be to disallow int a; completely, requiring it to either be initialized to ”void” or some value. On the other hand, that would then also need to be true for göobals, and I don’t know if this added requirement for statics is something that would be appreciated.
29
Ideas / Re: Require explicit (un)initialization.
« Last post by chqrlie on January 30, 2019, 09:06:15 PM »
I don't like mandatory initialization either, but implicit initialization to the zero value of the type is confusing too. The compiler should just issue an error, not a warning when it detects that a variable is used before intialization. There are border cases where it is difficult, or plain impossible to determine if a variable was initialized or not, requiring explicit initialization in these cases is OK IMHO. This is basically what I get with `-Wall -Werror`.

Regarding a possible syntax to specify uninitialized status, I would suggest:

Code: [Select]
int a = void;  // a not initialized, warning if used before later assignment.
This could be used to tell the compiler that a variable is no longer valid and thus prevent its use in further computations. For example:

Code: [Select]
free(p);
p = void;
return p;   // error: use of an invalid value.

30
Ideas / Require explicit (un)initialization.
« Last post by lerno on January 30, 2019, 06:07:18 PM »
Instead of warning on non-initialized variables and explicitly initializing, consider the following change:

Code: [Select]
int a; // a initialized to 0, as if it was static.
int a = 10; // a initialized to 10
int a = uninitialized; // a not initialized, warning if used before later assignment.

Alternatives to uninitialized:

Code: [Select]
int a = ---;
int a = *;
int a = ?;
Pages: 1 2 [3] 4 5 ... 10