diff --git a/compile.c b/compile.c
index 33e0ba3..b6fbee8 100644
--- a/compile.c
+++ b/compile.c
@@ -3826,6 +3826,72 @@ compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
     ADD_LABEL(ret, end_label);
 }
 
+static void
+iseq_compile_call(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped, unsigned int flag)
+{
+    enum node_type type = nd_type(node);
+    int line = (int)nd_line(node);
+    /*
+      call:  obj.method(...)
+      fcall: func(...)
+      vcall: func
+    */
+    DECL_ANCHOR(recv);
+    DECL_ANCHOR(args);
+    LABEL *lskip = 0;
+    ID mid = node->nd_mid;
+    VALUE argc;
+    struct rb_call_info_kw_arg *keywords = NULL;
+    const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
+    ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
+
+    INIT_ANCHOR(recv);
+    INIT_ANCHOR(args);
+    /* receiver */
+    if (type == NODE_CALL || type == NODE_QCALL) {
+	COMPILE(recv, "recv", node->nd_recv);
+	if (type == NODE_QCALL) {
+	    lskip = NEW_LABEL(line);
+	    ADD_INSN(recv, line, dup);
+	    ADD_INSNL(recv, line, branchnil, lskip);
+	}
+    }
+    else if (type == NODE_FCALL || type == NODE_VCALL) {
+	ADD_CALL_RECEIVER(recv, line);
+    }
+
+    /* args */
+    if (nd_type(node) != NODE_VCALL) {
+	argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
+    }
+    else {
+	argc = INT2FIX(0);
+    }
+
+    ADD_SEQ(ret, recv);
+    ADD_SEQ(ret, args);
+
+    debugp_param("call args argc", argc);
+    debugp_param("call method", ID2SYM(mid));
+
+    switch (nd_type(node)) {
+      case NODE_VCALL:
+	flag |= VM_CALL_VCALL;
+	/* VCALL is funcall, so fall through */
+      case NODE_FCALL:
+	flag |= VM_CALL_FCALL;
+    }
+
+    ADD_SEND_R(ret, line, mid, argc, parent_block, INT2FIX(flag), keywords);
+
+    if (lskip) {
+	ADD_LABEL(ret, lskip);
+    }
+    if (poped) {
+	ADD_INSN(ret, line, pop);
+    }
+}
+
 /**
   compile each node
 
@@ -5042,130 +5108,11 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
       case NODE_QCALL:
       case NODE_FCALL:
       case NODE_VCALL:{		/* VCALL: variable or call */
