Thread overview
Re: Proposal for design of 'scope' (Was: Re: Opportunities for D)
Jul 10, 2014
H. S. Teoh
Jul 11, 2014
Jacob Carlborg
Jul 11, 2014
H. S. Teoh
Jul 13, 2014
Jacob Carlborg
Jul 13, 2014
H. S. Teoh
Jul 14, 2014
Jacob Carlborg
Jul 14, 2014
H. S. Teoh
Jul 15, 2014
Jacob Carlborg
Jul 15, 2014
H. S. Teoh
Jul 15, 2014
Jacob Carlborg
July 10, 2014
On Thu, Jul 10, 2014 at 01:56:39PM +0200, Jacob Carlborg via Digitalmars-d wrote:
> On 10/07/14 01:57, H. S. Teoh via Digitalmars-d wrote:
> 
> >[...]
> >I'm sure there are plenty of holes in this proposal, so destroy away.
> >;-)
> 
> You should post this in a new thread.

Done.


> I'm wondering if a lot more data can be statically allocated. Then passed by reference to functions taking scope parameters. This should be safe since the parameter is guaranteed to outlive the function call.

Yep. In whatever shape or form we finally implement 'scope', it would allow us to address such issues.


On Thu, Jul 10, 2014 at 01:26:54PM +0000, Wyatt via Digitalmars-d wrote:
> On Wednesday, 9 July 2014 at 23:58:39 UTC, H. S. Teoh via Digitalmars-d wrote:
> >
> >So here's a first stab at refining (and extending) what 'scope'
> >should be:
> >
> In general, I like it, but can scopedness be inferred?  The impression I get from this is we're supposed to manually annotate every scoped everything, which IMO kind of moots the benefits in a broad sense.

I think in some cases it can be inferred. For example, taking the address of a local variable should return an appropriately-scoped pointer type, so that the type system can statically verify that it doesn't leak past the variable's lifetime:

	int* myFunc(int* q) {
		int x;
		auto ptr = &x;	// typeof(ptr) == scoped(int*) with lifetime == x.lifetime
		if (cond)
			return q;	// OK, q has infinite lifetime
		else
			return ptr;	// compile error: cannot convert scoped(int*) to int*
	}

There is an interesting subtlety here, in that local variables themselves are not necessarily scoped, for example:

	class C {}
	C createObj() {
		auto obj = new C;	// obj is a local variable
		return obj;		// but its lifetime can be extended outside the function
	}

Yet addresses of local variables are scoped:

	class C {}
	C* createObj() {
		auto obj = new C;
		return &obj;	// INVALID: this would allow the caller
				// to access a local variable that's
				// gone out of scope
	}

This leads to some further complications, for example:

	class C {
		C* getRef() { return &this; }
	}
	C* func(scope C obj) {
		// In here, obj's lifetime is the body of func

		// But this violates obj's lifetime:
		return obj.getRef();
	}

The problem is, how do we statically prevent this sort of abuse? since the definition of C.getRef may not know anything about func, and so can't possibly return a type whose lifetime is restricted to func, yet that's what's needed to prevent the invalid return of obj.getRef's value outside func.


> If it _cannot_ be inferred (even if imperfectly), then I wonder if it doesn't make more sense to invert the proposed default and require annotation when scope restrictions need to be eased.

The way I see it, scope is really a way for a function to state that it won't keep a persistent reference to the scoped parameter, and so it's OK to pass in, say, a reference to a temporary that will disappear after the function returns. Defaulting to scope will cause far too much breakage, I think, esp. given that code like the following is probably common in the wild:

	class C {}
	C myFunc(C obj) {
		obj.doSomething();
		return obj; // will be rejected if parameters are scoped by default
	}


T

-- 
One disk to rule them all, One disk to find them. One disk to bring them all and in the darkness grind them. In the Land of Redmond where the shadows lie. -- The Silicon Valley Tarot
July 11, 2014
On 10/07/14 20:15, H. S. Teoh via Digitalmars-d wrote:

> 	class C {}
> 	C myFunc(C obj) {
> 		obj.doSomething();
> 		return obj; // will be rejected if parameters are scoped by default
> 	}

Hmm, why wouldn't that work? The scope where you called "myFunc" is guaranteed to outlive "myFunc".

-- 
/Jacob Carlborg
July 11, 2014
On Fri, Jul 11, 2014 at 08:56:10AM +0200, Jacob Carlborg via Digitalmars-d wrote:
> On 10/07/14 20:15, H. S. Teoh via Digitalmars-d wrote:
> 
> >	class C {}
> >	C myFunc(C obj) {
> >		obj.doSomething();
> >		return obj; // will be rejected if parameters are scoped by default
> >	}
> 
> Hmm, why wouldn't that work? The scope where you called "myFunc" is guaranteed to outlive "myFunc".
[...]

