cast result of copyNode()

Started by Peter Eisentrautabout 9 years ago11 messages
#1Peter Eisentraut
peter.eisentraut@2ndquadrant.com
1 attachment(s)

In order to reduce the number of useless casts and make the useful casts
more interesting, here is a patch that automatically casts the result of
copyNode() back to the input type, if the compiler supports something
like typeof(), which most current compilers appear to. That gets us
some more type safety and we only need to retain the casts that actually
do change the type.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

0001-Cast-result-of-copyObject-to-correct-type.patchtext/x-patch; name=0001-Cast-result-of-copyObject-to-correct-type.patchDownload
From 4782417a58c44d08c461dd90887ac463631fbd4a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Sun, 25 Dec 2016 12:00:00 -0500
Subject: [PATCH] Cast result of copyObject() to correct type

copyObject() is declared to return void *, which allows easily assigning
the result independent of the input, but it loses all type checking.

If the compiler supports typeof or something similar, cast the result to
the input type.  This creates a greater amount of type safety.  In some
cases, where the result is assigned to a generic type such as Node * or
Expr *, new casts are now necessary, but in general casts are now
unnecessary in the normal case and indicate that something unusual is
happening.
---
 config/c-compiler.m4                      | 27 +++++++++++++++++++++
 configure                                 | 40 +++++++++++++++++++++++++++++++
 configure.in                              |  1 +
 src/backend/bootstrap/bootstrap.c         |  4 ++--
 src/backend/commands/createas.c           |  2 +-
 src/backend/commands/event_trigger.c      |  8 +++----
 src/backend/commands/prepare.c            |  4 ++--
 src/backend/commands/view.c               |  2 +-
 src/backend/nodes/copyfuncs.c             |  4 ++++
 src/backend/optimizer/path/indxpath.c     |  4 ++--
 src/backend/optimizer/plan/createplan.c   |  6 ++---
 src/backend/optimizer/plan/initsplan.c    |  8 +++----
 src/backend/optimizer/plan/planagg.c      |  4 ++--
 src/backend/optimizer/plan/planner.c      |  4 ++--
 src/backend/optimizer/plan/setrefs.c      | 26 ++++++++++----------
 src/backend/optimizer/plan/subselect.c    | 14 +++++------
 src/backend/optimizer/prep/prepjointree.c |  2 +-
 src/backend/optimizer/prep/preptlist.c    |  2 +-
 src/backend/optimizer/prep/prepunion.c    |  4 ++--
 src/backend/optimizer/util/tlist.c        | 10 ++++----
 src/backend/parser/analyze.c              |  2 +-
 src/backend/parser/gram.y                 |  2 +-
 src/backend/parser/parse_clause.c         |  2 +-
 src/backend/parser/parse_expr.c           |  2 +-
 src/backend/parser/parse_relation.c       |  2 +-
 src/backend/parser/parse_utilcmd.c        |  6 ++---
 src/backend/rewrite/rewriteHandler.c      |  8 +++----
 src/backend/rewrite/rewriteManip.c        |  8 +++----
 src/backend/tcop/postgres.c               |  6 ++---
 src/backend/utils/cache/plancache.c       | 14 +++++------
 src/backend/utils/cache/relcache.c        |  8 +++----
 src/include/nodes/nodes.h                 |  4 ++++
 src/include/optimizer/tlist.h             |  4 ++--
 src/include/pg_config.h.in                |  6 +++++
 src/include/pg_config.h.win32             |  6 +++++
 35 files changed, 172 insertions(+), 84 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7d901e1f1a..7afaec5f85 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -178,6 +178,33 @@ fi])# PGAC_C_STATIC_ASSERT
 
 
 
+# PGAC_C_TYPEOF
+# -------------
+# Check if the C compiler understands typeof or a variant.  Define
+# HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
+#
+AC_DEFUN([PGAC_C_TYPEOF],
+[AC_CACHE_CHECK(for typeof, pgac_cv_c_typeof,
+[pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;])],
+[pgac_cv_c_typeof=$pgac_kw])
+  test "$pgac_cv_c_typeof" != no && break
+done])
+if test "$pgac_cv_c_typeof" != no; then
+  AC_DEFINE(HAVE_TYPEOF, 1,
+            [Define to 1 if your compiler understands `typeof' or something similar.])
+  if test "$pgac_cv_c_typeof" != typeof; then
+    AC_DEFINE(typeof, $pgac_cv_c_typeof, [Define to how the compiler spells `typeof'.])
+  fi
+fi])# PGAC_C_TYPEOF
+
+
+
 # PGAC_C_TYPES_COMPATIBLE
 # -----------------------
 # Check if the C compiler understands __builtin_types_compatible_p,
diff --git a/configure b/configure
index 0f143a0fad..5c2e0145f1 100755
--- a/configure
+++ b/configure
@@ -11359,6 +11359,46 @@ if test x"$pgac_cv__static_assert" = xyes ; then
 $as_echo "#define HAVE__STATIC_ASSERT 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5
+$as_echo_n "checking for typeof... " >&6; }
+if ${pgac_cv_c_typeof+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv_c_typeof=$pgac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  test "$pgac_cv_c_typeof" != no && break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_c_typeof" >&5
+$as_echo "$pgac_cv_c_typeof" >&6; }
+if test "$pgac_cv_c_typeof" != no; then
+
+$as_echo "#define HAVE_TYPEOF 1" >>confdefs.h
+
+  if test "$pgac_cv_c_typeof" != typeof; then
+
+$as_echo "#define typeof \$pgac_cv_c_typeof" >>confdefs.h
+
+  fi
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p" >&5
 $as_echo_n "checking for __builtin_types_compatible_p... " >&6; }
 if ${pgac_cv__types_compatible+:} false; then :
diff --git a/configure.in b/configure.in
index b9831bc340..f7c562f5fe 100644
--- a/configure.in
+++ b/configure.in
@@ -1322,6 +1322,7 @@ AC_C_FLEXIBLE_ARRAY_MEMBER
 PGAC_C_SIGNED
 PGAC_C_FUNCNAME_SUPPORT
 PGAC_C_STATIC_ASSERT
+PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_BSWAP32
 PGAC_C_BUILTIN_BSWAP64
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 5c5ba7bec2..096fb64851 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1078,11 +1078,11 @@ index_register(Oid heap,
 
 	memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
 	/* expressions will likely be null, but may as well copy it */
-	newind->il_info->ii_Expressions = (List *)
+	newind->il_info->ii_Expressions =
 		copyObject(indexInfo->ii_Expressions);
 	newind->il_info->ii_ExpressionsState = NIL;
 	/* predicate will likely be null, but may as well copy it */
-	newind->il_info->ii_Predicate = (List *)
+	newind->il_info->ii_Predicate =
 		copyObject(indexInfo->ii_Predicate);
 	newind->il_info->ii_PredicateState = NIL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index d6d52d9929..eec7073a77 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -316,7 +316,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
 		 * and is executed repeatedly.  (See also the same hack in EXPLAIN and
 		 * PREPARE.)
 		 */
-		rewritten = QueryRewrite((Query *) copyObject(query));
+		rewritten = QueryRewrite(copyObject(query));
 
 		/* SELECT should never rewrite to more or less than one SELECT query */
 		if (list_length(rewritten) != 1)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index e87fce7e6a..e0c421eb87 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1861,7 +1861,7 @@ EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 					 OperatorFamilyRelationId, opfamoid);
 	command->d.opfam.operators = operators;
 	command->d.opfam.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1894,7 +1894,7 @@ EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
 					 OperatorClassRelationId, opcoid);
 	command->d.createopc.operators = operators;
 	command->d.createopc.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1929,7 +1929,7 @@ EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
 	command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
 	memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
 	command->d.atscfg.ndicts = ndicts;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1959,7 +1959,7 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
 	command->type = SCT_AlterDefaultPrivileges;
 	command->d.defprivs.objtype = stmt->action->objtype;
 	command->in_extension = creating_extension;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index b01051df9d..f0da4dbc54 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -342,7 +342,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	 * We have to run parse analysis for the expressions.  Since the parser is
 	 * not cool about scribbling on its input, copy first.
 	 */
-	params = (List *) copyObject(params);
+	params = copyObject(params);
 
 	pstate = make_parsestate(NULL);
 	pstate->p_sourcetext = queryString;
@@ -545,7 +545,7 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
 	tlist = CachedPlanGetTargetList(stmt->plansource);
 
 	/* Copy into caller's context in case plan gets invalidated */
-	return (List *) copyObject(tlist);
+	return copyObject(tlist);
 }
 
 /*
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 414507f3b7..f7768d3624 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -373,7 +373,7 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 	 * Var node twice.  copyObject will expand any multiply-referenced subtree
 	 * into multiple copies.
 	 */
-	viewParse = (Query *) copyObject(viewParse);
+	viewParse = copyObject(viewParse);
 
 	/* Create a dummy ParseState for addRangeTableEntryForRelation */
 	pstate = make_parsestate(NULL);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6955298577..bec98a5f39 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4372,6 +4372,10 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
  * Create a copy of a Node tree or list.  This is a "deep" copy: all
  * substructure is copied too, recursively.
  */
+
+/* see declaration in nodes.h */
+#undef copyObject
+
 void *
 copyObject(const void *from)
 {
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 2952bfb7c2..04951e4867 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3876,9 +3876,9 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
 									   matching_cols);
 		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
 										 matching_cols);
-		rc->largs = list_truncate((List *) copyObject(clause->largs),
+		rc->largs = list_truncate(copyObject(clause->largs),
 								  matching_cols);
-		rc->rargs = list_truncate((List *) copyObject(clause->rargs),
+		rc->rargs = list_truncate(copyObject(clause->rargs),
 								  matching_cols);
 		return (Expr *) rc;
 	}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ad49674684..b0fd6e51b7 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1224,7 +1224,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -1267,7 +1267,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 	groupColPos = 0;
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -4157,7 +4157,7 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
 				/* No, so add it */
 				nlp = makeNode(NestLoopParam);
 				nlp->paramno = pitem->paramId;
-				nlp->paramval = copyObject(phv);
+				nlp->paramval = (Var *) copyObject(phv);
 				root->curOuterParams = lappend(root->curOuterParams, nlp);
 			}
 		}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index 84ce6b3125..91c71d3a25 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -2221,8 +2221,8 @@ process_implied_equality(PlannerInfo *root,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
@@ -2282,8 +2282,8 @@ build_implied_join_equality(Oid opno,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 4216fc4a6e..c2aa0d3aa4 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -369,11 +369,11 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	subroot->outer_params = NULL;
 	subroot->init_plans = NIL;
 
-	subroot->parse = parse = (Query *) copyObject(root->parse);
+	subroot->parse = parse = copyObject(root->parse);
 	IncrementVarSublevelsUp((Node *) parse, 1, 1);
 
 	/* append_rel_list might contain outer Vars? */
-	subroot->append_rel_list = (List *) copyObject(root->append_rel_list);
+	subroot->append_rel_list = copyObject(root->append_rel_list);
 	IncrementVarSublevelsUp((Node *) subroot->append_rel_list, 1, 1);
 	/* There shouldn't be any OJ info to translate, as yet */
 	Assert(subroot->join_info_list == NIL);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 41dde50d85..8af6b14c84 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1113,7 +1113,7 @@ inheritance_planner(PlannerInfo *root)
 		 * executor doesn't need to see the modified copies --- we can just
 		 * pass it the original rowMarks list.)
 		 */
-		subroot->rowMarks = (List *) copyObject(root->rowMarks);
+		subroot->rowMarks = copyObject(root->rowMarks);
 
 		/*
 		 * The append_rel_list likewise might contain references to subquery
@@ -1135,7 +1135,7 @@ inheritance_planner(PlannerInfo *root)
 				AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
 
 				if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
-					appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
+					appinfo2 = copyObject(appinfo2);
 
 				subroot->append_rel_list = lappend(subroot->append_rel_list,
 												   appinfo2);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 2fe1c8cb9d..53ce3a5089 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -111,10 +111,10 @@ static Var *search_indexed_tlist_for_var(Var *var,
 							 indexed_tlist *itlist,
 							 Index newvarno,
 							 int rtoffset);
-static Var *search_indexed_tlist_for_non_var(Node *node,
+static Var *search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist,
 								 Index newvarno);
-static Var *search_indexed_tlist_for_sortgroupref(Node *node,
+static Var *search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno);
@@ -1403,7 +1403,7 @@ fix_param_node(PlannerInfo *root, Param *p)
 			elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
 		return copyObject(list_nth(params, colno - 1));
 	}
-	return copyObject(p);
+	return (Node *) copyObject(p);
 }
 
 /*
@@ -1690,7 +1690,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
 		if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
 		{
 			newexpr = (Node *)
-				search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+				search_indexed_tlist_for_sortgroupref(tle->expr,
 													  tle->ressortgroupref,
 													  subplan_itlist,
 													  OUTER_VAR);
@@ -1773,7 +1773,7 @@ convert_combining_aggrefs(Node *node, void *context)
 		 */
 		child_agg->args = NIL;
 		child_agg->aggfilter = NULL;
-		parent_agg = (Aggref *) copyObject(child_agg);
+		parent_agg = copyObject(child_agg);
 		child_agg->args = orig_agg->args;
 		child_agg->aggfilter = orig_agg->aggfilter;
 
@@ -2017,7 +2017,7 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
  * so there's a correctness reason not to call it unless that's set.
  */
 static Var *
-search_indexed_tlist_for_non_var(Node *node,
+search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist, Index newvarno)
 {
 	TargetEntry *tle;
@@ -2058,7 +2058,7 @@ search_indexed_tlist_for_non_var(Node *node,
  * And it's also faster than search_indexed_tlist_for_non_var.
  */
 static Var *
-search_indexed_tlist_for_sortgroupref(Node *node,
+search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno)
@@ -2192,7 +2192,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->outer_itlist && context->outer_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->outer_itlist,
 													  OUTER_VAR);
 			if (newvar)
@@ -2200,7 +2200,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		}
 		if (context->inner_itlist && context->inner_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->inner_itlist,
 													  INNER_VAR);
 			if (newvar)
@@ -2215,7 +2215,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	/* Try matching more complex expressions too, if tlists have any */
 	if (context->outer_itlist && context->outer_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->outer_itlist,
 												  OUTER_VAR);
 		if (newvar)
@@ -2223,7 +2223,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	}
 	if (context->inner_itlist && context->inner_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->inner_itlist,
 												  INNER_VAR);
 		if (newvar)
