refactored parameterization support


git-svn-id: https://asn1c.svn.sourceforge.net/svnroot/asn1c/trunk@1102 59561ff5-6e30-0410-9f3c-9617f08c8826
diff --git a/libasn1fix/asn1fix.c b/libasn1fix/asn1fix.c
index d8cc863..24ba7c8 100644
--- a/libasn1fix/asn1fix.c
+++ b/libasn1fix/asn1fix.c
@@ -15,6 +15,7 @@
 static int asn1f_check_constraints(arg_t *arg);	/* For subtype constraints */
 static int asn1f_check_duplicate(arg_t *arg);
 static int asn1f_apply_unique_index(arg_t *arg);
+static int phase_1_1(arg_t *arg, int prm2);
 
 arg_t a1f_replace_me_with_proper_interface_arg;
 
@@ -179,71 +180,21 @@
 	 */
 	TQ_FOR(expr, &(arg->mod->members), next) {
 		arg->expr = expr;
-
-		/* Check whether this type is a duplicate */
-		ret = asn1f_check_duplicate(arg);
+		ret = phase_1_1(arg, 0);
 		RET2RVAL(ret, rvalue);
-
-		if(expr->meta_type == AMT_PARAMTYPE)
-			/* Do not process the parametrized type just yet */
-			continue;
-
-		DEBUG("=== Now processing \"%s\" (%d/0x%x) at line %d ===",
-			expr->Identifier, expr->meta_type, expr->expr_type,
-			expr->_lineno);
-		assert(expr->meta_type != AMT_INVALID);
-
-		/*
-		 * 2.1 Pre-process simple types (ENUMERATED, INTEGER, etc).
-		 */
-		ret = asn1f_recurse_expr(arg, asn1f_fix_simple);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * 2.5.4
-		 */
-		ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_types);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * Fix tagging of top-level types.
-		 */
-		ret = asn1f_fix_constr_tag(arg, 1);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * 2.[234] Process SEQUENCE/SET/CHOICE types.
-		 */
-		ret = asn1f_recurse_expr(arg, asn1f_fix_constructed);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * 2.5.5
-		 */
-		ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_values);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * Parse class objects and fill up the object class with data.
-		 */
-		ret = asn1f_parse_class_object(arg);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * Resolve references in constraints.
-		 */
-		ret = asn1f_recurse_expr(arg, asn1f_resolve_constraints);
-		RET2RVAL(ret, rvalue);
-
-		/*
-		 * 6. INTEGER value processed at 2.5.4.
-		 */
-
 		/*
 		 * Make sure everybody's behaving well.
 		 */
 		assert(arg->expr == expr);
 	}
+	TQ_FOR(expr, &(arg->mod->members), next) {
+		arg->expr = expr;
+		ret = phase_1_1(arg, 1);
+		RET2RVAL(ret, rvalue);
+		assert(arg->expr == expr);
+	}
+
+
 
 	/*
 	 * 5. Automatic tagging
@@ -296,15 +247,8 @@
 	int ret;
 
 	TQ_FOR(expr, &(arg->mod->members), next) {
-	}
-
-	TQ_FOR(expr, &(arg->mod->members), next) {
 		arg->expr = expr;
 
-		if(expr->meta_type == AMT_PARAMTYPE)
-			/* Do not process the parametrized types here */
-			continue;
-
 		/*
 		 * Dereference DEFAULT values.
 		 */
@@ -331,6 +275,88 @@
 }
 
 static int