-	/*
-	  call:  obj.method(...)
-	  fcall: func(...)
-	  vcall: func
-	*/
-	DECL_ANCHOR(recv);
-	DECL_ANCHOR(args);
-	LABEL *lskip = 0;
-	ID mid = node->nd_mid;
-	VALUE argc;
-	unsigned int flag = 0;
-	struct rb_call_info_kw_arg *keywords = NULL;
-	const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
-	ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
-
-	INIT_ANCHOR(recv);
-	INIT_ANCHOR(args);
-#if SUPPORT_JOKE
-	if (nd_type(node) == NODE_VCALL) {
-	    ID id_bitblt;
-	    ID id_answer;
-
-	    CONST_ID(id_bitblt, "bitblt");
-	    CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
-
-	    if (mid == id_bitblt) {
-		ADD_INSN(ret, line, bitblt);
-		break;
-	    }
-	    else if (mid == id_answer) {
-		ADD_INSN(ret, line, answer);
-		break;
-	    }
-	}
-	/* only joke */
-	{
-	    ID goto_id;
-	    ID label_id;
-
-	    CONST_ID(goto_id, "__goto__");
-	    CONST_ID(label_id, "__label__");
-
-	    if (nd_type(node) == NODE_FCALL &&
-		(mid == goto_id || mid == label_id)) {
-		LABEL *label;
-		st_data_t data;
-		st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
-		ID label_name;
-
-		if (!labels_table) {
-		    labels_table = st_init_numtable();
-		    ISEQ_COMPILE_DATA(iseq)->labels_table = labels_table;
-		}
-		if (nd_type(node->nd_args->nd_head) == NODE_LIT &&
-		    SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
-
-		    label_name = SYM2ID(node->nd_args->nd_head->nd_lit);
-		    if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
-			label = NEW_LABEL(line);
-			label->position = line;
-			st_insert(labels_table, (st_data_t)label_name, (st_data_t)label);
-		    }
-		    else {
-			label = (LABEL *)data;
-		    }
-		}
-		else {
-		    COMPILE_ERROR(ERROR_ARGS "invalid goto/label format");
-		}
-
-
-		if (mid == goto_id) {
-		    ADD_INSNL(ret, line, jump, label);
-		}
-		else {
-		    ADD_LABEL(ret, label);
-		}
-		break;
-	    }
-	}
-#endif
-	/* receiver */
-	if (type == NODE_CALL || type == NODE_QCALL) {
-	    COMPILE(recv, "recv", node->nd_recv);
-	    if (type == NODE_QCALL) {
-		lskip = NEW_LABEL(line);
-		ADD_INSN(recv, line, dup);
-		ADD_INSNL(recv, line, branchnil, lskip);
-	    }
-	}
-	else if (type == NODE_FCALL || type == NODE_VCALL) {
-	    ADD_CALL_RECEIVER(recv, line);
-	}
-
-	/* args */
-	if (nd_type(node) != NODE_VCALL) {
-	    argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
-	}
-	else {
-	    argc = INT2FIX(0);
-	}
-
-	ADD_SEQ(ret, recv);
-	ADD_SEQ(ret, args);
-
-	debugp_param("call args argc", argc);
-	debugp_param("call method", ID2SYM(mid));
-
-	switch (nd_type(node)) {
-	  case NODE_VCALL:
-	    flag |= VM_CALL_VCALL;
-	    /* VCALL is funcall, so fall through */
-	  case NODE_FCALL:
-	    flag |= VM_CALL_FCALL;
-	}
-
-	ADD_SEND_R(ret, line, mid, argc, parent_block, INT2FIX(flag), keywords);
-
-	if (lskip) {
-	    ADD_LABEL(ret, lskip);
-	}
-	if (poped) {
-	    ADD_INSN(ret, line, pop);
-	}
+	iseq_compile_call(iseq, ret, node, poped, 0);
+	break;
+      }
+      case NODE_TAIL:{
+	iseq_compile_call(iseq, ret, node->nd_head, poped, VM_CALL_TAILCALL);
 	break;
       }
       case NODE_SUPER:
diff --git a/defs/keywords b/defs/keywords
index e0d931c..42f2d24 100644
--- a/defs/keywords
+++ b/defs/keywords
@@ -41,7 +41,7 @@ retry, {keyword_retry, keyword_retry}, EXPR_END
 return, {keyword_return, keyword_return}, EXPR_MID
 self, {keyword_self, keyword_self}, EXPR_END
 super, {keyword_super, keyword_super}, EXPR_ARG