@@ -2307,7 +2307,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->subplan_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->subplan_itlist,
 													  context->newvarno);
 			if (newvar)
@@ -2343,7 +2343,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 	/* Try matching more complex expressions too, if tlist has any */
 	if (context->subplan_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->subplan_itlist,
 												  context->newvarno);
 		if (newvar)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 31717439b6..4f3712db21 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -125,7 +125,7 @@ assign_param_for_var(PlannerInfo *root, Var *var)
 	}
 
 	/* Nope, so make a new one */
-	var = (Var *) copyObject(var);
+	var = copyObject(var);
 	var->varlevelsup = 0;
 
 	pitem = makeNode(PlannerParamItem);
@@ -224,7 +224,7 @@ assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
 	}
 
 	/* Nope, so make a new one */
-	phv = (PlaceHolderVar *) copyObject(phv);
+	phv = copyObject(phv);
 	if (phv->phlevelsup != 0)
 	{
 		IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
@@ -316,7 +316,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	agg = (Aggref *) copyObject(agg);
+	agg = copyObject(agg);
 	IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
 	Assert(agg->agglevelsup == 0);
 
@@ -358,7 +358,7 @@ replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	grp = (GroupingFunc *) copyObject(grp);
+	grp = copyObject(grp);
 	IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
 	Assert(grp->agglevelsup == 0);
 
@@ -492,7 +492,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 	 * same sub-Query node, but the planner wants to scribble on the Query.
 	 * Try to clean this up when we do querytree redesign...
 	 */
-	subquery = (Query *) copyObject(orig_subquery);
+	subquery = copyObject(orig_subquery);
 
 	/*
 	 * If it's an EXISTS subplan, we might be able to simplify it.
@@ -568,7 +568,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 		List	   *paramIds;
 
 		/* Make a second copy of the original subquery */
-		subquery = (Query *) copyObject(orig_subquery);
+		subquery = copyObject(orig_subquery);
 		/* and re-simplify */
 		simple_exists = simplify_EXISTS_query(root, subquery);
 		Assert(simple_exists);
@@ -1423,7 +1423,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 	 * Copy the subquery so we can modify it safely (see comments in
 	 * make_subplan).
 	 */
-	subselect = (Query *) copyObject(subselect);
+	subselect = copyObject(subselect);
 
 	/*
 	 * See if the subquery can be simplified based on the knowledge that it's
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index b681230349..fbaa1e6283 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -2124,7 +2124,7 @@ pullup_replace_vars_callback(Var *var,
 				 varattno);
 
 		/* Make a copy of the tlist item to return */
-		newnode = copyObject(tle->expr);
+		newnode = (Node *) copyObject(tle->expr);
 
 		/* Insert PlaceHolderVar if needed */
 		if (rcon->need_phvs)
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 1c8d1052c5..770bf6ed68 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -180,7 +180,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
 				var->varno == result_relation)
 				continue;		/* don't need it */
 
