Difference between revisions of "Brush Up Language Features"

From D Wiki
Jump to: navigation, search
(Consider narrowing conversions for literal expressions)
(Relation: Category:Proposals)
 
(2 intermediate revisions by 2 users not shown)
Line 155: Line 155:
 
However, I think that inferring context-ness in general case is mostly impossible.
 
However, I think that inferring context-ness in general case is mostly impossible.
 
A particular problem against the context inference is the deterministic mangling scheme for the local symbols of the inferred function.
 
A particular problem against the context inference is the deterministic mangling scheme for the local symbols of the inferred function.
(Related: "Do not mangle context-ness of parent lambdas")
+
(Related: [[#Do_not_mangle_context-ness_of_parent_lambdas|"Do not mangle context-ness of parent lambdas"]])
  
 
A small idea to avoid problems is:
 
A small idea to avoid problems is:
Line 195: Line 195:
 
  auto a1 = true ? [1L,2] : [3,4];  // should work
 
  auto a1 = true ? [1L,2] : [3,4];  // should work
 
  auto a2 = true ? [3,4] : [1L,2];  // should work
 
  auto a2 = true ? [3,4] : [1L,2];  // should work
 +
 +
== DIPxx - New purity definition ==
 +
 +
=== Abstract ===
 +
 +
D has `pure` function attribute. And all pure functions are categorized to one of three purity - weak, constant, and strong purity.
 +
However, the definition for the three purity level is still ambiguous a little. This DIP challenges to define it strictly.
 +
 +
=== Define a new concept - "isolate return" ===
 +
 +
When a function takes any indirections via parameters, but the indirections
 +
won't appear in its return value, we call it an "isolate return" function.
 +
 +
TODO: Is there better naming for the concept?
 +
 +
==== Example ====
 +
 +
<syntaxhighlight lang=D>
 +
module sample:
 +
 +
// f has no parameter, so it's definitely an isolate return function.
 +
int* f();
 +
 +
// all paramters copied and has no indirections,
 +
// so f can be isolate return function.
 +
struct Val { int n; }
 +
int* f(int, double, Val)
 +
 +
// The const(char) data won't appear in the return value in normal type system.
 +
// so f can be isolate return function.
 +
// Note that, any unsafe operations not considered in the concept.
 +
char[] f(const(char)[] s);
 +
 +
// The indirection *p may appear in the return value,
 +
// so the return value is not isolated from parameters.
 +
int* f(int* p);
 +
const(int*) f(int* p);  // same, with const conversion
 +
const(int*) f(const(int*) p);
 +
const(int*) f(immutable(int*) p);
 +
 +
// The int[] element of 'a' is implicitly convertible to void[],
 +
// so it's not isolate return.
 +
void[] f(int[][3] a);
 +
 +
// s.ptr may appear as the return value of f, so it's not isolate return.
 +
struct S { int* ptr; }
 +
int* f(S s);
 +
 +
// As its body says, f is not isolate return.
 +
struct T { int[] arr; }
 +
const(T) f(S s) { T t = T(s.ptr[a..b]); return t; }
 +
 +
void test()
 +
{
 +
    int x;
 +
    int* f() { return &x; }
 +
 +
    // the returned pointer may point the context via hidden pointer.
 +
    // so all nested functions are not isolate return.
 +
    auto p  = f();
 +
}
 +
</syntaxhighlight>
 +
 +
=== New definition of purity levels ===
 +
 +
By using the "isolate return" concept, we can update the purity level definition.
 +
 +
1. If a pure function takes mutable indirection via parameter, it's deduced to weak purity.
 +
 +
<syntaxhighlight lang=D>
 +
// functioin may modify mutable referenced data
 +
pure int foo(int* p);
 +
</syntaxhighlight>
 +
 +
2. If a pure function is an isolate return function, or all taking indirections are immutable, it's deduced to string purity.
 +
 +
<syntaxhighlight lang=D>
 +
// takes no indirection
 +
pure int foo(int);
 +
 +
// takes const indirection but it doesn't apper in return value
 +
pure int foo(const(int*) p);
 +
 +
// takes immutable indirection but it doesn't appear in return value
 +
pure int foo(immutable(int)* p);
 +
 +
// takes immutable indirection and it may appear in return value
 +
pure const(int*) foo(immutable(int)* p);
 +
</syntaxhighlight>
 +
 +
3. If a pure function is neither weak or string purity, it's deduced to constant purity.
 +
 +
<syntaxhighlight lang=D>
 +
// takes const indirection and it may appear in return value
 +
pure const(int*) foo(const(int*) p);
 +
</syntaxhighlight>
 +
 +
Note that, an isolate return function is not always return unique object. For example:
 +
 +
<syntaxhighlight lang=D>
 +
module test:
 +
int g;
 +
 +
// f1 is an isolate return function, but its returned is not unique.
 +
int* f1() { return &g; }
 +
 +
void test()
 +
{
 +
    int x;
 +
 +
    // f2 is an isolate return function, but its returned is not unique.
 +
    int* f2() { return &x; }
 +
}
 +
</syntaxhighlight>
 +
 +
=== One more idea - caller side purity deduction ===
 +
 +
Even if the called pure function is deduced to constant purity,
 +
it could be handled as a strong purity call when all funciton arguments
 +
are immutable.
 +
 +
==== Example ====
 +
 +
<syntaxhighlight lang=D>
 +
// constant purity function
 +
pure const(int*) foo(const(int*) p);
 +
 +
void test(int* mp, immutable int *ip)
 +
{
 +
    foo(mp);    // const purity call
 +
    foo(ip);    // can be deduced to strong purity call.
 +
}
 +
</syntaxhighlight>
 +
 +
=== Relation ===
 +
 +
A good article written by David Nadlinger:
 +
http://klickverbot.at/blog/2012/05/purity-in-d/
 +
 +
 +
[[Category:Proposals]]

Latest revision as of 09:20, 17 February 2018

Mangling Scheme

Consistently stop encoding return type of parent functions

Implemented: Issue 12352 - Consistently stop encoding return type of parent functions (commit)

Currently, for function local symbols, the return types of their parent functions are normally mangled into the name.

module test;
int foo()
{
    void bar() {
        struct S {}
        pragma(msg, S.mangleof);
        // S 4test 3fooFZi 3barMFZv 1S
        //               |        |
        //      int of foo        |
        //              void of bar
    }
    return 0;
}

But, for voldemort types, we already has an exception of the rule.

module test;
auto foo()
{
    auto bar() {
        struct S {}
        pragma(msg, S.mangleof);
        //         S 4test 3fooFZ 3barMFZ 1S
        //                      |       |
        // no return type for foo       |
        //         no return type for bar
        return S();
    }
    return 0;
}

The change was introduced to fix Issue 8847. https://d.puremagic.com/issues/show_bug.cgi?id=8847

In D, functions cannot be overloaded based on the return types.

void foo() {}
int foo() {}   // wrong overloading of foo

So the return type mangling is essentially redundant.

module test;
void foo()    { struct S {}  pragma(msg, "1: ", S.mangleof); }
void foo(int) { struct S {}  pragma(msg, "2: ", S.mangleof); }

Current result;

1: S4test3fooFZv1S
2: S4test3fooFiZv1S

Modified result:

1: S4test3fooFZ1S
2: S4test3fooFiZ1S
// --> The two local symbols S still have unique mangled names.

Do not mangle context-ness of parent lambdas

Lambdas are the only one element of D language which have context-inference.

void main()
{
    int x;
    auto fp = (){ return 1; };
    auto dg = (){ return x; };
    static assert(!is(typeof(fp) == delegate));
    static assert( is(typeof(dg) == delegate));
}

But the context-ness is normally mangled into the name:

http://dlang.org/abi

MangledName:
    _D QualifiedName Type
    _D QualifiedName M Type

And has an undeterministic case for lambda local symbols:

module test;
void main()
{
    int x;
    auto y = (){   // __lambda1
        struct S {}
        pragma(msg, S.mangleof);
        //      S 4test 4mainFZv 9__lambda1MFZ 1S
        // or:  S 4test 4mainFZv 9__lambda1FZ  1S  ?
        //                                 |
        // the context-ness of __lambda1 is not yet determined.

        version(A) return 1;
        else       return x;
    };
}

To avoid the ambiguity, I think we should add a special mangling rule for lambdas:

module test;
void main()
{
    int x;
    auto y = (){   // __lambda1
        struct S {}
        pragma(msg, S.mangleof);
        // S 4test 4mainFZv 9__lambda1 1S
        //                            |
        // stop mangling of the context, parameters, and return type

        version(A) return 1;
        else       return x;
    };
}

Today, most of lambdas have unique names in the defined scope.

void main()
{
    pragma(msg, __traits(identifier, {}));  // __lambda1
    pragma(msg, __traits(identifier, {}));  // __lambda2
    // in 'main' function, unique numbers are distributed.
}

The proposed rule will work relying on the lambdas unique names.

Nested Symbols

Static alias parameter

void foo(alias sym)() { ... }

void main()
{
    static int x;
    int y;
    static class C { int z; }
    pragma(msg, typeof(&foo!x));     // the instantiated function will be global function
    pragma(msg, typeof(&foo!y));     // the instantiated function will be a local function of 'main'
    pragma(msg, typeof(&foo!(C.z))); // the instantiated function will be a member function of 'C'
}

The behavior is necessary if foo will access to sym in runtime. But, if sym is only needed for the compile-time calculation, foo!y and foo!(C.z) will cause need 'this' for ... error in some cases.

Issue 11946 - "need 'this' to access member" when passing field to template parameter https://d.puremagic.com/issues/show_bug.cgi?id=11946

However, I think that inferring context-ness in general case is mostly impossible. A particular problem against the context inference is the deterministic mangling scheme for the local symbols of the inferred function. (Related: "Do not mangle context-ness of parent lambdas")

A small idea to avoid problems is:

void bar(static alias sym)() { ... }

void main()
{
    static int x;
    int y;
    static class C { int z; }
    pragma(msg, typeof(&bar!x));     // the instantiated function will be global function
    pragma(msg, typeof(&bar!y));     // the instantiated function will be global function
    pragma(msg, typeof(&bar!(C.z))); // the instantiated function will be global function
    // In all cases, bar will ignore the context of the symbol that passed to sym
}

IFTI

Consider narrowing conversions for literal expressions

Implemented: Issue 12290 - IFTI should consider implicit conversions of the literal arguments (commit)

It was necessary to change AA.get() method to the UFCS function. In 2.066, some implementation flaw had been remained. Those will be fixed in 2.067 release.

void foo(E)(E[], E) {}

void main()
{
    short[] arr;
    foo(arr, 1);
    // Current:  E = common-type-of(short, int)       => no match
    // Possible: E = common-type-of(short, typeof(1)) => E = short
}

Related issue: improve common type calculation

auto a1 = true ? [1L,2] : [3,4];  // should work
auto a2 = true ? [3,4] : [1L,2];  // should work

DIPxx - New purity definition

Abstract

D has `pure` function attribute. And all pure functions are categorized to one of three purity - weak, constant, and strong purity. However, the definition for the three purity level is still ambiguous a little. This DIP challenges to define it strictly.

Define a new concept - "isolate return"

When a function takes any indirections via parameters, but the indirections won't appear in its return value, we call it an "isolate return" function.

TODO: Is there better naming for the concept?

Example

module sample:

// f has no parameter, so it's definitely an isolate return function.
int* f();

// all paramters copied and has no indirections,
// so f can be isolate return function.
struct Val { int n; }
int* f(int, double, Val)

// The const(char) data won't appear in the return value in normal type system.
// so f can be isolate return function.
// Note that, any unsafe operations not considered in the concept.
char[] f(const(char)[] s);

// The indirection *p may appear in the return value,
// so the return value is not isolated from parameters.
int* f(int* p);
const(int*) f(int* p);  // same, with const conversion
const(int*) f(const(int*) p);
const(int*) f(immutable(int*) p);

// The int[] element of 'a' is implicitly convertible to void[],
// so it's not isolate return.
void[] f(int[][3] a);

// s.ptr may appear as the return value of f, so it's not isolate return.
struct S { int* ptr; }
int* f(S s);

// As its body says, f is not isolate return.
struct T { int[] arr; }
const(T) f(S s) { T t = T(s.ptr[a..b]); return t; }

void test()
{
    int x;
    int* f() { return &x; }

    // the returned pointer may point the context via hidden pointer.
    // so all nested functions are not isolate return.
    auto p  = f();
}

New definition of purity levels

By using the "isolate return" concept, we can update the purity level definition.

1. If a pure function takes mutable indirection via parameter, it's deduced to weak purity.

// functioin may modify mutable referenced data
pure int foo(int* p);

2. If a pure function is an isolate return function, or all taking indirections are immutable, it's deduced to string purity.

// takes no indirection
pure int foo(int);

// takes const indirection but it doesn't apper in return value
pure int foo(const(int*) p);

// takes immutable indirection but it doesn't appear in return value
pure int foo(immutable(int)* p);

// takes immutable indirection and it may appear in return value
pure const(int*) foo(immutable(int)* p);

3. If a pure function is neither weak or string purity, it's deduced to constant purity.

// takes const indirection and it may appear in return value
pure const(int*) foo(const(int*) p);

Note that, an isolate return function is not always return unique object. For example:

module test:
int g;

// f1 is an isolate return function, but its returned is not unique.
int* f1() { return &g; }

void test()
{
    int x;

    // f2 is an isolate return function, but its returned is not unique.
    int* f2() { return &x; }
}

One more idea - caller side purity deduction

Even if the called pure function is deduced to constant purity, it could be handled as a strong purity call when all funciton arguments are immutable.

Example

// constant purity function
pure const(int*) foo(const(int*) p);

void test(int* mp, immutable int *ip)
{
    foo(mp);    // const purity call
    foo(ip);    // can be deduced to strong purity call.
}

Relation

A good article written by David Nadlinger: http://klickverbot.at/blog/2012/05/purity-in-d/