-then, {keyword_then, keyword_then}, EXPR_BEG
+then, {keyword_then, modifier_then}, EXPR_BEG
 true, {keyword_true, keyword_true}, EXPR_END
 undef, {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM
 unless, {keyword_unless, modifier_unless}, EXPR_VALUE
diff --git a/defs/lex.c.src b/defs/lex.c.src
index e0d931c..42f2d24 100644
--- a/defs/lex.c.src
+++ b/defs/lex.c.src
@@ -41,7 +41,7 @@ retry, {keyword_retry, keyword_retry}, EXPR_END
 return, {keyword_return, keyword_return}, EXPR_MID
 self, {keyword_self, keyword_self}, EXPR_END
 super, {keyword_super, keyword_super}, EXPR_ARG
-then, {keyword_then, keyword_then}, EXPR_BEG
+then, {keyword_then, modifier_then}, EXPR_BEG
 true, {keyword_true, keyword_true}, EXPR_END
 undef, {keyword_undef, keyword_undef}, EXPR_FNAME|EXPR_FITEM
 unless, {keyword_unless, modifier_unless}, EXPR_VALUE
diff --git a/lex.c.blt b/lex.c.blt
index 7d8dd5b..dfae9d7 100644
--- a/lex.c.blt
+++ b/lex.c.blt
@@ -213,7 +213,7 @@ rb_reserved_word (str, len)
 #line 27 "defs/keywords"
       {(int)offsetof(struct stringpool_t, stringpool_str12), {keyword_end, keyword_end}, EXPR_END},
 #line 44 "defs/keywords"
-      {(int)offsetof(struct stringpool_t, stringpool_str13), {keyword_then, keyword_then}, EXPR_BEG},
+      {(int)offsetof(struct stringpool_t, stringpool_str13), {keyword_then, modifier_then}, EXPR_BEG},
 #line 36 "defs/keywords"
       {(int)offsetof(struct stringpool_t, stringpool_str14), {keyword_not, keyword_not}, EXPR_ARG},
 #line 29 "defs/keywords"
diff --git a/node.h b/node.h
index 8d2ea53..c2f5cc2 100644
--- a/node.h
+++ b/node.h
@@ -98,6 +98,8 @@ enum node_type {
 #define NODE_VCALL       NODE_VCALL
     NODE_QCALL,
 #define NODE_QCALL       NODE_QCALL
+    NODE_TAIL,
+#define NODE_TAIL        NODE_TAIL
     NODE_SUPER,
 #define NODE_SUPER       NODE_SUPER
     NODE_ZSUPER,
@@ -420,6 +422,7 @@ typedef struct RNode {
 #define NEW_CALL(r,m,a) NEW_NODE(NODE_CALL,r,m,a)
 #define NEW_FCALL(m,a) NEW_NODE(NODE_FCALL,0,m,a)
 #define NEW_VCALL(m) NEW_NODE(NODE_VCALL,0,m,0)
+#define NEW_TAIL(m) NEW_NODE(NODE_TAIL,m,0,0)
 #define NEW_SUPER(a) NEW_NODE(NODE_SUPER,0,0,a)
 #define NEW_ZSUPER() NEW_NODE(NODE_ZSUPER,0,0,0)
 #define NEW_ARGS_AUX(r,b) NEW_NODE(NODE_ARGS_AUX,r,b,0)
diff --git a/parse.y b/parse.y
index 064aee8..77671c6 100644
--- a/parse.y
+++ b/parse.y
@@ -836,6 +836,7 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
 	modifier_while
 	modifier_until
 	modifier_rescue
+	modifier_then
 	keyword_alias
 	keyword_defined
 	keyword_BEGIN
@@ -922,7 +923,7 @@ static void token_info_pop_gen(struct parser_params*, const char *token, size_t
 %nonassoc tLOWEST
 %nonassoc tLBRACE_ARG
 
-%nonassoc  modifier_if modifier_unless modifier_while modifier_until
+%nonassoc  modifier_if modifier_unless modifier_while modifier_until modifier_then
 %left  keyword_or keyword_and
 %right keyword_not
 %nonassoc keyword_defined
@@ -1187,6 +1188,14 @@ stmt		: keyword_alias fitem {SET_LEX_STATE(EXPR_FNAME|EXPR_FITEM);} fitem
 			$$ = dispatch1(undef, $2);
 		    %*/
 		    }
+		| expr modifier_then keyword_return
+		    {
+		    /*%%%*/
+			$$ = NEW_TAIL($1);
+		    /*%
+			$$ = dispatch1(tail, $1);
+		    %*/
+		    }
 		| stmt modifier_if expr_value
 		    {
 		    /*%%%*/
@@ -3206,7 +3215,13 @@ then		: term
 		    /*%c
 		    { $$ = Qnil; }
 		    %*/
+		| modifier_then
 		| keyword_then
+		| term modifier_then
+		    /*%c%*/
+		    /*%c
+		    { $$ = $2; }
+		    %*/
 		| term keyword_then
 		    /*%c%*/
 		    /*%c
@@ -11239,6 +11254,7 @@ static const struct kw_assoc {
     {modifier_while,	"while"},
     {modifier_until,	"until"},
     {modifier_rescue,	"rescue"},
+    {modifier_then,	"then"},
     {keyword_alias,	"alias"},
     {keyword_defined,	"defined?"},
     {keyword_BEGIN,	"BEGIN"},