-			if (tlist_member((Node *) var, tlist))
+			if (tlist_member((Expr *) var, tlist))
 				continue;		/* already got it */
 
 			tle = makeTargetEntry((Expr *) var,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index b7147832e0..029bc8af1e 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1275,7 +1275,7 @@ generate_append_tlist(List *colTypes, List *colCollations,
 static List *
 generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
 {
-	List	   *grouplist = (List *) copyObject(op->groupClauses);
+	List	   *grouplist = copyObject(op->groupClauses);
 	ListCell   *lg;
 	ListCell   *lt;
 
@@ -1840,7 +1840,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 
 					rte = rt_fetch(appinfo->parent_relid,
 								   context->root->parse->rtable);
-					fields = (List *) copyObject(appinfo->translated_vars);
+					fields = copyObject(appinfo->translated_vars);
 					foreach(lc, fields)
 					{
 						Var		   *field = (Var *) lfirst(lc);
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 68096b309c..fa3a8d0c07 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -29,7 +29,7 @@
  *	  equal() to the given expression.  Result is NULL if no such member.
  */
 TargetEntry *
-tlist_member(Node *node, List *targetlist)
+tlist_member(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
@@ -50,12 +50,12 @@ tlist_member(Node *node, List *targetlist)
  *	  involving binary-compatible sort operations.
  */
 TargetEntry *
-tlist_member_ignore_relabel(Node *node, List *targetlist)
+tlist_member_ignore_relabel(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
 	while (node && IsA(node, RelabelType))
-		node = (Node *) ((RelabelType *) node)->arg;
+		node = ((RelabelType *) node)->arg;
 
 	foreach(temp, targetlist)
 	{
@@ -117,7 +117,7 @@ add_to_flat_tlist(List *tlist, List *exprs)
 
 	foreach(lc, exprs)
 	{
-		Node	   *expr = (Node *) lfirst(lc);
+		Expr	   *expr = (Expr *) lfirst(lc);
 
 		if (!tlist_member(expr, tlist))
 		{
@@ -740,7 +740,7 @@ apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
 			if (expr && IsA(expr, Var))
 				tle = tlist_member_match_var((Var *) expr, tlist);
 			else
-				tle = tlist_member((Node *) expr, tlist);
+				tle = tlist_member(expr, tlist);
 
 			/*
 			 * Complain if noplace for the sortgrouprefs label, or if we'd
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a558083f43..6cecf795de 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2520,7 +2520,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		 * in the IntoClause because that's where intorel_startup() can
 		 * conveniently get it from.
 		 */
-		stmt->into->viewQuery = copyObject(query);
+		stmt->into->viewQuery = (Node *) copyObject(query);
 	}
 
 	/* represent the command as a utility Query */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 08cf5b78f5..034558b902 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -15059,7 +15059,7 @@ TableFuncTypeName(List *columns)
 	{
 		FunctionParameter *p = (FunctionParameter *) linitial(columns);
 
-		result = (TypeName *) copyObject(p->argType);
+		result = copyObject(p->argType);
 	}
 	else
 		result = SystemTypeName("record");
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index f7bb09701e..ff8f0f5615 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -346,7 +346,7 @@ transformJoinUsingClause(ParseState *pstate,
 
 		/* Now create the lvar = rvar join condition */
 		e = makeSimpleA_Expr(AEXPR_OP, "=",
-							 copyObject(lvar), copyObject(rvar),
+							 (Node *) copyObject(lvar), (Node *) copyObject(rvar),
 							 -1);
 
 		/* Prepare to combine into an AND clause, if multiple join columns */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8a2bdf06e8..7144b2008e 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1278,7 +1278,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 			/* ROW() op ROW() is handled specially */
 			cmp = make_row_comparison_op(pstate,
 										 a->name,
-							  (List *) copyObject(((RowExpr *) lexpr)->args),
+										 copyObject(((RowExpr *) lexpr)->args),
 										 ((RowExpr *) rexpr)->args,
 										 a->location);
 		}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 58f70508fe..166589549d 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1741,7 +1741,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
 	rte->joinaliasvars = aliasvars;
 	rte->alias = alias;
 
-	eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
+	eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL);
 	numaliases = list_length(eref->colnames);
 
 	/* fill in any unspecified alias columns */
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 4f74208633..0ad8d94550 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -167,7 +167,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (CreateStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2115,7 +2115,7 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in IndexStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (IndexStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2529,7 +2529,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 	 * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
 	 * is overkill, but easy.)
 	 */
-	stmt = (AlterTableStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Caller is responsible for locking the relation */
 	rel = relation_open(relid, NoLock);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index bf4f098c15..dccb5f5947 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -349,8 +349,8 @@ rewriteRuleAction(Query *parsetree,
 	 * Make modifiable copies of rule action and qual (what we're passed are
 	 * the stored versions in the relcache; don't touch 'em!).
 	 */
-	rule_action = (Query *) copyObject(rule_action);
-	rule_qual = (Node *) copyObject(rule_qual);
+	rule_action = copyObject(rule_action);
+	rule_qual = copyObject(rule_qual);
 
 	/*
 	 * Acquire necessary locks and fix any deleted JOIN RTE entries.
@@ -408,7 +408,7 @@ rewriteRuleAction(Query *parsetree,
 	 * that rule action's rtable is separate and shares no substructure with
 	 * the main rtable.  Hence do a deep copy here.
 	 */
-	sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),
+	sub_action->rtable = list_concat(copyObject(parsetree->rtable),
 									 sub_action->rtable);
 
 	/*
@@ -1893,7 +1893,7 @@ CopyAndAddInvertedQual(Query *parsetree,
 					   CmdType event)
 {
 	/* Don't scribble on the passed qual (it's in the relcache!) */
-	Node	   *new_qual = (Node *) copyObject(rule_qual);
+	Node	   *new_qual = copyObject(rule_qual);
 	acquireLocksOnSubLinks_context context;
 
 	context.for_execute = true;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 884f4f8856..a9d7013b72 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1412,11 +1412,11 @@ ReplaceVarsFromTargetList_callback(Var *var,
 	else
 	{
 		/* Make a copy of the tlist item to return */
-		Node	   *newnode = copyObject(tle->expr);
+		Expr	   *newnode = copyObject(tle->expr);
 
 		/* Must adjust varlevelsup if tlist item is from higher query */
 		if (var->varlevelsup > 0)
-			IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
+			IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
 
 		/*
 		 * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
@@ -1428,12 +1428,12 @@ ReplaceVarsFromTargetList_callback(Var *var,
 		 * create semantic oddities that users of rules would probably prefer
 		 * not to cope with.  So treat it as an unimplemented feature.
 		 */
-		if (contains_multiexpr_param(newnode, NULL))
+		if (contains_multiexpr_param((Node *) newnode, NULL))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
 
-		return newnode;
+		return (Node *) newnode;
 	}
 }
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b17923106a..57d20a965d 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -616,7 +616,7 @@ pg_parse_query(const char *query_string)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass raw parsetrees through copyObject() */
 	{
-		List	   *new_list = (List *) copyObject(raw_parsetree_list);
+		List	   *new_list = copyObject(raw_parsetree_list);
 
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, raw_parsetree_list))
@@ -756,7 +756,7 @@ pg_rewrite_query(Query *query)
 	{
 		List	   *new_list;
 
-		new_list = (List *) copyObject(querytree_list);
+		new_list = copyObject(querytree_list);
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, querytree_list))
 			elog(WARNING, "copyObject() failed to produce equal parse tree");
@@ -803,7 +803,7 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass plan output through copyObject() */
 	{
-		PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
+		PlannedStmt *new_plan = copyObject(plan);
 
 		/*
 		 * equal() currently does not have routines to compare Plan nodes, so
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index aa146d6943..1a1df37400 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -358,7 +358,7 @@ CompleteCachedPlan(CachedPlanSource *plansource,
 												  "CachedPlanQuery",
 												  ALLOCSET_START_SMALL_SIZES);
 		MemoryContextSwitchTo(querytree_context);
-		querytree_list = (List *) copyObject(querytree_list);
+		querytree_list = copyObject(querytree_list);
 	}
 
 	plansource->query_context = querytree_context;
@@ -731,7 +731,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
 											  ALLOCSET_START_SMALL_SIZES);
 	oldcxt = MemoryContextSwitchTo(querytree_context);
 
-	qlist = (List *) copyObject(tlist);
+	qlist = copyObject(tlist);
 
 	/*
 	 * Use the planner machinery to extract dependencies.  Data is saved in
@@ -906,7 +906,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	if (qlist == NIL)
 	{
 		if (!plansource->is_oneshot)
-			qlist = (List *) copyObject(plansource->query_list);
+			qlist = copyObject(plansource->query_list);
 		else
 			qlist = plansource->query_list;
 	}
@@ -950,7 +950,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		 */
 		MemoryContextSwitchTo(plan_context);
 
-		plist = (List *) copyObject(plist);
+		plist = copyObject(plist);
 	}
 	else
 		plan_context = CurrentMemoryContext;
@@ -1364,9 +1364,9 @@ CopyCachedPlan(CachedPlanSource *plansource)
 											  "CachedPlanQuery",
 											  ALLOCSET_START_SMALL_SIZES);
 	MemoryContextSwitchTo(querytree_context);
-	newsource->query_list = (List *) copyObject(plansource->query_list);
-	newsource->relationOids = (List *) copyObject(plansource->relationOids);
-	newsource->invalItems = (List *) copyObject(plansource->invalItems);
+	newsource->query_list = copyObject(plansource->query_list);
+	newsource->relationOids = copyObject(plansource->relationOids);
+	newsource->invalItems = copyObject(plansource->invalItems);
 	if (plansource->search_path)
 		newsource->search_path = CopyOverrideSearchPath(plansource->search_path);
 	newsource->query_context = querytree_context;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2a6835991c..3f0f3184d7 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4584,7 +4584,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indexprs)
-		return (List *) copyObject(relation->rd_indexprs);
+		return copyObject(relation->rd_indexprs);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4620,7 +4620,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indexprs = (List *) copyObject(result);
+	relation->rd_indexprs = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
@@ -4647,7 +4647,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indpred)
-		return (List *) copyObject(relation->rd_indpred);
+		return copyObject(relation->rd_indpred);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4689,7 +4689,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indpred = (List *) copyObject(result);
+	relation->rd_indpred = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 201f248774..3deecc86ab 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -584,6 +584,10 @@ extern int16 *readAttrNumberCols(int numCols);
  * nodes/copyfuncs.c
  */
 extern void *copyObject(const void *obj);
+/* cast result back to argument type, if supported by compiler */
+#ifdef HAVE_TYPEOF
+#define copyObject(obj) ((typeof(obj)) copyObject(obj))
+#endif
 
 /*
  * nodes/equalfuncs.c
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 0d745a0891..80c331c9c4 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -17,8 +17,8 @@
 #include "nodes/relation.h"
 
 
-extern TargetEntry *tlist_member(Node *node, List *targetlist);
-extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
+extern TargetEntry *tlist_member(Expr *node, List *targetlist);
+extern TargetEntry *tlist_member_ignore_relabel(Expr *node, List *targetlist);
 
 extern List *add_to_flat_tlist(List *tlist, List *exprs);
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 42a3fc862e..95868e15bc 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -612,6 +612,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #undef HAVE_TOWLOWER
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#undef HAVE_TYPEOF
+
 /* Define to 1 if you have the external array `tzname'. */
 #undef HAVE_TZNAME
 
@@ -938,6 +941,9 @@
 /* Define to empty if the C compiler does not understand signed types. */
 #undef signed
 
+/* Define to how the compiler spells `typeof'. */
+#undef typeof
+
 /* Define to the type of an unsigned integer type wide enough to hold a
    pointer, if such a type exists, and if the system does not define it. */
 #undef uintptr_t
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index ceb8b7956e..cf9bf5ff9e 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -460,6 +460,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #define HAVE_TOWLOWER 1
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#define HAVE_TYPEOF 1
+
 /* Define to 1 if you have the external array `tzname'. */
 /* #undef HAVE_TZNAME */
 
@@ -692,3 +695,6 @@
 
 /* Define to empty if the C compiler does not understand signed types. */
 /* #undef signed */
+
+/* Define to how the compiler spells `typeof'. */
+#define typeof decltype
-- 
2.11.0

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Peter Eisentraut (#1)
Re: cast result of copyNode()

Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:

In order to reduce the number of useless casts and make the useful casts
more interesting, here is a patch that automatically casts the result of
copyNode() back to the input type, if the compiler supports something
like typeof(), which most current compilers appear to. That gets us
some more type safety and we only need to retain the casts that actually
do change the type.

But doesn't this result in a boatload of warnings on compilers that
don't have typeof()?

If typeof() were in C99 I might figure that is an okay tradeoff,
but it isn't. I'm not sure that this is the most useful place
to be moving our C standards compliance expectations by 20 years
in one jump.

Also, if your answer is "you shouldn't get any warnings because
copyObject is already declared to return void *", then why aren't
we just relying on that today?

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Tom Lane (#2)
Re: cast result of copyNode()

On 12/31/16 11:56 AM, Tom Lane wrote:

But doesn't this result in a boatload of warnings on compilers that
don't have typeof()?

Also, if your answer is "you shouldn't get any warnings because
copyObject is already declared to return void *", then why aren't
we just relying on that today?

Currently, you don't get any warnings, and that would continue to be the
case if a compiler doesn't have typeof().

The casts that are currently there are (apparently) merely for style,
same as casting the result of malloc().

The problem this patch would address is that currently you can write

SomeNode *x = ...;

...

OtherNode *y = copyObject(x);

and there is no notice about that potential mistake.

This patch makes that an error.

If you are sure about what you are doing, you can add a cast. But
casting the result of copyObject() should be limited to a few specific
cases where the result is assigned to a generic Node * or something like
that.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Peter Eisentraut (#3)
1 attachment(s)
Re: cast result of copyObject()

The problem this patch would address is that currently you can write

SomeNode *x = ...;

...

OtherNode *y = copyObject(x);

and there is no notice about that potential mistake.

This patch makes that an error.

If you are sure about what you are doing, you can add a cast. But
casting the result of copyObject() should be limited to a few specific
cases where the result is assigned to a generic Node * or something like
that.

Updated patch, which I will register in the commit fest for some wider
portability testing (Windows!).

(changed subject copyNode -> copyObject (was excited about castNode at
the time))

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v2-0001-Cast-result-of-copyObject-to-correct-type.patchtext/x-patch; name=v2-0001-Cast-result-of-copyObject-to-correct-type.patchDownload
From a6cccd06e22cbd4d84da1c2d1b085c68ae3d8a9a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Tue, 28 Feb 2017 15:07:05 -0500
Subject: [PATCH v2] Cast result of copyObject() to correct type

copyObject() is declared to return void *, which allows easily assigning
the result independent of the input, but it loses all type checking.

If the compiler supports typeof or something similar, cast the result to
the input type.  This creates a greater amount of type safety.  In some
cases, where the result is assigned to a generic type such as Node * or
Expr *, new casts are now necessary, but in general casts are now
unnecessary in the normal case and indicate that something unusual is
happening.
---
 config/c-compiler.m4                      | 27 +++++++++++++++++++++
 configure                                 | 40 +++++++++++++++++++++++++++++++
 configure.in                              |  1 +
 src/backend/bootstrap/bootstrap.c         |  4 ++--
 src/backend/commands/createas.c           |  2 +-
 src/backend/commands/event_trigger.c      |  8 +++----
 src/backend/commands/prepare.c            |  4 ++--
 src/backend/commands/view.c               |  2 +-
 src/backend/nodes/copyfuncs.c             |  4 ++++
 src/backend/optimizer/path/indxpath.c     |  4 ++--
 src/backend/optimizer/plan/createplan.c   |  6 ++---
 src/backend/optimizer/plan/initsplan.c    |  8 +++----
 src/backend/optimizer/plan/planagg.c      |  4 ++--
 src/backend/optimizer/plan/planner.c      |  4 ++--
 src/backend/optimizer/plan/setrefs.c      | 26 ++++++++++----------
 src/backend/optimizer/plan/subselect.c    | 14 +++++------
 src/backend/optimizer/prep/prepjointree.c |  2 +-
 src/backend/optimizer/prep/preptlist.c    |  2 +-
 src/backend/optimizer/prep/prepunion.c    |  4 ++--
 src/backend/optimizer/util/tlist.c        | 12 +++++-----
 src/backend/parser/analyze.c              |  2 +-
 src/backend/parser/gram.y                 |  2 +-
 src/backend/parser/parse_clause.c         |  2 +-
 src/backend/parser/parse_expr.c           |  2 +-
 src/backend/parser/parse_relation.c       |  2 +-
 src/backend/parser/parse_utilcmd.c        |  6 ++---
 src/backend/rewrite/rewriteHandler.c      |  8 +++----
 src/backend/rewrite/rewriteManip.c        |  8 +++----
 src/backend/tcop/postgres.c               |  6 ++---
 src/backend/utils/cache/plancache.c       | 14 +++++------
 src/backend/utils/cache/relcache.c        |  8 +++----
 src/include/nodes/nodes.h                 |  4 ++++
 src/include/optimizer/tlist.h             |  4 ++--
 src/include/pg_config.h.in                |  6 +++++
 src/include/pg_config.h.win32             |  6 +++++
 35 files changed, 173 insertions(+), 85 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7d901e1f1a..7afaec5f85 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -178,6 +178,33 @@ fi])# PGAC_C_STATIC_ASSERT
 
 
 
+# PGAC_C_TYPEOF
+# -------------
+# Check if the C compiler understands typeof or a variant.  Define
+# HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
+#
+AC_DEFUN([PGAC_C_TYPEOF],
+[AC_CACHE_CHECK(for typeof, pgac_cv_c_typeof,
+[pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;])],
+[pgac_cv_c_typeof=$pgac_kw])
+  test "$pgac_cv_c_typeof" != no && break
+done])
+if test "$pgac_cv_c_typeof" != no; then
+  AC_DEFINE(HAVE_TYPEOF, 1,
+            [Define to 1 if your compiler understands `typeof' or something similar.])
+  if test "$pgac_cv_c_typeof" != typeof; then
+    AC_DEFINE(typeof, $pgac_cv_c_typeof, [Define to how the compiler spells `typeof'.])
+  fi
+fi])# PGAC_C_TYPEOF
+
+
+
 # PGAC_C_TYPES_COMPATIBLE
 # -----------------------
 # Check if the C compiler understands __builtin_types_compatible_p,
diff --git a/configure b/configure
index b5cdebb510..47a1a59e8e 100755
--- a/configure
+++ b/configure
@@ -11399,6 +11399,46 @@ if test x"$pgac_cv__static_assert" = xyes ; then
 $as_echo "#define HAVE__STATIC_ASSERT 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5
+$as_echo_n "checking for typeof... " >&6; }
+if ${pgac_cv_c_typeof+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv_c_typeof=$pgac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  test "$pgac_cv_c_typeof" != no && break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_c_typeof" >&5
+$as_echo "$pgac_cv_c_typeof" >&6; }
+if test "$pgac_cv_c_typeof" != no; then
+
+$as_echo "#define HAVE_TYPEOF 1" >>confdefs.h
+
+  if test "$pgac_cv_c_typeof" != typeof; then
+
+$as_echo "#define typeof \$pgac_cv_c_typeof" >>confdefs.h
+
+  fi
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p" >&5
 $as_echo_n "checking for __builtin_types_compatible_p... " >&6; }
 if ${pgac_cv__types_compatible+:} false; then :
diff --git a/configure.in b/configure.in
index 1d99cda1d8..e8acfdc98d 100644
--- a/configure.in
+++ b/configure.in
@@ -1317,6 +1317,7 @@ AC_C_FLEXIBLE_ARRAY_MEMBER
 PGAC_C_SIGNED
 PGAC_C_FUNCNAME_SUPPORT
 PGAC_C_STATIC_ASSERT
+PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_BSWAP32
 PGAC_C_BUILTIN_BSWAP64
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 6511c6064b..1514a4c890 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1078,11 +1078,11 @@ index_register(Oid heap,
 
 	memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
 	/* expressions will likely be null, but may as well copy it */
-	newind->il_info->ii_Expressions = (List *)
+	newind->il_info->ii_Expressions =
 		copyObject(indexInfo->ii_Expressions);
 	newind->il_info->ii_ExpressionsState = NIL;
 	/* predicate will likely be null, but may as well copy it */
-	newind->il_info->ii_Predicate = (List *)
+	newind->il_info->ii_Predicate =
 		copyObject(indexInfo->ii_Predicate);
 	newind->il_info->ii_PredicateState = NIL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 646a88409f..520a5da8a2 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -315,7 +315,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
 		 * and is executed repeatedly.  (See also the same hack in EXPLAIN and
 		 * PREPARE.)
 		 */
-		rewritten = QueryRewrite((Query *) copyObject(query));
+		rewritten = QueryRewrite(copyObject(query));
 
 		/* SELECT should never rewrite to more or less than one SELECT query */
 		if (list_length(rewritten) != 1)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 346b347ae1..f3073ab2ed 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1866,7 +1866,7 @@ EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 					 OperatorFamilyRelationId, opfamoid);
 	command->d.opfam.operators = operators;
 	command->d.opfam.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1899,7 +1899,7 @@ EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
 					 OperatorClassRelationId, opcoid);
 	command->d.createopc.operators = operators;
 	command->d.createopc.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1934,7 +1934,7 @@ EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
 	command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
 	memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
 	command->d.atscfg.ndicts = ndicts;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1964,7 +1964,7 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
 	command->type = SCT_AlterDefaultPrivileges;
 	command->d.defprivs.objtype = stmt->action->objtype;
 	command->in_extension = creating_extension;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 116ed67547..c18b7d2ebe 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -352,7 +352,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	 * We have to run parse analysis for the expressions.  Since the parser is
 	 * not cool about scribbling on its input, copy first.
 	 */
-	params = (List *) copyObject(params);
+	params = copyObject(params);
 
 	pstate = make_parsestate(NULL);
 	pstate->p_sourcetext = queryString;
@@ -554,7 +554,7 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
 	tlist = CachedPlanGetTargetList(stmt->plansource);
 
 	/* Copy into caller's context in case plan gets invalidated */
-	return (List *) copyObject(tlist);
+	return copyObject(tlist);
 }
 
 /*
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 7d76f567a8..35e25db7dc 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -373,7 +373,7 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 	 * Var node twice.  copyObject will expand any multiply-referenced subtree
 	 * into multiple copies.
 	 */
-	viewParse = (Query *) copyObject(viewParse);
+	viewParse = copyObject(viewParse);
 
 	/* Create a dummy ParseState for addRangeTableEntryForRelation */
 	pstate = make_parsestate(NULL);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 05d8538717..f6e04c42a6 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -4471,6 +4471,10 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
  * Create a copy of a Node tree or list.  This is a "deep" copy: all
  * substructure is copied too, recursively.
  */
+
+/* see declaration in nodes.h */
+#undef copyObject
+
 void *
 copyObject(const void *from)
 {
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index d8e5b81126..cf98129bc2 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -3989,9 +3989,9 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
 									   matching_cols);
 		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
 										 matching_cols);
