GOAL P0-2: Name(args){...} 로 제네릭 인스턴스를 짓는다
Name{...} 과 binding.Name{...} 만 있고 호출 뒤의 { 를 아무도 받지 않았다.
그래서 타입 인자를 명시한 리터럴이 파싱되지 않았고, 제네릭의 인스턴스는
Self{...} 나 생성자 함수로만 만들 수 있었다.
파서는 호출 뒤의 { 를 struct 리터럴로 받고, 체커는 그것을 타입 표기와 똑같은
resolver 에 넘긴다 -- 같은 철자니 같은 답이어야 한다. { 의 애매함은 이미
Name{...} 에 있던 것과 같고 같은 guard 가 정리한다.
hold.Cell(i32){ v: 7 } 다른 유닛의 제네릭
hold.Handle(Node){ raw: 3 } 본문에서 T 를 안 쓰는 것
Boxed(i32){ v: 9 } 이 유닛의 제네릭
미사용 타입 파라미터는 그대로 둔다. typed handle 이 바로 그 모양이고,
Handle(Node) 와 Handle(Kind) 가 실제로 다른 타입이라는 것은 badphant 가
거부로 고정한다.
233/233, 34/34.
This commit is contained in:
@@ -249,6 +249,17 @@ FeType *check_struct_init(FeCheckerState *s, FeNode *n)
|
|||||||
} else if (n->children) err(s->c,n->loc,"empty enum variant cannot have payload");
|
} else if (n->children) err(s->c,n->loc,"empty enum variant cannot have payload");
|
||||||
n->sem_type=et; return et;
|
n->sem_type=et; return et;
|
||||||
}
|
}
|
||||||
|
if (n->a && n->a->kind==FE_N_CALL) {
|
||||||
|
/* `Name(args){...}` -- the same spelling a type annotation uses, so
|
||||||
|
the same resolver answers it. */
|
||||||
|
int ok=0;
|
||||||
|
t=type_from_expr(s,n->a,&ok);
|
||||||
|
if (!ok || !t || t->kind!=FE_TYPE_STRUCT) {
|
||||||
|
err(s->c,n->a->loc,"unknown struct type");
|
||||||
|
return unknown(s->c);
|
||||||
|
}
|
||||||
|
return check_struct_fields(s,n,t);
|
||||||
|
}
|
||||||
t=fe_type_intern(&s->c->types,n->text ? n->text : "<unknown>");
|
t=fe_type_intern(&s->c->types,n->text ? n->text : "<unknown>");
|
||||||
if (!t || t->kind!=FE_TYPE_STRUCT) { err(s->c,n->loc,"unknown struct type"); return unknown(s->c); }
|
if (!t || t->kind!=FE_TYPE_STRUCT) { err(s->c,n->loc,"unknown struct type"); return unknown(s->c); }
|
||||||
return check_struct_fields(s,n,t);
|
return check_struct_fields(s,n,t);
|
||||||
|
|||||||
+16
-1
@@ -121,7 +121,22 @@ static FeNode *postfix(FeParser *p)
|
|||||||
FeNode *n=primary(p);
|
FeNode *n=primary(p);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
FeToken t=p->current; FeNode *m;
|
FeToken t=p->current; FeNode *m;
|
||||||
if(eat(p,FE_TOK_LPAREN)) { m=toknode(p,FE_N_CALL,t); m->a=n; while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(m,delimited_expr(p));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' after call"); n=m; }
|
if(eat(p,FE_TOK_LPAREN)) { m=toknode(p,FE_N_CALL,t); m->a=n; while(!is(p,FE_TOK_RPAREN)&&!is(p,FE_TOK_EOF)){fe_node_add(m,delimited_expr(p));if(!eat(p,FE_TOK_COMMA))break;} want(p,FE_TOK_RPAREN,"expected ')' after call"); n=m;
|
||||||
|
/* `Name(args){...}` builds an instance of a generic struct. Without
|
||||||
|
this the arguments have nowhere to go and only `Self{...}` or a
|
||||||
|
constructor can name one. Same `{` ambiguity as `Name{...}`
|
||||||
|
above, and the same guard settles it. */
|
||||||
|
if(is(p,FE_TOK_LBRACE) && !p->forbid_struct_literal) {
|
||||||
|
FeNode *s=toknode(p,FE_N_STRUCT_INIT,t); s->a=n; next(p);
|
||||||
|
while(!is(p,FE_TOK_RBRACE)&&!is(p,FE_TOK_EOF)) { FeNode *f;
|
||||||
|
if(!is_name(p)){error(p,"expected field name");recover(p);break;}
|
||||||
|
f=toknode(p,FE_N_FIELD,p->current);next(p);
|
||||||
|
want(p,FE_TOK_COLON,"expected ':' after field");
|
||||||
|
f->a=expr(p,0);fe_node_add(s,f);
|
||||||
|
if(!eat(p,FE_TOK_COMMA))break;
|
||||||
|
}
|
||||||
|
want(p,FE_TOK_RBRACE,"expected '}' in struct literal"); n=s;
|
||||||
|
} }
|
||||||
else if(eat(p,FE_TOK_LBRACKET)) {
|
else if(eat(p,FE_TOK_LBRACKET)) {
|
||||||
m=toknode(p,FE_N_INDEX,t);m->a=n;
|
m=toknode(p,FE_N_INDEX,t);m->a=n;
|
||||||
if(is(p,FE_TOK_DOTDOT)) { m->b=0; m->flags|=FE_NODE_SLICE; } else m->b=delimited_expr(p);
|
if(is(p,FE_TOK_DOTDOT)) { m->b=0; m->flags|=FE_NODE_SLICE; } else m->b=delimited_expr(p);
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# 제네릭 인스턴스 리터럴
|
||||||
|
|
||||||
|
`Name(args){...}` 와 `binding.Name(args){...}` 로 제네릭의 인스턴스를 짓는다.
|
||||||
|
전에는 `Self{...}` 나 생성자 함수로만 만들 수 있었다.
|
||||||
|
|
||||||
|
`Handle(T)` 는 `T` 를 본문에서 쓰지 않는다 -- typed handle 의 자연스러운 모양이고,
|
||||||
|
`Handle(Node)` 와 `Handle(Kind)` 를 갈라놓는 것 말고는 하는 일이 없다. 그 둘이
|
||||||
|
실제로 다른 타입이라는 것은 `generic/badphant.fe` 가 거부로 고정한다.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
unit hold;
|
||||||
|
|
||||||
|
// Fields are `pub` so another unit can write the literal directly. A generic
|
||||||
|
// with a `pub` field is the only way to reach `binding.Name(args){...}`.
|
||||||
|
pub struct Cell(T) {
|
||||||
|
pub v: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// `T` is never used in the body. That is the natural shape of a typed handle:
|
||||||
|
// the parameter is there to keep `Handle(Node)` and `Handle(Type)` apart, not
|
||||||
|
// to describe any storage.
|
||||||
|
pub struct Handle(T) {
|
||||||
|
pub raw: u32,
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// EXIT:0
|
||||||
|
// OUTPUT:cell 7 41
|
||||||
|
// OUTPUT:handle 3 4 sum 7
|
||||||
|
// OUTPUT:local 9
|
||||||
|
unit main;
|
||||||
|
|
||||||
|
import std.io;
|
||||||
|
import hold;
|
||||||
|
|
||||||
|
struct Node { v: i32, }
|
||||||
|
struct Kind { v: i32, }
|
||||||
|
|
||||||
|
// A generic declared here, instantiated with an explicit argument below.
|
||||||
|
struct Boxed(T) {
|
||||||
|
v: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Two instances of the same phantom generic are different nominal types, so
|
||||||
|
// this only accepts one of them.
|
||||||
|
fn only_node(h: hold.Handle(Node)) -> u32 { return h.raw; }
|
||||||
|
fn only_kind(h: hold.Handle(Kind)) -> u32 { return h.raw; }
|
||||||
|
|
||||||
|
fn main() -> i32 {
|
||||||
|
// `binding.Name(args){...}` -- a generic instance from another unit.
|
||||||
|
let a: hold.Cell(i32) = hold.Cell(i32){ v: 7 };
|
||||||
|
let b: hold.Cell(u8) = hold.Cell(u8){ v: 41 };
|
||||||
|
@print("cell {} {}\n", a.v, b.v);
|
||||||
|
|
||||||
|
let n: hold.Handle(Node) = hold.Handle(Node){ raw: 3 };
|
||||||
|
let k: hold.Handle(Kind) = hold.Handle(Kind){ raw: 4 };
|
||||||
|
@print("handle {} {} sum {}\n", only_node(n), only_kind(k),
|
||||||
|
only_node(n) + only_kind(k));
|
||||||
|
|
||||||
|
// `Name(args){...}` -- a generic declared in this unit.
|
||||||
|
let c: Boxed(i32) = Boxed(i32){ v: 9 };
|
||||||
|
@print("local {}\n", c.v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// ERROR:14:argument type mismatch
|
||||||
|
unit badphant;
|
||||||
|
|
||||||
|
// A type parameter that the body never mentions still tells two instances
|
||||||
|
// apart. `Handle(Node)` and `Handle(Kind)` are different nominal types.
|
||||||
|
struct Handle(T) { raw: u32, }
|
||||||
|
|
||||||
|
struct Node { v: i32, }
|
||||||
|
struct Kind { v: i32, }
|
||||||
|
|
||||||
|
fn only_node(h: Handle(Node)) -> u32 { return h.raw; }
|
||||||
|
|
||||||
|
fn bad() -> u32 {
|
||||||
|
return only_node(Handle(Kind){ raw: 1 });
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
unit okphant;
|
||||||
|
|
||||||
|
// The parameter is used nowhere in the body. That is legal, and it is the
|
||||||
|
// natural shape of a typed handle.
|
||||||
|
struct Handle(T) { raw: u32, }
|
||||||
|
|
||||||
|
struct Node { v: i32, }
|
||||||
|
struct Kind { v: i32, }
|
||||||
|
|
||||||
|
fn only_node(h: Handle(Node)) -> u32 { return h.raw; }
|
||||||
|
fn only_kind(h: Handle(Kind)) -> u32 { return h.raw; }
|
||||||
|
|
||||||
|
fn ok() -> u32 {
|
||||||
|
return only_node(Handle(Node){ raw: 1 }) +
|
||||||
|
only_kind(Handle(Kind){ raw: 2 });
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user