Because the scope of the parameter 'obj' is defined to be the scope of myFunc only, according to the current proposal.


T

-- 
What are you when you run out of Monet? Baroque.
July 13, 2014
On 2014-07-11 16:29, H. S. Teoh via Digitalmars-d wrote:

> Because the scope of the parameter 'obj' is defined to be the scope of
> myFunc only, according to the current proposal.

Wouldn't it be possible to define the scope of a parameter to the caller's scope?

-- 
/Jacob Carlborg
July 13, 2014
On Sun, Jul 13, 2014 at 12:07:58PM +0200, Jacob Carlborg via Digitalmars-d wrote:
> On 2014-07-11 16:29, H. S. Teoh via Digitalmars-d wrote:
> 
> >Because the scope of the parameter 'obj' is defined to be the scope of myFunc only, according to the current proposal.
> 
> Wouldn't it be possible to define the scope of a parameter to the caller's scope?
[...]

We could, but how would that help static analysis within the function's body, since the caller's scope is unknown?


T

-- 
Truth, Sir, is a cow which will give [skeptics] no more milk, and so they are gone to milk the bull. -- Sam. Johnson
July 14, 2014
On 13/07/14 16:37, H. S. Teoh via Digitalmars-d wrote:

> We could, but how would that help static analysis within the function's
> body, since the caller's scope is unknown?

Won't the caller's scope always outlive the callee's?

-- 
/Jacob Carlborg
July 14, 2014
On Mon, Jul 14, 2014 at 10:41:10AM +0200, Jacob Carlborg via Digitalmars-d wrote:
> On 13/07/14 16:37, H. S. Teoh via Digitalmars-d wrote:
> 
> >We could, but how would that help static analysis within the function's body, since the caller's scope is unknown?
> 
> Won't the caller's scope always outlive the callee's?
[...]

Yes, but since the extent of this scope is unknown from inside the function body, it doesn't easily lend itself nicely to check things like this:

	int* ptr;
	void func(scope int* arg) {
		ptr = arg; // should this be allowed?
	}

If we only know that 'arg' has a longer lifetime than func, but we don't know how long it is, then we don't know if it has the same lifetime as 'ptr', or less. So it doesn't really let us do useful checks.


T

-- 
"I suspect the best way to deal with procrastination is to put off the procrastination itself until later. I've been meaning to try this, but haven't gotten around to it yet. " -- swr
July 15, 2014
On 15/07/14 01:48, H. S. Teoh via Digitalmars-d wrote:

> Yes, but since the extent of this scope is unknown from inside the
> function body, it doesn't easily lend itself nicely to check things like
> this:
>
> 	int* ptr;
> 	void func(scope int* arg) {
> 		ptr = arg; // should this be allowed?
> 	}
>
> If we only know that 'arg' has a longer lifetime than func, but we don't
> know how long it is, then we don't know if it has the same lifetime as
> 'ptr', or less. So it doesn't really let us do useful checks.

I was thinking that "arg" would have at least the same lifetime as the caller, i.e. the same as "ptr".

-- 
/Jacob Carlborg
July 15, 2014
On Tue, Jul 15, 2014 at 09:19:34AM +0200, Jacob Carlborg via Digitalmars-d wrote:
> On 15/07/14 01:48, H. S. Teoh via Digitalmars-d wrote:
> 
> >Yes, but since the extent of this scope is unknown from inside the function body, it doesn't easily lend itself nicely to check things like this:
> >
> >	int* ptr;
> >	void func(scope int* arg) {
> >		ptr = arg; // should this be allowed?
> >	}
> >
> >If we only know that 'arg' has a longer lifetime than func, but we don't know how long it is, then we don't know if it has the same lifetime as 'ptr', or less. So it doesn't really let us do useful checks.
> 
> I was thinking that "arg" would have at least the same lifetime as the caller, i.e. the same as "ptr".
[...]

But what if 'ptr' is declared in a private binary-only module, and only the signature of 'func' is known? Then what should 'scope' mean to the compiler when 'func' is being called from another module?


T

-- 
ASCII stupid question, getty stupid ANSI.
July 15, 2014
On 2014-07-15 16:58, H. S. Teoh via Digitalmars-d wrote:

> But what if 'ptr' is declared in a private binary-only module, and only
> the signature of 'func' is known? Then what should 'scope' mean to the
> compiler when 'func' is being called from another module?

Hmm, I didn't think of that :(

-- 
/Jacob Carlborg