-		rc->largs = list_truncate((List *) copyObject(clause->largs),
+		rc->largs = list_truncate(copyObject(clause->largs),
 								  matching_cols);
-		rc->rargs = list_truncate((List *) copyObject(clause->rargs),
+		rc->rargs = list_truncate(copyObject(clause->rargs),
 								  matching_cols);
 		return (Expr *) rc;
 	}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1e953b40d6..9d9e1d3581 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1254,7 +1254,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -1297,7 +1297,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 	groupColPos = 0;
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -4184,7 +4184,7 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
 				/* No, so add it */
 				nlp = makeNode(NestLoopParam);
 				nlp->paramno = pitem->paramId;
-				nlp->paramval = copyObject(phv);
+				nlp->paramval = (Var *) copyObject(phv);
 				root->curOuterParams = lappend(root->curOuterParams, nlp);
 			}
 		}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index c170e9614f..7645198897 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -2304,8 +2304,8 @@ process_implied_equality(PlannerInfo *root,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
@@ -2367,8 +2367,8 @@ build_implied_join_equality(Oid opno,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index c3fbf3cdf8..55657360fc 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -369,11 +369,11 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	subroot->outer_params = NULL;
 	subroot->init_plans = NIL;
 
-	subroot->parse = parse = (Query *) copyObject(root->parse);
+	subroot->parse = parse = copyObject(root->parse);
 	IncrementVarSublevelsUp((Node *) parse, 1, 1);
 
 	/* append_rel_list might contain outer Vars? */
-	subroot->append_rel_list = (List *) copyObject(root->append_rel_list);
+	subroot->append_rel_list = copyObject(root->append_rel_list);
 	IncrementVarSublevelsUp((Node *) subroot->append_rel_list, 1, 1);
 	/* There shouldn't be any OJ info to translate, as yet */
 	Assert(subroot->join_info_list == NIL);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index ca0ae7883e..bb45268744 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1102,7 +1102,7 @@ inheritance_planner(PlannerInfo *root)
 		 * executor doesn't need to see the modified copies --- we can just
 		 * pass it the original rowMarks list.)
 		 */
-		subroot->rowMarks = (List *) copyObject(root->rowMarks);
+		subroot->rowMarks = copyObject(root->rowMarks);
 
 		/*
 		 * The append_rel_list likewise might contain references to subquery
@@ -1124,7 +1124,7 @@ inheritance_planner(PlannerInfo *root)
 				AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
 
 				if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
-					appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
+					appinfo2 = copyObject(appinfo2);
 
 				subroot->append_rel_list = lappend(subroot->append_rel_list,
 												   appinfo2);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 07ddbcf37e..cf652ddac1 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -111,10 +111,10 @@ static Var *search_indexed_tlist_for_var(Var *var,
 							 indexed_tlist *itlist,
 							 Index newvarno,
 							 int rtoffset);
-static Var *search_indexed_tlist_for_non_var(Node *node,
+static Var *search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist,
 								 Index newvarno);
-static Var *search_indexed_tlist_for_sortgroupref(Node *node,
+static Var *search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno);
@@ -1404,7 +1404,7 @@ fix_param_node(PlannerInfo *root, Param *p)
 			elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
 		return copyObject(list_nth(params, colno - 1));
 	}
-	return copyObject(p);
+	return (Node *) copyObject(p);
 }
 
 /*
@@ -1691,7 +1691,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
 		if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
 		{
 			newexpr = (Node *)
-				search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+				search_indexed_tlist_for_sortgroupref(tle->expr,
 													  tle->ressortgroupref,
 													  subplan_itlist,
 													  OUTER_VAR);
@@ -1774,7 +1774,7 @@ convert_combining_aggrefs(Node *node, void *context)
 		 */
 		child_agg->args = NIL;
 		child_agg->aggfilter = NULL;
-		parent_agg = (Aggref *) copyObject(child_agg);
+		parent_agg = copyObject(child_agg);
 		child_agg->args = orig_agg->args;
 		child_agg->aggfilter = orig_agg->aggfilter;
 
@@ -2018,7 +2018,7 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
  * so there's a correctness reason not to call it unless that's set.
  */
 static Var *
-search_indexed_tlist_for_non_var(Node *node,
+search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist, Index newvarno)
 {
 	TargetEntry *tle;
@@ -2059,7 +2059,7 @@ search_indexed_tlist_for_non_var(Node *node,
  * And it's also faster than search_indexed_tlist_for_non_var.
  */
 static Var *
-search_indexed_tlist_for_sortgroupref(Node *node,
+search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno)
@@ -2193,7 +2193,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->outer_itlist && context->outer_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->outer_itlist,
 													  OUTER_VAR);
 			if (newvar)
@@ -2201,7 +2201,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		}
 		if (context->inner_itlist && context->inner_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->inner_itlist,
 													  INNER_VAR);
 			if (newvar)
@@ -2216,7 +2216,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	/* Try matching more complex expressions too, if tlists have any */
 	if (context->outer_itlist && context->outer_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->outer_itlist,
 												  OUTER_VAR);
 		if (newvar)
@@ -2224,7 +2224,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	}
 	if (context->inner_itlist && context->inner_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->inner_itlist,
 												  INNER_VAR);
 		if (newvar)
@@ -2308,7 +2308,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->subplan_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->subplan_itlist,
 													  context->newvarno);
 			if (newvar)
@@ -2344,7 +2344,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 	/* Try matching more complex expressions too, if tlist has any */
 	if (context->subplan_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->subplan_itlist,
 												  context->newvarno);
 		if (newvar)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3eb2bb749e..7b2b368246 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -125,7 +125,7 @@ assign_param_for_var(PlannerInfo *root, Var *var)
 	}
 
 	/* Nope, so make a new one */
-	var = (Var *) copyObject(var);
+	var = copyObject(var);
 	var->varlevelsup = 0;
 
 	pitem = makeNode(PlannerParamItem);
@@ -224,7 +224,7 @@ assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
 	}
 
 	/* Nope, so make a new one */
-	phv = (PlaceHolderVar *) copyObject(phv);
+	phv = copyObject(phv);
 	if (phv->phlevelsup != 0)
 	{
 		IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
@@ -316,7 +316,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	agg = (Aggref *) copyObject(agg);
+	agg = copyObject(agg);
 	IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
 	Assert(agg->agglevelsup == 0);
 
@@ -358,7 +358,7 @@ replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	grp = (GroupingFunc *) copyObject(grp);
+	grp = copyObject(grp);
 	IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
 	Assert(grp->agglevelsup == 0);
 
@@ -491,7 +491,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 	 * same sub-Query node, but the planner wants to scribble on the Query.
 	 * Try to clean this up when we do querytree redesign...
 	 */
-	subquery = (Query *) copyObject(orig_subquery);
+	subquery = copyObject(orig_subquery);
 
 	/*
 	 * If it's an EXISTS subplan, we might be able to simplify it.
@@ -568,7 +568,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 		List	   *paramIds;
 
 		/* Make a second copy of the original subquery */
-		subquery = (Query *) copyObject(orig_subquery);
+		subquery = copyObject(orig_subquery);
 		/* and re-simplify */
 		simple_exists = simplify_EXISTS_query(root, subquery);
 		Assert(simple_exists);
@@ -1431,7 +1431,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 	 * Copy the subquery so we can modify it safely (see comments in
 	 * make_subplan).
 	 */
-	subselect = (Query *) copyObject(subselect);
+	subselect = copyObject(subselect);
 
 	/*
 	 * See if the subquery can be simplified based on the knowledge that it's
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 6c6ac8dc0a..de74d448f7 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -2122,7 +2122,7 @@ pullup_replace_vars_callback(Var *var,
 				 varattno);
 
 		/* Make a copy of the tlist item to return */
-		newnode = copyObject(tle->expr);
+		newnode = (Node *) copyObject(tle->expr);
 
 		/* Insert PlaceHolderVar if needed */
 		if (rcon->need_phvs)
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 824af3f54c..de47153bac 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -180,7 +180,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
 				var->varno == result_relation)
 				continue;		/* don't need it */
 