+phase_1_1(arg_t *arg, int prm2) {
+	asn1p_expr_t *expr = arg->expr;
+	int rvalue = 0;
+	int ret;
+
+	if(expr->lhs_params && expr->spec_index == -1) {
+		int i;
+		if(!prm2)
+			/* Do not process the parameterized type just yet */
+			return 0;
+		for(i = 0; i < expr->specializations.pspecs_count; i++) {
+			arg->expr = expr->specializations.pspec[i].my_clone;
+			ret = phase_1_1(arg, 0);
+			RET2RVAL(ret, rvalue);
+		}
+		arg->expr = expr;	/* revert */
+		return rvalue;
+	} else if(prm2) {
+		return 0;	/* Already done! */
+	}
+
+	/* Check whether this type is a duplicate */
+	if(!expr->lhs_params) {
+		ret = asn1f_check_duplicate(arg);
+		RET2RVAL(ret, rvalue);
+	}
+
+	DEBUG("=== Now processing \"%s\" (%d/0x%x) at line %d ===",
+		expr->Identifier, expr->meta_type, expr->expr_type,
+		expr->_lineno);
+	assert(expr->meta_type != AMT_INVALID);
+
+	/*
+	 * 2.1 Pre-process simple types (ENUMERATED, INTEGER, etc).
+	 */
+	ret = asn1f_recurse_expr(arg, asn1f_fix_simple);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * 2.5.4
+	 */
+	ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_types);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * Fix tagging of top-level types.
+	 */
+	ret = asn1f_fix_constr_tag(arg, 1);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * 2.[234] Process SEQUENCE/SET/CHOICE types.
+	 */
+	ret = asn1f_recurse_expr(arg, asn1f_fix_constructed);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * 2.5.5
+	 */
+	ret = asn1f_recurse_expr(arg, asn1f_fix_dereference_values);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * Parse class objects and fill up the object class with data.
+	 */
+	ret = asn1f_parse_class_object(arg);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * Resolve references in constraints.
+	 */
+	ret = asn1f_recurse_expr(arg, asn1f_resolve_constraints);
+	RET2RVAL(ret, rvalue);
+
+	/*
+	 * 6. INTEGER value processed at 2.5.4.
+	 */
+
+	return rvalue;
+}
+
+static int
 asn1f_fix_simple(arg_t *arg) {
 	int rvalue = 0;
 	int ret;
diff --git a/libasn1fix/asn1fix_class.c b/libasn1fix/asn1fix_class.c
index b3bfae5..bf35719 100644
--- a/libasn1fix/asn1fix_class.c
+++ b/libasn1fix/asn1fix_class.c
@@ -1,7 +1,7 @@
 #include "asn1fix_internal.h"
 
 asn1p_expr_t *
-asn1f_class_access(arg_t *arg, asn1p_module_t *mod, asn1p_ref_t *ref) {
+asn1f_class_access(arg_t *arg, asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *ref) {
 	asn1p_expr_t *ioclass;
 	asn1p_expr_t *classfield;
 	asn1p_expr_t *expr;
@@ -20,14 +20,16 @@
 
 	tmpref = *ref;
 	tmpref.comp_count = 1;
-	ioclass = asn1f_lookup_symbol(arg, mod, &tmpref);
+	ioclass = asn1f_lookup_symbol(arg, mod, rhs_pspecs, &tmpref);
 	if(ioclass == NULL) {
 		errno = ESRCH;
 		return NULL;
 	}
 	if(ioclass->expr_type == A1TC_REFERENCE) {
 		ioclass = asn1f_lookup_symbol(arg,
-				ioclass->module, ioclass->reference);
+				ioclass->module,
+				ioclass->rhs_pspecs,
+				ioclass->reference);
 		if(ioclass == NULL) {
 			errno = ESRCH;
 			return NULL;
diff --git a/libasn1fix/asn1fix_class.h b/libasn1fix/asn1fix_class.h
index 763e48c..b704ff8 100644
--- a/libasn1fix/asn1fix_class.h
+++ b/libasn1fix/asn1fix_class.h
@@ -4,12 +4,6 @@
 /*
  * Fetch the element from the class-related stuff (thing) by its reference.
  */
-asn1p_expr_t *asn1f_class_access(arg_t *, asn1p_module_t *mod, asn1p_ref_t *);
-
-/*
- * Externally accessible version of above function.
- */
-asn1p_expr_t *asn1f_class_access2(asn1p_t *asn, asn1p_module_t *mod,
-	asn1p_expr_t *expr, asn1p_ref_t *);
+asn1p_expr_t *asn1f_class_access(arg_t *, asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *);
 
 #endif	/* _ASN1FIX_CLASS_H_ */
diff --git a/libasn1fix/asn1fix_constr.c b/libasn1fix/asn1fix_constr.c
index 2c995cc..353473d 100644
--- a/libasn1fix/asn1fix_constr.c
+++ b/libasn1fix/asn1fix_constr.c
@@ -471,7 +471,8 @@
 
 		DEBUG(" %s is a type reference", a->Identifier);
 
-		a = asn1f_lookup_symbol(arg, a->module, a->reference);
+		a = asn1f_lookup_symbol(arg,
+			a->module, a->rhs_pspecs, a->reference);
 		if(!a) return 0;	/* Already FATAL()'ed somewhere else */
 		WITH_MODULE(a->module, ret = _asn1f_compare_tags(arg, a, b));
 		return ret;
diff --git a/libasn1fix/asn1fix_constraint.c b/libasn1fix/asn1fix_constraint.c
index 4afca5a..23709a4 100644
--- a/libasn1fix/asn1fix_constraint.c
+++ b/libasn1fix/asn1fix_constraint.c
@@ -30,7 +30,7 @@
 		asn1p_expr_t *parent_expr;
 
 		assert(ref);
-		parent_expr = asn1f_lookup_symbol(arg, expr->module, ref);
+		parent_expr = asn1f_lookup_symbol(arg, expr->module, expr->rhs_pspecs, ref);
 		if(!parent_expr) {
 			if(errno != EEXIST) {
 				DEBUG("\tWhile fetching parent constraints: "
@@ -259,6 +259,7 @@
 	assert(ct->containedSubtype->type == ATV_REFERENCED);
 
 	rtype = asn1f_lookup_symbol(arg, arg->expr->module,
+		arg->expr->rhs_pspecs,
 		ct->containedSubtype->value.reference);
 	if(!rtype) {
 		FATAL("Cannot find type \"%s\" in constraints at line %d",
diff --git a/libasn1fix/asn1fix_cws.c b/libasn1fix/asn1fix_cws.c
index 0279c96..c6bd1ab 100644
--- a/libasn1fix/asn1fix_cws.c
+++ b/libasn1fix/asn1fix_cws.c
@@ -206,7 +206,7 @@
 		asn1p_ref_add_component(ref, p, RLT_UNKNOWN);
 		assert(ref);
 	
-		expr = asn1f_lookup_symbol(arg, arg->mod, ref);
+		expr = asn1f_lookup_symbol(arg, arg->mod, arg->expr->rhs_pspecs, ref);
 		if(!expr) {
 			FATAL("Cannot find %s referenced by %s at line %d",
 				p, arg->expr->Identifier,
diff --git a/libasn1fix/asn1fix_dereft.c b/libasn1fix/asn1fix_dereft.c
index 769abed..8f570f6 100644
--- a/libasn1fix/asn1fix_dereft.c
+++ b/libasn1fix/asn1fix_dereft.c
@@ -6,9 +6,6 @@
 	asn1p_expr_t *type_expr;
 	int r_value = 0;
 
-	if(expr->expr_type == A1TC_PARAMETRIZED)
-		return asn1f_fix_parametrized_assignment(arg);
-
 	if(expr->expr_type != A1TC_REFERENCE
 	|| expr->meta_type != AMT_TYPEREF) {
 		//assert(expr->reference == 0);
diff --git a/libasn1fix/asn1fix_export.c b/libasn1fix/asn1fix_export.c
index 0a543dc..6eb8cda 100644
--- a/libasn1fix/asn1fix_export.c
+++ b/libasn1fix/asn1fix_export.c
@@ -20,13 +20,14 @@
 	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
 
 
-	return asn1f_lookup_symbol(&arg, expr->module, ref);
+	return asn1f_lookup_symbol(&arg, expr->module, expr->rhs_pspecs, ref);
 }
 
 asn1p_expr_t *
 asn1f_class_access_ex(asn1p_t *asn,
 		asn1p_module_t *mod,
 		asn1p_expr_t *expr,
+		asn1p_expr_t *rhs_pspecs,
 		asn1p_ref_t *ref) {
 	arg_t arg;
 
@@ -38,7 +39,7 @@
 	arg.eh = a1f_replace_me_with_proper_interface_arg.eh;
 	arg.debug = a1f_replace_me_with_proper_interface_arg.debug;
 
-	return asn1f_class_access(&arg, mod, ref);
+	return asn1f_class_access(&arg, mod, rhs_pspecs, ref);
 }
 
 asn1p_expr_t *
diff --git a/libasn1fix/asn1fix_export.h b/libasn1fix/asn1fix_export.h
index 533ba28..606ddf8 100644
--- a/libasn1fix/asn1fix_export.h
+++ b/libasn1fix/asn1fix_export.h
@@ -25,7 +25,7 @@
  *  Exportable version of an asn1f_class_access().
  */
 asn1p_expr_t *asn1f_class_access_ex(asn1p_t *asn, asn1p_module_t *mod,
-	asn1p_expr_t *expr, asn1p_ref_t *);
+	asn1p_expr_t *expr, asn1p_expr_t *rhs_rspecs, asn1p_ref_t *);
 
 /*
  * Exportable version of asn1f_find_terminal_type().
diff --git a/libasn1fix/asn1fix_integer.c b/libasn1fix/asn1fix_integer.c
index 892e927..8a14491 100644
--- a/libasn1fix/asn1fix_integer.c
+++ b/libasn1fix/asn1fix_integer.c
@@ -143,7 +143,8 @@
 	 * Then, it is a reference. For a reference, try to resolve type
 	 * and try again.
 	 */
-	next_expr = asn1f_lookup_symbol(arg, expr->module, expr->reference);
+	next_expr = asn1f_lookup_symbol(arg, expr->module,
+			expr->rhs_pspecs, expr->reference);
 	if(next_expr == NULL) {
 		errno = ESRCH;
 		return -1;
diff --git a/libasn1fix/asn1fix_internal.h b/libasn1fix/asn1fix_internal.h
index dabf856..612b718 100644
--- a/libasn1fix/asn1fix_internal.h
+++ b/libasn1fix/asn1fix_internal.h
@@ -62,7 +62,7 @@
 #include "asn1fix_constr.h"		/* Constructed types */
 #include "asn1fix_class.h"		/* CLASS support */
 #include "asn1fix_cws.h"		/* CLASS WITH SYNTAX support */
-#include "asn1fix_param.h"		/* Parametrization */
+#include "asn1fix_param.h"		/* Parameterization */
 #include "asn1fix_retrieve.h"		/* Data retrieval */
 #include "asn1fix_enum.h"		/* Process ENUMERATED */
 #include "asn1fix_integer.h"		/* Process INTEGER */
diff --git a/libasn1fix/asn1fix_misc.c b/libasn1fix/asn1fix_misc.c
index b28a58a..5728bd4 100644
--- a/libasn1fix/asn1fix_misc.c
+++ b/libasn1fix/asn1fix_misc.c
@@ -196,6 +196,17 @@
 
 	assert(expr);
 
+	if(expr->lhs_params && expr->spec_index == -1) {
+		int i;
+		for(i = 0; i < expr->specializations.pspecs_count; i++) {
+			arg->expr = expr->specializations.pspec[i].my_clone;
+			ret = asn1f_recurse_expr(arg, callback);
+			RET2RVAL(ret, rvalue);
+		}
+		arg->expr = expr;       /* revert */
+		return rvalue;
+	}
+
 	/*
 	 * Invoke the callback at this very level.
 	 */
diff --git a/libasn1fix/asn1fix_param.c b/libasn1fix/asn1fix_param.c
index a4d9690..bbd7cf4 100644
--- a/libasn1fix/asn1fix_param.c
+++ b/libasn1fix/asn1fix_param.c
@@ -1,306 +1,217 @@
 #include "asn1fix_internal.h"
 
-static int asn1f_parametrize(arg_t *arg, asn1p_expr_t *ex, asn1p_expr_t *ptype);
-static int asn1f_param_process_recursive(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *ptype, asn1p_expr_t *actargs);
-static int asn1f_param_process_constraints(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *ptype, asn1p_expr_t *actargs);
+typedef struct resolver_arg {
+	asn1p_expr_t	  *(*resolver)(asn1p_expr_t *, void *arg);
+	arg_t		  *arg;
+	asn1p_expr_t	  *original_expr;
+	asn1p_paramlist_t *lhs_params;
+	asn1p_expr_t	  *rhs_pspecs;
+} resolver_arg_t;
 
-static asn1p_expr_t *_referenced_argument(asn1p_ref_t *ref, asn1p_expr_t *ptype, asn1p_expr_t *actargs);
-static int _process_constraints(arg_t *arg, asn1p_constraint_t *ct, asn1p_expr_t *ptype, asn1p_expr_t *actargs);
+static asn1p_expr_t *resolve_expr(asn1p_expr_t *, void *resolver_arg);
+static int compare_specializations(arg_t *, asn1p_expr_t *a, asn1p_expr_t *b);
+static asn1p_expr_t *find_target_specialization_byref(resolver_arg_t *rarg, asn1p_ref_t *ref);
+static asn1p_expr_t *find_target_specialization_bystr(resolver_arg_t *rarg, char *str);
 
-int
-asn1f_fix_parametrized_assignment(arg_t *arg) {
-	asn1p_expr_t *expr = arg->expr;
-	asn1p_expr_t *ptype;
-
-	assert(expr->expr_type == A1TC_PARAMETRIZED);
-	assert(expr->reference);
-
-	DEBUG("(\"%s\" ::= \"%s\" { %s }) for line %d",
-		expr->Identifier,
-		asn1f_printable_reference(expr->reference),
-		asn1f_printable_value(expr->value),
-		expr->_lineno);
-
-	/*
-	 * Find the corresponding parametrized type definition.
-	 */
-	DEBUG("Looking for parametrized type definition \"%s\"",
-		asn1f_printable_reference(expr->reference));
-	ptype = asn1f_lookup_symbol(arg, expr->module, expr->reference);
-	if(ptype == NULL) {
-		DEBUG("%s: missing parametrized type declaration",
-			asn1f_printable_reference(expr->reference));
-		return -1;
-	}
-
-	/*
-	 * Check that the number of arguments which are expected by
-	 * the parametrized type declaration is consistent with the
-	 * number of arguments supplied by the parametrized assignment.
-	 */
-	if(asn1f_count_children(expr) != ptype->params->params_count) {
-		FATAL("Number of actual arguments %d in %s at line %d "
-			"is not equal to number of expected arguments "
-			"%d in %s at line %d",
-			asn1f_count_children(expr),
-			asn1f_printable_reference(expr->reference),
-			expr->_lineno,
-			ptype->params->params_count,
-			ptype->Identifier,
-			ptype->_lineno
-		);
-		return -1;
-	}
-
-	/*
-	 * Perform an expansion of a parametrized assignment.
-	 */
-	return asn1f_parametrize(arg, expr, ptype);
-}
-
-#define	SUBSTITUTE(to, from)	do {				\
-	asn1p_expr_t tmp, *__v;					\
-	if((to)->tag.tag_class					\
-	&& (from)->tag.tag_class) {				\
-		FATAL("Layered tagging in parametrization "	\
-		"is not yet supported, "			\
-		"contact asn1c author for assistance "		\
-		"with line %d", (to)->_lineno);			\
-		return -1;					\
-	}							\
-	/* This code shall not be invoked too early */		\
-	assert((to)->combined_constraints == NULL);		\
-	assert((from)->combined_constraints == NULL);		\
-	/* Copy stuff, and merge some parameters */		\
-	tmp = *(to);						\
-	*(to) = *(from);					\
-	TQ_MOVE(&(to)->members, &(from)->members);		\
-	*(from) = tmp;						\
-	(to)->next = tmp.next;					\
-	(to)->parent_expr = tmp.parent_expr;			\
-	assert((to)->marker.flags == EM_NOMARK);		\
-	(to)->marker = tmp.marker;				\
-	if(tmp.tag.tag_class)					\
-		(to)->tag = tmp.tag;				\
-	if(tmp.constraints) {					\
-		if((to)->constraints) {				\
-			asn1p_constraint_t *ct;			\
-			ct = asn1p_constraint_new(		\
-				(to)->constraints->_lineno);	\
-			ct->type = ACT_CA_SET;			\
-			asn1p_constraint_insert(ct,		\
-				(to)->constraints);		\
-			asn1p_constraint_insert(ct,		\
-				tmp.constraints);		\
-			(to)->constraints = ct;			\
-		} else {					\
-			(to)->constraints = tmp.constraints;	\
-		}						\
-	}							\
-	(from)->constraints = 0;				\
-	(from)->marker.default_value = 0;			\
-	memset(&((from)->next), 0, sizeof((from)->next));	\
-	memset(&((from)->members), 0, sizeof((from)->members));	\
-	asn1p_expr_free(from);					\
-	TQ_FOR(__v, &((to)->members), next) {			\
-		assert(__v->parent_expr == (from));		\
-		__v->parent_expr = (to);			\
-	}							\
-} while(0)
-
-static int
-asn1f_parametrize(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *ptype) {
-	asn1p_expr_t *nex;
+asn1p_expr_t *
+asn1f_parameterization_fork(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *rhs_pspecs) {
+	resolver_arg_t rarg;	/* resolver argument */
+	asn1p_expr_t *exc;	/* expr clone */
+	asn1p_expr_t *rpc;	/* rhs_pspecs clone */
+	asn1p_expr_t *target;
 	void *p;
-	int ret;
+	struct asn1p_pspec_s *pspec;
+	int npspecs;
+	int i;
 
-	DEBUG("asn1f_parametrize(%s <= %s)",
-		expr->Identifier, ptype->Identifier);
+	assert(rhs_pspecs);
+	assert(expr->lhs_params);
+	assert(expr->parent_expr == 0);
+
+	DEBUG("Forking parameterization at %d for %s (%d alr)",
+		rhs_pspecs->_lineno, expr->Identifier,
+		expr->specializations.pspecs_count);
 
 	/*
-	 * The algorithm goes like that:
-	 * 1. Replace the expression's type with parametrized type.
-	 * 2. For every child in the parametrized type, import it
-	 * as a child of the expression, replacing all occurences of
-	 * symbols which are defined as parametrized type arguments
-	 * with the actual values.
-	 * 3. Don't forget to parametrize the subtype constraints.
+	 * Find if this exact specialization has been used already.
 	 */
-
-	nex = asn1p_expr_clone(ptype, 0);
-	if(nex == NULL) return -1;
-
-	/*
-	 * Cleanup the new expression so there is no ptype-related
-	 * stuff hanging around.
-	 */
-	if(expr->Identifier) {
-		p = strdup(expr->Identifier);
-		if(p) {
-			free(nex->Identifier);
-			nex->Identifier = p;
-		} else {
-			asn1p_expr_free(nex);
-			return -1;
+	for(npspecs = 0;
+		npspecs < expr->specializations.pspecs_count;
+			npspecs++) {
+		if(compare_specializations(arg, rhs_pspecs,
+			expr->specializations.pspec[npspecs].rhs_pspecs) == 0) {
+			DEBUG("Reused parameterization for %s",
+				expr->Identifier);
+			return expr->specializations.pspec[npspecs].my_clone;
 		}
 	}
-	asn1p_paramlist_free(nex->params);
-	nex->params = NULL;
-	nex->meta_type = expr->meta_type;
 
-	ret = asn1f_param_process_recursive(arg, nex, ptype, expr);
-	if(ret != 0) {
-		asn1p_expr_free(nex);
-		return ret;
+	rarg.resolver = resolve_expr;
+	rarg.arg = arg;
+	rarg.original_expr = expr;
+	rarg.lhs_params = expr->lhs_params;
+	rarg.rhs_pspecs = rhs_pspecs;
+	exc = asn1p_expr_clone_with_resolver(expr, resolve_expr, &rarg);
+	rpc = asn1p_expr_clone(rhs_pspecs, 0);
+	assert(exc && rpc);
+
+	/*
+	 * Create a new specialization.
+	 */
+	npspecs = expr->specializations.pspecs_count;
+	p = realloc(expr->specializations.pspec,
+			(npspecs + 1) * sizeof(expr->specializations.pspec[0]));
+	assert(p);
+	expr->specializations.pspec = p;
+	pspec = &expr->specializations.pspec[npspecs];
+	memset(pspec, 0, sizeof *pspec);
+
+	pspec->rhs_pspecs = rpc;
+	pspec->my_clone = exc;
+	exc->spec_index = npspecs;
+
+	/* Update LHS->RHS specialization in target */
+	target = TQ_FIRST(&rpc->members);
+	for(i = 0; i < exc->lhs_params->params_count;
+			i++, target = TQ_NEXT(target, next)) {
+		if(!target) { target = (void *)0xdeadbeef; break; }
+
+		assert(exc->lhs_params->params[i].into_expr == 0);
+		exc->lhs_params->params[i].into_expr = target;
+	}
+	if(target) {
+		asn1p_expr_free(exc);
+		asn1p_expr_free(rpc);
+		FATAL("Parameterization of %s failed: "
+			"parameters number mismatch", expr->Identifier);
+		errno = EPERM;
+		return NULL;
 	}
 
-	SUBSTITUTE(expr, nex);
+	DEBUG("Forked new parameterization for %s", expr->Identifier);
 
-	return ret;
+	/* Commit */
+	expr->specializations.pspecs_count = npspecs + 1;
+	return exc;
 }
 
 static int
-asn1f_param_process_recursive(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *ptype, asn1p_expr_t *actargs) {
-	asn1p_expr_t *child;
+compare_specializations(arg_t *arg, asn1p_expr_t *a, asn1p_expr_t *b) {
+	asn1p_expr_t *ac = TQ_FIRST(&a->members);
+	asn1p_expr_t *bc = TQ_FIRST(&b->members);
 
+	for(;ac && bc; ac = TQ_NEXT(ac, next), bc = TQ_NEXT(bc, next)) {
+	  retry:
+		if(ac == bc) continue;
+		if(ac->meta_type != bc->meta_type) break;
+		if(ac->expr_type != bc->expr_type) break;
 
-	TQ_FOR(child, &(expr->members), next) {
-		asn1p_expr_t *ra;
-		asn1p_expr_t *ne;	/* new expression (clone) */
+		if(!ac->reference && !bc->reference)
+			continue;
 
-		if(asn1f_param_process_constraints(arg, child, ptype, actargs))
-			return -1;
-
-		ra = _referenced_argument(child->reference, ptype, actargs);
-		if(ra) {
-			DEBUG("Substituting parameter for %s %s at line %d",
-				child->Identifier,
-				asn1f_printable_reference(child->reference),
-				child->_lineno
-			);
-	
-			assert(child->meta_type == AMT_TYPEREF);
-			assert(child->expr_type == A1TC_REFERENCE);
-	
-			ne = asn1p_expr_clone(ra, 0);
-			if(ne == NULL) return -1;
-			assert(ne->Identifier == 0);
-			ne->Identifier = strdup(child->Identifier);
-			if(ne->Identifier == 0) {
-				asn1p_expr_free(ne);
-				return -1;
-			}
-			SUBSTITUTE(child, ne);
+		if(ac->reference) {
+			ac = asn1f_lookup_symbol(arg,
+				ac->module, ac->rhs_pspecs, ac->reference);
+			if(!ac) break;
 		}
+		if(bc->reference) {
+			bc = asn1f_lookup_symbol(arg,
+				bc->module, bc->rhs_pspecs, bc->reference);
+			if(!bc) break;
+		}
+	  goto retry;
 	}
 
+	if(ac || bc)
+		/* Specializations do not match: different size option sets */
+		return -1;
+
 	return 0;
 }
 
-/*
- * Check that the given ref looks like an argument of a parametrized type.
- */
 static asn1p_expr_t *
-_referenced_argument(asn1p_ref_t *ref, asn1p_expr_t *ptype, asn1p_expr_t *actargs) {
-	asn1p_expr_t *aa;
-	int i;
+resolve_expr(asn1p_expr_t *expr_to_resolve, void *resolver_arg) {
+	resolver_arg_t *rarg = resolver_arg;
+	arg_t *arg = rarg->arg;
+	asn1p_expr_t *expr;
+	asn1p_expr_t *nex;
 
-	if(ref == NULL || ref->comp_count != 1)
+	DEBUG("Resolving %s (meta %d)",
+		expr_to_resolve->Identifier, expr_to_resolve->meta_type);
+
+	if(expr_to_resolve->meta_type == AMT_TYPEREF) {
+		expr = find_target_specialization_byref(rarg,
+				expr_to_resolve->reference);
+		if(!expr) return NULL;
+	} else if(expr_to_resolve->meta_type == AMT_VALUE) {
+		assert(expr_to_resolve->value);
+		expr = find_target_specialization_bystr(rarg,
+				expr_to_resolve->Identifier);
+		if(!expr) return NULL;
+	} else {
+		errno = ESRCH;
 		return NULL;
+	}
 
-	aa = TQ_FIRST(&(actargs->members));
-	for(i = 0; i < ptype->params->params_count;
-				i++, aa = TQ_NEXT(aa, next)) {
-		if(strcmp(ref->components[0].name,
-			ptype->params->params[i].argument) == 0)
-			return aa;
+	DEBUG("Found target %s", expr->Identifier);
+	if(expr->meta_type == AMT_TYPE
+	|| expr->meta_type == AMT_VALUE) {
+		DEBUG("Target is a simple type %s",
+			ASN_EXPR_TYPE2STR(expr->expr_type));
+		nex = asn1p_expr_clone(expr, 0);
+		free(nex->Identifier);
+		nex->Identifier = expr_to_resolve->Identifier
+			? strdup(expr_to_resolve->Identifier) : 0;
+		return nex;
+	} else {
+		FATAL("Feature not implemented for %s",
+			rarg->original_expr->Identifier);
+		errno = EPERM;
+		return NULL;
 	}
 
 	return NULL;
 }
 
-/*
- * Search for parameters inside constraints.
- */
-static int
-asn1f_param_process_constraints(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *ptype, asn1p_expr_t *actargs) {
-	asn1p_constraint_t *cts;
-	int ret;
+static asn1p_expr_t *
+find_target_specialization_byref(resolver_arg_t *rarg, asn1p_ref_t *ref) {
+	char *refstr;
 
-	if(!expr->constraints) return 0;
-
-	cts = asn1p_constraint_clone(expr->constraints);
-	assert(cts);
-
-	ret = _process_constraints(arg, cts, ptype, actargs);
-	if(ret == 1) {
-		asn1p_constraint_free(expr->constraints);
-		expr->constraints = cts;
-		ret = 0;
-	} else {
-		asn1p_constraint_free(cts);
+	if(!ref || ref->comp_count != 1) {
+		errno = ESRCH;
+		return NULL;
 	}
 
-	return ret;
+	refstr = ref->components[0].name;	/* T */
+
+	return find_target_specialization_bystr(rarg, refstr);
 }
 
-static int
-_process_constraints(arg_t *arg, asn1p_constraint_t *ct, asn1p_expr_t *ptype, asn1p_expr_t *actargs) {
-	asn1p_value_t *values[3];
-	int rvalue = 0;
-	size_t i;
+static asn1p_expr_t *
+find_target_specialization_bystr(resolver_arg_t *rarg, char *refstr) {
+	arg_t *arg = rarg->arg;
+	asn1p_expr_t *target;
+	int i;
 
-	values[0] = ct->value;
-	values[1] = ct->range_start;
-	values[2] = ct->range_stop;
+	target = TQ_FIRST(&rarg->rhs_pspecs->members);
+	for(i = 0; i < rarg->lhs_params->params_count;
+			i++, target = TQ_NEXT(target, next)) {
+		struct asn1p_param_s *param = &rarg->lhs_params->params[i];
+		if(!target) break;
 
-	for(i = 0; i < sizeof(values)/sizeof(values[0]); i++) {
-		asn1p_value_t *v = values[i];
-		asn1p_expr_t *ra;
-		asn1p_ref_t *ref;
-		char *str;
+		if(strcmp(param->argument, refstr))
+			continue;
 
-		if(!v || v->type != ATV_REFERENCED) continue;
-
-		ref = v->value.reference;
-		ra = _referenced_argument(ref, ptype, actargs);
-		if(!ra) continue;
-
-		DEBUG("_process_constraints(%s), ra=%s",
-			asn1f_printable_reference(ref), ra->Identifier);
-
-		if(ra->expr_type == A1TC_PARAMETRIZED) {
-			DEBUG("Double %s", "parametrization");
-		}
-
-		assert(ra->Identifier);
-		str = strdup(ra->Identifier);
-		if(!str) return -1;
-
-		assert(ref->comp_count == 1);
-		ref = asn1p_ref_new(ref->_lineno);
-		if(!ref) { free(str); return -1; }
-
-		if(asn1p_ref_add_component(ref, str, 0)) {
-			free(str);
-			return -1;
-		}
-
-		asn1p_ref_free(v->value.reference);
-		v->value.reference = ref;
-		rvalue = 1;
+		return target;
+	}
+	if(i != rarg->lhs_params->params_count) {
+		FATAL("Parameterization of %s failed: "
+			"parameters number mismatch",
+				rarg->original_expr->Identifier);
+		errno = EPERM;
+		return NULL;
 	}
 
-	/* Process the rest of constraints recursively */
-	for(i = 0; i < ct->el_count; i++) {
-		int ret = _process_constraints(arg, ct->elements[i],
-			ptype, actargs);
-		if(ret == -1)
-			rvalue = -1;
-		else if(ret == 1 && rvalue != -1)
-			rvalue = 1;
-	}
-
-	return rvalue;
+	errno = ESRCH;
+	return NULL;
 }
-
diff --git a/libasn1fix/asn1fix_param.h b/libasn1fix/asn1fix_param.h
index 062ad8b..5940b0e 100644
--- a/libasn1fix/asn1fix_param.h
+++ b/libasn1fix/asn1fix_param.h
@@ -1,6 +1,6 @@
-#ifndef	_ASN1FIX_PARAMETRIZATION_H_
-#define	_ASN1FIX_PARAMETRIZATION_H_
+#ifndef	_ASN1FIX_PARAMETERIZATION_H_
+#define	_ASN1FIX_PARAMETERIZATION_H_
 
-int asn1f_fix_parametrized_assignment(arg_t *arg);
+asn1p_expr_t *asn1f_parameterization_fork(arg_t *arg, asn1p_expr_t *expr, asn1p_expr_t *rhs_pspecs);
 
-#endif	/* _ASN1FIX_PARAMETRIZATION_H_ */
+#endif	/* _ASN1FIX_PARAMETERIZATION_H_ */
diff --git a/libasn1fix/asn1fix_retrieve.c b/libasn1fix/asn1fix_retrieve.c
index 2c004be..52506b8 100644
--- a/libasn1fix/asn1fix_retrieve.c
+++ b/libasn1fix/asn1fix_retrieve.c
@@ -146,7 +146,7 @@
 }
 
 static asn1p_expr_t *
-asn1f_lookup_symbol_impl(arg_t *arg, asn1p_module_t *mod, asn1p_ref_t *ref, int recursion_depth) {
+asn1f_lookup_symbol_impl(arg_t *arg, asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *ref, int recursion_depth) {
 	asn1p_expr_t *ref_tc;			/* Referenced tc */
 	asn1p_module_t *imports_from;
 	char *modulename;
@@ -193,7 +193,7 @@
 		 * This is a reference to a CLASS-related stuff.
 		 * Employ a separate function for that.
 		 */
-		extract = asn1f_class_access(arg, mod, ref);
+		extract = asn1f_class_access(arg, mod, rhs_pspecs, ref);
 		
 		return extract;
 	} else {
@@ -255,7 +255,8 @@
 			assert(tmpref.comp_count > 0);
 		}
 
-		expr = asn1f_lookup_symbol_impl(arg, imports_from, &tmpref, recursion_depth);
+		expr = asn1f_lookup_symbol_impl(arg, imports_from,
+				rhs_pspecs, &tmpref, recursion_depth);
 		if(!expr && !(arg->expr->_mark & TM_BROKEN)
 		&& !(imports_from->_tags & MT_STANDARD_MODULE)) {
 			arg->expr->_mark |= TM_BROKEN;
@@ -285,9 +286,38 @@
 		if(strcmp(ref_tc->Identifier, identifier) == 0)
 			break;
 	}
-	if(ref_tc)
-		return ref_tc;
+	if(ref_tc) {
+		if(rhs_pspecs && !ref_tc->lhs_params) {
+			FATAL("Parameterized type %s expected "
+				"for %s at line %d",
+				ref_tc->Identifier,
+				asn1f_printable_reference(ref),
+				ref->_lineno);
+			errno = EPERM;
+			return NULL;
+		}
+		if(!rhs_pspecs && ref_tc->lhs_params) {
+			FATAL("Type %s expects specialization "
+				"from %s at line %d",
+				ref_tc->Identifier,
+				asn1f_printable_reference(ref),
+				ref->_lineno);
+			errno = EPERM;
+			return NULL;
+		}
+		if(rhs_pspecs && ref_tc->lhs_params) {
+			/* Specialize the target */
+			ref_tc = asn1f_parameterization_fork(arg,
+				ref_tc, rhs_pspecs);
+		}
 
+		return ref_tc;
+	}
+
+	/*
+	 * Not found in the current module.
+	 * Search in our default standard module.
+	 */
 	{
 		/* Search inside standard module */
 		static asn1p_oid_t *uioc_oid;
@@ -335,8 +365,9 @@
 
 
 asn1p_expr_t *
-asn1f_lookup_symbol(arg_t *arg, asn1p_module_t *mod, asn1p_ref_t *ref) {
-	return asn1f_lookup_symbol_impl(arg, mod, ref, 0);
+asn1f_lookup_symbol(arg_t *arg,
+	asn1p_module_t *mod, asn1p_expr_t *rhs_pspecs, asn1p_ref_t *ref) {
+	return asn1f_lookup_symbol_impl(arg, mod, rhs_pspecs, ref, 0);
 }
 
 asn1p_expr_t *
@@ -397,7 +428,7 @@
 	/*
 	 * Lookup inside the default module and its IMPORTS section.
 	 */
-	tc = asn1f_lookup_symbol(arg, expr->module, ref);
+	tc = asn1f_lookup_symbol(arg, expr->module, expr->rhs_pspecs, ref);
 	if(tc == NULL) {
 		DEBUG("\tSymbol \"%s\" not found: %s",
 			asn1f_printable_reference(ref),
diff --git a/libasn1fix/asn1fix_retrieve.h b/libasn1fix/asn1fix_retrieve.h
index 8ab0260..2aeb82b 100644
--- a/libasn1fix/asn1fix_retrieve.h
+++ b/libasn1fix/asn1fix_retrieve.h
@@ -39,6 +39,7 @@
  */
 asn1p_expr_t *asn1f_lookup_symbol(arg_t *arg,
 		asn1p_module_t *mod,
+		asn1p_expr_t *rhs_pspecs,
 		asn1p_ref_t *ref);
 
 /*
diff --git a/libasn1fix/asn1fix_tags.c b/libasn1fix/asn1fix_tags.c
index 3215fff..db4d4ed 100644
--- a/libasn1fix/asn1fix_tags.c
+++ b/libasn1fix/asn1fix_tags.c
@@ -66,7 +66,7 @@
 	if(expr->meta_type == AMT_TYPEREF) {
 		asn1p_expr_t *nexpr;
 		DEBUG("Following the reference %s", expr->Identifier);
-		nexpr = asn1f_lookup_symbol(arg, expr->module, expr->reference);
+		nexpr = asn1f_lookup_symbol(arg, expr->module, expr->rhs_pspecs, expr->reference);
 		if(nexpr == NULL) {
 			if(errno != EEXIST)	/* -fknown-extern-type */
 				return -1;