Repository: Qiskit/qiskit

Issue:
Parameterized circuits yield incorrect QASM output
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master
- **Python version**:
- **Operating system**:

### What is the current behavior?
For example:
```python

from qiskit import *
from qiskit.circuit import Parameter

alpha = Parameter('alpha')
qc = QuantumCircuit(1)
qc.p(alpha, 0)
qc.qasm(True)
```
gives

```
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
p(alpha) q[0];
```
but alpha is not defined.  This leads to errors when importing the QASM.
### Steps to reproduce the problem



### What is the expected behavior?



### Suggested solutions
I am guessing that this is a QASM2 issue that is to be fixed in QASM3.  However there is no exception raised here to indicate that the QASM is invalid for a parameterized circuit.




Discussion:

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -29,6 +29,7 @@
 from qiskit.circuit.gate import Gate
 from qiskit.circuit.parameter import Parameter
 from qiskit.qasm.qasm import Qasm
+from qiskit.qasm.exceptions import QasmError
 from qiskit.circuit.exceptions import CircuitError
 from qiskit.utils.deprecation import deprecate_function
 from .parameterexpression import ParameterExpression
@@ -1143,7 +1144,10 @@ def qasm(self, formatted=False, filename=None):
         Raises:
             ImportError: If pygments is not installed and ``formatted`` is
                 ``True``.
+            QasmError: If circuit has free parameters.
         """
+        if self.num_parameters > 0:
+            raise QasmError('Cannot represent circuits with unbound parameters in OpenQASM 2.')
         existing_gate_names = ['ch', 'cp', 'cx', 'cy', 'cz', 'crx', 'cry', 'crz', 'ccx', 'cswap',
                                'csx', 'cu', 'cu1', 'cu3', 'dcx', 'h', 'i', 'id', 'iden', 'iswap',
                                'ms', 'p', 'r', 'rx', 'rxx', 'ry', 'ryy', 'rz', 'rzx', 'rzz', 's',


Test patch:
diff --git a/test/python/circuit/library/test_blueprintcircuit.py b/test/python/circuit/library/test_blueprintcircuit.py
--- a/test/python/circuit/library/test_blueprintcircuit.py
+++ b/test/python/circuit/library/test_blueprintcircuit.py
@@ -96,6 +96,8 @@ def test_calling_attributes_works(self):
         for method in methods:
             with self.subTest(method=method):
                 circuit = MockBlueprint(3)
+                if method == 'qasm':
+                    continue  # raises since parameterized circuits produce invalid qasm 2.0.
                 getattr(circuit, method)()
                 self.assertGreater(len(circuit._data), 0)
 
diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py
--- a/test/python/circuit/test_circuit_qasm.py
+++ b/test/python/circuit/test_circuit_qasm.py
@@ -16,6 +16,8 @@
 
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit.test import QiskitTestCase
+from qiskit.circuit import Parameter
+from qiskit.qasm.exceptions import QasmError
 
 
 class TestCircuitQasm(QiskitTestCase):
@@ -210,3 +212,11 @@ def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self):
         qc = QuantumCircuit.from_qasm_str(original_str)
 
         self.assertEqual(original_str, qc.qasm())
+
+    def test_unbound_circuit_raises(self):
+        """Test circuits with unbound parameters raises."""
+        qc = QuantumCircuit(1)
+        theta = Parameter('θ')
+        qc.rz(theta, 0)
+        with self.assertRaises(QasmError):
+            qc.qasm()