-			if (tlist_member((Node *) var, tlist))
+			if (tlist_member((Expr *) var, tlist))
 				continue;		/* already got it */
 
 			tle = makeTargetEntry((Expr *) var,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1389db18ba..e7f6d6b10f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1274,7 +1274,7 @@ generate_append_tlist(List *colTypes, List *colCollations,
 static List *
 generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
 {
-	List	   *grouplist = (List *) copyObject(op->groupClauses);
+	List	   *grouplist = copyObject(op->groupClauses);
 	ListCell   *lg;
 	ListCell   *lt;
 
@@ -1840,7 +1840,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 
 					rte = rt_fetch(appinfo->parent_relid,
 								   context->root->parse->rtable);
-					fields = (List *) copyObject(appinfo->translated_vars);
+					fields = copyObject(appinfo->translated_vars);
 					rowexpr = makeNode(RowExpr);
 					rowexpr->args = fields;
 					rowexpr->row_typeid = var->vartype;
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 5728f70c8b..09523853d0 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -51,7 +51,7 @@ static bool split_pathtarget_walker(Node *node,
  *	  equal() to the given expression.  Result is NULL if no such member.
  */
 TargetEntry *
-tlist_member(Node *node, List *targetlist)
+tlist_member(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
@@ -72,12 +72,12 @@ tlist_member(Node *node, List *targetlist)
  *	  involving binary-compatible sort operations.
  */
 TargetEntry *
-tlist_member_ignore_relabel(Node *node, List *targetlist)
+tlist_member_ignore_relabel(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
 	while (node && IsA(node, RelabelType))
-		node = (Node *) ((RelabelType *) node)->arg;
+		node = ((RelabelType *) node)->arg;
 
 	foreach(temp, targetlist)
 	{
@@ -139,7 +139,7 @@ add_to_flat_tlist(List *tlist, List *exprs)
 
 	foreach(lc, exprs)
 	{
-		Node	   *expr = (Node *) lfirst(lc);
+		Expr	   *expr = (Expr *) lfirst(lc);
 
 		if (!tlist_member(expr, tlist))
 		{
@@ -762,7 +762,7 @@ apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
 			if (expr && IsA(expr, Var))
 				tle = tlist_member_match_var((Var *) expr, tlist);
 			else
-				tle = tlist_member((Node *) expr, tlist);
+				tle = tlist_member(expr, tlist);
 
 			/*
 			 * Complain if noplace for the sortgrouprefs label, or if we'd
@@ -999,7 +999,7 @@ split_pathtarget_at_srfs(PlannerInfo *root,
 
 				foreach(lcx, input_srfs)
 				{
-					Node	   *srf = (Node *) lfirst(lcx);
+					Expr	   *srf = (Expr *) lfirst(lcx);
 
 					if (list_member(prev_level_tlist, srf))
 						add_new_column_to_pathtarget(ntarget, copyObject(srf));
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 796b5c9a5f..a4e8088d82 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2556,7 +2556,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		 * in the IntoClause because that's where intorel_startup() can
 		 * conveniently get it from.
 		 */
-		stmt->into->viewQuery = copyObject(query);
+		stmt->into->viewQuery = (Node *) copyObject(query);
 	}
 
 	/* represent the command as a utility Query */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e833b2eba5..38168eb2c2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -15372,7 +15372,7 @@ TableFuncTypeName(List *columns)
 	{
 		FunctionParameter *p = (FunctionParameter *) linitial(columns);
 
-		result = (TypeName *) copyObject(p->argType);
+		result = copyObject(p->argType);
 	}
 	else
 		result = SystemTypeName("record");
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index b5eae56006..ecc725c5d2 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -345,7 +345,7 @@ transformJoinUsingClause(ParseState *pstate,
 
 		/* Now create the lvar = rvar join condition */
 		e = makeSimpleA_Expr(AEXPR_OP, "=",
-							 copyObject(lvar), copyObject(rvar),
+							 (Node *) copyObject(lvar), (Node *) copyObject(rvar),
 							 -1);
 
 		/* Prepare to combine into an AND clause, if multiple join columns */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d3ed073cee..323be23bca 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1255,7 +1255,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 			/* ROW() op ROW() is handled specially */
 			cmp = make_row_comparison_op(pstate,
 										 a->name,
-							  (List *) copyObject(((RowExpr *) lexpr)->args),
+										 copyObject(((RowExpr *) lexpr)->args),
 										 ((RowExpr *) rexpr)->args,
 										 a->location);
 		}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index cf69533b53..9b82edccbe 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1741,7 +1741,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
 	rte->joinaliasvars = aliasvars;
 	rte->alias = alias;
 
-	eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
+	eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL);
 	numaliases = list_length(eref->colnames);
 
 	/* fill in any unspecified alias columns */
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index ff2bab6551..f77e87a898 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -167,7 +167,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (CreateStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2109,7 +2109,7 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in IndexStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (IndexStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2523,7 +2523,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 	 * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
 	 * is overkill, but easy.)
 	 */
-	stmt = (AlterTableStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Caller is responsible for locking the relation */
 	rel = relation_open(relid, NoLock);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 20bbb37484..b354abc482 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -349,8 +349,8 @@ rewriteRuleAction(Query *parsetree,
 	 * Make modifiable copies of rule action and qual (what we're passed are
 	 * the stored versions in the relcache; don't touch 'em!).
 	 */
-	rule_action = (Query *) copyObject(rule_action);
-	rule_qual = (Node *) copyObject(rule_qual);
+	rule_action = copyObject(rule_action);
+	rule_qual = copyObject(rule_qual);
 
 	/*
 	 * Acquire necessary locks and fix any deleted JOIN RTE entries.
@@ -408,7 +408,7 @@ rewriteRuleAction(Query *parsetree,
 	 * that rule action's rtable is separate and shares no substructure with
 	 * the main rtable.  Hence do a deep copy here.
 	 */
-	sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),
+	sub_action->rtable = list_concat(copyObject(parsetree->rtable),
 									 sub_action->rtable);
 
 	/*
@@ -1893,7 +1893,7 @@ CopyAndAddInvertedQual(Query *parsetree,
 					   CmdType event)
 {
 	/* Don't scribble on the passed qual (it's in the relcache!) */
-	Node	   *new_qual = (Node *) copyObject(rule_qual);
+	Node	   *new_qual = copyObject(rule_qual);
 	acquireLocksOnSubLinks_context context;
 
 	context.for_execute = true;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index b23a3b7046..da02cfd25c 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1412,11 +1412,11 @@ ReplaceVarsFromTargetList_callback(Var *var,
 	else
 	{
 		/* Make a copy of the tlist item to return */
-		Node	   *newnode = copyObject(tle->expr);
+		Expr	   *newnode = copyObject(tle->expr);
 
 		/* Must adjust varlevelsup if tlist item is from higher query */
 		if (var->varlevelsup > 0)
-			IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
+			IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
 
 		/*
 		 * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
@@ -1428,12 +1428,12 @@ ReplaceVarsFromTargetList_callback(Var *var,
 		 * create semantic oddities that users of rules would probably prefer
 		 * not to cope with.  So treat it as an unimplemented feature.
 		 */
-		if (contains_multiexpr_param(newnode, NULL))
+		if (contains_multiexpr_param((Node *) newnode, NULL))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
 
-		return newnode;
+		return (Node *) newnode;
 	}
 }
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b07d6c6cb9..3de4c0aed5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -616,7 +616,7 @@ pg_parse_query(const char *query_string)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass raw parsetrees through copyObject() */
 	{
-		List	   *new_list = (List *) copyObject(raw_parsetree_list);
+		List	   *new_list = copyObject(raw_parsetree_list);
 
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, raw_parsetree_list))
@@ -756,7 +756,7 @@ pg_rewrite_query(Query *query)
 	{
 		List	   *new_list;
 
-		new_list = (List *) copyObject(querytree_list);
+		new_list = copyObject(querytree_list);
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, querytree_list))
 			elog(WARNING, "copyObject() failed to produce equal parse tree");
@@ -803,7 +803,7 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass plan output through copyObject() */
 	{
-		PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
+		PlannedStmt *new_plan = copyObject(plan);
 
 		/*
 		 * equal() currently does not have routines to compare Plan nodes, so
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index dffc92762b..15125ee85f 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -361,7 +361,7 @@ CompleteCachedPlan(CachedPlanSource *plansource,
 												  "CachedPlanQuery",
 												  ALLOCSET_START_SMALL_SIZES);
 		MemoryContextSwitchTo(querytree_context);
-		querytree_list = (List *) copyObject(querytree_list);
+		querytree_list = copyObject(querytree_list);
 	}
 
 	plansource->query_context = querytree_context;
@@ -734,7 +734,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
 											  ALLOCSET_START_SMALL_SIZES);
 	oldcxt = MemoryContextSwitchTo(querytree_context);
 
-	qlist = (List *) copyObject(tlist);
+	qlist = copyObject(tlist);
 
 	/*
 	 * Use the planner machinery to extract dependencies.  Data is saved in
@@ -909,7 +909,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	if (qlist == NIL)
 	{
 		if (!plansource->is_oneshot)
-			qlist = (List *) copyObject(plansource->query_list);
+			qlist = copyObject(plansource->query_list);
 		else
 			qlist = plansource->query_list;
 	}
@@ -953,7 +953,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		 */
 		MemoryContextSwitchTo(plan_context);
 
-		plist = (List *) copyObject(plist);
+		plist = copyObject(plist);
 	}
 	else
 		plan_context = CurrentMemoryContext;
@@ -1367,9 +1367,9 @@ CopyCachedPlan(CachedPlanSource *plansource)
 											  "CachedPlanQuery",
 											  ALLOCSET_START_SMALL_SIZES);
 	MemoryContextSwitchTo(querytree_context);
-	newsource->query_list = (List *) copyObject(plansource->query_list);
-	newsource->relationOids = (List *) copyObject(plansource->relationOids);
-	newsource->invalItems = (List *) copyObject(plansource->invalItems);
+	newsource->query_list = copyObject(plansource->query_list);
+	newsource->relationOids = copyObject(plansource->relationOids);
+	newsource->invalItems = copyObject(plansource->invalItems);
 	if (plansource->search_path)
 		newsource->search_path = CopyOverrideSearchPath(plansource->search_path);
 	newsource->query_context = querytree_context;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9001e202b0..6f75546f33 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4620,7 +4620,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indexprs)
-		return (List *) copyObject(relation->rd_indexprs);
+		return copyObject(relation->rd_indexprs);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4656,7 +4656,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indexprs = (List *) copyObject(result);
+	relation->rd_indexprs = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
@@ -4683,7 +4683,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indpred)
-		return (List *) copyObject(relation->rd_indpred);
+		return copyObject(relation->rd_indpred);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4725,7 +4725,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indpred = (List *) copyObject(result);
+	relation->rd_indpred = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 28aca928a8..bd6a102df7 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -614,6 +614,10 @@ extern int16 *readAttrNumberCols(int numCols);
  * nodes/copyfuncs.c
  */
 extern void *copyObject(const void *obj);
+/* cast result back to argument type, if supported by compiler */
+#ifdef HAVE_TYPEOF
+#define copyObject(obj) ((typeof(obj)) copyObject(obj))
+#endif
 
 /*
  * nodes/equalfuncs.c
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 976024a164..ccb93d8c80 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -17,8 +17,8 @@
 #include "nodes/relation.h"
 
 
-extern TargetEntry *tlist_member(Node *node, List *targetlist);
-extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
+extern TargetEntry *tlist_member(Expr *node, List *targetlist);
+extern TargetEntry *tlist_member_ignore_relabel(Expr *node, List *targetlist);
 
 extern List *add_to_flat_tlist(List *tlist, List *exprs);
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bcd8a1160..c95c30a1b2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -600,6 +600,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #undef HAVE_TOWLOWER
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#undef HAVE_TYPEOF
+
 /* Define to 1 if you have the external array `tzname'. */
 #undef HAVE_TZNAME
 
@@ -922,6 +925,9 @@
 /* Define to empty if the C compiler does not understand signed types. */
 #undef signed
 
+/* Define to how the compiler spells `typeof'. */
+#undef typeof
+
 /* Define to the type of an unsigned integer type wide enough to hold a
    pointer, if such a type exists, and if the system does not define it. */
 #undef uintptr_t
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 3e4132cd82..cc60f9432d 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -451,6 +451,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #define HAVE_TOWLOWER 1
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#define HAVE_TYPEOF 1
+
 /* Define to 1 if you have the external array `tzname'. */
 /* #undef HAVE_TZNAME */
 
@@ -679,3 +682,6 @@
 
 /* Define to empty if the C compiler does not understand signed types. */
 /* #undef signed */
+
+/* Define to how the compiler spells `typeof'. */
+#define typeof decltype
-- 
2.12.0

#5Mark Dilger
hornschnorter@gmail.com
In reply to: Peter Eisentraut (#4)
Re: cast result of copyNode()

The following review has been posted through the commitfest application:
make installcheck-world: tested, passed
Implements feature: not tested
Spec compliant: not tested
Documentation: not tested

Hi Peter,

I like the patch so far, and it passes all the regression tests for me on both mac and linux. I
am very much in favor of having more compiler type checking, so +1 from me.

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

I don't necessarily have a problem with that, but it struck me as a bit odd, and
grep'ing through the sources, I don't see anywhere else in the project where that
is done for a function of the same number of arguments, and only one other
place where it is done at all:

$ find src/ -type f -name "*.h" -or -name "*.c" | xargs cat | perl -e 'while(<>) { print if (/^#define (\w+)\b.*\b\1\s*\(\b/); }'
#define copyObject(obj) ((typeof(obj)) copyObject(obj))
#define mkdir(a,b) mkdir(a)

I'm just flagging that for discussion if anybody thinks it is too magical.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Mark Dilger (#5)
Re: cast result of copyNode()

Mark Dilger <hornschnorter@gmail.com> writes:

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

I don't necessarily have a problem with that, but it struck me as a bit odd, and
grep'ing through the sources, I don't see anywhere else in the project where that
is done for a function of the same number of arguments, and only one other
place where it is done at all:

I agree that that seems like a bad idea. Better to rename the underlying
function --- for one thing, that provides an "out" if some caller doesn't
want this behavior.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Mark Dilger (#5)
1 attachment(s)
Re: cast result of copyNode()

On 3/7/17 18:27, Mark Dilger wrote:

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

Yeah, that's a bit silly. Here is an updated version that changes that.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachments:

v2-0001-Cast-result-of-copyObject-to-correct-type.patchapplication/x-patch; name=v2-0001-Cast-result-of-copyObject-to-correct-type.patchDownload
From 696e605c358e7ca5786a13c82504e9d7dbed5eb4 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Thu, 9 Mar 2017 15:18:59 -0500
Subject: [PATCH v2] Cast result of copyObject() to correct type

copyObject() is declared to return void *, which allows easily assigning
the result independent of the input, but it loses all type checking.

If the compiler supports typeof or something similar, cast the result to
the input type.  This creates a greater amount of type safety.  In some
cases, where the result is assigned to a generic type such as Node * or
Expr *, new casts are now necessary, but in general casts are now
unnecessary in the normal case and indicate that something unusual is
happening.
---
 config/c-compiler.m4                      | 27 +++++++++++++++++++++
 configure                                 | 40 +++++++++++++++++++++++++++++++
 configure.in                              |  1 +
 src/backend/bootstrap/bootstrap.c         |  4 ++--
 src/backend/commands/copy.c               |  2 +-
 src/backend/commands/createas.c           |  2 +-
 src/backend/commands/event_trigger.c      |  8 +++----
 src/backend/commands/prepare.c            |  4 ++--
 src/backend/commands/view.c               |  2 +-
 src/backend/nodes/copyfuncs.c             |  8 +++----
 src/backend/optimizer/path/indxpath.c     |  4 ++--
 src/backend/optimizer/plan/createplan.c   |  6 ++---
 src/backend/optimizer/plan/initsplan.c    |  8 +++----
 src/backend/optimizer/plan/planagg.c      |  4 ++--
 src/backend/optimizer/plan/planner.c      |  4 ++--
 src/backend/optimizer/plan/setrefs.c      | 26 ++++++++++----------
 src/backend/optimizer/plan/subselect.c    | 14 +++++------
 src/backend/optimizer/prep/prepjointree.c |  4 ++--
 src/backend/optimizer/prep/preptlist.c    |  2 +-
 src/backend/optimizer/prep/prepunion.c    |  4 ++--
 src/backend/optimizer/util/tlist.c        | 12 +++++-----
 src/backend/parser/analyze.c              |  2 +-
 src/backend/parser/gram.y                 |  2 +-
 src/backend/parser/parse_clause.c         |  2 +-
 src/backend/parser/parse_expr.c           |  2 +-
 src/backend/parser/parse_relation.c       |  2 +-
 src/backend/parser/parse_utilcmd.c        |  6 ++---
 src/backend/rewrite/rewriteHandler.c      |  8 +++----
 src/backend/rewrite/rewriteManip.c        |  8 +++----
 src/backend/tcop/postgres.c               |  6 ++---
 src/backend/utils/cache/plancache.c       | 14 +++++------
 src/backend/utils/cache/relcache.c        |  8 +++----
 src/include/nodes/nodes.h                 |  8 ++++++-
 src/include/optimizer/tlist.h             |  4 ++--
 src/include/pg_config.h.in                |  6 +++++
 src/include/pg_config.h.win32             |  6 +++++
 36 files changed, 178 insertions(+), 92 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 7d901e1f1a..7afaec5f85 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -178,6 +178,33 @@ fi])# PGAC_C_STATIC_ASSERT
 
 
 
+# PGAC_C_TYPEOF
+# -------------
+# Check if the C compiler understands typeof or a variant.  Define
+# HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
+#
+AC_DEFUN([PGAC_C_TYPEOF],
+[AC_CACHE_CHECK(for typeof, pgac_cv_c_typeof,
+[pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;])],
+[pgac_cv_c_typeof=$pgac_kw])
+  test "$pgac_cv_c_typeof" != no && break
+done])
+if test "$pgac_cv_c_typeof" != no; then
+  AC_DEFINE(HAVE_TYPEOF, 1,
+            [Define to 1 if your compiler understands `typeof' or something similar.])
+  if test "$pgac_cv_c_typeof" != typeof; then
+    AC_DEFINE(typeof, $pgac_cv_c_typeof, [Define to how the compiler spells `typeof'.])
+  fi
+fi])# PGAC_C_TYPEOF
+
+
+
 # PGAC_C_TYPES_COMPATIBLE
 # -----------------------
 # Check if the C compiler understands __builtin_types_compatible_p,
diff --git a/configure b/configure
index b5cdebb510..47a1a59e8e 100755
--- a/configure
+++ b/configure
@@ -11399,6 +11399,46 @@ if test x"$pgac_cv__static_assert" = xyes ; then
 $as_echo "#define HAVE__STATIC_ASSERT 1" >>confdefs.h
 
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5
+$as_echo_n "checking for typeof... " >&6; }
+if ${pgac_cv_c_typeof+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  pgac_cv_c_typeof=no
+for pgac_kw in typeof __typeof__ decltype; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv_c_typeof=$pgac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  test "$pgac_cv_c_typeof" != no && break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_c_typeof" >&5
+$as_echo "$pgac_cv_c_typeof" >&6; }
+if test "$pgac_cv_c_typeof" != no; then
+
+$as_echo "#define HAVE_TYPEOF 1" >>confdefs.h
+
+  if test "$pgac_cv_c_typeof" != typeof; then
+
+$as_echo "#define typeof \$pgac_cv_c_typeof" >>confdefs.h
+
+  fi
+fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p" >&5
 $as_echo_n "checking for __builtin_types_compatible_p... " >&6; }
 if ${pgac_cv__types_compatible+:} false; then :
diff --git a/configure.in b/configure.in
index 1d99cda1d8..e8acfdc98d 100644
--- a/configure.in
+++ b/configure.in
@@ -1317,6 +1317,7 @@ AC_C_FLEXIBLE_ARRAY_MEMBER
 PGAC_C_SIGNED
 PGAC_C_FUNCNAME_SUPPORT
 PGAC_C_STATIC_ASSERT
+PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_BSWAP32
 PGAC_C_BUILTIN_BSWAP64
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 6511c6064b..1514a4c890 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1078,11 +1078,11 @@ index_register(Oid heap,
 
 	memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
 	/* expressions will likely be null, but may as well copy it */
-	newind->il_info->ii_Expressions = (List *)
+	newind->il_info->ii_Expressions =
 		copyObject(indexInfo->ii_Expressions);
 	newind->il_info->ii_ExpressionsState = NIL;
 	/* predicate will likely be null, but may as well copy it */
-	newind->il_info->ii_Predicate = (List *)
+	newind->il_info->ii_Predicate =
 		copyObject(indexInfo->ii_Predicate);
 	newind->il_info->ii_PredicateState = NIL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3102ab18c5..a3cf7dcdf0 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1463,7 +1463,7 @@ BeginCopy(ParseState *pstate,
 		 * function and is executed repeatedly.  (See also the same hack in
 		 * DECLARE CURSOR and PREPARE.)  XXX FIXME someday.
 		 */
-		rewritten = pg_analyze_and_rewrite((RawStmt *) copyObject(raw_query),
+		rewritten = pg_analyze_and_rewrite(copyObject(raw_query),
 										   pstate->p_sourcetext, NULL, 0);
 
 		/* check that we got back something we can work with */
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 646a88409f..520a5da8a2 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -315,7 +315,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
 		 * and is executed repeatedly.  (See also the same hack in EXPLAIN and
 		 * PREPARE.)
 		 */
-		rewritten = QueryRewrite((Query *) copyObject(query));
+		rewritten = QueryRewrite(copyObject(query));
 
 		/* SELECT should never rewrite to more or less than one SELECT query */
 		if (list_length(rewritten) != 1)
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 346b347ae1..f3073ab2ed 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1866,7 +1866,7 @@ EventTriggerCollectAlterOpFam(AlterOpFamilyStmt *stmt, Oid opfamoid,
 					 OperatorFamilyRelationId, opfamoid);
 	command->d.opfam.operators = operators;
 	command->d.opfam.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1899,7 +1899,7 @@ EventTriggerCollectCreateOpClass(CreateOpClassStmt *stmt, Oid opcoid,
 					 OperatorClassRelationId, opcoid);
 	command->d.createopc.operators = operators;
 	command->d.createopc.procedures = procedures;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1934,7 +1934,7 @@ EventTriggerCollectAlterTSConfig(AlterTSConfigurationStmt *stmt, Oid cfgId,
 	command->d.atscfg.dictIds = palloc(sizeof(Oid) * ndicts);
 	memcpy(command->d.atscfg.dictIds, dictIds, sizeof(Oid) * ndicts);
 	command->d.atscfg.ndicts = ndicts;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
@@ -1964,7 +1964,7 @@ EventTriggerCollectAlterDefPrivs(AlterDefaultPrivilegesStmt *stmt)
 	command->type = SCT_AlterDefaultPrivileges;
 	command->d.defprivs.objtype = stmt->action->objtype;
 	command->in_extension = creating_extension;
-	command->parsetree = copyObject(stmt);
+	command->parsetree = (Node *) copyObject(stmt);
 
 	currentEventTriggerState->commandList =
 		lappend(currentEventTriggerState->commandList, command);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 1cf0d2b971..7e65b0850e 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -352,7 +352,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
 	 * We have to run parse analysis for the expressions.  Since the parser is
 	 * not cool about scribbling on its input, copy first.
 	 */
-	params = (List *) copyObject(params);
+	params = copyObject(params);
 
 	pstate = make_parsestate(NULL);
 	pstate->p_sourcetext = queryString;
@@ -554,7 +554,7 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
 	tlist = CachedPlanGetTargetList(stmt->plansource);
 
 	/* Copy into caller's context in case plan gets invalidated */
-	return (List *) copyObject(tlist);
+	return copyObject(tlist);
 }
 
 /*
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 7d76f567a8..35e25db7dc 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -373,7 +373,7 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
 	 * Var node twice.  copyObject will expand any multiply-referenced subtree
 	 * into multiple copies.
 	 */
-	viewParse = (Query *) copyObject(viewParse);
+	viewParse = copyObject(viewParse);
 
 	/* Create a dummy ParseState for addRangeTableEntryForRelation */
 	pstate = make_parsestate(NULL);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index bfc2ac1716..c237952c78 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -43,7 +43,7 @@
 
 /* Copy a field that is a pointer to some kind of Node or Node tree */
 #define COPY_NODE_FIELD(fldname) \
-	(newnode->fldname = copyObject(from->fldname))
+	(newnode->fldname = copyObjectImpl(from->fldname))
 
 /* Copy a field that is a pointer to a Bitmapset */
 #define COPY_BITMAPSET_FIELD(fldname) \
@@ -4459,7 +4459,7 @@ _copyDropSubscriptionStmt(const DropSubscriptionStmt *from)
  */
 #define COPY_NODE_CELL(new, old)					\
 	(new) = (ListCell *) palloc(sizeof(ListCell));	\
-	lfirst(new) = copyObject(lfirst(old));
+	lfirst(new) = copyObjectImpl(lfirst(old));
 
 static List *
 _copyList(const List *from)
@@ -4562,13 +4562,13 @@ _copyForeignKeyCacheInfo(const ForeignKeyCacheInfo *from)
 
 
 /*
- * copyObject
+ * copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h
  *
  * Create a copy of a Node tree or list.  This is a "deep" copy: all
  * substructure is copied too, recursively.
  */
 void *
-copyObject(const void *from)
+copyObjectImpl(const void *from)
 {
 	void	   *retval;
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index c2b72d410a..a5d19f9b1c 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -4004,9 +4004,9 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
 									   matching_cols);
 		rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
 										 matching_cols);
-		rc->largs = list_truncate((List *) copyObject(clause->largs),
+		rc->largs = list_truncate(copyObject(clause->largs),
 								  matching_cols);
-		rc->rargs = list_truncate((List *) copyObject(clause->rargs),
+		rc->rargs = list_truncate(copyObject(clause->rargs),
 								  matching_cols);
 		return (Expr *) rc;
 	}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d002e6d567..92d3d876a8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1274,7 +1274,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -1317,7 +1317,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
 	groupColPos = 0;
 	foreach(l, uniq_exprs)
 	{
-		Node	   *uniqexpr = lfirst(l);
+		Expr	   *uniqexpr = lfirst(l);
 		TargetEntry *tle;
 
 		tle = tlist_member(uniqexpr, newtlist);
@@ -4331,7 +4331,7 @@ process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
 				/* No, so add it */
 				nlp = makeNode(NestLoopParam);
 				nlp->paramno = pitem->paramId;
-				nlp->paramval = copyObject(phv);
+				nlp->paramval = (Var *) copyObject(phv);
 				root->curOuterParams = lappend(root->curOuterParams, nlp);
 			}
 		}
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index b4ac224a7a..53aefbd1a3 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -2306,8 +2306,8 @@ process_implied_equality(PlannerInfo *root,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
@@ -2369,8 +2369,8 @@ build_implied_join_equality(Oid opno,
 	clause = make_opclause(opno,
 						   BOOLOID,		/* opresulttype */
 						   false,		/* opretset */
-						   (Expr *) copyObject(item1),
-						   (Expr *) copyObject(item2),
+						   copyObject(item1),
+						   copyObject(item2),
 						   InvalidOid,
 						   collation);
 
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index c3fbf3cdf8..55657360fc 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -369,11 +369,11 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
 	subroot->outer_params = NULL;
 	subroot->init_plans = NIL;
 
-	subroot->parse = parse = (Query *) copyObject(root->parse);
+	subroot->parse = parse = copyObject(root->parse);
 	IncrementVarSublevelsUp((Node *) parse, 1, 1);
 
 	/* append_rel_list might contain outer Vars? */
-	subroot->append_rel_list = (List *) copyObject(root->append_rel_list);
+	subroot->append_rel_list = copyObject(root->append_rel_list);
 	IncrementVarSublevelsUp((Node *) subroot->append_rel_list, 1, 1);
 	/* There shouldn't be any OJ info to translate, as yet */
 	Assert(subroot->join_info_list == NIL);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 02286d9c52..3d52bf6ed7 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1113,7 +1113,7 @@ inheritance_planner(PlannerInfo *root)
 		 * executor doesn't need to see the modified copies --- we can just
 		 * pass it the original rowMarks list.)
 		 */
-		subroot->rowMarks = (List *) copyObject(root->rowMarks);
+		subroot->rowMarks = copyObject(root->rowMarks);
 
 		/*
 		 * The append_rel_list likewise might contain references to subquery
@@ -1135,7 +1135,7 @@ inheritance_planner(PlannerInfo *root)
 				AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
 
 				if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
-					appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
+					appinfo2 = copyObject(appinfo2);
 
 				subroot->append_rel_list = lappend(subroot->append_rel_list,
 												   appinfo2);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 5f3027e96f..8de28bb03d 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -111,10 +111,10 @@ static Var *search_indexed_tlist_for_var(Var *var,
 							 indexed_tlist *itlist,
 							 Index newvarno,
 							 int rtoffset);
-static Var *search_indexed_tlist_for_non_var(Node *node,
+static Var *search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist,
 								 Index newvarno);
-static Var *search_indexed_tlist_for_sortgroupref(Node *node,
+static Var *search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno);
@@ -1419,7 +1419,7 @@ fix_param_node(PlannerInfo *root, Param *p)
 			elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
 		return copyObject(list_nth(params, colno - 1));
 	}
-	return copyObject(p);
+	return (Node *) copyObject(p);
 }
 
 /*
@@ -1706,7 +1706,7 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
 		if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
 		{
 			newexpr = (Node *)
-				search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+				search_indexed_tlist_for_sortgroupref(tle->expr,
 													  tle->ressortgroupref,
 													  subplan_itlist,
 													  OUTER_VAR);
@@ -1789,7 +1789,7 @@ convert_combining_aggrefs(Node *node, void *context)
 		 */
 		child_agg->args = NIL;
 		child_agg->aggfilter = NULL;
-		parent_agg = (Aggref *) copyObject(child_agg);
+		parent_agg = copyObject(child_agg);
 		child_agg->args = orig_agg->args;
 		child_agg->aggfilter = orig_agg->aggfilter;
 
@@ -2033,7 +2033,7 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
  * so there's a correctness reason not to call it unless that's set.
  */
 static Var *
-search_indexed_tlist_for_non_var(Node *node,
+search_indexed_tlist_for_non_var(Expr *node,
 								 indexed_tlist *itlist, Index newvarno)
 {
 	TargetEntry *tle;
@@ -2074,7 +2074,7 @@ search_indexed_tlist_for_non_var(Node *node,
  * And it's also faster than search_indexed_tlist_for_non_var.
  */
 static Var *
-search_indexed_tlist_for_sortgroupref(Node *node,
+search_indexed_tlist_for_sortgroupref(Expr *node,
 									  Index sortgroupref,
 									  indexed_tlist *itlist,
 									  Index newvarno)
@@ -2208,7 +2208,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->outer_itlist && context->outer_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->outer_itlist,
 													  OUTER_VAR);
 			if (newvar)
@@ -2216,7 +2216,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 		}
 		if (context->inner_itlist && context->inner_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->inner_itlist,
 													  INNER_VAR);
 			if (newvar)
@@ -2231,7 +2231,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	/* Try matching more complex expressions too, if tlists have any */
 	if (context->outer_itlist && context->outer_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->outer_itlist,
 												  OUTER_VAR);
 		if (newvar)
@@ -2239,7 +2239,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
 	}
 	if (context->inner_itlist && context->inner_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->inner_itlist,
 												  INNER_VAR);
 		if (newvar)
@@ -2323,7 +2323,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 		/* See if the PlaceHolderVar has bubbled up from a lower plan node */
 		if (context->subplan_itlist->has_ph_vars)
 		{
-			newvar = search_indexed_tlist_for_non_var((Node *) phv,
+			newvar = search_indexed_tlist_for_non_var((Expr *) phv,
 													  context->subplan_itlist,
 													  context->newvarno);
 			if (newvar)
@@ -2359,7 +2359,7 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
 	/* Try matching more complex expressions too, if tlist has any */
 	if (context->subplan_itlist->has_non_vars)
 	{
-		newvar = search_indexed_tlist_for_non_var(node,
+		newvar = search_indexed_tlist_for_non_var((Expr *) node,
 												  context->subplan_itlist,
 												  context->newvarno);
 		if (newvar)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 6fa6540662..db0e5b31e2 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -125,7 +125,7 @@ assign_param_for_var(PlannerInfo *root, Var *var)
 	}
 
 	/* Nope, so make a new one */
-	var = (Var *) copyObject(var);
+	var = copyObject(var);
 	var->varlevelsup = 0;
 
 	pitem = makeNode(PlannerParamItem);
@@ -224,7 +224,7 @@ assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
 	}
 
 	/* Nope, so make a new one */
-	phv = (PlaceHolderVar *) copyObject(phv);
+	phv = copyObject(phv);
 	if (phv->phlevelsup != 0)
 	{
 		IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0);
@@ -316,7 +316,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	agg = (Aggref *) copyObject(agg);
+	agg = copyObject(agg);
 	IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
 	Assert(agg->agglevelsup == 0);
 
@@ -358,7 +358,7 @@ replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp)
 	 * It does not seem worthwhile to try to match duplicate outer aggs. Just
 	 * make a new slot every time.
 	 */
-	grp = (GroupingFunc *) copyObject(grp);
+	grp = copyObject(grp);
 	IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0);
 	Assert(grp->agglevelsup == 0);
 
@@ -491,7 +491,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 	 * same sub-Query node, but the planner wants to scribble on the Query.
 	 * Try to clean this up when we do querytree redesign...
 	 */
-	subquery = (Query *) copyObject(orig_subquery);
+	subquery = copyObject(orig_subquery);
 
 	/*
 	 * If it's an EXISTS subplan, we might be able to simplify it.
@@ -568,7 +568,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery,
 		List	   *paramIds;
 
 		/* Make a second copy of the original subquery */
-		subquery = (Query *) copyObject(orig_subquery);
+		subquery = copyObject(orig_subquery);
 		/* and re-simplify */
 		simple_exists = simplify_EXISTS_query(root, subquery);
 		Assert(simple_exists);
@@ -1431,7 +1431,7 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
 	 * Copy the subquery so we can modify it safely (see comments in
 	 * make_subplan).
 	 */
-	subselect = (Query *) copyObject(subselect);
+	subselect = copyObject(subselect);
 
 	/*
 	 * See if the subquery can be simplified based on the knowledge that it's
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index 048815d7b0..348c6b791f 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1592,7 +1592,7 @@ pull_up_simple_values(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte)
 	 * Need a modifiable copy of the VALUES list to hack on, just in case it's
 	 * multiply referenced.
 	 */
-	values_list = (List *) copyObject(linitial(rte->values_lists));
+	values_list = copyObject(linitial(rte->values_lists));
 
 	/*
 	 * The VALUES RTE can't contain any Vars of level zero, let alone any that
@@ -2128,7 +2128,7 @@ pullup_replace_vars_callback(Var *var,
 				 varattno);
 
 		/* Make a copy of the tlist item to return */
-		newnode = copyObject(tle->expr);
+		newnode = (Node *) copyObject(tle->expr);
 
 		/* Insert PlaceHolderVar if needed */
 		if (rcon->need_phvs)
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 824af3f54c..de47153bac 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -180,7 +180,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
 				var->varno == result_relation)
 				continue;		/* don't need it */
 
-			if (tlist_member((Node *) var, tlist))
+			if (tlist_member((Expr *) var, tlist))
 				continue;		/* already got it */
 
 			tle = makeTargetEntry((Expr *) var,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1389db18ba..e7f6d6b10f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1274,7 +1274,7 @@ generate_append_tlist(List *colTypes, List *colCollations,
 static List *
 generate_setop_grouplist(SetOperationStmt *op, List *targetlist)
 {
-	List	   *grouplist = (List *) copyObject(op->groupClauses);
+	List	   *grouplist = copyObject(op->groupClauses);
 	ListCell   *lg;
 	ListCell   *lt;
 
@@ -1840,7 +1840,7 @@ adjust_appendrel_attrs_mutator(Node *node,
 
 					rte = rt_fetch(appinfo->parent_relid,
 								   context->root->parse->rtable);
-					fields = (List *) copyObject(appinfo->translated_vars);
+					fields = copyObject(appinfo->translated_vars);
 					rowexpr = makeNode(RowExpr);
 					rowexpr->args = fields;
 					rowexpr->row_typeid = var->vartype;
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 5728f70c8b..09523853d0 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -51,7 +51,7 @@ static bool split_pathtarget_walker(Node *node,
  *	  equal() to the given expression.  Result is NULL if no such member.
  */
 TargetEntry *
-tlist_member(Node *node, List *targetlist)
+tlist_member(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
@@ -72,12 +72,12 @@ tlist_member(Node *node, List *targetlist)
  *	  involving binary-compatible sort operations.
  */
 TargetEntry *
-tlist_member_ignore_relabel(Node *node, List *targetlist)
+tlist_member_ignore_relabel(Expr *node, List *targetlist)
 {
 	ListCell   *temp;
 
 	while (node && IsA(node, RelabelType))
-		node = (Node *) ((RelabelType *) node)->arg;
+		node = ((RelabelType *) node)->arg;
 
 	foreach(temp, targetlist)
 	{
@@ -139,7 +139,7 @@ add_to_flat_tlist(List *tlist, List *exprs)
 
 	foreach(lc, exprs)
 	{
-		Node	   *expr = (Node *) lfirst(lc);
+		Expr	   *expr = (Expr *) lfirst(lc);
 
 		if (!tlist_member(expr, tlist))
 		{
@@ -762,7 +762,7 @@ apply_pathtarget_labeling_to_tlist(List *tlist, PathTarget *target)
 			if (expr && IsA(expr, Var))
 				tle = tlist_member_match_var((Var *) expr, tlist);
 			else
-				tle = tlist_member((Node *) expr, tlist);
+				tle = tlist_member(expr, tlist);
 
 			/*
 			 * Complain if noplace for the sortgrouprefs label, or if we'd
@@ -999,7 +999,7 @@ split_pathtarget_at_srfs(PlannerInfo *root,
 
 				foreach(lcx, input_srfs)
 				{
-					Node	   *srf = (Node *) lfirst(lcx);
+					Expr	   *srf = (Expr *) lfirst(lcx);
 
 					if (list_member(prev_level_tlist, srf))
 						add_new_column_to_pathtarget(ntarget, copyObject(srf));
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 3571e50aea..8f11c46621 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -2556,7 +2556,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
 		 * in the IntoClause because that's where intorel_startup() can
 		 * conveniently get it from.
 		 */
-		stmt->into->viewQuery = copyObject(query);
+		stmt->into->viewQuery = (Node *) copyObject(query);
 	}
 
 	/* represent the command as a utility Query */
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e7acc2d9a2..4334f8f6c8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -15444,7 +15444,7 @@ TableFuncTypeName(List *columns)
 	{
 		FunctionParameter *p = (FunctionParameter *) linitial(columns);
 
-		result = (TypeName *) copyObject(p->argType);
+		result = copyObject(p->argType);
 	}
 	else
 		result = SystemTypeName("record");
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 47ca685b56..4f391d2d41 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -348,7 +348,7 @@ transformJoinUsingClause(ParseState *pstate,
 
 		/* Now create the lvar = rvar join condition */
 		e = makeSimpleA_Expr(AEXPR_OP, "=",
-							 copyObject(lvar), copyObject(rvar),
+							 (Node *) copyObject(lvar), (Node *) copyObject(rvar),
 							 -1);
 
 		/* Prepare to combine into an AND clause, if multiple join columns */
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index d3ed073cee..323be23bca 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1255,7 +1255,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
 			/* ROW() op ROW() is handled specially */
 			cmp = make_row_comparison_op(pstate,
 										 a->name,
-							  (List *) copyObject(((RowExpr *) lexpr)->args),
+										 copyObject(((RowExpr *) lexpr)->args),
 										 ((RowExpr *) rexpr)->args,
 										 a->location);
 		}
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 2eea258d28..2c19e0cbf5 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1804,7 +1804,7 @@ addRangeTableEntryForJoin(ParseState *pstate,
 	rte->joinaliasvars = aliasvars;
 	rte->alias = alias;
 
-	eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL);
+	eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL);
 	numaliases = list_length(eref->colnames);
 
 	/* fill in any unspecified alias columns */
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 673276a9d3..1ae43dc25d 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -167,7 +167,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (CreateStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2107,7 +2107,7 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
 	 * We must not scribble on the passed-in IndexStmt, so copy it.  (This is
 	 * overkill, but easy.)
 	 */
-	stmt = (IndexStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Set up pstate */
 	pstate = make_parsestate(NULL);
@@ -2521,7 +2521,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 	 * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
 	 * is overkill, but easy.)
 	 */
-	stmt = (AlterTableStmt *) copyObject(stmt);
+	stmt = copyObject(stmt);
 
 	/* Caller is responsible for locking the relation */
 	rel = relation_open(relid, NoLock);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 354e5d0462..424be0c768 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -349,8 +349,8 @@ rewriteRuleAction(Query *parsetree,
 	 * Make modifiable copies of rule action and qual (what we're passed are
 	 * the stored versions in the relcache; don't touch 'em!).
 	 */
-	rule_action = (Query *) copyObject(rule_action);
-	rule_qual = (Node *) copyObject(rule_qual);
+	rule_action = copyObject(rule_action);
+	rule_qual = copyObject(rule_qual);
 
 	/*
 	 * Acquire necessary locks and fix any deleted JOIN RTE entries.
@@ -408,7 +408,7 @@ rewriteRuleAction(Query *parsetree,
 	 * that rule action's rtable is separate and shares no substructure with
 	 * the main rtable.  Hence do a deep copy here.
 	 */
-	sub_action->rtable = list_concat((List *) copyObject(parsetree->rtable),
+	sub_action->rtable = list_concat(copyObject(parsetree->rtable),
 									 sub_action->rtable);
 
 	/*
@@ -1897,7 +1897,7 @@ CopyAndAddInvertedQual(Query *parsetree,
 					   CmdType event)
 {
 	/* Don't scribble on the passed qual (it's in the relcache!) */
-	Node	   *new_qual = (Node *) copyObject(rule_qual);
+	Node	   *new_qual = copyObject(rule_qual);
 	acquireLocksOnSubLinks_context context;
 
 	context.for_execute = true;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index b23a3b7046..da02cfd25c 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1412,11 +1412,11 @@ ReplaceVarsFromTargetList_callback(Var *var,
 	else
 	{
 		/* Make a copy of the tlist item to return */
-		Node	   *newnode = copyObject(tle->expr);
+		Expr	   *newnode = copyObject(tle->expr);
 
 		/* Must adjust varlevelsup if tlist item is from higher query */
 		if (var->varlevelsup > 0)
-			IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
+			IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
 
 		/*
 		 * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
@@ -1428,12 +1428,12 @@ ReplaceVarsFromTargetList_callback(Var *var,
 		 * create semantic oddities that users of rules would probably prefer
 		 * not to cope with.  So treat it as an unimplemented feature.
 		 */
-		if (contains_multiexpr_param(newnode, NULL))
+		if (contains_multiexpr_param((Node *) newnode, NULL))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
 
-		return newnode;
+		return (Node *) newnode;
 	}
 }
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index b07d6c6cb9..3de4c0aed5 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -616,7 +616,7 @@ pg_parse_query(const char *query_string)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass raw parsetrees through copyObject() */
 	{
-		List	   *new_list = (List *) copyObject(raw_parsetree_list);
+		List	   *new_list = copyObject(raw_parsetree_list);
 
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, raw_parsetree_list))
@@ -756,7 +756,7 @@ pg_rewrite_query(Query *query)
 	{
 		List	   *new_list;
 
-		new_list = (List *) copyObject(querytree_list);
+		new_list = copyObject(querytree_list);
 		/* This checks both copyObject() and the equal() routines... */
 		if (!equal(new_list, querytree_list))
 			elog(WARNING, "copyObject() failed to produce equal parse tree");
@@ -803,7 +803,7 @@ pg_plan_query(Query *querytree, int cursorOptions, ParamListInfo boundParams)
 #ifdef COPY_PARSE_PLAN_TREES
 	/* Optional debugging check: pass plan output through copyObject() */
 	{
-		PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
+		PlannedStmt *new_plan = copyObject(plan);
 
 		/*
 		 * equal() currently does not have routines to compare Plan nodes, so
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index dffc92762b..15125ee85f 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -361,7 +361,7 @@ CompleteCachedPlan(CachedPlanSource *plansource,
 												  "CachedPlanQuery",
 												  ALLOCSET_START_SMALL_SIZES);
 		MemoryContextSwitchTo(querytree_context);
-		querytree_list = (List *) copyObject(querytree_list);
+		querytree_list = copyObject(querytree_list);
 	}
 
 	plansource->query_context = querytree_context;
@@ -734,7 +734,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
 											  ALLOCSET_START_SMALL_SIZES);
 	oldcxt = MemoryContextSwitchTo(querytree_context);
 
-	qlist = (List *) copyObject(tlist);
+	qlist = copyObject(tlist);
 
 	/*
 	 * Use the planner machinery to extract dependencies.  Data is saved in
@@ -909,7 +909,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 	if (qlist == NIL)
 	{
 		if (!plansource->is_oneshot)
-			qlist = (List *) copyObject(plansource->query_list);
+			qlist = copyObject(plansource->query_list);
 		else
 			qlist = plansource->query_list;
 	}
@@ -953,7 +953,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
 		 */
 		MemoryContextSwitchTo(plan_context);
 
-		plist = (List *) copyObject(plist);
+		plist = copyObject(plist);
 	}
 	else
 		plan_context = CurrentMemoryContext;
@@ -1367,9 +1367,9 @@ CopyCachedPlan(CachedPlanSource *plansource)
 											  "CachedPlanQuery",
 											  ALLOCSET_START_SMALL_SIZES);
 	MemoryContextSwitchTo(querytree_context);
-	newsource->query_list = (List *) copyObject(plansource->query_list);
-	newsource->relationOids = (List *) copyObject(plansource->relationOids);
-	newsource->invalItems = (List *) copyObject(plansource->invalItems);
+	newsource->query_list = copyObject(plansource->query_list);
+	newsource->relationOids = copyObject(plansource->relationOids);
+	newsource->invalItems = copyObject(plansource->invalItems);
 	if (plansource->search_path)
 		newsource->search_path = CopyOverrideSearchPath(plansource->search_path);
 	newsource->query_context = querytree_context;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9001e202b0..6f75546f33 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4620,7 +4620,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indexprs)
-		return (List *) copyObject(relation->rd_indexprs);
+		return copyObject(relation->rd_indexprs);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4656,7 +4656,7 @@ RelationGetIndexExpressions(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indexprs = (List *) copyObject(result);
+	relation->rd_indexprs = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
@@ -4683,7 +4683,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Quick exit if we already computed the result. */
 	if (relation->rd_indpred)
-		return (List *) copyObject(relation->rd_indpred);
+		return copyObject(relation->rd_indpred);
 
 	/* Quick exit if there is nothing to do. */
 	if (relation->rd_indextuple == NULL ||
@@ -4725,7 +4725,7 @@ RelationGetIndexPredicate(Relation relation)
 
 	/* Now save a copy of the completed tree in the relcache entry. */
 	oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
-	relation->rd_indpred = (List *) copyObject(result);
+	relation->rd_indpred = copyObject(result);
 	MemoryContextSwitchTo(oldcxt);
 
 	return result;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2bc7a5df11..0aba092dca 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -621,7 +621,13 @@ extern int16 *readAttrNumberCols(int numCols);
 /*
  * nodes/copyfuncs.c
  */
-extern void *copyObject(const void *obj);
+extern void *copyObjectImpl(const void *obj);
+/* cast result back to argument type, if supported by compiler */
+#ifdef HAVE_TYPEOF
+#define copyObject(obj) ((typeof(obj)) copyObjectImpl(obj))
+#else
+#define copyObject(obj) copyObjectImpl(obj)
+#endif
 
 /*
  * nodes/equalfuncs.c
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index 976024a164..ccb93d8c80 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -17,8 +17,8 @@
 #include "nodes/relation.h"
 
 
-extern TargetEntry *tlist_member(Node *node, List *targetlist);
-extern TargetEntry *tlist_member_ignore_relabel(Node *node, List *targetlist);
+extern TargetEntry *tlist_member(Expr *node, List *targetlist);
+extern TargetEntry *tlist_member_ignore_relabel(Expr *node, List *targetlist);
 
 extern List *add_to_flat_tlist(List *tlist, List *exprs);
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bcd8a1160..c95c30a1b2 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -600,6 +600,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #undef HAVE_TOWLOWER
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#undef HAVE_TYPEOF
+
 /* Define to 1 if you have the external array `tzname'. */
 #undef HAVE_TZNAME
 
@@ -922,6 +925,9 @@
 /* Define to empty if the C compiler does not understand signed types. */
 #undef signed
 
+/* Define to how the compiler spells `typeof'. */
+#undef typeof
+
 /* Define to the type of an unsigned integer type wide enough to hold a
    pointer, if such a type exists, and if the system does not define it. */
 #undef uintptr_t
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 3e4132cd82..cc60f9432d 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -451,6 +451,9 @@
 /* Define to 1 if you have the `towlower' function. */
 #define HAVE_TOWLOWER 1
 
+/* Define to 1 if your compiler understands `typeof' or something similar. */
+#define HAVE_TYPEOF 1
+
 /* Define to 1 if you have the external array `tzname'. */
 /* #undef HAVE_TZNAME */
 
@@ -679,3 +682,6 @@
 
 /* Define to empty if the C compiler does not understand signed types. */
 /* #undef signed */
+
+/* Define to how the compiler spells `typeof'. */
+#define typeof decltype
-- 
2.12.0

#8David Steele
david@pgmasters.net
In reply to: Peter Eisentraut (#7)
Re: cast result of copyNode()

Hi Mark,

On 3/9/17 3:34 PM, Peter Eisentraut wrote:

On 3/7/17 18:27, Mark Dilger wrote:

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

Yeah, that's a bit silly. Here is an updated version that changes that.

Do you know when you'll have a chance to take a look at the updated patch?

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9Mark Dilger
hornschnorter@gmail.com
In reply to: David Steele (#8)
1 attachment(s)
Re: cast result of copyNode()

On Mar 21, 2017, at 2:13 PM, David Steele <david@pgmasters.net> wrote:

Hi Mark,

On 3/9/17 3:34 PM, Peter Eisentraut wrote:

On 3/7/17 18:27, Mark Dilger wrote:

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

Yeah, that's a bit silly. Here is an updated version that changes that.

Do you know when you'll have a chance to take a look at the updated patch?

The patch applies cleanly, compiles, and passes all the regression tests
for me on my laptop. Peter appears to have renamed the function copyObject
as copyObjectImpl, which struct me as odd when I first saw it, but I don't have
a better name in mind, so that seems ok.

If the purpose of this patch is to avoid casting so many things down to (Node *),
perhaps some additional work along the lines of the patch I'm attaching are
appropriate. (This patch applies on top Peter's v2 patch). The idea being to
keep objects as (Expr *) where appropriate, rather than casting through (Node *)
quite so much.

I'm not certain that this is (a) merely a bad idea, (b) a different idea than what
Peter is proposing, and as such should be submitted independently, or
(c) something that aught to be included in Peter's patch prior to commit.
I only applied this idea to one file, and maybe not completely in that file, because
I'd like feedback before going any further along these lines.

mark

Attachments:

ideas_on_top_v2.patchapplication/octet-stream; name=ideas_on_top_v2.patchDownload
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index e01ef86..9706733 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1155,7 +1155,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 	ListCell   *cell,
 			   *prev,
 			   *next;
-	Node	   *keyCol;
+	Expr	   *keyCol;
 	Oid			operoid;
 	bool		need_relabel,
 				list_has_null = false;
@@ -1164,14 +1164,14 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 
 	/* Left operand is either a simple Var or arbitrary expression */
 	if (key->partattrs[0] != 0)
-		keyCol = (Node *) makeVar(1,
+		keyCol = (Expr *) makeVar(1,
 								  key->partattrs[0],
 								  key->parttypid[0],
 								  key->parttypmod[0],
 								  key->parttypcoll[0],
 								  0);
 	else
-		keyCol = (Node *) copyObject(linitial(key->partexprs));
+		keyCol = (Expr *) copyObject(linitial(key->partexprs));
 
 	/*
 	 * We must remove any NULL value in the list; we handle it separately
@@ -1201,7 +1201,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 		 * expressions
 		 */
 		nulltest1 = makeNode(NullTest);
-		nulltest1->arg = (Expr *) keyCol;
+		nulltest1->arg = keyCol;
 		nulltest1->nulltesttype = IS_NOT_NULL;
 		nulltest1->argisrow = false;
 		nulltest1->location = -1;
@@ -1212,7 +1212,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 		 * Gin up a col IS NULL test that will be OR'd with other expressions
 		 */
 		nulltest2 = makeNode(NullTest);
-		nulltest2->arg = (Expr *) keyCol;
+		nulltest2->arg = keyCol;
 		nulltest2->nulltesttype = IS_NULL;
 		nulltest2->argisrow = false;
 		nulltest2->location = -1;
@@ -1233,7 +1233,7 @@ get_qual_for_list(PartitionKey key, PartitionBoundSpec *spec)
 	operoid = get_partition_operator(key, 0, BTEqualStrategyNumber,
 									 &need_relabel);
 	if (need_relabel || key->partcollation[0] != key->parttypcoll[0])
-		keyCol = (Node *) makeRelabelType((Expr *) keyCol,
+		keyCol = (Expr *) makeRelabelType((Expr *) keyCol,
 										  key->partopcintype[0],
 										  -1,
 										  key->partcollation[0],
@@ -1287,7 +1287,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 	{
 		PartitionRangeDatum *ldatum = lfirst(cell1),
 				   *udatum = lfirst(cell2);
-		Node	   *keyCol;
+		Expr	   *keyCol;
 		Const	   *lower_val = NULL,
 				   *upper_val = NULL;
 		EState	   *estate;
@@ -1303,7 +1303,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 		/* Left operand */
 		if (key->partattrs[i] != 0)
 		{
-			keyCol = (Node *) makeVar(1,
+			keyCol = (Expr *) makeVar(1,
 									  key->partattrs[i],
 									  key->parttypid[i],
 									  key->parttypmod[i],
@@ -1312,7 +1312,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 		}
 		else
 		{
-			keyCol = (Node *) copyObject(lfirst(partexprs_item));
+			keyCol = copyObject(lfirst(partexprs_item));
 			partexprs_item = lnext(partexprs_item);
 		}
 
@@ -1325,7 +1325,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 		if (!IsA(keyCol, Var))
 		{
 			nulltest = makeNode(NullTest);
-			nulltest->arg = (Expr *) keyCol;
+			nulltest->arg = keyCol;
 			nulltest->nulltesttype = IS_NOT_NULL;
 			nulltest->argisrow = false;
 			nulltest->location = -1;
@@ -1380,7 +1380,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 					elog(ERROR, "invalid range bound specification");
 
 				if (need_relabel || key->partcollation[i] != key->parttypcoll[i])
-					keyCol = (Node *) makeRelabelType((Expr *) keyCol,
+					keyCol = (Expr *) makeRelabelType(keyCol,
 													  key->partopcintype[i],
 													  -1,
 													  key->partcollation[i],
@@ -1389,7 +1389,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 								 make_opclause(operoid,
 											   BOOLOID,
 											   false,
-											   (Expr *) keyCol,
+											   keyCol,
 											   (Expr *) lower_val,
 											   InvalidOid,
 											   key->partcollation[i]));
@@ -1411,7 +1411,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 											 &need_relabel);
 
 			if (need_relabel || key->partcollation[i] != key->parttypcoll[i])
-				keyCol = (Node *) makeRelabelType((Expr *) keyCol,
+				keyCol = (Expr *) makeRelabelType(keyCol,
 												  key->partopcintype[i],
 												  -1,
 												  key->partcollation[i],
@@ -1420,7 +1420,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 							 make_opclause(operoid,
 										   BOOLOID,
 										   false,
-										   (Expr *) keyCol,
+										   keyCol,
 										   (Expr *) lower_val,
 										   InvalidOid,
 										   key->partcollation[i]));
@@ -1433,7 +1433,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 											 &need_relabel);
 
 			if (need_relabel || key->partcollation[i] != key->parttypcoll[i])
-				keyCol = (Node *) makeRelabelType((Expr *) keyCol,
+				keyCol = (Expr *) makeRelabelType(keyCol,
 												  key->partopcintype[i],
 												  -1,
 												  key->partcollation[i],
@@ -1443,7 +1443,7 @@ get_qual_for_range(PartitionKey key, PartitionBoundSpec *spec)
 							 make_opclause(operoid,
 										   BOOLOID,
 										   false,
-										   (Expr *) keyCol,
+										   keyCol,
 										   (Expr *) upper_val,
 										   InvalidOid,
 										   key->partcollation[i]));
#10David Steele
david@pgmasters.net
In reply to: Mark Dilger (#9)
Re: cast result of copyNode()

On 3/21/17 6:52 PM, Mark Dilger wrote:

On Mar 21, 2017, at 2:13 PM, David Steele <david@pgmasters.net> wrote:

Hi Mark,

On 3/9/17 3:34 PM, Peter Eisentraut wrote:

On 3/7/17 18:27, Mark Dilger wrote:

You appear to be using a #define macro to wrap a function of the same name
with the code:

#define copyObject(obj) ((typeof(obj)) copyObject(obj))

Yeah, that's a bit silly. Here is an updated version that changes that.

Do you know when you'll have a chance to take a look at the updated patch?

The patch applies cleanly, compiles, and passes all the regression tests
for me on my laptop. Peter appears to have renamed the function copyObject
as copyObjectImpl, which struct me as odd when I first saw it, but I don't have
a better name in mind, so that seems ok.

If the purpose of this patch is to avoid casting so many things down to (Node *),
perhaps some additional work along the lines of the patch I'm attaching are
appropriate. (This patch applies on top Peter's v2 patch). The idea being to
keep objects as (Expr *) where appropriate, rather than casting through (Node *)
quite so much.

I'm not certain that this is (a) merely a bad idea, (b) a different idea than what
Peter is proposing, and as such should be submitted independently, or
(c) something that aught to be included in Peter's patch prior to commit.
I only applied this idea to one file, and maybe not completely in that file, because
I'd like feedback before going any further along these lines.

I have marked this "Waiting on Author" pending Peter's input.

--
-David
david@pgmasters.net

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Peter Eisentraut
peter.eisentraut@2ndquadrant.com
In reply to: Mark Dilger (#9)
Re: cast result of copyNode()

On 3/21/17 18:52, Mark Dilger wrote:

The patch applies cleanly, compiles, and passes all the regression tests
for me on my laptop. Peter appears to have renamed the function copyObject
as copyObjectImpl, which struct me as odd when I first saw it, but I don't have
a better name in mind, so that seems ok.

Committed that.

If the purpose of this patch is to avoid casting so many things down to (Node *),
perhaps some additional work along the lines of the patch I'm attaching are
appropriate. (This patch applies on top Peter's v2 patch). The idea being to
keep objects as (Expr *) where appropriate, rather than casting through (Node *)
quite so much.

And that.

The distinction between Node and Expr is more theoretical and not
handled very ridigly throughout the code. However, your patch seemed
like a gentle improvement in relatively new code, so it seems like a
good change.

--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers